{ "version": 3, "sources": ["../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/adapters.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/logger.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/connection_monitor.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/internal.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/connection.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/subscription.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/subscription_guarantor.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/subscriptions.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/consumer.js", "../../../node_modules/@hotwired/turbo-rails/node_modules/@rails/actioncable/src/index.js", "../../../node_modules/picocolors/picocolors.browser.js", "../../../node_modules/tailwindcss/lib/util/log.js", "../../../node_modules/tailwindcss/lib/public/colors.js", "../../../node_modules/tailwindcss/colors.js", "../../../node_modules/mini-svg-data-uri/shorter-css-color-names.js", "../../../node_modules/mini-svg-data-uri/index.js", "../../../node_modules/tailwindcss/lib/util/createPlugin.js", "../../../node_modules/tailwindcss/lib/public/create-plugin.js", "../../../node_modules/tailwindcss/plugin.js", "../../../node_modules/tailwindcss/lib/util/cloneDeep.js", "../../../node_modules/tailwindcss/stubs/config.full.js", "../../../node_modules/tailwindcss/lib/public/default-theme.js", "../../../node_modules/tailwindcss/defaultTheme.js", "../../../node_modules/@tailwindcss/forms/src/index.js", "../../../tailwind.config.js", "../../../node_modules/pluralize/pluralize.js", "../../../node_modules/@datadog/browser-core/src/tools/display.ts", "../../../node_modules/@datadog/browser-core/src/tools/catchUserErrors.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/numberUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/timeUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/byteUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/objectUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/getGlobalObject.ts", "../../../node_modules/@datadog/browser-core/src/tools/getZoneJsOriginalValue.ts", "../../../node_modules/@datadog/browser-core/src/tools/monitor.ts", "../../../node_modules/@datadog/browser-core/src/tools/timer.ts", "../../../node_modules/@datadog/browser-core/src/tools/observable.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/functionUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/stringUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/browserDetection.ts", "../../../node_modules/@datadog/browser-core/src/browser/cookie.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionStoreStrategy.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/polyfills.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionConstants.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStateValidation.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionState.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/oldCookiesMigration.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionInLocalStorage.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStoreOperations.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStore.ts", "../../../node_modules/@datadog/browser-core/src/domain/trackingConsent.ts", "../../../node_modules/@datadog/browser-core/src/tools/serialisation/jsonStringify.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/urlPolyfill.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/intakeSites.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/endpointBuilder.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/tags.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/transportConfiguration.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/configuration.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/index.ts", "../../../node_modules/@datadog/browser-core/src/tools/experimentalFeatures.ts", "../../../node_modules/@datadog/browser-core/src/tools/stackTrace/computeStackTrace.ts", "../../../node_modules/@datadog/browser-core/src/tools/stackTrace/handlingStack.ts", "../../../node_modules/@datadog/browser-core/src/tools/instrumentMethod.ts", "../../../node_modules/@datadog/browser-core/src/tools/serialisation/sanitize.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/error.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/error.types.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/trackRuntimeError.ts", "../../../node_modules/@datadog/browser-core/src/boot/init.ts", "../../../node_modules/@datadog/browser-core/src/boot/displayAlreadyInitializedError.ts", "../../../node_modules/@datadog/browser-core/src/browser/addEventListener.ts", "../../../node_modules/@datadog/browser-core/src/domain/report/reportObservable.ts", "../../../node_modules/@datadog/browser-core/src/tools/sendToExtension.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/typeUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/mergeInto.ts", "../../../node_modules/@datadog/browser-core/src/domain/connectivity/connectivity.ts", "../../../node_modules/@datadog/browser-core/src/domain/connectivity/index.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/arrayUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/boundedBuffer.ts", "../../../node_modules/@datadog/browser-core/src/domain/telemetry/rawTelemetryEvent.types.ts", "../../../node_modules/@datadog/browser-core/src/domain/telemetry/telemetry.ts", "../../../node_modules/@datadog/browser-core/src/domain/telemetry/index.ts", "../../../node_modules/@datadog/browser-core/src/tools/valueHistory.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionManager.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/responseUtils.ts", "../../../node_modules/@datadog/browser-core/src/transport/sendWithRetryStrategy.ts", "../../../node_modules/@datadog/browser-core/src/transport/httpRequest.ts", "../../../node_modules/@datadog/browser-core/src/transport/eventBridge.ts", "../../../node_modules/@datadog/browser-core/src/browser/pageMayExitObservable.ts", "../../../node_modules/@datadog/browser-core/src/transport/batch.ts", "../../../node_modules/@datadog/browser-core/src/transport/flushController.ts", "../../../node_modules/@datadog/browser-core/src/transport/startBatchWithReplica.ts", "../../../node_modules/@datadog/browser-core/src/transport/index.ts", "../../../node_modules/@datadog/browser-core/src/tools/encoder.ts", "../../../node_modules/@datadog/browser-core/src/tools/abstractLifeCycle.ts", "../../../node_modules/@datadog/browser-core/src/domain/eventRateLimiter/createEventRateLimiter.ts", "../../../node_modules/@datadog/browser-core/src/browser/runOnReadyState.ts", "../../../node_modules/@datadog/browser-core/src/browser/xhrObservable.ts", "../../../node_modules/@datadog/browser-core/src/browser/fetchObservable.ts", "../../../node_modules/@datadog/browser-core/src/tools/requestIdleCallback.ts", "../../../node_modules/@datadog/browser-core/src/tools/taskQueue.ts", "../../../node_modules/@datadog/browser-core/src/domain/console/consoleObservable.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/contextUtils.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/contextManager.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/storeContextManager.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/customerDataTracker.ts", "../../../node_modules/@datadog/browser-core/src/tools/readBytesFromStream.ts", "../../../node_modules/@datadog/browser-core/src/domain/synthetics/syntheticsWorkerValues.ts", "../../../node_modules/@datadog/browser-core/esm/domain/resourceUtils.js", "../../../node_modules/@datadog/browser-core/src/tools/matchOption.ts", "../../../node_modules/@datadog/browser-core/esm/domain/deflate/deflate.types.js", "../../../node_modules/@datadog/browser-core/src/domain/deflate/index.ts", "../../../node_modules/@datadog/browser-core/src/index.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/commonContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/vital/vitalCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/plugins.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/tracing/identifier.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/tracing/sampler.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/tracing/encodedContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/tracing/tracer.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/configuration/configuration.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/configuration/remoteConfiguration.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/configuration/index.ts", "../../../node_modules/@datadog/browser-rum-core/src/boot/preStartRum.ts", "../../../node_modules/@datadog/browser-rum-core/src/boot/rumPublicApi.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/domMutationObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/windowOpenObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/limitModification.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/assembly.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/internalContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/lifeCycle.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/viewHistory.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/resource/resourceUtils.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/requestCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/discardNegativeDuration.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/trackEventCounts.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/firstInputPolyfill.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/performanceObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/waitPageActivityEnd.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/htmlDomUtils.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/privacy.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/getActionNameFromElement.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/getSelectorFromElement.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/clickChain.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/listenActionEvents.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/computeFrustration.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/interactionSelectorCache.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/trackClickActions.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/action/actionCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/error/trackConsoleError.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/error/trackReportError.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/error/errorCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/resource/matchRequestResourceEntry.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/tracing/getDocumentTraceId.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/performanceUtils.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/resource/retrieveInitialDocumentResourceTiming.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/resource/resourceCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/trackViewEventCounts.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackFirstContentfulPaint.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackFirstInput.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackNavigationTimings.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackFirstHidden.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackInitialViewMetrics.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackCumulativeLayoutShift.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/interactionCountPolyfill.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackInteractionToNextPaint.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackLoadingTime.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/scroll.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/viewportObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackScrollMetrics.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewMetrics/trackCommonViewMetrics.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/trackViews.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/view/viewCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/rumSessionManager.ts", "../../../node_modules/@datadog/browser-rum-core/src/transport/startRumBatch.ts", "../../../node_modules/@datadog/browser-rum-core/src/transport/startRumEventBridge.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/urlContexts.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/locationChangeObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/featureFlagContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/startCustomerDataTelemetry.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/pageStateHistory.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/displayContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/browser/cookieObservable.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/ciVisibilityContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/longAnimationFrame/longAnimationFrameCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/longTask/longTaskCollection.ts", "../../../node_modules/@datadog/browser-rum-core/src/hooks.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/contexts/syntheticsContext.ts", "../../../node_modules/@datadog/browser-rum-core/src/boot/startRum.ts", "../../../node_modules/@datadog/browser-rum-core/src/domain/getSessionReplayUrl.ts", "../../../node_modules/@datadog/browser-rum-core/src/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/replayStats.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializationUtils.ts", "../../../node_modules/@datadog/browser-rum/src/types/sessionReplayConstants.ts", "../../../node_modules/@datadog/browser-rum/src/types/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializeStyleSheets.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializeAttribute.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializeAttributes.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializeNode.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/serializeDocument.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/serialization/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/eventsUtils.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/viewports.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/assembly.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackMove.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackMouseInteraction.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackScroll.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackViewportResize.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackMediaInteraction.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackStyleSheet.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackFocus.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackFrustration.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackViewEnd.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackInput.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/mutationBatch.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/trackMutation.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/trackers/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/elementsScrollPositions.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/shadowRootsController.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/startFullSnapshots.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/recordIds.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/record.ts", "../../../node_modules/@datadog/browser-rum/src/domain/record/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/segmentCollection/buildReplayPayload.ts", "../../../node_modules/@datadog/browser-rum/src/domain/segmentCollection/segment.ts", "../../../node_modules/@datadog/browser-rum/src/domain/segmentCollection/segmentCollection.ts", "../../../node_modules/@datadog/browser-rum/src/domain/segmentCollection/index.ts", "../../../node_modules/@datadog/browser-rum/src/domain/startRecordBridge.ts", "../../../node_modules/@datadog/browser-rum/src/boot/startRecording.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/utils/getNumberOfSamples.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/utils/longTaskRegistry.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/profilingCorrelation.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/transport/transport.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/profiler.ts", "../../../node_modules/crypt/crypt.js", "../../../node_modules/charenc/charenc.js", "../../../node_modules/sha1/sha1.js", "../../../node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/snakeize.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable_stream_source_element.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/fetch_requests.js", "../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/index.js", "../../../node_modules/@hotwired/stimulus/dist/stimulus.js", "../../javascript/controllers/application.js", "../../javascript/helpers/tailwind_theme.js", "../../../node_modules/@rails/request.js/src/fetch_response.js", "../../../node_modules/@rails/request.js/src/request_interceptor.js", "../../../node_modules/@rails/request.js/src/lib/utils.js", "../../../node_modules/@rails/request.js/src/fetch_request.js", "../../javascript/helpers/annotations_helper.js", "../../javascript/helpers/highcharts_helper.js", "../../javascript/controllers/base_graph_controller.js", "../../javascript/helpers/tooltip_helper.js", "../../javascript/helpers/copilot_helper.js", "../../javascript/controllers/acceptance_rate_by_language_controller.js", "../../javascript/controllers/account_controller.js", "../../javascript/controllers/active_code_time_graph_controller.js", "../../javascript/controllers/active_contributor_history_graph_controller.js", "../../javascript/controllers/anchor_controller.js", "../../javascript/controllers/annotations_controller.js", "../../javascript/helpers/analytics_helper.js", "../../javascript/controllers/application_controller.js", "../../javascript/controllers/auto_submit_form_controller.js", "../../javascript/controllers/avatar_fallback_controller.js", "../../javascript/controllers/avatar_tooltip_controller.js", "../../javascript/controllers/benchmarks_histogram_controller.js", "../../javascript/controllers/billing_controller.js", "../../javascript/controllers/bitbucket_status_controller.js", "../../javascript/controllers/breakdown_select_controller.js", "../../javascript/controllers/calendar_controller.js", "../../javascript/controllers/change_size_graph_controller.js", "../../javascript/controllers/changes_delivered_graph_controller.js", "../../javascript/controllers/character_counter_controller.js", "../../javascript/helpers/url_helper.js", "../../javascript/controllers/chart_type_toggle_controller.js", "../../javascript/controllers/clipboard_controller.js", "../../javascript/controllers/code_time_reports_graph_controller.js", "../../javascript/controllers/color_palette_controller.js", "../../../node_modules/stimulus-use/dist/index.js", "../../javascript/controllers/companies_search_controller.js", "../../javascript/controllers/company_report_features_delivered_graph_controller.js", "../../javascript/controllers/comparison_chart_controller.js", "../../javascript/controllers/comparison_show_controller.js", "../../javascript/controllers/comparison_table_controller.js", "../../javascript/controllers/comparison_view_form_controller.js", "../../javascript/controllers/content_tabs_controller.js", "../../javascript/controllers/copilot_adoption_rate_chart_controller.js", "../../javascript/controllers/copilot_comparison_chart_controller.js", "../../javascript/controllers/copilot_usage_chart_controller.js", "../../javascript/controllers/copy_controller.js", "../../javascript/controllers/cost_efficiency_chart_controller.js", "../../javascript/controllers/cost_efficiency_table_controller.js", "../../javascript/controllers/cost_per_feature_trend_chart_controller.js", "../../javascript/controllers/currency_input_controller.js", "../../javascript/controllers/dark_mode_controller.js", "../../javascript/controllers/data_page_header_filters_controller.js", "../../javascript/controllers/data_page_select_search_controller.js", "../../../node_modules/@datadog/browser-rum/src/entries/main.ts", "../../../node_modules/@datadog/browser-rum/src/boot/recorderApi.ts", "../../../node_modules/@datadog/browser-rum/src/domain/deflate/deflateEncoder.ts", "../../../node_modules/@datadog/browser-rum/src/domain/deflate/deflateWorker.ts", "../../../node_modules/@datadog/browser-rum/src/domain/scriptLoadingError.ts", "../../../node_modules/@datadog/browser-rum/src/boot/isBrowserSupported.ts", "../../../node_modules/@datadog/browser-rum/src/boot/postStartStrategy.ts", "../../../node_modules/@datadog/browser-rum/src/domain/getSessionReplayLink.ts", "../../../node_modules/@datadog/browser-rum/src/boot/preStartStrategy.ts", "../../../node_modules/@datadog/browser-rum/src/boot/lazyLoadRecorder.ts", "../../../node_modules/@datadog/browser-rum/src/boot/profilerApi.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/profilingSupported.ts", "../../../node_modules/@datadog/browser-rum/src/domain/profiling/lazyLoadProfiler.ts", "../../javascript/controllers/datadog_controller.js", "../../../node_modules/@datadog/browser-logs/src/entries/main.ts", "../../../node_modules/@datadog/browser-logs/src/boot/logsPublicApi.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger/isAuthorized.ts", "../../../node_modules/@datadog/browser-logs/src/domain/createErrorFieldFromRawError.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/commonContext.ts", "../../../node_modules/@datadog/browser-logs/src/boot/preStartLogs.ts", "../../../node_modules/@datadog/browser-logs/src/domain/configuration.ts", "../../../node_modules/@datadog/browser-logs/src/boot/startLogs.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logsSessionManager.ts", "../../../node_modules/@datadog/browser-logs/src/domain/assembly.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/rumInternalContext.ts", "../../../node_modules/@datadog/browser-logs/src/domain/console/consoleCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/report/reportCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/networkError/networkErrorCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/runtimeError/runtimeErrorCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/lifeCycle.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger/loggerCollection.ts", "../../../node_modules/@datadog/browser-logs/src/transport/startLogsBatch.ts", "../../../node_modules/@datadog/browser-logs/src/transport/startLogsBridge.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/internalContext.ts", "../../../node_modules/@datadog/browser-logs/src/domain/reportError.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logsTelemetry.ts", "../../javascript/controllers/datadog_logs_controller.js", "../../javascript/controllers/date_range_controller.js", "../../javascript/controllers/date_select_controller.js", "../../javascript/controllers/debounced_search_controller.js", "../../javascript/helpers/axis_helper.js", "../../javascript/controllers/deployment_frequency_graph_controller.js", "../../javascript/controllers/deployment_ranked_bar_graph_controller.js", "../../javascript/controllers/deployment_success_rate_graph_controller.js", "../../javascript/helpers/fill_color_helper.js", "../../javascript/controllers/deployment_trend_chart_controller.js", "../../javascript/controllers/dropdown_search_controller.js", "../../javascript/controllers/editable_form_controller.js", "../../javascript/controllers/email_field_replicator_controller.js", "../../javascript/controllers/epics_bar_chart_controller.js", "../../javascript/controllers/epics_navigation_controller.js", "../../javascript/controllers/feature_ratio_chart_controller.js", "../../javascript/controllers/flash_controller.js", "../../javascript/controllers/flyout_controller.js", "../../javascript/controllers/form_submit_controller.js", "../../javascript/controllers/git_connect_controller.js", "../../javascript/controllers/git_user_group_controller.js", "../../javascript/controllers/github_status_controller.js", "../../javascript/controllers/gitlab_status_controller.js", "../../javascript/controllers/glossary_controller.js", "../../javascript/controllers/graph_legend_controller.js", "../../javascript/controllers/group_report_features_delivered_graph_controller.js", "../../javascript/controllers/individual_pull_requests_delivered_graph_controller.js", "../../javascript/controllers/install_calendar_controller.js", "../../javascript/helpers/current_period_plot_band_helper.js", "../../javascript/helpers/kpi_graph_helper.js", "../../javascript/controllers/kpi_contributors_graph_controller.js", "../../javascript/controllers/kpi_custom_date_range_picker_controller.js", "../../javascript/controllers/kpi_deployment_cost_graph_controller.js", "../../javascript/controllers/kpi_report_deployment_frequency_graph_controller.js", "../../javascript/controllers/kpi_report_development_cost_graph_controller.js", "../../javascript/controllers/kpi_features_graph_controller.js", "../../javascript/helpers/benchmark_plot_line_helper.js", "../../javascript/controllers/kpi_lead_time_graph_controller.js", "../../javascript/controllers/kpi_report_features_delivered_graph_controller.js", "../../javascript/controllers/kpi_rework_graph_controller.js", "../../javascript/controllers/kpi_trend_graph_controller.js", "../../javascript/controllers/labor_cost_edit_form_controller.js", "../../javascript/controllers/labor_cost_inline_form_controller.js", "../../javascript/controllers/lead_time_breakdown_by_stage_chart_controller.js", "../../javascript/controllers/lead_time_breakdown_chart_controller.js", "../../javascript/controllers/lead_time_pull_requests_chart_controller.js", "../../javascript/controllers/lead_time_table_select_controller.js", "../../javascript/controllers/leading_indicators_lead_time_graph_controller.js", "../../javascript/helpers/benchmark_plot_band_helper.js", "../../javascript/controllers/leading_indicators_metric_graph_controller.js", "../../javascript/controllers/leading_indicators_pull_requests_by_stage_graph_controller.js", "../../javascript/helpers/leading_indicators_graph_helper.js", "../../javascript/controllers/leading_indicators_trend_graph_controller.js", "../../javascript/controllers/main_navigation_controller.js", "../../javascript/controllers/manage_org_plan_controller.js", "../../javascript/controllers/meeting_vs_code_graph_controller.js", "../../javascript/controllers/modal_controller.js", "../../javascript/controllers/modern_text_input_field_controller.js", "../../javascript/controllers/multi_group_select_controller.js", "../../javascript/controllers/notification_channel_load_channels_controller.js", "../../javascript/controllers/option_button_controller.js", "../../javascript/controllers/organization_controller.js", "../../javascript/controllers/organization_selector_controller.js", "../../javascript/controllers/persisted_tabs_controller.js", "../../javascript/controllers/plugin_oauth_controller.js", "../../javascript/controllers/portfolio_cost_per_engineer_controller.js", "../../javascript/controllers/portfolio_filters_form_controller.js", "../../javascript/controllers/portfolio_org_name_form_controller.js", "../../javascript/controllers/portfolio_organization_search_controller.js", "../../javascript/controllers/portfolio_organization_trend_chart_controller.js", "../../javascript/controllers/portfolio_organization_trend_step_chart_controller.js", "../../javascript/controllers/portfolio_overview_chart_controller.js", "../../javascript/controllers/portfolio_overview_trend_chart_controller.js", "../../javascript/controllers/portfolio_set_privacy_controller.js", "../../javascript/controllers/portfolio_table_hover_controller.js", "../../javascript/controllers/productivity_at_scale_controller.js", "../../javascript/controllers/productivity_to_scale_graph_controller.js", "../../javascript/controllers/ready_to_deploy_column_charts_controller.js", "../../javascript/controllers/ready_to_deploy_controller.js", "../../javascript/controllers/related_metrics_column_chart_controller.js", "../../javascript/controllers/repo_config_flyout_controller.js", "../../javascript/controllers/reports_controller.js", "../../javascript/controllers/reset_form_controller.js", "../../javascript/controllers/review_cycles_graph_controller.js", "../../../node_modules/modern-screenshot/dist/index.mjs", "../../javascript/controllers/screen_grab_controller.js", "../../javascript/controllers/scroll_to_controller.js", "../../javascript/controllers/selector_controller.js", "../../javascript/controllers/slack_channel_reload_controller.js", "../../javascript/controllers/slack_onboard_controller.js", "../../../node_modules/tslib/tslib.es6.mjs", "../../../node_modules/uuid/dist/esm-browser/stringify.js", "../../../node_modules/uuid/dist/esm-browser/rng.js", "../../../node_modules/uuid/dist/esm-browser/native.js", "../../../node_modules/uuid/dist/esm-browser/v4.js", "../../../node_modules/@snowplow/tracker-core/src/base64.ts", "../../../node_modules/@snowplow/tracker-core/src/payload.ts", "../../../node_modules/@snowplow/tracker-core/src/logger.ts", "../../../node_modules/@snowplow/tracker-core/src/contexts.ts", "../../../node_modules/@snowplow/tracker-core/src/event_store_payload.ts", "../../../node_modules/@snowplow/tracker-core/src/core.ts", "../../../node_modules/@snowplow/tracker-core/src/event_store.ts", "../../../node_modules/@snowplow/tracker-core/src/schemata.ts", "../../../node_modules/@snowplow/tracker-core/src/emitter/emitter_request.ts", "../../../node_modules/@snowplow/tracker-core/src/emitter/emitter_event.ts", "../../../node_modules/@snowplow/tracker-core/src/emitter/index.ts", "../../../node_modules/@snowplow/tracker-core/src/index.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/helpers/storage.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/helpers/cross_domain.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/helpers/index.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/local_storage_event_store.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/out_queue.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/proxies.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/id_cookie.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/schemata.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/helpers/browser_props.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/cookie_storage.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/tracker/index.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/snowplow.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/detectors.ts", "../../../node_modules/@snowplow/browser-tracker-core/src/state.ts", "../../../node_modules/@snowplow/browser-tracker/src/api.ts", "../../../node_modules/@snowplow/browser-tracker/src/index.ts", "../../javascript/controllers/snowplow_tracking_controller.js", "../../javascript/controllers/url_history_controller.js", "../../javascript/controllers/sql_like_tag_matcher_controller.js", "../../javascript/controllers/stale_data_controller.js", "../../javascript/controllers/startend_controller.js", "../../javascript/controllers/strategic_event_comparison_chart_controller.js", "../../javascript/controllers/tab_toggle_controller.js", "../../javascript/controllers/table_controller.js", "../../javascript/controllers/table_search_controller.js", "../../javascript/controllers/table_sort_controller.js", "../../javascript/controllers/team_code_change_rate_graph_controller.js", "../../javascript/controllers/team_code_review_graph_controller.js", "../../javascript/controllers/team_lead_time_graph_controller.js", "../../javascript/controllers/toggle_inline_loading_controller.js", "../../javascript/controllers/toggle_layout_column_controller.js", "../../javascript/controllers/toggle_visibility_controller.js", "../../javascript/controllers/turbo_false_button_disable_controller.js", "../../javascript/controllers/turbo_graph_error_handler_controller.js", "../../javascript/controllers/upgrade_to_business_plan_modal_controller.js", "../../javascript/controllers/user_role_select_controller.js", "../../javascript/controllers/work_hours_row_controller.js", "../../javascript/controllers/working_agreement_graph_controller.js", "../../javascript/controllers/index.js", "../../../node_modules/@rails/actioncable/app/assets/javascripts/actioncable.esm.js", "../../javascript/channels/consumer.js", "../../javascript/channels/portfolio_user_channel.js", "../../javascript/channels/user_channel.js", "../../javascript/helpers/turbo_confirm_dialog.js", "../../javascript/helpers/turbo_frame.js"], "sourcesContent": ["export default {\n logger: typeof console !== \"undefined\" ? console : undefined,\n WebSocket: typeof WebSocket !== \"undefined\" ? WebSocket : undefined,\n}\n", "import adapters from \"./adapters\"\n\n// The logger is disabled by default. You can enable it with:\n//\n// ActionCable.logger.enabled = true\n//\n// Example:\n//\n// import * as ActionCable from '@rails/actioncable'\n//\n// ActionCable.logger.enabled = true\n// ActionCable.logger.log('Connection Established.')\n//\n\nexport default {\n log(...messages) {\n if (this.enabled) {\n messages.push(Date.now())\n adapters.logger.log(\"[ActionCable]\", ...messages)\n }\n },\n}\n", "import logger from \"./logger\"\n\n// Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting\n// revival reconnections if things go astray. Internal class, not intended for direct user manipulation.\n\nconst now = () => new Date().getTime()\n\nconst secondsSince = time => (now() - time) / 1000\n\nclass ConnectionMonitor {\n constructor(connection) {\n this.visibilityDidChange = this.visibilityDidChange.bind(this)\n this.connection = connection\n this.reconnectAttempts = 0\n }\n\n start() {\n if (!this.isRunning()) {\n this.startedAt = now()\n delete this.stoppedAt\n this.startPolling()\n addEventListener(\"visibilitychange\", this.visibilityDidChange)\n logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`)\n }\n }\n\n stop() {\n if (this.isRunning()) {\n this.stoppedAt = now()\n this.stopPolling()\n removeEventListener(\"visibilitychange\", this.visibilityDidChange)\n logger.log(\"ConnectionMonitor stopped\")\n }\n }\n\n isRunning() {\n return this.startedAt && !this.stoppedAt\n }\n\n recordMessage() {\n this.pingedAt = now()\n }\n\n recordConnect() {\n this.reconnectAttempts = 0\n delete this.disconnectedAt\n logger.log(\"ConnectionMonitor recorded connect\")\n }\n\n recordDisconnect() {\n this.disconnectedAt = now()\n logger.log(\"ConnectionMonitor recorded disconnect\")\n }\n\n // Private\n\n startPolling() {\n this.stopPolling()\n this.poll()\n }\n\n stopPolling() {\n clearTimeout(this.pollTimeout)\n }\n\n poll() {\n this.pollTimeout = setTimeout(() => {\n this.reconnectIfStale()\n this.poll()\n }\n , this.getPollInterval())\n }\n\n getPollInterval() {\n const { staleThreshold, reconnectionBackoffRate } = this.constructor\n const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10))\n const jitterMax = this.reconnectAttempts === 0 ? 1.0 : reconnectionBackoffRate\n const jitter = jitterMax * Math.random()\n return staleThreshold * 1000 * backoff * (1 + jitter)\n }\n\n reconnectIfStale() {\n if (this.connectionIsStale()) {\n logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`)\n this.reconnectAttempts++\n if (this.disconnectedRecently()) {\n logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`)\n } else {\n logger.log(\"ConnectionMonitor reopening\")\n this.connection.reopen()\n }\n }\n }\n\n get refreshedAt() {\n return this.pingedAt ? this.pingedAt : this.startedAt\n }\n\n connectionIsStale() {\n return secondsSince(this.refreshedAt) > this.constructor.staleThreshold\n }\n\n disconnectedRecently() {\n return this.disconnectedAt && (secondsSince(this.disconnectedAt) < this.constructor.staleThreshold)\n }\n\n visibilityDidChange() {\n if (document.visibilityState === \"visible\") {\n setTimeout(() => {\n if (this.connectionIsStale() || !this.connection.isOpen()) {\n logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`)\n this.connection.reopen()\n }\n }\n , 200)\n }\n }\n\n}\n\nConnectionMonitor.staleThreshold = 6 // Server::Connections::BEAT_INTERVAL * 2 (missed two pings)\nConnectionMonitor.reconnectionBackoffRate = 0.15\n\nexport default ConnectionMonitor\n", "export default {\n \"message_types\": {\n \"welcome\": \"welcome\",\n \"disconnect\": \"disconnect\",\n \"ping\": \"ping\",\n \"confirmation\": \"confirm_subscription\",\n \"rejection\": \"reject_subscription\"\n },\n \"disconnect_reasons\": {\n \"unauthorized\": \"unauthorized\",\n \"invalid_request\": \"invalid_request\",\n \"server_restart\": \"server_restart\",\n \"remote\": \"remote\"\n },\n \"default_mount_path\": \"/cable\",\n \"protocols\": [\n \"actioncable-v1-json\",\n \"actioncable-unsupported\"\n ]\n}\n", "import adapters from \"./adapters\"\nimport ConnectionMonitor from \"./connection_monitor\"\nimport INTERNAL from \"./internal\"\nimport logger from \"./logger\"\n\n// Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.\n\nconst {message_types, protocols} = INTERNAL\nconst supportedProtocols = protocols.slice(0, protocols.length - 1)\n\nconst indexOf = [].indexOf\n\nclass Connection {\n constructor(consumer) {\n this.open = this.open.bind(this)\n this.consumer = consumer\n this.subscriptions = this.consumer.subscriptions\n this.monitor = new ConnectionMonitor(this)\n this.disconnected = true\n }\n\n send(data) {\n if (this.isOpen()) {\n this.webSocket.send(JSON.stringify(data))\n return true\n } else {\n return false\n }\n }\n\n open() {\n if (this.isActive()) {\n logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`)\n return false\n } else {\n const socketProtocols = [...protocols, ...this.consumer.subprotocols || []]\n logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`)\n if (this.webSocket) { this.uninstallEventHandlers() }\n this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols)\n this.installEventHandlers()\n this.monitor.start()\n return true\n }\n }\n\n close({allowReconnect} = {allowReconnect: true}) {\n if (!allowReconnect) { this.monitor.stop() }\n // Avoid closing websockets in a \"connecting\" state due to Safari 15.1+ bug. See: https://github.com/rails/rails/issues/43835#issuecomment-1002288478\n if (this.isOpen()) {\n return this.webSocket.close()\n }\n }\n\n reopen() {\n logger.log(`Reopening WebSocket, current state is ${this.getState()}`)\n if (this.isActive()) {\n try {\n return this.close()\n } catch (error) {\n logger.log(\"Failed to reopen WebSocket\", error)\n }\n finally {\n logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)\n setTimeout(this.open, this.constructor.reopenDelay)\n }\n } else {\n return this.open()\n }\n }\n\n getProtocol() {\n if (this.webSocket) {\n return this.webSocket.protocol\n }\n }\n\n isOpen() {\n return this.isState(\"open\")\n }\n\n isActive() {\n return this.isState(\"open\", \"connecting\")\n }\n\n triedToReconnect() {\n return this.monitor.reconnectAttempts > 0\n }\n\n // Private\n\n isProtocolSupported() {\n return indexOf.call(supportedProtocols, this.getProtocol()) >= 0\n }\n\n isState(...states) {\n return indexOf.call(states, this.getState()) >= 0\n }\n\n getState() {\n if (this.webSocket) {\n for (let state in adapters.WebSocket) {\n if (adapters.WebSocket[state] === this.webSocket.readyState) {\n return state.toLowerCase()\n }\n }\n }\n return null\n }\n\n installEventHandlers() {\n for (let eventName in this.events) {\n const handler = this.events[eventName].bind(this)\n this.webSocket[`on${eventName}`] = handler\n }\n }\n\n uninstallEventHandlers() {\n for (let eventName in this.events) {\n this.webSocket[`on${eventName}`] = function() {}\n }\n }\n\n}\n\nConnection.reopenDelay = 500\n\nConnection.prototype.events = {\n message(event) {\n if (!this.isProtocolSupported()) { return }\n const {identifier, message, reason, reconnect, type} = JSON.parse(event.data)\n this.monitor.recordMessage()\n switch (type) {\n case message_types.welcome:\n if (this.triedToReconnect()) {\n this.reconnectAttempted = true\n }\n this.monitor.recordConnect()\n return this.subscriptions.reload()\n case message_types.disconnect:\n logger.log(`Disconnecting. Reason: ${reason}`)\n return this.close({allowReconnect: reconnect})\n case message_types.ping:\n return null\n case message_types.confirmation:\n this.subscriptions.confirmSubscription(identifier)\n if (this.reconnectAttempted) {\n this.reconnectAttempted = false\n return this.subscriptions.notify(identifier, \"connected\", {reconnected: true})\n } else {\n return this.subscriptions.notify(identifier, \"connected\", {reconnected: false})\n }\n case message_types.rejection:\n return this.subscriptions.reject(identifier)\n default:\n return this.subscriptions.notify(identifier, \"received\", message)\n }\n },\n\n open() {\n logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)\n this.disconnected = false\n if (!this.isProtocolSupported()) {\n logger.log(\"Protocol is unsupported. Stopping monitor and disconnecting.\")\n return this.close({allowReconnect: false})\n }\n },\n\n close(event) {\n logger.log(\"WebSocket onclose event\")\n if (this.disconnected) { return }\n this.disconnected = true\n this.monitor.recordDisconnect()\n return this.subscriptions.notifyAll(\"disconnected\", {willAttemptReconnect: this.monitor.isRunning()})\n },\n\n error() {\n logger.log(\"WebSocket onerror event\")\n }\n}\n\nexport default Connection\n", "// A new subscription is created through the ActionCable.Subscriptions instance available on the consumer.\n// It provides a number of callbacks and a method for calling remote procedure calls on the corresponding\n// Channel instance on the server side.\n//\n// An example demonstrates the basic functionality:\n//\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\", {\n// connected() {\n// // Called once the subscription has been successfully completed\n// },\n//\n// disconnected({ willAttemptReconnect: boolean }) {\n// // Called when the client has disconnected with the server.\n// // The object will have an `willAttemptReconnect` property which\n// // says whether the client has the intention of attempting\n// // to reconnect.\n// },\n//\n// appear() {\n// this.perform('appear', {appearing_on: this.appearingOn()})\n// },\n//\n// away() {\n// this.perform('away')\n// },\n//\n// appearingOn() {\n// $('main').data('appearing-on')\n// }\n// })\n//\n// The methods #appear and #away forward their intent to the remote AppearanceChannel instance on the server\n// by calling the `perform` method with the first parameter being the action (which maps to AppearanceChannel#appear/away).\n// The second parameter is a hash that'll get JSON encoded and made available on the server in the data parameter.\n//\n// This is how the server component would look:\n//\n// class AppearanceChannel < ApplicationActionCable::Channel\n// def subscribed\n// current_user.appear\n// end\n//\n// def unsubscribed\n// current_user.disappear\n// end\n//\n// def appear(data)\n// current_user.appear on: data['appearing_on']\n// end\n//\n// def away\n// current_user.away\n// end\n// end\n//\n// The \"AppearanceChannel\" name is automatically mapped between the client-side subscription creation and the server-side Ruby class name.\n// The AppearanceChannel#appear/away public methods are exposed automatically to client-side invocation through the perform method.\n\nconst extend = function(object, properties) {\n if (properties != null) {\n for (let key in properties) {\n const value = properties[key]\n object[key] = value\n }\n }\n return object\n}\n\nexport default class Subscription {\n constructor(consumer, params = {}, mixin) {\n this.consumer = consumer\n this.identifier = JSON.stringify(params)\n extend(this, mixin)\n }\n\n // Perform a channel action with the optional data passed as an attribute\n perform(action, data = {}) {\n data.action = action\n return this.send(data)\n }\n\n send(data) {\n return this.consumer.send({command: \"message\", identifier: this.identifier, data: JSON.stringify(data)})\n }\n\n unsubscribe() {\n return this.consumer.subscriptions.remove(this)\n }\n}\n", "import logger from \"./logger\"\n\n// Responsible for ensuring channel subscribe command is confirmed, retrying until confirmation is received.\n// Internal class, not intended for direct user manipulation.\n\nclass SubscriptionGuarantor {\n constructor(subscriptions) {\n this.subscriptions = subscriptions\n this.pendingSubscriptions = []\n }\n\n guarantee(subscription) {\n if(this.pendingSubscriptions.indexOf(subscription) == -1){ \n logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`)\n this.pendingSubscriptions.push(subscription) \n }\n else {\n logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`)\n }\n this.startGuaranteeing()\n }\n\n forget(subscription) {\n logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`)\n this.pendingSubscriptions = (this.pendingSubscriptions.filter((s) => s !== subscription))\n }\n\n startGuaranteeing() {\n this.stopGuaranteeing()\n this.retrySubscribing()\n }\n \n stopGuaranteeing() {\n clearTimeout(this.retryTimeout)\n }\n\n retrySubscribing() {\n this.retryTimeout = setTimeout(() => {\n if (this.subscriptions && typeof(this.subscriptions.subscribe) === \"function\") {\n this.pendingSubscriptions.map((subscription) => {\n logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`)\n this.subscriptions.subscribe(subscription)\n })\n }\n }\n , 500)\n }\n}\n\nexport default SubscriptionGuarantor", "import Subscription from \"./subscription\"\nimport SubscriptionGuarantor from \"./subscription_guarantor\"\nimport logger from \"./logger\"\n\n// Collection class for creating (and internally managing) channel subscriptions.\n// The only method intended to be triggered by the user is ActionCable.Subscriptions#create,\n// and it should be called through the consumer like so:\n//\n// App = {}\n// App.cable = ActionCable.createConsumer(\"ws://example.com/accounts/1\")\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\")\n//\n// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.\n\nexport default class Subscriptions {\n constructor(consumer) {\n this.consumer = consumer\n this.guarantor = new SubscriptionGuarantor(this)\n this.subscriptions = []\n }\n\n create(channelName, mixin) {\n const channel = channelName\n const params = typeof channel === \"object\" ? channel : {channel}\n const subscription = new Subscription(this.consumer, params, mixin)\n return this.add(subscription)\n }\n\n // Private\n\n add(subscription) {\n this.subscriptions.push(subscription)\n this.consumer.ensureActiveConnection()\n this.notify(subscription, \"initialized\")\n this.subscribe(subscription)\n return subscription\n }\n\n remove(subscription) {\n this.forget(subscription)\n if (!this.findAll(subscription.identifier).length) {\n this.sendCommand(subscription, \"unsubscribe\")\n }\n return subscription\n }\n\n reject(identifier) {\n return this.findAll(identifier).map((subscription) => {\n this.forget(subscription)\n this.notify(subscription, \"rejected\")\n return subscription\n })\n }\n\n forget(subscription) {\n this.guarantor.forget(subscription)\n this.subscriptions = (this.subscriptions.filter((s) => s !== subscription))\n return subscription\n }\n\n findAll(identifier) {\n return this.subscriptions.filter((s) => s.identifier === identifier)\n }\n\n reload() {\n return this.subscriptions.map((subscription) =>\n this.subscribe(subscription))\n }\n\n notifyAll(callbackName, ...args) {\n return this.subscriptions.map((subscription) =>\n this.notify(subscription, callbackName, ...args))\n }\n\n notify(subscription, callbackName, ...args) {\n let subscriptions\n if (typeof subscription === \"string\") {\n subscriptions = this.findAll(subscription)\n } else {\n subscriptions = [subscription]\n }\n\n return subscriptions.map((subscription) =>\n (typeof subscription[callbackName] === \"function\" ? subscription[callbackName](...args) : undefined))\n }\n\n subscribe(subscription) {\n if (this.sendCommand(subscription, \"subscribe\")) {\n this.guarantor.guarantee(subscription)\n }\n }\n\n confirmSubscription(identifier) {\n logger.log(`Subscription confirmed ${identifier}`)\n this.findAll(identifier).map((subscription) =>\n this.guarantor.forget(subscription))\n }\n\n sendCommand(subscription, command) {\n const {identifier} = subscription\n return this.consumer.send({command, identifier})\n }\n}\n", "import Connection from \"./connection\"\nimport Subscriptions from \"./subscriptions\"\n\n// The ActionCable.Consumer establishes the connection to a server-side Ruby Connection object. Once established,\n// the ActionCable.ConnectionMonitor will ensure that its properly maintained through heartbeats and checking for stale updates.\n// The Consumer instance is also the gateway to establishing subscriptions to desired channels through the #createSubscription\n// method.\n//\n// The following example shows how this can be set up:\n//\n// App = {}\n// App.cable = ActionCable.createConsumer(\"ws://example.com/accounts/1\")\n// App.appearance = App.cable.subscriptions.create(\"AppearanceChannel\")\n//\n// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.\n//\n// When a consumer is created, it automatically connects with the server.\n//\n// To disconnect from the server, call\n//\n// App.cable.disconnect()\n//\n// and to restart the connection:\n//\n// App.cable.connect()\n//\n// Any channel subscriptions which existed prior to disconnecting will\n// automatically resubscribe.\n\nexport default class Consumer {\n constructor(url) {\n this._url = url\n this.subscriptions = new Subscriptions(this)\n this.connection = new Connection(this)\n this.subprotocols = []\n }\n\n get url() {\n return createWebSocketURL(this._url)\n }\n\n send(data) {\n return this.connection.send(data)\n }\n\n connect() {\n return this.connection.open()\n }\n\n disconnect() {\n return this.connection.close({allowReconnect: false})\n }\n\n ensureActiveConnection() {\n if (!this.connection.isActive()) {\n return this.connection.open()\n }\n }\n\n addSubProtocol(subprotocol) {\n this.subprotocols = [...this.subprotocols, subprotocol]\n }\n}\n\nexport function createWebSocketURL(url) {\n if (typeof url === \"function\") {\n url = url()\n }\n\n if (url && !/^wss?:/i.test(url)) {\n const a = document.createElement(\"a\")\n a.href = url\n // Fix populating Location properties in IE. Otherwise, protocol will be blank.\n a.href = a.href\n a.protocol = a.protocol.replace(\"http\", \"ws\")\n return a.href\n } else {\n return url\n }\n}\n", "import Connection from \"./connection\"\nimport ConnectionMonitor from \"./connection_monitor\"\nimport Consumer, { createWebSocketURL } from \"./consumer\"\nimport INTERNAL from \"./internal\"\nimport Subscription from \"./subscription\"\nimport Subscriptions from \"./subscriptions\"\nimport SubscriptionGuarantor from \"./subscription_guarantor\"\nimport adapters from \"./adapters\"\nimport logger from \"./logger\"\n\nexport {\n Connection,\n ConnectionMonitor,\n Consumer,\n INTERNAL,\n Subscription,\n Subscriptions,\n SubscriptionGuarantor,\n adapters,\n createWebSocketURL,\n logger,\n}\n\nexport function createConsumer(url = getConfig(\"url\") || INTERNAL.default_mount_path) {\n return new Consumer(url)\n}\n\nexport function getConfig(name) {\n const element = document.head.querySelector(`meta[name='action-cable-${name}']`)\n if (element) {\n return element.getAttribute(\"content\")\n }\n}\n", "var x=String;\nvar create=function() {return {isColorSupported:false,reset:x,bold:x,dim:x,italic:x,underline:x,inverse:x,hidden:x,strikethrough:x,black:x,red:x,green:x,yellow:x,blue:x,magenta:x,cyan:x,white:x,gray:x,bgBlack:x,bgRed:x,bgGreen:x,bgYellow:x,bgBlue:x,bgMagenta:x,bgCyan:x,bgWhite:x,blackBright:x,redBright:x,greenBright:x,yellowBright:x,blueBright:x,magentaBright:x,cyanBright:x,whiteBright:x,bgBlackBright:x,bgRedBright:x,bgGreenBright:x,bgYellowBright:x,bgBlueBright:x,bgMagentaBright:x,bgCyanBright:x,bgWhiteBright:x}};\nmodule.exports=create();\nmodule.exports.createColors = create;\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nfunction _export(target, all) {\n for(var name in all)Object.defineProperty(target, name, {\n enumerable: true,\n get: all[name]\n });\n}\n_export(exports, {\n dim: function() {\n return dim;\n },\n default: function() {\n return _default;\n }\n});\nconst _picocolors = /*#__PURE__*/ _interop_require_default(require(\"picocolors\"));\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : {\n default: obj\n };\n}\nlet alreadyShown = new Set();\nfunction log(type, messages, key) {\n if (typeof process !== \"undefined\" && process.env.JEST_WORKER_ID) return;\n if (key && alreadyShown.has(key)) return;\n if (key) alreadyShown.add(key);\n console.warn(\"\");\n messages.forEach((message)=>console.warn(type, \"-\", message));\n}\nfunction dim(input) {\n return _picocolors.default.dim(input);\n}\nconst _default = {\n info (key, messages) {\n log(_picocolors.default.bold(_picocolors.default.cyan(\"info\")), ...Array.isArray(key) ? [\n key\n ] : [\n messages,\n key\n ]);\n },\n warn (key, messages) {\n log(_picocolors.default.bold(_picocolors.default.yellow(\"warn\")), ...Array.isArray(key) ? [\n key\n ] : [\n messages,\n key\n ]);\n },\n risk (key, messages) {\n log(_picocolors.default.bold(_picocolors.default.magenta(\"risk\")), ...Array.isArray(key) ? [\n key\n ] : [\n messages,\n key\n ]);\n }\n};\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"default\", {\n enumerable: true,\n get: function() {\n return _default;\n }\n});\nconst _log = /*#__PURE__*/ _interop_require_default(require(\"../util/log\"));\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : {\n default: obj\n };\n}\nfunction warn({ version , from , to }) {\n _log.default.warn(`${from}-color-renamed`, [\n `As of Tailwind CSS ${version}, \\`${from}\\` has been renamed to \\`${to}\\`.`,\n \"Update your configuration file to silence this warning.\"\n ]);\n}\nconst _default = {\n inherit: \"inherit\",\n current: \"currentColor\",\n transparent: \"transparent\",\n black: \"#000\",\n white: \"#fff\",\n slate: {\n 50: \"#f8fafc\",\n 100: \"#f1f5f9\",\n 200: \"#e2e8f0\",\n 300: \"#cbd5e1\",\n 400: \"#94a3b8\",\n 500: \"#64748b\",\n 600: \"#475569\",\n 700: \"#334155\",\n 800: \"#1e293b\",\n 900: \"#0f172a\",\n 950: \"#020617\"\n },\n gray: {\n 50: \"#f9fafb\",\n 100: \"#f3f4f6\",\n 200: \"#e5e7eb\",\n 300: \"#d1d5db\",\n 400: \"#9ca3af\",\n 500: \"#6b7280\",\n 600: \"#4b5563\",\n 700: \"#374151\",\n 800: \"#1f2937\",\n 900: \"#111827\",\n 950: \"#030712\"\n },\n zinc: {\n 50: \"#fafafa\",\n 100: \"#f4f4f5\",\n 200: \"#e4e4e7\",\n 300: \"#d4d4d8\",\n 400: \"#a1a1aa\",\n 500: \"#71717a\",\n 600: \"#52525b\",\n 700: \"#3f3f46\",\n 800: \"#27272a\",\n 900: \"#18181b\",\n 950: \"#09090b\"\n },\n neutral: {\n 50: \"#fafafa\",\n 100: \"#f5f5f5\",\n 200: \"#e5e5e5\",\n 300: \"#d4d4d4\",\n 400: \"#a3a3a3\",\n 500: \"#737373\",\n 600: \"#525252\",\n 700: \"#404040\",\n 800: \"#262626\",\n 900: \"#171717\",\n 950: \"#0a0a0a\"\n },\n stone: {\n 50: \"#fafaf9\",\n 100: \"#f5f5f4\",\n 200: \"#e7e5e4\",\n 300: \"#d6d3d1\",\n 400: \"#a8a29e\",\n 500: \"#78716c\",\n 600: \"#57534e\",\n 700: \"#44403c\",\n 800: \"#292524\",\n 900: \"#1c1917\",\n 950: \"#0c0a09\"\n },\n red: {\n 50: \"#fef2f2\",\n 100: \"#fee2e2\",\n 200: \"#fecaca\",\n 300: \"#fca5a5\",\n 400: \"#f87171\",\n 500: \"#ef4444\",\n 600: \"#dc2626\",\n 700: \"#b91c1c\",\n 800: \"#991b1b\",\n 900: \"#7f1d1d\",\n 950: \"#450a0a\"\n },\n orange: {\n 50: \"#fff7ed\",\n 100: \"#ffedd5\",\n 200: \"#fed7aa\",\n 300: \"#fdba74\",\n 400: \"#fb923c\",\n 500: \"#f97316\",\n 600: \"#ea580c\",\n 700: \"#c2410c\",\n 800: \"#9a3412\",\n 900: \"#7c2d12\",\n 950: \"#431407\"\n },\n amber: {\n 50: \"#fffbeb\",\n 100: \"#fef3c7\",\n 200: \"#fde68a\",\n 300: \"#fcd34d\",\n 400: \"#fbbf24\",\n 500: \"#f59e0b\",\n 600: \"#d97706\",\n 700: \"#b45309\",\n 800: \"#92400e\",\n 900: \"#78350f\",\n 950: \"#451a03\"\n },\n yellow: {\n 50: \"#fefce8\",\n 100: \"#fef9c3\",\n 200: \"#fef08a\",\n 300: \"#fde047\",\n 400: \"#facc15\",\n 500: \"#eab308\",\n 600: \"#ca8a04\",\n 700: \"#a16207\",\n 800: \"#854d0e\",\n 900: \"#713f12\",\n 950: \"#422006\"\n },\n lime: {\n 50: \"#f7fee7\",\n 100: \"#ecfccb\",\n 200: \"#d9f99d\",\n 300: \"#bef264\",\n 400: \"#a3e635\",\n 500: \"#84cc16\",\n 600: \"#65a30d\",\n 700: \"#4d7c0f\",\n 800: \"#3f6212\",\n 900: \"#365314\",\n 950: \"#1a2e05\"\n },\n green: {\n 50: \"#f0fdf4\",\n 100: \"#dcfce7\",\n 200: \"#bbf7d0\",\n 300: \"#86efac\",\n 400: \"#4ade80\",\n 500: \"#22c55e\",\n 600: \"#16a34a\",\n 700: \"#15803d\",\n 800: \"#166534\",\n 900: \"#14532d\",\n 950: \"#052e16\"\n },\n emerald: {\n 50: \"#ecfdf5\",\n 100: \"#d1fae5\",\n 200: \"#a7f3d0\",\n 300: \"#6ee7b7\",\n 400: \"#34d399\",\n 500: \"#10b981\",\n 600: \"#059669\",\n 700: \"#047857\",\n 800: \"#065f46\",\n 900: \"#064e3b\",\n 950: \"#022c22\"\n },\n teal: {\n 50: \"#f0fdfa\",\n 100: \"#ccfbf1\",\n 200: \"#99f6e4\",\n 300: \"#5eead4\",\n 400: \"#2dd4bf\",\n 500: \"#14b8a6\",\n 600: \"#0d9488\",\n 700: \"#0f766e\",\n 800: \"#115e59\",\n 900: \"#134e4a\",\n 950: \"#042f2e\"\n },\n cyan: {\n 50: \"#ecfeff\",\n 100: \"#cffafe\",\n 200: \"#a5f3fc\",\n 300: \"#67e8f9\",\n 400: \"#22d3ee\",\n 500: \"#06b6d4\",\n 600: \"#0891b2\",\n 700: \"#0e7490\",\n 800: \"#155e75\",\n 900: \"#164e63\",\n 950: \"#083344\"\n },\n sky: {\n 50: \"#f0f9ff\",\n 100: \"#e0f2fe\",\n 200: \"#bae6fd\",\n 300: \"#7dd3fc\",\n 400: \"#38bdf8\",\n 500: \"#0ea5e9\",\n 600: \"#0284c7\",\n 700: \"#0369a1\",\n 800: \"#075985\",\n 900: \"#0c4a6e\",\n 950: \"#082f49\"\n },\n blue: {\n 50: \"#eff6ff\",\n 100: \"#dbeafe\",\n 200: \"#bfdbfe\",\n 300: \"#93c5fd\",\n 400: \"#60a5fa\",\n 500: \"#3b82f6\",\n 600: \"#2563eb\",\n 700: \"#1d4ed8\",\n 800: \"#1e40af\",\n 900: \"#1e3a8a\",\n 950: \"#172554\"\n },\n indigo: {\n 50: \"#eef2ff\",\n 100: \"#e0e7ff\",\n 200: \"#c7d2fe\",\n 300: \"#a5b4fc\",\n 400: \"#818cf8\",\n 500: \"#6366f1\",\n 600: \"#4f46e5\",\n 700: \"#4338ca\",\n 800: \"#3730a3\",\n 900: \"#312e81\",\n 950: \"#1e1b4b\"\n },\n violet: {\n 50: \"#f5f3ff\",\n 100: \"#ede9fe\",\n 200: \"#ddd6fe\",\n 300: \"#c4b5fd\",\n 400: \"#a78bfa\",\n 500: \"#8b5cf6\",\n 600: \"#7c3aed\",\n 700: \"#6d28d9\",\n 800: \"#5b21b6\",\n 900: \"#4c1d95\",\n 950: \"#2e1065\"\n },\n purple: {\n 50: \"#faf5ff\",\n 100: \"#f3e8ff\",\n 200: \"#e9d5ff\",\n 300: \"#d8b4fe\",\n 400: \"#c084fc\",\n 500: \"#a855f7\",\n 600: \"#9333ea\",\n 700: \"#7e22ce\",\n 800: \"#6b21a8\",\n 900: \"#581c87\",\n 950: \"#3b0764\"\n },\n fuchsia: {\n 50: \"#fdf4ff\",\n 100: \"#fae8ff\",\n 200: \"#f5d0fe\",\n 300: \"#f0abfc\",\n 400: \"#e879f9\",\n 500: \"#d946ef\",\n 600: \"#c026d3\",\n 700: \"#a21caf\",\n 800: \"#86198f\",\n 900: \"#701a75\",\n 950: \"#4a044e\"\n },\n pink: {\n 50: \"#fdf2f8\",\n 100: \"#fce7f3\",\n 200: \"#fbcfe8\",\n 300: \"#f9a8d4\",\n 400: \"#f472b6\",\n 500: \"#ec4899\",\n 600: \"#db2777\",\n 700: \"#be185d\",\n 800: \"#9d174d\",\n 900: \"#831843\",\n 950: \"#500724\"\n },\n rose: {\n 50: \"#fff1f2\",\n 100: \"#ffe4e6\",\n 200: \"#fecdd3\",\n 300: \"#fda4af\",\n 400: \"#fb7185\",\n 500: \"#f43f5e\",\n 600: \"#e11d48\",\n 700: \"#be123c\",\n 800: \"#9f1239\",\n 900: \"#881337\",\n 950: \"#4c0519\"\n },\n get lightBlue () {\n warn({\n version: \"v2.2\",\n from: \"lightBlue\",\n to: \"sky\"\n });\n return this.sky;\n },\n get warmGray () {\n warn({\n version: \"v3.0\",\n from: \"warmGray\",\n to: \"stone\"\n });\n return this.stone;\n },\n get trueGray () {\n warn({\n version: \"v3.0\",\n from: \"trueGray\",\n to: \"neutral\"\n });\n return this.neutral;\n },\n get coolGray () {\n warn({\n version: \"v3.0\",\n from: \"coolGray\",\n to: \"gray\"\n });\n return this.gray;\n },\n get blueGray () {\n warn({\n version: \"v3.0\",\n from: \"blueGray\",\n to: \"slate\"\n });\n return this.slate;\n }\n};\n", "let colors = require('./lib/public/colors')\nmodule.exports = (colors.__esModule ? colors : { default: colors }).default\n", "module.exports = {\n aqua: /#00ffff(ff)?(?!\\w)|#0ff(f)?(?!\\w)/gi,\n azure: /#f0ffff(ff)?(?!\\w)/gi,\n beige: /#f5f5dc(ff)?(?!\\w)/gi,\n bisque: /#ffe4c4(ff)?(?!\\w)/gi,\n black: /#000000(ff)?(?!\\w)|#000(f)?(?!\\w)/gi,\n blue: /#0000ff(ff)?(?!\\w)|#00f(f)?(?!\\w)/gi,\n brown: /#a52a2a(ff)?(?!\\w)/gi,\n coral: /#ff7f50(ff)?(?!\\w)/gi,\n cornsilk: /#fff8dc(ff)?(?!\\w)/gi,\n crimson: /#dc143c(ff)?(?!\\w)/gi,\n cyan: /#00ffff(ff)?(?!\\w)|#0ff(f)?(?!\\w)/gi,\n darkblue: /#00008b(ff)?(?!\\w)/gi,\n darkcyan: /#008b8b(ff)?(?!\\w)/gi,\n darkgrey: /#a9a9a9(ff)?(?!\\w)/gi,\n darkred: /#8b0000(ff)?(?!\\w)/gi,\n deeppink: /#ff1493(ff)?(?!\\w)/gi,\n dimgrey: /#696969(ff)?(?!\\w)/gi,\n gold: /#ffd700(ff)?(?!\\w)/gi,\n green: /#008000(ff)?(?!\\w)/gi,\n grey: /#808080(ff)?(?!\\w)/gi,\n honeydew: /#f0fff0(ff)?(?!\\w)/gi,\n hotpink: /#ff69b4(ff)?(?!\\w)/gi,\n indigo: /#4b0082(ff)?(?!\\w)/gi,\n ivory: /#fffff0(ff)?(?!\\w)/gi,\n khaki: /#f0e68c(ff)?(?!\\w)/gi,\n lavender: /#e6e6fa(ff)?(?!\\w)/gi,\n lime: /#00ff00(ff)?(?!\\w)|#0f0(f)?(?!\\w)/gi,\n linen: /#faf0e6(ff)?(?!\\w)/gi,\n maroon: /#800000(ff)?(?!\\w)/gi,\n moccasin: /#ffe4b5(ff)?(?!\\w)/gi,\n navy: /#000080(ff)?(?!\\w)/gi,\n oldlace: /#fdf5e6(ff)?(?!\\w)/gi,\n olive: /#808000(ff)?(?!\\w)/gi,\n orange: /#ffa500(ff)?(?!\\w)/gi,\n orchid: /#da70d6(ff)?(?!\\w)/gi,\n peru: /#cd853f(ff)?(?!\\w)/gi,\n pink: /#ffc0cb(ff)?(?!\\w)/gi,\n plum: /#dda0dd(ff)?(?!\\w)/gi,\n purple: /#800080(ff)?(?!\\w)/gi,\n red: /#ff0000(ff)?(?!\\w)|#f00(f)?(?!\\w)/gi,\n salmon: /#fa8072(ff)?(?!\\w)/gi,\n seagreen: /#2e8b57(ff)?(?!\\w)/gi,\n seashell: /#fff5ee(ff)?(?!\\w)/gi,\n sienna: /#a0522d(ff)?(?!\\w)/gi,\n silver: /#c0c0c0(ff)?(?!\\w)/gi,\n skyblue: /#87ceeb(ff)?(?!\\w)/gi,\n snow: /#fffafa(ff)?(?!\\w)/gi,\n tan: /#d2b48c(ff)?(?!\\w)/gi,\n teal: /#008080(ff)?(?!\\w)/gi,\n thistle: /#d8bfd8(ff)?(?!\\w)/gi,\n tomato: /#ff6347(ff)?(?!\\w)/gi,\n violet: /#ee82ee(ff)?(?!\\w)/gi,\n wheat: /#f5deb3(ff)?(?!\\w)/gi,\n white: /#ffffff(ff)?(?!\\w)|#fff(f)?(?!\\w)/gi,\n};\n", "var shorterNames = require('./shorter-css-color-names');\nvar REGEX = {\n whitespace: /\\s+/g,\n urlHexPairs: /%[\\dA-F]{2}/g,\n quotes: /\"/g,\n}\n\nfunction collapseWhitespace(str) {\n return str.trim().replace(REGEX.whitespace, ' ');\n}\n\nfunction dataURIPayload(string) {\n return encodeURIComponent(string)\n .replace(REGEX.urlHexPairs, specialHexEncode);\n}\n\n// `#` gets converted to `%23`, so quite a few CSS named colors are shorter than\n// their equivalent URL-encoded hex codes.\nfunction colorCodeToShorterNames(string) {\n Object.keys(shorterNames).forEach(function(key) {\n if (shorterNames[key].test(string)) {\n string = string.replace(shorterNames[key], key);\n }\n });\n\n return string;\n}\n\nfunction specialHexEncode(match) {\n switch (match) { // Browsers tolerate these characters, and they're frequent\n case '%20': return ' ';\n case '%3D': return '=';\n case '%3A': return ':';\n case '%2F': return '/';\n default: return match.toLowerCase(); // compresses better\n }\n}\n\nfunction svgToTinyDataUri(svgString) {\n if (typeof svgString !== 'string') {\n throw new TypeError('Expected a string, but received ' + typeof svgString);\n }\n // Strip the Byte-Order Mark if the SVG has one\n if (svgString.charCodeAt(0) === 0xfeff) { svgString = svgString.slice(1) }\n\n var body = colorCodeToShorterNames(collapseWhitespace(svgString))\n .replace(REGEX.quotes, \"'\");\n return 'data:image/svg+xml,' + dataURIPayload(body);\n}\n\nsvgToTinyDataUri.toSrcset = function toSrcset(svgString) {\n return svgToTinyDataUri(svgString).replace(/ /g, '%20');\n}\n\nmodule.exports = svgToTinyDataUri;\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"default\", {\n enumerable: true,\n get: function() {\n return _default;\n }\n});\nfunction createPlugin(plugin, config) {\n return {\n handler: plugin,\n config\n };\n}\ncreatePlugin.withOptions = function(pluginFunction, configFunction = ()=>({})) {\n const optionsFunction = function(options) {\n return {\n __options: options,\n handler: pluginFunction(options),\n config: configFunction(options)\n };\n };\n optionsFunction.__isOptionsFunction = true;\n // Expose plugin dependencies so that `object-hash` returns a different\n // value if anything here changes, to ensure a rebuild is triggered.\n optionsFunction.__pluginFunction = pluginFunction;\n optionsFunction.__configFunction = configFunction;\n return optionsFunction;\n};\nconst _default = createPlugin;\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"default\", {\n enumerable: true,\n get: function() {\n return _default;\n }\n});\nconst _createPlugin = /*#__PURE__*/ _interop_require_default(require(\"../util/createPlugin\"));\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : {\n default: obj\n };\n}\nconst _default = _createPlugin.default;\n", "let createPlugin = require('./lib/public/create-plugin')\nmodule.exports = (createPlugin.__esModule ? createPlugin : { default: createPlugin }).default\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"cloneDeep\", {\n enumerable: true,\n get: function() {\n return cloneDeep;\n }\n});\nfunction cloneDeep(value) {\n if (Array.isArray(value)) {\n return value.map((child)=>cloneDeep(child));\n }\n if (typeof value === \"object\" && value !== null) {\n return Object.fromEntries(Object.entries(value).map(([k, v])=>[\n k,\n cloneDeep(v)\n ]));\n }\n return value;\n}\n", "module.exports = {\n content: [],\n presets: [],\n darkMode: 'media', // or 'class'\n theme: {\n accentColor: ({ theme }) => ({\n ...theme('colors'),\n auto: 'auto',\n }),\n animation: {\n none: 'none',\n spin: 'spin 1s linear infinite',\n ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',\n pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',\n bounce: 'bounce 1s infinite',\n },\n aria: {\n busy: 'busy=\"true\"',\n checked: 'checked=\"true\"',\n disabled: 'disabled=\"true\"',\n expanded: 'expanded=\"true\"',\n hidden: 'hidden=\"true\"',\n pressed: 'pressed=\"true\"',\n readonly: 'readonly=\"true\"',\n required: 'required=\"true\"',\n selected: 'selected=\"true\"',\n },\n aspectRatio: {\n auto: 'auto',\n square: '1 / 1',\n video: '16 / 9',\n },\n backdropBlur: ({ theme }) => theme('blur'),\n backdropBrightness: ({ theme }) => theme('brightness'),\n backdropContrast: ({ theme }) => theme('contrast'),\n backdropGrayscale: ({ theme }) => theme('grayscale'),\n backdropHueRotate: ({ theme }) => theme('hueRotate'),\n backdropInvert: ({ theme }) => theme('invert'),\n backdropOpacity: ({ theme }) => theme('opacity'),\n backdropSaturate: ({ theme }) => theme('saturate'),\n backdropSepia: ({ theme }) => theme('sepia'),\n backgroundColor: ({ theme }) => theme('colors'),\n backgroundImage: {\n none: 'none',\n 'gradient-to-t': 'linear-gradient(to top, var(--tw-gradient-stops))',\n 'gradient-to-tr': 'linear-gradient(to top right, var(--tw-gradient-stops))',\n 'gradient-to-r': 'linear-gradient(to right, var(--tw-gradient-stops))',\n 'gradient-to-br': 'linear-gradient(to bottom right, var(--tw-gradient-stops))',\n 'gradient-to-b': 'linear-gradient(to bottom, var(--tw-gradient-stops))',\n 'gradient-to-bl': 'linear-gradient(to bottom left, var(--tw-gradient-stops))',\n 'gradient-to-l': 'linear-gradient(to left, var(--tw-gradient-stops))',\n 'gradient-to-tl': 'linear-gradient(to top left, var(--tw-gradient-stops))',\n },\n backgroundOpacity: ({ theme }) => theme('opacity'),\n backgroundPosition: {\n bottom: 'bottom',\n center: 'center',\n left: 'left',\n 'left-bottom': 'left bottom',\n 'left-top': 'left top',\n right: 'right',\n 'right-bottom': 'right bottom',\n 'right-top': 'right top',\n top: 'top',\n },\n backgroundSize: {\n auto: 'auto',\n cover: 'cover',\n contain: 'contain',\n },\n blur: {\n 0: '0',\n none: '',\n sm: '4px',\n DEFAULT: '8px',\n md: '12px',\n lg: '16px',\n xl: '24px',\n '2xl': '40px',\n '3xl': '64px',\n },\n borderColor: ({ theme }) => ({\n ...theme('colors'),\n DEFAULT: theme('colors.gray.200', 'currentColor'),\n }),\n borderOpacity: ({ theme }) => theme('opacity'),\n borderRadius: {\n none: '0px',\n sm: '0.125rem',\n DEFAULT: '0.25rem',\n md: '0.375rem',\n lg: '0.5rem',\n xl: '0.75rem',\n '2xl': '1rem',\n '3xl': '1.5rem',\n full: '9999px',\n },\n borderSpacing: ({ theme }) => ({\n ...theme('spacing'),\n }),\n borderWidth: {\n DEFAULT: '1px',\n 0: '0px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n boxShadow: {\n sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',\n DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',\n md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',\n '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',\n inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',\n none: 'none',\n },\n boxShadowColor: ({ theme }) => theme('colors'),\n brightness: {\n 0: '0',\n 50: '.5',\n 75: '.75',\n 90: '.9',\n 95: '.95',\n 100: '1',\n 105: '1.05',\n 110: '1.1',\n 125: '1.25',\n 150: '1.5',\n 200: '2',\n },\n caretColor: ({ theme }) => theme('colors'),\n colors: ({ colors }) => ({\n inherit: colors.inherit,\n current: colors.current,\n transparent: colors.transparent,\n black: colors.black,\n white: colors.white,\n slate: colors.slate,\n gray: colors.gray,\n zinc: colors.zinc,\n neutral: colors.neutral,\n stone: colors.stone,\n red: colors.red,\n orange: colors.orange,\n amber: colors.amber,\n yellow: colors.yellow,\n lime: colors.lime,\n green: colors.green,\n emerald: colors.emerald,\n teal: colors.teal,\n cyan: colors.cyan,\n sky: colors.sky,\n blue: colors.blue,\n indigo: colors.indigo,\n violet: colors.violet,\n purple: colors.purple,\n fuchsia: colors.fuchsia,\n pink: colors.pink,\n rose: colors.rose,\n }),\n columns: {\n auto: 'auto',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n '3xs': '16rem',\n '2xs': '18rem',\n xs: '20rem',\n sm: '24rem',\n md: '28rem',\n lg: '32rem',\n xl: '36rem',\n '2xl': '42rem',\n '3xl': '48rem',\n '4xl': '56rem',\n '5xl': '64rem',\n '6xl': '72rem',\n '7xl': '80rem',\n },\n container: {},\n content: {\n none: 'none',\n },\n contrast: {\n 0: '0',\n 50: '.5',\n 75: '.75',\n 100: '1',\n 125: '1.25',\n 150: '1.5',\n 200: '2',\n },\n cursor: {\n auto: 'auto',\n default: 'default',\n pointer: 'pointer',\n wait: 'wait',\n text: 'text',\n move: 'move',\n help: 'help',\n 'not-allowed': 'not-allowed',\n none: 'none',\n 'context-menu': 'context-menu',\n progress: 'progress',\n cell: 'cell',\n crosshair: 'crosshair',\n 'vertical-text': 'vertical-text',\n alias: 'alias',\n copy: 'copy',\n 'no-drop': 'no-drop',\n grab: 'grab',\n grabbing: 'grabbing',\n 'all-scroll': 'all-scroll',\n 'col-resize': 'col-resize',\n 'row-resize': 'row-resize',\n 'n-resize': 'n-resize',\n 'e-resize': 'e-resize',\n 's-resize': 's-resize',\n 'w-resize': 'w-resize',\n 'ne-resize': 'ne-resize',\n 'nw-resize': 'nw-resize',\n 'se-resize': 'se-resize',\n 'sw-resize': 'sw-resize',\n 'ew-resize': 'ew-resize',\n 'ns-resize': 'ns-resize',\n 'nesw-resize': 'nesw-resize',\n 'nwse-resize': 'nwse-resize',\n 'zoom-in': 'zoom-in',\n 'zoom-out': 'zoom-out',\n },\n divideColor: ({ theme }) => theme('borderColor'),\n divideOpacity: ({ theme }) => theme('borderOpacity'),\n divideWidth: ({ theme }) => theme('borderWidth'),\n dropShadow: {\n sm: '0 1px 1px rgb(0 0 0 / 0.05)',\n DEFAULT: ['0 1px 2px rgb(0 0 0 / 0.1)', '0 1px 1px rgb(0 0 0 / 0.06)'],\n md: ['0 4px 3px rgb(0 0 0 / 0.07)', '0 2px 2px rgb(0 0 0 / 0.06)'],\n lg: ['0 10px 8px rgb(0 0 0 / 0.04)', '0 4px 3px rgb(0 0 0 / 0.1)'],\n xl: ['0 20px 13px rgb(0 0 0 / 0.03)', '0 8px 5px rgb(0 0 0 / 0.08)'],\n '2xl': '0 25px 25px rgb(0 0 0 / 0.15)',\n none: '0 0 #0000',\n },\n fill: ({ theme }) => ({\n none: 'none',\n ...theme('colors'),\n }),\n flex: {\n 1: '1 1 0%',\n auto: '1 1 auto',\n initial: '0 1 auto',\n none: 'none',\n },\n flexBasis: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n '1/5': '20%',\n '2/5': '40%',\n '3/5': '60%',\n '4/5': '80%',\n '1/6': '16.666667%',\n '2/6': '33.333333%',\n '3/6': '50%',\n '4/6': '66.666667%',\n '5/6': '83.333333%',\n '1/12': '8.333333%',\n '2/12': '16.666667%',\n '3/12': '25%',\n '4/12': '33.333333%',\n '5/12': '41.666667%',\n '6/12': '50%',\n '7/12': '58.333333%',\n '8/12': '66.666667%',\n '9/12': '75%',\n '10/12': '83.333333%',\n '11/12': '91.666667%',\n full: '100%',\n }),\n flexGrow: {\n 0: '0',\n DEFAULT: '1',\n },\n flexShrink: {\n 0: '0',\n DEFAULT: '1',\n },\n fontFamily: {\n sans: [\n 'ui-sans-serif',\n 'system-ui',\n 'sans-serif',\n '\"Apple Color Emoji\"',\n '\"Segoe UI Emoji\"',\n '\"Segoe UI Symbol\"',\n '\"Noto Color Emoji\"',\n ],\n serif: ['ui-serif', 'Georgia', 'Cambria', '\"Times New Roman\"', 'Times', 'serif'],\n mono: [\n 'ui-monospace',\n 'SFMono-Regular',\n 'Menlo',\n 'Monaco',\n 'Consolas',\n '\"Liberation Mono\"',\n '\"Courier New\"',\n 'monospace',\n ],\n },\n fontSize: {\n xs: ['0.75rem', { lineHeight: '1rem' }],\n sm: ['0.875rem', { lineHeight: '1.25rem' }],\n base: ['1rem', { lineHeight: '1.5rem' }],\n lg: ['1.125rem', { lineHeight: '1.75rem' }],\n xl: ['1.25rem', { lineHeight: '1.75rem' }],\n '2xl': ['1.5rem', { lineHeight: '2rem' }],\n '3xl': ['1.875rem', { lineHeight: '2.25rem' }],\n '4xl': ['2.25rem', { lineHeight: '2.5rem' }],\n '5xl': ['3rem', { lineHeight: '1' }],\n '6xl': ['3.75rem', { lineHeight: '1' }],\n '7xl': ['4.5rem', { lineHeight: '1' }],\n '8xl': ['6rem', { lineHeight: '1' }],\n '9xl': ['8rem', { lineHeight: '1' }],\n },\n fontWeight: {\n thin: '100',\n extralight: '200',\n light: '300',\n normal: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n extrabold: '800',\n black: '900',\n },\n gap: ({ theme }) => theme('spacing'),\n gradientColorStops: ({ theme }) => theme('colors'),\n gradientColorStopPositions: {\n '0%': '0%',\n '5%': '5%',\n '10%': '10%',\n '15%': '15%',\n '20%': '20%',\n '25%': '25%',\n '30%': '30%',\n '35%': '35%',\n '40%': '40%',\n '45%': '45%',\n '50%': '50%',\n '55%': '55%',\n '60%': '60%',\n '65%': '65%',\n '70%': '70%',\n '75%': '75%',\n '80%': '80%',\n '85%': '85%',\n '90%': '90%',\n '95%': '95%',\n '100%': '100%',\n },\n grayscale: {\n 0: '0',\n DEFAULT: '100%',\n },\n gridAutoColumns: {\n auto: 'auto',\n min: 'min-content',\n max: 'max-content',\n fr: 'minmax(0, 1fr)',\n },\n gridAutoRows: {\n auto: 'auto',\n min: 'min-content',\n max: 'max-content',\n fr: 'minmax(0, 1fr)',\n },\n gridColumn: {\n auto: 'auto',\n 'span-1': 'span 1 / span 1',\n 'span-2': 'span 2 / span 2',\n 'span-3': 'span 3 / span 3',\n 'span-4': 'span 4 / span 4',\n 'span-5': 'span 5 / span 5',\n 'span-6': 'span 6 / span 6',\n 'span-7': 'span 7 / span 7',\n 'span-8': 'span 8 / span 8',\n 'span-9': 'span 9 / span 9',\n 'span-10': 'span 10 / span 10',\n 'span-11': 'span 11 / span 11',\n 'span-12': 'span 12 / span 12',\n 'span-full': '1 / -1',\n },\n gridColumnEnd: {\n auto: 'auto',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n 13: '13',\n },\n gridColumnStart: {\n auto: 'auto',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n 13: '13',\n },\n gridRow: {\n auto: 'auto',\n 'span-1': 'span 1 / span 1',\n 'span-2': 'span 2 / span 2',\n 'span-3': 'span 3 / span 3',\n 'span-4': 'span 4 / span 4',\n 'span-5': 'span 5 / span 5',\n 'span-6': 'span 6 / span 6',\n 'span-7': 'span 7 / span 7',\n 'span-8': 'span 8 / span 8',\n 'span-9': 'span 9 / span 9',\n 'span-10': 'span 10 / span 10',\n 'span-11': 'span 11 / span 11',\n 'span-12': 'span 12 / span 12',\n 'span-full': '1 / -1',\n },\n gridRowEnd: {\n auto: 'auto',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n 13: '13',\n },\n gridRowStart: {\n auto: 'auto',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n 13: '13',\n },\n gridTemplateColumns: {\n none: 'none',\n subgrid: 'subgrid',\n 1: 'repeat(1, minmax(0, 1fr))',\n 2: 'repeat(2, minmax(0, 1fr))',\n 3: 'repeat(3, minmax(0, 1fr))',\n 4: 'repeat(4, minmax(0, 1fr))',\n 5: 'repeat(5, minmax(0, 1fr))',\n 6: 'repeat(6, minmax(0, 1fr))',\n 7: 'repeat(7, minmax(0, 1fr))',\n 8: 'repeat(8, minmax(0, 1fr))',\n 9: 'repeat(9, minmax(0, 1fr))',\n 10: 'repeat(10, minmax(0, 1fr))',\n 11: 'repeat(11, minmax(0, 1fr))',\n 12: 'repeat(12, minmax(0, 1fr))',\n },\n gridTemplateRows: {\n none: 'none',\n subgrid: 'subgrid',\n 1: 'repeat(1, minmax(0, 1fr))',\n 2: 'repeat(2, minmax(0, 1fr))',\n 3: 'repeat(3, minmax(0, 1fr))',\n 4: 'repeat(4, minmax(0, 1fr))',\n 5: 'repeat(5, minmax(0, 1fr))',\n 6: 'repeat(6, minmax(0, 1fr))',\n 7: 'repeat(7, minmax(0, 1fr))',\n 8: 'repeat(8, minmax(0, 1fr))',\n 9: 'repeat(9, minmax(0, 1fr))',\n 10: 'repeat(10, minmax(0, 1fr))',\n 11: 'repeat(11, minmax(0, 1fr))',\n 12: 'repeat(12, minmax(0, 1fr))',\n },\n height: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n '1/5': '20%',\n '2/5': '40%',\n '3/5': '60%',\n '4/5': '80%',\n '1/6': '16.666667%',\n '2/6': '33.333333%',\n '3/6': '50%',\n '4/6': '66.666667%',\n '5/6': '83.333333%',\n full: '100%',\n screen: '100vh',\n svh: '100svh',\n lvh: '100lvh',\n dvh: '100dvh',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n hueRotate: {\n 0: '0deg',\n 15: '15deg',\n 30: '30deg',\n 60: '60deg',\n 90: '90deg',\n 180: '180deg',\n },\n inset: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n full: '100%',\n }),\n invert: {\n 0: '0',\n DEFAULT: '100%',\n },\n keyframes: {\n spin: {\n to: {\n transform: 'rotate(360deg)',\n },\n },\n ping: {\n '75%, 100%': {\n transform: 'scale(2)',\n opacity: '0',\n },\n },\n pulse: {\n '50%': {\n opacity: '.5',\n },\n },\n bounce: {\n '0%, 100%': {\n transform: 'translateY(-25%)',\n animationTimingFunction: 'cubic-bezier(0.8,0,1,1)',\n },\n '50%': {\n transform: 'none',\n animationTimingFunction: 'cubic-bezier(0,0,0.2,1)',\n },\n },\n },\n letterSpacing: {\n tighter: '-0.05em',\n tight: '-0.025em',\n normal: '0em',\n wide: '0.025em',\n wider: '0.05em',\n widest: '0.1em',\n },\n lineHeight: {\n none: '1',\n tight: '1.25',\n snug: '1.375',\n normal: '1.5',\n relaxed: '1.625',\n loose: '2',\n 3: '.75rem',\n 4: '1rem',\n 5: '1.25rem',\n 6: '1.5rem',\n 7: '1.75rem',\n 8: '2rem',\n 9: '2.25rem',\n 10: '2.5rem',\n },\n listStyleType: {\n none: 'none',\n disc: 'disc',\n decimal: 'decimal',\n },\n listStyleImage: {\n none: 'none',\n },\n margin: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n }),\n lineClamp: {\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n },\n maxHeight: ({ theme }) => ({\n ...theme('spacing'),\n none: 'none',\n full: '100%',\n screen: '100vh',\n svh: '100svh',\n lvh: '100lvh',\n dvh: '100dvh',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n maxWidth: ({ theme, breakpoints }) => ({\n ...theme('spacing'),\n none: 'none',\n xs: '20rem',\n sm: '24rem',\n md: '28rem',\n lg: '32rem',\n xl: '36rem',\n '2xl': '42rem',\n '3xl': '48rem',\n '4xl': '56rem',\n '5xl': '64rem',\n '6xl': '72rem',\n '7xl': '80rem',\n full: '100%',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n prose: '65ch',\n ...breakpoints(theme('screens')),\n }),\n minHeight: ({ theme }) => ({\n ...theme('spacing'),\n full: '100%',\n screen: '100vh',\n svh: '100svh',\n lvh: '100lvh',\n dvh: '100dvh',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n minWidth: ({ theme }) => ({\n ...theme('spacing'),\n full: '100%',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n objectPosition: {\n bottom: 'bottom',\n center: 'center',\n left: 'left',\n 'left-bottom': 'left bottom',\n 'left-top': 'left top',\n right: 'right',\n 'right-bottom': 'right bottom',\n 'right-top': 'right top',\n top: 'top',\n },\n opacity: {\n 0: '0',\n 5: '0.05',\n 10: '0.1',\n 15: '0.15',\n 20: '0.2',\n 25: '0.25',\n 30: '0.3',\n 35: '0.35',\n 40: '0.4',\n 45: '0.45',\n 50: '0.5',\n 55: '0.55',\n 60: '0.6',\n 65: '0.65',\n 70: '0.7',\n 75: '0.75',\n 80: '0.8',\n 85: '0.85',\n 90: '0.9',\n 95: '0.95',\n 100: '1',\n },\n order: {\n first: '-9999',\n last: '9999',\n none: '0',\n 1: '1',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: '11',\n 12: '12',\n },\n outlineColor: ({ theme }) => theme('colors'),\n outlineOffset: {\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n outlineWidth: {\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n padding: ({ theme }) => theme('spacing'),\n placeholderColor: ({ theme }) => theme('colors'),\n placeholderOpacity: ({ theme }) => theme('opacity'),\n ringColor: ({ theme }) => ({\n DEFAULT: theme('colors.blue.500', '#3b82f6'),\n ...theme('colors'),\n }),\n ringOffsetColor: ({ theme }) => theme('colors'),\n ringOffsetWidth: {\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n ringOpacity: ({ theme }) => ({\n DEFAULT: '0.5',\n ...theme('opacity'),\n }),\n ringWidth: {\n DEFAULT: '3px',\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n rotate: {\n 0: '0deg',\n 1: '1deg',\n 2: '2deg',\n 3: '3deg',\n 6: '6deg',\n 12: '12deg',\n 45: '45deg',\n 90: '90deg',\n 180: '180deg',\n },\n saturate: {\n 0: '0',\n 50: '.5',\n 100: '1',\n 150: '1.5',\n 200: '2',\n },\n scale: {\n 0: '0',\n 50: '.5',\n 75: '.75',\n 90: '.9',\n 95: '.95',\n 100: '1',\n 105: '1.05',\n 110: '1.1',\n 125: '1.25',\n 150: '1.5',\n },\n screens: {\n sm: '640px',\n md: '768px',\n lg: '1024px',\n xl: '1280px',\n '2xl': '1536px',\n },\n scrollMargin: ({ theme }) => ({\n ...theme('spacing'),\n }),\n scrollPadding: ({ theme }) => theme('spacing'),\n sepia: {\n 0: '0',\n DEFAULT: '100%',\n },\n skew: {\n 0: '0deg',\n 1: '1deg',\n 2: '2deg',\n 3: '3deg',\n 6: '6deg',\n 12: '12deg',\n },\n space: ({ theme }) => ({\n ...theme('spacing'),\n }),\n spacing: {\n px: '1px',\n 0: '0px',\n 0.5: '0.125rem',\n 1: '0.25rem',\n 1.5: '0.375rem',\n 2: '0.5rem',\n 2.5: '0.625rem',\n 3: '0.75rem',\n 3.5: '0.875rem',\n 4: '1rem',\n 5: '1.25rem',\n 6: '1.5rem',\n 7: '1.75rem',\n 8: '2rem',\n 9: '2.25rem',\n 10: '2.5rem',\n 11: '2.75rem',\n 12: '3rem',\n 14: '3.5rem',\n 16: '4rem',\n 20: '5rem',\n 24: '6rem',\n 28: '7rem',\n 32: '8rem',\n 36: '9rem',\n 40: '10rem',\n 44: '11rem',\n 48: '12rem',\n 52: '13rem',\n 56: '14rem',\n 60: '15rem',\n 64: '16rem',\n 72: '18rem',\n 80: '20rem',\n 96: '24rem',\n },\n stroke: ({ theme }) => ({\n none: 'none',\n ...theme('colors'),\n }),\n strokeWidth: {\n 0: '0',\n 1: '1',\n 2: '2',\n },\n supports: {},\n data: {},\n textColor: ({ theme }) => theme('colors'),\n textDecorationColor: ({ theme }) => theme('colors'),\n textDecorationThickness: {\n auto: 'auto',\n 'from-font': 'from-font',\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n textIndent: ({ theme }) => ({\n ...theme('spacing'),\n }),\n textOpacity: ({ theme }) => theme('opacity'),\n textUnderlineOffset: {\n auto: 'auto',\n 0: '0px',\n 1: '1px',\n 2: '2px',\n 4: '4px',\n 8: '8px',\n },\n transformOrigin: {\n center: 'center',\n top: 'top',\n 'top-right': 'top right',\n right: 'right',\n 'bottom-right': 'bottom right',\n bottom: 'bottom',\n 'bottom-left': 'bottom left',\n left: 'left',\n 'top-left': 'top left',\n },\n transitionDelay: {\n 0: '0s',\n 75: '75ms',\n 100: '100ms',\n 150: '150ms',\n 200: '200ms',\n 300: '300ms',\n 500: '500ms',\n 700: '700ms',\n 1000: '1000ms',\n },\n transitionDuration: {\n DEFAULT: '150ms',\n 0: '0s',\n 75: '75ms',\n 100: '100ms',\n 150: '150ms',\n 200: '200ms',\n 300: '300ms',\n 500: '500ms',\n 700: '700ms',\n 1000: '1000ms',\n },\n transitionProperty: {\n none: 'none',\n all: 'all',\n DEFAULT:\n 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter',\n colors: 'color, background-color, border-color, text-decoration-color, fill, stroke',\n opacity: 'opacity',\n shadow: 'box-shadow',\n transform: 'transform',\n },\n transitionTimingFunction: {\n DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',\n linear: 'linear',\n in: 'cubic-bezier(0.4, 0, 1, 1)',\n out: 'cubic-bezier(0, 0, 0.2, 1)',\n 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',\n },\n translate: ({ theme }) => ({\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n full: '100%',\n }),\n size: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n '1/5': '20%',\n '2/5': '40%',\n '3/5': '60%',\n '4/5': '80%',\n '1/6': '16.666667%',\n '2/6': '33.333333%',\n '3/6': '50%',\n '4/6': '66.666667%',\n '5/6': '83.333333%',\n '1/12': '8.333333%',\n '2/12': '16.666667%',\n '3/12': '25%',\n '4/12': '33.333333%',\n '5/12': '41.666667%',\n '6/12': '50%',\n '7/12': '58.333333%',\n '8/12': '66.666667%',\n '9/12': '75%',\n '10/12': '83.333333%',\n '11/12': '91.666667%',\n full: '100%',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n width: ({ theme }) => ({\n auto: 'auto',\n ...theme('spacing'),\n '1/2': '50%',\n '1/3': '33.333333%',\n '2/3': '66.666667%',\n '1/4': '25%',\n '2/4': '50%',\n '3/4': '75%',\n '1/5': '20%',\n '2/5': '40%',\n '3/5': '60%',\n '4/5': '80%',\n '1/6': '16.666667%',\n '2/6': '33.333333%',\n '3/6': '50%',\n '4/6': '66.666667%',\n '5/6': '83.333333%',\n '1/12': '8.333333%',\n '2/12': '16.666667%',\n '3/12': '25%',\n '4/12': '33.333333%',\n '5/12': '41.666667%',\n '6/12': '50%',\n '7/12': '58.333333%',\n '8/12': '66.666667%',\n '9/12': '75%',\n '10/12': '83.333333%',\n '11/12': '91.666667%',\n full: '100%',\n screen: '100vw',\n svw: '100svw',\n lvw: '100lvw',\n dvw: '100dvw',\n min: 'min-content',\n max: 'max-content',\n fit: 'fit-content',\n }),\n willChange: {\n auto: 'auto',\n scroll: 'scroll-position',\n contents: 'contents',\n transform: 'transform',\n },\n zIndex: {\n auto: 'auto',\n 0: '0',\n 10: '10',\n 20: '20',\n 30: '30',\n 40: '40',\n 50: '50',\n },\n },\n plugins: [],\n}\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"default\", {\n enumerable: true,\n get: function() {\n return _default;\n }\n});\nconst _cloneDeep = require(\"../util/cloneDeep\");\nconst _configfull = /*#__PURE__*/ _interop_require_default(require(\"../../stubs/config.full\"));\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : {\n default: obj\n };\n}\nconst _default = (0, _cloneDeep.cloneDeep)(_configfull.default.theme);\n", "let defaultTheme = require('./lib/public/default-theme')\nmodule.exports = (defaultTheme.__esModule ? defaultTheme : { default: defaultTheme }).default\n", "const svgToDataUri = require('mini-svg-data-uri')\nconst plugin = require('tailwindcss/plugin')\nconst defaultTheme = require('tailwindcss/defaultTheme')\nconst colors = require('tailwindcss/colors')\nconst [baseFontSize, { lineHeight: baseLineHeight }] = defaultTheme.fontSize.base\nconst { spacing, borderWidth, borderRadius } = defaultTheme\n\nfunction resolveColor(color, opacityVariableName) {\n return color.replace('', `var(${opacityVariableName}, 1)`)\n}\n\nconst forms = plugin.withOptions(function (options = { strategy: undefined }) {\n return function ({ addBase, addComponents, theme }) {\n function resolveChevronColor(color, fallback) {\n let resolved = theme(color)\n\n if (!resolved || resolved.includes('var(')) {\n return fallback\n }\n\n return resolved.replace('', '1')\n }\n\n const strategy = options.strategy === undefined ? ['base', 'class'] : [options.strategy]\n\n const rules = [\n {\n base: [\n \"[type='text']\",\n 'input:where(:not([type]))',\n \"[type='email']\",\n \"[type='url']\",\n \"[type='password']\",\n \"[type='number']\",\n \"[type='date']\",\n \"[type='datetime-local']\",\n \"[type='month']\",\n \"[type='search']\",\n \"[type='tel']\",\n \"[type='time']\",\n \"[type='week']\",\n '[multiple]',\n 'textarea',\n 'select',\n ],\n class: ['.form-input', '.form-textarea', '.form-select', '.form-multiselect'],\n styles: {\n appearance: 'none',\n 'background-color': '#fff',\n 'border-color': resolveColor(\n theme('colors.gray.500', colors.gray[500]),\n '--tw-border-opacity'\n ),\n 'border-width': borderWidth['DEFAULT'],\n 'border-radius': borderRadius.none,\n 'padding-top': spacing[2],\n 'padding-right': spacing[3],\n 'padding-bottom': spacing[2],\n 'padding-left': spacing[3],\n 'font-size': baseFontSize,\n 'line-height': baseLineHeight,\n '--tw-shadow': '0 0 #0000',\n '&:focus': {\n outline: '2px solid transparent',\n 'outline-offset': '2px',\n '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',\n '--tw-ring-offset-width': '0px',\n '--tw-ring-offset-color': '#fff',\n '--tw-ring-color': resolveColor(\n theme('colors.blue.600', colors.blue[600]),\n '--tw-ring-opacity'\n ),\n '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,\n '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,\n 'box-shadow': `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,\n 'border-color': resolveColor(\n theme('colors.blue.600', colors.blue[600]),\n '--tw-border-opacity'\n ),\n },\n },\n },\n {\n base: ['input::placeholder', 'textarea::placeholder'],\n class: ['.form-input::placeholder', '.form-textarea::placeholder'],\n styles: {\n color: resolveColor(theme('colors.gray.500', colors.gray[500]), '--tw-text-opacity'),\n opacity: '1',\n },\n },\n {\n base: ['::-webkit-datetime-edit-fields-wrapper'],\n class: ['.form-input::-webkit-datetime-edit-fields-wrapper'],\n styles: {\n padding: '0',\n },\n },\n {\n // Unfortunate hack until https://bugs.webkit.org/show_bug.cgi?id=198959 is fixed.\n // This sucks because users can't change line-height with a utility on date inputs now.\n // Reference: https://github.com/twbs/bootstrap/pull/31993\n base: ['::-webkit-date-and-time-value'],\n class: ['.form-input::-webkit-date-and-time-value'],\n styles: {\n 'min-height': '1.5em',\n },\n },\n {\n // In Safari on iOS date and time inputs are centered instead of left-aligned and can't be\n // changed with `text-align` utilities on the input by default. Resetting this to `inherit`\n // makes them left-aligned by default and makes it possible to override the alignment with\n // utility classes without using an arbitrary variant to target the pseudo-elements.\n base: ['::-webkit-date-and-time-value'],\n class: ['.form-input::-webkit-date-and-time-value'],\n styles: {\n 'text-align': 'inherit',\n },\n },\n {\n // In Safari on macOS date time inputs that are set to `display: block` have unexpected\n // extra bottom spacing. This can be corrected by setting the `::-webkit-datetime-edit`\n // pseudo-element to `display: inline-flex`, instead of the browser default of\n // `display: inline-block`.\n base: ['::-webkit-datetime-edit'],\n class: ['.form-input::-webkit-datetime-edit'],\n styles: {\n display: 'inline-flex',\n },\n },\n {\n // In Safari on macOS date time inputs are 4px taller than normal inputs\n // This is because there is extra padding on the datetime-edit and datetime-edit-{part}-field pseudo elements\n // See https://github.com/tailwindlabs/tailwindcss-forms/issues/95\n base: [\n '::-webkit-datetime-edit',\n '::-webkit-datetime-edit-year-field',\n '::-webkit-datetime-edit-month-field',\n '::-webkit-datetime-edit-day-field',\n '::-webkit-datetime-edit-hour-field',\n '::-webkit-datetime-edit-minute-field',\n '::-webkit-datetime-edit-second-field',\n '::-webkit-datetime-edit-millisecond-field',\n '::-webkit-datetime-edit-meridiem-field',\n ],\n class: [\n '.form-input::-webkit-datetime-edit',\n '.form-input::-webkit-datetime-edit-year-field',\n '.form-input::-webkit-datetime-edit-month-field',\n '.form-input::-webkit-datetime-edit-day-field',\n '.form-input::-webkit-datetime-edit-hour-field',\n '.form-input::-webkit-datetime-edit-minute-field',\n '.form-input::-webkit-datetime-edit-second-field',\n '.form-input::-webkit-datetime-edit-millisecond-field',\n '.form-input::-webkit-datetime-edit-meridiem-field',\n ],\n styles: {\n 'padding-top': 0,\n 'padding-bottom': 0,\n },\n },\n {\n base: ['select'],\n class: ['.form-select'],\n styles: {\n 'background-image': `url(\"${svgToDataUri(\n ``\n )}\")`,\n 'background-position': `right ${spacing[2]} center`,\n 'background-repeat': `no-repeat`,\n 'background-size': `1.5em 1.5em`,\n 'padding-right': spacing[10],\n 'print-color-adjust': `exact`,\n },\n },\n {\n base: ['[multiple]', '[size]:where(select:not([size=\"1\"]))'],\n class: ['.form-select:where([size]:not([size=\"1\"]))'],\n styles: {\n 'background-image': 'initial',\n 'background-position': 'initial',\n 'background-repeat': 'unset',\n 'background-size': 'initial',\n 'padding-right': spacing[3],\n 'print-color-adjust': 'unset',\n },\n },\n {\n base: [`[type='checkbox']`, `[type='radio']`],\n class: ['.form-checkbox', '.form-radio'],\n styles: {\n appearance: 'none',\n padding: '0',\n 'print-color-adjust': 'exact',\n display: 'inline-block',\n 'vertical-align': 'middle',\n 'background-origin': 'border-box',\n 'user-select': 'none',\n 'flex-shrink': '0',\n height: spacing[4],\n width: spacing[4],\n color: resolveColor(theme('colors.blue.600', colors.blue[600]), '--tw-text-opacity'),\n 'background-color': '#fff',\n 'border-color': resolveColor(\n theme('colors.gray.500', colors.gray[500]),\n '--tw-border-opacity'\n ),\n 'border-width': borderWidth['DEFAULT'],\n '--tw-shadow': '0 0 #0000',\n },\n },\n {\n base: [`[type='checkbox']`],\n class: ['.form-checkbox'],\n styles: {\n 'border-radius': borderRadius['none'],\n },\n },\n {\n base: [`[type='radio']`],\n class: ['.form-radio'],\n styles: {\n 'border-radius': '100%',\n },\n },\n {\n base: [`[type='checkbox']:focus`, `[type='radio']:focus`],\n class: ['.form-checkbox:focus', '.form-radio:focus'],\n styles: {\n outline: '2px solid transparent',\n 'outline-offset': '2px',\n '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',\n '--tw-ring-offset-width': '2px',\n '--tw-ring-offset-color': '#fff',\n '--tw-ring-color': resolveColor(\n theme('colors.blue.600', colors.blue[600]),\n '--tw-ring-opacity'\n ),\n '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,\n '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,\n 'box-shadow': `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,\n },\n },\n {\n base: [`[type='checkbox']:checked`, `[type='radio']:checked`],\n class: ['.form-checkbox:checked', '.form-radio:checked'],\n styles: {\n 'border-color': `transparent`,\n 'background-color': `currentColor`,\n 'background-size': `100% 100%`,\n 'background-position': `center`,\n 'background-repeat': `no-repeat`,\n },\n },\n {\n base: [`[type='checkbox']:checked`],\n class: ['.form-checkbox:checked'],\n styles: {\n 'background-image': `url(\"${svgToDataUri(\n ``\n )}\")`,\n\n '@media (forced-colors: active) ': {\n appearance: 'auto',\n },\n },\n },\n {\n base: [`[type='radio']:checked`],\n class: ['.form-radio:checked'],\n styles: {\n 'background-image': `url(\"${svgToDataUri(\n ``\n )}\")`,\n\n '@media (forced-colors: active) ': {\n appearance: 'auto',\n },\n },\n },\n {\n base: [\n `[type='checkbox']:checked:hover`,\n `[type='checkbox']:checked:focus`,\n `[type='radio']:checked:hover`,\n `[type='radio']:checked:focus`,\n ],\n class: [\n '.form-checkbox:checked:hover',\n '.form-checkbox:checked:focus',\n '.form-radio:checked:hover',\n '.form-radio:checked:focus',\n ],\n styles: {\n 'border-color': 'transparent',\n 'background-color': 'currentColor',\n },\n },\n {\n base: [`[type='checkbox']:indeterminate`],\n class: ['.form-checkbox:indeterminate'],\n styles: {\n 'background-image': `url(\"${svgToDataUri(\n ``\n )}\")`,\n 'border-color': `transparent`,\n 'background-color': `currentColor`,\n 'background-size': `100% 100%`,\n 'background-position': `center`,\n 'background-repeat': `no-repeat`,\n\n '@media (forced-colors: active) ': {\n appearance: 'auto',\n },\n },\n },\n {\n base: [`[type='checkbox']:indeterminate:hover`, `[type='checkbox']:indeterminate:focus`],\n class: ['.form-checkbox:indeterminate:hover', '.form-checkbox:indeterminate:focus'],\n styles: {\n 'border-color': 'transparent',\n 'background-color': 'currentColor',\n },\n },\n {\n base: [`[type='file']`],\n class: null,\n styles: {\n background: 'unset',\n 'border-color': 'inherit',\n 'border-width': '0',\n 'border-radius': '0',\n padding: '0',\n 'font-size': 'unset',\n 'line-height': 'inherit',\n },\n },\n {\n base: [`[type='file']:focus`],\n class: null,\n styles: {\n outline: [`1px solid ButtonText`, `1px auto -webkit-focus-ring-color`],\n },\n },\n ]\n\n const getStrategyRules = (strategy) =>\n rules\n .map((rule) => {\n if (rule[strategy] === null) return null\n\n return { [rule[strategy]]: rule.styles }\n })\n .filter(Boolean)\n\n if (strategy.includes('base')) {\n addBase(getStrategyRules('base'))\n }\n\n if (strategy.includes('class')) {\n addComponents(getStrategyRules('class'))\n }\n }\n})\n\nmodule.exports = forms\n", "const colors = require('tailwindcss/colors')\nconst forms = require('@tailwindcss/forms')\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n './app/components/**/*.html.erb',\n './app/components/**/*.rb',\n './app/views/**/*.html.erb',\n './app/views/**/*.turbo_stream.erb',\n './app/helpers/**/*.rb',\n './app/models/github_team.rb',\n './app/javascript/**/*.js',\n './public/**/*.html'\n ],\n safeList: [\n 'w-36',\n 'h-36'\n ],\n darkMode: 'class',\n theme: {\n colors: {\n transparent: 'transparent',\n current: 'currentColor',\n black: '#111827',\n white: colors.white,\n blue: {\n 50: '#F0F9FF',\n 100: '#E1F2FF',\n 200: '#BFE2FF',\n 300: '#8BCEFE',\n 400: '#54B6FD',\n 500: '#0896FC',\n 600: '#0388E8',\n 700: '#0274C5',\n 800: '#0265AB',\n 900: '#014779'\n },\n orange: {\n 50: '#FFF7ED',\n 100: '#FFEDD5',\n 200: '#FED7AA',\n 300: '#FDBA74',\n 400: '#FB923C',\n 500: '#F97316',\n 600: '#EA580C',\n 700: '#C2410C',\n 800: '#9A3412'\n },\n purple: {\n 50: '#F5F3FF',\n 100: '#EDE9FE',\n 200: '#DDD6FE',\n 300: '#C4B5FD',\n 400: '#A78BFA',\n 500: '#8B5CF6',\n 600: '#7C3AED',\n 700: '#6D28D9',\n 800: '#6D28D9'\n },\n yellow: {\n 50: '#FFFBEB',\n 100: '#FEF3C7',\n 200: '#FDE68A',\n 300: '#FCD34D',\n 400: '#FBBF24',\n 500: '#F59E0B',\n 600: '#D97706',\n 700: '#B45309',\n 800: '#92400E'\n },\n green: {\n 50: '#F0FDF4',\n 100: '#DCFCE7',\n 200: '#BBF7D0',\n 300: '#86EFAC',\n 400: '#4ADE80',\n 500: '#22C55E',\n 600: '#16A34A',\n 700: '#15803D',\n 800: '#166534'\n },\n cyan: {\n 50: '#E6F8FB',\n 100: '#CBF0F6',\n 200: '#ADE7F1',\n 300: '#80DEEE',\n 400: '#4ED1E5',\n 500: '#06B6D4',\n 600: '#0891B2',\n 800: '#155E75'\n },\n gray: {\n 50: '#F9FAFB',\n 100: '#F3F4F6',\n 200: '#E5E7EB',\n 300: '#D1D5DB',\n 400: '#9CA3AF',\n 500: '#6B7280',\n 600: '#4B5563',\n 700: '#374151',\n 800: '#1F2937',\n 900: '#111827'\n },\n red: {\n 50: '#FFF1F2',\n 100: '#FFE4E6',\n 200: '#FECDD3',\n 300: '#FDA4AF',\n 400: '#FB7185',\n 500: '#F43F5E',\n 600: '#E11D48',\n 700: '#BE123C',\n 800: '#9F1239'\n },\n indigo: {\n 50: '#EEF2FF',\n 100: '#E0E7FF',\n 200: '#C7D2FE',\n 300: '#A5B4FC',\n 400: '#8FA2FE',\n 500: '#6366F1',\n 600: '#4F46E5',\n 800: '#3730A3'\n },\n pink: {\n 100: '#FCE7F3',\n 200: '#FBCFE8',\n 300: '#F9A8D4',\n 500: '#EC4899',\n 600: '#DB2777',\n 800: '#9D174D'\n },\n teal: {\n 50: '#E8F8F6',\n 100: '#D0F1ED',\n 200: '#B9EAE4',\n 300: '#8ADCD3',\n 400: '#5BCDC1',\n 500: '#14B8A6',\n 600: '#0D9488',\n 700: '#0F766E',\n 800: '#115E59'\n },\n 'sequence-light': {\n 1: '#CBF0F6',\n 2: '#BFE2FF',\n 3: '#A9CAF9',\n 4: '#9FADF7',\n 5: '#A385F9'\n },\n 'sequence-dark': {\n 1: '#ADE7F1',\n 2: '#94CFFF',\n 3: '#6FA7F4',\n 4: '#5E77F2',\n 5: '#6634F5'\n },\n 'categorical-light': {\n 1: '#BFE2FF',\n 2: '#C4B5FD',\n 3: '#8ADCD3',\n 4: '#FED7AA',\n 5: '#ADE7F1',\n 6: '#8FA2FE',\n 7: '#FDA4AF',\n 8: '#FDE68A',\n 9: '#4ED1E5',\n 10: '#6366F1',\n 11: '#F472B6',\n 12: '#5BCDC1'\n },\n 'categorical-dark': {\n 1: '#0896FC',\n 2: '#A78BFA',\n 3: '#14B8A6',\n 4: '#FB923C',\n 5: '#4ED1E5',\n 6: '#6366F1',\n 7: '#FB7185',\n 8: '#FBBF24',\n 9: '#0891B2',\n 10: '#A5B4FC',\n 11: '#EC4899',\n 12: '#8ADCD3'\n }\n },\n extend: {\n spacing: {\n 18: '4.5rem'\n },\n aspectRatio: {\n '3/4': '3 / 4'\n },\n screens: {\n '3xl': '1792px',\n '4xl': '2048px',\n '5xl': '2304px'\n }\n },\n zIndex: {\n 0: 0,\n 10: 10,\n 20: 20,\n 25: 25,\n 30: 30,\n 40: 40,\n 50: 50,\n 75: 75,\n 100: 100,\n 1000: 1000,\n max: 2147483648,\n auto: 'auto'\n }\n },\n variants: {\n extend: {\n backgroundOpacity: ['active']\n }\n },\n plugins: [forms]\n}\n", "/* global define */\n\n(function (root, pluralize) {\n /* istanbul ignore else */\n if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {\n // Node.\n module.exports = pluralize();\n } else if (typeof define === 'function' && define.amd) {\n // AMD, registers as an anonymous module.\n define(function () {\n return pluralize();\n });\n } else {\n // Browser global.\n root.pluralize = pluralize();\n }\n})(this, function () {\n // Rule storage - pluralize and singularize need to be run sequentially,\n // while other rules can be optimized using an object for instant lookups.\n var pluralRules = [];\n var singularRules = [];\n var uncountables = {};\n var irregularPlurals = {};\n var irregularSingles = {};\n\n /**\n * Sanitize a pluralization rule to a usable regular expression.\n *\n * @param {(RegExp|string)} rule\n * @return {RegExp}\n */\n function sanitizeRule (rule) {\n if (typeof rule === 'string') {\n return new RegExp('^' + rule + '$', 'i');\n }\n\n return rule;\n }\n\n /**\n * Pass in a word token to produce a function that can replicate the case on\n * another word.\n *\n * @param {string} word\n * @param {string} token\n * @return {Function}\n */\n function restoreCase (word, token) {\n // Tokens are an exact match.\n if (word === token) return token;\n\n // Lower cased words. E.g. \"hello\".\n if (word === word.toLowerCase()) return token.toLowerCase();\n\n // Upper cased words. E.g. \"WHISKY\".\n if (word === word.toUpperCase()) return token.toUpperCase();\n\n // Title cased words. E.g. \"Title\".\n if (word[0] === word[0].toUpperCase()) {\n return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();\n }\n\n // Lower cased words. E.g. \"test\".\n return token.toLowerCase();\n }\n\n /**\n * Interpolate a regexp string.\n *\n * @param {string} str\n * @param {Array} args\n * @return {string}\n */\n function interpolate (str, args) {\n return str.replace(/\\$(\\d{1,2})/g, function (match, index) {\n return args[index] || '';\n });\n }\n\n /**\n * Replace a word using a rule.\n *\n * @param {string} word\n * @param {Array} rule\n * @return {string}\n */\n function replace (word, rule) {\n return word.replace(rule[0], function (match, index) {\n var result = interpolate(rule[1], arguments);\n\n if (match === '') {\n return restoreCase(word[index - 1], result);\n }\n\n return restoreCase(match, result);\n });\n }\n\n /**\n * Sanitize a word by passing in the word and sanitization rules.\n *\n * @param {string} token\n * @param {string} word\n * @param {Array} rules\n * @return {string}\n */\n function sanitizeWord (token, word, rules) {\n // Empty string or doesn't need fixing.\n if (!token.length || uncountables.hasOwnProperty(token)) {\n return word;\n }\n\n var len = rules.length;\n\n // Iterate over the sanitization rules and use the first one to match.\n while (len--) {\n var rule = rules[len];\n\n if (rule[0].test(word)) return replace(word, rule);\n }\n\n return word;\n }\n\n /**\n * Replace a word with the updated word.\n *\n * @param {Object} replaceMap\n * @param {Object} keepMap\n * @param {Array} rules\n * @return {Function}\n */\n function replaceWord (replaceMap, keepMap, rules) {\n return function (word) {\n // Get the correct token and case restoration functions.\n var token = word.toLowerCase();\n\n // Check against the keep object map.\n if (keepMap.hasOwnProperty(token)) {\n return restoreCase(word, token);\n }\n\n // Check against the replacement map for a direct word replacement.\n if (replaceMap.hasOwnProperty(token)) {\n return restoreCase(word, replaceMap[token]);\n }\n\n // Run all the rules against the word.\n return sanitizeWord(token, word, rules);\n };\n }\n\n /**\n * Check if a word is part of the map.\n */\n function checkWord (replaceMap, keepMap, rules, bool) {\n return function (word) {\n var token = word.toLowerCase();\n\n if (keepMap.hasOwnProperty(token)) return true;\n if (replaceMap.hasOwnProperty(token)) return false;\n\n return sanitizeWord(token, token, rules) === token;\n };\n }\n\n /**\n * Pluralize or singularize a word based on the passed in count.\n *\n * @param {string} word The word to pluralize\n * @param {number} count How many of the word exist\n * @param {boolean} inclusive Whether to prefix with the number (e.g. 3 ducks)\n * @return {string}\n */\n function pluralize (word, count, inclusive) {\n var pluralized = count === 1\n ? pluralize.singular(word) : pluralize.plural(word);\n\n return (inclusive ? count + ' ' : '') + pluralized;\n }\n\n /**\n * Pluralize a word.\n *\n * @type {Function}\n */\n pluralize.plural = replaceWord(\n irregularSingles, irregularPlurals, pluralRules\n );\n\n /**\n * Check if a word is plural.\n *\n * @type {Function}\n */\n pluralize.isPlural = checkWord(\n irregularSingles, irregularPlurals, pluralRules\n );\n\n /**\n * Singularize a word.\n *\n * @type {Function}\n */\n pluralize.singular = replaceWord(\n irregularPlurals, irregularSingles, singularRules\n );\n\n /**\n * Check if a word is singular.\n *\n * @type {Function}\n */\n pluralize.isSingular = checkWord(\n irregularPlurals, irregularSingles, singularRules\n );\n\n /**\n * Add a pluralization rule to the collection.\n *\n * @param {(string|RegExp)} rule\n * @param {string} replacement\n */\n pluralize.addPluralRule = function (rule, replacement) {\n pluralRules.push([sanitizeRule(rule), replacement]);\n };\n\n /**\n * Add a singularization rule to the collection.\n *\n * @param {(string|RegExp)} rule\n * @param {string} replacement\n */\n pluralize.addSingularRule = function (rule, replacement) {\n singularRules.push([sanitizeRule(rule), replacement]);\n };\n\n /**\n * Add an uncountable word rule.\n *\n * @param {(string|RegExp)} word\n */\n pluralize.addUncountableRule = function (word) {\n if (typeof word === 'string') {\n uncountables[word.toLowerCase()] = true;\n return;\n }\n\n // Set singular and plural references for the word.\n pluralize.addPluralRule(word, '$0');\n pluralize.addSingularRule(word, '$0');\n };\n\n /**\n * Add an irregular word definition.\n *\n * @param {string} single\n * @param {string} plural\n */\n pluralize.addIrregularRule = function (single, plural) {\n plural = plural.toLowerCase();\n single = single.toLowerCase();\n\n irregularSingles[single] = plural;\n irregularPlurals[plural] = single;\n };\n\n /**\n * Irregular rules.\n */\n [\n // Pronouns.\n ['I', 'we'],\n ['me', 'us'],\n ['he', 'they'],\n ['she', 'they'],\n ['them', 'them'],\n ['myself', 'ourselves'],\n ['yourself', 'yourselves'],\n ['itself', 'themselves'],\n ['herself', 'themselves'],\n ['himself', 'themselves'],\n ['themself', 'themselves'],\n ['is', 'are'],\n ['was', 'were'],\n ['has', 'have'],\n ['this', 'these'],\n ['that', 'those'],\n // Words ending in with a consonant and `o`.\n ['echo', 'echoes'],\n ['dingo', 'dingoes'],\n ['volcano', 'volcanoes'],\n ['tornado', 'tornadoes'],\n ['torpedo', 'torpedoes'],\n // Ends with `us`.\n ['genus', 'genera'],\n ['viscus', 'viscera'],\n // Ends with `ma`.\n ['stigma', 'stigmata'],\n ['stoma', 'stomata'],\n ['dogma', 'dogmata'],\n ['lemma', 'lemmata'],\n ['schema', 'schemata'],\n ['anathema', 'anathemata'],\n // Other irregular rules.\n ['ox', 'oxen'],\n ['axe', 'axes'],\n ['die', 'dice'],\n ['yes', 'yeses'],\n ['foot', 'feet'],\n ['eave', 'eaves'],\n ['goose', 'geese'],\n ['tooth', 'teeth'],\n ['quiz', 'quizzes'],\n ['human', 'humans'],\n ['proof', 'proofs'],\n ['carve', 'carves'],\n ['valve', 'valves'],\n ['looey', 'looies'],\n ['thief', 'thieves'],\n ['groove', 'grooves'],\n ['pickaxe', 'pickaxes'],\n ['passerby', 'passersby']\n ].forEach(function (rule) {\n return pluralize.addIrregularRule(rule[0], rule[1]);\n });\n\n /**\n * Pluralization rules.\n */\n [\n [/s?$/i, 's'],\n [/[^\\u0000-\\u007F]$/i, '$0'],\n [/([^aeiou]ese)$/i, '$1'],\n [/(ax|test)is$/i, '$1es'],\n [/(alias|[^aou]us|t[lm]as|gas|ris)$/i, '$1es'],\n [/(e[mn]u)s?$/i, '$1s'],\n [/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, '$1'],\n [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, '$1i'],\n [/(alumn|alg|vertebr)(?:a|ae)$/i, '$1ae'],\n [/(seraph|cherub)(?:im)?$/i, '$1im'],\n [/(her|at|gr)o$/i, '$1oes'],\n [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i, '$1a'],\n [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i, '$1a'],\n [/sis$/i, 'ses'],\n [/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, '$1$2ves'],\n [/([^aeiouy]|qu)y$/i, '$1ies'],\n [/([^ch][ieo][ln])ey$/i, '$1ies'],\n [/(x|ch|ss|sh|zz)$/i, '$1es'],\n [/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, '$1ices'],\n [/\\b((?:tit)?m|l)(?:ice|ouse)$/i, '$1ice'],\n [/(pe)(?:rson|ople)$/i, '$1ople'],\n [/(child)(?:ren)?$/i, '$1ren'],\n [/eaux$/i, '$0'],\n [/m[ae]n$/i, 'men'],\n ['thou', 'you']\n ].forEach(function (rule) {\n return pluralize.addPluralRule(rule[0], rule[1]);\n });\n\n /**\n * Singularization rules.\n */\n [\n [/s$/i, ''],\n [/(ss)$/i, '$1'],\n [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\\w]|^)li)ves$/i, '$1fe'],\n [/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, '$1f'],\n [/ies$/i, 'y'],\n [/\\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i, '$1ie'],\n [/\\b(mon|smil)ies$/i, '$1ey'],\n [/\\b((?:tit)?m|l)ice$/i, '$1ouse'],\n [/(seraph|cherub)im$/i, '$1'],\n [/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i, '$1'],\n [/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i, '$1sis'],\n [/(movie|twelve|abuse|e[mn]u)s$/i, '$1'],\n [/(test)(?:is|es)$/i, '$1is'],\n [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, '$1us'],\n [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i, '$1um'],\n [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i, '$1on'],\n [/(alumn|alg|vertebr)ae$/i, '$1a'],\n [/(cod|mur|sil|vert|ind)ices$/i, '$1ex'],\n [/(matr|append)ices$/i, '$1ix'],\n [/(pe)(rson|ople)$/i, '$1rson'],\n [/(child)ren$/i, '$1'],\n [/(eau)x?$/i, '$1'],\n [/men$/i, 'man']\n ].forEach(function (rule) {\n return pluralize.addSingularRule(rule[0], rule[1]);\n });\n\n /**\n * Uncountable rules.\n */\n [\n // Singular words with no plurals.\n 'adulthood',\n 'advice',\n 'agenda',\n 'aid',\n 'aircraft',\n 'alcohol',\n 'ammo',\n 'analytics',\n 'anime',\n 'athletics',\n 'audio',\n 'bison',\n 'blood',\n 'bream',\n 'buffalo',\n 'butter',\n 'carp',\n 'cash',\n 'chassis',\n 'chess',\n 'clothing',\n 'cod',\n 'commerce',\n 'cooperation',\n 'corps',\n 'debris',\n 'diabetes',\n 'digestion',\n 'elk',\n 'energy',\n 'equipment',\n 'excretion',\n 'expertise',\n 'firmware',\n 'flounder',\n 'fun',\n 'gallows',\n 'garbage',\n 'graffiti',\n 'hardware',\n 'headquarters',\n 'health',\n 'herpes',\n 'highjinks',\n 'homework',\n 'housework',\n 'information',\n 'jeans',\n 'justice',\n 'kudos',\n 'labour',\n 'literature',\n 'machinery',\n 'mackerel',\n 'mail',\n 'media',\n 'mews',\n 'moose',\n 'music',\n 'mud',\n 'manga',\n 'news',\n 'only',\n 'personnel',\n 'pike',\n 'plankton',\n 'pliers',\n 'police',\n 'pollution',\n 'premises',\n 'rain',\n 'research',\n 'rice',\n 'salmon',\n 'scissors',\n 'series',\n 'sewage',\n 'shambles',\n 'shrimp',\n 'software',\n 'species',\n 'staff',\n 'swine',\n 'tennis',\n 'traffic',\n 'transportation',\n 'trout',\n 'tuna',\n 'wealth',\n 'welfare',\n 'whiting',\n 'wildebeest',\n 'wildlife',\n 'you',\n /pok[e\u00E9]mon$/i,\n // Regexes.\n /[^aeiou]ese$/i, // \"chinese\", \"japanese\"\n /deer$/i, // \"deer\", \"reindeer\"\n /fish$/i, // \"fish\", \"blowfish\", \"angelfish\"\n /measles$/i,\n /o[iu]s$/i, // \"carnivorous\"\n /pox$/i, // \"chickpox\", \"smallpox\"\n /sheep$/i\n ].forEach(pluralize.addUncountableRule);\n\n return pluralize;\n});\n", "/* eslint-disable local-rules/disallow-side-effects */\n/**\n * Keep references on console methods to avoid triggering patched behaviors\n *\n * NB: in some setup, console could already be patched by another SDK.\n * In this case, some display messages can be sent by the other SDK\n * but we should be safe from infinite loop nonetheless.\n */\n\nexport const ConsoleApiName = {\n log: 'log',\n debug: 'debug',\n info: 'info',\n warn: 'warn',\n error: 'error',\n} as const\n\nexport type ConsoleApiName = (typeof ConsoleApiName)[keyof typeof ConsoleApiName]\n\ninterface Display {\n debug: typeof console.debug\n log: typeof console.log\n info: typeof console.info\n warn: typeof console.warn\n error: typeof console.error\n}\n\n/**\n * When building JS bundles, some users might use a plugin[1] or configuration[2] to remove\n * \"console.*\" references. This causes some issue as we expect `console.*` to be defined.\n * As a workaround, let's use a variable alias, so those expressions won't be taken into account by\n * simple static analysis.\n *\n * [1]: https://babeljs.io/docs/babel-plugin-transform-remove-console/\n * [2]: https://github.com/terser/terser#compress-options (look for drop_console)\n */\nexport const globalConsole = console\n\nexport const originalConsoleMethods = {} as Display\nObject.keys(ConsoleApiName).forEach((name) => {\n originalConsoleMethods[name as ConsoleApiName] = globalConsole[name as ConsoleApiName]\n})\n\nconst PREFIX = 'Datadog Browser SDK:'\n\nexport const display: Display = {\n debug: originalConsoleMethods.debug.bind(globalConsole, PREFIX),\n log: originalConsoleMethods.log.bind(globalConsole, PREFIX),\n info: originalConsoleMethods.info.bind(globalConsole, PREFIX),\n warn: originalConsoleMethods.warn.bind(globalConsole, PREFIX),\n error: originalConsoleMethods.error.bind(globalConsole, PREFIX),\n}\n\nexport const DOCS_ORIGIN = 'https://docs.datadoghq.com'\nexport const DOCS_TROUBLESHOOTING = `${DOCS_ORIGIN}/real_user_monitoring/browser/troubleshooting`\nexport const MORE_DETAILS = 'More details:'\n", "import { display } from './display'\n\nexport function catchUserErrors(fn: (...args: Args) => R, errorMsg: string) {\n return (...args: Args) => {\n try {\n return fn(...args)\n } catch (err) {\n display.error(errorMsg, err)\n }\n }\n}\n", "/**\n * Return true if the draw is successful\n * @param threshold between 0 and 100\n */\nexport function performDraw(threshold: number): boolean {\n return threshold !== 0 && Math.random() * 100 <= threshold\n}\n\nexport function round(num: number, decimals: 0 | 1 | 2 | 3 | 4) {\n return +num.toFixed(decimals)\n}\n\nexport function isPercentage(value: unknown) {\n return isNumber(value) && value >= 0 && value <= 100\n}\n\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number'\n}\n", "import { isNumber, round } from './numberUtils'\n\nexport const ONE_SECOND = 1000\nexport const ONE_MINUTE = 60 * ONE_SECOND\nexport const ONE_HOUR = 60 * ONE_MINUTE\nexport const ONE_DAY = 24 * ONE_HOUR\nexport const ONE_YEAR = 365 * ONE_DAY\n\nexport type Duration = number & { d: 'Duration in ms' }\nexport type ServerDuration = number & { s: 'Duration in ns' }\nexport type TimeStamp = number & { t: 'Epoch time' }\nexport type RelativeTime = number & { r: 'Time relative to navigation start' } & { d: 'Duration in ms' }\nexport type ClocksState = { relative: RelativeTime; timeStamp: TimeStamp }\n\nexport function relativeToClocks(relative: RelativeTime) {\n return { relative, timeStamp: getCorrectedTimeStamp(relative) }\n}\n\nexport function timeStampToClocks(timeStamp: TimeStamp) {\n return { relative: getRelativeTime(timeStamp), timeStamp }\n}\n\nfunction getCorrectedTimeStamp(relativeTime: RelativeTime) {\n const correctedOrigin = (dateNow() - performance.now()) as TimeStamp\n // apply correction only for positive drift\n if (correctedOrigin > getNavigationStart()) {\n return Math.round(addDuration(correctedOrigin, relativeTime)) as TimeStamp\n }\n return getTimeStamp(relativeTime)\n}\n\nexport function currentDrift() {\n return Math.round(dateNow() - addDuration(getNavigationStart(), performance.now() as Duration))\n}\n\nexport function toServerDuration(duration: Duration): ServerDuration\nexport function toServerDuration(duration: Duration | undefined): ServerDuration | undefined\nexport function toServerDuration(duration: Duration | undefined) {\n if (!isNumber(duration)) {\n return duration\n }\n return round(duration * 1e6, 0) as ServerDuration\n}\n\nexport function dateNow() {\n // Do not use `Date.now` because sometimes websites are wrongly \"polyfilling\" it. For example, we\n // had some users using a very old version of `datejs`, which patched `Date.now` to return a Date\n // instance instead of a timestamp[1]. Those users are unlikely to fix this, so let's handle this\n // case ourselves.\n // [1]: https://github.com/datejs/Datejs/blob/97f5c7c58c5bc5accdab8aa7602b6ac56462d778/src/core-debug.js#L14-L16\n return new Date().getTime()\n}\n\nexport function timeStampNow() {\n return dateNow() as TimeStamp\n}\n\nexport function relativeNow() {\n return performance.now() as RelativeTime\n}\n\nexport function clocksNow() {\n return { relative: relativeNow(), timeStamp: timeStampNow() }\n}\n\nexport function clocksOrigin() {\n return { relative: 0 as RelativeTime, timeStamp: getNavigationStart() }\n}\n\nexport function elapsed(start: TimeStamp, end: TimeStamp): Duration\nexport function elapsed(start: RelativeTime, end: RelativeTime): Duration\nexport function elapsed(start: number, end: number) {\n return (end - start) as Duration\n}\n\nexport function addDuration(a: TimeStamp, b: Duration): TimeStamp\nexport function addDuration(a: RelativeTime, b: Duration): RelativeTime\nexport function addDuration(a: Duration, b: Duration): Duration\nexport function addDuration(a: number, b: number) {\n return a + b\n}\n\n// Get the time since the navigation was started.\nexport function getRelativeTime(timestamp: TimeStamp) {\n return (timestamp - getNavigationStart()) as RelativeTime\n}\n\nexport function getTimeStamp(relativeTime: RelativeTime) {\n return Math.round(addDuration(getNavigationStart(), relativeTime)) as TimeStamp\n}\n\nexport function looksLikeRelativeTime(time: RelativeTime | TimeStamp): time is RelativeTime {\n return time < ONE_YEAR\n}\n\n/**\n * Navigation start slightly change on some rare cases\n */\nlet navigationStart: TimeStamp | undefined\n\n/**\n * Notes: this does not use `performance.timeOrigin` because:\n * - It doesn't seem to reflect the actual time on which the navigation has started: it may be much farther in the past,\n * at least in Firefox 71. (see: https://bugzilla.mozilla.org/show_bug.cgi?id=1429926)\n * - It is not supported in Safari <15\n */\nfunction getNavigationStart() {\n if (navigationStart === undefined) {\n navigationStart = performance.timing.navigationStart as TimeStamp\n }\n return navigationStart\n}\n", "export const ONE_KIBI_BYTE = 1024\nexport const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE\n\n// eslint-disable-next-line no-control-regex\nconst HAS_MULTI_BYTES_CHARACTERS = /[^\\u0000-\\u007F]/\n\nexport function computeBytesCount(candidate: string): number {\n // Accurate bytes count computations can degrade performances when there is a lot of events to process\n if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {\n return candidate.length\n }\n\n if (window.TextEncoder !== undefined) {\n return new TextEncoder().encode(candidate).length\n }\n\n return new Blob([candidate]).size\n}\n\nexport function concatBuffers(buffers: Uint8Array[]): Uint8Array {\n const length = buffers.reduce((total, buffer) => total + buffer.length, 0)\n const result: Uint8Array = new Uint8Array(length)\n let offset = 0\n for (const buffer of buffers) {\n result.set(buffer, offset)\n offset += buffer.length\n }\n return result\n}\n", "export function shallowClone(object: T): T & Record {\n return { ...object } as T & Record\n}\n\nexport function objectHasValue(object: T, value: unknown): value is T[keyof T] {\n return Object.keys(object).some((key) => object[key] === value)\n}\n\nexport function isEmptyObject(object: object) {\n return Object.keys(object).length === 0\n}\n\nexport function mapValues(object: { [key: string]: A }, fn: (arg: A) => B) {\n const newObject: { [key: string]: B } = {}\n for (const key of Object.keys(object)) {\n newObject[key] = fn(object[key])\n }\n return newObject\n}\n", "/**\n * inspired by https://mathiasbynens.be/notes/globalthis\n */\n\nexport function getGlobalObject(): T {\n if (typeof globalThis === 'object') {\n return globalThis as unknown as T\n }\n Object.defineProperty(Object.prototype, '_dd_temp_', {\n get() {\n return this as object\n },\n configurable: true,\n })\n // @ts-ignore _dd_temp is defined using defineProperty\n let globalObject: unknown = _dd_temp_\n // @ts-ignore _dd_temp is defined using defineProperty\n delete Object.prototype._dd_temp_\n if (typeof globalObject !== 'object') {\n // on safari _dd_temp_ is available on window but not globally\n // fallback on other browser globals check\n if (typeof self === 'object') {\n globalObject = self\n } else if (typeof window === 'object') {\n globalObject = window\n } else {\n globalObject = {}\n }\n }\n return globalObject as T\n}\n", "import { getGlobalObject } from './getGlobalObject'\n\nexport interface BrowserWindowWithZoneJs extends Window {\n Zone?: {\n // All Zone.js versions expose the __symbol__ method, but we observed that some website have a\n // 'Zone' global variable unrelated to Zone.js, so let's consider this method optional\n // nonetheless.\n __symbol__?: (name: string) => string\n }\n}\n\n/**\n * Gets the original value for a DOM API that was potentially patched by Zone.js.\n *\n * Zone.js[1] is a library that patches a bunch of JS and DOM APIs. It usually stores the original\n * value of the patched functions/constructors/methods in a hidden property prefixed by\n * __zone_symbol__.\n *\n * In multiple occasions, we observed that Zone.js is the culprit of important issues leading to\n * browser resource exhaustion (memory leak, high CPU usage). This method is used as a workaround to\n * use the original DOM API instead of the one patched by Zone.js.\n *\n * [1]: https://github.com/angular/angular/tree/main/packages/zone.js\n */\nexport function getZoneJsOriginalValue(\n target: Target,\n name: Name\n): Target[Name] {\n const browserWindow = getGlobalObject()\n let original: Target[Name] | undefined\n if (browserWindow.Zone && typeof browserWindow.Zone.__symbol__ === 'function') {\n original = (target as any)[browserWindow.Zone.__symbol__(name)]\n }\n if (!original) {\n original = target[name]\n }\n return original\n}\n", "import { display } from './display'\n\nlet onMonitorErrorCollected: undefined | ((error: unknown) => void)\nlet debugMode = false\n\nexport function startMonitorErrorCollection(newOnMonitorErrorCollected: (error: unknown) => void) {\n onMonitorErrorCollected = newOnMonitorErrorCollected\n}\n\nexport function setDebugMode(newDebugMode: boolean) {\n debugMode = newDebugMode\n}\n\nexport function resetMonitor() {\n onMonitorErrorCollected = undefined\n debugMode = false\n}\n\nexport function monitored unknown>(\n _: any,\n __: string,\n descriptor: TypedPropertyDescriptor\n) {\n const originalMethod = descriptor.value!\n descriptor.value = function (this: any, ...args: Parameters) {\n const decorated = onMonitorErrorCollected ? monitor(originalMethod) : originalMethod\n return decorated.apply(this, args) as ReturnType\n } as T\n}\n\nexport function monitor any>(fn: T): T {\n return function (this: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return callMonitored(fn, this, arguments as unknown as Parameters)\n } as unknown as T // consider output type has input type\n}\n\nexport function callMonitored any>(\n fn: T,\n context: ThisParameterType,\n args: Parameters\n): ReturnType | undefined\nexport function callMonitored any>(fn: T): ReturnType | undefined\nexport function callMonitored any>(\n fn: T,\n context?: any,\n args?: any\n): ReturnType | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return fn.apply(context, args)\n } catch (e) {\n monitorError(e)\n }\n}\n\nexport function monitorError(e: unknown) {\n displayIfDebugEnabled(e)\n if (onMonitorErrorCollected) {\n try {\n onMonitorErrorCollected(e)\n } catch (e) {\n displayIfDebugEnabled(e)\n }\n }\n}\n\nexport function displayIfDebugEnabled(...args: any[]) {\n if (debugMode) {\n display.error('[MONITOR]', ...args)\n }\n}\n", "import { getZoneJsOriginalValue } from './getZoneJsOriginalValue'\nimport { monitor } from './monitor'\nimport { getGlobalObject } from './getGlobalObject'\n\nexport type TimeoutId = ReturnType\n\nexport function setTimeout(callback: () => void, delay?: number): TimeoutId {\n return getZoneJsOriginalValue(getGlobalObject(), 'setTimeout')(monitor(callback), delay)\n}\n\nexport function clearTimeout(timeoutId: TimeoutId | undefined) {\n getZoneJsOriginalValue(getGlobalObject(), 'clearTimeout')(timeoutId)\n}\n\nexport function setInterval(callback: () => void, delay?: number): TimeoutId {\n return getZoneJsOriginalValue(getGlobalObject(), 'setInterval')(monitor(callback), delay)\n}\n\nexport function clearInterval(timeoutId: TimeoutId | undefined) {\n getZoneJsOriginalValue(getGlobalObject(), 'clearInterval')(timeoutId)\n}\n", "export interface Subscription {\n unsubscribe: () => void\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport class Observable {\n private observers: Array<(data: T) => void> = []\n private onLastUnsubscribe?: () => void\n\n constructor(private onFirstSubscribe?: (observable: Observable) => (() => void) | void) {}\n\n subscribe(f: (data: T) => void): Subscription {\n this.observers.push(f)\n if (this.observers.length === 1 && this.onFirstSubscribe) {\n this.onLastUnsubscribe = this.onFirstSubscribe(this) || undefined\n }\n return {\n unsubscribe: () => {\n this.observers = this.observers.filter((other) => f !== other)\n if (!this.observers.length && this.onLastUnsubscribe) {\n this.onLastUnsubscribe()\n }\n },\n }\n }\n\n notify(data: T) {\n this.observers.forEach((observer) => observer(data))\n }\n}\n\nexport function mergeObservables(...observables: Array>) {\n return new Observable((globalObservable) => {\n const subscriptions: Subscription[] = observables.map((observable) =>\n observable.subscribe((data) => globalObservable.notify(data))\n )\n return () => subscriptions.forEach((subscription) => subscription.unsubscribe())\n })\n}\n", "import type { TimeoutId } from '../timer'\nimport { setTimeout, clearTimeout } from '../timer'\n\n// use lodash API\nexport function throttle void>(\n fn: T,\n wait: number,\n options?: { leading?: boolean; trailing?: boolean }\n) {\n const needLeadingExecution = options && options.leading !== undefined ? options.leading : true\n const needTrailingExecution = options && options.trailing !== undefined ? options.trailing : true\n let inWaitPeriod = false\n let pendingExecutionWithParameters: Parameters | undefined\n let pendingTimeoutId: TimeoutId\n\n return {\n throttled: (...parameters: Parameters) => {\n if (inWaitPeriod) {\n pendingExecutionWithParameters = parameters\n return\n }\n if (needLeadingExecution) {\n fn(...parameters)\n } else {\n pendingExecutionWithParameters = parameters\n }\n inWaitPeriod = true\n pendingTimeoutId = setTimeout(() => {\n if (needTrailingExecution && pendingExecutionWithParameters) {\n fn(...pendingExecutionWithParameters)\n }\n inWaitPeriod = false\n pendingExecutionWithParameters = undefined\n }, wait)\n },\n cancel: () => {\n clearTimeout(pendingTimeoutId)\n inWaitPeriod = false\n pendingExecutionWithParameters = undefined\n },\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nexport function noop() {}\n", "/**\n * UUID v4\n * from https://gist.github.com/jed/982883\n */\nexport function generateUUID(placeholder?: string): string {\n return placeholder\n ? // eslint-disable-next-line no-bitwise\n (parseInt(placeholder, 10) ^ ((Math.random() * 16) >> (parseInt(placeholder, 10) / 4))).toString(16)\n : `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, generateUUID)\n}\n\nconst COMMA_SEPARATED_KEY_VALUE = /([\\w-]+)\\s*=\\s*([^;]+)/g\n\nexport function findCommaSeparatedValue(rawString: string, name: string): string | undefined {\n COMMA_SEPARATED_KEY_VALUE.lastIndex = 0\n while (true) {\n const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString)\n if (match) {\n if (match[1] === name) {\n return match[2]\n }\n } else {\n break\n }\n }\n}\n\nexport function findCommaSeparatedValues(rawString: string): Map {\n const result = new Map()\n COMMA_SEPARATED_KEY_VALUE.lastIndex = 0\n while (true) {\n const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString)\n if (match) {\n result.set(match[1], match[2])\n } else {\n break\n }\n }\n return result\n}\n\nexport function safeTruncate(candidate: string, length: number, suffix = '') {\n const lastChar = candidate.charCodeAt(length - 1)\n const isLastCharSurrogatePair = lastChar >= 0xd800 && lastChar <= 0xdbff\n const correctedLength = isLastCharSurrogatePair ? length + 1 : length\n\n if (candidate.length <= correctedLength) {\n return candidate\n }\n\n return `${candidate.slice(0, correctedLength)}${suffix}`\n}\n", "// Exported only for tests\nexport const enum Browser {\n CHROMIUM,\n SAFARI,\n OTHER,\n}\n\nexport function isChromium() {\n return detectBrowserCached() === Browser.CHROMIUM\n}\n\nexport function isSafari() {\n return detectBrowserCached() === Browser.SAFARI\n}\n\nlet browserCache: Browser | undefined\nfunction detectBrowserCached() {\n return browserCache ?? (browserCache = detectBrowser())\n}\n\n// Exported only for tests\nexport function detectBrowser(browserWindow: Window = window) {\n const userAgent = browserWindow.navigator.userAgent\n if ((browserWindow as any).chrome || /HeadlessChrome/.test(userAgent)) {\n return Browser.CHROMIUM\n }\n\n if (\n // navigator.vendor is deprecated, but it is the most resilient way we found to detect\n // \"Apple maintained browsers\" (AKA Safari). If one day it gets removed, we still have the\n // useragent test as a semi-working fallback.\n browserWindow.navigator.vendor?.indexOf('Apple') === 0 ||\n (/safari/i.test(userAgent) && !/chrome|android/i.test(userAgent))\n ) {\n return Browser.SAFARI\n }\n\n return Browser.OTHER\n}\n", "import { display } from '../tools/display'\nimport { ONE_MINUTE, ONE_SECOND } from '../tools/utils/timeUtils'\nimport { findCommaSeparatedValue, findCommaSeparatedValues, generateUUID } from '../tools/utils/stringUtils'\n\nexport interface CookieOptions {\n secure?: boolean\n crossSite?: boolean\n partitioned?: boolean\n domain?: string\n}\n\nexport function setCookie(name: string, value: string, expireDelay: number = 0, options?: CookieOptions) {\n const date = new Date()\n date.setTime(date.getTime() + expireDelay)\n const expires = `expires=${date.toUTCString()}`\n const sameSite = options && options.crossSite ? 'none' : 'strict'\n const domain = options && options.domain ? `;domain=${options.domain}` : ''\n const secure = options && options.secure ? ';secure' : ''\n const partitioned = options && options.partitioned ? ';partitioned' : ''\n document.cookie = `${name}=${value};${expires};path=/;samesite=${sameSite}${domain}${secure}${partitioned}`\n}\n\nexport function getCookie(name: string) {\n return findCommaSeparatedValue(document.cookie, name)\n}\n\nlet initCookieParsed: Map | undefined\n\n/**\n * Returns a cached value of the cookie. Use this during SDK initialization (and whenever possible)\n * to avoid accessing document.cookie multiple times.\n */\nexport function getInitCookie(name: string) {\n if (!initCookieParsed) {\n initCookieParsed = findCommaSeparatedValues(document.cookie)\n }\n return initCookieParsed.get(name)\n}\n\nexport function resetInitCookies() {\n initCookieParsed = undefined\n}\n\nexport function deleteCookie(name: string, options?: CookieOptions) {\n setCookie(name, '', 0, options)\n}\n\nexport function areCookiesAuthorized(options: CookieOptions): boolean {\n if (document.cookie === undefined || document.cookie === null) {\n return false\n }\n try {\n // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during\n // the test cookie lifetime\n const testCookieName = `dd_cookie_test_${generateUUID()}`\n const testCookieValue = 'test'\n setCookie(testCookieName, testCookieValue, ONE_MINUTE, options)\n const isCookieCorrectlySet = getCookie(testCookieName) === testCookieValue\n deleteCookie(testCookieName, options)\n return isCookieCorrectlySet\n } catch (error) {\n display.error(error)\n return false\n }\n}\n\n/**\n * No API to retrieve it, number of levels for subdomain and suffix are unknown\n * strategy: find the minimal domain on which cookies are allowed to be set\n * https://web.dev/same-site-same-origin/#site\n */\nlet getCurrentSiteCache: string | undefined\nexport function getCurrentSite() {\n if (getCurrentSiteCache === undefined) {\n // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during\n // the test cookie lifetime\n const testCookieName = `dd_site_test_${generateUUID()}`\n const testCookieValue = 'test'\n\n const domainLevels = window.location.hostname.split('.')\n let candidateDomain = domainLevels.pop()!\n while (domainLevels.length && !getCookie(testCookieName)) {\n candidateDomain = `${domainLevels.pop()!}.${candidateDomain}`\n setCookie(testCookieName, testCookieValue, ONE_SECOND, { domain: candidateDomain })\n }\n deleteCookie(testCookieName, { domain: candidateDomain })\n getCurrentSiteCache = candidateDomain\n }\n return getCurrentSiteCache\n}\n", "import type { CookieOptions } from '../../../browser/cookie'\nimport type { SessionPersistence } from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\n\nexport const SESSION_STORE_KEY = '_dd_s'\n\nexport type SessionStoreStrategyType =\n | { type: typeof SessionPersistence.COOKIE; cookieOptions: CookieOptions }\n | { type: typeof SessionPersistence.LOCAL_STORAGE }\n\nexport interface SessionStoreStrategy {\n isLockEnabled: boolean\n persistSession: (session: SessionState) => void\n retrieveSession: () => SessionState\n expireSession: (previousSessionState: SessionState) => void\n}\n", "export function findLast(\n array: T[],\n predicate: (item: T, index: number, array: T[]) => item is S\n): S | undefined {\n for (let i = array.length - 1; i >= 0; i -= 1) {\n const item = array[i]\n if (predicate(item, i, array)) {\n return item\n }\n }\n return undefined\n}\n\n// Keep the following wrapper functions as it can be mangled and will result in smaller bundle size that using\n// the native Object.values and Object.entries directly\n\nexport function objectValues(object: { [key: string]: T }) {\n return Object.values(object)\n}\n\nexport function objectEntries(object: { [key: string]: T }): Array<[string, T]> {\n return Object.entries(object)\n}\n", "import { ONE_HOUR, ONE_MINUTE, ONE_YEAR } from '../../tools/utils/timeUtils'\n\nexport const SESSION_TIME_OUT_DELAY = 4 * ONE_HOUR\nexport const SESSION_EXPIRATION_DELAY = 15 * ONE_MINUTE\nexport const SESSION_COOKIE_EXPIRATION_DELAY = ONE_YEAR\n\nexport const SessionPersistence = {\n COOKIE: 'cookie',\n LOCAL_STORAGE: 'local-storage',\n} as const\nexport type SessionPersistence = (typeof SessionPersistence)[keyof typeof SessionPersistence]\n", "export const SESSION_ENTRY_REGEXP = /^([a-zA-Z]+)=([a-z0-9-]+)$/\nexport const SESSION_ENTRY_SEPARATOR = '&'\n\nexport function isValidSessionString(sessionString: string | undefined | null): sessionString is string {\n return (\n !!sessionString &&\n (sessionString.indexOf(SESSION_ENTRY_SEPARATOR) !== -1 || SESSION_ENTRY_REGEXP.test(sessionString))\n )\n}\n", "import { isEmptyObject } from '../../tools/utils/objectUtils'\nimport { objectEntries } from '../../tools/utils/polyfills'\nimport { dateNow } from '../../tools/utils/timeUtils'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { Configuration } from '../configuration'\nimport { SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from './sessionConstants'\nimport { isValidSessionString, SESSION_ENTRY_REGEXP, SESSION_ENTRY_SEPARATOR } from './sessionStateValidation'\nexport const EXPIRED = '1'\n\nexport interface SessionState {\n id?: string\n created?: string\n expire?: string\n isExpired?: typeof EXPIRED\n\n [key: string]: string | undefined\n}\n\nexport function getExpiredSessionState(\n previousSessionState: SessionState | undefined,\n configuration: Configuration\n): SessionState {\n const expiredSessionState: SessionState = {\n isExpired: EXPIRED,\n }\n if (configuration.trackAnonymousUser) {\n if (previousSessionState?.anonymousId) {\n expiredSessionState.anonymousId = previousSessionState?.anonymousId\n } else {\n expiredSessionState.anonymousId = generateUUID()\n }\n }\n return expiredSessionState\n}\n\nexport function isSessionInNotStartedState(session: SessionState) {\n return isEmptyObject(session)\n}\n\nexport function isSessionStarted(session: SessionState) {\n return !isSessionInNotStartedState(session)\n}\n\nexport function isSessionInExpiredState(session: SessionState) {\n return session.isExpired !== undefined || !isActiveSession(session)\n}\n\n// An active session is a session in either `Tracked` or `NotTracked` state\nfunction isActiveSession(sessionState: SessionState) {\n // created and expire can be undefined for versions which was not storing them\n // these checks could be removed when older versions will not be available/live anymore\n return (\n (sessionState.created === undefined || dateNow() - Number(sessionState.created) < SESSION_TIME_OUT_DELAY) &&\n (sessionState.expire === undefined || dateNow() < Number(sessionState.expire))\n )\n}\n\nexport function expandSessionState(session: SessionState) {\n session.expire = String(dateNow() + SESSION_EXPIRATION_DELAY)\n}\n\nexport function toSessionString(session: SessionState) {\n return (\n objectEntries(session)\n // we use `aid` as a key for anonymousId\n .map(([key, value]) => (key === 'anonymousId' ? `aid=${value}` : `${key}=${value}`))\n .join(SESSION_ENTRY_SEPARATOR)\n )\n}\n\nexport function toSessionState(sessionString: string | undefined | null) {\n const session: SessionState = {}\n if (isValidSessionString(sessionString)) {\n sessionString.split(SESSION_ENTRY_SEPARATOR).forEach((entry) => {\n const matches = SESSION_ENTRY_REGEXP.exec(entry)\n if (matches !== null) {\n const [, key, value] = matches\n if (key === 'aid') {\n // we use `aid` as a key for anonymousId\n session.anonymousId = value\n } else {\n session[key] = value\n }\n }\n })\n }\n return session\n}\n", "import { getInitCookie } from '../../browser/cookie'\nimport type { SessionStoreStrategy } from './storeStrategies/sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './storeStrategies/sessionStoreStrategy'\nimport type { SessionState } from './sessionState'\nimport { expandSessionState, isSessionStarted } from './sessionState'\n\nexport const OLD_SESSION_COOKIE_NAME = '_dd'\nexport const OLD_RUM_COOKIE_NAME = '_dd_r'\nexport const OLD_LOGS_COOKIE_NAME = '_dd_l'\n\n// duplicate values to avoid dependency issues\nexport const RUM_SESSION_KEY = 'rum'\nexport const LOGS_SESSION_KEY = 'logs'\n\n/**\n * This migration should remain in the codebase as long as older versions are available/live\n * to allow older sdk versions to be upgraded to newer versions without compatibility issues.\n */\nexport function tryOldCookiesMigration(cookieStoreStrategy: SessionStoreStrategy) {\n const sessionString = getInitCookie(SESSION_STORE_KEY)\n if (!sessionString) {\n const oldSessionId = getInitCookie(OLD_SESSION_COOKIE_NAME)\n const oldRumType = getInitCookie(OLD_RUM_COOKIE_NAME)\n const oldLogsType = getInitCookie(OLD_LOGS_COOKIE_NAME)\n const session: SessionState = {}\n\n if (oldSessionId) {\n session.id = oldSessionId\n }\n if (oldLogsType && /^[01]$/.test(oldLogsType)) {\n session[LOGS_SESSION_KEY] = oldLogsType\n }\n if (oldRumType && /^[012]$/.test(oldRumType)) {\n session[RUM_SESSION_KEY] = oldRumType\n }\n\n if (isSessionStarted(session)) {\n expandSessionState(session)\n cookieStoreStrategy.persistSession(session)\n }\n }\n}\n", "import { isChromium } from '../../../tools/utils/browserDetection'\nimport type { CookieOptions } from '../../../browser/cookie'\nimport { getCurrentSite, areCookiesAuthorized, getCookie, setCookie } from '../../../browser/cookie'\nimport type { InitConfiguration, Configuration } from '../../configuration'\nimport { tryOldCookiesMigration } from '../oldCookiesMigration'\nimport {\n SESSION_COOKIE_EXPIRATION_DELAY,\n SESSION_EXPIRATION_DELAY,\n SESSION_TIME_OUT_DELAY,\n SessionPersistence,\n} from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\nimport { toSessionString, toSessionState, getExpiredSessionState } from '../sessionState'\nimport type { SessionStoreStrategy, SessionStoreStrategyType } from './sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './sessionStoreStrategy'\n\nexport function selectCookieStrategy(initConfiguration: InitConfiguration): SessionStoreStrategyType | undefined {\n const cookieOptions = buildCookieOptions(initConfiguration)\n return areCookiesAuthorized(cookieOptions) ? { type: SessionPersistence.COOKIE, cookieOptions } : undefined\n}\n\nexport function initCookieStrategy(configuration: Configuration, cookieOptions: CookieOptions): SessionStoreStrategy {\n const cookieStore = {\n /**\n * Lock strategy allows mitigating issues due to concurrent access to cookie.\n * This issue concerns only chromium browsers and enabling this on firefox increases cookie write failures.\n */\n isLockEnabled: isChromium(),\n persistSession: persistSessionCookie(cookieOptions),\n retrieveSession: retrieveSessionCookie,\n expireSession: (sessionState: SessionState) => expireSessionCookie(cookieOptions, sessionState, configuration),\n }\n\n tryOldCookiesMigration(cookieStore)\n\n return cookieStore\n}\n\nfunction persistSessionCookie(options: CookieOptions) {\n return (session: SessionState) => {\n setCookie(SESSION_STORE_KEY, toSessionString(session), SESSION_EXPIRATION_DELAY, options)\n }\n}\n\nfunction expireSessionCookie(options: CookieOptions, sessionState: SessionState, configuration: Configuration) {\n const expiredSessionState = getExpiredSessionState(sessionState, configuration)\n // we do not extend cookie expiration date\n setCookie(\n SESSION_STORE_KEY,\n toSessionString(expiredSessionState),\n configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : SESSION_TIME_OUT_DELAY,\n options\n )\n}\n\nfunction retrieveSessionCookie(): SessionState {\n const sessionString = getCookie(SESSION_STORE_KEY)\n const sessionState = toSessionState(sessionString)\n return sessionState\n}\n\nexport function buildCookieOptions(initConfiguration: InitConfiguration) {\n const cookieOptions: CookieOptions = {}\n\n cookieOptions.secure =\n !!initConfiguration.useSecureSessionCookie || !!initConfiguration.usePartitionedCrossSiteSessionCookie\n cookieOptions.crossSite = !!initConfiguration.usePartitionedCrossSiteSessionCookie\n cookieOptions.partitioned = !!initConfiguration.usePartitionedCrossSiteSessionCookie\n\n if (initConfiguration.trackSessionAcrossSubdomains) {\n cookieOptions.domain = getCurrentSite()\n }\n\n return cookieOptions\n}\n", "import { generateUUID } from '../../../tools/utils/stringUtils'\nimport type { Configuration } from '../../configuration'\nimport { SessionPersistence } from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\nimport { toSessionString, toSessionState, getExpiredSessionState } from '../sessionState'\nimport type { SessionStoreStrategy, SessionStoreStrategyType } from './sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './sessionStoreStrategy'\n\nconst LOCAL_STORAGE_TEST_KEY = '_dd_test_'\n\nexport function selectLocalStorageStrategy(): SessionStoreStrategyType | undefined {\n try {\n const id = generateUUID()\n const testKey = `${LOCAL_STORAGE_TEST_KEY}${id}`\n localStorage.setItem(testKey, id)\n const retrievedId = localStorage.getItem(testKey)\n localStorage.removeItem(testKey)\n return id === retrievedId ? { type: SessionPersistence.LOCAL_STORAGE } : undefined\n } catch {\n return undefined\n }\n}\n\nexport function initLocalStorageStrategy(configuration: Configuration): SessionStoreStrategy {\n return {\n isLockEnabled: false,\n persistSession: persistInLocalStorage,\n retrieveSession: retrieveSessionFromLocalStorage,\n expireSession: (sessionState: SessionState) => expireSessionFromLocalStorage(sessionState, configuration),\n }\n}\n\nfunction persistInLocalStorage(sessionState: SessionState) {\n localStorage.setItem(SESSION_STORE_KEY, toSessionString(sessionState))\n}\n\nfunction retrieveSessionFromLocalStorage(): SessionState {\n const sessionString = localStorage.getItem(SESSION_STORE_KEY)\n return toSessionState(sessionString)\n}\n\nfunction expireSessionFromLocalStorage(previousSessionState: SessionState, configuration: Configuration) {\n persistInLocalStorage(getExpiredSessionState(previousSessionState, configuration))\n}\n", "import { setTimeout } from '../../tools/timer'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { SessionStoreStrategy } from './storeStrategies/sessionStoreStrategy'\nimport type { SessionState } from './sessionState'\nimport { expandSessionState, isSessionInExpiredState } from './sessionState'\n\ntype Operations = {\n process: (sessionState: SessionState) => SessionState | undefined\n after?: (sessionState: SessionState) => void\n}\n\nexport const LOCK_RETRY_DELAY = 10\nexport const LOCK_MAX_TRIES = 100\nconst bufferedOperations: Operations[] = []\nlet ongoingOperations: Operations | undefined\n\nexport function processSessionStoreOperations(\n operations: Operations,\n sessionStoreStrategy: SessionStoreStrategy,\n numberOfRetries = 0\n) {\n const { isLockEnabled, persistSession, expireSession } = sessionStoreStrategy\n const persistWithLock = (session: SessionState) => persistSession({ ...session, lock: currentLock })\n const retrieveStore = () => {\n const session = sessionStoreStrategy.retrieveSession()\n const lock = session.lock\n\n if (session.lock) {\n delete session.lock\n }\n\n return {\n session,\n lock,\n }\n }\n\n if (!ongoingOperations) {\n ongoingOperations = operations\n }\n if (operations !== ongoingOperations) {\n bufferedOperations.push(operations)\n return\n }\n if (isLockEnabled && numberOfRetries >= LOCK_MAX_TRIES) {\n next(sessionStoreStrategy)\n return\n }\n let currentLock: string\n let currentStore = retrieveStore()\n if (isLockEnabled) {\n // if someone has lock, retry later\n if (currentStore.lock) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n // acquire lock\n currentLock = generateUUID()\n persistWithLock(currentStore.session)\n // if lock is not acquired, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n }\n let processedSession = operations.process(currentStore.session)\n if (isLockEnabled) {\n // if lock corrupted after process, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock!) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n }\n if (processedSession) {\n if (isSessionInExpiredState(processedSession)) {\n expireSession(processedSession)\n } else {\n expandSessionState(processedSession)\n if (isLockEnabled) {\n persistWithLock(processedSession)\n } else {\n persistSession(processedSession)\n }\n }\n }\n if (isLockEnabled) {\n // correctly handle lock around expiration would require to handle this case properly at several levels\n // since we don't have evidence of lock issues around expiration, let's just not do the corruption check for it\n if (!(processedSession && isSessionInExpiredState(processedSession))) {\n // if lock corrupted after persist, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock!) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n persistSession(currentStore.session)\n processedSession = currentStore.session\n }\n }\n // call after even if session is not persisted in order to perform operations on\n // up-to-date session state value => the value could have been modified by another tab\n operations.after?.(processedSession || currentStore.session)\n next(sessionStoreStrategy)\n}\n\nfunction retryLater(operations: Operations, sessionStore: SessionStoreStrategy, currentNumberOfRetries: number) {\n setTimeout(() => {\n processSessionStoreOperations(operations, sessionStore, currentNumberOfRetries + 1)\n }, LOCK_RETRY_DELAY)\n}\n\nfunction next(sessionStore: SessionStoreStrategy) {\n ongoingOperations = undefined\n const nextOperations = bufferedOperations.shift()\n if (nextOperations) {\n processSessionStoreOperations(nextOperations, sessionStore)\n }\n}\n", "import { clearInterval, setInterval } from '../../tools/timer'\nimport { Observable } from '../../tools/observable'\nimport { ONE_SECOND, dateNow } from '../../tools/utils/timeUtils'\nimport { throttle } from '../../tools/utils/functionUtils'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { InitConfiguration, Configuration } from '../configuration'\nimport { display } from '../../tools/display'\nimport { selectCookieStrategy, initCookieStrategy } from './storeStrategies/sessionInCookie'\nimport type { SessionStoreStrategyType } from './storeStrategies/sessionStoreStrategy'\nimport {\n getExpiredSessionState,\n isSessionInExpiredState,\n isSessionInNotStartedState,\n isSessionStarted,\n} from './sessionState'\nimport type { SessionState } from './sessionState'\nimport { initLocalStorageStrategy, selectLocalStorageStrategy } from './storeStrategies/sessionInLocalStorage'\nimport { processSessionStoreOperations } from './sessionStoreOperations'\nimport { SessionPersistence } from './sessionConstants'\n\nexport interface SessionStore {\n expandOrRenewSession: () => void\n expandSession: () => void\n getSession: () => SessionState\n restartSession: () => void\n renewObservable: Observable\n expireObservable: Observable\n sessionStateUpdateObservable: Observable<{ previousState: SessionState; newState: SessionState }>\n expire: () => void\n stop: () => void\n updateSessionState: (state: Partial) => void\n}\n\n/**\n * Every second, the storage will be polled to check for any change that can occur\n * to the session state in another browser tab, or another window.\n * This value has been determined from our previous cookie-only implementation.\n */\nexport const STORAGE_POLL_DELAY = ONE_SECOND\n\n/**\n * Selects the correct session store strategy type based on the configuration and storage\n * availability.\n */\nexport function selectSessionStoreStrategyType(\n initConfiguration: InitConfiguration\n): SessionStoreStrategyType | undefined {\n switch (initConfiguration.sessionPersistence) {\n case SessionPersistence.COOKIE:\n return selectCookieStrategy(initConfiguration)\n\n case SessionPersistence.LOCAL_STORAGE:\n return selectLocalStorageStrategy()\n\n case undefined: {\n let sessionStoreStrategyType = selectCookieStrategy(initConfiguration)\n if (!sessionStoreStrategyType && initConfiguration.allowFallbackToLocalStorage) {\n sessionStoreStrategyType = selectLocalStorageStrategy()\n }\n return sessionStoreStrategyType\n }\n\n default:\n display.error(`Invalid session persistence '${String(initConfiguration.sessionPersistence)}'`)\n }\n}\n\n/**\n * Different session concepts:\n * - tracked, the session has an id and is updated along the user navigation\n * - not tracked, the session does not have an id but it is updated along the user navigation\n * - inactive, no session in store or session expired, waiting for a renew session\n */\nexport function startSessionStore(\n sessionStoreStrategyType: SessionStoreStrategyType,\n configuration: Configuration,\n productKey: string,\n computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean }\n): SessionStore {\n const renewObservable = new Observable()\n const expireObservable = new Observable()\n const sessionStateUpdateObservable = new Observable<{ previousState: SessionState; newState: SessionState }>()\n\n const sessionStoreStrategy =\n sessionStoreStrategyType.type === SessionPersistence.COOKIE\n ? initCookieStrategy(configuration, sessionStoreStrategyType.cookieOptions)\n : initLocalStorageStrategy(configuration)\n const { expireSession } = sessionStoreStrategy\n\n const watchSessionTimeoutId = setInterval(watchSession, STORAGE_POLL_DELAY)\n let sessionCache: SessionState\n\n startSession()\n\n const { throttled: throttledExpandOrRenewSession, cancel: cancelExpandOrRenewSession } = throttle(() => {\n processSessionStoreOperations(\n {\n process: (sessionState) => {\n if (isSessionInNotStartedState(sessionState)) {\n return\n }\n\n const synchronizedSession = synchronizeSession(sessionState)\n expandOrRenewSessionState(synchronizedSession)\n return synchronizedSession\n },\n after: (sessionState) => {\n if (isSessionStarted(sessionState) && !hasSessionInCache()) {\n renewSessionInCache(sessionState)\n }\n sessionCache = sessionState\n },\n },\n sessionStoreStrategy\n )\n }, STORAGE_POLL_DELAY)\n\n function expandSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) => (hasSessionInCache() ? synchronizeSession(sessionState) : undefined),\n },\n sessionStoreStrategy\n )\n }\n\n /**\n * allows two behaviors:\n * - if the session is active, synchronize the session cache without updating the session store\n * - if the session is not active, clear the session store and expire the session cache\n */\n function watchSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) =>\n isSessionInExpiredState(sessionState) ? getExpiredSessionState(sessionState, configuration) : undefined,\n after: synchronizeSession,\n },\n sessionStoreStrategy\n )\n }\n\n function synchronizeSession(sessionState: SessionState) {\n if (isSessionInExpiredState(sessionState)) {\n sessionState = getExpiredSessionState(sessionState, configuration)\n }\n if (hasSessionInCache()) {\n if (isSessionInCacheOutdated(sessionState)) {\n expireSessionInCache()\n } else {\n sessionStateUpdateObservable.notify({ previousState: sessionCache, newState: sessionState })\n sessionCache = sessionState\n }\n }\n return sessionState\n }\n\n function startSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) => {\n if (isSessionInNotStartedState(sessionState)) {\n return getExpiredSessionState(sessionState, configuration)\n }\n },\n after: (sessionState) => {\n sessionCache = sessionState\n },\n },\n sessionStoreStrategy\n )\n }\n\n function expandOrRenewSessionState(sessionState: SessionState) {\n if (isSessionInNotStartedState(sessionState)) {\n return false\n }\n\n const { trackingType, isTracked } = computeSessionState(sessionState[productKey])\n sessionState[productKey] = trackingType\n delete sessionState.isExpired\n if (isTracked && !sessionState.id) {\n sessionState.id = generateUUID()\n sessionState.created = String(dateNow())\n }\n }\n\n function hasSessionInCache() {\n return sessionCache[productKey] !== undefined\n }\n\n function isSessionInCacheOutdated(sessionState: SessionState) {\n return sessionCache.id !== sessionState.id || sessionCache[productKey] !== sessionState[productKey]\n }\n\n function expireSessionInCache() {\n sessionCache = getExpiredSessionState(sessionCache, configuration)\n expireObservable.notify()\n }\n\n function renewSessionInCache(sessionState: SessionState) {\n sessionCache = sessionState\n renewObservable.notify()\n }\n\n function updateSessionState(partialSessionState: Partial) {\n processSessionStoreOperations(\n {\n process: (sessionState) => ({ ...sessionState, ...partialSessionState }),\n after: synchronizeSession,\n },\n sessionStoreStrategy\n )\n }\n\n return {\n expandOrRenewSession: throttledExpandOrRenewSession,\n expandSession,\n getSession: () => sessionCache,\n renewObservable,\n expireObservable,\n sessionStateUpdateObservable,\n restartSession: startSession,\n expire: () => {\n cancelExpandOrRenewSession()\n expireSession(sessionCache)\n synchronizeSession(getExpiredSessionState(sessionCache, configuration))\n },\n stop: () => {\n clearInterval(watchSessionTimeoutId)\n },\n updateSessionState,\n }\n}\n", "import { Observable } from '../tools/observable'\n\nexport const TrackingConsent = {\n GRANTED: 'granted',\n NOT_GRANTED: 'not-granted',\n} as const\nexport type TrackingConsent = (typeof TrackingConsent)[keyof typeof TrackingConsent]\n\nexport interface TrackingConsentState {\n tryToInit: (trackingConsent: TrackingConsent) => void\n update: (trackingConsent: TrackingConsent) => void\n isGranted: () => boolean\n observable: Observable\n}\n\nexport function createTrackingConsentState(currentConsent?: TrackingConsent): TrackingConsentState {\n const observable = new Observable()\n\n return {\n tryToInit(trackingConsent: TrackingConsent) {\n if (!currentConsent) {\n currentConsent = trackingConsent\n }\n },\n update(trackingConsent: TrackingConsent) {\n currentConsent = trackingConsent\n observable.notify()\n },\n isGranted() {\n return currentConsent === TrackingConsent.GRANTED\n },\n observable,\n }\n}\n", "import { noop } from '../utils/functionUtils'\n\n/**\n * Custom implementation of JSON.stringify that ignores some toJSON methods. We need to do that\n * because some sites badly override toJSON on certain objects. Removing all toJSON methods from\n * nested values would be too costly, so we just detach them from the root value, and native classes\n * used to build JSON values (Array and Object).\n *\n * Note: this still assumes that JSON.stringify is correct.\n */\nexport function jsonStringify(\n value: unknown,\n replacer?: Array,\n space?: string | number\n): string | undefined {\n if (typeof value !== 'object' || value === null) {\n return JSON.stringify(value)\n }\n\n // Note: The order matter here. We need to detach toJSON methods on parent classes before their\n // subclasses.\n const restoreObjectPrototypeToJson = detachToJsonMethod(Object.prototype)\n const restoreArrayPrototypeToJson = detachToJsonMethod(Array.prototype)\n const restoreValuePrototypeToJson = detachToJsonMethod(Object.getPrototypeOf(value))\n const restoreValueToJson = detachToJsonMethod(value)\n\n try {\n return JSON.stringify(value, replacer, space)\n } catch {\n return ''\n } finally {\n restoreObjectPrototypeToJson()\n restoreArrayPrototypeToJson()\n restoreValuePrototypeToJson()\n restoreValueToJson()\n }\n}\n\nexport interface ObjectWithToJsonMethod {\n toJSON?: () => unknown\n}\n\nexport function detachToJsonMethod(value: object) {\n const object = value as ObjectWithToJsonMethod\n const objectToJson = object.toJSON\n if (objectToJson) {\n delete object.toJSON\n return () => {\n object.toJSON = objectToJson\n }\n }\n return noop\n}\n", "import { jsonStringify } from '../serialisation/jsonStringify'\n\nexport function normalizeUrl(url: string) {\n return buildUrl(url, location.href).href\n}\n\nexport function isValidUrl(url: string) {\n try {\n return !!buildUrl(url)\n } catch {\n return false\n }\n}\n\nexport function getPathName(url: string) {\n const pathname = buildUrl(url).pathname\n return pathname[0] === '/' ? pathname : `/${pathname}`\n}\n\nexport function buildUrl(url: string, base?: string) {\n const supportedURL = getSupportedUrl()\n if (supportedURL) {\n try {\n return base !== undefined ? new supportedURL(url, base) : new supportedURL(url)\n } catch (error) {\n throw new Error(`Failed to construct URL: ${String(error)} ${jsonStringify({ url, base })!}`)\n }\n }\n if (base === undefined && !/:/.test(url)) {\n throw new Error(`Invalid URL: '${url}'`)\n }\n let doc = document\n const anchorElement = doc.createElement('a')\n if (base !== undefined) {\n doc = document.implementation.createHTMLDocument('')\n const baseElement = doc.createElement('base')\n baseElement.href = base\n doc.head.appendChild(baseElement)\n doc.body.appendChild(anchorElement)\n }\n anchorElement.href = url\n return anchorElement\n}\n\nconst originalURL = URL\nlet isURLSupported: boolean | undefined\nfunction getSupportedUrl(): typeof URL | undefined {\n if (isURLSupported === undefined) {\n try {\n const url = new originalURL('http://test/path')\n isURLSupported = url.href === 'http://test/path'\n } catch {\n isURLSupported = false\n }\n }\n return isURLSupported ? originalURL : undefined\n}\n", "export type Site =\n | 'datadoghq.com'\n | 'us3.datadoghq.com'\n | 'us5.datadoghq.com'\n | 'datadoghq.eu'\n | 'ddog-gov.com'\n | 'ap1.datadoghq.com'\n\nexport const INTAKE_SITE_STAGING: Site = 'datad0g.com' as Site\nexport const INTAKE_SITE_FED_STAGING: Site = 'dd0g-gov.com' as Site\nexport const INTAKE_SITE_US1: Site = 'datadoghq.com'\nexport const INTAKE_SITE_EU1: Site = 'datadoghq.eu'\nexport const INTAKE_SITE_US1_FED: Site = 'ddog-gov.com'\n\nexport const PCI_INTAKE_HOST_US1 = 'pci.browser-intake-datadoghq.com'\nexport const INTAKE_URL_PARAMETERS = ['ddsource', 'ddtags']\n", "import type { Payload } from '../../transport'\nimport { timeStampNow } from '../../tools/utils/timeUtils'\nimport { normalizeUrl } from '../../tools/utils/urlPolyfill'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { InitConfiguration } from './configuration'\nimport { INTAKE_SITE_US1, INTAKE_SITE_FED_STAGING, PCI_INTAKE_HOST_US1 } from './intakeSites'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\n\nexport type TrackType = 'logs' | 'rum' | 'replay' | 'profile'\nexport type ApiType =\n | 'fetch-keepalive'\n | 'fetch'\n | 'beacon'\n // 'manual' reflects that the request have been sent manually, outside of the SDK (ex: via curl or\n // a Node.js script).\n | 'manual'\n\nexport type EndpointBuilder = ReturnType\n\nexport function createEndpointBuilder(\n initConfiguration: InitConfiguration,\n trackType: TrackType,\n configurationTags: string[]\n) {\n const buildUrlWithParameters = createEndpointUrlWithParametersBuilder(initConfiguration, trackType)\n\n return {\n build(api: ApiType, payload: Payload) {\n const parameters = buildEndpointParameters(initConfiguration, trackType, configurationTags, api, payload)\n return buildUrlWithParameters(parameters)\n },\n tags: configurationTags,\n urlPrefix: buildUrlWithParameters(''),\n trackType,\n }\n}\n\n/**\n * Create a function used to build a full endpoint url from provided parameters. The goal of this\n * function is to pre-compute some parts of the URL to avoid re-computing everything on every\n * request, as only parameters are changing.\n */\nfunction createEndpointUrlWithParametersBuilder(\n initConfiguration: InitConfiguration,\n trackType: TrackType\n): (parameters: string) => string {\n const path = `/api/v2/${trackType}`\n const proxy = initConfiguration.proxy\n if (typeof proxy === 'string') {\n const normalizedProxyUrl = normalizeUrl(proxy)\n return (parameters) => `${normalizedProxyUrl}?ddforward=${encodeURIComponent(`${path}?${parameters}`)}`\n }\n if (typeof proxy === 'function') {\n return (parameters) => proxy({ path, parameters })\n }\n const host = buildEndpointHost(trackType, initConfiguration)\n return (parameters) => `https://${host}${path}?${parameters}`\n}\n\nexport function buildEndpointHost(\n trackType: TrackType,\n initConfiguration: InitConfiguration & { usePciIntake?: boolean }\n) {\n const { site = INTAKE_SITE_US1, internalAnalyticsSubdomain } = initConfiguration\n\n if (trackType === 'logs' && initConfiguration.usePciIntake && site === INTAKE_SITE_US1) {\n return PCI_INTAKE_HOST_US1\n }\n\n if (internalAnalyticsSubdomain && site === INTAKE_SITE_US1) {\n return `${internalAnalyticsSubdomain}.${INTAKE_SITE_US1}`\n }\n\n if (site === INTAKE_SITE_FED_STAGING) {\n return `http-intake.logs.${site}`\n }\n\n const domainParts = site.split('.')\n const extension = domainParts.pop()\n return `browser-intake-${domainParts.join('-')}.${extension!}`\n}\n\n/**\n * Build parameters to be used for an intake request. Parameters should be re-built for each\n * request, as they change randomly.\n */\nfunction buildEndpointParameters(\n { clientToken, internalAnalyticsSubdomain }: InitConfiguration,\n trackType: TrackType,\n configurationTags: string[],\n api: ApiType,\n { retry, encoding }: Payload\n) {\n const tags = [`sdk_version:${__BUILD_ENV__SDK_VERSION__}`, `api:${api}`].concat(configurationTags)\n if (retry) {\n tags.push(`retry_count:${retry.count}`, `retry_after:${retry.lastFailureStatus}`)\n }\n\n const parameters = [\n 'ddsource=browser',\n `ddtags=${encodeURIComponent(tags.join(','))}`,\n `dd-api-key=${clientToken}`,\n `dd-evp-origin-version=${encodeURIComponent(__BUILD_ENV__SDK_VERSION__)}`,\n 'dd-evp-origin=browser',\n `dd-request-id=${generateUUID()}`,\n ]\n\n if (encoding) {\n parameters.push(`dd-evp-encoding=${encoding}`)\n }\n\n if (trackType === 'rum') {\n parameters.push(`batch_time=${timeStampNow()}`)\n }\n\n if (internalAnalyticsSubdomain) {\n parameters.reverse()\n }\n\n return parameters.join('&')\n}\n", "import { DOCS_ORIGIN, MORE_DETAILS, display } from '../../tools/display'\nimport type { InitConfiguration } from './configuration'\n\nexport const TAG_SIZE_LIMIT = 200\n\nexport function buildTags(configuration: InitConfiguration): string[] {\n const { env, service, version, datacenter } = configuration\n const tags = []\n\n if (env) {\n tags.push(buildTag('env', env))\n }\n if (service) {\n tags.push(buildTag('service', service))\n }\n if (version) {\n tags.push(buildTag('version', version))\n }\n if (datacenter) {\n tags.push(buildTag('datacenter', datacenter))\n }\n\n return tags\n}\n\nexport function buildTag(key: string, rawValue: string) {\n // See https://docs.datadoghq.com/getting_started/tagging/#defining-tags for tags syntax. Note\n // that the backend may not follow the exact same rules, so we only want to display an informal\n // warning.\n const valueSizeLimit = TAG_SIZE_LIMIT - key.length - 1\n\n if (rawValue.length > valueSizeLimit || hasForbiddenCharacters(rawValue)) {\n display.warn(\n `${key} value doesn't meet tag requirements and will be sanitized. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/tagging/#defining-tags`\n )\n }\n\n // Let the backend do most of the sanitization, but still make sure multiple tags can't be crafted\n // by forging a value containing commas.\n const sanitizedValue = rawValue.replace(/,/g, '_')\n\n return `${key}:${sanitizedValue}`\n}\n\nfunction hasForbiddenCharacters(rawValue: string) {\n // Unicode property escapes is not supported in all browsers, so we use a try/catch.\n // Todo: Remove the try/catch when dropping support for Chrome 63 and Firefox 67\n // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape#browser_compatibility\n if (!supportUnicodePropertyEscapes()) {\n return false\n }\n\n // We use the Unicode property escapes to match any character that is a letter including other languages like Chinese, Japanese, etc.\n // p{Ll} matches a lowercase letter.\n // p{Lo} matches a letter that is neither uppercase nor lowercase (ex: Japanese characters).\n // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape#unicode_property_escapes_vs._character_classes\n return new RegExp('[^\\\\p{Ll}\\\\p{Lo}0-9_:./-]', 'u').test(rawValue)\n}\n\nexport function supportUnicodePropertyEscapes() {\n try {\n new RegExp('[\\\\p{Ll}]', 'u')\n return true\n } catch {\n return false\n }\n}\n", "import type { InitConfiguration } from './configuration'\nimport type { EndpointBuilder } from './endpointBuilder'\nimport { createEndpointBuilder } from './endpointBuilder'\nimport { buildTags } from './tags'\nimport type { Site } from './intakeSites'\nimport { INTAKE_SITE_US1, INTAKE_URL_PARAMETERS } from './intakeSites'\n\nexport interface TransportConfiguration {\n logsEndpointBuilder: EndpointBuilder\n rumEndpointBuilder: EndpointBuilder\n sessionReplayEndpointBuilder: EndpointBuilder\n profilingEndpointBuilder: EndpointBuilder\n replica?: ReplicaConfiguration\n site: Site\n}\n\nexport interface ReplicaConfiguration {\n applicationId?: string\n logsEndpointBuilder: EndpointBuilder\n rumEndpointBuilder: EndpointBuilder\n}\n\nexport function computeTransportConfiguration(initConfiguration: InitConfiguration): TransportConfiguration {\n const site = initConfiguration.site || INTAKE_SITE_US1\n\n const tags = buildTags(initConfiguration)\n\n const endpointBuilders = computeEndpointBuilders(initConfiguration, tags)\n const replicaConfiguration = computeReplicaConfiguration(initConfiguration, tags)\n\n return {\n replica: replicaConfiguration,\n site,\n ...endpointBuilders,\n }\n}\n\nfunction computeEndpointBuilders(initConfiguration: InitConfiguration, tags: string[]) {\n return {\n logsEndpointBuilder: createEndpointBuilder(initConfiguration, 'logs', tags),\n rumEndpointBuilder: createEndpointBuilder(initConfiguration, 'rum', tags),\n profilingEndpointBuilder: createEndpointBuilder(initConfiguration, 'profile', tags),\n sessionReplayEndpointBuilder: createEndpointBuilder(initConfiguration, 'replay', tags),\n }\n}\n\nfunction computeReplicaConfiguration(\n initConfiguration: InitConfiguration,\n tags: string[]\n): ReplicaConfiguration | undefined {\n if (!initConfiguration.replica) {\n return\n }\n\n const replicaConfiguration: InitConfiguration = {\n ...initConfiguration,\n site: INTAKE_SITE_US1,\n clientToken: initConfiguration.replica.clientToken,\n }\n\n const replicaEndpointBuilders = {\n logsEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'logs', tags),\n rumEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'rum', tags),\n }\n\n return { applicationId: initConfiguration.replica.applicationId, ...replicaEndpointBuilders }\n}\n\nexport function isIntakeUrl(url: string): boolean {\n // check if tags is present in the query string\n return INTAKE_URL_PARAMETERS.every((param) => url.includes(param))\n}\n", "import { catchUserErrors } from '../../tools/catchUserErrors'\nimport { DOCS_ORIGIN, MORE_DETAILS, display } from '../../tools/display'\nimport type { RawTelemetryConfiguration } from '../telemetry'\nimport type { Duration } from '../../tools/utils/timeUtils'\nimport { ONE_SECOND } from '../../tools/utils/timeUtils'\nimport { isPercentage } from '../../tools/utils/numberUtils'\nimport { ONE_KIBI_BYTE } from '../../tools/utils/byteUtils'\nimport { objectHasValue } from '../../tools/utils/objectUtils'\nimport { selectSessionStoreStrategyType } from '../session/sessionStore'\nimport type { SessionStoreStrategyType } from '../session/storeStrategies/sessionStoreStrategy'\nimport { TrackingConsent } from '../trackingConsent'\nimport type { SessionPersistence } from '../session/sessionConstants'\nimport type { TransportConfiguration } from './transportConfiguration'\nimport { computeTransportConfiguration } from './transportConfiguration'\nimport type { Site } from './intakeSites'\n\nexport const DefaultPrivacyLevel = {\n ALLOW: 'allow',\n MASK: 'mask',\n MASK_USER_INPUT: 'mask-user-input',\n} as const\nexport type DefaultPrivacyLevel = (typeof DefaultPrivacyLevel)[keyof typeof DefaultPrivacyLevel]\n\nexport const TraceContextInjection = {\n ALL: 'all',\n SAMPLED: 'sampled',\n} as const\n\nexport type TraceContextInjection = (typeof TraceContextInjection)[keyof typeof TraceContextInjection]\n\nexport interface InitConfiguration {\n /**\n * The client token for Datadog. Required for authenticating your application with Datadog.\n */\n clientToken: string\n /**\n * A callback function that can be used to modify events before they are sent to Datadog.\n */\n beforeSend?: GenericBeforeSendCallback | undefined\n /**\n * The percentage of sessions tracked. A value between 0 and 100.\n * @default 100\n */\n sessionSampleRate?: number | undefined\n /**\n * The percentage of telemetry events sent. A value between 0 and 100.\n * @default 20\n */\n telemetrySampleRate?: number | undefined\n /**\n * Initialization fails silently if the RUM Browser SDK is already initialized on the page.\n * @default false\n */\n silentMultipleInit?: boolean | undefined\n\n /**\n * Which storage strategy to use for persisting sessions. Can be either 'cookie' or 'local-storage'.\n *\n * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values\n * @default \"cookie\"\n */\n sessionPersistence?: SessionPersistence | undefined\n\n /**\n * Allows the use of localStorage when cookies cannot be set. This enables the RUM Browser SDK to run in environments that do not provide cookie support.\n *\n * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values\n * See [Monitor Electron Applications Using the Browser SDK](https://docs.datadoghq.com/real_user_monitoring/guide/monitor-electron-applications-using-browser-sdk) for further information.\n * @deprecated use `sessionPersistence: local-storage` where you want to use localStorage instead\n */\n allowFallbackToLocalStorage?: boolean | undefined\n\n /**\n * Allow listening to DOM events dispatched programmatically ([untrusted events](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted)). Enabling this option can be useful if you heavily rely on programmatic events, such as in an automated UI test environment.\n * @default false\n */\n allowUntrustedEvents?: boolean | undefined\n /**\n * Store global context and user context in localStorage to preserve them along the user navigation.\n * See [Contexts life cycle](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/?tab=npm#contexts-life-cycle) for further information.\n * @default false\n */\n storeContextsAcrossPages?: boolean | undefined\n /**\n * Set the initial user tracking consent state.\n * See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/?tab=npm#user-tracking-consent) for further information.\n * @default granted\n */\n trackingConsent?: TrackingConsent | undefined\n\n // transport options\n /**\n * Optional proxy URL, for example: https://www.proxy.com/path.\n * See [Proxy Your Browser RUM Data](https://docs.datadoghq.com/real_user_monitoring/guide/proxy-rum-data) for further information.\n */\n proxy?: string | ProxyFn | undefined\n /**\n * The Datadog [site](https://docs.datadoghq.com/getting_started/site) parameter of your organization.\n * @default datadoghq.com\n */\n site?: Site | undefined\n\n // tag and context options\n /**\n * The service name for your application. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n service?: string | undefined | null\n /**\n * The application\u2019s environment, for example: prod, pre-prod, and staging. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n env?: string | undefined | null\n /**\n * The application\u2019s version, for example: 1.2.3, 6c44da20, and 2020.02.13. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n version?: string | undefined | null\n\n // cookie options\n /**\n * Use a partitioned secure cross-site session cookie. This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`.\n *\n * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values\n * @default false\n */\n usePartitionedCrossSiteSessionCookie?: boolean | undefined\n /**\n * Use a secure session cookie. This disables RUM events sent on insecure (non-HTTPS) connections.\n *\n * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values\n * @default false\n */\n useSecureSessionCookie?: boolean | undefined\n /**\n * Preserve the session across subdomains for the same site.\n *\n * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values\n * @default false\n */\n trackSessionAcrossSubdomains?: boolean | undefined\n /**\n * Track anonymous user for the same site and extend cookie expiration date\n * @default true\n */\n trackAnonymousUser?: boolean | undefined\n // internal options\n /**\n * [Internal option] Enable experimental features\n */\n enableExperimentalFeatures?: string[] | undefined\n /**\n * [Internal option] Configure the dual chipping to another datacenter\n */\n replica?: ReplicaUserConfiguration | undefined\n /**\n * [Internal option] Set the datacenter from where the data is dual chipped\n */\n datacenter?: string\n /**\n * [Internal option] Datadog internal analytics subdomain\n */\n // TODO next major: remove this option and replace usages by proxyFn\n internalAnalyticsSubdomain?: string\n /**\n * [Internal option] The percentage of telemetry configuration sent. A value between 0 and 100.\n * @default 5\n */\n telemetryConfigurationSampleRate?: number\n /**\n * [Internal option] The percentage of telemetry usage sent. A value between 0 and 100.\n * @default 5\n */\n telemetryUsageSampleRate?: number\n}\n\n// This type is only used to build the core configuration. Logs and RUM SDKs are using a proper type\n// for this option.\ntype GenericBeforeSendCallback = (event: any, context?: any) => unknown\n\n/**\n * path: /api/vX/product\n * parameters: xxx=yyy&zzz=aaa\n */\ntype ProxyFn = (options: { path: string; parameters: string }) => string\n\nexport interface ReplicaUserConfiguration {\n applicationId?: string\n clientToken: string\n}\n\nexport interface Configuration extends TransportConfiguration {\n // Built from init configuration\n beforeSend: GenericBeforeSendCallback | undefined\n sessionStoreStrategyType: SessionStoreStrategyType | undefined\n sessionSampleRate: number\n telemetrySampleRate: number\n telemetryConfigurationSampleRate: number\n telemetryUsageSampleRate: number\n service: string | undefined\n silentMultipleInit: boolean\n allowUntrustedEvents: boolean\n trackingConsent: TrackingConsent\n storeContextsAcrossPages: boolean\n trackAnonymousUser?: boolean\n\n // Event limits\n eventRateLimiterThreshold: number // Limit the maximum number of actions, errors and logs per minutes\n maxTelemetryEventsPerPage: number\n\n // Batch configuration\n batchBytesLimit: number\n flushTimeout: Duration\n batchMessagesLimit: number\n messageBytesLimit: number\n}\n\nfunction isString(tag: unknown, tagName: string): tag is string | undefined | null {\n if (tag !== undefined && tag !== null && typeof tag !== 'string') {\n display.error(`${tagName} must be defined as a string`)\n return false\n }\n return true\n}\n\nfunction isDatadogSite(site: unknown) {\n if (site && typeof site === 'string' && !/(datadog|ddog|datad0g|dd0g)/.test(site)) {\n display.error(`Site should be a valid Datadog site. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/site/.`)\n return false\n }\n return true\n}\n\nexport function isSampleRate(sampleRate: unknown, name: string) {\n if (sampleRate !== undefined && !isPercentage(sampleRate)) {\n display.error(`${name} Sample Rate should be a number between 0 and 100`)\n return false\n }\n return true\n}\n\nexport function validateAndBuildConfiguration(initConfiguration: InitConfiguration): Configuration | undefined {\n if (!initConfiguration || !initConfiguration.clientToken) {\n display.error('Client Token is not configured, we will not send any data.')\n return\n }\n\n if (\n !isDatadogSite(initConfiguration.site) ||\n !isSampleRate(initConfiguration.sessionSampleRate, 'Session') ||\n !isSampleRate(initConfiguration.telemetrySampleRate, 'Telemetry') ||\n !isSampleRate(initConfiguration.telemetryConfigurationSampleRate, 'Telemetry Configuration') ||\n !isSampleRate(initConfiguration.telemetryUsageSampleRate, 'Telemetry Usage') ||\n !isString(initConfiguration.version, 'Version') ||\n !isString(initConfiguration.env, 'Env') ||\n !isString(initConfiguration.service, 'Service')\n ) {\n return\n }\n\n if (\n initConfiguration.trackingConsent !== undefined &&\n !objectHasValue(TrackingConsent, initConfiguration.trackingConsent)\n ) {\n display.error('Tracking Consent should be either \"granted\" or \"not-granted\"')\n return\n }\n\n return {\n beforeSend:\n initConfiguration.beforeSend && catchUserErrors(initConfiguration.beforeSend, 'beforeSend threw an error:'),\n sessionStoreStrategyType: selectSessionStoreStrategyType(initConfiguration),\n sessionSampleRate: initConfiguration.sessionSampleRate ?? 100,\n telemetrySampleRate: initConfiguration.telemetrySampleRate ?? 20,\n telemetryConfigurationSampleRate: initConfiguration.telemetryConfigurationSampleRate ?? 5,\n telemetryUsageSampleRate: initConfiguration.telemetryUsageSampleRate ?? 5,\n service: initConfiguration.service || undefined,\n silentMultipleInit: !!initConfiguration.silentMultipleInit,\n allowUntrustedEvents: !!initConfiguration.allowUntrustedEvents,\n trackingConsent: initConfiguration.trackingConsent ?? TrackingConsent.GRANTED,\n trackAnonymousUser: initConfiguration.trackAnonymousUser ?? true,\n storeContextsAcrossPages: !!initConfiguration.storeContextsAcrossPages,\n /**\n * beacon payload max queue size implementation is 64kb\n * ensure that we leave room for logs, rum and potential other users\n */\n batchBytesLimit: 16 * ONE_KIBI_BYTE,\n\n eventRateLimiterThreshold: 3000,\n maxTelemetryEventsPerPage: 15,\n\n /**\n * flush automatically, aim to be lower than ALB connection timeout\n * to maximize connection reuse.\n */\n flushTimeout: (30 * ONE_SECOND) as Duration,\n\n /**\n * Logs intake limit\n */\n batchMessagesLimit: 50,\n messageBytesLimit: 256 * ONE_KIBI_BYTE,\n ...computeTransportConfiguration(initConfiguration),\n }\n}\n\nexport function serializeConfiguration(initConfiguration: InitConfiguration) {\n return {\n session_sample_rate: initConfiguration.sessionSampleRate,\n telemetry_sample_rate: initConfiguration.telemetrySampleRate,\n telemetry_configuration_sample_rate: initConfiguration.telemetryConfigurationSampleRate,\n telemetry_usage_sample_rate: initConfiguration.telemetryUsageSampleRate,\n use_before_send: !!initConfiguration.beforeSend,\n use_partitioned_cross_site_session_cookie: initConfiguration.usePartitionedCrossSiteSessionCookie,\n use_secure_session_cookie: initConfiguration.useSecureSessionCookie,\n use_proxy: !!initConfiguration.proxy,\n silent_multiple_init: initConfiguration.silentMultipleInit,\n track_session_across_subdomains: initConfiguration.trackSessionAcrossSubdomains,\n track_anonymous_user: initConfiguration.trackAnonymousUser,\n session_persistence: initConfiguration.sessionPersistence,\n allow_fallback_to_local_storage: !!initConfiguration.allowFallbackToLocalStorage,\n store_contexts_across_pages: !!initConfiguration.storeContextsAcrossPages,\n allow_untrusted_events: !!initConfiguration.allowUntrustedEvents,\n tracking_consent: initConfiguration.trackingConsent,\n } satisfies RawTelemetryConfiguration\n}\n", "export type { Configuration, InitConfiguration } from './configuration'\nexport {\n DefaultPrivacyLevel,\n TraceContextInjection,\n isSampleRate,\n validateAndBuildConfiguration,\n serializeConfiguration,\n} from './configuration'\nexport type { EndpointBuilder, TrackType } from './endpointBuilder'\nexport { createEndpointBuilder, buildEndpointHost } from './endpointBuilder'\nexport * from './intakeSites'\nexport { computeTransportConfiguration, isIntakeUrl } from './transportConfiguration'\n", "/**\n * LIMITATION:\n * For NPM setup, this feature flag singleton is shared between RUM and Logs product.\n * This means that an experimental flag set on the RUM product will be set on the Logs product.\n * So keep in mind that in certain configurations, your experimental feature flag may affect other products.\n *\n * FORMAT:\n * All feature flags should be snake_cased\n */\n// We want to use a real enum (i.e. not a const enum) here, to be able to check whether an arbitrary\n// string is an expected feature flag\n\nimport { objectHasValue } from './utils/objectUtils'\n\n// eslint-disable-next-line no-restricted-syntax\nexport enum ExperimentalFeature {\n WRITABLE_RESOURCE_GRAPHQL = 'writable_resource_graphql',\n MISSING_URL_CONTEXT_TELEMETRY = 'missing_url_context_telemetry',\n USER_ACCOUNT_TRACE_HEADER = 'user_account_trace_header',\n PROFILING = 'profiling',\n}\n\nconst enabledExperimentalFeatures: Set = new Set()\n\nexport function initFeatureFlags(enableExperimentalFeatures: string[] | undefined) {\n if (Array.isArray(enableExperimentalFeatures)) {\n addExperimentalFeatures(\n enableExperimentalFeatures.filter((flag): flag is ExperimentalFeature =>\n objectHasValue(ExperimentalFeature, flag)\n )\n )\n }\n}\n\nexport function addExperimentalFeatures(enabledFeatures: ExperimentalFeature[]): void {\n enabledFeatures.forEach((flag) => {\n enabledExperimentalFeatures.add(flag)\n })\n}\n\nexport function isExperimentalFeatureEnabled(featureName: ExperimentalFeature): boolean {\n return enabledExperimentalFeatures.has(featureName)\n}\n\nexport function resetExperimentalFeatures(): void {\n enabledExperimentalFeatures.clear()\n}\n\nexport function getExperimentalFeatures(): Set {\n return enabledExperimentalFeatures\n}\n", "/**\n * Cross-browser stack trace computation.\n *\n * Reference implementation: https://github.com/csnover/TraceKit/blob/04530298073c3823de72deb0b97e7b38ca7bcb59/tracekit.js\n */\n\nexport interface StackFrame {\n url?: string\n func?: string\n /** The arguments passed to the function, if known. */\n args?: string[]\n line?: number\n column?: number\n /** An array of source code lines; the middle element corresponds to the correct line. */\n context?: string[]\n}\n\nexport interface StackTrace {\n name?: string\n message?: string\n url?: string\n stack: StackFrame[]\n incomplete?: boolean\n partial?: boolean\n}\n\nconst UNKNOWN_FUNCTION = '?'\n\nexport function computeStackTrace(ex: unknown): StackTrace {\n const stack: StackFrame[] = []\n\n let stackProperty = tryToGetString(ex, 'stack')\n const exString = String(ex)\n if (stackProperty && stackProperty.startsWith(exString)) {\n stackProperty = stackProperty.slice(exString.length)\n }\n if (stackProperty) {\n stackProperty.split('\\n').forEach((line) => {\n const stackFrame =\n parseChromeLine(line) || parseChromeAnonymousLine(line) || parseWinLine(line) || parseGeckoLine(line)\n if (stackFrame) {\n if (!stackFrame.func && stackFrame.line) {\n stackFrame.func = UNKNOWN_FUNCTION\n }\n\n stack.push(stackFrame)\n }\n })\n }\n\n return {\n message: tryToGetString(ex, 'message'),\n name: tryToGetString(ex, 'name'),\n stack,\n }\n}\nconst fileUrl =\n '((?:file|https?|blob|chrome-extension|electron|native|eval|webpack|snippet||\\\\w+\\\\.|\\\\/).*?)'\nconst filePosition = '(?::(\\\\d+))'\nconst CHROME_LINE_RE = new RegExp(`^\\\\s*at (.*?) ?\\\\(${fileUrl}${filePosition}?${filePosition}?\\\\)?\\\\s*$`, 'i')\n\nconst CHROME_EVAL_RE = new RegExp(`\\\\((\\\\S*)${filePosition}${filePosition}\\\\)`)\n\nfunction parseChromeLine(line: string): StackFrame | undefined {\n const parts = CHROME_LINE_RE.exec(line)\n\n if (!parts) {\n return\n }\n\n const isNative = parts[2] && parts[2].indexOf('native') === 0 // start of line\n const isEval = parts[2] && parts[2].indexOf('eval') === 0 // start of line\n const submatch = CHROME_EVAL_RE.exec(parts[2])\n\n if (isEval && submatch) {\n // throw out eval line/column and use top-most line/column number\n parts[2] = submatch[1] // url\n parts[3] = submatch[2] // line\n parts[4] = submatch[3] // column\n }\n\n return {\n args: isNative ? [parts[2]] : [],\n column: parts[4] ? +parts[4] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: parts[3] ? +parts[3] : undefined,\n url: !isNative ? parts[2] : undefined,\n }\n}\n\nconst CHROME_ANONYMOUS_FUNCTION_RE = new RegExp(`^\\\\s*at ?${fileUrl}${filePosition}?${filePosition}??\\\\s*$`, 'i')\n\nfunction parseChromeAnonymousLine(line: string): StackFrame | undefined {\n const parts = CHROME_ANONYMOUS_FUNCTION_RE.exec(line)\n\n if (!parts) {\n return\n }\n\n return {\n args: [],\n column: parts[3] ? +parts[3] : undefined,\n func: UNKNOWN_FUNCTION,\n line: parts[2] ? +parts[2] : undefined,\n url: parts[1],\n }\n}\n\nconst WINJS_LINE_RE =\n /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i\n\nfunction parseWinLine(line: string): StackFrame | undefined {\n const parts = WINJS_LINE_RE.exec(line)\n if (!parts) {\n return\n }\n\n return {\n args: [],\n column: parts[4] ? +parts[4] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: +parts[3],\n url: parts[2],\n }\n}\n\nconst GECKO_LINE_RE =\n /^\\s*(.*?)(?:\\((.*?)\\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|capacitor|\\[native).*?|[^@]*bundle)(?::(\\d+))?(?::(\\d+))?\\s*$/i\nconst GECKO_EVAL_RE = /(\\S+) line (\\d+)(?: > eval line \\d+)* > eval/i\n\nfunction parseGeckoLine(line: string): StackFrame | undefined {\n const parts = GECKO_LINE_RE.exec(line)\n if (!parts) {\n return\n }\n\n const isEval = parts[3] && parts[3].indexOf(' > eval') > -1\n const submatch = GECKO_EVAL_RE.exec(parts[3])\n\n if (isEval && submatch) {\n // throw out eval line/column and use top-most line number\n parts[3] = submatch[1]\n parts[4] = submatch[2]\n parts[5] = undefined! // no column when eval\n }\n\n return {\n args: parts[2] ? parts[2].split(',') : [],\n column: parts[5] ? +parts[5] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: parts[4] ? +parts[4] : undefined,\n url: parts[3],\n }\n}\n\nfunction tryToGetString(candidate: unknown, property: string) {\n if (typeof candidate !== 'object' || !candidate || !(property in candidate)) {\n return undefined\n }\n const value = (candidate as { [k: string]: unknown })[property]\n return typeof value === 'string' ? value : undefined\n}\n\nexport function computeStackTraceFromOnErrorMessage(messageObj: unknown, url?: string, line?: number, column?: number) {\n const stack = [{ url, column, line }]\n const { name, message } = tryToParseMessage(messageObj)\n return {\n name,\n message,\n stack,\n }\n}\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types\nconst ERROR_TYPES_RE =\n /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?([\\s\\S]*)$/\n\nfunction tryToParseMessage(messageObj: unknown) {\n let name\n let message\n if ({}.toString.call(messageObj) === '[object String]') {\n ;[, name, message] = ERROR_TYPES_RE.exec(messageObj as string)!\n }\n return { name, message }\n}\n", "import { callMonitored } from '../monitor'\nimport type { StackTrace } from './computeStackTrace'\nimport { computeStackTrace } from './computeStackTrace'\n\n/**\n * Creates a stacktrace without SDK internal frames.\n * Constraints:\n * - Has to be called at the utmost position of the call stack.\n * - No monitored function should encapsulate it, that is why we need to use callMonitored inside it.\n */\nexport function createHandlingStack(\n type: 'console error' | 'action' | 'error' | 'instrumented method' | 'log' | 'react error'\n): string {\n /**\n * Skip the two internal frames:\n * - SDK API (console.error, ...)\n * - this function\n * in order to keep only the user calls\n */\n const internalFramesToSkip = 2\n const error = new Error(type)\n error.name = 'HandlingStack'\n let formattedStack: string\n\n callMonitored(() => {\n const stackTrace = computeStackTrace(error)\n stackTrace.stack = stackTrace.stack.slice(internalFramesToSkip)\n formattedStack = toStackTraceString(stackTrace)\n })\n\n return formattedStack!\n}\n\nexport function toStackTraceString(stack: StackTrace) {\n let result = formatErrorMessage(stack)\n stack.stack.forEach((frame) => {\n const func = frame.func === '?' ? '' : frame.func\n const args = frame.args && frame.args.length > 0 ? `(${frame.args.join(', ')})` : ''\n const line = frame.line ? `:${frame.line}` : ''\n const column = frame.line && frame.column ? `:${frame.column}` : ''\n result += `\\n at ${func!}${args} @ ${frame.url!}${line}${column}`\n })\n return result\n}\n\nexport function formatErrorMessage(stack: StackTrace) {\n return `${stack.name || 'Error'}: ${stack.message!}`\n}\n", "import { setTimeout } from './timer'\nimport { callMonitored } from './monitor'\nimport { noop } from './utils/functionUtils'\nimport { createHandlingStack } from './stackTrace/handlingStack'\n\n/**\n * Object passed to the callback of an instrumented method call. See `instrumentMethod` for more\n * info.\n */\nexport type InstrumentedMethodCall = {\n /**\n * The target object on which the method was called.\n */\n target: TARGET\n\n /**\n * The parameters with which the method was called.\n *\n * Note: if needed, parameters can be mutated by the instrumentation\n */\n parameters: Parameters\n\n /**\n * Registers a callback that will be called after the original method is called, with the method\n * result passed as argument.\n */\n onPostCall: (callback: PostCallCallback) => void\n\n /**\n * The stack trace of the method call.\n */\n handlingStack?: string\n}\n\ntype PostCallCallback = (\n result: ReturnType\n) => void\n\n/**\n * Instruments a method on a object, calling the given callback before the original method is\n * invoked. The callback receives an object with information about the method call.\n *\n * This function makes sure that we are \"good citizens\" regarding third party instrumentations: when\n * removing the instrumentation, the original method is usually restored, but if a third party\n * instrumentation was set after ours, we keep it in place and just replace our instrumentation with\n * a noop.\n *\n * Note: it is generally better to instrument methods that are \"owned\" by the object instead of ones\n * that are inherited from the prototype chain. Example:\n * * do: `instrumentMethod(Array.prototype, 'push', ...)`\n * * don't: `instrumentMethod([], 'push', ...)`\n *\n * This method is also used to set event handler properties (ex: window.onerror = ...), as it has\n * the same requirements as instrumenting a method:\n * * if the event handler is already set by a third party, we need to call it and not just blindly\n * override it.\n * * if the event handler is set by a third party after us, we need to keep it in place when\n * removing ours.\n *\n * @example\n *\n * instrumentMethod(window, 'fetch', ({ target, parameters, onPostCall }) => {\n * console.log('Before calling fetch on', target, 'with parameters', parameters)\n *\n * onPostCall((result) => {\n * console.log('After fetch calling on', target, 'with parameters', parameters, 'and result', result)\n * })\n * })\n */\nexport function instrumentMethod(\n targetPrototype: TARGET,\n method: METHOD,\n onPreCall: (this: null, callInfos: InstrumentedMethodCall) => void,\n { computeHandlingStack }: { computeHandlingStack?: boolean } = {}\n) {\n let original = targetPrototype[method]\n\n if (typeof original !== 'function') {\n if (method in targetPrototype && method.startsWith('on')) {\n original = noop as TARGET[METHOD]\n } else {\n return { stop: noop }\n }\n }\n\n let stopped = false\n\n const instrumentation = function (this: TARGET): ReturnType | undefined {\n if (stopped) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call\n return original.apply(this, arguments as unknown as Parameters)\n }\n\n const parameters = Array.from(arguments) as Parameters\n\n let postCallCallback: PostCallCallback | undefined\n\n callMonitored(onPreCall, null, [\n {\n target: this,\n parameters,\n onPostCall: (callback) => {\n postCallCallback = callback\n },\n handlingStack: computeHandlingStack ? createHandlingStack('instrumented method') : undefined,\n },\n ])\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n const result = original.apply(this, parameters)\n\n if (postCallCallback) {\n callMonitored(postCallCallback, null, [result])\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return result\n }\n\n targetPrototype[method] = instrumentation as TARGET[METHOD]\n\n return {\n stop: () => {\n stopped = true\n // If the instrumentation has been removed by a third party, keep the last one\n if (targetPrototype[method] === instrumentation) {\n targetPrototype[method] = original\n }\n },\n }\n}\n\nexport function instrumentSetter(\n targetPrototype: TARGET,\n property: PROPERTY,\n after: (target: TARGET, value: TARGET[PROPERTY]) => void\n) {\n const originalDescriptor = Object.getOwnPropertyDescriptor(targetPrototype, property)\n if (!originalDescriptor || !originalDescriptor.set || !originalDescriptor.configurable) {\n return { stop: noop }\n }\n\n const stoppedInstrumentation = noop\n let instrumentation = (target: TARGET, value: TARGET[PROPERTY]) => {\n // put hooked setter into event loop to avoid of set latency\n setTimeout(() => {\n if (instrumentation !== stoppedInstrumentation) {\n after(target, value)\n }\n }, 0)\n }\n\n const instrumentationWrapper = function (this: TARGET, value: TARGET[PROPERTY]) {\n originalDescriptor.set!.call(this, value)\n instrumentation(this, value)\n }\n\n Object.defineProperty(targetPrototype, property, {\n set: instrumentationWrapper,\n })\n\n return {\n stop: () => {\n if (Object.getOwnPropertyDescriptor(targetPrototype, property)?.set === instrumentationWrapper) {\n Object.defineProperty(targetPrototype, property, originalDescriptor)\n }\n instrumentation = stoppedInstrumentation\n },\n }\n}\n", "import { display } from '../display'\nimport { ONE_KIBI_BYTE } from '../utils/byteUtils'\nimport type { Context, ContextArray, ContextValue } from './context'\nimport type { ObjectWithToJsonMethod } from './jsonStringify'\nimport { detachToJsonMethod } from './jsonStringify'\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\ntype PrimitivesAndFunctions = string | number | boolean | undefined | null | symbol | bigint | Function\ntype ExtendedContextValue = PrimitivesAndFunctions | object | ExtendedContext | ExtendedContextArray\ntype ExtendedContext = { [key: string]: ExtendedContextValue }\ntype ExtendedContextArray = ExtendedContextValue[]\n\ntype ContainerElementToProcess = {\n source: ExtendedContextArray | ExtendedContext\n target: ContextArray | Context\n path: string\n}\n\ntype SanitizedEvent = {\n type: string\n isTrusted: boolean\n currentTarget: string | null | undefined\n target: string | null | undefined\n}\n\n// The maximum size of a single event is 256KiB. By default, we ensure that user-provided data\n// going through sanitize fits inside our events, while leaving room for other contexts, metadata, ...\nconst SANITIZE_DEFAULT_MAX_CHARACTER_COUNT = 220 * ONE_KIBI_BYTE\n\n// Symbol for the root element of the JSONPath used for visited objects\nconst JSON_PATH_ROOT_ELEMENT = '$'\n\n// When serializing (using JSON.stringify) a key of an object, { key: 42 } gets wrapped in quotes as \"key\".\n// With the separator (:), we need to add 3 characters to the count.\nconst KEY_DECORATION_LENGTH = 3\n\n/**\n * Ensures user-provided data is 'safe' for the SDK\n * - Deep clones data\n * - Removes cyclic references\n * - Transforms unserializable types to a string representation\n *\n * LIMITATIONS:\n * - Size is in characters, not byte count (may differ according to character encoding)\n * - Size does not take into account indentation that can be applied to JSON.stringify\n * - Non-numerical properties of Arrays are ignored. Same behavior as JSON.stringify\n *\n * @param source User-provided data meant to be serialized using JSON.stringify\n * @param maxCharacterCount Maximum number of characters allowed in serialized form\n */\nexport function sanitize(source: string, maxCharacterCount?: number): string | undefined\nexport function sanitize(source: Context, maxCharacterCount?: number): Context\nexport function sanitize(source: unknown, maxCharacterCount?: number): ContextValue\nexport function sanitize(source: unknown, maxCharacterCount = SANITIZE_DEFAULT_MAX_CHARACTER_COUNT) {\n // Unbind any toJSON function we may have on [] or {} prototypes\n const restoreObjectPrototypeToJson = detachToJsonMethod(Object.prototype)\n const restoreArrayPrototypeToJson = detachToJsonMethod(Array.prototype)\n\n // Initial call to sanitizeProcessor - will populate containerQueue if source is an Array or a plain Object\n const containerQueue: ContainerElementToProcess[] = []\n const visitedObjectsWithPath = new WeakMap()\n const sanitizedData = sanitizeProcessor(\n source as ExtendedContextValue,\n JSON_PATH_ROOT_ELEMENT,\n undefined,\n containerQueue,\n visitedObjectsWithPath\n )\n const serializedSanitizedData = JSON.stringify(sanitizedData)\n let accumulatedCharacterCount = serializedSanitizedData ? serializedSanitizedData.length : 0\n\n if (accumulatedCharacterCount > maxCharacterCount) {\n warnOverCharacterLimit(maxCharacterCount, 'discarded', source)\n return undefined\n }\n\n while (containerQueue.length > 0 && accumulatedCharacterCount < maxCharacterCount) {\n const containerToProcess = containerQueue.shift()!\n let separatorLength = 0 // 0 for the first element, 1 for subsequent elements\n\n // Arrays and Objects have to be handled distinctly to ensure\n // we do not pick up non-numerical properties from Arrays\n if (Array.isArray(containerToProcess.source)) {\n for (let key = 0; key < containerToProcess.source.length; key++) {\n const targetData = sanitizeProcessor(\n containerToProcess.source[key],\n containerToProcess.path,\n key,\n containerQueue,\n visitedObjectsWithPath\n )\n\n if (targetData !== undefined) {\n accumulatedCharacterCount += JSON.stringify(targetData).length\n } else {\n // When an element of an Array (targetData) is undefined, it is serialized as null:\n // JSON.stringify([undefined]) => '[null]' - This accounts for 4 characters\n accumulatedCharacterCount += 4\n }\n accumulatedCharacterCount += separatorLength\n separatorLength = 1\n if (accumulatedCharacterCount > maxCharacterCount) {\n warnOverCharacterLimit(maxCharacterCount, 'truncated', source)\n break\n }\n ;(containerToProcess.target as ContextArray)[key] = targetData\n }\n } else {\n for (const key in containerToProcess.source) {\n if (Object.prototype.hasOwnProperty.call(containerToProcess.source, key)) {\n const targetData = sanitizeProcessor(\n containerToProcess.source[key],\n containerToProcess.path,\n key,\n containerQueue,\n visitedObjectsWithPath\n )\n // When a property of an object has an undefined value, it will be dropped during serialization:\n // JSON.stringify({a:undefined}) => '{}'\n if (targetData !== undefined) {\n accumulatedCharacterCount +=\n JSON.stringify(targetData).length + separatorLength + key.length + KEY_DECORATION_LENGTH\n separatorLength = 1\n }\n if (accumulatedCharacterCount > maxCharacterCount) {\n warnOverCharacterLimit(maxCharacterCount, 'truncated', source)\n break\n }\n ;(containerToProcess.target as Context)[key] = targetData\n }\n }\n }\n }\n\n // Rebind detached toJSON functions\n restoreObjectPrototypeToJson()\n restoreArrayPrototypeToJson()\n\n return sanitizedData\n}\n\n/**\n * Internal function to factorize the process common to the\n * initial call to sanitize, and iterations for Arrays and Objects\n *\n */\nfunction sanitizeProcessor(\n source: ExtendedContextValue,\n parentPath: string,\n key: string | number | undefined,\n queue: ContainerElementToProcess[],\n visitedObjectsWithPath: WeakMap\n) {\n // Start by handling toJSON, as we want to sanitize its output\n const sourceToSanitize = tryToApplyToJSON(source)\n\n if (!sourceToSanitize || typeof sourceToSanitize !== 'object') {\n return sanitizePrimitivesAndFunctions(sourceToSanitize)\n }\n\n const sanitizedSource = sanitizeObjects(sourceToSanitize)\n if (sanitizedSource !== '[Object]' && sanitizedSource !== '[Array]' && sanitizedSource !== '[Error]') {\n return sanitizedSource\n }\n\n // Handle potential cyclic references\n // We need to use source as sourceToSanitize could be a reference to a new object\n // At this stage, we know the source is an object type\n const sourceAsObject = source as object\n if (visitedObjectsWithPath.has(sourceAsObject)) {\n return `[Reference seen at ${visitedObjectsWithPath.get(sourceAsObject)!}]`\n }\n\n // Add processed source to queue\n const currentPath = key !== undefined ? `${parentPath}.${key}` : parentPath\n const target = Array.isArray(sourceToSanitize) ? ([] as ContextArray) : ({} as Context)\n visitedObjectsWithPath.set(sourceAsObject, currentPath)\n queue.push({ source: sourceToSanitize as ExtendedContext | ExtendedContextArray, target, path: currentPath })\n\n return target\n}\n\n/**\n * Handles sanitization of simple, non-object types\n *\n */\nfunction sanitizePrimitivesAndFunctions(value: PrimitivesAndFunctions) {\n // BigInt cannot be serialized by JSON.stringify(), convert it to a string representation\n if (typeof value === 'bigint') {\n return `[BigInt] ${value.toString()}`\n }\n // Functions cannot be serialized by JSON.stringify(). Moreover, if a faulty toJSON is present, it needs to be converted\n // so it won't prevent stringify from serializing later\n if (typeof value === 'function') {\n return `[Function] ${value.name || 'unknown'}`\n }\n // JSON.stringify() does not serialize symbols.\n if (typeof value === 'symbol') {\n // symbol.description is part of ES2019+\n type symbolWithDescription = symbol & { description: string }\n return `[Symbol] ${(value as symbolWithDescription).description || value.toString()}`\n }\n\n return value\n}\n\n/**\n * Handles sanitization of object types\n *\n * LIMITATIONS\n * - If a class defines a toStringTag Symbol, it will fall in the catch-all method and prevent enumeration of properties.\n * To avoid this, a toJSON method can be defined.\n */\nfunction sanitizeObjects(value: object): string | SanitizedEvent {\n try {\n if (value instanceof Event) {\n return sanitizeEvent(value)\n }\n\n if (value instanceof RegExp) {\n return `[RegExp] ${value.toString()}`\n }\n\n // Handle all remaining object types in a generic way\n const result = Object.prototype.toString.call(value)\n const match = result.match(/\\[object (.*)\\]/)\n if (match && match[1]) {\n return `[${match[1]}]`\n }\n } catch {\n // If the previous serialization attempts failed, and we cannot convert using\n // Object.prototype.toString, declare the value unserializable\n }\n return '[Unserializable]'\n}\n\nfunction sanitizeEvent(event: Event): SanitizedEvent {\n return {\n type: event.type,\n isTrusted: event.isTrusted,\n currentTarget: event.currentTarget ? (sanitizeObjects(event.currentTarget) as string) : null,\n target: event.target ? (sanitizeObjects(event.target) as string) : null,\n }\n}\n\n/**\n * Checks if a toJSON function exists and tries to execute it\n *\n */\nfunction tryToApplyToJSON(value: ExtendedContextValue) {\n const object = value as ObjectWithToJsonMethod\n if (object && typeof object.toJSON === 'function') {\n try {\n return object.toJSON() as ExtendedContextValue\n } catch {\n // If toJSON fails, we continue by trying to serialize the value manually\n }\n }\n\n return value\n}\n\n/**\n * Helper function to display the warning when the accumulated character count is over the limit\n */\nfunction warnOverCharacterLimit(maxCharacterCount: number, changeType: 'discarded' | 'truncated', source: unknown) {\n display.warn(\n `The data provided has been ${changeType} as it is over the limit of ${maxCharacterCount} characters:`,\n source\n )\n}\n", "import { sanitize } from '../../tools/serialisation/sanitize'\nimport type { ClocksState } from '../../tools/utils/timeUtils'\nimport type { Context } from '../../tools/serialisation/context'\nimport { jsonStringify } from '../../tools/serialisation/jsonStringify'\nimport type { StackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { toStackTraceString } from '../../tools/stackTrace/handlingStack'\nimport type { ErrorSource, ErrorHandling, RawError, RawErrorCause, ErrorWithCause, NonErrorPrefix } from './error.types'\n\nexport const NO_ERROR_STACK_PRESENT_MESSAGE = 'No stack, consider using an instance of Error'\n\ntype RawErrorParams = {\n stackTrace?: StackTrace\n originalError: unknown\n handlingStack?: string\n componentStack?: string\n startClocks: ClocksState\n nonErrorPrefix: NonErrorPrefix\n source: ErrorSource\n handling: ErrorHandling\n}\n\nexport function computeRawError({\n stackTrace,\n originalError,\n handlingStack,\n componentStack,\n startClocks,\n nonErrorPrefix,\n source,\n handling,\n}: RawErrorParams): RawError {\n const isErrorInstance = isError(originalError)\n\n const message = computeMessage(stackTrace, isErrorInstance, nonErrorPrefix, originalError)\n const stack = hasUsableStack(isErrorInstance, stackTrace)\n ? toStackTraceString(stackTrace)\n : NO_ERROR_STACK_PRESENT_MESSAGE\n const causes = isErrorInstance ? flattenErrorCauses(originalError as ErrorWithCause, source) : undefined\n const type = stackTrace ? stackTrace.name : undefined\n const fingerprint = tryToGetFingerprint(originalError)\n const context = tryToGetErrorContext(originalError)\n\n return {\n startClocks,\n source,\n handling,\n handlingStack,\n componentStack,\n originalError,\n type,\n message,\n stack,\n causes,\n fingerprint,\n context,\n }\n}\n\nfunction computeMessage(\n stackTrace: StackTrace | undefined,\n isErrorInstance: boolean,\n nonErrorPrefix: NonErrorPrefix,\n originalError: unknown\n) {\n // Favor stackTrace message only if tracekit has really been able to extract something meaningful (message + name)\n // TODO rework tracekit integration to avoid scattering error building logic\n return stackTrace?.message && stackTrace?.name\n ? stackTrace.message\n : !isErrorInstance\n ? `${nonErrorPrefix} ${jsonStringify(sanitize(originalError))!}`\n : 'Empty message'\n}\n\nfunction hasUsableStack(isErrorInstance: boolean, stackTrace?: StackTrace): stackTrace is StackTrace {\n if (stackTrace === undefined) {\n return false\n }\n if (isErrorInstance) {\n return true\n }\n // handle cases where tracekit return stack = [] or stack = [{url: undefined, line: undefined, column: undefined}]\n // TODO rework tracekit integration to avoid generating those unusable stack\n return stackTrace.stack.length > 0 && (stackTrace.stack.length > 1 || stackTrace.stack[0].url !== undefined)\n}\n\nexport function tryToGetFingerprint(originalError: unknown) {\n return isError(originalError) && 'dd_fingerprint' in originalError ? String(originalError.dd_fingerprint) : undefined\n}\n\nexport function tryToGetErrorContext(originalError: unknown) {\n if (originalError !== null && typeof originalError === 'object' && 'dd_context' in originalError) {\n return originalError.dd_context as Context\n }\n}\n\nexport function getFileFromStackTraceString(stack: string) {\n return /@ (.+)/.exec(stack)?.[1]\n}\n\nexport function isError(error: unknown): error is Error {\n return error instanceof Error || Object.prototype.toString.call(error) === '[object Error]'\n}\n\nexport function flattenErrorCauses(error: ErrorWithCause, parentSource: ErrorSource): RawErrorCause[] | undefined {\n let currentError = error\n const causes: RawErrorCause[] = []\n while (isError(currentError?.cause) && causes.length < 10) {\n const stackTrace = computeStackTrace(currentError.cause)\n causes.push({\n message: currentError.cause.message,\n source: parentSource,\n type: stackTrace?.name,\n stack: stackTrace && toStackTraceString(stackTrace),\n })\n currentError = currentError.cause\n }\n return causes.length ? causes : undefined\n}\n", "import type { Context } from '../../tools/serialisation/context'\nimport type { ClocksState } from '../../tools/utils/timeUtils'\n\n// TS v4.6 introduced Error.cause[1] typed as `Error`. TS v4.8 changed Error.cause to be\n// `unknown`[2].\n//\n// Because we still support TS 3.8, we need to declare our own type. We can remove it once we drop\n// support for TS v4.7 and before. The 'cause' property defined by TS needs to be omitted because\n// we define it with a type `unknown` which is incompatible with TS 4.6 and 4.7.\n//\n// [1]: https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/#target-es2022\n// [2]: https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/#lib-d-ts-updates\nexport interface ErrorWithCause extends Omit {\n cause?: unknown\n}\n\nexport type RawErrorCause = {\n message: string\n source: string\n type?: string\n stack?: string\n}\n\nexport type Csp = {\n disposition: 'enforce' | 'report'\n}\n\nexport interface RawError {\n startClocks: ClocksState\n message: string\n type?: string\n stack?: string\n source: ErrorSource\n originalError?: unknown\n handling?: ErrorHandling\n handlingStack?: string\n componentStack?: string\n causes?: RawErrorCause[]\n fingerprint?: string\n csp?: Csp\n context?: Context\n}\n\nexport const ErrorSource = {\n AGENT: 'agent',\n CONSOLE: 'console',\n CUSTOM: 'custom',\n LOGGER: 'logger',\n NETWORK: 'network',\n SOURCE: 'source',\n REPORT: 'report',\n} as const\n\nexport const enum NonErrorPrefix {\n UNCAUGHT = 'Uncaught',\n PROVIDED = 'Provided',\n}\n\nexport const enum ErrorHandling {\n HANDLED = 'handled',\n UNHANDLED = 'unhandled',\n}\n\nexport type ErrorSource = (typeof ErrorSource)[keyof typeof ErrorSource]\n", "import { instrumentMethod } from '../../tools/instrumentMethod'\nimport type { Observable } from '../../tools/observable'\nimport { clocksNow } from '../../tools/utils/timeUtils'\nimport type { StackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { computeStackTrace, computeStackTraceFromOnErrorMessage } from '../../tools/stackTrace/computeStackTrace'\nimport { computeRawError, isError } from './error'\nimport type { RawError } from './error.types'\nimport { ErrorHandling, ErrorSource, NonErrorPrefix } from './error.types'\n\nexport type UnhandledErrorCallback = (stackTrace: StackTrace, originalError?: any) => any\n\nexport function trackRuntimeError(errorObservable: Observable) {\n const handleRuntimeError = (stackTrace: StackTrace, originalError?: any) => {\n const rawError = computeRawError({\n stackTrace,\n originalError,\n startClocks: clocksNow(),\n nonErrorPrefix: NonErrorPrefix.UNCAUGHT,\n source: ErrorSource.SOURCE,\n handling: ErrorHandling.UNHANDLED,\n })\n errorObservable.notify(rawError)\n }\n const { stop: stopInstrumentingOnError } = instrumentOnError(handleRuntimeError)\n const { stop: stopInstrumentingOnUnhandledRejection } = instrumentUnhandledRejection(handleRuntimeError)\n\n return {\n stop: () => {\n stopInstrumentingOnError()\n stopInstrumentingOnUnhandledRejection()\n },\n }\n}\n\nexport function instrumentOnError(callback: UnhandledErrorCallback) {\n return instrumentMethod(window, 'onerror', ({ parameters: [messageObj, url, line, column, errorObj] }) => {\n let stackTrace\n if (isError(errorObj)) {\n stackTrace = computeStackTrace(errorObj)\n } else {\n stackTrace = computeStackTraceFromOnErrorMessage(messageObj, url, line, column)\n }\n callback(stackTrace, errorObj ?? messageObj)\n })\n}\n\nexport function instrumentUnhandledRejection(callback: UnhandledErrorCallback) {\n return instrumentMethod(window, 'onunhandledrejection', ({ parameters: [e] }) => {\n const reason = e.reason || 'Empty reason'\n const stack = computeStackTrace(reason)\n callback(stack, reason)\n })\n}\n", "import { catchUserErrors } from '../tools/catchUserErrors'\nimport { setDebugMode } from '../tools/monitor'\nimport { display } from '../tools/display'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\n\nexport interface PublicApi {\n /**\n * Version of the Logs browser SDK\n */\n version: string\n\n /**\n * [For CDN async setup] Early RUM API calls must be wrapped in the `window.DD_RUM.onReady()` callback. This ensures the code only gets executed once the SDK is properly loaded.\n *\n * See [CDN async setup](https://docs.datadoghq.com/real_user_monitoring/browser/#cdn-async) for further information.\n */\n onReady: (callback: () => void) => void\n}\n\nexport function makePublicApi(stub: Omit): T {\n const publicApi = {\n version: __BUILD_ENV__SDK_VERSION__,\n // This API method is intentionally not monitored, since the only thing executed is the\n // user-provided 'callback'. All SDK usages executed in the callback should be monitored, and\n // we don't want to interfere with the user uncaught exceptions.\n onReady(callback: () => void) {\n callback()\n },\n ...stub,\n }\n\n // Add a \"hidden\" property to set debug mode. We define it that way to hide it\n // as much as possible but of course it's not a real protection.\n Object.defineProperty(publicApi, '_setDebug', {\n get() {\n return setDebugMode\n },\n enumerable: false,\n })\n\n return publicApi as T\n}\n\nexport function defineGlobal(global: Global, name: Name, api: Global[Name]) {\n const existingGlobalVariable = global[name] as { q?: Array<() => void>; version?: string } | undefined\n if (existingGlobalVariable && !existingGlobalVariable.q && existingGlobalVariable.version) {\n display.warn('SDK is loaded more than once. This is unsupported and might have unexpected behavior.')\n }\n global[name] = api\n if (existingGlobalVariable && existingGlobalVariable.q) {\n existingGlobalVariable.q.forEach((fn) => catchUserErrors(fn, 'onReady callback threw an error:')())\n }\n}\n", "import type { InitConfiguration } from '../domain/configuration'\nimport { display } from '../tools/display'\n\nexport function displayAlreadyInitializedError(sdkName: 'DD_RUM' | 'DD_LOGS', initConfiguration: InitConfiguration) {\n if (!initConfiguration.silentMultipleInit) {\n display.error(`${sdkName} is already initialized.`)\n }\n}\n", "import { monitor } from '../tools/monitor'\nimport { getZoneJsOriginalValue } from '../tools/getZoneJsOriginalValue'\nimport type { CookieStore, CookieStoreEventMap, VisualViewport, VisualViewportEventMap } from './browser.types'\n\nexport type TrustableEvent = E & { __ddIsTrusted?: boolean }\n\nexport const enum DOM_EVENT {\n BEFORE_UNLOAD = 'beforeunload',\n CLICK = 'click',\n DBL_CLICK = 'dblclick',\n KEY_DOWN = 'keydown',\n LOAD = 'load',\n POP_STATE = 'popstate',\n SCROLL = 'scroll',\n TOUCH_START = 'touchstart',\n TOUCH_END = 'touchend',\n TOUCH_MOVE = 'touchmove',\n VISIBILITY_CHANGE = 'visibilitychange',\n PAGE_SHOW = 'pageshow',\n FREEZE = 'freeze',\n RESUME = 'resume',\n DOM_CONTENT_LOADED = 'DOMContentLoaded',\n POINTER_DOWN = 'pointerdown',\n POINTER_UP = 'pointerup',\n POINTER_CANCEL = 'pointercancel',\n HASH_CHANGE = 'hashchange',\n PAGE_HIDE = 'pagehide',\n MOUSE_DOWN = 'mousedown',\n MOUSE_UP = 'mouseup',\n MOUSE_MOVE = 'mousemove',\n FOCUS = 'focus',\n BLUR = 'blur',\n CONTEXT_MENU = 'contextmenu',\n RESIZE = 'resize',\n CHANGE = 'change',\n INPUT = 'input',\n PLAY = 'play',\n PAUSE = 'pause',\n SECURITY_POLICY_VIOLATION = 'securitypolicyviolation',\n SELECTION_CHANGE = 'selectionchange',\n STORAGE = 'storage',\n}\n\ninterface AddEventListenerOptions {\n once?: boolean\n capture?: boolean\n passive?: boolean\n}\n\ntype EventMapFor = T extends Window\n ? WindowEventMap & {\n // TS 4.9.5 does not support `freeze` and `resume` events yet\n freeze: Event\n resume: Event\n // TS 4.9.5 does not define `visibilitychange` on Window (only Document)\n visibilitychange: Event\n }\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends VisualViewport\n ? VisualViewportEventMap\n : T extends ShadowRoot\n ? // ShadowRootEventMap is not yet defined in our supported TS version. Instead, use\n // GlobalEventHandlersEventMap which is more than enough as we only need to listen for events bubbling\n // through the ShadowRoot like \"change\" or \"input\"\n GlobalEventHandlersEventMap\n : T extends XMLHttpRequest\n ? XMLHttpRequestEventMap\n : T extends Performance\n ? PerformanceEventMap\n : T extends Worker\n ? WorkerEventMap\n : T extends CookieStore\n ? CookieStoreEventMap\n : Record\n\n/**\n * Add an event listener to an event target object (Window, Element, mock object...). This provides\n * a few conveniences compared to using `element.addEventListener` directly:\n *\n * * supports IE11 by: using an option object only if needed and emulating the `once` option\n *\n * * wraps the listener with a `monitor` function\n *\n * * returns a `stop` function to remove the listener\n */\nexport function addEventListener & string>(\n configuration: { allowUntrustedEvents?: boolean | undefined },\n eventTarget: Target,\n eventName: EventName,\n listener: (event: EventMapFor[EventName] & { type: EventName }) => void,\n options?: AddEventListenerOptions\n) {\n return addEventListeners(configuration, eventTarget, [eventName], listener, options)\n}\n\n/**\n * Add event listeners to an event target object (Window, Element, mock object...). This provides\n * a few conveniences compared to using `element.addEventListener` directly:\n *\n * * supports IE11 by: using an option object only if needed and emulating the `once` option\n *\n * * wraps the listener with a `monitor` function\n *\n * * returns a `stop` function to remove the listener\n *\n * * with `once: true`, the listener will be called at most once, even if different events are listened\n */\nexport function addEventListeners & string>(\n configuration: { allowUntrustedEvents?: boolean | undefined },\n eventTarget: Target,\n eventNames: EventName[],\n listener: (event: EventMapFor[EventName] & { type: EventName }) => void,\n { once, capture, passive }: AddEventListenerOptions = {}\n) {\n const listenerWithMonitor = monitor((event: TrustableEvent) => {\n if (!event.isTrusted && !event.__ddIsTrusted && !configuration.allowUntrustedEvents) {\n return\n }\n if (once) {\n stop()\n }\n listener(event as unknown as EventMapFor[EventName] & { type: EventName })\n })\n\n const options = passive ? { capture, passive } : capture\n\n // Use the window.EventTarget.prototype when possible to avoid wrong overrides (e.g: https://github.com/salesforce/lwc/issues/1824)\n const listenerTarget =\n window.EventTarget && eventTarget instanceof EventTarget ? window.EventTarget.prototype : eventTarget\n\n const add = getZoneJsOriginalValue(listenerTarget, 'addEventListener')\n eventNames.forEach((eventName) => add.call(eventTarget, eventName, listenerWithMonitor, options))\n\n function stop() {\n const remove = getZoneJsOriginalValue(listenerTarget, 'removeEventListener')\n eventNames.forEach((eventName) => remove.call(eventTarget, eventName, listenerWithMonitor, options))\n }\n\n return {\n stop,\n }\n}\n", "import { toStackTraceString } from '../../tools/stackTrace/handlingStack'\nimport { monitor } from '../../tools/monitor'\nimport { mergeObservables, Observable } from '../../tools/observable'\nimport { addEventListener, DOM_EVENT } from '../../browser/addEventListener'\nimport { safeTruncate } from '../../tools/utils/stringUtils'\nimport type { Configuration } from '../configuration'\nimport type { RawError } from '../error/error.types'\nimport { ErrorHandling, ErrorSource } from '../error/error.types'\nimport { clocksNow } from '../../tools/utils/timeUtils'\nimport type { ReportType, InterventionReport, DeprecationReport } from './browser.types'\n\nexport const RawReportType = {\n intervention: 'intervention',\n deprecation: 'deprecation',\n cspViolation: 'csp_violation',\n} as const\n\nexport type RawReportType = (typeof RawReportType)[keyof typeof RawReportType]\n\nexport type RawReportError = RawError & {\n originalError: SecurityPolicyViolationEvent | DeprecationReport | InterventionReport\n}\n\nexport function initReportObservable(configuration: Configuration, apis: RawReportType[]) {\n const observables: Array> = []\n\n if (apis.includes(RawReportType.cspViolation)) {\n observables.push(createCspViolationReportObservable(configuration))\n }\n\n const reportTypes = apis.filter((api: RawReportType): api is ReportType => api !== RawReportType.cspViolation)\n if (reportTypes.length) {\n observables.push(createReportObservable(reportTypes))\n }\n\n return mergeObservables(...observables)\n}\n\nfunction createReportObservable(reportTypes: ReportType[]) {\n return new Observable((observable) => {\n if (!window.ReportingObserver) {\n return\n }\n\n const handleReports = monitor((reports: Array, _: ReportingObserver) =>\n reports.forEach((report) => observable.notify(buildRawReportErrorFromReport(report)))\n ) as ReportingObserverCallback\n\n const observer = new window.ReportingObserver(handleReports, {\n types: reportTypes,\n buffered: true,\n })\n\n observer.observe()\n return () => {\n observer.disconnect()\n }\n })\n}\n\nfunction createCspViolationReportObservable(configuration: Configuration) {\n return new Observable((observable) => {\n const { stop } = addEventListener(configuration, document, DOM_EVENT.SECURITY_POLICY_VIOLATION, (event) => {\n observable.notify(buildRawReportErrorFromCspViolation(event))\n })\n\n return stop\n })\n}\n\nfunction buildRawReportErrorFromReport(report: DeprecationReport | InterventionReport): RawReportError {\n const { type, body } = report\n\n return buildRawReportError({\n type: body.id,\n message: `${type}: ${body.message}`,\n originalError: report,\n stack: buildStack(body.id, body.message, body.sourceFile, body.lineNumber, body.columnNumber),\n })\n}\n\nfunction buildRawReportErrorFromCspViolation(event: SecurityPolicyViolationEvent): RawReportError {\n const message = `'${event.blockedURI}' blocked by '${event.effectiveDirective}' directive`\n return buildRawReportError({\n type: event.effectiveDirective,\n message: `${RawReportType.cspViolation}: ${message}`,\n originalError: event,\n csp: {\n disposition: event.disposition,\n },\n stack: buildStack(\n event.effectiveDirective,\n event.originalPolicy\n ? `${message} of the policy \"${safeTruncate(event.originalPolicy, 100, '...')}\"`\n : 'no policy',\n event.sourceFile,\n event.lineNumber,\n event.columnNumber\n ),\n })\n}\n\nfunction buildRawReportError(partial: Omit): RawReportError {\n return {\n startClocks: clocksNow(),\n source: ErrorSource.REPORT,\n handling: ErrorHandling.UNHANDLED,\n ...partial,\n }\n}\n\nfunction buildStack(\n name: string,\n message: string,\n sourceFile: string | null,\n lineNumber: number | null,\n columnNumber: number | null\n): string | undefined {\n return sourceFile\n ? toStackTraceString({\n name,\n message,\n stack: [\n {\n func: '?',\n url: sourceFile,\n line: lineNumber ?? undefined,\n column: columnNumber ?? undefined,\n },\n ],\n })\n : undefined\n}\n", "interface BrowserWindow {\n __ddBrowserSdkExtensionCallback?: (message: unknown) => void\n}\n\ntype ExtensionMessageType = 'logs' | 'record' | 'rum' | 'telemetry'\n\nexport function sendToExtension(type: ExtensionMessageType, payload: unknown) {\n const callback = (window as BrowserWindow).__ddBrowserSdkExtensionCallback\n if (callback) {\n callback({ type, payload })\n }\n}\n", "/**\n * Similar to `typeof`, but distinguish plain objects from `null` and arrays\n */\nexport function getType(value: unknown) {\n if (value === null) {\n return 'null'\n }\n if (Array.isArray(value)) {\n return 'array'\n }\n return typeof value\n}\n", "import { getType } from './utils/typeUtils'\n\ntype Merged =\n // case 1 - source is undefined - return destination\n TSource extends undefined\n ? TDestination\n : // case 2 - destination is undefined - return source\n TDestination extends undefined\n ? TSource\n : // case 3 - source is an array - see if it merges or overwrites\n TSource extends any[]\n ? TDestination extends any[]\n ? TDestination & TSource\n : TSource\n : // case 4 - source is an object - see if it merges or overwrites\n TSource extends object\n ? TDestination extends object\n ? TDestination extends any[]\n ? TSource\n : TDestination & TSource\n : TSource\n : // case 5 - cannot merge - return source\n TSource\n\n/**\n * Iterate over source and affect its sub values into destination, recursively.\n * If the source and destination can't be merged, return source.\n */\nexport function mergeInto(\n destination: D,\n source: S,\n circularReferenceChecker = createCircularReferenceChecker()\n): Merged {\n // ignore the source if it is undefined\n if (source === undefined) {\n return destination as Merged\n }\n\n if (typeof source !== 'object' || source === null) {\n // primitive values - just return source\n return source as Merged\n } else if (source instanceof Date) {\n return new Date(source.getTime()) as unknown as Merged\n } else if (source instanceof RegExp) {\n const flags =\n source.flags ||\n // old browsers compatibility\n [\n source.global ? 'g' : '',\n source.ignoreCase ? 'i' : '',\n source.multiline ? 'm' : '',\n source.sticky ? 'y' : '',\n source.unicode ? 'u' : '',\n ].join('')\n return new RegExp(source.source, flags) as unknown as Merged\n }\n\n if (circularReferenceChecker.hasAlreadyBeenSeen(source)) {\n // remove circular references\n return undefined as unknown as Merged\n } else if (Array.isArray(source)) {\n const merged: any[] = Array.isArray(destination) ? destination : []\n for (let i = 0; i < source.length; ++i) {\n merged[i] = mergeInto(merged[i], source[i], circularReferenceChecker)\n }\n return merged as unknown as Merged\n }\n\n const merged = getType(destination) === 'object' ? (destination as Record) : {}\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n merged[key] = mergeInto(merged[key], source[key], circularReferenceChecker)\n }\n }\n return merged as unknown as Merged\n}\n\n/**\n * A simplistic implementation of a deep clone algorithm.\n * Caveats:\n * - It doesn't maintain prototype chains - don't use with instances of custom classes.\n * - It doesn't handle Map and Set\n */\nexport function deepClone(value: T): T {\n return mergeInto(undefined, value) as T\n}\n\ntype Combined = A extends null ? B : B extends null ? A : Merged\n\n/*\n * Performs a deep merge of objects and arrays.\n * - Arguments won't be mutated\n * - Object and arrays in the output value are de-referenced (\"deep cloned\")\n * - Arrays values are merged index by index\n * - Objects are merged by keys\n * - Values get replaced, unless undefined\n */\nexport function combine(a: A, b: B): Combined\nexport function combine(a: A, b: B, c: C): Combined, C>\nexport function combine(a: A, b: B, c: C, d: D): Combined, C>, D>\nexport function combine(\n a: A,\n b: B,\n c: C,\n d: D,\n e: E\n): Combined, C>, D>, E>\nexport function combine(\n a: A,\n b: B,\n c: C,\n d: D,\n e: E,\n f: F\n): Combined, C>, D>, E>, F>\nexport function combine(\n a: A,\n b: B,\n c: C,\n d: D,\n e: E,\n f: F,\n g: G\n): Combined, C>, D>, E>, F>, G>\nexport function combine(\n a: A,\n b: B,\n c: C,\n d: D,\n e: E,\n f: F,\n g: G,\n h: H\n): Combined, C>, D>, E>, F>, G>, H>\nexport function combine(...sources: any[]): unknown {\n let destination: any\n\n for (const source of sources) {\n // Ignore any undefined or null sources.\n if (source === undefined || source === null) {\n continue\n }\n\n destination = mergeInto(destination, source)\n }\n\n return destination as unknown\n}\n\ninterface CircularReferenceChecker {\n hasAlreadyBeenSeen(value: any): boolean\n}\n\nfunction createCircularReferenceChecker(): CircularReferenceChecker {\n if (typeof WeakSet !== 'undefined') {\n const set: WeakSet = new WeakSet()\n return {\n hasAlreadyBeenSeen(value) {\n const has = set.has(value)\n if (!has) {\n set.add(value)\n }\n return has\n },\n }\n }\n const array: any[] = []\n return {\n hasAlreadyBeenSeen(value) {\n const has = array.indexOf(value) >= 0\n if (!has) {\n array.push(value)\n }\n return has\n },\n }\n}\n", "export type NetworkInterface = 'bluetooth' | 'cellular' | 'ethernet' | 'none' | 'wifi' | 'wimax' | 'other' | 'unknown'\nexport type EffectiveType = 'slow-2g' | '2g' | '3g' | '4g'\n\ninterface BrowserNavigator extends Navigator {\n connection?: NetworkInformation\n}\n\nexport interface NetworkInformation {\n type?: NetworkInterface\n effectiveType?: EffectiveType\n saveData: boolean\n}\n\nexport interface Connectivity {\n status: 'connected' | 'not_connected'\n interfaces?: NetworkInterface[]\n effective_type?: EffectiveType\n [key: string]: unknown\n}\n\nexport function getConnectivity(): Connectivity {\n const navigator = window.navigator as BrowserNavigator\n return {\n status: navigator.onLine ? 'connected' : 'not_connected',\n interfaces: navigator.connection && navigator.connection.type ? [navigator.connection.type] : undefined,\n effective_type: navigator.connection?.effectiveType,\n }\n}\n", "export * from './connectivity'\n", "export function removeDuplicates(array: T[]) {\n const set = new Set()\n array.forEach((item) => set.add(item))\n return Array.from(set)\n}\n\nexport function removeItem(array: T[], item: T) {\n const index = array.indexOf(item)\n if (index >= 0) {\n array.splice(index, 1)\n }\n}\n", "import { removeItem } from './utils/arrayUtils'\n\nconst BUFFER_LIMIT = 500\n\nexport interface BoundedBuffer {\n add: (callback: (arg: T) => void) => void\n remove: (callback: (arg: T) => void) => void\n drain: (arg: T) => void\n}\n\nexport function createBoundedBuffer(): BoundedBuffer {\n const buffer: Array<(arg: T) => void> = []\n\n const add: BoundedBuffer['add'] = (callback: (arg: T) => void) => {\n const length = buffer.push(callback)\n if (length > BUFFER_LIMIT) {\n buffer.splice(0, 1)\n }\n }\n\n const remove: BoundedBuffer['remove'] = (callback: (arg: T) => void) => {\n removeItem(buffer, callback)\n }\n\n const drain = (arg: T) => {\n buffer.forEach((callback) => callback(arg))\n buffer.length = 0\n }\n\n return {\n add,\n remove,\n drain,\n }\n}\n", "import type { TelemetryEvent, TelemetryConfigurationEvent, TelemetryUsageEvent } from './telemetryEvent.types'\n\nexport const TelemetryType = {\n log: 'log',\n configuration: 'configuration',\n usage: 'usage',\n} as const\n\nexport const enum StatusType {\n debug = 'debug',\n error = 'error',\n}\n\nexport interface RuntimeEnvInfo {\n is_local_file: boolean\n is_worker: boolean\n}\n\nexport type RawTelemetryEvent = TelemetryEvent['telemetry']\nexport type RawTelemetryConfiguration = TelemetryConfigurationEvent['telemetry']['configuration']\nexport type RawTelemetryUsage = TelemetryUsageEvent['telemetry']['usage']\n", "import type { Context } from '../../tools/serialisation/context'\nimport { ConsoleApiName } from '../../tools/display'\nimport { NO_ERROR_STACK_PRESENT_MESSAGE, isError } from '../error/error'\nimport { toStackTraceString } from '../../tools/stackTrace/handlingStack'\nimport { getExperimentalFeatures } from '../../tools/experimentalFeatures'\nimport type { Configuration } from '../configuration'\nimport { INTAKE_SITE_STAGING, INTAKE_SITE_US1_FED } from '../configuration'\nimport { Observable } from '../../tools/observable'\nimport { timeStampNow } from '../../tools/utils/timeUtils'\nimport { displayIfDebugEnabled, startMonitorErrorCollection } from '../../tools/monitor'\nimport { sendToExtension } from '../../tools/sendToExtension'\nimport { performDraw } from '../../tools/utils/numberUtils'\nimport { jsonStringify } from '../../tools/serialisation/jsonStringify'\nimport { combine } from '../../tools/mergeInto'\nimport { NonErrorPrefix } from '../error/error.types'\nimport type { StackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { getConnectivity } from '../connectivity'\nimport { createBoundedBuffer } from '../../tools/boundedBuffer'\nimport type { TelemetryEvent } from './telemetryEvent.types'\nimport type {\n RawTelemetryConfiguration,\n RawTelemetryEvent,\n RuntimeEnvInfo,\n RawTelemetryUsage,\n} from './rawTelemetryEvent.types'\nimport { StatusType, TelemetryType } from './rawTelemetryEvent.types'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\ndeclare const __BUILD_ENV__SDK_SETUP__: string\n\nconst ALLOWED_FRAME_URLS = [\n 'https://www.datadoghq-browser-agent.com',\n 'https://www.datad0g-browser-agent.com',\n 'https://d3uc069fcn7uxw.cloudfront.net',\n 'https://d20xtzwzcl0ceb.cloudfront.net',\n 'http://localhost',\n '',\n]\n\nexport const enum TelemetryService {\n LOGS = 'browser-logs-sdk',\n RUM = 'browser-rum-sdk',\n}\n\nexport interface Telemetry {\n setContextProvider: (provider: () => Context) => void\n observable: Observable\n enabled: boolean\n}\n\nconst TELEMETRY_EXCLUDED_SITES: string[] = [INTAKE_SITE_US1_FED]\n\n// eslint-disable-next-line local-rules/disallow-side-effects\nlet preStartTelemetryBuffer = createBoundedBuffer()\nlet onRawTelemetryEventCollected = (event: RawTelemetryEvent) => {\n preStartTelemetryBuffer.add(() => onRawTelemetryEventCollected(event))\n}\n\nexport function startTelemetry(telemetryService: TelemetryService, configuration: Configuration): Telemetry {\n let contextProvider: () => Context\n const observable = new Observable()\n const alreadySentEvents = new Set()\n\n const telemetryEnabled =\n !TELEMETRY_EXCLUDED_SITES.includes(configuration.site) && performDraw(configuration.telemetrySampleRate)\n\n const telemetryEnabledPerType = {\n [TelemetryType.log]: telemetryEnabled,\n [TelemetryType.configuration]: telemetryEnabled && performDraw(configuration.telemetryConfigurationSampleRate),\n [TelemetryType.usage]: telemetryEnabled && performDraw(configuration.telemetryUsageSampleRate),\n }\n\n const runtimeEnvInfo = getRuntimeEnvInfo()\n onRawTelemetryEventCollected = (rawEvent: RawTelemetryEvent) => {\n const stringifiedEvent = jsonStringify(rawEvent)!\n if (\n telemetryEnabledPerType[rawEvent.type!] &&\n alreadySentEvents.size < configuration.maxTelemetryEventsPerPage &&\n !alreadySentEvents.has(stringifiedEvent)\n ) {\n const event = toTelemetryEvent(telemetryService, rawEvent, runtimeEnvInfo)\n observable.notify(event)\n sendToExtension('telemetry', event)\n alreadySentEvents.add(stringifiedEvent)\n }\n }\n startMonitorErrorCollection(addTelemetryError)\n\n function toTelemetryEvent(\n telemetryService: TelemetryService,\n event: RawTelemetryEvent,\n runtimeEnvInfo: RuntimeEnvInfo\n ): TelemetryEvent & Context {\n return combine(\n {\n type: 'telemetry' as const,\n date: timeStampNow(),\n service: telemetryService,\n version: __BUILD_ENV__SDK_VERSION__,\n source: 'browser' as const,\n _dd: {\n format_version: 2 as const,\n },\n telemetry: combine(event, {\n runtime_env: runtimeEnvInfo,\n connectivity: getConnectivity(),\n sdk_setup: __BUILD_ENV__SDK_SETUP__,\n }),\n experimental_features: Array.from(getExperimentalFeatures()),\n },\n contextProvider !== undefined ? contextProvider() : {}\n ) as TelemetryEvent & Context\n }\n\n return {\n setContextProvider: (provider: () => Context) => {\n contextProvider = provider\n },\n observable,\n enabled: telemetryEnabled,\n }\n}\nfunction getRuntimeEnvInfo(): RuntimeEnvInfo {\n return {\n is_local_file: window.location.protocol === 'file:',\n is_worker: 'WorkerGlobalScope' in self,\n }\n}\n\nexport function startFakeTelemetry() {\n const events: RawTelemetryEvent[] = []\n\n onRawTelemetryEventCollected = (event: RawTelemetryEvent) => {\n events.push(event)\n }\n\n return events\n}\n\n// need to be called after telemetry context is provided and observers are registered\nexport function drainPreStartTelemetry() {\n preStartTelemetryBuffer.drain()\n}\n\nexport function resetTelemetry() {\n preStartTelemetryBuffer = createBoundedBuffer()\n onRawTelemetryEventCollected = (event: RawTelemetryEvent) => {\n preStartTelemetryBuffer.add(() => onRawTelemetryEventCollected(event))\n }\n}\n\n/**\n * Avoid mixing telemetry events from different data centers\n * but keep replicating staging events for reliability\n */\nexport function isTelemetryReplicationAllowed(configuration: Configuration) {\n return configuration.site === INTAKE_SITE_STAGING\n}\n\nexport function addTelemetryDebug(message: string, context?: Context) {\n displayIfDebugEnabled(ConsoleApiName.debug, message, context)\n onRawTelemetryEventCollected({\n type: TelemetryType.log,\n message,\n status: StatusType.debug,\n ...context,\n })\n}\n\nexport function addTelemetryError(e: unknown, context?: Context) {\n onRawTelemetryEventCollected({\n type: TelemetryType.log,\n status: StatusType.error,\n ...formatError(e),\n ...context,\n })\n}\n\nexport function addTelemetryConfiguration(configuration: RawTelemetryConfiguration) {\n onRawTelemetryEventCollected({\n type: TelemetryType.configuration,\n configuration,\n })\n}\n\nexport function addTelemetryUsage(usage: RawTelemetryUsage) {\n onRawTelemetryEventCollected({\n type: TelemetryType.usage,\n usage,\n })\n}\n\nexport function formatError(e: unknown) {\n if (isError(e)) {\n const stackTrace = computeStackTrace(e)\n return {\n error: {\n kind: stackTrace.name,\n stack: toStackTraceString(scrubCustomerFrames(stackTrace)),\n },\n message: stackTrace.message!,\n }\n }\n return {\n error: {\n stack: NO_ERROR_STACK_PRESENT_MESSAGE,\n },\n message: `${NonErrorPrefix.UNCAUGHT} ${jsonStringify(e)!}`,\n }\n}\n\nexport function scrubCustomerFrames(stackTrace: StackTrace) {\n stackTrace.stack = stackTrace.stack.filter(\n (frame) => !frame.url || ALLOWED_FRAME_URLS.some((allowedFrameUrl) => frame.url!.startsWith(allowedFrameUrl))\n )\n return stackTrace\n}\n", "export type { Telemetry } from './telemetry'\nexport {\n TelemetryService,\n addTelemetryDebug,\n addTelemetryError,\n startFakeTelemetry,\n resetTelemetry,\n startTelemetry,\n isTelemetryReplicationAllowed,\n addTelemetryConfiguration,\n addTelemetryUsage,\n drainPreStartTelemetry,\n} from './telemetry'\n\nexport * from './rawTelemetryEvent.types'\nexport type {\n TelemetryEvent,\n TelemetryErrorEvent,\n TelemetryDebugEvent,\n TelemetryConfigurationEvent,\n TelemetryUsageEvent,\n} from './telemetryEvent.types'\n", "import type { Context } from './serialisation/context'\nimport { setInterval, clearInterval } from './timer'\nimport type { TimeoutId } from './timer'\nimport { removeItem } from './utils/arrayUtils'\nimport type { Duration, RelativeTime } from './utils/timeUtils'\nimport { addDuration, relativeNow, ONE_MINUTE } from './utils/timeUtils'\n\nconst END_OF_TIMES = Infinity as RelativeTime\n\nexport interface ValueHistoryEntry {\n startTime: RelativeTime\n endTime: RelativeTime\n value: T\n remove(): void\n close(endTime: RelativeTime): void\n}\n\nexport const CLEAR_OLD_VALUES_INTERVAL = ONE_MINUTE\n\n/**\n * Store and keep track of values spans. This whole cache assumes that values are added in\n * chronological order (i.e. all entries have an increasing start time).\n */\nexport interface ValueHistory {\n add: (value: Value, startTime: RelativeTime) => ValueHistoryEntry\n find: (startTime?: RelativeTime, options?: { returnInactive: boolean }) => Value | undefined\n\n closeActive: (endTime: RelativeTime) => void\n findAll: (startTime?: RelativeTime, duration?: Duration) => Value[]\n reset: () => void\n stop: () => void\n\n getAllEntries: () => Context[]\n getDeletedEntries: () => RelativeTime[]\n}\n\nlet cleanupHistoriesInterval: TimeoutId | null = null\n\nconst cleanupTasks: Set<() => void> = new Set()\n\nfunction cleanupHistories() {\n cleanupTasks.forEach((task) => task())\n}\n\nexport function createValueHistory({\n expireDelay,\n maxEntries,\n}: {\n expireDelay: number\n maxEntries?: number\n}): ValueHistory {\n let entries: Array> = []\n const deletedEntries: RelativeTime[] = []\n\n if (!cleanupHistoriesInterval) {\n cleanupHistoriesInterval = setInterval(() => cleanupHistories(), CLEAR_OLD_VALUES_INTERVAL)\n }\n\n const clearExpiredValues = () => {\n const oldTimeThreshold = relativeNow() - expireDelay\n while (entries.length > 0 && entries[entries.length - 1].endTime < oldTimeThreshold) {\n const entry = entries.pop()\n if (entry) {\n deletedEntries.push(entry.startTime)\n }\n }\n }\n\n cleanupTasks.add(clearExpiredValues)\n\n /**\n * Add a value to the history associated with a start time. Returns a reference to this newly\n * added entry that can be removed or closed.\n */\n function add(value: Value, startTime: RelativeTime): ValueHistoryEntry {\n const entry: ValueHistoryEntry = {\n value,\n startTime,\n endTime: END_OF_TIMES,\n remove: () => {\n removeItem(entries, entry)\n },\n close: (endTime: RelativeTime) => {\n entry.endTime = endTime\n },\n }\n\n if (maxEntries && entries.length >= maxEntries) {\n entries.pop()\n }\n\n entries.unshift(entry)\n\n return entry\n }\n\n /**\n * Return the latest value that was active during `startTime`, or the currently active value\n * if no `startTime` is provided. This method assumes that entries are not overlapping.\n *\n * If `option.returnInactive` is true, returns the value at `startTime` (active or not).\n */\n function find(\n startTime: RelativeTime = END_OF_TIMES,\n options: { returnInactive: boolean } = { returnInactive: false }\n ): Value | undefined {\n for (const entry of entries) {\n if (entry.startTime <= startTime) {\n if (options.returnInactive || startTime <= entry.endTime) {\n return entry.value\n }\n break\n }\n }\n }\n\n /**\n * Helper function to close the currently active value, if any. This method assumes that entries\n * are not overlapping.\n */\n function closeActive(endTime: RelativeTime) {\n const latestEntry = entries[0]\n if (latestEntry && latestEntry.endTime === END_OF_TIMES) {\n latestEntry.close(endTime)\n }\n }\n\n /**\n * Return all values with an active period overlapping with the duration,\n * or all values that were active during `startTime` if no duration is provided,\n * or all currently active values if no `startTime` is provided.\n */\n function findAll(startTime: RelativeTime = END_OF_TIMES, duration = 0 as Duration): Value[] {\n const endTime = addDuration(startTime, duration)\n return entries\n .filter((entry) => entry.startTime <= endTime && startTime <= entry.endTime)\n .map((entry) => entry.value)\n }\n\n function getAllEntries() {\n return entries.map(({ startTime, endTime, value }) => ({\n startTime,\n endTime: endTime === END_OF_TIMES ? 'Infinity' : endTime,\n value,\n })) as Context[]\n }\n\n function getDeletedEntries() {\n return deletedEntries\n }\n\n /**\n * Remove all entries from this collection.\n */\n function reset() {\n entries = []\n }\n\n /**\n * Stop internal garbage collection of past entries.\n */\n function stop() {\n cleanupTasks.delete(clearExpiredValues)\n if (cleanupTasks.size === 0 && cleanupHistoriesInterval) {\n clearInterval(cleanupHistoriesInterval)\n cleanupHistoriesInterval = null\n }\n }\n\n return { add, find, closeActive, findAll, reset, stop, getAllEntries, getDeletedEntries }\n}\n", "import { Observable } from '../../tools/observable'\nimport type { Context } from '../../tools/serialisation/context'\nimport { createValueHistory } from '../../tools/valueHistory'\nimport type { RelativeTime } from '../../tools/utils/timeUtils'\nimport { relativeNow, clocksOrigin, ONE_MINUTE } from '../../tools/utils/timeUtils'\nimport { DOM_EVENT, addEventListener, addEventListeners } from '../../browser/addEventListener'\nimport { clearInterval, setInterval } from '../../tools/timer'\nimport type { Configuration } from '../configuration'\nimport type { TrackingConsentState } from '../trackingConsent'\nimport { SESSION_TIME_OUT_DELAY } from './sessionConstants'\nimport { startSessionStore } from './sessionStore'\nimport type { SessionState } from './sessionState'\n\nexport interface SessionManager {\n findSession: (\n startTime?: RelativeTime,\n options?: { returnInactive: boolean }\n ) => SessionContext | undefined\n renewObservable: Observable\n expireObservable: Observable\n sessionStateUpdateObservable: Observable<{ previousState: SessionState; newState: SessionState }>\n expire: () => void\n updateSessionState: (state: Partial) => void\n}\n\nexport interface SessionContext extends Context {\n id: string\n trackingType: TrackingType\n isReplayForced: boolean\n anonymousId: string | undefined\n}\n\nexport const VISIBILITY_CHECK_DELAY = ONE_MINUTE\nconst SESSION_CONTEXT_TIMEOUT_DELAY = SESSION_TIME_OUT_DELAY\nlet stopCallbacks: Array<() => void> = []\n\nexport function startSessionManager(\n configuration: Configuration,\n productKey: string,\n computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean },\n trackingConsentState: TrackingConsentState\n): SessionManager {\n const renewObservable = new Observable()\n const expireObservable = new Observable()\n\n // TODO - Improve configuration type and remove assertion\n const sessionStore = startSessionStore(\n configuration.sessionStoreStrategyType!,\n configuration,\n productKey,\n computeSessionState\n )\n stopCallbacks.push(() => sessionStore.stop())\n\n const sessionContextHistory = createValueHistory>({\n expireDelay: SESSION_CONTEXT_TIMEOUT_DELAY,\n })\n stopCallbacks.push(() => sessionContextHistory.stop())\n\n sessionStore.renewObservable.subscribe(() => {\n sessionContextHistory.add(buildSessionContext(), relativeNow())\n renewObservable.notify()\n })\n sessionStore.expireObservable.subscribe(() => {\n expireObservable.notify()\n sessionContextHistory.closeActive(relativeNow())\n })\n\n // We expand/renew session unconditionally as tracking consent is always granted when the session\n // manager is started.\n sessionStore.expandOrRenewSession()\n sessionContextHistory.add(buildSessionContext(), clocksOrigin().relative)\n\n trackingConsentState.observable.subscribe(() => {\n if (trackingConsentState.isGranted()) {\n sessionStore.expandOrRenewSession()\n } else {\n sessionStore.expire()\n }\n })\n\n trackActivity(configuration, () => {\n if (trackingConsentState.isGranted()) {\n sessionStore.expandOrRenewSession()\n }\n })\n trackVisibility(configuration, () => sessionStore.expandSession())\n trackResume(configuration, () => sessionStore.restartSession())\n\n function buildSessionContext() {\n return {\n id: sessionStore.getSession().id!,\n trackingType: sessionStore.getSession()[productKey] as TrackingType,\n isReplayForced: !!sessionStore.getSession().forcedReplay,\n anonymousId: sessionStore.getSession().anonymousId,\n }\n }\n\n return {\n findSession: (startTime, options) => sessionContextHistory.find(startTime, options),\n renewObservable,\n expireObservable,\n sessionStateUpdateObservable: sessionStore.sessionStateUpdateObservable,\n expire: sessionStore.expire,\n updateSessionState: sessionStore.updateSessionState,\n }\n}\n\nexport function stopSessionManager() {\n stopCallbacks.forEach((e) => e())\n stopCallbacks = []\n}\n\nfunction trackActivity(configuration: Configuration, expandOrRenewSession: () => void) {\n const { stop } = addEventListeners(\n configuration,\n window,\n [DOM_EVENT.CLICK, DOM_EVENT.TOUCH_START, DOM_EVENT.KEY_DOWN, DOM_EVENT.SCROLL],\n expandOrRenewSession,\n { capture: true, passive: true }\n )\n stopCallbacks.push(stop)\n}\n\nfunction trackVisibility(configuration: Configuration, expandSession: () => void) {\n const expandSessionWhenVisible = () => {\n if (document.visibilityState === 'visible') {\n expandSession()\n }\n }\n\n const { stop } = addEventListener(configuration, document, DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible)\n stopCallbacks.push(stop)\n\n const visibilityCheckInterval = setInterval(expandSessionWhenVisible, VISIBILITY_CHECK_DELAY)\n stopCallbacks.push(() => {\n clearInterval(visibilityCheckInterval)\n })\n}\n\nfunction trackResume(configuration: Configuration, cb: () => void) {\n const { stop } = addEventListener(configuration, window, DOM_EVENT.RESUME, cb, { capture: true })\n stopCallbacks.push(stop)\n}\n", "export function isServerError(status: number) {\n return status >= 500\n}\n\nexport function tryToClone(response: Response): Response | undefined {\n try {\n return response.clone()\n } catch {\n // clone can throw if the response has already been used by another instrumentation or is disturbed\n return\n }\n}\n", "import type { TrackType } from '../domain/configuration'\nimport { setTimeout } from '../tools/timer'\nimport { clocksNow, ONE_MINUTE, ONE_SECOND } from '../tools/utils/timeUtils'\nimport { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '../tools/utils/byteUtils'\nimport { isServerError } from '../tools/utils/responseUtils'\nimport type { RawError } from '../domain/error/error.types'\nimport { ErrorSource } from '../domain/error/error.types'\nimport type { Payload, HttpResponse } from './httpRequest'\n\nexport const MAX_ONGOING_BYTES_COUNT = 80 * ONE_KIBI_BYTE\nexport const MAX_ONGOING_REQUESTS = 32\nexport const MAX_QUEUE_BYTES_COUNT = 3 * ONE_MEBI_BYTE\nexport const MAX_BACKOFF_TIME = ONE_MINUTE\nexport const INITIAL_BACKOFF_TIME = ONE_SECOND\n\nconst enum TransportStatus {\n UP,\n FAILURE_DETECTED,\n DOWN,\n}\n\nconst enum RetryReason {\n AFTER_SUCCESS,\n AFTER_RESUME,\n}\n\nexport interface RetryState {\n transportStatus: TransportStatus\n currentBackoffTime: number\n bandwidthMonitor: ReturnType\n queuedPayloads: ReturnType\n queueFullReported: boolean\n}\n\ntype SendStrategy = (payload: Payload, onResponse: (r: HttpResponse) => void) => void\n\nexport function sendWithRetryStrategy(\n payload: Payload,\n state: RetryState,\n sendStrategy: SendStrategy,\n trackType: TrackType,\n reportError: (error: RawError) => void\n) {\n if (\n state.transportStatus === TransportStatus.UP &&\n state.queuedPayloads.size() === 0 &&\n state.bandwidthMonitor.canHandle(payload)\n ) {\n send(payload, state, sendStrategy, {\n onSuccess: () => retryQueuedPayloads(RetryReason.AFTER_SUCCESS, state, sendStrategy, trackType, reportError),\n onFailure: () => {\n state.queuedPayloads.enqueue(payload)\n scheduleRetry(state, sendStrategy, trackType, reportError)\n },\n })\n } else {\n state.queuedPayloads.enqueue(payload)\n }\n}\n\nfunction scheduleRetry(\n state: RetryState,\n sendStrategy: SendStrategy,\n trackType: TrackType,\n reportError: (error: RawError) => void\n) {\n if (state.transportStatus !== TransportStatus.DOWN) {\n return\n }\n setTimeout(() => {\n const payload = state.queuedPayloads.first()\n send(payload, state, sendStrategy, {\n onSuccess: () => {\n state.queuedPayloads.dequeue()\n state.currentBackoffTime = INITIAL_BACKOFF_TIME\n retryQueuedPayloads(RetryReason.AFTER_RESUME, state, sendStrategy, trackType, reportError)\n },\n onFailure: () => {\n state.currentBackoffTime = Math.min(MAX_BACKOFF_TIME, state.currentBackoffTime * 2)\n scheduleRetry(state, sendStrategy, trackType, reportError)\n },\n })\n }, state.currentBackoffTime)\n}\n\nfunction send(\n payload: Payload,\n state: RetryState,\n sendStrategy: SendStrategy,\n { onSuccess, onFailure }: { onSuccess: () => void; onFailure: () => void }\n) {\n state.bandwidthMonitor.add(payload)\n sendStrategy(payload, (response) => {\n state.bandwidthMonitor.remove(payload)\n if (!shouldRetryRequest(response)) {\n state.transportStatus = TransportStatus.UP\n onSuccess()\n } else {\n // do not consider transport down if another ongoing request could succeed\n state.transportStatus =\n state.bandwidthMonitor.ongoingRequestCount > 0 ? TransportStatus.FAILURE_DETECTED : TransportStatus.DOWN\n payload.retry = {\n count: payload.retry ? payload.retry.count + 1 : 1,\n lastFailureStatus: response.status,\n }\n onFailure()\n }\n })\n}\n\nfunction retryQueuedPayloads(\n reason: RetryReason,\n state: RetryState,\n sendStrategy: SendStrategy,\n trackType: TrackType,\n reportError: (error: RawError) => void\n) {\n if (reason === RetryReason.AFTER_SUCCESS && state.queuedPayloads.isFull() && !state.queueFullReported) {\n reportError({\n message: `Reached max ${trackType} events size queued for upload: ${MAX_QUEUE_BYTES_COUNT / ONE_MEBI_BYTE}MiB`,\n source: ErrorSource.AGENT,\n startClocks: clocksNow(),\n })\n state.queueFullReported = true\n }\n const previousQueue = state.queuedPayloads\n state.queuedPayloads = newPayloadQueue()\n while (previousQueue.size() > 0) {\n sendWithRetryStrategy(previousQueue.dequeue()!, state, sendStrategy, trackType, reportError)\n }\n}\n\nfunction shouldRetryRequest(response: HttpResponse) {\n return (\n response.type !== 'opaque' &&\n ((response.status === 0 && !navigator.onLine) ||\n response.status === 408 ||\n response.status === 429 ||\n isServerError(response.status))\n )\n}\n\nexport function newRetryState(): RetryState {\n return {\n transportStatus: TransportStatus.UP,\n currentBackoffTime: INITIAL_BACKOFF_TIME,\n bandwidthMonitor: newBandwidthMonitor(),\n queuedPayloads: newPayloadQueue(),\n queueFullReported: false,\n }\n}\n\nfunction newPayloadQueue() {\n const queue: Payload[] = []\n return {\n bytesCount: 0,\n enqueue(payload: Payload) {\n if (this.isFull()) {\n return\n }\n queue.push(payload)\n this.bytesCount += payload.bytesCount\n },\n first() {\n return queue[0]\n },\n dequeue() {\n const payload = queue.shift()\n if (payload) {\n this.bytesCount -= payload.bytesCount\n }\n return payload\n },\n size() {\n return queue.length\n },\n isFull() {\n return this.bytesCount >= MAX_QUEUE_BYTES_COUNT\n },\n }\n}\n\nfunction newBandwidthMonitor() {\n return {\n ongoingRequestCount: 0,\n ongoingByteCount: 0,\n canHandle(payload: Payload) {\n return (\n this.ongoingRequestCount === 0 ||\n (this.ongoingByteCount + payload.bytesCount <= MAX_ONGOING_BYTES_COUNT &&\n this.ongoingRequestCount < MAX_ONGOING_REQUESTS)\n )\n },\n add(payload: Payload) {\n this.ongoingRequestCount += 1\n this.ongoingByteCount += payload.bytesCount\n },\n remove(payload: Payload) {\n this.ongoingRequestCount -= 1\n this.ongoingByteCount -= payload.bytesCount\n },\n }\n}\n", "import type { EndpointBuilder } from '../domain/configuration'\nimport type { Context } from '../tools/serialisation/context'\nimport { monitor, monitorError } from '../tools/monitor'\nimport type { RawError } from '../domain/error/error.types'\nimport { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'\n\n/**\n * Use POST request without content type to:\n * - avoid CORS preflight requests\n * - allow usage of sendBeacon\n *\n * multiple elements are sent separated by \\n in order\n * to be parsed correctly without content type header\n */\n\nexport type HttpRequest = ReturnType\n\nexport interface HttpResponse extends Context {\n status: number\n type?: ResponseType\n}\n\nexport interface Payload {\n data: string | FormData | Blob\n bytesCount: number\n retry?: RetryInfo\n encoding?: 'deflate'\n}\n\nexport interface RetryInfo {\n count: number\n lastFailureStatus: number\n}\n\nexport function createHttpRequest(\n endpointBuilder: EndpointBuilder,\n bytesLimit: number,\n reportError: (error: RawError) => void\n) {\n const retryState = newRetryState()\n const sendStrategyForRetry = (payload: Payload, onResponse: (r: HttpResponse) => void) =>\n fetchKeepAliveStrategy(endpointBuilder, bytesLimit, payload, onResponse)\n\n return {\n send: (payload: Payload) => {\n sendWithRetryStrategy(payload, retryState, sendStrategyForRetry, endpointBuilder.trackType, reportError)\n },\n /**\n * Since fetch keepalive behaves like regular fetch on Firefox,\n * keep using sendBeaconStrategy on exit\n */\n sendOnExit: (payload: Payload) => {\n sendBeaconStrategy(endpointBuilder, bytesLimit, payload)\n },\n }\n}\n\nfunction sendBeaconStrategy(endpointBuilder: EndpointBuilder, bytesLimit: number, payload: Payload) {\n const canUseBeacon = !!navigator.sendBeacon && payload.bytesCount < bytesLimit\n if (canUseBeacon) {\n try {\n const beaconUrl = endpointBuilder.build('beacon', payload)\n const isQueued = navigator.sendBeacon(beaconUrl, payload.data)\n\n if (isQueued) {\n return\n }\n } catch (e) {\n reportBeaconError(e)\n }\n }\n\n fetchStrategy(endpointBuilder, payload)\n}\n\nlet hasReportedBeaconError = false\n\nfunction reportBeaconError(e: unknown) {\n if (!hasReportedBeaconError) {\n hasReportedBeaconError = true\n monitorError(e)\n }\n}\n\nexport function fetchKeepAliveStrategy(\n endpointBuilder: EndpointBuilder,\n bytesLimit: number,\n payload: Payload,\n onResponse?: (r: HttpResponse) => void\n) {\n const canUseKeepAlive = isKeepAliveSupported() && payload.bytesCount < bytesLimit\n\n if (canUseKeepAlive) {\n const fetchUrl = endpointBuilder.build('fetch-keepalive', payload)\n\n fetch(fetchUrl, { method: 'POST', body: payload.data, keepalive: true, mode: 'cors' })\n .then(monitor((response: Response) => onResponse?.({ status: response.status, type: response.type })))\n .catch(monitor(() => fetchStrategy(endpointBuilder, payload, onResponse)))\n } else {\n fetchStrategy(endpointBuilder, payload, onResponse)\n }\n}\n\nexport function fetchStrategy(\n endpointBuilder: EndpointBuilder,\n payload: Payload,\n onResponse?: (r: HttpResponse) => void\n) {\n const fetchUrl = endpointBuilder.build('fetch', payload)\n\n fetch(fetchUrl, { method: 'POST', body: payload.data, mode: 'cors' })\n .then(monitor((response: Response) => onResponse?.({ status: response.status, type: response.type })))\n .catch(monitor(() => onResponse?.({ status: 0 })))\n}\n\nfunction isKeepAliveSupported() {\n // Request can throw, cf https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#errors\n try {\n return window.Request && 'keepalive' in new Request('http://a')\n } catch {\n return false\n }\n}\n", "import { getGlobalObject } from '../tools/getGlobalObject'\nimport type { DefaultPrivacyLevel } from '../domain/configuration'\n\nexport interface BrowserWindowWithEventBridge extends Window {\n DatadogEventBridge?: DatadogEventBridge\n}\n\nexport interface DatadogEventBridge {\n getCapabilities?(): string\n getPrivacyLevel?(): DefaultPrivacyLevel\n getAllowedWebViewHosts(): string\n send(msg: string): void\n}\n\nexport const enum BridgeCapability {\n RECORDS = 'records',\n}\n\nexport function getEventBridge() {\n const eventBridgeGlobal = getEventBridgeGlobal()\n\n if (!eventBridgeGlobal) {\n return\n }\n\n return {\n getCapabilities() {\n return JSON.parse(eventBridgeGlobal.getCapabilities?.() || '[]') as BridgeCapability[]\n },\n getPrivacyLevel() {\n return eventBridgeGlobal.getPrivacyLevel?.()\n },\n getAllowedWebViewHosts() {\n return JSON.parse(eventBridgeGlobal.getAllowedWebViewHosts()) as string[]\n },\n send(eventType: T, event: E, viewId?: string) {\n const view = viewId ? { id: viewId } : undefined\n eventBridgeGlobal.send(JSON.stringify({ eventType, event, view }))\n },\n }\n}\n\nexport function bridgeSupports(capability: BridgeCapability): boolean {\n const bridge = getEventBridge()\n return !!bridge && bridge.getCapabilities().includes(capability)\n}\n\nexport function canUseEventBridge(currentHost = getGlobalObject().location?.hostname): boolean {\n const bridge = getEventBridge()\n return (\n !!bridge &&\n bridge\n .getAllowedWebViewHosts()\n .some((allowedHost) => currentHost === allowedHost || currentHost.endsWith(`.${allowedHost}`))\n )\n}\n\nfunction getEventBridgeGlobal() {\n return getGlobalObject().DatadogEventBridge\n}\n", "import { Observable } from '../tools/observable'\nimport { objectValues } from '../tools/utils/polyfills'\nimport type { Configuration } from '../domain/configuration'\nimport { addEventListeners, addEventListener, DOM_EVENT } from './addEventListener'\n\nexport const PageExitReason = {\n HIDDEN: 'visibility_hidden',\n UNLOADING: 'before_unload',\n PAGEHIDE: 'page_hide',\n FROZEN: 'page_frozen',\n} as const\n\nexport type PageExitReason = (typeof PageExitReason)[keyof typeof PageExitReason]\n\nexport interface PageMayExitEvent {\n reason: PageExitReason\n}\n\nexport function createPageMayExitObservable(configuration: Configuration): Observable {\n return new Observable((observable) => {\n const { stop: stopListeners } = addEventListeners(\n configuration,\n window,\n [DOM_EVENT.VISIBILITY_CHANGE, DOM_EVENT.FREEZE],\n (event) => {\n if (event.type === DOM_EVENT.VISIBILITY_CHANGE && document.visibilityState === 'hidden') {\n /**\n * Only event that guarantee to fire on mobile devices when the page transitions to background state\n * (e.g. when user switches to a different application, goes to homescreen, etc), or is being unloaded.\n */\n observable.notify({ reason: PageExitReason.HIDDEN })\n } else if (event.type === DOM_EVENT.FREEZE) {\n /**\n * After transitioning in background a tab can be freezed to preserve resources. (cf: https://developer.chrome.com/blog/page-lifecycle-api)\n * Allow to collect events happening between hidden and frozen state.\n */\n observable.notify({ reason: PageExitReason.FROZEN })\n }\n },\n { capture: true }\n )\n\n const stopBeforeUnloadListener = addEventListener(configuration, window, DOM_EVENT.BEFORE_UNLOAD, () => {\n observable.notify({ reason: PageExitReason.UNLOADING })\n }).stop\n\n return () => {\n stopListeners()\n stopBeforeUnloadListener()\n }\n })\n}\n\nexport function isPageExitReason(reason: string): reason is PageExitReason {\n return objectValues(PageExitReason).includes(reason as PageExitReason)\n}\n", "import { DOCS_TROUBLESHOOTING, MORE_DETAILS, display } from '../tools/display'\nimport type { Context } from '../tools/serialisation/context'\nimport { objectValues } from '../tools/utils/polyfills'\nimport { isPageExitReason } from '../browser/pageMayExitObservable'\nimport { jsonStringify } from '../tools/serialisation/jsonStringify'\nimport type { Encoder, EncoderResult } from '../tools/encoder'\nimport { computeBytesCount } from '../tools/utils/byteUtils'\nimport type { HttpRequest, Payload } from './httpRequest'\nimport type { FlushController, FlushEvent } from './flushController'\n\nexport interface Batch {\n flushController: FlushController\n add: (message: Context) => void\n upsert: (message: Context, key: string) => void\n stop: () => void\n}\n\nexport function createBatch({\n encoder,\n request,\n flushController,\n messageBytesLimit,\n}: {\n encoder: Encoder\n request: HttpRequest\n flushController: FlushController\n messageBytesLimit: number\n}): Batch {\n let upsertBuffer: { [key: string]: string } = {}\n const flushSubscription = flushController.flushObservable.subscribe((event) => flush(event))\n\n function push(serializedMessage: string, estimatedMessageBytesCount: number, key?: string) {\n flushController.notifyBeforeAddMessage(estimatedMessageBytesCount)\n\n if (key !== undefined) {\n upsertBuffer[key] = serializedMessage\n flushController.notifyAfterAddMessage()\n } else {\n encoder.write(encoder.isEmpty ? serializedMessage : `\\n${serializedMessage}`, (realMessageBytesCount) => {\n flushController.notifyAfterAddMessage(realMessageBytesCount - estimatedMessageBytesCount)\n })\n }\n }\n\n function hasMessageFor(key?: string): key is string {\n return key !== undefined && upsertBuffer[key] !== undefined\n }\n\n function remove(key: string) {\n const removedMessage = upsertBuffer[key]\n delete upsertBuffer[key]\n const messageBytesCount = encoder.estimateEncodedBytesCount(removedMessage)\n flushController.notifyAfterRemoveMessage(messageBytesCount)\n }\n\n function addOrUpdate(message: Context, key?: string) {\n const serializedMessage = jsonStringify(message)!\n\n const estimatedMessageBytesCount = encoder.estimateEncodedBytesCount(serializedMessage)\n\n if (estimatedMessageBytesCount >= messageBytesLimit) {\n display.warn(\n `Discarded a message whose size was bigger than the maximum allowed size ${messageBytesLimit}KB. ${MORE_DETAILS} ${DOCS_TROUBLESHOOTING}/#technical-limitations`\n )\n return\n }\n\n if (hasMessageFor(key)) {\n remove(key)\n }\n\n push(serializedMessage, estimatedMessageBytesCount, key)\n }\n\n function flush(event: FlushEvent) {\n const upsertMessages = objectValues(upsertBuffer).join('\\n')\n upsertBuffer = {}\n\n const pageMightExit = isPageExitReason(event.reason)\n const send = pageMightExit ? request.sendOnExit : request.send\n\n if (\n pageMightExit &&\n // Note: checking that the encoder is async is not strictly needed, but it's an optimization:\n // if the encoder is async we need to send two requests in some cases (one for encoded data\n // and the other for non-encoded data). But if it's not async, we don't have to worry about\n // it and always send a single request.\n encoder.isAsync\n ) {\n const encoderResult = encoder.finishSync()\n\n // Send encoded messages\n if (encoderResult.outputBytesCount) {\n send(formatPayloadFromEncoder(encoderResult))\n }\n\n // Send messages that are not yet encoded at this point\n const pendingMessages = [encoderResult.pendingData, upsertMessages].filter(Boolean).join('\\n')\n if (pendingMessages) {\n send({\n data: pendingMessages,\n bytesCount: computeBytesCount(pendingMessages),\n })\n }\n } else {\n if (upsertMessages) {\n encoder.write(encoder.isEmpty ? upsertMessages : `\\n${upsertMessages}`)\n }\n encoder.finish((encoderResult) => {\n send(formatPayloadFromEncoder(encoderResult))\n })\n }\n }\n\n return {\n flushController,\n add: addOrUpdate,\n upsert: addOrUpdate,\n stop: flushSubscription.unsubscribe,\n }\n}\n\nfunction formatPayloadFromEncoder(encoderResult: EncoderResult): Payload {\n let data: string | Blob\n if (typeof encoderResult.output === 'string') {\n data = encoderResult.output\n } else {\n data = new Blob([encoderResult.output], {\n // This will set the 'Content-Type: text/plain' header. Reasoning:\n // * The intake rejects the request if there is no content type.\n // * The browser will issue CORS preflight requests if we set it to 'application/json', which\n // could induce higher intake load (and maybe has other impacts).\n // * Also it's not quite JSON, since we are concatenating multiple JSON objects separated by\n // new lines.\n type: 'text/plain',\n })\n }\n\n return {\n data,\n bytesCount: encoderResult.outputBytesCount,\n encoding: encoderResult.encoding,\n }\n}\n", "import type { PageMayExitEvent, PageExitReason } from '../browser/pageMayExitObservable'\nimport { Observable } from '../tools/observable'\nimport type { TimeoutId } from '../tools/timer'\nimport { clearTimeout, setTimeout } from '../tools/timer'\nimport type { Duration } from '../tools/utils/timeUtils'\n\nexport type FlushReason = PageExitReason | 'duration_limit' | 'bytes_limit' | 'messages_limit' | 'session_expire'\n\nexport type FlushController = ReturnType\nexport interface FlushEvent {\n reason: FlushReason\n bytesCount: number\n messagesCount: number\n}\n\ninterface FlushControllerOptions {\n messagesLimit: number\n bytesLimit: number\n durationLimit: Duration\n pageMayExitObservable: Observable\n sessionExpireObservable: Observable\n}\n\n/**\n * Returns a \"flush controller\", responsible of notifying when flushing a pool of pending data needs\n * to happen. The implementation is designed to support both synchronous and asynchronous usages,\n * but relies on invariants described in each method documentation to keep a coherent state.\n */\nexport function createFlushController({\n messagesLimit,\n bytesLimit,\n durationLimit,\n pageMayExitObservable,\n sessionExpireObservable,\n}: FlushControllerOptions) {\n const pageMayExitSubscription = pageMayExitObservable.subscribe((event) => flush(event.reason))\n const sessionExpireSubscription = sessionExpireObservable.subscribe(() => flush('session_expire'))\n\n const flushObservable = new Observable(() => () => {\n pageMayExitSubscription.unsubscribe()\n sessionExpireSubscription.unsubscribe()\n })\n\n let currentBytesCount = 0\n let currentMessagesCount = 0\n\n function flush(flushReason: FlushReason) {\n if (currentMessagesCount === 0) {\n return\n }\n\n const messagesCount = currentMessagesCount\n const bytesCount = currentBytesCount\n\n currentMessagesCount = 0\n currentBytesCount = 0\n cancelDurationLimitTimeout()\n\n flushObservable.notify({\n reason: flushReason,\n messagesCount,\n bytesCount,\n })\n }\n\n let durationLimitTimeoutId: TimeoutId | undefined\n function scheduleDurationLimitTimeout() {\n if (durationLimitTimeoutId === undefined) {\n durationLimitTimeoutId = setTimeout(() => {\n flush('duration_limit')\n }, durationLimit)\n }\n }\n\n function cancelDurationLimitTimeout() {\n clearTimeout(durationLimitTimeoutId)\n durationLimitTimeoutId = undefined\n }\n\n return {\n flushObservable,\n get messagesCount() {\n return currentMessagesCount\n },\n\n /**\n * Notifies that a message will be added to a pool of pending messages waiting to be flushed.\n *\n * This function needs to be called synchronously, right before adding the message, so no flush\n * event can happen after `notifyBeforeAddMessage` and before adding the message.\n *\n * @param estimatedMessageBytesCount: an estimation of the message bytes count once it is\n * actually added.\n */\n notifyBeforeAddMessage(estimatedMessageBytesCount: number) {\n if (currentBytesCount + estimatedMessageBytesCount >= bytesLimit) {\n flush('bytes_limit')\n }\n // Consider the message to be added now rather than in `notifyAfterAddMessage`, because if no\n // message was added yet and `notifyAfterAddMessage` is called asynchronously, we still want\n // to notify when a flush is needed (for example on page exit).\n currentMessagesCount += 1\n currentBytesCount += estimatedMessageBytesCount\n scheduleDurationLimitTimeout()\n },\n\n /**\n * Notifies that a message *was* added to a pool of pending messages waiting to be flushed.\n *\n * This function can be called asynchronously after the message was added, but in this case it\n * should not be called if a flush event occurred in between.\n *\n * @param messageBytesCountDiff: the difference between the estimated message bytes count and\n * its actual bytes count once added to the pool.\n */\n notifyAfterAddMessage(messageBytesCountDiff = 0) {\n currentBytesCount += messageBytesCountDiff\n\n if (currentMessagesCount >= messagesLimit) {\n flush('messages_limit')\n } else if (currentBytesCount >= bytesLimit) {\n flush('bytes_limit')\n }\n },\n\n /**\n * Notifies that a message was removed from a pool of pending messages waiting to be flushed.\n *\n * This function needs to be called synchronously, right after removing the message, so no flush\n * event can happen after removing the message and before `notifyAfterRemoveMessage`.\n *\n * @param messageBytesCount: the message bytes count that was added to the pool. Should\n * correspond to the sum of bytes counts passed to `notifyBeforeAddMessage` and\n * `notifyAfterAddMessage`.\n */\n notifyAfterRemoveMessage(messageBytesCount: number) {\n currentBytesCount -= messageBytesCount\n currentMessagesCount -= 1\n if (currentMessagesCount === 0) {\n cancelDurationLimitTimeout()\n }\n },\n }\n}\n", "import type { Configuration, EndpointBuilder } from '../domain/configuration'\nimport type { Context } from '../tools/serialisation/context'\nimport type { Observable } from '../tools/observable'\nimport type { PageMayExitEvent } from '../browser/pageMayExitObservable'\nimport type { RawError } from '../domain/error/error.types'\nimport type { Encoder } from '../tools/encoder'\nimport { createBatch } from './batch'\nimport { createHttpRequest } from './httpRequest'\nimport { createFlushController } from './flushController'\n\nexport interface BatchConfiguration {\n endpoint: EndpointBuilder\n encoder: Encoder\n}\n\ninterface ReplicaBatchConfiguration extends BatchConfiguration {\n transformMessage?: (message: T) => T\n}\n\nexport function startBatchWithReplica(\n configuration: Configuration,\n primary: BatchConfiguration,\n replica: ReplicaBatchConfiguration | undefined,\n reportError: (error: RawError) => void,\n pageMayExitObservable: Observable,\n sessionExpireObservable: Observable,\n batchFactoryImp = createBatch\n) {\n const primaryBatch = createBatchFromConfig(configuration, primary)\n const replicaBatch = replica && createBatchFromConfig(configuration, replica)\n\n function createBatchFromConfig(configuration: Configuration, { endpoint, encoder }: BatchConfiguration) {\n return batchFactoryImp({\n encoder,\n request: createHttpRequest(endpoint, configuration.batchBytesLimit, reportError),\n flushController: createFlushController({\n messagesLimit: configuration.batchMessagesLimit,\n bytesLimit: configuration.batchBytesLimit,\n durationLimit: configuration.flushTimeout,\n pageMayExitObservable,\n sessionExpireObservable,\n }),\n messageBytesLimit: configuration.messageBytesLimit,\n })\n }\n\n return {\n flushObservable: primaryBatch.flushController.flushObservable,\n\n add(message: T, replicated = true) {\n primaryBatch.add(message)\n if (replicaBatch && replicated) {\n replicaBatch.add(replica.transformMessage ? replica.transformMessage(message) : message)\n }\n },\n\n upsert: (message: T, key: string) => {\n primaryBatch.upsert(message, key)\n if (replicaBatch) {\n replicaBatch.upsert(replica.transformMessage ? replica.transformMessage(message) : message, key)\n }\n },\n\n stop: () => {\n primaryBatch.stop()\n if (replicaBatch) {\n replicaBatch.stop()\n }\n },\n }\n}\n", "export type { HttpRequest, Payload, RetryInfo } from './httpRequest'\nexport { createHttpRequest } from './httpRequest'\nexport type { BrowserWindowWithEventBridge, DatadogEventBridge } from './eventBridge'\nexport { canUseEventBridge, bridgeSupports, getEventBridge, BridgeCapability } from './eventBridge'\nexport { startBatchWithReplica } from './startBatchWithReplica'\nexport type { FlushController, FlushEvent, FlushReason } from './flushController'\nexport { createFlushController } from './flushController'\n", "import { computeBytesCount } from './utils/byteUtils'\n\nexport interface Encoder {\n /**\n * Whether this encoder might call the provided callbacks asynchronously\n */\n isAsync: boolean\n\n /**\n * Whether some data has been written since the last finish() or finishSync() call\n */\n isEmpty: boolean\n\n /**\n * Write a string to be encoded.\n *\n * This operation can be synchronous or asynchronous depending on the encoder implementation.\n *\n * If specified, the callback will be invoked when the operation finishes, unless the operation is\n * asynchronous and finish() or finishSync() is called in the meantime.\n */\n write(data: string, callback?: (additionalEncodedBytesCount: number) => void): void\n\n /**\n * Waits for pending data to be encoded and resets the encoder state.\n *\n * This operation can be synchronous or asynchronous depending on the encoder implementation.\n *\n * The callback will be invoked when the operation finishes, unless the operation is asynchronous\n * and another call to finish() or finishSync() occurs in the meantime.\n */\n finish(callback: (result: EncoderResult) => void): void\n\n /**\n * Resets the encoder state then returns the encoded data and any potential pending data directly,\n * discarding all pending write operations and finish() callbacks.\n */\n finishSync(): EncoderResult & { pendingData: string }\n\n /**\n * Returns a rough estimation of the bytes count if the data was encoded.\n */\n estimateEncodedBytesCount(data: string): number\n}\n\nexport interface EncoderResult {\n output: Output\n outputBytesCount: number\n\n /**\n * An encoding type supported by HTTP Content-Encoding, if applicable.\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding#directives\n */\n encoding?: 'deflate'\n\n /**\n * Total bytes count of the input strings encoded to UTF-8.\n */\n rawBytesCount: number\n}\n\nexport function createIdentityEncoder(): Encoder {\n let output = ''\n let outputBytesCount = 0\n\n return {\n isAsync: false,\n\n get isEmpty() {\n return !output\n },\n\n write(data, callback) {\n const additionalEncodedBytesCount = computeBytesCount(data)\n outputBytesCount += additionalEncodedBytesCount\n output += data\n if (callback) {\n callback(additionalEncodedBytesCount)\n }\n },\n\n finish(callback) {\n callback(this.finishSync())\n },\n\n finishSync() {\n const result = {\n output,\n outputBytesCount,\n rawBytesCount: outputBytesCount,\n pendingData: '',\n }\n output = ''\n outputBytesCount = 0\n return result\n },\n\n estimateEncodedBytesCount(data) {\n return data.length\n },\n }\n}\n", "import type { Subscription } from './observable'\n\n/**\n * Type helper to extract event types that have \"void\" data. This allows to call `notify` without a\n * second argument. Ex:\n *\n * ```\n * interface EventMap {\n * foo: void\n * }\n * const LifeCycle = AbstractLifeCycle\n * new LifeCycle().notify('foo')\n * ```\n */\ntype EventTypesWithoutData = {\n [K in keyof EventMap]: EventMap[K] extends void ? K : never\n}[keyof EventMap]\n\n// eslint-disable-next-line no-restricted-syntax\nexport class AbstractLifeCycle {\n private callbacks: { [key in keyof EventMap]?: Array<(data: any) => void> } = {}\n\n notify>(eventType: EventType): void\n notify(eventType: EventType, data: EventMap[EventType]): void\n notify(eventType: keyof EventMap, data?: unknown) {\n const eventCallbacks = this.callbacks[eventType]\n if (eventCallbacks) {\n eventCallbacks.forEach((callback) => callback(data))\n }\n }\n\n subscribe(\n eventType: EventType,\n callback: (data: EventMap[EventType]) => void\n ): Subscription {\n if (!this.callbacks[eventType]) {\n this.callbacks[eventType] = []\n }\n this.callbacks[eventType]!.push(callback)\n return {\n unsubscribe: () => {\n this.callbacks[eventType] = this.callbacks[eventType]!.filter((other) => callback !== other)\n },\n }\n }\n}\n", "import { setTimeout } from '../../tools/timer'\nimport { clocksNow, ONE_MINUTE } from '../../tools/utils/timeUtils'\nimport type { RawError } from '../error/error.types'\nimport { ErrorSource } from '../error/error.types'\n\nexport type EventRateLimiter = ReturnType\n\nexport function createEventRateLimiter(\n eventType: string,\n limit: number,\n onLimitReached: (limitError: RawError) => void\n) {\n let eventCount = 0\n let allowNextEvent = false\n\n return {\n isLimitReached() {\n if (eventCount === 0) {\n setTimeout(() => {\n eventCount = 0\n }, ONE_MINUTE)\n }\n\n eventCount += 1\n if (eventCount <= limit || allowNextEvent) {\n allowNextEvent = false\n return false\n }\n\n if (eventCount === limit + 1) {\n allowNextEvent = true\n try {\n onLimitReached({\n message: `Reached max number of ${eventType}s by minute: ${limit}`,\n source: ErrorSource.AGENT,\n startClocks: clocksNow(),\n })\n } finally {\n allowNextEvent = false\n }\n }\n\n return true\n },\n }\n}\n", "import type { Configuration } from '../domain/configuration'\nimport { noop } from '../tools/utils/functionUtils'\nimport { DOM_EVENT, addEventListener } from './addEventListener'\n\nexport function runOnReadyState(\n configuration: Configuration,\n expectedReadyState: 'complete' | 'interactive',\n callback: () => void\n): { stop: () => void } {\n if (document.readyState === expectedReadyState || document.readyState === 'complete') {\n callback()\n return { stop: noop }\n }\n const eventName = expectedReadyState === 'complete' ? DOM_EVENT.LOAD : DOM_EVENT.DOM_CONTENT_LOADED\n return addEventListener(configuration, window, eventName, callback, { once: true })\n}\n\nexport function asyncRunOnReadyState(\n configuration: Configuration,\n expectedReadyState: 'complete' | 'interactive'\n): Promise {\n return new Promise((resolve) => {\n runOnReadyState(configuration, expectedReadyState, resolve)\n })\n}\n", "import type { InstrumentedMethodCall } from '../tools/instrumentMethod'\nimport { instrumentMethod } from '../tools/instrumentMethod'\nimport { Observable } from '../tools/observable'\nimport type { Duration, ClocksState } from '../tools/utils/timeUtils'\nimport { elapsed, clocksNow, timeStampNow } from '../tools/utils/timeUtils'\nimport { normalizeUrl } from '../tools/utils/urlPolyfill'\nimport { shallowClone } from '../tools/utils/objectUtils'\nimport type { Configuration } from '../domain/configuration'\nimport { addEventListener } from './addEventListener'\n\nexport interface XhrOpenContext {\n state: 'open'\n method: string\n url: string\n}\n\nexport interface XhrStartContext extends Omit {\n state: 'start'\n startClocks: ClocksState\n isAborted: boolean\n xhr: XMLHttpRequest\n handlingStack?: string\n}\n\nexport interface XhrCompleteContext extends Omit {\n state: 'complete'\n duration: Duration\n status: number\n}\n\nexport type XhrContext = XhrOpenContext | XhrStartContext | XhrCompleteContext\n\nlet xhrObservable: Observable | undefined\nconst xhrContexts = new WeakMap()\n\nexport function initXhrObservable(configuration: Configuration) {\n if (!xhrObservable) {\n xhrObservable = createXhrObservable(configuration)\n }\n return xhrObservable\n}\n\nfunction createXhrObservable(configuration: Configuration) {\n return new Observable((observable) => {\n const { stop: stopInstrumentingStart } = instrumentMethod(XMLHttpRequest.prototype, 'open', openXhr)\n\n const { stop: stopInstrumentingSend } = instrumentMethod(\n XMLHttpRequest.prototype,\n 'send',\n (call) => {\n sendXhr(call, configuration, observable)\n },\n { computeHandlingStack: true }\n )\n\n const { stop: stopInstrumentingAbort } = instrumentMethod(XMLHttpRequest.prototype, 'abort', abortXhr)\n\n return () => {\n stopInstrumentingStart()\n stopInstrumentingSend()\n stopInstrumentingAbort()\n }\n })\n}\n\nfunction openXhr({ target: xhr, parameters: [method, url] }: InstrumentedMethodCall) {\n xhrContexts.set(xhr, {\n state: 'open',\n method: String(method).toUpperCase(),\n url: normalizeUrl(String(url)),\n })\n}\n\nfunction sendXhr(\n { target: xhr, handlingStack }: InstrumentedMethodCall,\n configuration: Configuration,\n observable: Observable\n) {\n const context = xhrContexts.get(xhr)\n if (!context) {\n return\n }\n\n const startContext = context as XhrStartContext\n startContext.state = 'start'\n startContext.startClocks = clocksNow()\n startContext.isAborted = false\n startContext.xhr = xhr\n startContext.handlingStack = handlingStack\n\n let hasBeenReported = false\n\n const { stop: stopInstrumentingOnReadyStateChange } = instrumentMethod(xhr, 'onreadystatechange', () => {\n if (xhr.readyState === XMLHttpRequest.DONE) {\n // Try to report the XHR as soon as possible, because the XHR may be mutated by the\n // application during a future event. For example, Angular is calling .abort() on\n // completed requests during an onreadystatechange event, so the status becomes '0'\n // before the request is collected.\n onEnd()\n }\n })\n\n const onEnd = () => {\n unsubscribeLoadEndListener()\n stopInstrumentingOnReadyStateChange()\n if (hasBeenReported) {\n return\n }\n hasBeenReported = true\n\n const completeContext = context as XhrCompleteContext\n completeContext.state = 'complete'\n completeContext.duration = elapsed(startContext.startClocks.timeStamp, timeStampNow())\n completeContext.status = xhr.status\n observable.notify(shallowClone(completeContext))\n }\n\n const { stop: unsubscribeLoadEndListener } = addEventListener(configuration, xhr, 'loadend', onEnd)\n\n observable.notify(startContext)\n}\n\nfunction abortXhr({ target: xhr }: InstrumentedMethodCall) {\n const context = xhrContexts.get(xhr) as XhrStartContext | undefined\n if (context) {\n context.isAborted = true\n }\n}\n", "import type { InstrumentedMethodCall } from '../tools/instrumentMethod'\nimport { instrumentMethod } from '../tools/instrumentMethod'\nimport { monitor } from '../tools/monitor'\nimport { Observable } from '../tools/observable'\nimport type { ClocksState } from '../tools/utils/timeUtils'\nimport { clocksNow } from '../tools/utils/timeUtils'\nimport { normalizeUrl } from '../tools/utils/urlPolyfill'\n\ninterface FetchContextBase {\n method: string\n startClocks: ClocksState\n input: unknown\n init?: RequestInit\n url: string\n handlingStack?: string\n}\n\nexport interface FetchStartContext extends FetchContextBase {\n state: 'start'\n}\n\nexport interface FetchResolveContext extends FetchContextBase {\n state: 'resolve'\n status: number\n response?: Response\n responseType?: string\n isAborted: boolean\n error?: Error\n}\n\nexport type FetchContext = FetchStartContext | FetchResolveContext\n\nlet fetchObservable: Observable | undefined\n\nexport function initFetchObservable() {\n if (!fetchObservable) {\n fetchObservable = createFetchObservable()\n }\n return fetchObservable\n}\n\nexport function resetFetchObservable() {\n fetchObservable = undefined\n}\n\nfunction createFetchObservable() {\n return new Observable((observable) => {\n if (!window.fetch) {\n return\n }\n\n const { stop } = instrumentMethod(window, 'fetch', (call) => beforeSend(call, observable), {\n computeHandlingStack: true,\n })\n\n return stop\n })\n}\n\nfunction beforeSend(\n { parameters, onPostCall, handlingStack }: InstrumentedMethodCall,\n observable: Observable\n) {\n const [input, init] = parameters\n let methodFromParams = init && init.method\n\n if (methodFromParams === undefined && input instanceof Request) {\n methodFromParams = input.method\n }\n\n const method = methodFromParams !== undefined ? String(methodFromParams).toUpperCase() : 'GET'\n const url = input instanceof Request ? input.url : normalizeUrl(String(input))\n const startClocks = clocksNow()\n\n const context: FetchStartContext = {\n state: 'start',\n init,\n input,\n method,\n startClocks,\n url,\n handlingStack,\n }\n\n observable.notify(context)\n\n // Those properties can be changed by observable subscribers\n parameters[0] = context.input as RequestInfo | URL\n parameters[1] = context.init\n\n onPostCall((responsePromise) => afterSend(observable, responsePromise, context))\n}\n\nfunction afterSend(\n observable: Observable,\n responsePromise: Promise,\n startContext: FetchStartContext\n) {\n const context = startContext as unknown as FetchResolveContext\n\n function reportFetch(partialContext: Partial) {\n context.state = 'resolve'\n Object.assign(context, partialContext)\n observable.notify(context)\n }\n\n responsePromise.then(\n monitor((response) => {\n reportFetch({\n response,\n responseType: response.type,\n status: response.status,\n isAborted: false,\n })\n }),\n monitor((error: Error) => {\n reportFetch({\n status: 0,\n isAborted:\n context.init?.signal?.aborted || (error instanceof DOMException && error.code === DOMException.ABORT_ERR),\n error,\n })\n })\n )\n}\n", "import { setTimeout, clearTimeout } from './timer'\nimport { monitor } from './monitor'\nimport { dateNow } from './utils/timeUtils'\n\n// This type is not yet supported in TS 3.8. Imported from the TS source until we upgrade the\n// minimum supported TS version.\n// https://github.com/microsoft/TypeScript/blob/13c374a868c926f6a907666a5599992c1351b773/src/lib/dom.generated.d.ts#L9513-L9516\nexport interface IdleDeadline {\n readonly didTimeout: boolean\n timeRemaining(): DOMHighResTimeStamp\n}\n\n/**\n * 'requestIdleCallback' with a shim.\n */\nexport function requestIdleCallback(callback: (deadline: IdleDeadline) => void, opts?: { timeout?: number }) {\n // Note: check both 'requestIdleCallback' and 'cancelIdleCallback' existence because some polyfills only implement 'requestIdleCallback'.\n if (window.requestIdleCallback && window.cancelIdleCallback) {\n const id = window.requestIdleCallback(monitor(callback), opts)\n return () => window.cancelIdleCallback(id)\n }\n return requestIdleCallbackShim(callback)\n}\n\nexport const MAX_TASK_TIME = 50\n\n/*\n * Shim from https://developer.chrome.com/blog/using-requestidlecallback#checking_for_requestidlecallback\n * Note: there is no simple way to support the \"timeout\" option, so we ignore it.\n */\nexport function requestIdleCallbackShim(callback: (deadline: IdleDeadline) => void) {\n const start = dateNow()\n const timeoutId = setTimeout(() => {\n callback({\n didTimeout: false,\n timeRemaining: () => Math.max(0, MAX_TASK_TIME - (dateNow() - start)),\n })\n }, 0)\n return () => clearTimeout(timeoutId)\n}\n", "import { ONE_SECOND } from './utils/timeUtils'\nimport { requestIdleCallback } from './requestIdleCallback'\n\n/**\n * Maximum delay before starting to execute tasks in the queue. We don't want to wait too long\n * before running tasks, as it might hurt reliability (ex: if the user navigates away, we might lose\n * the opportunity to send some data). We also don't want to run tasks too often, as it might hurt\n * performance.\n */\nconst IDLE_CALLBACK_TIMEOUT = ONE_SECOND\n\n/**\n * Maximum amount of time allocated to running tasks when a timeout (`IDLE_CALLBACK_TIMEOUT`) is\n * reached. We should not run tasks for too long as it will hurt performance, but we should still\n * run some tasks to avoid postponing them forever.\n *\n * Rational: Running tasks for 30ms every second (IDLE_CALLBACK_TIMEOUT) should be acceptable.\n */\nexport const MAX_EXECUTION_TIME_ON_TIMEOUT = 30\n\nexport interface TaskQueue {\n push(task: Task): void\n}\n\ntype Task = () => void\n\nexport function createTaskQueue(): TaskQueue {\n const pendingTasks: Task[] = []\n\n function run(deadline: IdleDeadline) {\n let executionTimeRemaining: () => number\n if (deadline.didTimeout) {\n const start = performance.now()\n executionTimeRemaining = () => MAX_EXECUTION_TIME_ON_TIMEOUT - (performance.now() - start)\n } else {\n executionTimeRemaining = deadline.timeRemaining.bind(deadline)\n }\n\n while (executionTimeRemaining() > 0 && pendingTasks.length) {\n pendingTasks.shift()!()\n }\n\n if (pendingTasks.length) {\n scheduleNextRun()\n }\n }\n\n function scheduleNextRun() {\n requestIdleCallback(run, { timeout: IDLE_CALLBACK_TIMEOUT })\n }\n\n return {\n push(task) {\n if (pendingTasks.push(task) === 1) {\n scheduleNextRun()\n }\n },\n }\n}\n", "import { flattenErrorCauses, isError, tryToGetFingerprint, tryToGetErrorContext } from '../error/error'\nimport { mergeObservables, Observable } from '../../tools/observable'\nimport { ConsoleApiName, globalConsole } from '../../tools/display'\nimport { callMonitored } from '../../tools/monitor'\nimport { sanitize } from '../../tools/serialisation/sanitize'\nimport { jsonStringify } from '../../tools/serialisation/jsonStringify'\nimport type { RawError } from '../error/error.types'\nimport { ErrorHandling, ErrorSource } from '../error/error.types'\nimport { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { createHandlingStack, toStackTraceString, formatErrorMessage } from '../../tools/stackTrace/handlingStack'\nimport { clocksNow } from '../../tools/utils/timeUtils'\n\nexport type ConsoleLog = NonErrorConsoleLog | ErrorConsoleLog\n\ninterface NonErrorConsoleLog extends ConsoleLogBase {\n api: Exclude\n error: undefined\n}\n\nexport interface ErrorConsoleLog extends ConsoleLogBase {\n api: typeof ConsoleApiName.error\n error: RawError\n}\n\ninterface ConsoleLogBase {\n message: string\n api: ConsoleApiName\n handlingStack: string\n}\n\ntype ConsoleLogForApi = T extends typeof ConsoleApiName.error\n ? ErrorConsoleLog\n : NonErrorConsoleLog\n\nlet consoleObservablesByApi: { [K in ConsoleApiName]?: Observable> } = {}\n\nexport function initConsoleObservable(apis: T): Observable> {\n const consoleObservables = apis.map((api) => {\n if (!consoleObservablesByApi[api]) {\n consoleObservablesByApi[api] = createConsoleObservable(api) as any // we are sure that the observable created for this api will yield the expected ConsoleLog type\n }\n return consoleObservablesByApi[api] as unknown as Observable>\n })\n\n return mergeObservables(...consoleObservables)\n}\n\nexport function resetConsoleObservable() {\n consoleObservablesByApi = {}\n}\n\nfunction createConsoleObservable(api: ConsoleApiName) {\n return new Observable((observable) => {\n const originalConsoleApi = globalConsole[api]\n\n globalConsole[api] = (...params: unknown[]) => {\n originalConsoleApi.apply(console, params)\n const handlingStack = createHandlingStack('console error')\n\n callMonitored(() => {\n observable.notify(buildConsoleLog(params, api, handlingStack))\n })\n }\n\n return () => {\n globalConsole[api] = originalConsoleApi\n }\n })\n}\n\nfunction buildConsoleLog(params: unknown[], api: ConsoleApiName, handlingStack: string): ConsoleLog {\n const message = params.map((param) => formatConsoleParameters(param)).join(' ')\n let error: RawError | undefined\n\n if (api === ConsoleApiName.error) {\n const firstErrorParam = params.find(isError)\n\n error = {\n stack: firstErrorParam ? toStackTraceString(computeStackTrace(firstErrorParam)) : undefined,\n fingerprint: tryToGetFingerprint(firstErrorParam),\n causes: firstErrorParam ? flattenErrorCauses(firstErrorParam, 'console') : undefined,\n startClocks: clocksNow(),\n message,\n source: ErrorSource.CONSOLE,\n handling: ErrorHandling.HANDLED,\n handlingStack,\n context: tryToGetErrorContext(firstErrorParam),\n }\n }\n\n return {\n api,\n message,\n error,\n handlingStack,\n } as ConsoleLog\n}\n\nfunction formatConsoleParameters(param: unknown) {\n if (typeof param === 'string') {\n return sanitize(param)\n }\n if (isError(param)) {\n return formatErrorMessage(computeStackTrace(param))\n }\n return jsonStringify(sanitize(param), undefined, 2)\n}\n", "import type { Context } from '../../tools/serialisation/context'\nimport { display } from '../../tools/display'\nimport { getType } from '../../tools/utils/typeUtils'\n\n/**\n * Simple check to ensure an object is a valid context\n */\nexport function checkContext(maybeContext: unknown): maybeContext is Context {\n const isValid = getType(maybeContext) === 'object'\n if (!isValid) {\n display.error('Unsupported context:', maybeContext)\n }\n return isValid\n}\n", "import { deepClone } from '../../tools/mergeInto'\nimport { sanitize } from '../../tools/serialisation/sanitize'\nimport type { Context } from '../../tools/serialisation/context'\nimport { Observable } from '../../tools/observable'\nimport { display } from '../../tools/display'\nimport type { CustomerDataTracker } from './customerDataTracker'\nimport { checkContext } from './contextUtils'\n\nexport type ContextManager = ReturnType\n\nexport type PropertiesConfig = {\n [key: string]: {\n required?: boolean\n type?: 'string'\n }\n}\n\nfunction ensureProperties(context: Context, propertiesConfig: PropertiesConfig, name: string) {\n const newContext = { ...context }\n\n for (const [key, { required, type }] of Object.entries(propertiesConfig)) {\n /**\n * Ensure specified properties are strings as defined here:\n * https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes\n */\n if (type === 'string' && key in newContext) {\n /* eslint-disable @typescript-eslint/no-base-to-string */\n newContext[key] = String(newContext[key])\n }\n\n if (required && !(key in context)) {\n display.warn(`The property ${key} of ${name} is required; context will not be sent to the intake.`)\n }\n }\n\n return newContext\n}\n\nexport function createContextManager(\n name: string = '',\n {\n customerDataTracker,\n propertiesConfig = {},\n }: {\n customerDataTracker?: CustomerDataTracker\n propertiesConfig?: PropertiesConfig\n } = {}\n) {\n let context: Context = {}\n const changeObservable = new Observable()\n\n const contextManager = {\n getContext: () => deepClone(context),\n\n setContext: (newContext: unknown) => {\n if (checkContext(newContext)) {\n context = sanitize(ensureProperties(newContext, propertiesConfig, name))\n customerDataTracker?.updateCustomerData(context)\n } else {\n contextManager.clearContext()\n }\n changeObservable.notify()\n },\n\n setContextProperty: (key: string, property: any) => {\n context = sanitize(ensureProperties({ ...context, [key]: property }, propertiesConfig, name))\n customerDataTracker?.updateCustomerData(context)\n changeObservable.notify()\n },\n\n removeContextProperty: (key: string) => {\n delete context[key]\n customerDataTracker?.updateCustomerData(context)\n ensureProperties(context, propertiesConfig, name)\n changeObservable.notify()\n },\n\n clearContext: () => {\n context = {}\n customerDataTracker?.resetCustomerData()\n changeObservable.notify()\n },\n\n changeObservable,\n }\n return contextManager\n}\n", "import { addEventListener, DOM_EVENT } from '../../browser/addEventListener'\nimport type { Context } from '../../tools/serialisation/context'\nimport type { Configuration } from '../configuration'\nimport { combine } from '../../tools/mergeInto'\nimport { isEmptyObject } from '../../tools/utils/objectUtils'\nimport type { ContextManager } from './contextManager'\nimport type { CustomerDataType } from './contextConstants'\n\nconst CONTEXT_STORE_KEY_PREFIX = '_dd_c'\n\nconst storageListeners: Array<{ stop: () => void }> = []\n\nexport function storeContextManager(\n configuration: Configuration,\n contextManager: ContextManager,\n productKey: string,\n customerDataType: CustomerDataType\n) {\n const storageKey = buildStorageKey(productKey, customerDataType)\n\n storageListeners.push(\n addEventListener(configuration, window, DOM_EVENT.STORAGE, ({ key }) => {\n if (storageKey === key) {\n synchronizeWithStorage()\n }\n })\n )\n contextManager.changeObservable.subscribe(dumpToStorage)\n\n const contextFromStorage = combine(getFromStorage(), contextManager.getContext())\n if (!isEmptyObject(contextFromStorage)) {\n contextManager.setContext(contextFromStorage)\n }\n\n function synchronizeWithStorage() {\n contextManager.setContext(getFromStorage())\n }\n\n function dumpToStorage() {\n localStorage.setItem(storageKey, JSON.stringify(contextManager.getContext()))\n }\n\n function getFromStorage() {\n const rawContext = localStorage.getItem(storageKey)\n return rawContext ? (JSON.parse(rawContext) as Context) : {}\n }\n}\n\nexport function buildStorageKey(productKey: string, customerDataType: CustomerDataType) {\n return `${CONTEXT_STORE_KEY_PREFIX}_${productKey}_${customerDataType}`\n}\n\nexport function removeStorageListeners() {\n storageListeners.map((listener) => listener.stop())\n}\n", "import { ONE_KIBI_BYTE, computeBytesCount } from '../../tools/utils/byteUtils'\nimport { throttle } from '../../tools/utils/functionUtils'\nimport type { Context } from '../../tools/serialisation/context'\nimport { jsonStringify } from '../../tools/serialisation/jsonStringify'\nimport { DOCS_TROUBLESHOOTING, MORE_DETAILS, display } from '../../tools/display'\nimport { isEmptyObject } from '../../tools/utils/objectUtils'\nimport type { CustomerDataType } from './contextConstants'\n\n// RUM and logs batch bytes limit is 16KB\n// ensure that we leave room for other event attributes and maintain a decent amount of event per batch\n// (3KB (customer data) + 1KB (other attributes)) * 4 (events per batch) = 16KB\nexport const CUSTOMER_DATA_BYTES_LIMIT = 3 * ONE_KIBI_BYTE\n\n// We observed that the compression ratio is around 8 in general, but we also want to keep a margin\n// because some data might not be compressed (ex: last view update on page exit). We chose 16KiB\n// because it is also the limit of the 'batchBytesCount' that we use for RUM and Logs data, but this\n// is a bit arbitrary.\nexport const CUSTOMER_COMPRESSED_DATA_BYTES_LIMIT = 16 * ONE_KIBI_BYTE\n\nexport const BYTES_COMPUTATION_THROTTLING_DELAY = 200\n\nexport type CustomerDataTracker = ReturnType\nexport type CustomerDataTrackerManager = ReturnType\n\nexport const enum CustomerDataCompressionStatus {\n Unknown,\n Enabled,\n Disabled,\n}\n\nexport function createCustomerDataTrackerManager(\n compressionStatus: CustomerDataCompressionStatus = CustomerDataCompressionStatus.Disabled\n) {\n const customerDataTrackers = new Map()\n\n let alreadyWarned = false\n function checkCustomerDataLimit(initialBytesCount = 0) {\n if (alreadyWarned || compressionStatus === CustomerDataCompressionStatus.Unknown) {\n return\n }\n\n const bytesCountLimit =\n compressionStatus === CustomerDataCompressionStatus.Disabled\n ? CUSTOMER_DATA_BYTES_LIMIT\n : CUSTOMER_COMPRESSED_DATA_BYTES_LIMIT\n\n let bytesCount = initialBytesCount\n customerDataTrackers.forEach((tracker) => {\n bytesCount += tracker.getBytesCount()\n })\n\n if (bytesCount > bytesCountLimit) {\n displayCustomerDataLimitReachedWarning(bytesCountLimit)\n alreadyWarned = true\n }\n }\n\n return {\n /**\n * Creates a detached tracker. The manager will not store a reference to that tracker, and the\n * bytes count will be counted independently from other detached trackers.\n *\n * This is particularly useful when we don't know when the tracker will be unused, so we don't\n * leak memory (ex: when used in Logger instances).\n */\n createDetachedTracker: () => {\n const tracker = createCustomerDataTracker(() => checkCustomerDataLimit(tracker.getBytesCount()))\n return tracker\n },\n\n /**\n * Creates a tracker if it doesn't exist, and returns it.\n */\n getOrCreateTracker: (type: CustomerDataType) => {\n if (!customerDataTrackers.has(type)) {\n customerDataTrackers.set(type, createCustomerDataTracker(checkCustomerDataLimit))\n }\n return customerDataTrackers.get(type)!\n },\n\n setCompressionStatus: (newCompressionStatus: CustomerDataCompressionStatus) => {\n if (compressionStatus === CustomerDataCompressionStatus.Unknown) {\n compressionStatus = newCompressionStatus\n checkCustomerDataLimit()\n }\n },\n\n getCompressionStatus: () => compressionStatus,\n\n stop: () => {\n customerDataTrackers.forEach((tracker) => tracker.stop())\n customerDataTrackers.clear()\n },\n }\n}\n\nexport function createCustomerDataTracker(checkCustomerDataLimit: () => void) {\n let bytesCountCache = 0\n\n // Throttle the bytes computation to minimize the impact on performance.\n // Especially useful if the user call context APIs synchronously multiple times in a row\n const { throttled: computeBytesCountThrottled, cancel: cancelComputeBytesCount } = throttle((context: Context) => {\n bytesCountCache = computeBytesCount(jsonStringify(context)!)\n checkCustomerDataLimit()\n }, BYTES_COMPUTATION_THROTTLING_DELAY)\n\n const resetBytesCount = () => {\n cancelComputeBytesCount()\n bytesCountCache = 0\n }\n\n return {\n updateCustomerData: (context: Context) => {\n if (isEmptyObject(context)) {\n resetBytesCount()\n } else {\n computeBytesCountThrottled(context)\n }\n },\n resetCustomerData: resetBytesCount,\n getBytesCount: () => bytesCountCache,\n stop: () => {\n cancelComputeBytesCount()\n },\n }\n}\n\nfunction displayCustomerDataLimitReachedWarning(bytesCountLimit: number) {\n display.warn(\n `Customer data exceeds the recommended ${\n bytesCountLimit / ONE_KIBI_BYTE\n }KiB threshold. ${MORE_DETAILS} ${DOCS_TROUBLESHOOTING}/#customer-data-exceeds-the-recommended-threshold-warning`\n )\n}\n", "import { monitor } from './monitor'\nimport { noop } from './utils/functionUtils'\n\ntype Options = {\n bytesLimit: number\n collectStreamBody?: boolean\n}\n/**\n * Read bytes from a ReadableStream until at least `limit` bytes have been read (or until the end of\n * the stream). The callback is invoked with the at most `limit` bytes, and indicates that the limit\n * has been exceeded if more bytes were available.\n */\nexport function readBytesFromStream(\n stream: ReadableStream,\n callback: (error?: Error, bytes?: Uint8Array, limitExceeded?: boolean) => void,\n options: Options\n) {\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n let readBytesCount = 0\n\n readMore()\n\n function readMore() {\n reader.read().then(\n monitor((result: ReadableStreamReadResult) => {\n if (result.done) {\n onDone()\n return\n }\n\n if (options.collectStreamBody) {\n chunks.push(result.value)\n }\n readBytesCount += result.value.length\n\n if (readBytesCount > options.bytesLimit) {\n onDone()\n } else {\n readMore()\n }\n }),\n monitor((error) => callback(error))\n )\n }\n\n function onDone() {\n reader.cancel().catch(\n // we don't care if cancel fails, but we still need to catch the error to avoid reporting it\n // as an unhandled rejection\n noop\n )\n\n let bytes: Uint8Array | undefined\n let limitExceeded: boolean | undefined\n if (options.collectStreamBody) {\n let completeBuffer: Uint8Array\n if (chunks.length === 1) {\n // optimization: if the response is small enough to fit in a single buffer (provided by the browser), just\n // use it directly.\n completeBuffer = chunks[0]\n } else {\n // else, we need to copy buffers into a larger buffer to concatenate them.\n completeBuffer = new Uint8Array(readBytesCount)\n let offset = 0\n chunks.forEach((chunk) => {\n completeBuffer.set(chunk, offset)\n offset += chunk.length\n })\n }\n bytes = completeBuffer.slice(0, options.bytesLimit)\n limitExceeded = completeBuffer.length > options.bytesLimit\n }\n\n callback(undefined, bytes, limitExceeded)\n }\n}\n", "import { getInitCookie } from '../../browser/cookie'\n\nexport const SYNTHETICS_TEST_ID_COOKIE_NAME = 'datadog-synthetics-public-id'\nexport const SYNTHETICS_RESULT_ID_COOKIE_NAME = 'datadog-synthetics-result-id'\nexport const SYNTHETICS_INJECTS_RUM_COOKIE_NAME = 'datadog-synthetics-injects-rum'\n\nexport interface BrowserWindow extends Window {\n _DATADOG_SYNTHETICS_PUBLIC_ID?: unknown\n _DATADOG_SYNTHETICS_RESULT_ID?: unknown\n _DATADOG_SYNTHETICS_INJECTS_RUM?: unknown\n}\n\nexport function willSyntheticsInjectRum(): boolean {\n return Boolean(\n (window as BrowserWindow)._DATADOG_SYNTHETICS_INJECTS_RUM || getInitCookie(SYNTHETICS_INJECTS_RUM_COOKIE_NAME)\n )\n}\n\nexport function getSyntheticsTestId(): string | undefined {\n const value = (window as BrowserWindow)._DATADOG_SYNTHETICS_PUBLIC_ID || getInitCookie(SYNTHETICS_TEST_ID_COOKIE_NAME)\n return typeof value === 'string' ? value : undefined\n}\n\nexport function getSyntheticsResultId(): string | undefined {\n const value =\n (window as BrowserWindow)._DATADOG_SYNTHETICS_RESULT_ID || getInitCookie(SYNTHETICS_RESULT_ID_COOKIE_NAME)\n return typeof value === 'string' ? value : undefined\n}\n", "export {};\n//# sourceMappingURL=resourceUtils.js.map", "import { display } from './display'\nimport { getType } from './utils/typeUtils'\n\nexport type MatchOption = string | RegExp | ((value: string) => boolean)\n\nexport function isMatchOption(item: unknown): item is MatchOption {\n const itemType = getType(item)\n return itemType === 'string' || itemType === 'function' || item instanceof RegExp\n}\n\n/**\n * Returns true if value can be matched by at least one of the provided MatchOptions.\n * When comparing strings, setting useStartsWith to true will compare the value with the start of\n * the option, instead of requiring an exact match.\n */\nexport function matchList(list: MatchOption[], value: string, useStartsWith = false): boolean {\n return list.some((item) => {\n try {\n if (typeof item === 'function') {\n return item(value)\n } else if (item instanceof RegExp) {\n return item.test(value)\n } else if (typeof item === 'string') {\n return useStartsWith ? value.startsWith(item) : item === value\n }\n } catch (e) {\n display.error(e)\n }\n return false\n })\n}\n", "export {};\n//# sourceMappingURL=deflate.types.js.map", "export * from './deflate.types'\n", "export type { Configuration, InitConfiguration, EndpointBuilder } from './domain/configuration'\nexport {\n validateAndBuildConfiguration,\n DefaultPrivacyLevel,\n TraceContextInjection,\n serializeConfiguration,\n isSampleRate,\n buildEndpointHost,\n INTAKE_SITE_STAGING,\n INTAKE_SITE_US1,\n INTAKE_SITE_US1_FED,\n INTAKE_SITE_EU1,\n INTAKE_URL_PARAMETERS,\n isIntakeUrl,\n} from './domain/configuration'\nexport type { TrackingConsentState } from './domain/trackingConsent'\nexport { TrackingConsent, createTrackingConsentState } from './domain/trackingConsent'\nexport {\n isExperimentalFeatureEnabled,\n addExperimentalFeatures,\n resetExperimentalFeatures,\n getExperimentalFeatures,\n initFeatureFlags,\n ExperimentalFeature,\n} from './tools/experimentalFeatures'\nexport { trackRuntimeError } from './domain/error/trackRuntimeError'\nexport type { StackTrace } from './tools/stackTrace/computeStackTrace'\nexport { computeStackTrace } from './tools/stackTrace/computeStackTrace'\nexport type { PublicApi } from './boot/init'\nexport { defineGlobal, makePublicApi } from './boot/init'\nexport { displayAlreadyInitializedError } from './boot/displayAlreadyInitializedError'\nexport { initReportObservable, RawReportType } from './domain/report/reportObservable'\nexport type {\n Telemetry,\n RawTelemetryEvent,\n RawTelemetryConfiguration,\n TelemetryEvent,\n TelemetryErrorEvent,\n TelemetryDebugEvent,\n TelemetryConfigurationEvent,\n TelemetryUsageEvent,\n} from './domain/telemetry'\nexport {\n startTelemetry,\n addTelemetryDebug,\n addTelemetryError,\n startFakeTelemetry,\n resetTelemetry,\n TelemetryService,\n isTelemetryReplicationAllowed,\n addTelemetryConfiguration,\n addTelemetryUsage,\n drainPreStartTelemetry,\n} from './domain/telemetry'\nexport { monitored, monitor, callMonitored, setDebugMode, monitorError } from './tools/monitor'\nexport type { Subscription } from './tools/observable'\nexport { Observable } from './tools/observable'\nexport type { SessionManager } from './domain/session/sessionManager'\nexport { startSessionManager, stopSessionManager } from './domain/session/sessionManager'\nexport {\n SESSION_TIME_OUT_DELAY, // Exposed for tests\n SessionPersistence,\n} from './domain/session/sessionConstants'\nexport type { HttpRequest, Payload, FlushEvent, FlushReason } from './transport'\nexport {\n createHttpRequest,\n canUseEventBridge,\n getEventBridge,\n bridgeSupports,\n BridgeCapability,\n startBatchWithReplica,\n createFlushController,\n} from './transport'\nexport * from './tools/display'\nexport type { Encoder, EncoderResult } from './tools/encoder'\nexport { createIdentityEncoder } from './tools/encoder'\nexport * from './tools/utils/urlPolyfill'\nexport * from './tools/utils/timeUtils'\nexport * from './tools/utils/arrayUtils'\nexport * from './tools/serialisation/sanitize'\nexport * from './tools/getGlobalObject'\nexport { AbstractLifeCycle } from './tools/abstractLifeCycle'\nexport * from './domain/eventRateLimiter/createEventRateLimiter'\nexport * from './tools/utils/browserDetection'\nexport { sendToExtension } from './tools/sendToExtension'\nexport { runOnReadyState, asyncRunOnReadyState } from './browser/runOnReadyState'\nexport { getZoneJsOriginalValue } from './tools/getZoneJsOriginalValue'\nexport type { InstrumentedMethodCall } from './tools/instrumentMethod'\nexport { instrumentMethod, instrumentSetter } from './tools/instrumentMethod'\nexport {\n computeRawError,\n getFileFromStackTraceString,\n isError,\n NO_ERROR_STACK_PRESENT_MESSAGE,\n} from './domain/error/error'\nexport { NonErrorPrefix } from './domain/error/error.types'\nexport type { Context, ContextArray, ContextValue } from './tools/serialisation/context'\nexport {\n areCookiesAuthorized,\n getCookie,\n getInitCookie,\n setCookie,\n deleteCookie,\n resetInitCookies,\n} from './browser/cookie'\nexport type { CookieStore, WeakRef, WeakRefConstructor } from './browser/browser.types'\nexport type { XhrCompleteContext, XhrStartContext } from './browser/xhrObservable'\nexport { initXhrObservable } from './browser/xhrObservable'\nexport type { FetchResolveContext, FetchStartContext, FetchContext } from './browser/fetchObservable'\nexport { initFetchObservable, resetFetchObservable } from './browser/fetchObservable'\nexport type { PageMayExitEvent } from './browser/pageMayExitObservable'\nexport { createPageMayExitObservable, PageExitReason, isPageExitReason } from './browser/pageMayExitObservable'\nexport * from './browser/addEventListener'\nexport { requestIdleCallback } from './tools/requestIdleCallback'\nexport * from './tools/taskQueue'\nexport * from './tools/timer'\nexport type { ConsoleLog } from './domain/console/consoleObservable'\nexport { initConsoleObservable, resetConsoleObservable } from './domain/console/consoleObservable'\nexport type { BoundedBuffer } from './tools/boundedBuffer'\nexport { createBoundedBuffer } from './tools/boundedBuffer'\nexport { catchUserErrors } from './tools/catchUserErrors'\nexport type { ContextManager } from './domain/context/contextManager'\nexport { createContextManager } from './domain/context/contextManager'\nexport { storeContextManager, removeStorageListeners } from './domain/context/storeContextManager'\nexport type { CustomerDataTracker, CustomerDataTrackerManager } from './domain/context/customerDataTracker'\nexport {\n createCustomerDataTrackerManager,\n createCustomerDataTracker,\n CUSTOMER_DATA_BYTES_LIMIT,\n CustomerDataCompressionStatus,\n} from './domain/context/customerDataTracker'\nexport { CustomerDataType } from './domain/context/contextConstants'\nexport type { ValueHistory, ValueHistoryEntry } from './tools/valueHistory'\nexport { createValueHistory, CLEAR_OLD_VALUES_INTERVAL } from './tools/valueHistory'\nexport { readBytesFromStream } from './tools/readBytesFromStream'\nexport type { SessionState } from './domain/session/sessionState'\nexport { STORAGE_POLL_DELAY } from './domain/session/sessionStore'\nexport { SESSION_STORE_KEY } from './domain/session/storeStrategies/sessionStoreStrategy'\nexport {\n willSyntheticsInjectRum,\n getSyntheticsTestId,\n getSyntheticsResultId,\n} from './domain/synthetics/syntheticsWorkerValues'\nexport type { User } from './domain/user.types'\nexport type { Account } from './domain/account.types'\nexport { checkContext } from './domain/context/contextUtils'\nexport * from './domain/resourceUtils'\nexport * from './tools/utils/polyfills'\nexport * from './tools/utils/numberUtils'\nexport * from './tools/utils/byteUtils'\nexport * from './tools/utils/objectUtils'\nexport * from './tools/utils/functionUtils'\nexport * from './tools/serialisation/jsonStringify'\nexport * from './tools/mergeInto'\nexport * from './tools/utils/stringUtils'\nexport * from './tools/matchOption'\nexport * from './tools/utils/responseUtils'\nexport * from './tools/utils/typeUtils'\nexport type { RawError, RawErrorCause, ErrorWithCause, Csp } from './domain/error/error.types'\nexport { ErrorHandling, ErrorSource } from './domain/error/error.types'\nexport * from './domain/deflate'\nexport * from './domain/connectivity'\nexport * from './tools/stackTrace/handlingStack'\n", "import type { Context, ContextManager, User } from '@datadog/browser-core'\nimport type { RecorderApi } from '../../boot/rumPublicApi'\n\nexport interface CommonContext {\n user: User\n // We don't want to enforce id internally so use Context as internal type\n account: Context\n context: Context\n hasReplay: true | undefined\n}\n\nexport function buildCommonContext(\n globalContextManager: ContextManager,\n userContextManager: ContextManager,\n accountContextManager: ContextManager,\n recorderApi: RecorderApi\n): CommonContext {\n return {\n context: globalContextManager.getContext(),\n user: userContextManager.getContext(),\n account: accountContextManager.getContext(),\n hasReplay: recorderApi.isRecording() ? true : undefined,\n }\n}\n", "import type { ClocksState, Duration, Context } from '@datadog/browser-core'\nimport { clocksNow, combine, elapsed, generateUUID, toServerDuration } from '@datadog/browser-core'\nimport type { LifeCycle, RawRumEventCollectedData } from '../lifeCycle'\nimport { LifeCycleEventType } from '../lifeCycle'\nimport type { RawRumVitalEvent } from '../../rawRumEvent.types'\nimport { RumEventType, VitalType } from '../../rawRumEvent.types'\nimport type { PageStateHistory } from '../contexts/pageStateHistory'\nimport { PageState } from '../contexts/pageStateHistory'\n\nexport interface DurationVitalOptions {\n context?: Context\n description?: string\n}\n\nexport interface DurationVitalReference {\n __dd_vital_reference: true\n}\n\nexport interface DurationVitalStart {\n name: string\n startClocks: ClocksState\n context?: Context\n description?: string\n}\n\nexport interface DurationVital {\n name: string\n type: VitalType.DURATION\n startClocks: ClocksState\n duration: Duration\n description?: string\n context?: Context\n}\n\nexport interface CustomVitalsState {\n vitalsByName: Map\n vitalsByReference: WeakMap\n}\n\nexport function createCustomVitalsState() {\n const vitalsByName = new Map()\n const vitalsByReference = new WeakMap()\n return { vitalsByName, vitalsByReference }\n}\n\nexport function startVitalCollection(\n lifeCycle: LifeCycle,\n pageStateHistory: PageStateHistory,\n customVitalsState: CustomVitalsState\n) {\n function isValid(vital: DurationVital) {\n return !pageStateHistory.wasInPageStateDuringPeriod(PageState.FROZEN, vital.startClocks.relative, vital.duration)\n }\n\n function addDurationVital(vital: DurationVital) {\n if (isValid(vital)) {\n lifeCycle.notify(LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processVital(vital, true))\n }\n }\n\n return {\n addDurationVital,\n startDurationVital: (name: string, options: DurationVitalOptions = {}) =>\n startDurationVital(customVitalsState, name, options),\n stopDurationVital: (nameOrRef: string | DurationVitalReference, options: DurationVitalOptions = {}) => {\n stopDurationVital(addDurationVital, customVitalsState, nameOrRef, options)\n },\n }\n}\n\nexport function startDurationVital(\n { vitalsByName, vitalsByReference }: CustomVitalsState,\n name: string,\n options: DurationVitalOptions = {}\n) {\n const vital = {\n name,\n startClocks: clocksNow(),\n context: options.context,\n description: options.description,\n }\n\n // To avoid leaking implementation details of the vital, we return a reference to it.\n const reference: DurationVitalReference = { __dd_vital_reference: true }\n\n vitalsByName.set(name, vital)\n\n // To avoid memory leaks caused by the creation of numerous references (e.g., from improper useEffect implementations), we use a WeakMap.\n vitalsByReference.set(reference, vital)\n\n return reference\n}\n\nexport function stopDurationVital(\n stopCallback: (vital: DurationVital) => void,\n { vitalsByName, vitalsByReference }: CustomVitalsState,\n nameOrRef: string | DurationVitalReference,\n options: DurationVitalOptions = {}\n) {\n const vitalStart = typeof nameOrRef === 'string' ? vitalsByName.get(nameOrRef) : vitalsByReference.get(nameOrRef)\n\n if (!vitalStart) {\n return\n }\n\n stopCallback(buildDurationVital(vitalStart, vitalStart.startClocks, options, clocksNow()))\n\n if (typeof nameOrRef === 'string') {\n vitalsByName.delete(nameOrRef)\n } else {\n vitalsByReference.delete(nameOrRef)\n }\n}\n\nfunction buildDurationVital(\n vitalStart: DurationVitalStart,\n startClocks: ClocksState,\n stopOptions: DurationVitalOptions,\n stopClocks: ClocksState\n): DurationVital {\n return {\n name: vitalStart.name,\n type: VitalType.DURATION,\n startClocks,\n duration: elapsed(startClocks.timeStamp, stopClocks.timeStamp),\n context: combine(vitalStart.context, stopOptions.context),\n description: stopOptions.description ?? vitalStart.description,\n }\n}\n\nfunction processVital(vital: DurationVital, valueComputedBySdk: boolean): RawRumEventCollectedData {\n const rawRumEvent: RawRumVitalEvent = {\n date: vital.startClocks.timeStamp,\n vital: {\n id: generateUUID(),\n type: vital.type,\n name: vital.name,\n duration: toServerDuration(vital.duration),\n description: vital.description,\n },\n type: RumEventType.VITAL,\n }\n\n if (valueComputedBySdk) {\n rawRumEvent._dd = {\n vital: {\n computed_value: true,\n },\n }\n }\n\n return {\n rawRumEvent,\n startTime: vital.startClocks.relative,\n duration: vital.duration,\n customerContext: vital.context,\n domainContext: {},\n }\n}\n", "import type { RumPublicApi, Strategy } from '../boot/rumPublicApi'\nimport type { RumInitConfiguration } from './configuration'\n\nexport interface RumPlugin {\n name: string\n getConfigurationTelemetry?(): Record\n onInit?(options: { initConfiguration: RumInitConfiguration; publicApi: RumPublicApi }): void\n onRumStart?(options: { strategy: Strategy }): void\n}\n\ntype MethodNames = 'onInit' | 'onRumStart'\ntype MethodParameter = Parameters>[0]\n\nexport function callPluginsMethod(\n plugins: RumPlugin[] | undefined,\n methodName: MethodName,\n parameter: MethodParameter\n): void\nexport function callPluginsMethod(\n plugins: RumPlugin[] | undefined,\n methodName: MethodName,\n parameter: any\n) {\n if (!plugins) {\n return\n }\n for (const plugin of plugins) {\n const method = plugin[methodName]\n if (method) {\n method(parameter)\n }\n }\n}\n", "interface BaseIdentifier {\n toString(radix?: number): string\n}\n\nexport interface TraceIdentifier extends BaseIdentifier {\n // We use a brand to distinguish between TraceIdentifier and SpanIdentifier, else TypeScript\n // considers them as the same type\n __brand: 'traceIdentifier'\n}\n\nexport interface SpanIdentifier extends BaseIdentifier {\n __brand: 'spanIdentifier'\n}\n\nexport function createTraceIdentifier() {\n return createIdentifier(64) as TraceIdentifier\n}\n\nexport function createSpanIdentifier() {\n return createIdentifier(63) as SpanIdentifier\n}\n\nfunction createIdentifier(bits: 63 | 64): BaseIdentifier {\n const buffer = crypto.getRandomValues(new Uint32Array(2))\n if (bits === 63) {\n // eslint-disable-next-line no-bitwise\n buffer[buffer.length - 1] >>>= 1 // force 63-bit\n }\n\n // The `.toString` function is intentionally similar to Number and BigInt `.toString` method.\n //\n // JavaScript numbers can represent integers up to 48 bits, this is why we need two of them to\n // represent a 64 bits identifier. But BigInts don't have this limitation and can represent larger\n // integer values.\n //\n // In the future, when we drop browsers without BigInts support, we could use BigInts directly\n // represent identifiers by simply returning a BigInt from this function (as all we need is a\n // value with a `.toString` method).\n //\n // Examples:\n // const buffer = getCrypto().getRandomValues(new Uint32Array(2))\n // return BigInt(buffer[0]) + BigInt(buffer[1]) << 32n\n //\n // // Alternative with BigUint64Array (different Browser support than plain bigints!):\n // return crypto.getRandomValues(new BigUint64Array(1))[0]\n //\n // For now, let's keep using two plain numbers as having two different implementations (one for\n // browsers with BigInt support and one for older browsers) don't bring much value.\n return {\n toString(radix = 10) {\n let high = buffer[1]\n let low = buffer[0]\n let str = ''\n\n do {\n const mod = (high % radix) * 4294967296 + low\n high = Math.floor(high / radix)\n low = Math.floor(mod / radix)\n str = (mod % radix).toString(radix) + str\n } while (high || low)\n\n return str\n },\n }\n}\n\nexport function toPaddedHexadecimalString(id: BaseIdentifier) {\n return id.toString(16).padStart(16, '0')\n}\n", "import { performDraw } from '@datadog/browser-core'\n\nlet sampleDecisionCache: { sessionId: string; decision: boolean } | undefined\n\nexport function isTraceSampled(sessionId: string, sampleRate: number) {\n // Shortcuts for common cases. This is not strictly necessary, but it makes the code faster for\n // customers willing to ingest all traces.\n if (sampleRate === 100) {\n return true\n }\n\n if (sampleRate === 0) {\n return false\n }\n\n if (sampleDecisionCache && sessionId === sampleDecisionCache.sessionId) {\n return sampleDecisionCache.decision\n }\n\n let decision: boolean\n // @ts-expect-error BigInt might not be defined in every browser we support\n if (window.BigInt) {\n decision = sampleUsingKnuthFactor(BigInt(`0x${sessionId.split('-')[4]}`), sampleRate)\n } else {\n // For simplicity, we don't use consistent sampling for browser without BigInt support\n // TODO: remove this when all browser we support have BigInt support\n decision = performDraw(sampleRate)\n }\n sampleDecisionCache = { sessionId, decision }\n return decision\n}\n\n// Exported for tests\nexport function resetSampleDecisionCache() {\n sampleDecisionCache = undefined\n}\n\n/**\n * Perform sampling using the Knuth factor method. This method offer consistent sampling result\n * based on the provided identifier.\n *\n * @param identifier The identifier to use for sampling.\n * @param sampleRate The sample rate in percentage between 0 and 100.\n */\nexport function sampleUsingKnuthFactor(identifier: bigint, sampleRate: number) {\n // The formula is:\n //\n // (identifier * knuthFactor) % 2^64 < sampleRate * 2^64\n //\n // Because JavaScript numbers are 64-bit floats, we can't represent 64-bit integers, and the\n // modulo would be incorrect. Thus, we are using BigInts here.\n //\n // Implementation in other languages:\n // * Go https://github.com/DataDog/dd-trace-go/blob/ec6fbb1f2d517b7b8e69961052adf7136f3af773/ddtrace/tracer/sampler.go#L86-L91\n // * Python https://github.com/DataDog/dd-trace-py/blob/0cee2f066fb6e79aa15947c1514c0f406dea47c5/ddtrace/sampling_rule.py#L197\n // * Ruby https://github.com/DataDog/dd-trace-rb/blob/1a6e255cdcb7e7e22235ea5955f90f6dfa91045d/lib/datadog/tracing/sampling/rate_sampler.rb#L42\n // * C++ https://github.com/DataDog/dd-trace-cpp/blob/159629edc438ae45f2bb318eb7bd51abd05e94b5/src/datadog/trace_sampler.cpp#L58\n // * Java https://github.com/DataDog/dd-trace-java/blob/896dd6b380533216e0bdee59614606c8272d313e/dd-trace-core/src/main/java/datadog/trace/common/sampling/DeterministicSampler.java#L48\n //\n // Note: All implementations have slight variations. Some of them use '<=' instead of '<', and\n // use `sampleRate * 2^64 - 1` instead of `sampleRate * 2^64`. The following implementation\n // should adhere to the spec and is a bit simpler than using a 2^64-1 limit as there are less\n // BigInt arithmetic to write. In practice this does not matter, as we are using floating point\n // numbers in the end, and Number(2n**64n-1n) === Number(2n**64n).\n const knuthFactor = BigInt('1111111111111111111')\n const twoPow64 = BigInt('0x10000000000000000') // 2n ** 64n\n const hash = (identifier * knuthFactor) % twoPow64\n return Number(hash) <= (sampleRate / 100) * Number(twoPow64)\n}\n", "export const encodedContextCache: Map = new Map()\n\nexport function getEncodedContext(key: unknown): string | undefined {\n if (!key || typeof key !== 'string') {\n return undefined\n }\n if (encodedContextCache.has(key)) {\n return encodedContextCache.get(key)\n }\n const encoded = encodeToUtf8Base64(key)\n encodedContextCache.set(key, encoded)\n return encoded\n}\n\n/**\n *\n * Helper function to encode a string to base64 in UTF-8\n * Comply with the `_dd.p.usr` standard and avoid non-ASCII characters\n * https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa\n */\nexport function encodeToUtf8Base64(plainText: string) {\n const bytes = new TextEncoder().encode(plainText)\n const binString = String.fromCodePoint(...bytes)\n return btoa(binString)\n}\n", "import {\n objectEntries,\n shallowClone,\n getType,\n isMatchOption,\n matchList,\n TraceContextInjection,\n isExperimentalFeatureEnabled,\n ExperimentalFeature,\n} from '@datadog/browser-core'\nimport type { RumConfiguration } from '../configuration'\nimport type {\n RumFetchResolveContext,\n RumFetchStartContext,\n RumXhrCompleteContext,\n RumXhrStartContext,\n} from '../requestCollection'\nimport type { CommonContext } from '../contexts/commonContext'\nimport type { RumSessionManager } from '../rumSessionManager'\nimport type { PropagatorType, TracingOption } from './tracer.types'\nimport type { SpanIdentifier, TraceIdentifier } from './identifier'\nimport { createSpanIdentifier, createTraceIdentifier, toPaddedHexadecimalString } from './identifier'\nimport { isTraceSampled } from './sampler'\nimport { getEncodedContext } from './encodedContext'\n\nexport interface Tracer {\n traceFetch: (context: Partial) => void\n traceXhr: (context: Partial, xhr: XMLHttpRequest) => void\n clearTracingIfNeeded: (context: RumFetchResolveContext | RumXhrCompleteContext) => void\n}\n\ninterface TracingHeaders {\n [key: string]: string\n}\n\nexport function isTracingOption(item: unknown): item is TracingOption {\n const expectedItem = item as TracingOption\n return (\n getType(expectedItem) === 'object' &&\n isMatchOption(expectedItem.match) &&\n Array.isArray(expectedItem.propagatorTypes)\n )\n}\n\n/**\n * Clear tracing information to avoid incomplete traces. Ideally, we should do it when the\n * request did not reach the server, but the browser does not expose this. So, we clear tracing\n * information if the request ended with status 0 without being aborted by the application.\n *\n * Reasoning:\n *\n * * Applications are usually aborting requests after a bit of time, for example when the user is\n * typing (autocompletion) or navigating away (in a SPA). With a performant device and good\n * network conditions, the request is likely to reach the server before being canceled.\n *\n * * Requests aborted otherwise (ex: lack of internet, CORS issue, blocked by a privacy extension)\n * are likely to finish quickly and without reaching the server.\n *\n * Of course, it might not be the case every time, but it should limit having incomplete traces a\n * bit.\n * */\nexport function clearTracingIfNeeded(context: RumFetchResolveContext | RumXhrCompleteContext) {\n if (context.status === 0 && !context.isAborted) {\n context.traceId = undefined\n context.spanId = undefined\n context.traceSampled = undefined\n }\n}\n\nexport function startTracer(\n configuration: RumConfiguration,\n sessionManager: RumSessionManager,\n getCommonContext: () => CommonContext\n): Tracer {\n return {\n clearTracingIfNeeded,\n traceFetch: (context) =>\n injectHeadersIfTracingAllowed(\n configuration,\n context,\n sessionManager,\n getCommonContext,\n (tracingHeaders: TracingHeaders) => {\n if (context.input instanceof Request && !context.init?.headers) {\n context.input = new Request(context.input)\n Object.keys(tracingHeaders).forEach((key) => {\n ;(context.input as Request).headers.append(key, tracingHeaders[key])\n })\n } else {\n context.init = shallowClone(context.init)\n const headers: Array<[string, string]> = []\n if (context.init.headers instanceof Headers) {\n context.init.headers.forEach((value, key) => {\n headers.push([key, value])\n })\n } else if (Array.isArray(context.init.headers)) {\n context.init.headers.forEach((header) => {\n headers.push(header)\n })\n } else if (context.init.headers) {\n Object.keys(context.init.headers).forEach((key) => {\n headers.push([key, (context.init!.headers as Record)[key]])\n })\n }\n context.init.headers = headers.concat(objectEntries(tracingHeaders))\n }\n }\n ),\n traceXhr: (context, xhr) =>\n injectHeadersIfTracingAllowed(\n configuration,\n context,\n sessionManager,\n getCommonContext,\n (tracingHeaders: TracingHeaders) => {\n Object.keys(tracingHeaders).forEach((name) => {\n xhr.setRequestHeader(name, tracingHeaders[name])\n })\n }\n ),\n }\n}\n\nfunction injectHeadersIfTracingAllowed(\n configuration: RumConfiguration,\n context: Partial,\n sessionManager: RumSessionManager,\n getCommonContext: () => CommonContext,\n inject: (tracingHeaders: TracingHeaders) => void\n) {\n const session = sessionManager.findTrackedSession()\n if (!session) {\n return\n }\n\n const tracingOption = configuration.allowedTracingUrls.find((tracingOption) =>\n matchList([tracingOption.match], context.url!, true)\n )\n if (!tracingOption) {\n return\n }\n\n const traceSampled = isTraceSampled(session.id, configuration.traceSampleRate)\n\n const shouldInjectHeaders = traceSampled || configuration.traceContextInjection === TraceContextInjection.ALL\n if (!shouldInjectHeaders) {\n return\n }\n\n context.traceSampled = traceSampled\n context.traceId = createTraceIdentifier()\n context.spanId = createSpanIdentifier()\n\n inject(\n makeTracingHeaders(\n context.traceId,\n context.spanId,\n context.traceSampled,\n tracingOption.propagatorTypes,\n getCommonContext\n )\n )\n}\n\n/**\n * When trace is not sampled, set priority to '0' instead of not adding the tracing headers\n * to prepare the implementation for sampling delegation.\n */\nfunction makeTracingHeaders(\n traceId: TraceIdentifier,\n spanId: SpanIdentifier,\n traceSampled: boolean,\n propagatorTypes: PropagatorType[],\n getCommonContext: () => CommonContext\n): TracingHeaders {\n const tracingHeaders: TracingHeaders = {}\n\n propagatorTypes.forEach((propagatorType) => {\n switch (propagatorType) {\n case 'datadog': {\n Object.assign(tracingHeaders, {\n 'x-datadog-origin': 'rum',\n 'x-datadog-parent-id': spanId.toString(),\n 'x-datadog-sampling-priority': traceSampled ? '1' : '0',\n 'x-datadog-trace-id': traceId.toString(),\n })\n break\n }\n // https://www.w3.org/TR/trace-context/\n case 'tracecontext': {\n Object.assign(tracingHeaders, {\n traceparent: `00-0000000000000000${toPaddedHexadecimalString(traceId)}-${toPaddedHexadecimalString(spanId)}-0${\n traceSampled ? '1' : '0'\n }`,\n tracestate: `dd=${getTraceStateDatadogItems(traceSampled, getCommonContext).join(';')}`,\n })\n break\n }\n // https://github.com/openzipkin/b3-propagation\n case 'b3': {\n Object.assign(tracingHeaders, {\n b3: `${toPaddedHexadecimalString(traceId)}-${toPaddedHexadecimalString(spanId)}-${traceSampled ? '1' : '0'}`,\n })\n break\n }\n case 'b3multi': {\n Object.assign(tracingHeaders, {\n 'X-B3-TraceId': toPaddedHexadecimalString(traceId),\n 'X-B3-SpanId': toPaddedHexadecimalString(spanId),\n 'X-B3-Sampled': traceSampled ? '1' : '0',\n })\n break\n }\n }\n })\n return tracingHeaders\n}\n\nfunction getTraceStateDatadogItems(traceSampled: boolean, getCommonContext: () => CommonContext): string[] {\n const traceStateDatadogItems: string[] = [`s:${traceSampled ? '1' : '0'}`, 'o:rum']\n if (isExperimentalFeatureEnabled(ExperimentalFeature.USER_ACCOUNT_TRACE_HEADER)) {\n /**\n * We should only enable this feature after the decoding service is available\n */\n const userIdEncoded = getEncodedContext(getCommonContext().user.id)\n const accountIdEncoded = getEncodedContext(getCommonContext().account.id)\n if (userIdEncoded) {\n traceStateDatadogItems.push(`t.usr.id:${userIdEncoded}`)\n }\n if (accountIdEncoded) {\n traceStateDatadogItems.push(`t.account.id:${accountIdEncoded}`)\n }\n }\n return traceStateDatadogItems\n}\n", "import type { Configuration, InitConfiguration, MatchOption, RawTelemetryConfiguration } from '@datadog/browser-core'\nimport {\n getType,\n isMatchOption,\n serializeConfiguration,\n DefaultPrivacyLevel,\n TraceContextInjection,\n display,\n objectHasValue,\n validateAndBuildConfiguration,\n isSampleRate,\n isNumber,\n isExperimentalFeatureEnabled,\n ExperimentalFeature,\n} from '@datadog/browser-core'\nimport type { RumEventDomainContext } from '../../domainContext.types'\nimport type { RumEvent } from '../../rumEvent.types'\nimport type { RumPlugin } from '../plugins'\nimport { isTracingOption } from '../tracing/tracer'\nimport type { PropagatorType, TracingOption } from '../tracing/tracer.types'\n\nexport const DEFAULT_PROPAGATOR_TYPES: PropagatorType[] = ['tracecontext', 'datadog']\n\nexport interface RumInitConfiguration extends InitConfiguration {\n // global options\n /**\n * The RUM application ID.\n */\n applicationId: string\n /**\n * Access to every event collected by the RUM SDK before they are sent to Datadog.\n * It allows:\n * - Enrich your RUM events with additional context attributes\n * - Modify your RUM events to modify their content, or redact sensitive sequences (see the list of editable properties)\n * - Discard selected RUM events\n *\n * See [Enrich And Control Browser RUM Data With beforeSend](https://docs.datadoghq.com/real_user_monitoring/guide/enrich-and-control-rum-data) for further information.\n */\n beforeSend?: ((event: RumEvent, context: RumEventDomainContext) => boolean) | undefined\n /**\n * A list of request origins ignored when computing the page activity.\n * See [How page activity is calculated](https://docs.datadoghq.com/real_user_monitoring/browser/monitoring_page_performance/#how-page-activity-is-calculated) for further information.\n */\n excludedActivityUrls?: MatchOption[] | undefined\n /**\n * URL pointing to the Datadog Browser SDK Worker JavaScript file. The URL can be relative or absolute, but is required to have the same origin as the web application.\n * See [Content Security Policy guidelines](https://docs.datadoghq.com/integrations/content_security_policy_logs/?tab=firefox#use-csp-with-real-user-monitoring-and-session-replay) for further information.\n */\n workerUrl?: string\n /**\n * Compress requests sent to the Datadog intake to reduce bandwidth usage when sending large amounts of data. The compression is done in a Worker thread.\n * See [Content Security Policy guidelines](https://docs.datadoghq.com/integrations/content_security_policy_logs/?tab=firefox#use-csp-with-real-user-monitoring-and-session-replay) for further information.\n */\n compressIntakeRequests?: boolean | undefined\n remoteConfigurationId?: string | undefined\n\n // tracing options\n /**\n * A list of request URLs used to inject tracing headers.\n * See [Connect RUM and Traces](https://docs.datadoghq.com/real_user_monitoring/platform/connect_rum_and_traces/?tab=browserrum) for further information.\n */\n allowedTracingUrls?: Array | undefined\n\n /**\n * The percentage of requests to trace: 100 for all, 0 for none.\n * See [Connect RUM and Traces](https://docs.datadoghq.com/real_user_monitoring/platform/connect_rum_and_traces/?tab=browserrum) for further information.\n */\n traceSampleRate?: number | undefined\n /**\n * If you set a `traceSampleRate`, to ensure backend services' sampling decisions are still applied, configure the `traceContextInjection` initialization parameter to sampled.\n * @default sampled\n * See [Connect RUM and Traces](https://docs.datadoghq.com/real_user_monitoring/platform/connect_rum_and_traces/?tab=browserrum) for further information.\n */\n traceContextInjection?: TraceContextInjection | undefined\n\n // replay options\n /**\n * Allow to protect end user privacy and prevent sensitive organizational information from being collected.\n * @default mask\n * See [Replay Privacy Options](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser/privacy_options) for further information.\n */\n defaultPrivacyLevel?: DefaultPrivacyLevel | undefined\n /**\n * If you are accessing Datadog through a custom subdomain, you can set `subdomain` to include your custom domain in the `getSessionReplayLink()` returned URL .\n * See [Connect Session Replay To Your Third-Party Tools](https://docs.datadoghq.com/real_user_monitoring/guide/connect-session-replay-to-your-third-party-tools) for further information.\n */\n subdomain?: string\n /**\n * The percentage of tracked sessions with [Browser RUM & Session Replay pricing](https://www.datadoghq.com/pricing/?product=real-user-monitoring--session-replay#real-user-monitoring--session-replay) features: 100 for all, 0 for none.\n * See [Configure Your Setup For Browser RUM and Browser RUM & Session Replay Sampling](https://docs.datadoghq.com/real_user_monitoring/guide/sampling-browser-plans) for further information.\n */\n sessionReplaySampleRate?: number | undefined\n /**\n * If the session is sampled for Session Replay, only start the recording when `startSessionReplayRecording()` is called, instead of at the beginning of the session. Default: if startSessionReplayRecording is 0, true; otherwise, false.\n * See [Session Replay Usage](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser/#usage) for further information.\n */\n startSessionReplayRecordingManually?: boolean | undefined\n\n /**\n * Enables privacy control for action names.\n */\n enablePrivacyForActionName?: boolean | undefined // TODO next major: remove this option and make privacy for action name the default behavior\n /**\n * Enables automatic collection of users actions.\n * See [Tracking User Actions](https://docs.datadoghq.com/real_user_monitoring/browser/tracking_user_actions) for further information.\n * @default true\n */\n trackUserInteractions?: boolean | undefined\n /**\n * Specify your own attribute to use to name actions.\n * See [Declare a name for click actions](https://docs.datadoghq.com/real_user_monitoring/browser/tracking_user_actions/#declare-a-name-for-click-actions) for further information.\n */\n actionNameAttribute?: string | undefined\n\n // view options\n /**\n * Allows you to control RUM views creation. See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/?tab=npm#override-default-rum-view-names) for further information.\n */\n trackViewsManually?: boolean | undefined\n /**\n * Enables collection of resource events.\n * @default true\n */\n trackResources?: boolean | undefined\n /**\n * Enables collection of long task events.\n * @default true\n */\n trackLongTasks?: boolean | undefined\n\n /**\n * List of plugins to enable. The plugins API is unstable and experimental, and may change without\n * notice. Please use only plugins provided by Datadog matching the version of the SDK you are\n * using.\n */\n plugins?: RumPlugin[] | undefined\n\n /**\n * Enables collection of features flags in chosen events.\n */\n trackFeatureFlagsForEvents?: FeatureFlagsForEvents[]\n\n /**\n * @experimental Not ready for production.\n * The percentage of users profiled. A value between 0 and 100.\n * @default 0\n */\n profilingSampleRate?: number | undefined\n}\n\nexport type HybridInitConfiguration = Omit\n\nexport type FeatureFlagsForEvents = 'vital' | 'action' | 'long_task' | 'resource'\n\nexport interface RumConfiguration extends Configuration {\n // Built from init configuration\n actionNameAttribute: string | undefined\n traceSampleRate: number\n rulePsr: number | undefined\n allowedTracingUrls: TracingOption[]\n excludedActivityUrls: MatchOption[]\n workerUrl: string | undefined\n compressIntakeRequests: boolean\n applicationId: string\n defaultPrivacyLevel: DefaultPrivacyLevel\n enablePrivacyForActionName: boolean\n sessionReplaySampleRate: number\n startSessionReplayRecordingManually: boolean\n trackUserInteractions: boolean\n trackViewsManually: boolean\n trackResources: boolean\n trackLongTasks: boolean\n version?: string\n subdomain?: string\n customerDataTelemetrySampleRate: number\n traceContextInjection: TraceContextInjection\n plugins: RumPlugin[]\n trackFeatureFlagsForEvents: FeatureFlagsForEvents[]\n profilingSampleRate: number\n}\n\nexport function validateAndBuildRumConfiguration(\n initConfiguration: RumInitConfiguration\n): RumConfiguration | undefined {\n if (\n initConfiguration.trackFeatureFlagsForEvents !== undefined &&\n !Array.isArray(initConfiguration.trackFeatureFlagsForEvents)\n ) {\n display.warn('trackFeatureFlagsForEvents should be an array')\n }\n\n if (!initConfiguration.applicationId) {\n display.error('Application ID is not configured, no RUM data will be collected.')\n return\n }\n\n if (\n !isSampleRate(initConfiguration.sessionReplaySampleRate, 'Session Replay') ||\n !isSampleRate(initConfiguration.traceSampleRate, 'Trace')\n ) {\n return\n }\n\n if (initConfiguration.excludedActivityUrls !== undefined && !Array.isArray(initConfiguration.excludedActivityUrls)) {\n display.error('Excluded Activity Urls should be an array')\n return\n }\n\n const allowedTracingUrls = validateAndBuildTracingOptions(initConfiguration)\n if (!allowedTracingUrls) {\n return\n }\n\n const baseConfiguration = validateAndBuildConfiguration(initConfiguration)\n if (!baseConfiguration) {\n return\n }\n\n const profilingEnabled = isExperimentalFeatureEnabled(ExperimentalFeature.PROFILING)\n\n const sessionReplaySampleRate = initConfiguration.sessionReplaySampleRate ?? 0\n\n return {\n applicationId: initConfiguration.applicationId,\n version: initConfiguration.version || undefined,\n actionNameAttribute: initConfiguration.actionNameAttribute,\n sessionReplaySampleRate,\n startSessionReplayRecordingManually:\n initConfiguration.startSessionReplayRecordingManually !== undefined\n ? !!initConfiguration.startSessionReplayRecordingManually\n : sessionReplaySampleRate === 0,\n traceSampleRate: initConfiguration.traceSampleRate ?? 100,\n rulePsr: isNumber(initConfiguration.traceSampleRate) ? initConfiguration.traceSampleRate / 100 : undefined,\n allowedTracingUrls,\n excludedActivityUrls: initConfiguration.excludedActivityUrls ?? [],\n workerUrl: initConfiguration.workerUrl,\n compressIntakeRequests: !!initConfiguration.compressIntakeRequests,\n trackUserInteractions: !!(initConfiguration.trackUserInteractions ?? true),\n trackViewsManually: !!initConfiguration.trackViewsManually,\n trackResources: !!(initConfiguration.trackResources ?? true),\n trackLongTasks: !!(initConfiguration.trackLongTasks ?? true),\n subdomain: initConfiguration.subdomain,\n defaultPrivacyLevel: objectHasValue(DefaultPrivacyLevel, initConfiguration.defaultPrivacyLevel)\n ? initConfiguration.defaultPrivacyLevel\n : DefaultPrivacyLevel.MASK,\n enablePrivacyForActionName: !!initConfiguration.enablePrivacyForActionName,\n customerDataTelemetrySampleRate: 1,\n traceContextInjection: objectHasValue(TraceContextInjection, initConfiguration.traceContextInjection)\n ? initConfiguration.traceContextInjection\n : TraceContextInjection.SAMPLED,\n plugins: initConfiguration.plugins || [],\n trackFeatureFlagsForEvents: initConfiguration.trackFeatureFlagsForEvents || [],\n profilingSampleRate: profilingEnabled ? (initConfiguration.profilingSampleRate ?? 0) : 0, // Enforce 0 if profiling is not enabled, and set 0 as default when not set.\n ...baseConfiguration,\n }\n}\n\n/**\n * Validates allowedTracingUrls and converts match options to tracing options\n */\nfunction validateAndBuildTracingOptions(initConfiguration: RumInitConfiguration): TracingOption[] | undefined {\n if (initConfiguration.allowedTracingUrls === undefined) {\n return []\n }\n if (!Array.isArray(initConfiguration.allowedTracingUrls)) {\n display.error('Allowed Tracing URLs should be an array')\n return\n }\n if (initConfiguration.allowedTracingUrls.length !== 0 && initConfiguration.service === undefined) {\n display.error('Service needs to be configured when tracing is enabled')\n return\n }\n // Convert from (MatchOption | TracingOption) to TracingOption, remove unknown properties\n const tracingOptions: TracingOption[] = []\n initConfiguration.allowedTracingUrls.forEach((option) => {\n if (isMatchOption(option)) {\n tracingOptions.push({ match: option, propagatorTypes: DEFAULT_PROPAGATOR_TYPES })\n } else if (isTracingOption(option)) {\n tracingOptions.push(option)\n } else {\n display.warn(\n 'Allowed Tracing Urls parameters should be a string, RegExp, function, or an object. Ignoring parameter',\n option\n )\n }\n })\n\n return tracingOptions\n}\n\n/**\n * Combines the selected tracing propagators from the different options in allowedTracingUrls\n */\nfunction getSelectedTracingPropagators(configuration: RumInitConfiguration): PropagatorType[] {\n const usedTracingPropagators = new Set()\n\n if (Array.isArray(configuration.allowedTracingUrls) && configuration.allowedTracingUrls.length > 0) {\n configuration.allowedTracingUrls.forEach((option) => {\n if (isMatchOption(option)) {\n DEFAULT_PROPAGATOR_TYPES.forEach((propagatorType) => usedTracingPropagators.add(propagatorType))\n } else if (getType(option) === 'object' && Array.isArray(option.propagatorTypes)) {\n // Ensure we have an array, as we cannot rely on types yet (configuration is provided by users)\n option.propagatorTypes.forEach((propagatorType) => usedTracingPropagators.add(propagatorType))\n }\n })\n }\n\n return Array.from(usedTracingPropagators)\n}\n\nexport function serializeRumConfiguration(configuration: RumInitConfiguration) {\n const baseSerializedConfiguration = serializeConfiguration(configuration)\n\n return {\n session_replay_sample_rate: configuration.sessionReplaySampleRate,\n start_session_replay_recording_manually: configuration.startSessionReplayRecordingManually,\n trace_sample_rate: configuration.traceSampleRate,\n trace_context_injection: configuration.traceContextInjection,\n action_name_attribute: configuration.actionNameAttribute,\n use_allowed_tracing_urls:\n Array.isArray(configuration.allowedTracingUrls) && configuration.allowedTracingUrls.length > 0,\n selected_tracing_propagators: getSelectedTracingPropagators(configuration),\n default_privacy_level: configuration.defaultPrivacyLevel,\n enable_privacy_for_action_name: configuration.enablePrivacyForActionName,\n use_excluded_activity_urls:\n Array.isArray(configuration.excludedActivityUrls) && configuration.excludedActivityUrls.length > 0,\n use_worker_url: !!configuration.workerUrl,\n compress_intake_requests: configuration.compressIntakeRequests,\n track_views_manually: configuration.trackViewsManually,\n track_user_interactions: configuration.trackUserInteractions,\n track_resources: configuration.trackResources,\n track_long_task: configuration.trackLongTasks,\n plugins: configuration.plugins?.map((plugin) => ({\n name: plugin.name,\n ...plugin.getConfigurationTelemetry?.(),\n })),\n track_feature_flags_for_events: configuration.trackFeatureFlagsForEvents,\n ...baseSerializedConfiguration,\n } satisfies RawTelemetryConfiguration\n}\n", "import { display, addEventListener, buildEndpointHost } from '@datadog/browser-core'\nimport type { RumInitConfiguration } from './configuration'\n\nconst REMOTE_CONFIGURATION_VERSION = 'v1'\n\nexport function fetchAndApplyRemoteConfiguration(\n initConfiguration: RumInitConfiguration,\n callback: (initConfiguration: RumInitConfiguration) => void\n) {\n fetchRemoteConfiguration(initConfiguration, (remoteInitConfiguration) => {\n callback(applyRemoteConfiguration(initConfiguration, remoteInitConfiguration))\n })\n}\n\nexport function applyRemoteConfiguration(\n initConfiguration: RumInitConfiguration,\n remoteInitConfiguration: Partial\n) {\n return { ...initConfiguration, ...remoteInitConfiguration }\n}\n\nexport function fetchRemoteConfiguration(\n configuration: RumInitConfiguration,\n callback: (remoteConfiguration: Partial) => void\n) {\n const xhr = new XMLHttpRequest()\n\n addEventListener(configuration, xhr, 'load', function () {\n if (xhr.status === 200) {\n const remoteConfiguration = JSON.parse(xhr.responseText)\n callback(remoteConfiguration.rum)\n } else {\n displayRemoteConfigurationFetchingError()\n }\n })\n\n addEventListener(configuration, xhr, 'error', function () {\n displayRemoteConfigurationFetchingError()\n })\n\n xhr.open('GET', buildEndpoint(configuration))\n xhr.send()\n}\n\nexport function buildEndpoint(configuration: RumInitConfiguration) {\n return `https://sdk-configuration.${buildEndpointHost('rum', configuration)}/${REMOTE_CONFIGURATION_VERSION}/${encodeURIComponent(configuration.remoteConfigurationId!)}.json`\n}\n\nfunction displayRemoteConfigurationFetchingError() {\n display.error('Error fetching the remote configuration.')\n}\n", "export * from './configuration'\nexport * from './remoteConfiguration'\n", "import {\n createBoundedBuffer,\n display,\n canUseEventBridge,\n displayAlreadyInitializedError,\n willSyntheticsInjectRum,\n noop,\n timeStampNow,\n clocksNow,\n getEventBridge,\n initFeatureFlags,\n addTelemetryConfiguration,\n initFetchObservable,\n} from '@datadog/browser-core'\nimport type { TrackingConsentState, DeflateWorker, Context } from '@datadog/browser-core'\nimport {\n validateAndBuildRumConfiguration,\n type RumConfiguration,\n type RumInitConfiguration,\n} from '../domain/configuration'\nimport type { CommonContext } from '../domain/contexts/commonContext'\nimport type { ViewOptions } from '../domain/view/trackViews'\nimport type { DurationVital, CustomVitalsState } from '../domain/vital/vitalCollection'\nimport { startDurationVital, stopDurationVital } from '../domain/vital/vitalCollection'\nimport { fetchAndApplyRemoteConfiguration, serializeRumConfiguration } from '../domain/configuration'\nimport { callPluginsMethod } from '../domain/plugins'\nimport type { RumPublicApiOptions, Strategy } from './rumPublicApi'\nimport type { StartRumResult } from './startRum'\n\nexport function createPreStartStrategy(\n { ignoreInitIfSyntheticsWillInjectRum, startDeflateWorker }: RumPublicApiOptions,\n getCommonContext: () => CommonContext,\n trackingConsentState: TrackingConsentState,\n customVitalsState: CustomVitalsState,\n doStartRum: (\n configuration: RumConfiguration,\n deflateWorker: DeflateWorker | undefined,\n initialViewOptions?: ViewOptions\n ) => StartRumResult\n): Strategy {\n const bufferApiCalls = createBoundedBuffer()\n\n let firstStartViewCall:\n | { options: ViewOptions | undefined; callback: (startRumResult: StartRumResult) => void }\n | undefined\n let deflateWorker: DeflateWorker | undefined\n\n let cachedInitConfiguration: RumInitConfiguration | undefined\n let cachedConfiguration: RumConfiguration | undefined\n\n const trackingConsentStateSubscription = trackingConsentState.observable.subscribe(tryStartRum)\n\n const emptyContext: Context = {}\n\n function tryStartRum() {\n if (!cachedInitConfiguration || !cachedConfiguration || !trackingConsentState.isGranted()) {\n return\n }\n\n trackingConsentStateSubscription.unsubscribe()\n\n let initialViewOptions: ViewOptions | undefined\n\n if (cachedConfiguration.trackViewsManually) {\n if (!firstStartViewCall) {\n return\n }\n // An initial view is always created when starting RUM.\n // When tracking views automatically, any startView call before RUM start creates an extra\n // view.\n // When tracking views manually, we use the ViewOptions from the first startView call as the\n // initial view options, and we remove the actual startView call so we don't create an extra\n // view.\n bufferApiCalls.remove(firstStartViewCall.callback)\n initialViewOptions = firstStartViewCall.options\n }\n\n const startRumResult = doStartRum(cachedConfiguration, deflateWorker, initialViewOptions)\n\n bufferApiCalls.drain(startRumResult)\n }\n\n function doInit(initConfiguration: RumInitConfiguration) {\n const eventBridgeAvailable = canUseEventBridge()\n if (eventBridgeAvailable) {\n initConfiguration = overrideInitConfigurationForBridge(initConfiguration)\n }\n\n // Update the exposed initConfiguration to reflect the bridge and remote configuration overrides\n cachedInitConfiguration = initConfiguration\n addTelemetryConfiguration(serializeRumConfiguration(initConfiguration))\n\n if (cachedConfiguration) {\n displayAlreadyInitializedError('DD_RUM', initConfiguration)\n return\n }\n\n const configuration = validateAndBuildRumConfiguration(initConfiguration)\n if (!configuration) {\n return\n }\n\n if (!eventBridgeAvailable && !configuration.sessionStoreStrategyType) {\n display.warn('No storage available for session. We will not send any data.')\n return\n }\n\n if (configuration.compressIntakeRequests && !eventBridgeAvailable && startDeflateWorker) {\n deflateWorker = startDeflateWorker(\n configuration,\n 'Datadog RUM',\n // Worker initialization can fail asynchronously, especially in Firefox where even CSP\n // issues are reported asynchronously. For now, the SDK will continue its execution even if\n // data won't be sent to Datadog. We could improve this behavior in the future.\n noop\n )\n if (!deflateWorker) {\n // `startDeflateWorker` should have logged an error message explaining the issue\n return\n }\n }\n\n cachedConfiguration = configuration\n // Instrumuent fetch to track network requests\n // This is needed in case the consent is not granted and some cutsomer\n // library (Apollo Client) is storing uninstrumented fetch to be used later\n // The subscrption is needed so that the instrumentation process is completed\n initFetchObservable().subscribe(noop)\n\n trackingConsentState.tryToInit(configuration.trackingConsent)\n tryStartRum()\n }\n\n const addDurationVital = (vital: DurationVital) => {\n bufferApiCalls.add((startRumResult) => startRumResult.addDurationVital(vital))\n }\n\n const strategy: Strategy = {\n init(initConfiguration, publicApi) {\n if (!initConfiguration) {\n display.error('Missing configuration')\n return\n }\n // Set the experimental feature flags as early as possible, so we can use them in most places\n initFeatureFlags(initConfiguration.enableExperimentalFeatures)\n\n // Expose the initial configuration regardless of initialization success.\n cachedInitConfiguration = initConfiguration\n\n // If we are in a Synthetics test configured to automatically inject a RUM instance, we want\n // to completely discard the customer application RUM instance by ignoring their init() call.\n // But, we should not ignore the init() call from the Synthetics-injected RUM instance, so the\n // internal `ignoreInitIfSyntheticsWillInjectRum` option is here to bypass this condition.\n if (ignoreInitIfSyntheticsWillInjectRum && willSyntheticsInjectRum()) {\n return\n }\n\n callPluginsMethod(initConfiguration.plugins, 'onInit', { initConfiguration, publicApi })\n\n if (initConfiguration.remoteConfigurationId) {\n fetchAndApplyRemoteConfiguration(initConfiguration, doInit)\n } else {\n doInit(initConfiguration)\n }\n },\n\n get initConfiguration() {\n return cachedInitConfiguration\n },\n\n getInternalContext: noop as () => undefined,\n\n stopSession: noop,\n\n addTiming(name, time = timeStampNow()) {\n bufferApiCalls.add((startRumResult) => startRumResult.addTiming(name, time))\n },\n\n startView(options, startClocks = clocksNow()) {\n const callback = (startRumResult: StartRumResult) => {\n startRumResult.startView(options, startClocks)\n }\n bufferApiCalls.add(callback)\n\n if (!firstStartViewCall) {\n firstStartViewCall = { options, callback }\n tryStartRum()\n }\n },\n\n setViewName(name) {\n bufferApiCalls.add((startRumResult) => startRumResult.setViewName(name))\n },\n\n setViewContext(context) {\n bufferApiCalls.add((startRumResult) => startRumResult.setViewContext(context))\n },\n\n setViewContextProperty(key, value) {\n bufferApiCalls.add((startRumResult) => startRumResult.setViewContextProperty(key, value))\n },\n\n getViewContext: () => emptyContext,\n\n addAction(action, commonContext = getCommonContext()) {\n bufferApiCalls.add((startRumResult) => startRumResult.addAction(action, commonContext))\n },\n\n addError(providedError, commonContext = getCommonContext()) {\n bufferApiCalls.add((startRumResult) => startRumResult.addError(providedError, commonContext))\n },\n\n addFeatureFlagEvaluation(key, value) {\n bufferApiCalls.add((startRumResult) => startRumResult.addFeatureFlagEvaluation(key, value))\n },\n\n startDurationVital(name, options) {\n return startDurationVital(customVitalsState, name, options)\n },\n\n stopDurationVital(name, options) {\n stopDurationVital(addDurationVital, customVitalsState, name, options)\n },\n\n addDurationVital,\n }\n\n return strategy\n}\n\nfunction overrideInitConfigurationForBridge(initConfiguration: RumInitConfiguration): RumInitConfiguration {\n return {\n ...initConfiguration,\n applicationId: '00000000-aaaa-0000-aaaa-000000000000',\n clientToken: 'empty',\n sessionSampleRate: 100,\n defaultPrivacyLevel: initConfiguration.defaultPrivacyLevel ?? getEventBridge()?.getPrivacyLevel(),\n }\n}\n", "import type {\n Context,\n TimeStamp,\n RelativeTime,\n User,\n Account,\n DeflateWorker,\n DeflateEncoderStreamId,\n DeflateEncoder,\n TrackingConsent,\n PublicApi,\n Duration,\n} from '@datadog/browser-core'\nimport {\n addTelemetryUsage,\n CustomerDataType,\n createContextManager,\n deepClone,\n makePublicApi,\n monitor,\n clocksNow,\n callMonitored,\n createHandlingStack,\n sanitize,\n createIdentityEncoder,\n CustomerDataCompressionStatus,\n createCustomerDataTrackerManager,\n storeContextManager,\n displayAlreadyInitializedError,\n createTrackingConsentState,\n timeStampToClocks,\n} from '@datadog/browser-core'\nimport type { LifeCycle } from '../domain/lifeCycle'\nimport type { ViewHistory } from '../domain/contexts/viewHistory'\nimport type { RumSessionManager } from '../domain/rumSessionManager'\nimport type { ReplayStats } from '../rawRumEvent.types'\nimport { ActionType, VitalType } from '../rawRumEvent.types'\nimport type { RumConfiguration, RumInitConfiguration } from '../domain/configuration'\nimport type { ViewOptions } from '../domain/view/trackViews'\nimport { buildCommonContext } from '../domain/contexts/commonContext'\nimport type { InternalContext } from '../domain/contexts/internalContext'\nimport type { DurationVitalReference } from '../domain/vital/vitalCollection'\nimport { createCustomVitalsState } from '../domain/vital/vitalCollection'\nimport { callPluginsMethod } from '../domain/plugins'\nimport { createPreStartStrategy } from './preStartRum'\nimport type { StartRum, StartRumResult } from './startRum'\n\nexport interface StartRecordingOptions {\n force: boolean\n}\nexport interface RumPublicApi extends PublicApi {\n /**\n * Init the RUM browser SDK.\n * @param initConfiguration Configuration options of the SDK\n *\n * See [RUM Browser Monitoring Setup](https://docs.datadoghq.com/real_user_monitoring/browser) for further information.\n */\n init: (initConfiguration: RumInitConfiguration) => void\n\n /**\n * Set the tracking consent of the current user.\n *\n * @param {\"granted\" | \"not-granted\"} trackingConsent The user tracking consent\n *\n * Data will be sent only if it is set to \"granted\". This value won't be stored by the library\n * across page loads: you will need to call this method or set the appropriate `trackingConsent`\n * field in the init() method at each page load.\n *\n * If this method is called before the init() method, the provided value will take precedence\n * over the one provided as initialization parameter.\n *\n * See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-tracking-consent) for further information.\n */\n setTrackingConsent: (trackingConsent: TrackingConsent) => void\n\n /**\n * Set View Name.\n *\n * Enable to manually change the name of the current view.\n * @param name name of the view\n * See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#override-default-rum-view-names) for further information.\n */\n setViewName: (name: string) => void\n\n /**\n * Set View Context.\n *\n * Enable to manually set the context of the current view.\n * @param context context of the view\n */\n setViewContext: (context: Context) => void\n /**\n * Set View Context Property.\n *\n * Enable to manually set a property of the context of the current view.\n * @param key key of the property\n * @param value value of the property\n */\n setViewContextProperty: (key: string, value: any) => void\n\n /**\n * Get View Context.\n */\n getViewContext: () => Context\n\n /**\n * Set the global context information to all events, stored in `@context`\n *\n * @param context Global context\n *\n * See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.\n */\n setGlobalContext: (context: any) => void\n\n /**\n * Get the global Context\n *\n * See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.\n */\n getGlobalContext: () => Context\n\n /**\n * Set or update a global context property, stored in `@context.`\n *\n * @param key Key of the property\n * @param property Value of the property\n *\n * See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.\n */\n setGlobalContextProperty: (key: any, value: any) => void\n\n /**\n * Remove a global context property\n *\n * See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.\n */\n removeGlobalContextProperty: (key: any) => void\n\n /**\n * Clear the global context\n *\n * See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.\n */\n clearGlobalContext: () => void\n\n /**\n * [Internal API] Get the internal SDK context\n */\n getInternalContext: (startTime?: number) => InternalContext | undefined\n\n /**\n * Get the init configuration\n */\n getInitConfiguration: () => RumInitConfiguration | undefined\n\n /**\n * Add a custom action, stored in `@action`\n * @param name Name of the action\n * @param context Context of the action\n *\n * See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.\n */\n addAction: (name: string, context?: object) => void\n\n /**\n * Add a custom error, stored in `@error`.\n * @param error Error. Favor sending a [Javascript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) to have a stack trace attached to the error event.\n * @param context Context of the error\n *\n * See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.\n */\n addError: (error: unknown, context?: object) => void\n\n /**\n * Add a custom timing relative to the start of the current view,\n * stored in `@view.custom_timings.`\n *\n * @param name Name of the custom timing\n * @param [time] Epoch timestamp of the custom timing (if not set, will use current time)\n *\n * Note: passing a relative time is discouraged since it is actually used as-is but displayed relative to the view start.\n * We currently don't provide a way to retrieve the view start time, so it can be challenging to provide a timing relative to the view start.\n * see https://github.com/DataDog/browser-sdk/issues/2552\n */\n addTiming: (name: string, time?: number) => void\n\n /**\n * Set user information to all events, stored in `@usr`\n *\n * See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.\n */\n setUser(newUser: User & { id: string }): void\n\n /**\n * Set user information to all events, stored in `@usr`\n *\n * @deprecated You must specify a user id\n * @see {@link setUser}\n */\n setUser(newUser: User): void\n /**\n * Get user information\n *\n * See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.\n */\n getUser: () => Context\n\n /**\n * Set or update the user property, stored in `@usr.`\n *\n * @param key Key of the property\n * @param property Value of the property\n *\n * See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.\n */\n setUserProperty: (key: any, property: any) => void\n\n /**\n * Remove a user property\n *\n * See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.\n */\n removeUserProperty: (key: any) => void\n\n /**\n * Clear all user information\n *\n * See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.\n */\n clearUser: () => void\n\n /**\n * Set account information to all events, stored in `@account`\n */\n setAccount: (newAccount: Account) => void\n\n /**\n * Get account information\n */\n getAccount: () => Context\n\n /**\n * Set or update the account property, stored in `@account.`\n *\n * @param key Key of the property\n * @param property Value of the property\n */\n setAccountProperty: (key: string, property: any) => void\n\n /**\n * Remove an account property\n */\n removeAccountProperty: (key: string) => void\n\n /**\n * Clear all account information\n */\n clearAccount: () => void\n\n /**\n * Start a view manually.\n * Enable to manual start a view, use `trackViewsManually: true` init parameter and call `startView()` to create RUM views and be aligned with how you\u2019ve defined them in your SPA application routing.\n *\n * @param options.name name of the view\n * @param options.service service of the view\n * @param options.version version of the view\n *\n * See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#override-default-rum-view-names) for further information.\n */\n startView: {\n (name?: string): void\n (options: ViewOptions): void\n }\n\n /**\n * Stop the session. A new session will start at the next user interaction with the page.\n */\n stopSession: () => void\n\n /**\n * Add a feature flag evaluation,\n * stored in `@feature_flags.`\n *\n * @param {string} key The key of the feature flag.\n * @param {any} value The value of the feature flag.\n *\n * We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.\n *\n * See [Feature Flag Tracking](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/) for further information.\n */\n addFeatureFlagEvaluation: (key: string, value: any) => void\n\n /**\n * Get the Session Replay Link.\n *\n * See [Connect Session Replay To Your Third-Party Tools](https://docs.datadoghq.com/real_user_monitoring/guide/connect-session-replay-to-your-third-party-tools) for further information.\n */\n getSessionReplayLink: () => string | undefined\n\n /**\n * Start Session Replay recording.\n * Enable to conditionally start the recording, use the `startSessionReplayRecordingManually:true` init parameter and call `startSessionReplayRecording()`\n *\n * See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.\n */\n startSessionReplayRecording: (options?: StartRecordingOptions) => void\n\n /**\n * Stop Session Replay recording.\n *\n * See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.\n */\n stopSessionReplayRecording: () => void\n\n /**\n * Add a custom duration vital\n *\n * @param name name of the custom vital\n * @param options.startTime epoch timestamp of the start of the custom vital\n * @param options.duration duration of the custom vital in millisecond\n * @param options.context custom context attached to the vital\n * @param options.description Description of the vital\n */\n addDurationVital: (\n name: string,\n options: { startTime: number; duration: number; context?: object; description?: string }\n ) => void\n\n /**\n * Start a custom duration vital.\n *\n * If you plan to have multiple durations for the same vital, you should use the reference returned by this method.\n *\n * @param name name of the custom vital\n * @param options.context custom context attached to the vital\n * @param options.description Description of the vital\n * @returns reference to the custom vital\n */\n startDurationVital: (name: string, options?: { context?: object; description?: string }) => DurationVitalReference\n\n /**\n * Stop a custom duration vital\n *\n * @param nameOrRef name of the custom vital or the reference to it\n * @param options.context custom context attached to the vital\n * @param options.description Description of the vital\n */\n stopDurationVital: (\n nameOrRef: string | DurationVitalReference,\n options?: { context?: object; description?: string }\n ) => void\n}\n\nexport interface RecorderApi {\n start: (options?: StartRecordingOptions) => void\n stop: () => void\n onRumStart: (\n lifeCycle: LifeCycle,\n configuration: RumConfiguration,\n sessionManager: RumSessionManager,\n viewHistory: ViewHistory,\n deflateWorker: DeflateWorker | undefined\n ) => void\n isRecording: () => boolean\n getReplayStats: (viewId: string) => ReplayStats | undefined\n getSessionReplayLink: () => string | undefined\n}\n\nexport interface ProfilerApi {\n stop: () => void\n onRumStart: (\n lifeCycle: LifeCycle,\n configuration: RumConfiguration,\n sessionManager: RumSessionManager,\n viewHistory: ViewHistory\n ) => void\n}\n\nexport interface RumPublicApiOptions {\n ignoreInitIfSyntheticsWillInjectRum?: boolean\n startDeflateWorker?: (\n configuration: RumConfiguration,\n source: string,\n onInitializationFailure: () => void\n ) => DeflateWorker | undefined\n createDeflateEncoder?: (\n configuration: RumConfiguration,\n worker: DeflateWorker,\n streamId: DeflateEncoderStreamId\n ) => DeflateEncoder\n}\n\nconst RUM_STORAGE_KEY = 'rum'\n\nexport interface Strategy {\n init: (initConfiguration: RumInitConfiguration, publicApi: RumPublicApi) => void\n initConfiguration: RumInitConfiguration | undefined\n getInternalContext: StartRumResult['getInternalContext']\n stopSession: StartRumResult['stopSession']\n addTiming: StartRumResult['addTiming']\n startView: StartRumResult['startView']\n setViewName: StartRumResult['setViewName']\n setViewContext: StartRumResult['setViewContext']\n setViewContextProperty: StartRumResult['setViewContextProperty']\n getViewContext: StartRumResult['getViewContext']\n addAction: StartRumResult['addAction']\n addError: StartRumResult['addError']\n addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation']\n startDurationVital: StartRumResult['startDurationVital']\n stopDurationVital: StartRumResult['stopDurationVital']\n addDurationVital: StartRumResult['addDurationVital']\n}\n\nexport function makeRumPublicApi(\n startRumImpl: StartRum,\n recorderApi: RecorderApi,\n profilerApi: ProfilerApi,\n options: RumPublicApiOptions = {}\n): RumPublicApi {\n const customerDataTrackerManager = createCustomerDataTrackerManager(CustomerDataCompressionStatus.Unknown)\n const globalContextManager = createContextManager('global context', {\n customerDataTracker: customerDataTrackerManager.getOrCreateTracker(CustomerDataType.GlobalContext),\n })\n const userContextManager = createContextManager('user', {\n customerDataTracker: customerDataTrackerManager.getOrCreateTracker(CustomerDataType.User),\n propertiesConfig: {\n id: { type: 'string' },\n name: { type: 'string' },\n email: { type: 'string' },\n },\n })\n const accountContextManager = createContextManager('account', {\n customerDataTracker: customerDataTrackerManager.getOrCreateTracker(CustomerDataType.Account),\n propertiesConfig: {\n id: { type: 'string', required: true },\n name: { type: 'string' },\n },\n })\n const trackingConsentState = createTrackingConsentState()\n const customVitalsState = createCustomVitalsState()\n\n function getCommonContext() {\n return buildCommonContext(globalContextManager, userContextManager, accountContextManager, recorderApi)\n }\n\n let strategy = createPreStartStrategy(\n options,\n getCommonContext,\n trackingConsentState,\n customVitalsState,\n (configuration, deflateWorker, initialViewOptions) => {\n if (configuration.storeContextsAcrossPages) {\n storeContextManager(configuration, globalContextManager, RUM_STORAGE_KEY, CustomerDataType.GlobalContext)\n storeContextManager(configuration, userContextManager, RUM_STORAGE_KEY, CustomerDataType.User)\n storeContextManager(configuration, accountContextManager, RUM_STORAGE_KEY, CustomerDataType.Account)\n }\n\n customerDataTrackerManager.setCompressionStatus(\n deflateWorker ? CustomerDataCompressionStatus.Enabled : CustomerDataCompressionStatus.Disabled\n )\n\n const startRumResult = startRumImpl(\n configuration,\n recorderApi,\n profilerApi,\n customerDataTrackerManager,\n getCommonContext,\n initialViewOptions,\n deflateWorker && options.createDeflateEncoder\n ? (streamId) => options.createDeflateEncoder!(configuration, deflateWorker, streamId)\n : createIdentityEncoder,\n trackingConsentState,\n customVitalsState\n )\n\n recorderApi.onRumStart(\n startRumResult.lifeCycle,\n configuration,\n startRumResult.session,\n startRumResult.viewHistory,\n deflateWorker\n )\n\n profilerApi.onRumStart(\n startRumResult.lifeCycle,\n configuration,\n startRumResult.session,\n startRumResult.viewHistory\n )\n\n strategy = createPostStartStrategy(strategy, startRumResult)\n\n callPluginsMethod(configuration.plugins, 'onRumStart', { strategy })\n\n return startRumResult\n }\n )\n\n const startView: {\n (name?: string): void\n (options: ViewOptions): void\n } = monitor((options?: string | ViewOptions) => {\n const sanitizedOptions = typeof options === 'object' ? options : { name: options }\n if (sanitizedOptions.context) {\n customerDataTrackerManager.getOrCreateTracker(CustomerDataType.View).updateCustomerData(sanitizedOptions.context)\n }\n strategy.startView(sanitizedOptions)\n addTelemetryUsage({ feature: 'start-view' })\n })\n\n const rumPublicApi: RumPublicApi = makePublicApi({\n init: monitor((initConfiguration) => {\n strategy.init(initConfiguration, rumPublicApi)\n }),\n\n setTrackingConsent: monitor((trackingConsent) => {\n trackingConsentState.update(trackingConsent)\n addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: trackingConsent })\n }),\n\n setViewName: monitor((name: string) => {\n strategy.setViewName(name)\n addTelemetryUsage({ feature: 'set-view-name' })\n }),\n\n setViewContext: monitor((context: Context) => {\n strategy.setViewContext(context)\n addTelemetryUsage({ feature: 'set-view-context' })\n }),\n\n setViewContextProperty: monitor((key: string, value: any) => {\n strategy.setViewContextProperty(key, value)\n addTelemetryUsage({ feature: 'set-view-context-property' })\n }),\n\n getViewContext: monitor(() => {\n addTelemetryUsage({ feature: 'set-view-context-property' })\n return strategy.getViewContext()\n }),\n\n setGlobalContext: monitor((context) => {\n globalContextManager.setContext(context)\n addTelemetryUsage({ feature: 'set-global-context' })\n }),\n\n getGlobalContext: monitor(() => globalContextManager.getContext()),\n\n setGlobalContextProperty: monitor((key, value) => {\n globalContextManager.setContextProperty(key, value)\n addTelemetryUsage({ feature: 'set-global-context' })\n }),\n\n removeGlobalContextProperty: monitor((key) => globalContextManager.removeContextProperty(key)),\n\n clearGlobalContext: monitor(() => globalContextManager.clearContext()),\n\n getInternalContext: monitor((startTime) => strategy.getInternalContext(startTime)),\n\n getInitConfiguration: monitor(() => deepClone(strategy.initConfiguration)),\n\n addAction: (name, context) => {\n const handlingStack = createHandlingStack('action')\n\n callMonitored(() => {\n strategy.addAction({\n name: sanitize(name)!,\n context: sanitize(context) as Context,\n startClocks: clocksNow(),\n type: ActionType.CUSTOM,\n handlingStack,\n })\n addTelemetryUsage({ feature: 'add-action' })\n })\n },\n\n addError: (error, context) => {\n const handlingStack = createHandlingStack('error')\n callMonitored(() => {\n strategy.addError({\n error, // Do not sanitize error here, it is needed unserialized by computeRawError()\n handlingStack,\n context: sanitize(context) as Context,\n startClocks: clocksNow(),\n })\n addTelemetryUsage({ feature: 'add-error' })\n })\n },\n\n addTiming: monitor((name, time) => {\n // TODO: next major decide to drop relative time support or update its behaviour\n strategy.addTiming(sanitize(name)!, time as RelativeTime | TimeStamp | undefined)\n }),\n\n setUser: monitor((newUser) => {\n userContextManager.setContext(newUser)\n addTelemetryUsage({ feature: 'set-user' })\n }),\n\n getUser: monitor(userContextManager.getContext),\n\n setUserProperty: monitor((key, property) => {\n userContextManager.setContextProperty(key, property)\n addTelemetryUsage({ feature: 'set-user' })\n }),\n\n removeUserProperty: monitor(userContextManager.removeContextProperty),\n\n clearUser: monitor(userContextManager.clearContext),\n\n setAccount: monitor(accountContextManager.setContext),\n\n getAccount: monitor(accountContextManager.getContext),\n\n setAccountProperty: monitor(accountContextManager.setContextProperty),\n\n removeAccountProperty: monitor(accountContextManager.removeContextProperty),\n\n clearAccount: monitor(accountContextManager.clearContext),\n\n startView,\n\n stopSession: monitor(() => {\n strategy.stopSession()\n addTelemetryUsage({ feature: 'stop-session' })\n }),\n\n addFeatureFlagEvaluation: monitor((key, value) => {\n strategy.addFeatureFlagEvaluation(sanitize(key)!, sanitize(value))\n addTelemetryUsage({ feature: 'add-feature-flag-evaluation' })\n }),\n\n getSessionReplayLink: monitor(() => recorderApi.getSessionReplayLink()),\n startSessionReplayRecording: monitor((options?: StartRecordingOptions) => {\n recorderApi.start(options)\n addTelemetryUsage({ feature: 'start-session-replay-recording', force: options && options.force })\n }),\n\n stopSessionReplayRecording: monitor(() => recorderApi.stop()),\n\n addDurationVital: monitor((name, options) => {\n addTelemetryUsage({ feature: 'add-duration-vital' })\n strategy.addDurationVital({\n name: sanitize(name)!,\n type: VitalType.DURATION,\n startClocks: timeStampToClocks(options.startTime as TimeStamp),\n duration: options.duration as Duration,\n context: sanitize(options && options.context) as Context,\n description: sanitize(options && options.description) as string | undefined,\n })\n }),\n\n startDurationVital: monitor((name, options) => {\n addTelemetryUsage({ feature: 'start-duration-vital' })\n return strategy.startDurationVital(sanitize(name)!, {\n context: sanitize(options && options.context) as Context,\n description: sanitize(options && options.description) as string | undefined,\n })\n }),\n\n stopDurationVital: monitor((nameOrRef, options) => {\n addTelemetryUsage({ feature: 'stop-duration-vital' })\n strategy.stopDurationVital(typeof nameOrRef === 'string' ? sanitize(nameOrRef)! : nameOrRef, {\n context: sanitize(options && options.context) as Context,\n description: sanitize(options && options.description) as string | undefined,\n })\n }),\n })\n\n return rumPublicApi\n}\n\nfunction createPostStartStrategy(preStartStrategy: Strategy, startRumResult: StartRumResult): Strategy {\n return {\n init: (initConfiguration: RumInitConfiguration) => {\n displayAlreadyInitializedError('DD_RUM', initConfiguration)\n },\n initConfiguration: preStartStrategy.initConfiguration,\n ...startRumResult,\n }\n}\n", "import { monitor, noop, Observable, getZoneJsOriginalValue } from '@datadog/browser-core'\n\nexport function createDOMMutationObservable() {\n const MutationObserver = getMutationObserverConstructor()\n\n return new Observable((observable) => {\n if (!MutationObserver) {\n return\n }\n const observer = new MutationObserver(monitor(() => observable.notify()))\n observer.observe(document, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n })\n return () => observer.disconnect()\n })\n}\n\ntype MutationObserverConstructor = new (callback: MutationCallback) => MutationObserver\n\nexport interface BrowserWindow extends Window {\n MutationObserver?: MutationObserverConstructor\n Zone?: unknown\n}\n\nexport function getMutationObserverConstructor(): MutationObserverConstructor | undefined {\n let constructor: MutationObserverConstructor | undefined\n const browserWindow: BrowserWindow = window\n\n // Angular uses Zone.js to provide a context persisting across async tasks. Zone.js replaces the\n // global MutationObserver constructor with a patched version to support the context propagation.\n // There is an ongoing issue[1][2] with this setup when using a MutationObserver within a Angular\n // component: on some occasions, the callback is being called in an infinite loop, causing the\n // page to freeze (even if the callback is completely empty).\n //\n // To work around this issue, we try to get the original MutationObserver constructor stored by\n // Zone.js.\n //\n // [1] https://github.com/angular/angular/issues/26948\n // [2] https://github.com/angular/angular/issues/31712\n if (browserWindow.Zone) {\n // Zone.js 0.8.6+ is storing original class constructors into the browser 'window' object[3].\n //\n // [3] https://github.com/angular/angular/blob/6375fa79875c0fe7b815efc45940a6e6f5c9c9eb/packages/zone.js/lib/common/utils.ts#L288\n constructor = getZoneJsOriginalValue(browserWindow, 'MutationObserver')\n\n if (browserWindow.MutationObserver && constructor === browserWindow.MutationObserver) {\n // Anterior Zone.js versions (used in Angular 2) does not expose the original MutationObserver\n // in the 'window' object. Luckily, the patched MutationObserver class is storing an original\n // instance in its properties[4]. Let's get the original MutationObserver constructor from\n // there.\n //\n // [4] https://github.com/angular/zone.js/blob/v0.8.5/lib/common/utils.ts#L412\n\n const patchedInstance = new browserWindow.MutationObserver(noop) as {\n originalInstance?: { constructor: MutationObserverConstructor }\n }\n\n const originalInstance = getZoneJsOriginalValue(patchedInstance, 'originalInstance')\n constructor = originalInstance && originalInstance.constructor\n }\n }\n\n if (!constructor) {\n constructor = browserWindow.MutationObserver\n }\n\n return constructor\n}\n", "import { instrumentMethod, Observable } from '@datadog/browser-core'\n\nexport function createWindowOpenObservable() {\n const observable = new Observable()\n const { stop } = instrumentMethod(window, 'open', () => observable.notify())\n return { observable, stop }\n}\n", "import { sanitize, deepClone, getType, objectEntries } from '@datadog/browser-core'\nimport type { Context } from '@datadog/browser-core'\n\nexport type ModifiableFieldPaths = Record\n\n/**\n * Allows declaring and enforcing modifications to specific fields of an object.\n * Only supports modifying properties of an object (even if nested in an array).\n * Does not support array manipulation (adding/removing items).\n */\nexport function limitModification(\n object: T,\n modifiableFieldPaths: ModifiableFieldPaths,\n modifier: (object: T) => Result\n): Result | undefined {\n const clone = deepClone(object)\n const result = modifier(clone)\n\n objectEntries(modifiableFieldPaths).forEach(([fieldPath, fieldType]) =>\n // Traverse both object and clone simultaneously up to the path and apply the modification from the clone to the original object when the type is valid\n setValueAtPath(object, clone, fieldPath.split(/\\.|(?=\\[\\])/), fieldType)\n )\n\n return result\n}\n\nfunction setValueAtPath(object: unknown, clone: unknown, pathSegments: string[], fieldType: 'string' | 'object') {\n const [field, ...restPathSegments] = pathSegments\n\n if (field === '[]') {\n if (Array.isArray(object) && Array.isArray(clone)) {\n object.forEach((item, i) => setValueAtPath(item, clone[i], restPathSegments, fieldType))\n }\n\n return\n }\n\n if (!isValidObject(object) || !isValidObject(clone)) {\n return\n }\n\n if (restPathSegments.length > 0) {\n return setValueAtPath(object[field], clone[field], restPathSegments, fieldType)\n }\n\n setNestedValue(object, field, clone[field], fieldType)\n}\n\nfunction setNestedValue(\n object: Record,\n field: string,\n value: unknown,\n fieldType: 'string' | 'object'\n) {\n const newType = getType(value)\n\n if (newType === fieldType) {\n object[field] = sanitize(value)\n } else if (fieldType === 'object' && (newType === 'undefined' || newType === 'null')) {\n object[field] = {}\n }\n}\n\nfunction isValidObject(object: unknown): object is Record {\n return getType(object) === 'object'\n}\n", "import type { Context, RawError, EventRateLimiter, User, Account } from '@datadog/browser-core'\nimport {\n combine,\n isEmptyObject,\n timeStampNow,\n currentDrift,\n display,\n createEventRateLimiter,\n canUseEventBridge,\n round,\n isExperimentalFeatureEnabled,\n ExperimentalFeature,\n getConnectivity,\n addTelemetryDebug,\n} from '@datadog/browser-core'\nimport type { RumEventDomainContext } from '../domainContext.types'\nimport { RumEventType } from '../rawRumEvent.types'\nimport type { CommonProperties, RumEvent } from '../rumEvent.types'\nimport type { Hooks } from '../hooks'\nimport { HookNames } from '../hooks'\nimport type { LifeCycle } from './lifeCycle'\nimport { LifeCycleEventType } from './lifeCycle'\nimport type { ViewHistory } from './contexts/viewHistory'\nimport { SessionReplayState, SessionType, type RumSessionManager } from './rumSessionManager'\nimport type { RumConfiguration } from './configuration'\nimport type { DisplayContext } from './contexts/displayContext'\nimport type { CommonContext } from './contexts/commonContext'\nimport type { ModifiableFieldPaths } from './limitModification'\nimport { limitModification } from './limitModification'\nimport type { UrlContexts } from './contexts/urlContexts'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\n\nconst VIEW_MODIFIABLE_FIELD_PATHS: ModifiableFieldPaths = {\n 'view.name': 'string',\n 'view.url': 'string',\n 'view.referrer': 'string',\n}\n\nconst USER_CUSTOMIZABLE_FIELD_PATHS: ModifiableFieldPaths = {\n context: 'object',\n}\n\nconst ROOT_MODIFIABLE_FIELD_PATHS: ModifiableFieldPaths = {\n service: 'string',\n version: 'string',\n}\n\nlet modifiableFieldPathsByEvent: { [key in RumEventType]: ModifiableFieldPaths }\n\ntype Mutable = { -readonly [P in keyof T]: T[P] }\n\nexport function startRumAssembly(\n configuration: RumConfiguration,\n lifeCycle: LifeCycle,\n hooks: Hooks,\n sessionManager: RumSessionManager,\n viewHistory: ViewHistory,\n urlContexts: UrlContexts,\n displayContext: DisplayContext,\n getCommonContext: () => CommonContext,\n reportError: (error: RawError) => void\n) {\n modifiableFieldPathsByEvent = {\n [RumEventType.VIEW]: {\n 'view.performance.lcp.resource_url': 'string',\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n },\n [RumEventType.ERROR]: {\n 'error.message': 'string',\n 'error.stack': 'string',\n 'error.resource.url': 'string',\n 'error.fingerprint': 'string',\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n ...ROOT_MODIFIABLE_FIELD_PATHS,\n },\n [RumEventType.RESOURCE]: {\n 'resource.url': 'string',\n ...(isExperimentalFeatureEnabled(ExperimentalFeature.WRITABLE_RESOURCE_GRAPHQL)\n ? { 'resource.graphql': 'object' }\n : {}),\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n ...ROOT_MODIFIABLE_FIELD_PATHS,\n },\n [RumEventType.ACTION]: {\n 'action.target.name': 'string',\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n ...ROOT_MODIFIABLE_FIELD_PATHS,\n },\n [RumEventType.LONG_TASK]: {\n 'long_task.scripts[].source_url': 'string',\n 'long_task.scripts[].invoker': 'string',\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n },\n [RumEventType.VITAL]: {\n ...USER_CUSTOMIZABLE_FIELD_PATHS,\n ...VIEW_MODIFIABLE_FIELD_PATHS,\n },\n }\n const eventRateLimiters = {\n [RumEventType.ERROR]: createEventRateLimiter(\n RumEventType.ERROR,\n configuration.eventRateLimiterThreshold,\n reportError\n ),\n [RumEventType.ACTION]: createEventRateLimiter(\n RumEventType.ACTION,\n configuration.eventRateLimiterThreshold,\n reportError\n ),\n [RumEventType.VITAL]: createEventRateLimiter(\n RumEventType.VITAL,\n configuration.eventRateLimiterThreshold,\n reportError\n ),\n }\n\n lifeCycle.subscribe(\n LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,\n ({ startTime, duration, rawRumEvent, domainContext, savedCommonContext, customerContext }) => {\n const viewHistoryEntry = viewHistory.findView(startTime)\n const urlContext = urlContexts.findUrl(startTime)\n const session = sessionManager.findTrackedSession(startTime)\n\n if (\n session &&\n viewHistoryEntry &&\n !urlContext &&\n isExperimentalFeatureEnabled(ExperimentalFeature.MISSING_URL_CONTEXT_TELEMETRY)\n ) {\n addTelemetryDebug('Missing URL entry', {\n debug: {\n eventType: rawRumEvent.type,\n startTime,\n urlEntries: urlContexts.getAllEntries(),\n urlDeletedEntries: urlContexts.getDeletedEntries(),\n viewEntries: viewHistory.getAllEntries(),\n viewDeletedEntries: viewHistory.getDeletedEntries(),\n },\n })\n }\n\n if (session && viewHistoryEntry && urlContext) {\n const commonContext = savedCommonContext || getCommonContext()\n\n const rumContext: Partial = {\n _dd: {\n format_version: 2,\n drift: currentDrift(),\n configuration: {\n session_sample_rate: round(configuration.sessionSampleRate, 3),\n session_replay_sample_rate: round(configuration.sessionReplaySampleRate, 3),\n },\n browser_sdk_version: canUseEventBridge() ? __BUILD_ENV__SDK_VERSION__ : undefined,\n },\n application: {\n id: configuration.applicationId,\n },\n date: timeStampNow(),\n source: 'browser',\n session: {\n id: session.id,\n type: SessionType.USER,\n },\n display: displayContext.get(),\n connectivity: getConnectivity(),\n context: commonContext.context,\n }\n\n const serverRumEvent = combine(\n rumContext,\n hooks.triggerHook(HookNames.Assemble, {\n eventType: rawRumEvent.type,\n startTime,\n duration,\n }) as RumEvent & Context,\n { context: customerContext },\n rawRumEvent\n ) as RumEvent & Context\n\n if (!('has_replay' in serverRumEvent.session)) {\n ;(serverRumEvent.session as Mutable).has_replay = commonContext.hasReplay\n }\n if (serverRumEvent.type === 'view') {\n ;(serverRumEvent.session as Mutable).sampled_for_replay =\n session.sessionReplay === SessionReplayState.SAMPLED\n }\n\n if (session.anonymousId && !commonContext.user.anonymous_id && !!configuration.trackAnonymousUser) {\n commonContext.user.anonymous_id = session.anonymousId\n }\n if (!isEmptyObject(commonContext.user)) {\n ;(serverRumEvent.usr as Mutable) = commonContext.user as User & Context\n }\n\n if (!isEmptyObject(commonContext.account) && commonContext.account.id) {\n ;(serverRumEvent.account as Mutable) = commonContext.account as Account\n }\n\n if (shouldSend(serverRumEvent, configuration.beforeSend, domainContext, eventRateLimiters)) {\n if (isEmptyObject(serverRumEvent.context!)) {\n delete serverRumEvent.context\n }\n lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent)\n }\n }\n }\n )\n}\n\nfunction shouldSend(\n event: RumEvent & Context,\n beforeSend: RumConfiguration['beforeSend'],\n domainContext: RumEventDomainContext,\n eventRateLimiters: { [key in RumEventType]?: EventRateLimiter }\n) {\n if (beforeSend) {\n const result = limitModification(event, modifiableFieldPathsByEvent[event.type], (event) =>\n beforeSend(event, domainContext)\n )\n if (result === false && event.type !== RumEventType.VIEW) {\n return false\n }\n if (result === false) {\n display.warn(\"Can't dismiss view events using beforeSend!\")\n }\n }\n\n const rateLimitReached = eventRateLimiters[event.type]?.isLimitReached()\n\n return !rateLimitReached\n}\n", "import type { RelativeTime } from '@datadog/browser-core'\nimport type { ActionContexts } from '../action/actionCollection'\nimport type { RumSessionManager } from '../rumSessionManager'\nimport type { ViewHistory } from './viewHistory'\nimport type { UrlContexts } from './urlContexts'\n\nexport interface InternalContext {\n application_id: string\n session_id: string | undefined\n view?: {\n id: string\n url: string\n referrer: string\n name?: string\n }\n user_action?: {\n id: string | string[]\n }\n}\n\n/**\n * Internal context keep returning v1 format\n * to not break compatibility with logs data format\n */\nexport function startInternalContext(\n applicationId: string,\n sessionManager: RumSessionManager,\n viewHistory: ViewHistory,\n actionContexts: ActionContexts,\n urlContexts: UrlContexts\n) {\n return {\n get: (startTime?: number): InternalContext | undefined => {\n const viewContext = viewHistory.findView(startTime as RelativeTime)\n const urlContext = urlContexts.findUrl(startTime as RelativeTime)\n const session = sessionManager.findTrackedSession(startTime as RelativeTime)\n if (session && viewContext && urlContext) {\n const actionId = actionContexts.findActionId(startTime as RelativeTime)\n return {\n application_id: applicationId,\n session_id: session.id,\n user_action: actionId ? { id: actionId } : undefined,\n view: { id: viewContext.id, name: viewContext.name, referrer: urlContext.referrer, url: urlContext.url },\n }\n }\n },\n }\n}\n", "import type { Context, Duration, PageMayExitEvent, RawError, RelativeTime } from '@datadog/browser-core'\nimport { AbstractLifeCycle } from '@datadog/browser-core'\nimport type { RumEventDomainContext } from '../domainContext.types'\nimport type { RawRumEvent } from '../rawRumEvent.types'\nimport type { RumEvent } from '../rumEvent.types'\nimport type { CommonContext } from './contexts/commonContext'\nimport type { RequestCompleteEvent, RequestStartEvent } from './requestCollection'\nimport type { AutoAction } from './action/actionCollection'\nimport type { ViewEvent, ViewCreatedEvent, ViewEndedEvent, BeforeViewUpdateEvent } from './view/trackViews'\n\nexport const enum LifeCycleEventType {\n // Contexts (like viewHistory) should be opened using prefixed BEFORE_XXX events and closed using prefixed AFTER_XXX events\n // It ensures the context is available during the non prefixed event callbacks\n AUTO_ACTION_COMPLETED,\n BEFORE_VIEW_CREATED,\n VIEW_CREATED,\n BEFORE_VIEW_UPDATED,\n VIEW_UPDATED,\n VIEW_ENDED,\n AFTER_VIEW_ENDED,\n REQUEST_STARTED,\n REQUEST_COMPLETED,\n\n // The SESSION_EXPIRED lifecycle event has been introduced to represent when a session has expired\n // and trigger cleanup tasks related to this, prior to renewing the session. Its implementation is\n // slightly naive: it is not triggered as soon as the session is expired, but rather just before\n // notifying that the session is renewed. Thus, the session id is already set to the newly renewed\n // session.\n //\n // This implementation is \"good enough\" for our use-cases. Improving this is not trivial,\n // primarily because multiple instances of the SDK may be managing the same session cookie at\n // the same time, for example when using Logs and RUM on the same page, or opening multiple tabs\n // on the same domain.\n SESSION_EXPIRED,\n SESSION_RENEWED,\n PAGE_MAY_EXIT,\n RAW_RUM_EVENT_COLLECTED,\n RUM_EVENT_COLLECTED,\n RAW_ERROR_COLLECTED,\n}\n\n// This is a workaround for an issue occurring when the Browser SDK is included in a TypeScript\n// project configured with `isolatedModules: true`. Even if the const enum is declared in this\n// module, we cannot use it directly to define the EventMap interface keys (TS error: \"Cannot access\n// ambient const enums when the '--isolatedModules' flag is provided.\").\n//\n// Using a plain enum would fix the issue, but would also add 2KB to the minified bundle. By using\n// this workaround, we can keep using a const enum without impacting the bundle size (since it is a\n// \"declare\" statement, it will only be used during typecheck and completely ignored when building\n// JavaScript).\n//\n// See issues:\n// * https://github.com/DataDog/browser-sdk/issues/2208\n// * https://github.com/microsoft/TypeScript/issues/54152\ndeclare const LifeCycleEventTypeAsConst: {\n AUTO_ACTION_COMPLETED: LifeCycleEventType.AUTO_ACTION_COMPLETED\n BEFORE_VIEW_CREATED: LifeCycleEventType.BEFORE_VIEW_CREATED\n VIEW_CREATED: LifeCycleEventType.VIEW_CREATED\n BEFORE_VIEW_UPDATED: LifeCycleEventType.BEFORE_VIEW_UPDATED\n VIEW_UPDATED: LifeCycleEventType.VIEW_UPDATED\n VIEW_ENDED: LifeCycleEventType.VIEW_ENDED\n AFTER_VIEW_ENDED: LifeCycleEventType.AFTER_VIEW_ENDED\n REQUEST_STARTED: LifeCycleEventType.REQUEST_STARTED\n REQUEST_COMPLETED: LifeCycleEventType.REQUEST_COMPLETED\n SESSION_EXPIRED: LifeCycleEventType.SESSION_EXPIRED\n SESSION_RENEWED: LifeCycleEventType.SESSION_RENEWED\n PAGE_MAY_EXIT: LifeCycleEventType.PAGE_MAY_EXIT\n RAW_RUM_EVENT_COLLECTED: LifeCycleEventType.RAW_RUM_EVENT_COLLECTED\n RUM_EVENT_COLLECTED: LifeCycleEventType.RUM_EVENT_COLLECTED\n RAW_ERROR_COLLECTED: LifeCycleEventType.RAW_ERROR_COLLECTED\n}\n\n// Note: this interface needs to be exported even if it is not used outside of this module, else TS\n// fails to build the rum-core package with error TS4058\nexport interface LifeCycleEventMap {\n [LifeCycleEventTypeAsConst.AUTO_ACTION_COMPLETED]: AutoAction\n [LifeCycleEventTypeAsConst.BEFORE_VIEW_CREATED]: ViewCreatedEvent\n [LifeCycleEventTypeAsConst.VIEW_CREATED]: ViewCreatedEvent\n [LifeCycleEventTypeAsConst.BEFORE_VIEW_UPDATED]: BeforeViewUpdateEvent\n [LifeCycleEventTypeAsConst.VIEW_UPDATED]: ViewEvent\n [LifeCycleEventTypeAsConst.VIEW_ENDED]: ViewEndedEvent\n [LifeCycleEventTypeAsConst.AFTER_VIEW_ENDED]: ViewEndedEvent\n [LifeCycleEventTypeAsConst.REQUEST_STARTED]: RequestStartEvent\n [LifeCycleEventTypeAsConst.REQUEST_COMPLETED]: RequestCompleteEvent\n [LifeCycleEventTypeAsConst.SESSION_EXPIRED]: void\n [LifeCycleEventTypeAsConst.SESSION_RENEWED]: void\n [LifeCycleEventTypeAsConst.PAGE_MAY_EXIT]: PageMayExitEvent\n [LifeCycleEventTypeAsConst.RAW_RUM_EVENT_COLLECTED]: RawRumEventCollectedData\n [LifeCycleEventTypeAsConst.RUM_EVENT_COLLECTED]: RumEvent & Context\n [LifeCycleEventTypeAsConst.RAW_ERROR_COLLECTED]: {\n error: RawError\n savedCommonContext?: CommonContext\n customerContext?: Context\n }\n}\n\nexport interface RawRumEventCollectedData {\n startTime: RelativeTime\n duration?: Duration\n savedCommonContext?: CommonContext\n customerContext?: Context\n rawRumEvent: E\n domainContext: RumEventDomainContext\n}\n\nexport const LifeCycle = AbstractLifeCycle\nexport type LifeCycle = AbstractLifeCycle\n", "import type { RelativeTime, ClocksState, Context } from '@datadog/browser-core'\nimport { SESSION_TIME_OUT_DELAY, createValueHistory } from '@datadog/browser-core'\nimport type { LifeCycle } from '../lifeCycle'\nimport { LifeCycleEventType } from '../lifeCycle'\nimport type { BeforeViewUpdateEvent, ViewCreatedEvent } from '../view/trackViews'\n\nexport const VIEW_CONTEXT_TIME_OUT_DELAY = SESSION_TIME_OUT_DELAY\n\nexport interface ViewHistoryEntry {\n service?: string\n version?: string\n context?: Context | undefined\n id: string\n name?: string\n startClocks: ClocksState\n}\n\nexport interface ViewHistory {\n findView: (startTime?: RelativeTime) => ViewHistoryEntry | undefined\n stop: () => void\n getAllEntries: () => Context[]\n getDeletedEntries: () => RelativeTime[]\n}\n\nexport function startViewHistory(lifeCycle: LifeCycle): ViewHistory {\n const viewValueHistory = createValueHistory({ expireDelay: VIEW_CONTEXT_TIME_OUT_DELAY })\n\n lifeCycle.subscribe(LifeCycleEventType.BEFORE_VIEW_CREATED, (view) => {\n viewValueHistory.add(buildViewHistoryEntry(view), view.startClocks.relative)\n })\n\n lifeCycle.subscribe(LifeCycleEventType.AFTER_VIEW_ENDED, ({ endClocks }) => {\n viewValueHistory.closeActive(endClocks.relative)\n })\n\n lifeCycle.subscribe(LifeCycleEventType.BEFORE_VIEW_UPDATED, (viewUpdate: BeforeViewUpdateEvent) => {\n const currentView = viewValueHistory.find(viewUpdate.startClocks.relative)\n if (currentView && viewUpdate.name) {\n currentView.name = viewUpdate.name\n }\n if (currentView && viewUpdate.context) {\n currentView.context = viewUpdate.context\n }\n })\n\n lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, () => {\n viewValueHistory.reset()\n })\n\n function buildViewHistoryEntry(view: ViewCreatedEvent) {\n return {\n service: view.service,\n version: view.version,\n context: view.context,\n id: view.id,\n name: view.name,\n startClocks: view.startClocks,\n }\n }\n\n return {\n findView: (startTime) => viewValueHistory.find(startTime),\n getAllEntries: () => viewValueHistory.getAllEntries(),\n getDeletedEntries: () => viewValueHistory.getDeletedEntries(),\n stop: () => {\n viewValueHistory.stop()\n },\n }\n}\n", "import type { Duration, RelativeTime } from '@datadog/browser-core'\nimport {\n addTelemetryDebug,\n elapsed,\n getPathName,\n isValidUrl,\n ResourceType,\n toServerDuration,\n isIntakeUrl,\n} from '@datadog/browser-core'\n\nimport type { RumPerformanceResourceTiming } from '../../browser/performanceObservable'\n\nimport type { ResourceEntryDetailsElement, DeliveryType } from '../../rawRumEvent.types'\n\nexport interface ResourceEntryDetails {\n worker?: ResourceEntryDetailsElement\n redirect?: ResourceEntryDetailsElement\n dns?: ResourceEntryDetailsElement\n connect?: ResourceEntryDetailsElement\n ssl?: ResourceEntryDetailsElement\n first_byte?: ResourceEntryDetailsElement\n download?: ResourceEntryDetailsElement\n}\n\nexport const FAKE_INITIAL_DOCUMENT = 'initial_document'\n\nconst RESOURCE_TYPES: Array<[ResourceType, (initiatorType: string, path: string) => boolean]> = [\n [ResourceType.DOCUMENT, (initiatorType: string) => FAKE_INITIAL_DOCUMENT === initiatorType],\n [ResourceType.XHR, (initiatorType: string) => 'xmlhttprequest' === initiatorType],\n [ResourceType.FETCH, (initiatorType: string) => 'fetch' === initiatorType],\n [ResourceType.BEACON, (initiatorType: string) => 'beacon' === initiatorType],\n [ResourceType.CSS, (_: string, path: string) => /\\.css$/i.test(path)],\n [ResourceType.JS, (_: string, path: string) => /\\.js$/i.test(path)],\n [\n ResourceType.IMAGE,\n (initiatorType: string, path: string) =>\n ['image', 'img', 'icon'].includes(initiatorType) || /\\.(gif|jpg|jpeg|tiff|png|svg|ico)$/i.exec(path) !== null,\n ],\n [ResourceType.FONT, (_: string, path: string) => /\\.(woff|eot|woff2|ttf)$/i.exec(path) !== null],\n [\n ResourceType.MEDIA,\n (initiatorType: string, path: string) =>\n ['audio', 'video'].includes(initiatorType) || /\\.(mp3|mp4)$/i.exec(path) !== null,\n ],\n]\n\nexport function computeResourceEntryType(entry: RumPerformanceResourceTiming) {\n const url = entry.name\n if (!isValidUrl(url)) {\n addTelemetryDebug(`Failed to construct URL for \"${entry.name}\"`)\n return ResourceType.OTHER\n }\n const path = getPathName(url)\n for (const [type, isType] of RESOURCE_TYPES) {\n if (isType(entry.initiatorType, path)) {\n return type\n }\n }\n return ResourceType.OTHER\n}\n\nfunction areInOrder(...numbers: number[]) {\n for (let i = 1; i < numbers.length; i += 1) {\n if (numbers[i - 1] > numbers[i]) {\n return false\n }\n }\n return true\n}\n\nexport function isResourceEntryRequestType(entry: RumPerformanceResourceTiming) {\n return entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch'\n}\n\nexport function computeResourceEntryDuration(entry: RumPerformanceResourceTiming): Duration {\n const { duration, startTime, responseEnd } = entry\n\n // Safari duration is always 0 on timings blocked by cross origin policies.\n if (duration === 0 && startTime < responseEnd) {\n return elapsed(startTime, responseEnd)\n }\n\n return duration\n}\n\nexport function computeResourceEntryDetails(entry: RumPerformanceResourceTiming): ResourceEntryDetails | undefined {\n if (!hasValidResourceEntryTimings(entry)) {\n return undefined\n }\n const {\n startTime,\n fetchStart,\n workerStart,\n redirectStart,\n redirectEnd,\n domainLookupStart,\n domainLookupEnd,\n connectStart,\n secureConnectionStart,\n connectEnd,\n requestStart,\n responseStart,\n responseEnd,\n } = entry\n\n const details: ResourceEntryDetails = {\n download: formatTiming(startTime, responseStart, responseEnd),\n first_byte: formatTiming(startTime, requestStart, responseStart),\n }\n\n // Make sure a worker processing time is recorded\n if (0 < workerStart && workerStart < fetchStart) {\n details.worker = formatTiming(startTime, workerStart, fetchStart)\n }\n\n // Make sure a connection occurred\n if (fetchStart < connectEnd) {\n details.connect = formatTiming(startTime, connectStart, connectEnd)\n\n // Make sure a secure connection occurred\n if (connectStart <= secureConnectionStart && secureConnectionStart <= connectEnd) {\n details.ssl = formatTiming(startTime, secureConnectionStart, connectEnd)\n }\n }\n\n // Make sure a domain lookup occurred\n if (fetchStart < domainLookupEnd) {\n details.dns = formatTiming(startTime, domainLookupStart, domainLookupEnd)\n }\n\n // Make sure a redirection occurred\n if (startTime < redirectEnd) {\n details.redirect = formatTiming(startTime, redirectStart, redirectEnd)\n }\n\n return details\n}\n\n/**\n * Entries with negative duration are unexpected and should be dismissed. The intake will ignore RUM\n * Resource events with negative durations anyway.\n * Since Chromium 128, more entries have unexpected negative durations, see\n * https://issues.chromium.org/issues/363031537\n */\nexport function hasValidResourceEntryDuration(entry: RumPerformanceResourceTiming) {\n return entry.duration >= 0\n}\n\nexport function hasValidResourceEntryTimings(entry: RumPerformanceResourceTiming) {\n // Ensure timings are in the right order. On top of filtering out potential invalid\n // RumPerformanceResourceTiming, it will ignore entries from requests where timings cannot be\n // collected, for example cross origin requests without a \"Timing-Allow-Origin\" header allowing\n // it.\n const areCommonTimingsInOrder = areInOrder(\n entry.startTime,\n entry.fetchStart,\n entry.domainLookupStart,\n entry.domainLookupEnd,\n entry.connectStart,\n entry.connectEnd,\n entry.requestStart,\n entry.responseStart,\n entry.responseEnd\n )\n\n const areRedirectionTimingsInOrder = hasRedirection(entry)\n ? areInOrder(entry.startTime, entry.redirectStart, entry.redirectEnd, entry.fetchStart)\n : true\n\n return areCommonTimingsInOrder && areRedirectionTimingsInOrder\n}\n\nfunction hasRedirection(entry: RumPerformanceResourceTiming) {\n return entry.redirectEnd > entry.startTime\n}\nfunction formatTiming(origin: RelativeTime, start: RelativeTime, end: RelativeTime) {\n if (origin <= start && start <= end) {\n return {\n duration: toServerDuration(elapsed(start, end)),\n start: toServerDuration(elapsed(origin, start)),\n }\n }\n}\n\n/**\n * The 'nextHopProtocol' is an empty string for cross-origin resources without CORS headers,\n * meaning the protocol is unknown, and we shouldn't report it.\n * https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/nextHopProtocol#cross-origin_resources\n */\nexport function computeResourceEntryProtocol(entry: RumPerformanceResourceTiming) {\n return entry.nextHopProtocol === '' ? undefined : entry.nextHopProtocol\n}\n\n/**\n * Handles the 'deliveryType' property to distinguish between supported values ('cache', 'navigational-prefetch'),\n * undefined (unsupported in some browsers), and other cases ('other' for unknown or unrecognized values).\n * see: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType\n */\nexport function computeResourceEntryDeliveryType(entry: RumPerformanceResourceTiming): DeliveryType | undefined {\n return entry.deliveryType === '' ? 'other' : entry.deliveryType\n}\n\nexport function computeResourceEntrySize(entry: RumPerformanceResourceTiming) {\n // Make sure a request actually occurred\n if (entry.startTime < entry.responseStart) {\n const { encodedBodySize, decodedBodySize, transferSize } = entry\n return {\n size: decodedBodySize,\n encoded_body_size: encodedBodySize,\n decoded_body_size: decodedBodySize,\n transfer_size: transferSize,\n }\n }\n return {\n size: undefined,\n encoded_body_size: undefined,\n decoded_body_size: undefined,\n transfer_size: undefined,\n }\n}\n\nexport function isAllowedRequestUrl(url: string) {\n return url && !isIntakeUrl(url)\n}\n\nconst DATA_URL_REGEX = /data:(.+)?(;base64)?,/g\nexport const MAX_ATTRIBUTE_VALUE_CHAR_LENGTH = 24_000\n\nexport function isLongDataUrl(url: string): boolean {\n if (url.length <= MAX_ATTRIBUTE_VALUE_CHAR_LENGTH) {\n return false\n } else if (url.substring(0, 5) === 'data:') {\n // Avoid String.match RangeError: Maximum call stack size exceeded\n url = url.substring(0, MAX_ATTRIBUTE_VALUE_CHAR_LENGTH)\n return true\n }\n return false\n}\n\nexport function sanitizeDataUrl(url: string): string {\n return `${url.match(DATA_URL_REGEX)![0]}[...]`\n}\n", "import type {\n Duration,\n XhrCompleteContext,\n XhrStartContext,\n ClocksState,\n FetchStartContext,\n FetchResolveContext,\n} from '@datadog/browser-core'\nimport {\n RequestType,\n initFetchObservable,\n initXhrObservable,\n readBytesFromStream,\n elapsed,\n timeStampNow,\n tryToClone,\n} from '@datadog/browser-core'\nimport type { RumSessionManager } from '..'\nimport type { RumConfiguration } from './configuration'\nimport type { LifeCycle } from './lifeCycle'\nimport { LifeCycleEventType } from './lifeCycle'\nimport { isAllowedRequestUrl } from './resource/resourceUtils'\nimport type { Tracer } from './tracing/tracer'\nimport { startTracer } from './tracing/tracer'\nimport type { SpanIdentifier, TraceIdentifier } from './tracing/identifier'\nimport type { CommonContext } from './contexts/commonContext'\n\nexport interface CustomContext {\n requestIndex: number\n spanId?: SpanIdentifier\n traceId?: TraceIdentifier\n traceSampled?: boolean\n}\nexport interface RumFetchStartContext extends FetchStartContext, CustomContext {}\nexport interface RumFetchResolveContext extends FetchResolveContext, CustomContext {}\nexport interface RumXhrStartContext extends XhrStartContext, CustomContext {}\nexport interface RumXhrCompleteContext extends XhrCompleteContext, CustomContext {}\n\nexport interface RequestStartEvent {\n requestIndex: number\n url: string\n}\nexport interface RequestCompleteEvent {\n requestIndex: number\n type: RequestType\n method: string\n url: string\n status: number\n responseType?: string\n startClocks: ClocksState\n duration: Duration\n spanId?: SpanIdentifier\n traceId?: TraceIdentifier\n traceSampled?: boolean\n xhr?: XMLHttpRequest\n response?: Response\n input?: unknown\n init?: RequestInit\n error?: Error\n isAborted: boolean\n handlingStack?: string\n}\n\nlet nextRequestIndex = 1\n\nexport function startRequestCollection(\n lifeCycle: LifeCycle,\n configuration: RumConfiguration,\n sessionManager: RumSessionManager,\n getCommonContext: () => CommonContext\n) {\n const tracer = startTracer(configuration, sessionManager, getCommonContext)\n trackXhr(lifeCycle, configuration, tracer)\n trackFetch(lifeCycle, tracer)\n}\n\nexport function trackXhr(lifeCycle: LifeCycle, configuration: RumConfiguration, tracer: Tracer) {\n const subscription = initXhrObservable(configuration).subscribe((rawContext) => {\n const context = rawContext as RumXhrStartContext | RumXhrCompleteContext\n if (!isAllowedRequestUrl(context.url)) {\n return\n }\n\n switch (context.state) {\n case 'start':\n tracer.traceXhr(context, context.xhr)\n context.requestIndex = getNextRequestIndex()\n\n lifeCycle.notify(LifeCycleEventType.REQUEST_STARTED, {\n requestIndex: context.requestIndex,\n url: context.url,\n })\n break\n case 'complete':\n tracer.clearTracingIfNeeded(context)\n lifeCycle.notify(LifeCycleEventType.REQUEST_COMPLETED, {\n duration: context.duration,\n method: context.method,\n requestIndex: context.requestIndex,\n spanId: context.spanId,\n startClocks: context.startClocks,\n status: context.status,\n traceId: context.traceId,\n traceSampled: context.traceSampled,\n type: RequestType.XHR,\n url: context.url,\n xhr: context.xhr,\n isAborted: context.isAborted,\n handlingStack: context.handlingStack,\n })\n break\n }\n })\n\n return { stop: () => subscription.unsubscribe() }\n}\n\nexport function trackFetch(lifeCycle: LifeCycle, tracer: Tracer) {\n const subscription = initFetchObservable().subscribe((rawContext) => {\n const context = rawContext as RumFetchResolveContext | RumFetchStartContext\n if (!isAllowedRequestUrl(context.url)) {\n return\n }\n\n switch (context.state) {\n case 'start':\n tracer.traceFetch(context)\n context.requestIndex = getNextRequestIndex()\n\n lifeCycle.notify(LifeCycleEventType.REQUEST_STARTED, {\n requestIndex: context.requestIndex,\n url: context.url,\n })\n break\n case 'resolve':\n waitForResponseToComplete(context, (duration: Duration) => {\n tracer.clearTracingIfNeeded(context)\n lifeCycle.notify(LifeCycleEventType.REQUEST_COMPLETED, {\n duration,\n method: context.method,\n requestIndex: context.requestIndex,\n responseType: context.responseType,\n spanId: context.spanId,\n startClocks: context.startClocks,\n status: context.status,\n traceId: context.traceId,\n traceSampled: context.traceSampled,\n type: RequestType.FETCH,\n url: context.url,\n response: context.response,\n init: context.init,\n input: context.input,\n isAborted: context.isAborted,\n handlingStack: context.handlingStack,\n })\n })\n break\n }\n })\n return { stop: () => subscription.unsubscribe() }\n}\n\nfunction getNextRequestIndex() {\n const result = nextRequestIndex\n nextRequestIndex += 1\n return result\n}\n\nfunction waitForResponseToComplete(context: RumFetchResolveContext, callback: (duration: Duration) => void) {\n const clonedResponse = context.response && tryToClone(context.response)\n if (!clonedResponse || !clonedResponse.body) {\n // do not try to wait for the response if the clone failed, fetch error or null body\n callback(elapsed(context.startClocks.timeStamp, timeStampNow()))\n } else {\n readBytesFromStream(\n clonedResponse.body,\n () => {\n callback(elapsed(context.startClocks.timeStamp, timeStampNow()))\n },\n {\n bytesLimit: Number.POSITIVE_INFINITY,\n collectStreamBody: false,\n }\n )\n }\n}\n", "import type { ServerDuration } from '@datadog/browser-core'\nimport { isNumber } from '@datadog/browser-core'\n\nexport function discardNegativeDuration(duration: ServerDuration | undefined): ServerDuration | undefined {\n return isNumber(duration) && duration < 0 ? undefined : duration\n}\n", "import { noop } from '@datadog/browser-core'\nimport { RumEventType } from '../rawRumEvent.types'\nimport type { RumActionEvent, RumErrorEvent, RumLongTaskEvent, RumResourceEvent } from '../rumEvent.types'\nimport type { LifeCycle } from './lifeCycle'\nimport { LifeCycleEventType } from './lifeCycle'\n\nexport interface EventCounts {\n errorCount: number\n actionCount: number\n longTaskCount: number\n resourceCount: number\n frustrationCount: number\n}\n\nexport function trackEventCounts({\n lifeCycle,\n isChildEvent,\n onChange: callback = noop,\n}: {\n lifeCycle: LifeCycle\n isChildEvent: (event: RumActionEvent | RumErrorEvent | RumLongTaskEvent | RumResourceEvent) => boolean\n onChange?: () => void\n}) {\n const eventCounts: EventCounts = {\n errorCount: 0,\n longTaskCount: 0,\n resourceCount: 0,\n actionCount: 0,\n frustrationCount: 0,\n }\n\n const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, (event): void => {\n if (event.type === 'view' || event.type === 'vital' || !isChildEvent(event)) {\n return\n }\n switch (event.type) {\n case RumEventType.ERROR:\n eventCounts.errorCount += 1\n callback()\n break\n case RumEventType.ACTION:\n eventCounts.actionCount += 1\n if (event.action.frustration) {\n eventCounts.frustrationCount += event.action.frustration.type.length\n }\n callback()\n break\n case RumEventType.LONG_TASK:\n eventCounts.longTaskCount += 1\n callback()\n break\n case RumEventType.RESOURCE:\n if (!event._dd?.discarded) {\n eventCounts.resourceCount += 1\n callback()\n }\n break\n }\n })\n\n return {\n stop: () => {\n subscription.unsubscribe()\n },\n eventCounts,\n }\n}\n", "import type { Duration, RelativeTime } from '@datadog/browser-core'\nimport { addEventListeners, dateNow, DOM_EVENT, relativeNow } from '@datadog/browser-core'\nimport type { RumConfiguration } from '../domain/configuration'\n\n/**\n * first-input timing entry polyfill based on\n * https://github.com/GoogleChrome/web-vitals/blob/master/src/lib/polyfills/firstInputPolyfill.ts\n */\nexport function retrieveFirstInputTiming(\n configuration: RumConfiguration,\n callback: (timing: PerformanceEventTiming) => void\n) {\n const startTimeStamp = dateNow()\n let timingSent = false\n\n const { stop: removeEventListeners } = addEventListeners(\n configuration,\n window,\n [DOM_EVENT.CLICK, DOM_EVENT.MOUSE_DOWN, DOM_EVENT.KEY_DOWN, DOM_EVENT.TOUCH_START, DOM_EVENT.POINTER_DOWN],\n (evt) => {\n // Only count cancelable events, which should trigger behavior important to the user.\n if (!evt.cancelable) {\n return\n }\n\n // This timing will be used to compute the \"first Input delay\", which is the delta between\n // when the system received the event (e.g. evt.timeStamp) and when it could run the callback\n // (e.g. performance.now()).\n const timing: PerformanceEventTiming = {\n entryType: 'first-input',\n processingStart: relativeNow(),\n processingEnd: relativeNow(),\n startTime: evt.timeStamp as RelativeTime,\n duration: 0 as Duration, // arbitrary value to avoid nullable duration and simplify INP logic\n name: '',\n cancelable: false,\n target: null,\n toJSON: () => ({}),\n }\n\n if (evt.type === DOM_EVENT.POINTER_DOWN) {\n sendTimingIfPointerIsNotCancelled(configuration, timing)\n } else {\n sendTiming(timing)\n }\n },\n { passive: true, capture: true }\n )\n\n return { stop: removeEventListeners }\n\n /**\n * Pointer events are a special case, because they can trigger main or compositor thread behavior.\n * We differentiate these cases based on whether or not we see a pointercancel event, which are\n * fired when we scroll. If we're scrolling we don't need to report input delay since FID excludes\n * scrolling and pinch/zooming.\n */\n function sendTimingIfPointerIsNotCancelled(configuration: RumConfiguration, timing: PerformanceEventTiming) {\n addEventListeners(\n configuration,\n window,\n [DOM_EVENT.POINTER_UP, DOM_EVENT.POINTER_CANCEL],\n (event) => {\n if (event.type === DOM_EVENT.POINTER_UP) {\n sendTiming(timing)\n }\n },\n { once: true }\n )\n }\n\n function sendTiming(timing: PerformanceEventTiming) {\n if (!timingSent) {\n timingSent = true\n removeEventListeners()\n // In some cases the recorded delay is clearly wrong, e.g. it's negative or it's larger than\n // the time between now and when the page was loaded.\n // - https://github.com/GoogleChromeLabs/first-input-delay/issues/4\n // - https://github.com/GoogleChromeLabs/first-input-delay/issues/6\n // - https://github.com/GoogleChromeLabs/first-input-delay/issues/7\n const delay = timing.processingStart - timing.startTime\n if (delay >= 0 && delay < dateNow() - startTimeStamp) {\n callback(timing)\n }\n }\n }\n}\n", "import type { Duration, RelativeTime, TimeoutId } from '@datadog/browser-core'\nimport { addEventListener, Observable, setTimeout, clearTimeout, monitor } from '@datadog/browser-core'\nimport type { RumConfiguration } from '../domain/configuration'\nimport { hasValidResourceEntryDuration, isAllowedRequestUrl } from '../domain/resource/resourceUtils'\nimport { retrieveFirstInputTiming } from './firstInputPolyfill'\n\ntype RumPerformanceObserverConstructor = new (callback: PerformanceObserverCallback) => RumPerformanceObserver\n\nexport interface BrowserWindow extends Window {\n PerformanceObserver: RumPerformanceObserverConstructor\n performance: Performance & { interactionCount?: number }\n}\n\nexport interface RumPerformanceObserver extends PerformanceObserver {\n observe(options?: PerformanceObserverInit & { durationThreshold?: number }): void\n}\n\n// We want to use a real enum (i.e. not a const enum) here, to be able to check whether an arbitrary\n// string is an expected performance entry\n// eslint-disable-next-line no-restricted-syntax\nexport enum RumPerformanceEntryType {\n EVENT = 'event',\n FIRST_INPUT = 'first-input',\n LARGEST_CONTENTFUL_PAINT = 'largest-contentful-paint',\n LAYOUT_SHIFT = 'layout-shift',\n LONG_TASK = 'longtask',\n LONG_ANIMATION_FRAME = 'long-animation-frame',\n NAVIGATION = 'navigation',\n PAINT = 'paint',\n RESOURCE = 'resource',\n VISIBILITY_STATE = 'visibility-state',\n}\n\nexport interface RumPerformanceLongTaskTiming {\n name: string\n entryType: RumPerformanceEntryType.LONG_TASK\n startTime: RelativeTime\n duration: Duration\n toJSON(): Omit\n}\n\nexport interface RumPerformanceResourceTiming {\n entryType: RumPerformanceEntryType.RESOURCE\n initiatorType: string\n responseStatus?: number\n name: string\n startTime: RelativeTime\n duration: Duration\n fetchStart: RelativeTime\n workerStart: RelativeTime\n domainLookupStart: RelativeTime\n domainLookupEnd: RelativeTime\n connectStart: RelativeTime\n secureConnectionStart: RelativeTime\n connectEnd: RelativeTime\n requestStart: RelativeTime\n responseStart: RelativeTime\n responseEnd: RelativeTime\n redirectStart: RelativeTime\n redirectEnd: RelativeTime\n decodedBodySize: number\n encodedBodySize: number\n transferSize: number\n nextHopProtocol?: string\n renderBlockingStatus?: string\n traceId?: string\n deliveryType?: 'cache' | 'navigational-prefetch' | ''\n toJSON(): Omit\n}\n\nexport interface RumPerformancePaintTiming {\n entryType: RumPerformanceEntryType.PAINT\n name: 'first-paint' | 'first-contentful-paint'\n startTime: RelativeTime\n toJSON(): Omit\n}\n\nexport interface RumPerformanceNavigationTiming extends Omit {\n entryType: RumPerformanceEntryType.NAVIGATION\n initiatorType: 'navigation'\n name: string\n\n domComplete: RelativeTime\n domContentLoadedEventEnd: RelativeTime\n domInteractive: RelativeTime\n loadEventEnd: RelativeTime\n\n toJSON(): Omit\n}\n\nexport interface RumLargestContentfulPaintTiming {\n entryType: RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT\n startTime: RelativeTime\n size: number\n element?: Element\n url?: string\n toJSON(): Omit\n}\n\nexport interface RumFirstInputTiming {\n entryType: RumPerformanceEntryType.FIRST_INPUT\n startTime: RelativeTime\n processingStart: RelativeTime\n processingEnd: RelativeTime\n duration: Duration\n target?: Node\n interactionId?: number\n toJSON(): Omit\n}\n\nexport interface RumPerformanceEventTiming {\n entryType: RumPerformanceEntryType.EVENT\n startTime: RelativeTime\n processingStart: RelativeTime\n processingEnd: RelativeTime\n duration: Duration\n interactionId?: number\n target?: Node\n name: string\n toJSON(): Omit\n}\n\nexport interface RumLayoutShiftAttribution {\n node: Node | null\n previousRect: DOMRectReadOnly\n currentRect: DOMRectReadOnly\n}\n\nexport interface RumLayoutShiftTiming {\n entryType: RumPerformanceEntryType.LAYOUT_SHIFT\n startTime: RelativeTime\n value: number\n hadRecentInput: boolean\n sources: RumLayoutShiftAttribution[]\n toJSON(): Omit\n}\n\n// Documentation https://developer.chrome.com/docs/web-platform/long-animation-frames#better-attribution\nexport type RumPerformanceScriptTiming = {\n duration: Duration\n entryType: 'script'\n executionStart: RelativeTime\n forcedStyleAndLayoutDuration: Duration\n invoker: string // e.g. \"https://static.datadoghq.com/static/c/93085/chunk-bc4db53278fd4c77a637.min.js\"\n invokerType:\n | 'user-callback'\n | 'event-listener'\n | 'resolve-promise'\n | 'reject-promise'\n | 'classic-script'\n | 'module-script'\n name: 'script'\n pauseDuration: Duration\n sourceCharPosition: number\n sourceFunctionName: string\n sourceURL: string\n startTime: RelativeTime\n window: Window\n windowAttribution: string\n}\n\nexport interface RumPerformanceLongAnimationFrameTiming {\n blockingDuration: Duration\n duration: Duration\n entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME\n firstUIEventTimestamp: RelativeTime\n name: 'long-animation-frame'\n renderStart: RelativeTime\n scripts: RumPerformanceScriptTiming[]\n startTime: RelativeTime\n styleAndLayoutStart: RelativeTime\n toJSON(): Omit\n}\n\nexport interface RumFirstHiddenTiming {\n entryType: RumPerformanceEntryType.VISIBILITY_STATE\n name: 'hidden' | 'visible'\n startTime: RelativeTime\n toJSON(): Omit\n}\n\nexport type RumPerformanceEntry =\n | RumPerformanceResourceTiming\n | RumPerformanceLongTaskTiming\n | RumPerformanceLongAnimationFrameTiming\n | RumPerformancePaintTiming\n | RumPerformanceNavigationTiming\n | RumLargestContentfulPaintTiming\n | RumFirstInputTiming\n | RumPerformanceEventTiming\n | RumLayoutShiftTiming\n | RumFirstHiddenTiming\n\nexport type EntryTypeToReturnType = {\n [RumPerformanceEntryType.EVENT]: RumPerformanceEventTiming\n [RumPerformanceEntryType.FIRST_INPUT]: RumFirstInputTiming\n [RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT]: RumLargestContentfulPaintTiming\n [RumPerformanceEntryType.LAYOUT_SHIFT]: RumLayoutShiftTiming\n [RumPerformanceEntryType.PAINT]: RumPerformancePaintTiming\n [RumPerformanceEntryType.LONG_TASK]: RumPerformanceLongTaskTiming\n [RumPerformanceEntryType.LONG_ANIMATION_FRAME]: RumPerformanceLongAnimationFrameTiming\n [RumPerformanceEntryType.NAVIGATION]: RumPerformanceNavigationTiming\n [RumPerformanceEntryType.RESOURCE]: RumPerformanceResourceTiming\n [RumPerformanceEntryType.VISIBILITY_STATE]: RumFirstHiddenTiming\n}\n\nexport function createPerformanceObservable(\n configuration: RumConfiguration,\n options: { type: T; buffered?: boolean; durationThreshold?: number }\n) {\n return new Observable>((observable) => {\n if (!window.PerformanceObserver) {\n return\n }\n\n const handlePerformanceEntries = (entries: PerformanceEntryList) => {\n const rumPerformanceEntries = filterRumPerformanceEntries(entries as Array)\n if (rumPerformanceEntries.length > 0) {\n observable.notify(rumPerformanceEntries)\n }\n }\n\n let timeoutId: TimeoutId | undefined\n let isObserverInitializing = true\n const observer = new PerformanceObserver(\n monitor((entries) => {\n // In Safari the performance observer callback is synchronous.\n // Because the buffered performance entry list can be quite large we delay the computation to prevent the SDK from blocking the main thread on init\n if (isObserverInitializing) {\n timeoutId = setTimeout(() => handlePerformanceEntries(entries.getEntries()))\n } else {\n handlePerformanceEntries(entries.getEntries())\n }\n })\n )\n try {\n observer.observe(options)\n } catch {\n // Some old browser versions (<= chrome 74 ) don't support the PerformanceObserver type and buffered options\n // In these cases, fallback to getEntriesByType and PerformanceObserver with entryTypes\n // TODO: remove this fallback in the next major version\n const fallbackSupportedEntryTypes = [\n RumPerformanceEntryType.RESOURCE,\n RumPerformanceEntryType.NAVIGATION,\n RumPerformanceEntryType.LONG_TASK,\n RumPerformanceEntryType.PAINT,\n ]\n if (fallbackSupportedEntryTypes.includes(options.type)) {\n if (options.buffered) {\n timeoutId = setTimeout(() => handlePerformanceEntries(performance.getEntriesByType(options.type)))\n }\n try {\n observer.observe({ entryTypes: [options.type] })\n } catch {\n // Old versions of Safari are throwing \"entryTypes contained only unsupported types\"\n // errors when observing only unsupported entry types.\n //\n // We could use `supportPerformanceTimingEvent` to make sure we don't invoke\n // `observer.observe` with an unsupported entry type, but Safari 11 and 12 don't support\n // `Performance.supportedEntryTypes`, so doing so would lose support for these versions\n // even if they do support the entry type.\n return\n }\n }\n }\n isObserverInitializing = false\n\n manageResourceTimingBufferFull(configuration)\n\n let stopFirstInputTiming: (() => void) | undefined\n if (\n !supportPerformanceTimingEvent(RumPerformanceEntryType.FIRST_INPUT) &&\n options.type === RumPerformanceEntryType.FIRST_INPUT\n ) {\n ;({ stop: stopFirstInputTiming } = retrieveFirstInputTiming(configuration, (timing) => {\n handlePerformanceEntries([timing])\n }))\n }\n\n return () => {\n observer.disconnect()\n if (stopFirstInputTiming) {\n stopFirstInputTiming()\n }\n clearTimeout(timeoutId)\n }\n })\n}\n\nlet resourceTimingBufferFullListener: { stop: () => void }\nfunction manageResourceTimingBufferFull(configuration: RumConfiguration) {\n if (!resourceTimingBufferFullListener && supportPerformanceObject() && 'addEventListener' in performance) {\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1559377\n resourceTimingBufferFullListener = addEventListener(configuration, performance, 'resourcetimingbufferfull', () => {\n performance.clearResourceTimings()\n })\n }\n return () => {\n resourceTimingBufferFullListener?.stop()\n }\n}\n\nfunction supportPerformanceObject() {\n return window.performance !== undefined && 'getEntries' in performance\n}\n\nexport function supportPerformanceTimingEvent(entryType: RumPerformanceEntryType) {\n return (\n window.PerformanceObserver &&\n PerformanceObserver.supportedEntryTypes !== undefined &&\n PerformanceObserver.supportedEntryTypes.includes(entryType)\n )\n}\n\nfunction filterRumPerformanceEntries(entries: Array) {\n return entries.filter((entry) => !isForbiddenResource(entry))\n}\n\nfunction isForbiddenResource(entry: RumPerformanceEntry) {\n return (\n entry.entryType === RumPerformanceEntryType.RESOURCE &&\n (!isAllowedRequestUrl(entry.name) || !hasValidResourceEntryDuration(entry))\n )\n}\n", "import type { Subscription, TimeoutId, TimeStamp } from '@datadog/browser-core'\nimport { matchList, monitor, Observable, timeStampNow, setTimeout, clearTimeout } from '@datadog/browser-core'\nimport { createPerformanceObservable, RumPerformanceEntryType } from '../browser/performanceObservable'\nimport type { RumConfiguration } from './configuration'\nimport type { LifeCycle } from './lifeCycle'\nimport { LifeCycleEventType } from './lifeCycle'\n\n// Delay to wait for a page activity to validate the tracking process\nexport const PAGE_ACTIVITY_VALIDATION_DELAY = 100\n// Delay to wait after a page activity to end the tracking process\nexport const PAGE_ACTIVITY_END_DELAY = 100\n\nexport interface PageActivityEvent {\n isBusy: boolean\n}\n\nexport type PageActivityEndEvent = { hadActivity: true; end: TimeStamp } | { hadActivity: false }\n\n/**\n * Wait for the page activity end\n *\n * Detection lifecycle:\n * ```\n * Wait page activity end\n * .-------------------'--------------------.\n * v v\n * [Wait for a page activity ] [Wait for a maximum duration]\n * [timeout: VALIDATION_DELAY] [ timeout: maxDuration ]\n * / \\ |\n * v v |\n * [No page activity] [Page activity] |\n * | |,----------------------. |\n * v v | |\n * (Discard) [Wait for a page activity] | |\n * [ timeout: END_DELAY ] | |\n * / \\ | |\n * v v | |\n * [No page activity] [Page activity] | |\n * | | | |\n * | '------------' |\n * '-----------. ,--------------------'\n * v\n * (End)\n * ```\n *\n * Note: by assuming that maxDuration is greater than VALIDATION_DELAY, we are sure that if the\n * process is still alive after maxDuration, it has been validated.\n */\nexport function waitPageActivityEnd(\n lifeCycle: LifeCycle,\n domMutationObservable: Observable,\n windowOpenObservable: Observable,\n configuration: RumConfiguration,\n pageActivityEndCallback: (event: PageActivityEndEvent) => void,\n maxDuration?: number\n) {\n const pageActivityObservable = createPageActivityObservable(\n lifeCycle,\n domMutationObservable,\n windowOpenObservable,\n configuration\n )\n return doWaitPageActivityEnd(pageActivityObservable, pageActivityEndCallback, maxDuration)\n}\n\nexport function doWaitPageActivityEnd(\n pageActivityObservable: Observable,\n pageActivityEndCallback: (event: PageActivityEndEvent) => void,\n maxDuration?: number\n) {\n let pageActivityEndTimeoutId: TimeoutId\n let hasCompleted = false\n\n const validationTimeoutId = setTimeout(\n monitor(() => complete({ hadActivity: false })),\n PAGE_ACTIVITY_VALIDATION_DELAY\n )\n const maxDurationTimeoutId =\n maxDuration !== undefined\n ? setTimeout(\n monitor(() => complete({ hadActivity: true, end: timeStampNow() })),\n maxDuration\n )\n : undefined\n\n const pageActivitySubscription = pageActivityObservable.subscribe(({ isBusy }) => {\n clearTimeout(validationTimeoutId)\n clearTimeout(pageActivityEndTimeoutId)\n const lastChangeTime = timeStampNow()\n if (!isBusy) {\n pageActivityEndTimeoutId = setTimeout(\n monitor(() => complete({ hadActivity: true, end: lastChangeTime })),\n PAGE_ACTIVITY_END_DELAY\n )\n }\n })\n\n const stop = () => {\n hasCompleted = true\n clearTimeout(validationTimeoutId)\n clearTimeout(pageActivityEndTimeoutId)\n clearTimeout(maxDurationTimeoutId)\n pageActivitySubscription.unsubscribe()\n }\n\n function complete(event: PageActivityEndEvent) {\n if (hasCompleted) {\n return\n }\n stop()\n pageActivityEndCallback(event)\n }\n return { stop }\n}\n\nexport function createPageActivityObservable(\n lifeCycle: LifeCycle,\n domMutationObservable: Observable,\n windowOpenObservable: Observable,\n configuration: RumConfiguration\n): Observable {\n return new Observable((observable) => {\n const subscriptions: Subscription[] = []\n let firstRequestIndex: undefined | number\n let pendingRequestsCount = 0\n\n subscriptions.push(\n domMutationObservable.subscribe(notifyPageActivity),\n windowOpenObservable.subscribe(notifyPageActivity),\n createPerformanceObservable(configuration, { type: RumPerformanceEntryType.RESOURCE }).subscribe((entries) => {\n if (entries.some((entry) => !isExcludedUrl(configuration, entry.name))) {\n notifyPageActivity()\n }\n }),\n lifeCycle.subscribe(LifeCycleEventType.REQUEST_STARTED, (startEvent) => {\n if (isExcludedUrl(configuration, startEvent.url)) {\n return\n }\n if (firstRequestIndex === undefined) {\n firstRequestIndex = startEvent.requestIndex\n }\n pendingRequestsCount += 1\n notifyPageActivity()\n }),\n lifeCycle.subscribe(LifeCycleEventType.REQUEST_COMPLETED, (request) => {\n if (\n isExcludedUrl(configuration, request.url) ||\n firstRequestIndex === undefined ||\n // If the request started before the tracking start, ignore it\n request.requestIndex < firstRequestIndex\n ) {\n return\n }\n pendingRequestsCount -= 1\n notifyPageActivity()\n })\n )\n\n return () => {\n subscriptions.forEach((s) => s.unsubscribe())\n }\n\n function notifyPageActivity() {\n observable.notify({ isBusy: pendingRequestsCount > 0 })\n }\n })\n}\n\nfunction isExcludedUrl(configuration: RumConfiguration, requestUrl: string): boolean {\n return matchList(configuration.excludedActivityUrls, requestUrl)\n}\n", "export function isTextNode(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE\n}\n\nexport function isCommentNode(node: Node): node is Comment {\n return node.nodeType === Node.COMMENT_NODE\n}\n\nexport function isElementNode(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE\n}\n\nexport function isNodeShadowHost(node: Node): node is Element & { shadowRoot: ShadowRoot } {\n return isElementNode(node) && Boolean(node.shadowRoot)\n}\n\nexport function isNodeShadowRoot(node: Node): node is ShadowRoot {\n const shadowRoot = node as ShadowRoot\n return !!shadowRoot.host && shadowRoot.nodeType === Node.DOCUMENT_FRAGMENT_NODE && isElementNode(shadowRoot.host)\n}\n\nexport function hasChildNodes(node: Node) {\n return node.childNodes.length > 0 || isNodeShadowHost(node)\n}\n\nexport function forEachChildNodes(node: Node, callback: (child: Node) => void) {\n let child = node.firstChild\n\n while (child) {\n callback(child)\n\n child = child.nextSibling\n }\n\n if (isNodeShadowHost(node)) {\n callback(node.shadowRoot)\n }\n}\n\n/**\n * Return `host` in case if the current node is a shadow root otherwise will return the `parentNode`\n */\nexport function getParentNode(node: Node): Node | null {\n return isNodeShadowRoot(node) ? node.host : node.parentNode\n}\n", "import { DefaultPrivacyLevel } from '@datadog/browser-core'\nimport { isElementNode, getParentNode, isTextNode } from '../browser/htmlDomUtils'\n\nexport const NodePrivacyLevel = {\n IGNORE: 'ignore',\n HIDDEN: 'hidden',\n ALLOW: DefaultPrivacyLevel.ALLOW,\n MASK: DefaultPrivacyLevel.MASK,\n MASK_USER_INPUT: DefaultPrivacyLevel.MASK_USER_INPUT,\n} as const\nexport type NodePrivacyLevel = (typeof NodePrivacyLevel)[keyof typeof NodePrivacyLevel]\n\nexport const PRIVACY_ATTR_NAME = 'data-dd-privacy'\n\n// Privacy Attrs\nexport const PRIVACY_ATTR_VALUE_ALLOW = 'allow'\nexport const PRIVACY_ATTR_VALUE_MASK = 'mask'\nexport const PRIVACY_ATTR_VALUE_MASK_USER_INPUT = 'mask-user-input'\nexport const PRIVACY_ATTR_VALUE_HIDDEN = 'hidden'\n\n// Privacy Classes - not all customers can set plain HTML attributes, so support classes too\nexport const PRIVACY_CLASS_PREFIX = 'dd-privacy-'\n\n// Private Replacement Templates\nexport const CENSORED_STRING_MARK = '***'\nexport const CENSORED_IMG_MARK = 'data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=='\n\nexport const FORM_PRIVATE_TAG_NAMES: { [tagName: string]: true } = {\n INPUT: true,\n OUTPUT: true,\n TEXTAREA: true,\n SELECT: true,\n OPTION: true,\n DATALIST: true,\n OPTGROUP: true,\n}\n\nconst TEXT_MASKING_CHAR = 'x'\n\nexport type NodePrivacyLevelCache = Map\n\n/**\n * Get node privacy level by iterating over its ancestors. When the direct parent privacy level is\n * know, it is best to use something like:\n *\n * derivePrivacyLevelGivenParent(getNodeSelfPrivacyLevel(node), parentNodePrivacyLevel)\n */\nexport function getNodePrivacyLevel(\n node: Node,\n defaultPrivacyLevel: NodePrivacyLevel,\n cache?: NodePrivacyLevelCache\n): NodePrivacyLevel {\n if (cache && cache.has(node)) {\n return cache.get(node)!\n }\n const parentNode = getParentNode(node)\n const parentNodePrivacyLevel = parentNode\n ? getNodePrivacyLevel(parentNode, defaultPrivacyLevel, cache)\n : defaultPrivacyLevel\n const selfNodePrivacyLevel = getNodeSelfPrivacyLevel(node)\n const nodePrivacyLevel = reducePrivacyLevel(selfNodePrivacyLevel, parentNodePrivacyLevel)\n if (cache) {\n cache.set(node, nodePrivacyLevel)\n }\n return nodePrivacyLevel\n}\n\n/**\n * Reduces the next privacy level based on self + parent privacy levels\n */\nexport function reducePrivacyLevel(\n childPrivacyLevel: NodePrivacyLevel | undefined,\n parentNodePrivacyLevel: NodePrivacyLevel\n): NodePrivacyLevel {\n switch (parentNodePrivacyLevel) {\n // These values cannot be overridden\n case NodePrivacyLevel.HIDDEN:\n case NodePrivacyLevel.IGNORE:\n return parentNodePrivacyLevel\n }\n switch (childPrivacyLevel) {\n case NodePrivacyLevel.ALLOW:\n case NodePrivacyLevel.MASK:\n case NodePrivacyLevel.MASK_USER_INPUT:\n case NodePrivacyLevel.HIDDEN:\n case NodePrivacyLevel.IGNORE:\n return childPrivacyLevel\n default:\n return parentNodePrivacyLevel\n }\n}\n\n/**\n * Determines the node's own privacy level without checking for ancestors.\n */\nexport function getNodeSelfPrivacyLevel(node: Node): NodePrivacyLevel | undefined {\n // Only Element types can have a privacy level set\n if (!isElementNode(node)) {\n return\n }\n\n // Overrules for replay purpose\n if (node.tagName === 'BASE') {\n return NodePrivacyLevel.ALLOW\n }\n\n // Overrules to enforce end-user protection\n if (node.tagName === 'INPUT') {\n const inputElement = node as HTMLInputElement\n if (inputElement.type === 'password' || inputElement.type === 'email' || inputElement.type === 'tel') {\n return NodePrivacyLevel.MASK\n }\n if (inputElement.type === 'hidden') {\n return NodePrivacyLevel.MASK\n }\n const autocomplete = inputElement.getAttribute('autocomplete')\n // Handle input[autocomplete=cc-number/cc-csc/cc-exp/cc-exp-month/cc-exp-year/new-password/current-password]\n if (autocomplete && (autocomplete.startsWith('cc-') || autocomplete.endsWith('-password'))) {\n return NodePrivacyLevel.MASK\n }\n }\n\n // Check HTML privacy attributes and classes\n if (node.matches(getPrivacySelector(NodePrivacyLevel.HIDDEN))) {\n return NodePrivacyLevel.HIDDEN\n }\n\n if (node.matches(getPrivacySelector(NodePrivacyLevel.MASK))) {\n return NodePrivacyLevel.MASK\n }\n\n if (node.matches(getPrivacySelector(NodePrivacyLevel.MASK_USER_INPUT))) {\n return NodePrivacyLevel.MASK_USER_INPUT\n }\n\n if (node.matches(getPrivacySelector(NodePrivacyLevel.ALLOW))) {\n return NodePrivacyLevel.ALLOW\n }\n\n if (shouldIgnoreElement(node)) {\n return NodePrivacyLevel.IGNORE\n }\n}\n\n/**\n * Helper aiming to unify `mask` and `mask-user-input` privacy levels:\n *\n * In the `mask` case, it is trivial: we should mask the element.\n *\n * In the `mask-user-input` case, we should mask the element only if it is a \"form\" element or the\n * direct parent is a form element for text nodes).\n *\n * Other `shouldMaskNode` cases are edge cases that should not matter too much (ex: should we mask a\n * node if it is ignored or hidden? it doesn't matter since it won't be serialized).\n */\nexport function shouldMaskNode(node: Node, privacyLevel: NodePrivacyLevel) {\n switch (privacyLevel) {\n case NodePrivacyLevel.MASK:\n case NodePrivacyLevel.HIDDEN:\n case NodePrivacyLevel.IGNORE:\n return true\n case NodePrivacyLevel.MASK_USER_INPUT:\n return isTextNode(node) ? isFormElement(node.parentNode) : isFormElement(node)\n default:\n return false\n }\n}\n\nfunction isFormElement(node: Node | null): boolean {\n if (!node || node.nodeType !== node.ELEMENT_NODE) {\n return false\n }\n const element = node as HTMLInputElement\n if (element.tagName === 'INPUT') {\n switch (element.type) {\n case 'button':\n case 'color':\n case 'reset':\n case 'submit':\n return false\n }\n }\n return !!FORM_PRIVATE_TAG_NAMES[element.tagName]\n}\n\n/**\n * Text censoring non-destructively maintains whitespace characters in order to preserve text shape\n * during replay.\n */\nexport const censorText = (text: string) => text.replace(/\\S/g, TEXT_MASKING_CHAR)\n\nexport function getTextContent(\n textNode: Node,\n ignoreWhiteSpace: boolean,\n parentNodePrivacyLevel: NodePrivacyLevel\n): string | undefined {\n // The parent node may not be a html element which has a tagName attribute.\n // So just let it be undefined which is ok in this use case.\n const parentTagName = textNode.parentElement?.tagName\n let textContent = textNode.textContent || ''\n\n if (ignoreWhiteSpace && !textContent.trim()) {\n return\n }\n\n const nodePrivacyLevel = parentNodePrivacyLevel\n\n const isScript = parentTagName === 'SCRIPT'\n\n if (isScript) {\n // For perf reasons, we don't record script (heuristic)\n textContent = CENSORED_STRING_MARK\n } else if (nodePrivacyLevel === NodePrivacyLevel.HIDDEN) {\n // Should never occur, but just in case, we set to CENSORED_MARK.\n textContent = CENSORED_STRING_MARK\n } else if (shouldMaskNode(textNode, nodePrivacyLevel)) {\n if (\n // Scrambling the child list breaks text nodes for DATALIST/SELECT/OPTGROUP\n parentTagName === 'DATALIST' ||\n parentTagName === 'SELECT' ||\n parentTagName === 'OPTGROUP'\n ) {\n if (!textContent.trim()) {\n return\n }\n } else if (parentTagName === 'OPTION') {\n //