@blueking/open-telemetry 0.0.27 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/bk-rum.global.js +1 -1
- package/dist/bk-rum.global.js.map +1 -1
- package/dist/core/config.d.ts +7 -4
- package/dist/core/runtime-context.d.ts +1 -1
- package/dist/core/url.d.ts +5 -0
- package/dist/index.js +430 -420
- package/dist/index.js.map +1 -1
- package/dist/schema/attributes.d.ts +1 -1
- package/package.json +1 -1
- package/skills/bk-rum-otele/SKILL.md +1 -1
- package/skills/bk-rum-otele/evals/evals.json +1 -1
- package/skills/bk-rum-otele/references/api.md +1 -1
- package/skills/bk-rum-otele/references/architecture.md +1 -1
- package/skills/bk-rum-otele/references/capabilities.md +1 -1
- package/skills/bk-rum-otele/references/configuration.md +5 -4
- package/skills/bk-rum-otele/references/framework-integration.md +7 -7
- package/skills/bk-rum-otele/references/getting-started.md +2 -2
- package/skills/bk-rum-otele/references/network.md +11 -3
- package/skills/bk-rum-otele/references/privacy.md +8 -6
- package/skills/bk-rum-otele/references/production.md +3 -3
- package/skills/bk-rum-otele/references/session-context.md +1 -1
- package/skills/bk-rum-otele/references/telemetry-reference.md +16 -12
- package/skills/bk-rum-otele/references/transport-sampling.md +2 -2
- package/skills/bk-rum-otele/references/troubleshooting.md +3 -3
- package/skills/bk-rum-otele/references/views-routing.md +9 -7
- package/dist/core/telemetry-attributes.d.ts +0 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../package.json","../src/core/hash.ts","../src/core/id.ts","../src/schema/attributes.ts","../src/plugins/device.ts","../src/core/config.ts","../src/core/plugin.ts","../src/core/url.ts","../src/core/processor.ts","../src/core/runtime-context.ts","../src/schema/span.ts","../src/plugins/action.ts","../src/plugins/activity.ts","../src/core/page-activity.ts","../src/plugins/blank-screen.ts","../src/core/request-timing.ts","../src/core/trace-context.ts","../src/plugins/resource.ts","../src/plugins/common.ts","../src/plugins/csp-violation.ts","../src/plugins/error.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/session-state.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/action-activity.ts","../src/core/sampling.ts","../src/core/sdk.ts"],"sourcesContent":["{\n \"name\": \"@blueking/open-telemetry\",\n \"version\": \"0.0.27\",\n \"description\": \"OpenTelemetry browser SDK for BK RUM\",\n \"author\": \"TencentCloud Real User Monitoring\",\n \"main\": \"dist/index.js\",\n \"unpkg\": \"dist/bk-rum.global.js\",\n \"jsdelivr\": \"dist/bk-rum.global.js\",\n \"license\": \"MIT\",\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"scripts\": {\n \"prepublishOnly\": \"pnpm build && pnpm dts && pnpm skill:refs\",\n \"skill:refs\": \"node scripts/gen-skill-references.mjs\",\n \"test:skill-refs\": \"node --test tests/gen-skill-references.test.mjs\",\n \"build\": \"vite build && vite build --mode cdn && pnpm dts\",\n \"build:cdn\": \"vite build --mode cdn\",\n \"build:esm\": \"vite build\",\n \"docs:build\": \"vitepress build wikis\",\n \"docs:dev\": \"vitepress dev wikis\",\n \"docs:preview\": \"vitepress preview wikis\",\n \"dev\": \"vite dev\",\n \"dts\": \"vue-tsc --project tsconfig.dts.json\",\n \"test:rum-context\": \"node --experimental-default-type=module --experimental-strip-types --test tests/action-plugin.test.mjs tests/consent.test.mjs tests/experience-plugins.test.mjs tests/request-model.test.mjs tests/runtime-context.test.mjs tests/sampling.test.mjs tests/session-state.test.ts tests/session-plugin.test.mjs tests/session-page-view.test.mjs tests/session-processors.test.mjs tests/trace-context.test.mjs tests/web-vitals.test.mjs\"\n },\n \"dependencies\": {\n \"@opentelemetry/api\": \"^1.9.1\",\n \"@opentelemetry/context-zone\": \"2.7.1\",\n \"@opentelemetry/core\": \"^2.7.1\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.217.0\",\n \"@opentelemetry/instrumentation\": \"^0.217.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-trace-base\": \"^2.7.1\",\n \"@opentelemetry/sdk-trace-web\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.38.0\",\n \"web-vitals\": \"^5.0.3\"\n },\n \"devDependencies\": {\n \"@vitejs/plugin-vue\": \"^6.0.6\",\n \"@vue/test-utils\": \"^2.4.6\",\n \"@vue/tsconfig\": \"^0.9.0\",\n \"oxc-minify\": \"^0.133.0\",\n \"vite\": \"^8.0.2\",\n \"vite-bundle-analyzer\": \"^1.3.2\",\n \"vitepress\": \"2.0.0-alpha.16\",\n \"vue\": \"^3.5.34\",\n \"vue-router\": \"^5.0.6\",\n \"vue-tsc\": \"^3.1.4\"\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/** 非密码学稳定 hash,仅用于生成浏览器本地 storage key。 */\nexport const hashString = (input: string): string => {\n let hash = 5381;\n for (let index = 0; index < input.length; index++) {\n hash = (hash * 33) ^ input.charCodeAt(index);\n }\n return (hash >>> 0).toString(36);\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 生成随机 ID。优先用 `crypto.randomUUID`(standards-compliant UUID v4),不可用时回退到\n * `${prefix}-${timestamp}-${rand}` 形式。\n *\n * 注意:fallback 实现使用 Math.random,**不是密码学安全**,仅适用于设备/会话/事件标识等场景,\n * 不要复用此函数生成 token、CSRF 防护等安全相关的值。\n */\nexport const createRandomId = (prefix: string) => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * RUM 上报字段 key 集中定义。\n *\n * 插件里不要散落硬编码字符串,后续字段命名调整只改这里,避免采集端与字段文档逐步漂移。\n */\nexport const RUM_ATTRIBUTES = {\n actionFrustrationType: 'action.frustration.type',\n actionId: 'action.id',\n actionLoadingTime: 'action.loading_time',\n actionName: 'action.name',\n actionTargetName: 'action.target.name',\n actionTargetTag: 'action.target.tag',\n actionType: 'action.type',\n deploymentEnvironmentName: 'deployment.environment.name',\n cacheHit: 'resource.cache.hit',\n deviceMemoryGib: 'device.memory_gib',\n deviceType: 'device.type',\n longTaskBlockingDuration: 'long_task.blocking_duration',\n longTaskDuration: 'long_task.duration',\n longTaskEntryType: 'long_task.entry_type',\n longTaskId: 'long_task.id',\n longTaskStartTime: 'long_task.start_time',\n networkEffectiveType: 'network.effective_type',\n networkStatus: 'network.status',\n nextHopProtocol: 'resource.protocol',\n outcomeReason: 'outcome.reason',\n outcomeType: 'outcome.type',\n serviceName: 'service.name',\n serviceVersion: 'service.version',\n sessionHasReplay: 'session.has_replay',\n sessionId: 'session.id',\n sessionSampleRate: 'session.sample_rate',\n sessionPhase: 'session.phase',\n sessionStartTime: 'session.start_time',\n sessionType: 'session.type',\n spanType: 'span_type',\n userId: 'user.id',\n userAgentName: 'user_agent.name',\n userAgentOsName: 'user_agent.os.name',\n userAgentVersion: 'user_agent.version',\n vitalMetric: 'vital.metric',\n vitalRating: 'vital.rating',\n vitalValue: 'vital.value',\n viewDomComplete: 'view.dom_complete',\n viewDomContentLoaded: 'view.dom_content_loaded',\n viewDomInteractive: 'view.dom_interactive',\n viewFirstByte: 'view.first_byte',\n viewId: 'view.id',\n viewLoadEvent: 'view.load_event',\n viewLoadingTime: 'view.loading_time',\n viewLoadingTimeSource: 'view.loading_time_source',\n viewLoadingType: 'view.loading_type',\n viewName: 'view.name',\n viewPhase: 'view.phase',\n viewStartedAt: 'view.started_at',\n viewUrl: 'view.url',\n viewUrlPathGroup: 'view.url_path_group',\n viewVersion: 'view.version',\n websocketPhase: 'websocket.phase',\n} as const;\n\nexport type RumAttributeKey = (typeof RUM_ATTRIBUTES)[keyof typeof RUM_ATTRIBUTES];\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { hashString } from '../core/hash';\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTDevicePluginOptions {\n /** 是否跨页面持久化匿名设备 ID,默认 false。 */\n persistentId?: boolean;\n storageKey?: string;\n}\n\nconst DEFAULT_DEVICE_STORAGE_KEY = 'BK_RUM_DEVICE_ID';\n// 用户拖窗口时 resize 高频触发,debounce 静默期内只在最后一次更新,避免无谓的 setRuntimeAttributes 风暴\nconst VIEWPORT_DEBOUNCE_MS = 250;\n\ninterface NetworkInformationLike {\n effectiveType?: string;\n type?: string;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ninterface UserAgentDataLike {\n mobile?: boolean;\n platform?: string;\n}\n\nconst getConnection = (): NetworkInformationLike | undefined =>\n (\n navigator as Navigator & {\n connection?: NetworkInformationLike;\n }\n ).connection;\n\nconst getUserAgentData = (): undefined | UserAgentDataLike =>\n (\n navigator as Navigator & {\n userAgentData?: UserAgentDataLike;\n }\n ).userAgentData;\n\nconst getBrowserInfo = (ua: string) => {\n const matchers: Array<[string, RegExp]> = [\n ['Edge', /Edg\\/([\\d.]+)/],\n ['Chrome', /Chrome\\/([\\d.]+)/],\n ['Firefox', /Firefox\\/([\\d.]+)/],\n ['Safari', /Version\\/([\\d.]+).*Safari/],\n ];\n for (const [name, pattern] of matchers) {\n const matched = ua.match(pattern);\n if (matched?.[1]) {\n return { name, version: matched[1].split('.')[0] };\n }\n }\n return { name: 'Other', version: undefined };\n};\n\nconst getOsName = (ua: string, platform?: string) => {\n const value = `${platform ?? ''} ${ua}`;\n if (/iphone|ipad|ipod/i.test(value)) return 'iOS';\n if (/android/i.test(value)) return 'Android';\n if (/mac/i.test(value)) return 'macOS';\n if (/win/i.test(value)) return 'Windows';\n if (/linux/i.test(value)) return 'Linux';\n return 'Other';\n};\n\nconst readDeviceId = (storageKey: string) => {\n let existed: null | string = null;\n try {\n existed = window.localStorage.getItem(storageKey);\n } catch {\n /* localStorage 不可用(隐私模式 / sandbox iframe),下面会创建一个 in-memory id */\n }\n if (existed) {\n return existed;\n }\n const deviceId = createRandomId('device');\n try {\n window.localStorage.setItem(storageKey, deviceId);\n } catch {\n /* 写不进去也行,至少本次 SDK 生命周期内 deviceId 仍然稳定 */\n }\n return deviceId;\n};\n\n// 视口尺寸:随窗口大小 / 屏幕旋转变化,需要在 resize / orientationchange 时刷新\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n return {\n 'browser.viewport.width': window.innerWidth,\n 'browser.viewport.height': window.innerHeight,\n 'browser.screen.width': window.screen?.width,\n 'browser.screen.height': window.screen?.height,\n };\n};\n\n// 网络信息:随 online / offline / connection.change 变化\nconst getNetworkAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const connection = getConnection();\n return {\n [RUM_ATTRIBUTES.networkStatus]: navigator.onLine ? 'connected' : 'not_connected',\n 'network.effective_type': connection?.effectiveType,\n 'network.connection.type': connection?.type,\n };\n};\n\n// 设备与 User Agent 信息:进入页面时一次性读取,运行期不会变化,适合作为 Resource 公共属性。\nexport const getDeviceResourceAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const uaData = getUserAgentData();\n const ua = navigator.userAgent;\n const browser = getBrowserInfo(ua);\n const osName = getOsName(ua, uaData?.platform);\n const deviceType = uaData?.mobile || /mobi|android|iphone|ipad|ipod/i.test(ua) ? 'mobile' : 'desktop';\n return {\n [RUM_ATTRIBUTES.deviceMemoryGib]: (navigator as Navigator & { deviceMemory?: number }).deviceMemory,\n [RUM_ATTRIBUTES.deviceType]: deviceType,\n [RUM_ATTRIBUTES.userAgentName]: browser.name,\n [RUM_ATTRIBUTES.userAgentVersion]: browser.version,\n [RUM_ATTRIBUTES.userAgentOsName]: osName,\n };\n};\n\n// trailing debounce:windowMs 静默期内的多次触发只执行最后一次。\n// 适合 resize 这类\"用户停下来后才需要拿到最终值\"的场景。\nconst createDebounce = (fn: () => void, windowMs: number) => {\n let timer: number | undefined;\n return () => {\n if (timer !== undefined) {\n window.clearTimeout(timer);\n }\n timer = window.setTimeout(() => {\n timer = undefined;\n fn();\n }, windowMs);\n };\n};\n\n/**\n * 设备元数据采集插件。稳定的设备能力由 SDK 写入 Resource;这里只维护设备 ID 和运行期动态属性。\n *\n * 上报字段:\n * - `device.id`:设备级永久标识,存于 localStorage(默认 key 复用旧 session 插件以保留现网用户)。\n * - `device.memory_gib`:硬件能力快照,写入 Resource。\n * - `user_agent.name` / `user_agent.version` / `user_agent.os.name`:User Agent 信息,写入 Resource。\n * - `browser.viewport.*` / `browser.screen.*`:视口与屏幕尺寸。\n * - `network.status` / `network.effective_type` / `network.connection.type`:网络信息。\n *\n * 实时更新:\n * - viewport 监听 `resize` / `orientationchange`,并经 250ms debounce 合并,避免拖窗口时高频触发;\n * 静默期结束后用最终值刷新 runtimeAttributes。\n * - network 监听 `online` / `offline` / `connection.change`(本身低频,不需要 throttle)。\n * - 二者只更新 in-memory 的 runtimeAttributes,不会主动产生新的 Span;\n * 更新后续生成的 Span 会带最新值。\n *\n * 与 session 插件的关系:device 跨会话持久(终身),session 有过期与续期(连续访问语义)。\n */\nexport const createDevicePlugin = (options: BkOTDevicePluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n return {\n name: 'device',\n init(context) {\n const storageKey =\n options.storageKey ?? `${DEFAULT_DEVICE_STORAGE_KEY}_${hashString(context.config.application.name)}`;\n const deviceId =\n typeof window === 'undefined' || options.persistentId !== true\n ? createRandomId('device')\n : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n // createDevicePlugin 作为扩展单独启用时,Resource 没有设备字段,仍回退到原有 runtime attributes 行为。\n ...(context.config.tracking.device === false ? getDeviceResourceAttributes() : {}),\n ...getViewportAttributes(),\n ...getNetworkAttributes(),\n });\n\n if (typeof window === 'undefined') {\n return;\n }\n\n const refreshViewport = createDebounce(() => {\n context.setRuntimeAttributes(getViewportAttributes());\n }, VIEWPORT_DEBOUNCE_MS);\n window.addEventListener('resize', refreshViewport);\n window.addEventListener('orientationchange', refreshViewport);\n teardownCallbacks.push(\n () => window.removeEventListener('resize', refreshViewport),\n () => window.removeEventListener('orientationchange', refreshViewport),\n );\n\n const refreshNetwork = () => {\n context.setRuntimeAttributes(getNetworkAttributes());\n };\n window.addEventListener('online', refreshNetwork);\n window.addEventListener('offline', refreshNetwork);\n teardownCallbacks.push(\n () => window.removeEventListener('online', refreshNetwork),\n () => window.removeEventListener('offline', refreshNetwork),\n );\n\n const connection = getConnection();\n // NetworkInformation 实现了 EventTarget;旧浏览器或某些 polyfill 可能没有 addEventListener,做能力检测\n if (connection && typeof connection.addEventListener === 'function') {\n connection.addEventListener('change', refreshNetwork);\n teardownCallbacks.push(() => {\n connection.removeEventListener?.('change', refreshNetwork);\n });\n }\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { version as packageVersion } from '../../package.json';\nimport { getDeviceResourceAttributes } from '../plugins/device';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTActionPluginOptions } from '../plugins/action';\nimport type { BkOTBlankScreenPluginOptions } from '../plugins/blank-screen';\nimport type { BkOTDevicePluginOptions } from '../plugins/device';\nimport type { BkOTLongTaskPluginOptions } from '../plugins/long-task';\nimport type { BkOTSessionConfig } from '../plugins/session';\nimport type { RumRequestOutcome } from '../schema/span';\nimport type { BkOTPlugin } from './plugin';\nimport type { BkOTUserContext } from './runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTActionEventPayload {\n attributes?: Attributes;\n name: string;\n}\n\nexport interface BkOTApplicationConfig {\n environment?: string;\n name: string;\n version?: string;\n}\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n page?: () => Attributes;\n}\n\nexport type BkOTAttributeValue = boolean | number | string;\n\nexport interface BkOTBatchConfig {\n exportTimeoutMillis?: number;\n maxExportBatchSize?: number;\n maxQueueSize?: number;\n scheduledDelayMillis?: number;\n}\n\nexport interface BkOTBeforeSendEvent {\n attributes: Record<string, unknown>;\n name: string;\n signal: 'span';\n}\n\nexport interface BkOTContextConfig {\n attributes?: BkOTAttributesConfig;\n user?: BkOTUserContext;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTExtensionsConfig {\n instrumentations?: Instrumentation[];\n plugins?: BkOTPlugin[];\n}\n\nexport interface BkOTOptions {\n application: BkOTApplicationConfig;\n autoStart?: boolean;\n context?: BkOTContextConfig;\n debug?: boolean;\n enabled?: boolean;\n extensions?: BkOTExtensionsConfig;\n privacy?: BkOTPrivacyConfig;\n session?: BkOTSessionConfig;\n tracking?: BkOTTrackingConfig;\n transport: BkOTTransportConfig;\n}\n\nexport interface BkOTPrivacyConfig {\n /** 初始化时的采集授权状态;运行期可通过 setTrackingConsent 更新。 */\n trackingConsent?: BkOTTrackingConsent;\n /** 在所有 RUM 自定义插件 emit 前统一过滤属性。 */\n redactAttributes?: (attributes: Attributes) => Attributes;\n /** 所有写入 attribute 的 URL 都会先经过此函数。 */\n redactUrl?: (url: string) => string;\n}\n\nexport interface BkOTRequestConfig {\n allowedTracingUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n excludedUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n beforeSend?: (event: BkOTRequestEvent, context: BkOTRequestContext) => boolean | void;\n}\n\nexport type BkOTRequestOutcome = RumRequestOutcome;\n\nexport type BkOTRequestContext =\n | { error?: unknown; request: Request | RequestInit; response?: Response; type: 'fetch' }\n | { type: 'xhr'; xhr: XMLHttpRequest };\n\nexport interface BkOTRequestEvent {\n attributes: Attributes;\n type: 'resource';\n resource: {\n method?: string;\n outcome: BkOTRequestOutcome;\n transport: 'fetch' | 'xhr';\n url?: string;\n };\n}\n\nexport type BkOTSpanProcessorMode = 'batch' | 'simple';\n\nexport interface BkOTTrackingConfig {\n action?: BkOTActionPluginOptions | boolean;\n blankScreen?: BkOTBlankScreenPluginOptions | boolean;\n cspViolation?: boolean;\n device?: BkOTDevicePluginOptions | boolean;\n error?: boolean;\n longTask?: BkOTLongTaskPluginOptions | boolean;\n request?: BkOTRequestConfig | boolean;\n resource?: boolean;\n view?: BkOTViewConfig;\n websocket?: boolean;\n webVitals?: boolean;\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n}\n\nexport type BkOTTrackingConsent = 'granted' | 'not-granted';\n\nexport interface BkOTTransportConfig {\n batch?: BkOTBatchConfig;\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n processor?: BkOTSpanProcessorMode;\n timeoutMillis?: number;\n token?: string;\n}\n\nexport type BkOTUrlMatcher = ((url: string) => boolean) | RegExp | string;\n\nexport interface BkOTViewConfig {\n /** 不参与 View loading time 的长轮询、流式请求或埋点 URL。 */\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n /** 自动计算初始加载与 SPA 路由的页面稳定耗时,默认开启。 */\n loadingTime?: boolean;\n tracking?: 'auto' | 'manual';\n /** 返回低基数路由分组,例如 `/trace/:id`。 */\n getPathGroup?: (url: string) => string | undefined;\n}\n\nexport interface ResolvedApplicationConfig {\n environment: string;\n name: string;\n version?: string;\n}\n\nexport interface ResolvedBkOTConfig {\n application: ResolvedApplicationConfig;\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n extensions: BkOTExtensionsConfig;\n hooks: ResolvedConfigHooks;\n resourceAttributes: Attributes;\n session: ResolvedSessionConfig;\n spanBatch?: BkOTBatchConfig;\n spanProcessor: BkOTSpanProcessorMode;\n tracking: BkOTTrackingConfig;\n trackingConsent: BkOTTrackingConsent;\n transport: ResolvedTransportConfig;\n user?: BkOTUserContext;\n view: ResolvedViewConfig;\n}\n\nexport interface ResolvedConfigHooks {\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n getViewPathGroup: (url: string) => string;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n}\n\nexport interface ResolvedSessionConfig extends BkOTSessionConfig {\n sampleRate: number;\n}\n\nexport interface ResolvedTransportConfig {\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n}\n\nexport interface ResolvedViewConfig {\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n loadingTime: boolean;\n tracking: 'auto' | 'manual';\n}\n\nconst trimTrailingSlash = (value: string) => value.trim().replace(/\\/+$/, '');\n\nconst resolveTransportConfig = (transport: BkOTTransportConfig): ResolvedTransportConfig => {\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n transport.token || transport.headers\n ? {\n ...(transport.token ? { Authorization: `Bearer ${transport.token}` } : {}),\n ...(transport.headers ?? {}),\n }\n : undefined;\n\n return {\n concurrencyLimit: transport.concurrencyLimit,\n endpoint: `${trimTrailingSlash(transport.endpoint)}/v1/rum`,\n headers: mergedHeaders,\n timeoutMillis: transport.timeoutMillis,\n };\n};\n\nconst clampSampleRate = (sampleRate?: number) => {\n if (typeof sampleRate !== 'number' || !Number.isFinite(sampleRate)) {\n return 1;\n }\n return Math.max(0, Math.min(1, sampleRate));\n};\n\nconst identityRedactAttributes = (attributes: Attributes) => attributes;\n\n// 默认只保留 URL 的定位信息;认证信息、查询参数和 fragment 可能携带用户数据,统一移除。\nconst defaultRedactUrl = (url: string) => {\n if (!url) return url;\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n parsed.username = '';\n parsed.password = '';\n parsed.search = '';\n parsed.hash = '';\n return parsed.href;\n } catch {\n return url.replace(/[?#].*$/, '');\n }\n};\n\nconst getDefaultPathGroup = (url: string) => {\n try {\n return new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href).pathname || '/';\n } catch {\n return url || '/';\n }\n};\n\nconst resolveApplicationConfig = (application: BkOTOptions['application']): ResolvedApplicationConfig => ({\n environment: application.environment ?? 'production',\n name: application.name,\n version: application.version,\n});\n\nconst resolveResourceAttributes = (\n application: ResolvedApplicationConfig,\n device: BkOTTrackingConfig['device'],\n sampleRate: number,\n): Attributes => ({\n [RUM_ATTRIBUTES.deploymentEnvironmentName]: application.environment,\n ...(device === false ? {} : getDeviceResourceAttributes()),\n [RUM_ATTRIBUTES.serviceName]: application.name,\n ...(application.version ? { [RUM_ATTRIBUTES.serviceVersion]: application.version } : {}),\n [RUM_ATTRIBUTES.sessionSampleRate]: sampleRate,\n 'telemetry.sdk.language': 'webjs',\n 'telemetry.sdk.name': '@blueking/open-telemetry',\n 'telemetry.sdk.version': packageVersion,\n});\n\nconst validateTransportConfig = (transport: BkOTTransportConfig | undefined) => {\n if (!transport) throw new Error('[bk-ot] transport is required.');\n if (!transport.endpoint?.trim()) throw new Error('[bk-ot] transport.endpoint is required.');\n};\n\nconst resolveHooks = (config: BkOTOptions): ResolvedConfigHooks => ({\n beforeSend: config.tracking?.beforeSend,\n getPageAttributes:\n config.context?.attributes?.page ??\n (() => ({})),\n getErrorAttributes: config.context?.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.context?.attributes?.custom ?? (() => ({})),\n getViewPathGroup: url =>\n (typeof config.tracking?.view === 'object' ? config.tracking.view.getPathGroup?.(url) : undefined) ??\n getDefaultPathGroup(url),\n redactAttributes: config.privacy?.redactAttributes ?? identityRedactAttributes,\n redactUrl: config.privacy?.redactUrl ?? defaultRedactUrl,\n});\n\nexport const resolveConfig = (config: BkOTOptions): ResolvedBkOTConfig => {\n if (!config.application?.name?.trim()) {\n throw new Error('[bk-ot] application.name is required.');\n }\n validateTransportConfig(config.transport);\n const application = resolveApplicationConfig(config.application);\n const viewConfig = typeof config.tracking?.view === 'object' ? config.tracking.view : undefined;\n const hooks = resolveHooks(config);\n const sampleRate = clampSampleRate(config.session?.sampleRate);\n\n return {\n application,\n autoStart: config.autoStart ?? true,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n extensions: {\n instrumentations: [...(config.extensions?.instrumentations ?? [])],\n plugins: [...(config.extensions?.plugins ?? [])],\n },\n hooks,\n resourceAttributes: hooks.redactAttributes(\n resolveResourceAttributes(application, config.tracking?.device, sampleRate),\n ),\n session: {\n ...config.session,\n sampleRate,\n },\n spanBatch: config.transport.batch,\n spanProcessor: config.transport.processor ?? 'batch',\n tracking: config.tracking ?? {},\n trackingConsent: config.privacy?.trackingConsent ?? 'granted',\n transport: resolveTransportConfig(config.transport),\n user: config.context?.user,\n view: {\n excludedActivityUrls: viewConfig?.excludedActivityUrls,\n loadingTime: viewConfig?.loadingTime !== false,\n tracking: viewConfig?.tracking === 'manual' ? 'manual' : 'auto',\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { ResolvedBkOTConfig } from './config';\nimport type { BkOTSessionLifecycleEvent, BkOTUserContext, BkOTViewCommand, BkOTViewContext } from './runtime-context';\nimport type { Attributes, Link, Span, SpanKind, TimeInput, Tracer } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTPlugin {\n enabled?: ((config: ResolvedBkOTConfig) => boolean) | boolean;\n name: string;\n flush?: () => Promise<void> | void;\n init: (context: BkOTRuntimeContext) => Promise<void> | void;\n shutdown?: () => Promise<void> | void;\n}\n\nexport interface BkOTRuntimeContext {\n config: ResolvedBkOTConfig;\n tracer: Tracer;\n /** 对外提供的属性脱敏入口,所有插件在 emit 前都应过一次 */\n applyRedact: (attributes: Attributes) => Attributes;\n flush: () => Promise<void>;\n getRuntimeAttributes: () => Attributes;\n getRuntimeAttributesAt: (timestamp: number) => Attributes;\n isSampled: () => boolean;\n isSessionActive: () => boolean;\n isViewActive: () => boolean;\n notifyActivity: (event: BkOTActivityEvent) => void;\n notifySessionLifecycle: (event: BkOTSessionLifecycleEvent) => void;\n onActivity: (listener: (event: BkOTActivityEvent) => void) => () => void;\n onSessionLifecycle: (listener: (event: BkOTSessionLifecycleEvent) => void) => () => void;\n onViewChange: (listener: (view: BkOTViewContext) => void) => () => void;\n onViewCommand: (listener: (command: BkOTViewCommand) => void) => () => void;\n registerInstrumentations: (instrumentations: Instrumentation[]) => void;\n setRuntimeAttributes: (attributes: Attributes, changedAt?: number) => void;\n /** 更新用户上下文;user.id 由接入方传入,SDK 原样透传 */\n setUser: (user: BkOTUserContext) => void;\n /** 更新当前 View 上下文,后续 span 会自动带上 view.* 关联字段 */\n setView: (view: BkOTViewContext, changedAt?: number) => void;\n startSpan: (name: string, attributes?: Attributes, options?: BkOTStartSpanOptions) => Span;\n}\n\n/**\n * startSpan 的扩展选项。\n *\n * 用于\"事件实际发生在过去某个时刻,但 span 现在才创建\"的场景:\n * 例如 PerformanceObserver 在条目结束后才回调,但 span 的时间窗仍需反映\n * 条目真实发生的区间,方便 trace 视图与并发请求对齐。\n *\n * startTime 接受 OT TimeInput;常用 epoch 毫秒(performance.timeOrigin + performance.now())。\n * 配合 span.end(endTime?) 可控制结束时间。\n */\nexport interface BkOTStartSpanOptions {\n /** 用于从历史中解析 Session/View/Action 归属;默认复用数字类型 startTime。 */\n contextTime?: number;\n kind?: SpanKind;\n /** 关联其他 trace 的 Span Link;OTel 要求在创建时传入。 */\n links?: Link[];\n startTime?: TimeInput;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: ResolvedBkOTConfig) => {\n if (typeof plugin.enabled === 'function') {\n return plugin.enabled(config);\n }\n return plugin.enabled !== false;\n};\n\nexport const createPlugin = (plugin: BkOTPlugin) => plugin;\n\nexport class PluginManager {\n private readonly plugins: BkOTPlugin[];\n private startedPlugins: BkOTPlugin[] = [];\n\n public constructor(plugins: BkOTPlugin[]) {\n this.plugins = plugins;\n }\n\n public async flush() {\n // 并行 flush,避免单个慢插件拖累整体\n await Promise.all(this.startedPlugins.map(plugin => Promise.resolve().then(() => plugin.flush?.())));\n }\n\n public async shutdown() {\n const plugins = [...this.startedPlugins].reverse();\n for (const plugin of plugins) {\n await plugin.shutdown?.();\n }\n this.startedPlugins = [];\n }\n\n public async start(context: BkOTRuntimeContext) {\n // 每个插件独立 try/catch,单个插件 init 失败不应拖累其他插件:\n // - 失败的插件不会进入 startedPlugins(shutdown 时不会被反向 teardown)\n // - debug 模式打 warn,方便排查;非 debug 静默失败避免污染业务日志\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n try {\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn(`[bk-ot] plugin \"${plugin.name}\" init failed:`, error);\n }\n }\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTUrlMatcher, ResolvedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nexport const toAbsoluteUrl = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).href;\n } catch {\n return url;\n }\n};\n\n// 去掉 trailing slash,便于 path 边界对齐比较\nconst stripTrailingSlash = (value: string) => (value.length > 1 ? value.replace(/\\/+$/, '') : value);\n\n// 比较两个 URL 是否指向同一资源(忽略 query/fragment 与末尾斜杠)\nconst isSameResource = (left: string, right: string) => {\n const stripped = (value: string) => stripTrailingSlash(value.replace(/[?#].*$/, ''));\n return stripped(left) === stripped(right);\n};\n\nexport const isBkOTEndpoint = (config: ResolvedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n return isSameResource(targetUrl, toAbsoluteUrl(config.transport.endpoint));\n};\n\nexport const shouldIgnoreUrl = (config: ResolvedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nconst matchesUrl = (matcher: BkOTUrlMatcher, url: string) => {\n if (typeof matcher === 'function') return matcher(url);\n if (typeof matcher === 'string') return url.includes(matcher);\n matcher.lastIndex = 0;\n return matcher.test(url);\n};\n\nexport const matchesUrlList = (matchers: BkOTUrlMatcher | BkOTUrlMatcher[] | undefined, url: string) =>\n (Array.isArray(matchers) ? matchers : matchers ? [matchers] : []).some(matcher => matchesUrl(matcher, url));\n\nconst getRequestConfig = (config: ResolvedBkOTConfig) =>\n typeof config.tracking.request === 'object' ? config.tracking.request : undefined;\n\nexport const shouldIgnoreRequest = (config: ResolvedBkOTConfig, url: string) =>\n shouldIgnoreUrl(config, url) || matchesUrlList(getRequestConfig(config)?.excludedUrls, toAbsoluteUrl(url));\n\n// Trace Header 仅向显式白名单注入,避免同源第三方路径被无意注入以及跨域 CORS 预检。\nexport const shouldPropagateTraceHeader = (config: ResolvedBkOTConfig, url: string) =>\n matchesUrlList(getRequestConfig(config)?.allowedTracingUrls, toAbsoluteUrl(url));\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { isBkOTEndpoint } from './url';\n\nimport type { ResolvedBkOTConfig } from './config';\nimport type { Attributes, Context, Span as ApiSpan } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\nconst discardedSpans = new WeakSet<object>();\n\nexport const markSpanDiscarded = (span: ApiSpan) => {\n discardedSpans.add(span);\n};\n\nconst getSpanUrl = (span: ReadableSpan): string | undefined => {\n for (const key of SELF_TELEMETRY_URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n return value;\n }\n }\n return undefined;\n};\n\nconst hasSessionId = (attributes: Record<string, unknown> | undefined) => {\n const sessionId = attributes?.[RUM_ATTRIBUTES.sessionId];\n return typeof sessionId === 'string' && sessionId.length > 0;\n};\n\nconst hasViewId = (attributes: Record<string, unknown> | undefined) => {\n const viewId = attributes?.[RUM_ATTRIBUTES.viewId];\n return typeof viewId === 'string' && viewId.length > 0;\n};\n\nconst hasRumContext = (attributes: Record<string, unknown> | undefined) =>\n hasSessionId(attributes) && hasViewId(attributes);\n\n/**\n * 给所有 span 固化创建时的运行时上下文,并在 onEnd 阶段过滤自身上报请求和无 RUM 上下文 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: ResolvedBkOTConfig,\n private readonly getRuntimeAttributes: () => Attributes,\n ) {}\n\n public forceFlush(): Promise<void> {\n return this.inner.forceFlush();\n }\n\n public onEnd(span: ReadableSpan): void {\n const url = getSpanUrl(span);\n if (\n discardedSpans.delete(span) ||\n (url && isBkOTEndpoint(this.config, url)) ||\n (!hasRumContext(span.attributes) &&\n !(span.name === 'browser.session' && hasSessionId(span.attributes)))\n ) {\n return;\n }\n try {\n if (\n this.config.hooks?.beforeSend?.({\n attributes: span.attributes,\n name: span.name,\n signal: 'span',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\n const existingAttributes = (span as Span & { attributes?: Attributes }).attributes ?? {};\n const missingRuntimeAttributes: Attributes = {};\n // instrumentation 显式写入的属性优先,只补齐创建时缺失的运行时上下文。\n for (const [key, value] of Object.entries(this.getRuntimeAttributes())) {\n if (existingAttributes[key] === undefined && value !== undefined) missingRuntimeAttributes[key] = value;\n }\n span.setAttributes(missingRuntimeAttributes);\n this.inner.onStart(span, parentContext);\n }\n\n public shutdown(): Promise<void> {\n return this.inner.shutdown();\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nexport type BkOTSessionLifecycleEvent =\n | {\n expiredAt: number;\n id: string;\n reason: 'external' | 'inactivity' | 'maxLifetime';\n type: 'expired';\n }\n | {\n id: string;\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n startedAt: number;\n type: 'renewed';\n };\n\nexport interface BkOTStartViewOptions {\n attributes?: Attributes;\n name?: string;\n url?: string;\n}\n\nexport interface BkOTUserContext {\n /** 由接入方传入并由 SDK 原样透传;SDK 不 hash、不脱敏、不改写。 */\n id?: string;\n}\n\nexport type BkOTViewCommand =\n | { name: string; type: 'set_name' }\n | { options: BkOTStartViewOptions; type: 'start' }\n | { time: number; type: 'set_loading_time' };\n\nexport interface BkOTViewContext {\n attributes?: Attributes;\n id?: string;\n loadingType?: 'bf_cache' | 'initial_load' | 'route_change' | 'session_renewal';\n name?: string;\n startedAt?: number;\n url?: string;\n urlPathGroup?: string;\n}\n\nconst PROTECTED_VIEW_ATTRIBUTE_KEYS = new Set<string>(Object.values(RUM_ATTRIBUTES));\n\n/**\n * 管理 SDK 运行期公共属性快照。\n *\n * 这里仅保存“后续事件都需要带上”的低成本上下文,Session/View/用户切换时更新快照,\n * 插件发 Span 时读取当前快照即可,避免每个插件重复维护公共字段。\n */\nexport class RumRuntimeContext {\n private readonly actionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly attributes: Attributes = {};\n private readonly queuedViewCommands: BkOTViewCommand[] = [];\n\n // 保存关联关系的生效区间,供延迟到达的 PerformanceEntry 按事件时间回查原 Session/View/Action。\n private recordAssociation = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n changedAt: number,\n active: boolean,\n ) => {\n const previous = history.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n if (active) {\n const associationAttributes: Attributes = {};\n for (const key of keys) {\n const value = this.attributes[key];\n if (value !== undefined) associationAttributes[key] = value;\n }\n history.push({ attributes: associationAttributes, startedAt: changedAt });\n }\n if (history.length > 100) history.splice(0, history.length - 100);\n };\n private readonly sessionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private sampled = false;\n private readonly samplingHistory: Array<{ endedAt?: number; sampled: boolean; startedAt: number }> = [];\n private readonly sessionLifecycleListeners = new Set<(event: BkOTSessionLifecycleEvent) => void>();\n private readonly setOptionalAttribute = (key: string, value: string | undefined) => {\n if (value === undefined) {\n delete this.attributes[key];\n return;\n }\n this.attributes[key] = value;\n };\n private user: BkOTUserContext = {};\n private view: BkOTViewContext = {};\n private readonly viewAttributeKeys = new Set<string>();\n private readonly viewChangeListeners = new Set<(view: BkOTViewContext) => void>();\n private readonly viewCommandListeners = new Set<(command: BkOTViewCommand) => void>();\n\n private readonly viewHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n\n private readonly viewScopedAttributeKeys = new Set<string>();\n\n public dispatchViewCommand = (command: BkOTViewCommand) => {\n // autoStart 下调用方可能早于 page-view 插件发命令,先排队可避免 startView/setViewName 丢失。\n if (this.viewCommandListeners.size === 0) {\n this.queuedViewCommands.push(command);\n return;\n }\n this.viewCommandListeners.forEach(listener => listener(command));\n };\n\n public getAttributes = (): Attributes => ({ ...this.attributes });\n\n public getAttributesAt = (timestamp: number): Attributes => {\n if (!Number.isFinite(timestamp)) return this.getAttributes();\n const result = this.getAttributes();\n const applyHistory = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n ) => {\n // 先移除当前关联字段,再用目标时间所在的历史快照覆盖,避免把旧事件错误归到当前 View。\n for (const key of keys) delete result[key];\n const matched = [...history]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt));\n if (matched) Object.assign(result, matched.attributes);\n };\n applyHistory(this.sessionHistory, [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay]);\n applyHistory(this.viewHistory, this.viewScopedAttributeKeys);\n applyHistory(this.actionHistory, [RUM_ATTRIBUTES.actionId]);\n return result;\n };\n\n public getUser = (): BkOTUserContext => ({ ...this.user });\n\n public getView = (): BkOTViewContext => ({ ...this.view });\n\n public isSessionActive = () => typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string';\n\n public isSessionSampledAt = (timestamp: number) => {\n if (!Number.isFinite(timestamp)) return this.sampled;\n return (\n [...this.samplingHistory]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt))\n ?.sampled ?? false\n );\n };\n\n public isViewActive = () => typeof this.attributes[RUM_ATTRIBUTES.viewId] === 'string';\n\n public notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.sessionLifecycleListeners.forEach(listener => listener(event));\n };\n\n public onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) => {\n this.sessionLifecycleListeners.add(listener);\n return () => this.sessionLifecycleListeners.delete(listener);\n };\n\n public onViewChange = (listener: (view: BkOTViewContext) => void) => {\n this.viewChangeListeners.add(listener);\n if (this.view.id) listener(this.getView());\n return () => this.viewChangeListeners.delete(listener);\n };\n\n public onViewCommand = (listener: (command: BkOTViewCommand) => void) => {\n this.viewCommandListeners.add(listener);\n while (this.queuedViewCommands.length) {\n listener(this.queuedViewCommands.shift()!);\n }\n return () => this.viewCommandListeners.delete(listener);\n };\n\n public setAttributes = (attributes: Attributes, changedAt = Date.now()) => {\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined) {\n delete this.attributes[key];\n continue;\n }\n this.attributes[key] = value;\n }\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n this.recordAssociation(\n this.sessionHistory,\n [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay],\n changedAt,\n typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string',\n );\n }\n if (RUM_ATTRIBUTES.actionId in attributes) {\n const actionId = this.attributes[RUM_ATTRIBUTES.actionId];\n this.recordAssociation(\n this.actionHistory,\n [RUM_ATTRIBUTES.actionId],\n changedAt,\n Array.isArray(actionId) ? actionId.length > 0 : typeof actionId === 'string',\n );\n }\n };\n\n public setUser = (user: BkOTUserContext) => {\n this.user = {\n ...this.user,\n ...user,\n };\n\n if ('id' in user) {\n if (user.id === undefined) {\n delete this.attributes[RUM_ATTRIBUTES.userId];\n return;\n }\n this.attributes[RUM_ATTRIBUTES.userId] = user.id;\n }\n };\n\n public setSessionSampled = (sampled: boolean, changedAt = Date.now()) => {\n if (sampled === this.sampled) return;\n const previous = this.samplingHistory.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n this.sampled = sampled;\n if (sampled) this.samplingHistory.push({ sampled, startedAt: changedAt });\n if (this.samplingHistory.length > 100) {\n this.samplingHistory.splice(0, this.samplingHistory.length - 100);\n }\n };\n\n public setView = (view: BkOTViewContext, changedAt = view.startedAt ?? Date.now()) => {\n this.viewAttributeKeys.forEach(key => delete this.attributes[key]);\n this.viewAttributeKeys.clear();\n const viewAttributes: Attributes = {};\n for (const [key, value] of Object.entries(view.attributes ?? {})) {\n // 自定义 View 属性不能覆盖 SDK 维护的关联和语义字段。\n if (value === undefined || PROTECTED_VIEW_ATTRIBUTE_KEYS.has(key)) continue;\n viewAttributes[key] = value;\n this.attributes[key] = value;\n this.viewAttributeKeys.add(key);\n this.viewScopedAttributeKeys.add(key);\n }\n this.view = { ...view, attributes: viewAttributes };\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewId, view.id);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrl, view.url);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrlPathGroup, view.urlPathGroup);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewLoadingType, view.loadingType);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewName, view.name);\n [\n RUM_ATTRIBUTES.viewId,\n RUM_ATTRIBUTES.viewUrl,\n RUM_ATTRIBUTES.viewUrlPathGroup,\n RUM_ATTRIBUTES.viewLoadingType,\n RUM_ATTRIBUTES.viewName,\n ].forEach(key => this.viewScopedAttributeKeys.add(key));\n this.recordAssociation(\n this.viewHistory,\n this.viewScopedAttributeKeys,\n changedAt,\n typeof view.id === 'string' && view.id.length > 0,\n );\n this.viewChangeListeners.forEach(listener => listener(this.getView()));\n };\n\n public constructor(initialAttributes: Attributes = {}, user?: BkOTUserContext) {\n this.setAttributes(initialAttributes);\n if (user) {\n this.setUser(user);\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from './attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\n// 所有插件通过同一分类模型生成检索字段,保证错误、资源、Action 等信号可按统一维度聚合。\nexport const RUM_SPAN_OUTCOME = {\n abort: 'abort',\n error: 'error',\n success: 'success',\n timeout: 'timeout',\n warning: 'warning',\n} as const;\n\nexport const RUM_SPAN_TYPE = {\n action: 'action',\n custom: 'custom',\n error: 'error',\n longTask: 'long_task',\n resource: 'resource',\n session: 'session',\n vital: 'vital',\n view: 'view',\n websocket: 'websocket',\n} as const;\n\nexport type RumRequestOutcome =\n | { error?: unknown; type: 'network_error' }\n | { error?: unknown; type: 'timeout' }\n | { statusCode?: number; type: 'response' }\n | { type: 'abort' };\n\nexport interface RumResourceSpanInput {\n initiatorType?: string;\n method?: string;\n outcome?: RumRequestOutcome;\n url: string;\n}\n\nexport interface RumSpanClassification {\n outcome?: RumSpanOutcome;\n outcomeReason?: string;\n spanType: RumSpanType;\n}\n\nexport type RumSpanOutcome = (typeof RUM_SPAN_OUTCOME)[keyof typeof RUM_SPAN_OUTCOME];\n\nexport type RumSpanType = (typeof RUM_SPAN_TYPE)[keyof typeof RUM_SPAN_TYPE];\n\nexport interface RumUrlTarget {\n serverAddress?: string;\n serverPort?: number;\n}\n\nexport const getHttpOutcome = (statusCode: number | undefined): RumSpanOutcome => {\n if (statusCode === 408 || statusCode === 504) return RUM_SPAN_OUTCOME.timeout;\n if (typeof statusCode === 'number' && statusCode >= 400) return RUM_SPAN_OUTCOME.error;\n return RUM_SPAN_OUTCOME.success;\n};\n\nexport const getHttpOutcomeReason = (statusCode: number | undefined) => {\n if (statusCode === 408 || statusCode === 504) return 'network_timeout';\n if (typeof statusCode !== 'number' || statusCode < 400) return undefined;\n return statusCode >= 500 ? 'http_5xx' : 'http_4xx';\n};\n\nexport const getVitalOutcome = (rating: string | undefined): RumSpanOutcome =>\n rating === 'good' ? RUM_SPAN_OUTCOME.success : RUM_SPAN_OUTCOME.warning;\n\nexport const getUrlTarget = (url: string | undefined): RumUrlTarget => {\n if (!url) {\n return {};\n }\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n return {\n serverAddress: parsed.hostname,\n serverPort: parsed.port ? Number(parsed.port) : undefined,\n };\n } catch {\n return {};\n }\n};\n\nexport const buildRumSpanAttributes = ({\n outcome = RUM_SPAN_OUTCOME.success,\n outcomeReason,\n spanType,\n}: RumSpanClassification): Attributes => ({\n [RUM_ATTRIBUTES.spanType]: spanType,\n [RUM_ATTRIBUTES.outcomeType]: outcome,\n ...(outcomeReason ? { [RUM_ATTRIBUTES.outcomeReason]: outcomeReason } : {}),\n});\n\nexport const buildRumResourceSpanAttributes = ({\n initiatorType = 'other',\n method,\n outcome,\n url,\n}: RumResourceSpanInput): Attributes => {\n const target = getUrlTarget(url);\n const responseStatus =\n outcome?.type === 'response' && typeof outcome.statusCode === 'number' && outcome.statusCode > 0\n ? outcome.statusCode\n : undefined;\n const outcomeType =\n outcome?.type === 'abort'\n ? RUM_SPAN_OUTCOME.abort\n : outcome?.type === 'timeout'\n ? RUM_SPAN_OUTCOME.timeout\n : outcome?.type === 'network_error'\n ? RUM_SPAN_OUTCOME.error\n : typeof responseStatus === 'number'\n ? getHttpOutcome(responseStatus)\n : RUM_SPAN_OUTCOME.success;\n const outcomeReason =\n outcome?.type === 'timeout'\n ? 'network_timeout'\n : outcome?.type === 'network_error'\n ? 'network'\n : typeof responseStatus === 'number'\n ? getHttpOutcomeReason(responseStatus)\n : undefined;\n const normalizedMethod = method?.toUpperCase();\n return {\n ...buildRumSpanAttributes({\n outcome: outcomeType,\n outcomeReason,\n spanType: RUM_SPAN_TYPE.resource,\n }),\n ...(target.serverAddress ? { 'server.address': target.serverAddress } : {}),\n ...(target.serverPort !== undefined ? { 'server.port': target.serverPort } : {}),\n 'resource.type': initiatorType,\n ...(normalizedMethod ? { 'http.request.method': normalizedMethod } : {}),\n ...(typeof responseStatus === 'number' ? { 'http.response.status_code': responseStatus } : {}),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createRandomId } from '../core/id';\nimport { matchesUrlList } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTActivityEvent } from '../core/action-activity';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 用户点击采集以“点击后产生的副作用”为 Action 生命周期:关联请求、资源、长任务、DOM 变化和错误,\n * 在活动安静后结束。完成后的 Action 暂缓至连击窗口结束再上报,以便把三次相近点击合并为一次 rage click。\n */\nconst ACTION_VALIDATION_DELAY = 100;\nconst ACTION_END_DELAY = 100;\nconst CLICK_CHAIN_DURATION = 1_000;\nconst CLICK_CHAIN_MAX_DISTANCE = 100;\nconst DEFAULT_ACTION_MAX_DURATION = 10_000;\nconst DEFAULT_ACTION_NAME_ATTRIBUTE = 'data-bk-action-name';\nconst MAX_NAME_LENGTH = 100;\nconst MAX_PARENTS_TO_CONSIDER = 10;\n\nexport interface BkOTActionPluginOptions {\n actionNameAttribute?: string;\n maxDuration?: number;\n /** 默认仅采集显式 data 属性;设为 text 才会读取可访问名称和 DOM 文本。 */\n nameCollection?: 'explicit' | 'text';\n}\n\ntype ActionFrustrationType = 'dead_click' | 'error_click' | 'rage_click';\n\ninterface ActiveAction {\n completed?: { endTime: number; hasLoadingTime: boolean };\n frustrations: Set<ActionFrustrationType>;\n hadActivity: boolean;\n hadError: boolean;\n id: string;\n lastActivityTime: number;\n maxDurationTimer: ReturnType<typeof setTimeout>;\n pendingRequests: Set<string>;\n quietTimer?: ReturnType<typeof setTimeout>;\n reportTimer?: ReturnType<typeof setTimeout>;\n runtimeAttributes: Attributes;\n startTime: number;\n target: Element;\n targetName: string;\n targetTag: string;\n userActivity: { input: boolean; scroll: boolean; selection: boolean };\n validationTimer: ReturnType<typeof setTimeout>;\n}\n\ninterface RecentClick {\n action: ActiveAction;\n target: Element;\n x: number;\n y: number;\n}\n\nconst normalizeText = (value: string) => value.replace(/\\s+/g, ' ').trim().slice(0, MAX_NAME_LENGTH);\n\nconst getParentElement = (element: Element) => {\n if (element.parentElement) {\n return element.parentElement;\n }\n const root = element.getRootNode?.();\n return typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.host : null;\n};\n\nconst findAttributeOnAncestors = (element: Element, attribute: string) => {\n let current: Element | null = element;\n while (current) {\n const value = current.getAttribute(attribute);\n if (value) {\n return normalizeText(value);\n }\n current = getParentElement(current);\n }\n return undefined;\n};\n\nconst getElementText = (element: Element) =>\n (element as HTMLElement).isContentEditable ? '' : normalizeText(element.textContent ?? '');\n\nconst getPriorityName = (element: Element) => {\n const formElement = element as Element & {\n labels?: HTMLLabelElement[] | NodeListOf<HTMLLabelElement>;\n value?: string;\n };\n const label = formElement.labels?.[0];\n if (label) {\n const labelText = getElementText(label);\n if (labelText) return labelText;\n }\n\n const tagName = element.tagName.toLowerCase();\n const inputType = element.getAttribute('type')?.toLowerCase();\n if (tagName === 'input' && ['button', 'reset', 'submit'].includes(inputType ?? '')) {\n const value = normalizeText(formElement.value ?? '');\n if (value) return value;\n }\n\n if (tagName === 'button' || tagName === 'label' || element.getAttribute('role') === 'button') {\n const text = getElementText(element);\n if (text) return text;\n }\n\n for (const attribute of ['aria-label', 'alt', 'name', 'title', 'placeholder']) {\n const value = normalizeText(element.getAttribute(attribute) ?? '');\n if (value) return value;\n }\n\n return undefined;\n};\n\nexport const getActionNameFromElement = (\n target: Element,\n actionNameAttribute = DEFAULT_ACTION_NAME_ATTRIBUTE,\n includeText = false,\n): string => {\n const explicitName = findAttributeOnAncestors(target, actionNameAttribute);\n if (explicitName) {\n return explicitName;\n }\n if (!includeText) return target.tagName.toLowerCase();\n\n let current: Element | null = target;\n let parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const priorityName = getPriorityName(current);\n if (priorityName) return priorityName;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n current = target;\n parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const text = getElementText(current);\n if (text) return text;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n return target.tagName.toLowerCase();\n};\n\nconst getElement = (event: Event) => {\n const composedTarget = event.composedPath?.()[0];\n if (composedTarget instanceof Element) return composedTarget;\n return event.target instanceof Element ? event.target : undefined;\n};\n\nconst toEpochTime = (event: Event) => {\n const eventTime = event.timeStamp;\n if (!Number.isFinite(eventTime) || eventTime <= 0) return Date.now();\n if (eventTime > 1_000_000_000_000) return eventTime;\n return typeof performance === 'undefined' ? Date.now() : performance.timeOrigin + eventTime;\n};\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\n\nexport const isDeadClickExcluded = (target: Element) => {\n let current: Element | null = target;\n if (current.tagName.toLowerCase() === 'label' && current.hasAttribute('for')) {\n current = document.getElementById(current.getAttribute('for') ?? '');\n }\n\n while (current) {\n const tagName = current.tagName.toLowerCase();\n if (\n current.hasAttribute('contenteditable') ||\n tagName === 'canvas' ||\n tagName === 'select' ||\n tagName === 'textarea'\n ) {\n return true;\n }\n if (tagName === 'a' && current.hasAttribute('href')) return true;\n if (tagName === 'input') {\n return !['button', 'checkbox', 'radio', 'range', 'reset', 'submit'].includes(\n current.getAttribute('type')?.toLowerCase() ?? 'text',\n );\n }\n current = getParentElement(current);\n }\n return false;\n};\n\nexport const createActionPlugin = (options: BkOTActionPluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeActions = new Map<string, ActiveAction>();\n const pendingActions = new Set<ActiveAction>();\n const actionNameAttribute = options.actionNameAttribute || DEFAULT_ACTION_NAME_ATTRIBUTE;\n const maxDuration = positiveNumber(options.maxDuration, DEFAULT_ACTION_MAX_DURATION);\n let recentClicks: RecentClick[] = [];\n let runtimeContext: BkOTRuntimeContext | undefined;\n\n const updateRuntimeActionIds = () => {\n runtimeContext?.setRuntimeAttributes({\n [RUM_ATTRIBUTES.actionId]: activeActions.size ? [...activeActions.keys()] : undefined,\n });\n };\n\n const isDeadAction = (action: ActiveAction) =>\n !action.hadActivity &&\n !action.userActivity.input &&\n !action.userActivity.scroll &&\n !action.userActivity.selection &&\n !isDeadClickExcluded(action.target);\n\n const reportAction = (action: ActiveAction) => {\n if (!pendingActions.delete(action) || !runtimeContext || !action.completed) return;\n clearTimeout(action.reportTimer);\n if (action.hadError) action.frustrations.add('error_click');\n if (isDeadAction(action)) action.frustrations.add('dead_click');\n const frustrationTypes = ['rage_click', 'dead_click', 'error_click'].filter(type =>\n action.frustrations.has(type as ActionFrustrationType),\n ) as ActionFrustrationType[];\n const { endTime, hasLoadingTime } = action.completed;\n const loadingTime = Math.max(0, endTime - action.startTime);\n const hasWarning = action.hadError || frustrationTypes.length > 0;\n const span = runtimeContext.startSpan(\n 'action.click',\n {\n ...runtimeContext.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n outcome: hasWarning ? RUM_SPAN_OUTCOME.warning : RUM_SPAN_OUTCOME.success,\n spanType: RUM_SPAN_TYPE.action,\n }),\n ...action.runtimeAttributes,\n [RUM_ATTRIBUTES.actionId]: action.id,\n [RUM_ATTRIBUTES.actionType]: 'click',\n [RUM_ATTRIBUTES.actionName]: `Click on ${action.targetName}`,\n [RUM_ATTRIBUTES.actionTargetName]: action.targetName,\n [RUM_ATTRIBUTES.actionTargetTag]: action.targetTag,\n ...(frustrationTypes.length ? { [RUM_ATTRIBUTES.actionFrustrationType]: frustrationTypes } : {}),\n ...(hasLoadingTime ? { [RUM_ATTRIBUTES.actionLoadingTime]: loadingTime } : {}),\n },\n { startTime: action.startTime },\n );\n span.end(hasLoadingTime ? endTime : action.startTime);\n };\n\n const finishAction = (action: ActiveAction, endTime: number, hasLoadingTime: boolean) => {\n if (!activeActions.delete(action.id) || !runtimeContext) return;\n clearTimeout(action.validationTimer);\n clearTimeout(action.quietTimer);\n clearTimeout(action.maxDurationTimer);\n updateRuntimeActionIds();\n action.completed = { endTime, hasLoadingTime };\n pendingActions.add(action);\n // 等待连击窗口关闭后再落数,否则前两次点击已上报便无法合并为 rage click。\n const reportDelay = Math.max(0, action.startTime + CLICK_CHAIN_DURATION - Date.now());\n action.reportTimer = setTimeout(() => reportAction(action), reportDelay);\n };\n\n const flushPendingActions = () => {\n [...pendingActions].forEach(reportAction);\n };\n\n const scheduleActionEnd = (action: ActiveAction) => {\n clearTimeout(action.quietTimer);\n if (!action.hadActivity || action.pendingRequests.size) return;\n const delay = Math.max(0, action.lastActivityTime + ACTION_END_DELAY - Date.now());\n action.quietTimer = setTimeout(() => finishAction(action, action.lastActivityTime, true), delay);\n };\n\n const markActivity = (action: ActiveAction, time: number) => {\n action.hadActivity = true;\n action.lastActivityTime = Math.max(action.lastActivityTime, time);\n scheduleActionEnd(action);\n };\n\n const isInActionWindow = (action: ActiveAction, time: number) =>\n time >= action.startTime && time <= action.startTime + maxDuration;\n\n const onActivity = (event: BkOTActivityEvent) => {\n for (const action of activeActions.values()) {\n if (event.type === 'request_start') {\n if (event.url && matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)) continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.pendingRequests.add(event.id);\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'request_end') {\n if (!action.pendingRequests.delete(event.id)) continue;\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'long_task' || event.type === 'resource') {\n if (\n event.type === 'resource' &&\n event.url &&\n matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)\n ) {\n continue;\n }\n if (\n event.startTime > action.startTime + maxDuration ||\n (event.type === 'resource' ? event.startTime < action.startTime : event.endTime < action.startTime)\n ) {\n continue;\n }\n markActivity(action, event.endTime);\n continue;\n }\n if (event.type === 'dom_mutation' || event.type === 'window_open') {\n if (isInActionWindow(action, event.time)) markActivity(action, event.time);\n continue;\n }\n if (event.type !== 'error') continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.hadError = true;\n markActivity(action, event.time);\n }\n };\n\n const finishActiveActions = (endTime = Date.now()) => {\n [...activeActions.values()].forEach(action => finishAction(action, endTime, action.hadActivity));\n };\n\n const registerClick = (action: ActiveAction, event: Event) => {\n const x = 'clientX' in event && typeof event.clientX === 'number' ? event.clientX : 0;\n const y = 'clientY' in event && typeof event.clientY === 'number' ? event.clientY : 0;\n recentClicks = recentClicks.filter(click => action.startTime - click.action.startTime <= CLICK_CHAIN_DURATION);\n const lastClick = recentClicks.at(-1);\n const hasInterruptedChain = recentClicks.some(\n click => click.action.userActivity.scroll || click.action.userActivity.selection,\n );\n // 只有同一目标附近、未被滚动或选中文本打断的连续点击才视为同一连击链。\n const isSimilar =\n lastClick?.target === action.target && Math.hypot(lastClick.x - x, lastClick.y - y) <= CLICK_CHAIN_MAX_DISTANCE;\n if (hasInterruptedChain || (lastClick && !isSimilar)) recentClicks = [];\n recentClicks.push({ action, target: action.target, x, y });\n if (\n recentClicks.length >= 3 &&\n action.startTime - recentClicks[recentClicks.length - 3].action.startTime <= CLICK_CHAIN_DURATION\n ) {\n const rageSources = recentClicks.slice(-3).map(click => click.action);\n rageSources.forEach(source => source.frustrations.add('rage_click'));\n recentClicks = [];\n }\n };\n\n return {\n name: 'action',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n runtimeContext = context;\n\n const listenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n\n const startAction = (event: Event) => {\n if (!context.isSampled()) return;\n if ('button' in event && typeof event.button === 'number' && event.button !== 0) return;\n const element = getElement(event);\n if (!element) return;\n const startTime = toEpochTime(event);\n const id = createRandomId('action');\n const action: ActiveAction = {\n frustrations: new Set(),\n hadActivity: false,\n hadError: false,\n id,\n lastActivityTime: startTime,\n maxDurationTimer: setTimeout(() => finishAction(action, startTime + maxDuration, true), maxDuration),\n pendingRequests: new Set(),\n runtimeAttributes: context.getRuntimeAttributes(),\n startTime,\n target: element,\n targetName: getActionNameFromElement(element, actionNameAttribute, options.nameCollection === 'text'),\n targetTag: element.tagName.toLowerCase(),\n userActivity: { input: false, scroll: false, selection: false },\n validationTimer: setTimeout(() => {\n if (!action.hadActivity) finishAction(action, action.startTime, false);\n }, ACTION_VALIDATION_DELAY),\n };\n activeActions.set(id, action);\n registerClick(action, event);\n updateRuntimeActionIds();\n };\n\n window.addEventListener('click', startAction, listenerOptions);\n teardownCallbacks.push(() => window.removeEventListener('click', startAction, listenerOptions));\n\n const unsubscribeActivity = context.onActivity(onActivity);\n const unsubscribeSession = context.onSessionLifecycle(event => {\n if (event.type !== 'expired') return;\n finishActiveActions(event.expiredAt);\n flushPendingActions();\n });\n teardownCallbacks.push(unsubscribeActivity, unsubscribeSession);\n\n const finishOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n finishActiveActions();\n flushPendingActions();\n }\n };\n const finishOnPageHide = () => {\n finishActiveActions();\n flushPendingActions();\n };\n window.addEventListener('pagehide', finishOnPageHide);\n document.addEventListener('visibilitychange', finishOnHidden);\n teardownCallbacks.push(\n () => window.removeEventListener('pagehide', finishOnPageHide),\n () => document.removeEventListener('visibilitychange', finishOnHidden),\n );\n\n const markUserActivity = (type: keyof ActiveAction['userActivity']) => {\n const actions = new Set([\n ...activeActions.values(),\n ...pendingActions,\n ...recentClicks.map(click => click.action),\n ]);\n actions.forEach(action => {\n action.userActivity[type] = true;\n // 滚动/选择表明用户仍在正常交互,撤销尚未上报的 rage click 判定。\n if (type !== 'input') action.frustrations.delete('rage_click');\n });\n };\n const onInput = () => markUserActivity('input');\n const onScroll = () => markUserActivity('scroll');\n const onSelectionChange = () => markUserActivity('selection');\n window.addEventListener('input', onInput, listenerOptions);\n window.addEventListener('scroll', onScroll, listenerOptions);\n document.addEventListener('selectionchange', onSelectionChange, listenerOptions);\n teardownCallbacks.push(\n () => window.removeEventListener('input', onInput, listenerOptions),\n () => window.removeEventListener('scroll', onScroll, listenerOptions),\n () => document.removeEventListener('selectionchange', onSelectionChange, listenerOptions),\n );\n },\n shutdown() {\n finishActiveActions();\n flushPendingActions();\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n recentClicks = [];\n runtimeContext = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nexport const EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE = 'data-bk-excluded-activity-mutations';\n\nconst isExcludedMutation = (mutation: MutationRecord) => {\n const target = mutation.type === 'characterData' ? mutation.target.parentElement : mutation.target;\n return target instanceof Element && Boolean(target.closest(`[${EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE}]`));\n};\n\n/**\n * 页面级 DOM 活动只观察一次,再通过运行时活动总线分发给 View、Action 和白屏检测。\n */\nexport const createActivityPlugin = (): BkOTPlugin => {\n let mutationObserver: MutationObserver | undefined;\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'activity',\n init(context) {\n if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {\n mutationObserver = new MutationObserver(mutations => {\n if (mutations.length && !mutations.every(isExcludedMutation)) {\n context.notifyActivity({ time: Date.now(), type: 'dom_mutation' });\n }\n });\n mutationObserver.observe(document, { attributes: true, characterData: true, childList: true, subtree: true });\n }\n\n if (typeof window === 'undefined') return;\n\n if (typeof window.open === 'function') {\n const originalOpen = window.open;\n const patchedOpen = ((...args: Parameters<typeof window.open>) => {\n context.notifyActivity({ time: Date.now(), type: 'window_open' });\n return Reflect.apply(originalOpen, window, args);\n }) as typeof window.open;\n window.open = patchedOpen;\n teardownCallbacks.push(() => {\n if (window.open === patchedOpen) window.open = originalOpen;\n });\n }\n\n },\n shutdown() {\n mutationObserver?.disconnect();\n mutationObserver = undefined;\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { matchesUrlList } from './url';\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { BkOTRuntimeContext } from './plugin';\n\nexport const PAGE_ACTIVITY_VALIDATION_DELAY = 100;\nexport const PAGE_ACTIVITY_END_DELAY = 100;\n\nexport type PageActivityEnd = { endTime: number; hadActivity: true } | { hadActivity: false };\n\nconst isActivityUrlExcluded = (context: BkOTRuntimeContext, url: string | undefined) =>\n Boolean(url && matchesUrlList(context.config.view.excludedActivityUrls, url));\n\n/**\n * 等待 View/Action 启动后的请求、静态资源和 DOM 变更全部安静 100ms。\n * 请求用 id 精确配对,避免并发请求提前结束等待。\n */\nexport const waitForPageActivityEnd = (\n context: BkOTRuntimeContext,\n startTime: number,\n callback: (result: PageActivityEnd) => void,\n) => {\n const pendingRequests = new Set<string>();\n let completed = false;\n let hadActivity = false;\n let lastActivityTime = startTime;\n let quietTimer: ReturnType<typeof setTimeout> | undefined;\n\n const complete = (result: PageActivityEnd) => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n callback(result);\n };\n\n const scheduleEnd = () => {\n clearTimeout(quietTimer);\n if (!hadActivity || pendingRequests.size) return;\n quietTimer = setTimeout(\n () => complete({ endTime: lastActivityTime, hadActivity: true }),\n Math.max(0, lastActivityTime + PAGE_ACTIVITY_END_DELAY - Date.now()),\n );\n };\n\n const markActivity = (time: number) => {\n hadActivity = true;\n lastActivityTime = Math.max(lastActivityTime, time);\n scheduleEnd();\n };\n\n const onActivity = (event: BkOTActivityEvent) => {\n if (event.type === 'error' || event.type === 'long_task') return;\n if (event.type === 'request_start') {\n if (event.time < startTime || isActivityUrlExcluded(context, event.url)) return;\n pendingRequests.add(event.id);\n markActivity(event.time);\n return;\n }\n if (event.type === 'request_end') {\n if (!pendingRequests.delete(event.id)) return;\n markActivity(event.time);\n return;\n }\n if (event.type === 'resource') {\n if (event.endTime < startTime || isActivityUrlExcluded(context, event.url)) return;\n markActivity(event.endTime);\n return;\n }\n if (event.time >= startTime) markActivity(event.time);\n };\n\n const unsubscribe = context.onActivity(onActivity);\n const validationTimer = setTimeout(() => {\n if (!hadActivity) complete({ hadActivity: false });\n }, PAGE_ACTIVITY_VALIDATION_DELAY);\n\n return () => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 通过视口九宫格采样估算空白比例,并在页面稳定后连续确认两次。每个 View 最多上报一次,\n * 避免首屏渲染、路由切换和 loading 状态造成瞬时误报。\n */\nexport interface BkOTBlankScreenPluginOptions {\n /** 页面活动稳定后首次检查的延迟,默认 1000ms。 */\n checkDelay?: number;\n /** 两次空白结果之间的确认间隔,默认 200ms。 */\n confirmationDelay?: number;\n /** loading、骨架屏等不参与空白比例计算的元素。 */\n ignoreSelectors?: string[];\n /** 应用挂载根节点,默认 body。根节点不存在时回退到 body 检测。 */\n rootSelector?: string;\n /** 空白采样点比例阈值,默认 0.8。 */\n threshold?: number;\n}\n\ninterface DetectionResult {\n centerElement: string;\n emptyCount: number;\n ignoredCount: number;\n reason: 'empty_viewport' | 'missing_root';\n rootFound: boolean;\n score: number;\n validCount: number;\n}\n\nconst DEFAULT_CHECK_DELAY = 1000;\nconst DEFAULT_CONFIRMATION_DELAY = 200;\nconst DEFAULT_ROOT_SELECTOR = 'body';\nconst DEFAULT_THRESHOLD = 0.8;\nconst DEFAULT_IGNORED_SELECTORS = [\n '[data-loading=\"true\"]',\n '[aria-busy=\"true\"]',\n '.loading',\n '.is-loading',\n '.spinner',\n '.skeleton',\n '.bk-loading',\n];\n\nconst SAMPLE_POINTS: ReadonlyArray<readonly [number, number]> = [\n [0.2, 0.2],\n [0.5, 0.2],\n [0.8, 0.2],\n [0.2, 0.5],\n [0.5, 0.5],\n [0.8, 0.5],\n [0.2, 0.8],\n [0.5, 0.8],\n [0.8, 0.8],\n];\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst boundedRatio = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : fallback;\n\nconst getElementSelector = (element: Element | undefined) => {\n if (!element) return '';\n const tag = element.tagName.toLowerCase();\n return element.id ? `${tag}#${element.id}` : tag;\n};\n\nconst matchesAny = (element: Element, selectors: string[]) =>\n selectors.some(selector => {\n try {\n return element.matches(selector) || Boolean(element.closest(selector));\n } catch {\n return false;\n }\n });\n\nconst isVisuallyPresent = (element: Element) => {\n if (typeof getComputedStyle !== 'function') return true;\n const style = getComputedStyle(element);\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity || 1) > 0;\n};\n\nconst getElementsAtPoint = (x: number, y: number): Element[] => {\n if (typeof document.elementsFromPoint === 'function') return document.elementsFromPoint(x, y);\n const element = document.elementFromPoint(x, y);\n return element ? [element] : [];\n};\n\nexport const detectBlankScreen = (rootSelector: string, ignoredSelectors: string[]): DetectionResult => {\n const configuredRoot = document.querySelector(rootSelector);\n const root = configuredRoot ?? document.body ?? document.documentElement;\n const rootFound = Boolean(configuredRoot);\n\n let emptyCount = 0;\n let ignoredCount = 0;\n let centerElement = '';\n // 采样视口九宫格而非遍历 DOM,检测成本固定;被 loading/骨架屏覆盖的点不进入分母。\n for (const [xRatio, yRatio] of SAMPLE_POINTS) {\n const layers = getElementsAtPoint(window.innerWidth * xRatio, window.innerHeight * yRatio);\n if (xRatio === 0.5 && yRatio === 0.5) centerElement = getElementSelector(layers[0]);\n if (layers.some(element => matchesAny(element, ignoredSelectors))) {\n ignoredCount += 1;\n continue;\n }\n const hasContent = layers.some(\n element =>\n element !== root &&\n element !== document.body &&\n element !== document.documentElement &&\n root.contains(element) &&\n isVisuallyPresent(element),\n );\n if (!hasContent) emptyCount += 1;\n }\n\n const validCount = SAMPLE_POINTS.length - ignoredCount;\n return {\n centerElement,\n emptyCount,\n ignoredCount,\n reason: rootFound ? 'empty_viewport' : 'missing_root',\n rootFound,\n score: validCount ? emptyCount / validCount : 0,\n validCount,\n };\n};\n\nexport const createBlankScreenPlugin = (options: BkOTBlankScreenPluginOptions = {}): BkOTPlugin => {\n const checkDelay = nonNegativeNumber(options.checkDelay, DEFAULT_CHECK_DELAY);\n const confirmationDelay = nonNegativeNumber(options.confirmationDelay, DEFAULT_CONFIRMATION_DELAY);\n const rootSelector = options.rootSelector || DEFAULT_ROOT_SELECTOR;\n const threshold = boundedRatio(options.threshold, DEFAULT_THRESHOLD);\n const ignoredSelectors = [...DEFAULT_IGNORED_SELECTORS, ...(options.ignoreSelectors ?? [])];\n let activeViewId: string | undefined;\n let reportedViewId: string | undefined;\n let stopActivityTracking: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeView = () => {};\n let visibilityListener: (() => void) | undefined;\n\n return {\n name: 'blank-screen',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n\n const report = (viewId: string, result: DetectionResult) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (!context.isSampled()) return;\n reportedViewId = viewId;\n const occurredAt = Date.now();\n const span = context.startSpan(\n 'browser.blank_screen',\n {\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n outcomeReason: 'blank_screen',\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'blank_screen.center_element': result.centerElement,\n 'blank_screen.empty_ratio': result.score,\n 'blank_screen.empty_sample_count': result.emptyCount,\n 'blank_screen.ignored_sample_count': result.ignoredCount,\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_found': result.rootFound,\n 'blank_screen.root_selector': rootSelector,\n 'blank_screen.sample_count': SAMPLE_POINTS.length,\n 'blank_screen.valid_sample_count': result.validCount,\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n const detectAndConfirm = (viewId: string) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (document.visibilityState === 'hidden') return;\n let first: DetectionResult;\n try {\n first = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] detection failed:', error);\n return;\n }\n if (!first.validCount || first.score < threshold) return;\n timer = setTimeout(() => {\n if (activeViewId !== viewId || document.visibilityState === 'hidden') return;\n let confirmed: DetectionResult;\n try {\n confirmed = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] confirmation failed:', error);\n return;\n }\n if (confirmed.validCount && confirmed.score >= threshold) report(viewId, confirmed);\n }, confirmationDelay);\n };\n\n const schedule = (viewId: string) => {\n clearTimer();\n if (document.visibilityState === 'hidden' || !context.isSampled()) return;\n timer = setTimeout(() => detectAndConfirm(viewId), checkDelay);\n };\n\n unsubscribeView = context.onViewChange(view => {\n if (view.id === activeViewId) return;\n clearTimer();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n activeViewId = view.id;\n reportedViewId = undefined;\n if (view.id) {\n const viewId = view.id;\n // 页面活动结束后再延迟检测,避免把正常渲染过程中的短暂空白误报为白屏。\n stopActivityTracking = waitForPageActivityEnd(context, Date.now(), () => {\n stopActivityTracking = undefined;\n schedule(viewId);\n });\n }\n });\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n clearTimer();\n } else if (activeViewId && reportedViewId !== activeViewId) {\n schedule(activeViewId);\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n },\n shutdown() {\n clearTimeout(timer);\n timer = undefined;\n unsubscribeView();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n activeViewId = undefined;\n reportedViewId = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\ninterface PendingRequestTiming {\n entry?: PerformanceResourceTiming;\n startTime: number;\n timer?: ReturnType<typeof setTimeout>;\n url: string;\n callback?: (entry?: PerformanceResourceTiming) => void;\n}\n\nconst MAX_PENDING_REQUESTS = 100;\nconst RESOURCE_TIMING_WAIT_MS = 200;\nconst pendingRequests: PendingRequestTiming[] = [];\n\nconst normalizeUrl = (url: string) => url.replace(/#.*$/, '');\n\nconst removePending = (pending: PendingRequestTiming) => {\n const index = pendingRequests.indexOf(pending);\n if (index >= 0) pendingRequests.splice(index, 1);\n if (pending.timer !== undefined) clearTimeout(pending.timer);\n};\n\nconst finishPending = (pending: PendingRequestTiming, entry?: PerformanceResourceTiming) => {\n const callback = pending.callback;\n removePending(pending);\n pending.callback = undefined;\n callback?.(entry);\n};\n\nexport const notifyRequestTiming = (entry: PerformanceResourceTiming) => {\n const entryStartTime = performance.timeOrigin + entry.startTime;\n // 同 URL 并发时选择起始时间最接近的请求;50ms 容差覆盖 JS 调用与浏览器创建 timing entry 的偏差。\n const candidate = pendingRequests\n .filter(\n pending =>\n !pending.entry &&\n normalizeUrl(pending.url) === normalizeUrl(entry.name) &&\n entryStartTime >= pending.startTime - 50,\n )\n .sort((left, right) => Math.abs(left.startTime - entryStartTime) - Math.abs(right.startTime - entryStartTime))[0];\n if (!candidate) return;\n candidate.entry = entry;\n if (candidate.callback) finishPending(candidate, entry);\n};\n\nexport const trackRequestTiming = (url: string, startTime = Date.now()) => {\n const pending: PendingRequestTiming = { startTime, url };\n pendingRequests.push(pending);\n if (pendingRequests.length > MAX_PENDING_REQUESTS) finishPending(pendingRequests[0]);\n return {\n cancel: () => finishPending(pending),\n complete: (callback: (entry?: PerformanceResourceTiming) => void) => {\n pending.callback = callback;\n if (pending.entry) {\n finishPending(pending, pending.entry);\n return;\n }\n // XHR 终止事件/Fetch settle 通常早于 ResourceTiming 回调,短暂等待以补齐 DNS、连接和下载阶段。\n pending.timer = setTimeout(() => finishPending(pending), RESOURCE_TIMING_WAIT_MS);\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * 被观测请求的 Trace Context 规划。\n *\n * 后端 APM 只解析 W3C `traceparent`,并且要求一个后端 trace 只对应一个 API 请求,因此这里不复用\n * RUM Span 自身的 trace context,而是为每个命中白名单的请求生成独立的 trace-id/span-id,再通过\n * Span Link 把后端 trace 关联回 RUM Span。\n *\n * 如果业务已经自行设置了 trace header(`traceparent`、`sentry-trace`、b3、Datadog、sw8),则完全\n * 不注入,避免破坏业务既有的传播方式,只把已有链路建成 Link。\n *\n * Link 结构恒定:`context` 承载 trace-id/span-id,`attributes` 记录命中的格式与原始 header。\n * sw8 这类无法映射为合法 OTel id 的格式会补一对占位 id,让 Link 结构保持统一。\n */\n\nimport { TraceFlags } from '@opentelemetry/api';\n\nimport type { Attributes, Link } from '@opentelemetry/api';\n\n/** 大小写不敏感地读取一个请求 header */\nexport type BkOTHeaderReader = (name: string) => string | undefined;\n\nexport interface BkOTRequestTracePlan {\n /** 关联后端 trace 的 Span Link,恒为一条 */\n links: Link[];\n /** 需要注入的 traceparent;业务已有 trace header 时为 undefined */\n traceparent?: string;\n}\n\nexport type BkOTUpstreamTraceFormat = 'b3' | 'b3-multi' | 'datadog' | 'sentry-trace' | 'sw8' | 'traceparent';\n\nexport const TRACEPARENT_HEADER = 'traceparent';\n\nconst TRACE_ID_LENGTH = 32;\nconst SPAN_ID_LENGTH = 16;\n\nconst HEX_DIGITS = '0123456789abcdef';\nconst HEX_PATTERN = /^[\\da-f]+$/;\nconst ZERO_PATTERN = /^0+$/;\nconst DECIMAL_PATTERN = /^\\d+$/;\n\n// 00-{trace-id}-{parent-id}-{trace-flags};允许未来版本追加字段,但 ff 是规范保留值\nconst TRACEPARENT_PATTERN = /^([\\da-f]{2})-([\\da-f]{32})-([\\da-f]{16})-([\\da-f]{2})(?:-.*)?$/;\nconst SENTRY_TRACE_PATTERN = /^([\\da-f]{32})-([\\da-f]{16})(?:-([01]))?$/;\n// Datadog 128 位 trace-id 的高 64 位放在 x-datadog-tags 的 _dd.p.tid 里\nconst DATADOG_TRACE_ID_HIGH_PATTERN = /(?:^|,)_dd\\.p\\.tid=([\\da-f]{16})(?:,|$)/;\nconst DATADOG_TRACE_ID_HIGH_DEFAULT = '0000000000000000';\n\n/** OTel 要求 id 为定长小写 hex 且不能全零 */\nconst isValidHexId = (value: string, length: number) =>\n value.length === length && HEX_PATTERN.test(value) && !ZERO_PATTERN.test(value);\n\n// b3 与 Datadog 允许 64 位 trace-id,映射到 OTel 的 128 位时按规范左侧补零\nconst normalizeTraceId = (value: string) => {\n const lower = value.toLowerCase();\n if (isValidHexId(lower, TRACE_ID_LENGTH)) return lower;\n return isValidHexId(lower, SPAN_ID_LENGTH) ? lower.padStart(TRACE_ID_LENGTH, '0') : undefined;\n};\n\nconst normalizeSpanId = (value: string) => {\n const lower = value.toLowerCase();\n return isValidHexId(lower, SPAN_ID_LENGTH) ? lower : undefined;\n};\n\nconst createRandomHexId = (length: number) => {\n const bytes = new Uint8Array(length / 2);\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n crypto.getRandomValues(bytes);\n } else {\n for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);\n }\n let hex = '';\n for (const byte of bytes) {\n hex += HEX_DIGITS[byte >> 4] + HEX_DIGITS[byte & 15];\n }\n // 全零是 OTel 的非法 id,概率极低但必须排除\n return ZERO_PATTERN.test(hex) ? `${hex.slice(1)}1` : hex;\n};\n\n/**\n * Datadog 用十进制字符串传 64 位 id。构建目标是 es2015,不能依赖 BigInt,\n * 这里用逐位进位的方式把十进制换算成 16 位 hex。\n */\nconst decimalToHexId = (value: string) => {\n if (!DECIMAL_PATTERN.test(value)) return undefined;\n const nibbles: number[] = [];\n for (let index = 0; index < value.length; index += 1) {\n let carry = value.charCodeAt(index) - 48;\n for (let position = 0; position < nibbles.length; position += 1) {\n const product = nibbles[position] * 10 + carry;\n nibbles[position] = product % 16;\n carry = (product - (product % 16)) / 16;\n }\n while (carry > 0) {\n nibbles.push(carry % 16);\n carry = (carry - (carry % 16)) / 16;\n }\n }\n if (nibbles.length > SPAN_ID_LENGTH) return undefined;\n let hex = '';\n for (let position = nibbles.length - 1; position >= 0; position -= 1) hex += HEX_DIGITS[nibbles[position]];\n return hex.padStart(SPAN_ID_LENGTH, '0');\n};\n\ninterface UpstreamTrace {\n format: BkOTUpstreamTraceFormat;\n /** 命中格式的原始 header,原样写入 Link Attributes */\n headers: Attributes;\n sampled?: boolean;\n /** 规范化后的合法 OTel id;无法映射时两者同时缺失 */\n spanId?: string;\n traceId?: string;\n}\n\n/** 按 header 原名收集实际存在的取值,缺失的不写入 */\nconst collectHeaders = (getHeader: BkOTHeaderReader, names: string[]) => {\n const headers: Attributes = {};\n for (const name of names) {\n const value = getHeader(name)?.trim();\n if (value) headers[name] = value;\n }\n return headers;\n};\n\n/**\n * `traceparent` 与 SDK 注入的是同一个 header,因此格式非法时视作没有既有链路:\n * 这种值后端同样无法接续,覆盖注入比保留它更有价值。\n */\nconst parseTraceparent = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader(TRACEPARENT_HEADER)?.trim().toLowerCase();\n if (!value) return undefined;\n const matched = TRACEPARENT_PATTERN.exec(value);\n if (!matched || matched[1] === 'ff') return undefined;\n const [, , traceId, spanId, flags] = matched;\n if (!isValidHexId(traceId, TRACE_ID_LENGTH) || !isValidHexId(spanId, SPAN_ID_LENGTH)) return undefined;\n return {\n format: 'traceparent',\n headers: collectHeaders(getHeader, [TRACEPARENT_HEADER, 'tracestate']),\n sampled: (Number.parseInt(flags, 16) & 1) === 1,\n spanId,\n traceId,\n };\n};\n\nconst parseSentryTrace = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('sentry-trace')?.trim().toLowerCase();\n if (!value) return undefined;\n const headers = collectHeaders(getHeader, ['sentry-trace']);\n const matched = SENTRY_TRACE_PATTERN.exec(value);\n if (!matched) return { format: 'sentry-trace', headers };\n const [, traceId, spanId, sampled] = matched;\n return {\n format: 'sentry-trace',\n headers,\n ...(sampled === undefined ? {} : { sampled: sampled === '1' }),\n ...(isValidHexId(traceId, TRACE_ID_LENGTH) && isValidHexId(spanId, SPAN_ID_LENGTH) ? { spanId, traceId } : {}),\n };\n};\n\n// b3 单头:{trace-id}-{span-id}[-{sampling-state}[-{parent-span-id}]],也允许只有 0 / 1 / d\nconst parseB3Single = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('b3')?.trim().toLowerCase();\n if (!value) return undefined;\n const [rawTraceId, rawSpanId, samplingState] = value.split('-');\n const traceId = rawTraceId ? normalizeTraceId(rawTraceId) : undefined;\n const spanId = rawSpanId ? normalizeSpanId(rawSpanId) : undefined;\n return {\n format: 'b3',\n headers: collectHeaders(getHeader, ['b3']),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingState ? { sampled: samplingState === '1' || samplingState === 'd' } : {}),\n };\n};\n\nconst parseB3Multi = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const rawTraceId = getHeader('x-b3-traceid')?.trim().toLowerCase();\n const rawSpanId = getHeader('x-b3-spanid')?.trim().toLowerCase();\n const samplingState = getHeader('x-b3-sampled')?.trim().toLowerCase();\n if (!rawTraceId && !rawSpanId && !samplingState) return undefined;\n const traceId = rawTraceId ? normalizeTraceId(rawTraceId) : undefined;\n const spanId = rawSpanId ? normalizeSpanId(rawSpanId) : undefined;\n return {\n format: 'b3-multi',\n headers: collectHeaders(getHeader, [\n 'x-b3-traceid',\n 'x-b3-spanid',\n 'x-b3-parentspanid',\n 'x-b3-sampled',\n 'x-b3-flags',\n ]),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingState ? { sampled: samplingState === '1' || samplingState === 'd' } : {}),\n };\n};\n\nconst parseDatadog = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const rawTraceId = getHeader('x-datadog-trace-id')?.trim();\n const rawSpanId = getHeader('x-datadog-parent-id')?.trim();\n const samplingPriority = getHeader('x-datadog-sampling-priority')?.trim();\n if (!rawTraceId && !rawSpanId && !samplingPriority) return undefined;\n const tags = getHeader('x-datadog-tags')?.trim().toLowerCase();\n const traceIdHigh =\n (tags ? DATADOG_TRACE_ID_HIGH_PATTERN.exec(tags)?.[1] : undefined) ?? DATADOG_TRACE_ID_HIGH_DEFAULT;\n const traceIdLow = rawTraceId ? decimalToHexId(rawTraceId) : undefined;\n const traceId = traceIdLow ? normalizeTraceId(`${traceIdHigh}${traceIdLow}`) : undefined;\n const spanIdHex = rawSpanId ? decimalToHexId(rawSpanId) : undefined;\n const spanId = spanIdHex ? normalizeSpanId(spanIdHex) : undefined;\n return {\n format: 'datadog',\n headers: collectHeaders(getHeader, [\n 'x-datadog-trace-id',\n 'x-datadog-parent-id',\n 'x-datadog-sampling-priority',\n 'x-datadog-origin',\n 'x-datadog-tags',\n ]),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingPriority ? { sampled: Number(samplingPriority) > 0 } : {}),\n };\n};\n\n/**\n * sw8:{sample}-{trace-id}-{segment-id}-{span-id}-{service}-{instance}-{endpoint}-{peer},\n * 其中 trace-id 与 segment-id 是 base64 后的 SkyWalking id。它们不是 16 字节 hex,无法映射为\n * 合法的 OTel SpanContext,因此不产出 traceId/spanId,由调用方补占位 id。\n */\nconst parseSw8 = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('sw8')?.trim();\n if (!value) return undefined;\n const samplingState = value.split('-')[0];\n return {\n format: 'sw8',\n headers: collectHeaders(getHeader, ['sw8']),\n ...(samplingState ? { sampled: samplingState === '1' } : {}),\n };\n};\n\n// 按格式明确度排序:先看与 SDK 同名的 traceparent,再看其他厂商格式\nconst UPSTREAM_PARSERS = [parseTraceparent, parseSentryTrace, parseB3Single, parseB3Multi, parseDatadog, parseSw8];\n\nexport const detectUpstreamTrace = (getHeader: BkOTHeaderReader) => {\n for (const parse of UPSTREAM_PARSERS) {\n const upstream = parse(getHeader);\n if (upstream) return upstream;\n }\n return undefined;\n};\n\nconst toLink = (traceId: string, spanId: string, attributes: Attributes, sampled?: boolean): Link => ({\n attributes,\n context: {\n spanId,\n traceFlags: sampled === false ? TraceFlags.NONE : TraceFlags.SAMPLED,\n traceId,\n },\n});\n\nexport const planRequestTrace = (getHeader: BkOTHeaderReader): BkOTRequestTracePlan => {\n const upstream = detectUpstreamTrace(getHeader);\n if (!upstream) {\n // 与 RUM Span 无关的独立 trace,保证后端按 trace-id 检索时只命中这一个 API 请求\n const traceId = createRandomHexId(TRACE_ID_LENGTH);\n const spanId = createRandomHexId(SPAN_ID_LENGTH);\n const traceparent = `00-${traceId}-${spanId}-01`;\n return {\n links: [toLink(traceId, spanId, { format: 'traceparent', [TRACEPARENT_HEADER]: traceparent })],\n traceparent,\n };\n }\n // sw8 等格式解析不出合法 OTel id,补占位 id 让 Link 结构统一;此时不注入,后端不会有对应 trace\n return {\n links: [\n toLink(\n upstream.traceId ?? createRandomHexId(TRACE_ID_LENGTH),\n upstream.spanId ?? createRandomHexId(SPAN_ID_LENGTH),\n { format: upstream.format, ...upstream.headers },\n upstream.sampled,\n ),\n ],\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SpanKind } from '@opentelemetry/api';\n\nimport { notifyRequestTiming } from '../core/request-timing';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * Resource Timing 的统一消费端:静态资源按配置生成 span;Fetch/XHR timing 转交 request 插件,\n * 与其已创建的请求 span 合并,避免同一次网络请求重复计数。\n */\ntype ResourceTimingWithStatus = PerformanceResourceTiming & {\n deliveryType?: string;\n renderBlockingStatus?: string;\n responseStatus?: number;\n};\n\nconst getCacheHit = (entry: ResourceTimingWithStatus) =>\n entry.deliveryType === 'cache' || (entry.transferSize === 0 && entry.decodedBodySize > 0) ? true : undefined;\n\nconst hasUnavailableResourceDetails = (entry: ResourceTimingWithStatus) =>\n entry.transferSize === 0 &&\n entry.encodedBodySize === 0 &&\n entry.decodedBodySize === 0 &&\n entry.requestStart === 0 &&\n entry.responseStart === 0 &&\n !entry.nextHopProtocol;\n\nconst areInOrder = (...values: number[]) => values.every((value, index) => index === 0 || values[index - 1] <= value);\n\nconst setTiming = (attributes: Record<string, number>, name: string, origin: number, start: number, end: number) => {\n if (!areInOrder(origin, start, end)) return;\n attributes[`resource.${name}.start`] = start - origin;\n attributes[`resource.${name}.duration`] = end - start;\n};\n\nexport const getResourceTimingAttributes = (entry: ResourceTimingWithStatus) => {\n const attributes: Record<string, number> = {};\n const commonTimings = [\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 // 浏览器因 TAO 或实现差异可能返回缺失/乱序时间点;整组校验可避免产生负 duration。\n if (!commonTimings.every(Number.isFinite) || !areInOrder(...commonTimings)) return attributes;\n\n setTiming(attributes, 'first_byte', entry.startTime, entry.requestStart, entry.responseStart);\n setTiming(attributes, 'download', entry.startTime, entry.responseStart, entry.responseEnd);\n if (entry.workerStart > 0 && entry.workerStart < entry.fetchStart) {\n setTiming(attributes, 'worker', entry.startTime, entry.workerStart, entry.fetchStart);\n }\n if (entry.fetchStart < entry.domainLookupEnd) {\n setTiming(attributes, 'dns', entry.startTime, entry.domainLookupStart, entry.domainLookupEnd);\n }\n if (entry.fetchStart < entry.connectEnd) {\n setTiming(attributes, 'connect', entry.startTime, entry.connectStart, entry.connectEnd);\n if (entry.connectStart <= entry.secureConnectionStart && entry.secureConnectionStart <= entry.connectEnd) {\n setTiming(attributes, 'ssl', entry.startTime, entry.secureConnectionStart, entry.connectEnd);\n }\n }\n if (\n entry.redirectEnd > entry.startTime &&\n areInOrder(entry.startTime, entry.redirectStart, entry.redirectEnd, entry.fetchStart)\n ) {\n setTiming(attributes, 'redirect', entry.startTime, entry.redirectStart, entry.redirectEnd);\n }\n return attributes;\n};\n\nexport const getResourceDuration = (entry: ResourceTimingWithStatus) =>\n entry.duration === 0 && entry.responseEnd > entry.startTime ? entry.responseEnd - entry.startTime : entry.duration;\n\nexport const getResourcePerformanceAttributes = (entry: ResourceTimingWithStatus) => {\n const detailsUnavailable = hasUnavailableResourceDetails(entry);\n const deliveryType = typeof entry.deliveryType === 'string' && entry.deliveryType ? entry.deliveryType : undefined;\n return {\n [RUM_ATTRIBUTES.cacheHit]: getCacheHit(entry),\n ...(entry.nextHopProtocol ? { [RUM_ATTRIBUTES.nextHopProtocol]: entry.nextHopProtocol } : {}),\n ...getResourceTimingAttributes(entry),\n ...(deliveryType ? { 'resource.delivery_type': deliveryType } : {}),\n ...(entry.renderBlockingStatus ? { 'resource.render_blocking_status': entry.renderBlockingStatus } : {}),\n ...(detailsUnavailable\n ? {}\n : {\n // 对齐 Datadog:resource.size 取解压后 body 体积,缓存命中也能反映真实大小;\n // 网络传输量单独用 resource.transfer_size 表达(受缓存/TAO 影响可能为 0)。\n 'resource.size': entry.decodedBodySize,\n 'resource.encoded_body_size': entry.encodedBodySize,\n 'resource.decoded_body_size': entry.decodedBodySize,\n 'resource.transfer_size': entry.transferSize,\n }),\n };\n};\n\nexport const createResourcePlugin = (emitResourceSpans = true): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'resource',\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n if (!PerformanceObserver.supportedEntryTypes?.includes('resource')) {\n return;\n }\n\n const emitEntry = (entry: ResourceTimingWithStatus) => {\n if (!context.isSampled()) return;\n if (!entry.name || shouldIgnoreUrl(context.config, entry.name)) {\n return;\n }\n if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {\n // Fetch/XHR 已由 request 插件建 span;这里只把 ResourceTiming 回传补齐网络阶段,避免重复上报。\n notifyRequestTiming(entry);\n return;\n }\n if (entry.duration < 0) return;\n const redactedUrl = context.config.hooks.redactUrl(entry.name);\n const duration = getResourceDuration(entry);\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + duration;\n if (emitResourceSpans && context.isSampled()) {\n const span = context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: entry.initiatorType || 'other',\n outcome:\n typeof entry.responseStatus === 'number'\n ? { statusCode: entry.responseStatus, type: 'response' }\n : undefined,\n url: redactedUrl,\n }),\n 'url.full': redactedUrl,\n ...getResourcePerformanceAttributes(entry),\n },\n { kind: SpanKind.CLIENT, startTime },\n );\n span.end(endTime);\n }\n context.notifyActivity({ endTime, startTime, type: 'resource', url: entry.name });\n };\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as ResourceTimingWithStatus[]) {\n emitEntry(entry);\n }\n });\n observer.observe({ type: 'resource', buffered: true });\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/**\n * Fetch/XHR 请求统一采集入口。\n *\n * 同一层 patch 负责请求 span、Trace Header、页面活动与 beforeSend,避免多套 patch\n * 重复计数,也不依赖 OpenTelemetry instrumentation 的私有方法。\n */\n\nimport { SpanKind, SpanStatusCode } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { markSpanDiscarded } from '../core/processor';\nimport { trackRequestTiming } from '../core/request-timing';\nimport { planRequestTrace, TRACEPARENT_HEADER } from '../core/trace-context';\nimport {\n matchesUrlList,\n shouldIgnoreRequest,\n shouldIgnoreUrl,\n shouldPropagateTraceHeader,\n toAbsoluteUrl,\n} from '../core/url';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\nimport { getResourceDuration, getResourcePerformanceAttributes } from './resource';\n\nimport type { BkOTRequestConfig, BkOTRequestContext, BkOTRequestOutcome } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { BkOTHeaderReader, BkOTRequestTracePlan } from '../core/trace-context';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface RequestDetails {\n method?: string;\n startedAt?: number;\n url?: string;\n}\n\nconst getRequestOptions = (context: BkOTRuntimeContext) =>\n typeof context.config.tracking.request === 'object' ? context.config.tracking.request : undefined;\n\n// 没有白名单时不会注入任何 header,也就不需要为探测既有 trace header 付出 patch 成本。\nconst hasTracingAllowlist = (context: BkOTRuntimeContext) => {\n const allowed = getRequestOptions(context)?.allowedTracingUrls;\n return Array.isArray(allowed) ? allowed.length > 0 : allowed !== undefined;\n};\n\nconst getErrorName = (error: unknown) =>\n typeof error === 'object' && error && 'name' in error && typeof error.name === 'string' ? error.name : '';\n\nconst getFetchFailureOutcome = (requestContext: Request | RequestInit, error: unknown): BkOTRequestOutcome => {\n const signal = requestContext.signal;\n if (getErrorName(signal?.reason) === 'TimeoutError' || getErrorName(error) === 'TimeoutError') {\n return { error, type: 'timeout' };\n }\n if (signal?.aborted || getErrorName(error) === 'AbortError') return { type: 'abort' };\n return { error, type: 'network_error' };\n};\n\nconst getXhrOutcome = (\n completionType: 'abort' | 'error' | 'load' | 'timeout',\n xhr: XMLHttpRequest,\n): BkOTRequestOutcome => {\n if (completionType === 'abort') return { type: 'abort' };\n if (completionType === 'timeout') return { type: 'timeout' };\n if (completionType === 'error' || xhr.status === 0) return { type: 'network_error' };\n return { statusCode: xhr.status, type: 'response' };\n};\n\nconst isErrorOutcome = (outcome: BkOTRequestOutcome) =>\n outcome.type === 'network_error' ||\n outcome.type === 'timeout' ||\n (outcome.type === 'response' && typeof outcome.statusCode === 'number' && outcome.statusCode >= 400);\n\nconst getFetchUrl = (input: RequestInfo | URL) => {\n if (typeof input === 'string' || input instanceof URL) return toAbsoluteUrl(String(input));\n return toAbsoluteUrl(input.url);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) =>\n (init?.method ?? (typeof Request !== 'undefined' && input instanceof Request ? input.method : 'GET')).toUpperCase();\n\nconst getFetchRequestContext = (input: RequestInfo | URL, init?: RequestInit): Request | RequestInit =>\n typeof Request !== 'undefined' && input instanceof Request ? input : (init ?? {});\n\n/** 取本次请求最终会发出的 header 集合:init.headers 一旦存在就会整体覆盖 Request 上的 header。 */\nconst getFetchRequestHeaders = (input: RequestInfo | URL, init: RequestInit | undefined) => {\n const requestHeaders = typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined;\n return new Headers(init?.headers ?? requestHeaders);\n};\n\nconst applyFetchTracePlan = (init: RequestInit | undefined, headers: Headers, plan: BkOTRequestTracePlan) => {\n if (!plan.traceparent) return init;\n headers.set(TRACEPARENT_HEADER, plan.traceparent);\n return { ...init, headers };\n};\n\nconst createHeaderReader =\n (headers: Record<string, string> | undefined): BkOTHeaderReader =>\n name =>\n headers?.[name.toLowerCase()];\n\nconst isActivityExcluded = (context: BkOTRuntimeContext, url: string) =>\n shouldIgnoreUrl(context.config, url) || matchesUrlList(context.config.view.excludedActivityUrls, url);\n\nconst startRequestActivity = (context: BkOTRuntimeContext, url: string) => {\n if (isActivityExcluded(context, url)) return () => {};\n const id = createRandomId('request');\n context.notifyActivity({ id, time: Date.now(), type: 'request_start', url });\n let ended = false;\n return () => {\n if (ended) return;\n ended = true;\n context.notifyActivity({ id, time: Date.now(), type: 'request_end' });\n };\n};\n\nconst trackRequestSpanTiming = (context: BkOTRuntimeContext, url: string) =>\n !context.isSampled()\n ? {\n cancel() {},\n complete(callback: (entry?: PerformanceResourceTiming) => void) {\n callback();\n },\n }\n : trackRequestTiming(url);\n\nconst startRequestSpan = (\n context: BkOTRuntimeContext,\n subtype: 'fetch' | 'xhr',\n details: RequestDetails,\n tracePlan?: BkOTRequestTracePlan,\n) => {\n const startedAt = details.startedAt ?? Date.now();\n return context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method: details.method,\n url: details.url ?? '',\n }),\n ...(details.url ? { 'url.full': context.config.hooks.redactUrl(details.url) } : {}),\n },\n {\n kind: SpanKind.CLIENT,\n startTime: startedAt,\n ...(tracePlan ? { links: tracePlan.links } : {}),\n },\n );\n};\n\nexport const decorateRequestSpan = (\n context: BkOTRuntimeContext,\n span: Span,\n subtype: 'fetch' | 'xhr',\n domainContext: BkOTRequestContext,\n outcome: BkOTRequestOutcome,\n details: RequestDetails = {},\n) => {\n const sourceUrl = details.url;\n const redactedUrl = sourceUrl ? context.config.hooks.redactUrl(sourceUrl) : undefined;\n const method = details.method;\n const attributes: Attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method,\n outcome,\n url: redactedUrl ?? '',\n }),\n ...(redactedUrl ? { 'url.full': redactedUrl } : {}),\n };\n\n const event = {\n attributes,\n resource: {\n method,\n outcome,\n transport: subtype,\n url: redactedUrl,\n },\n type: 'resource' as const,\n };\n let shouldSend: boolean | void = true;\n try {\n shouldSend = getRequestOptions(context)?.beforeSend?.(event, domainContext);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot] request.beforeSend failed:', error);\n }\n span.setAttributes(context.applyRedact(event.attributes));\n if (shouldSend === false) {\n // SpanProcessor 在 onEnd 统一识别内部状态,避免这里绕过正常的 span 结束流程。\n markSpanDiscarded(span);\n return;\n }\n if (isErrorOutcome(outcome)) span.setStatus({ code: SpanStatusCode.ERROR });\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTRequestConfig | boolean | undefined): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeRequestEnds = new Set<() => void>();\n\n return {\n name: 'request',\n enabled: option !== false,\n init(context) {\n if (typeof window === 'undefined') return;\n\n if (typeof window.fetch === 'function') {\n const originalFetch = window.fetch;\n const patchedFetch: typeof window.fetch = async function (this: Window, input, init) {\n const url = getFetchUrl(input);\n if (!context.isSampled() || shouldIgnoreRequest(context.config, url))\n return originalFetch.call(this, input, init);\n const method = getFetchMethod(input, init);\n const details = { method, startedAt: Date.now(), url };\n // Span Link 必须在创建 span 时给出,因此先规划 trace context 再建 span。\n const headers = shouldPropagateTraceHeader(context.config, url)\n ? getFetchRequestHeaders(input, init)\n : undefined;\n const tracePlan = headers ? planRequestTrace(name => headers.get(name) ?? undefined) : undefined;\n const span = startRequestSpan(context, 'fetch', details, tracePlan);\n const timing = trackRequestSpanTiming(context, url);\n const endActivity = startRequestActivity(context, url);\n const requestContext = getFetchRequestContext(input, init);\n const requestInit = headers && tracePlan ? applyFetchTracePlan(init, headers, tracePlan) : init;\n let ended = false;\n const endSpan = (entry?: PerformanceResourceTiming, fallbackEndTime = Date.now()) => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) {\n span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end(performance.timeOrigin + entry.startTime + getResourceDuration(entry));\n return;\n }\n span.end(fallbackEndTime);\n };\n const forceEnd = () => {\n // shutdown 不再等待异步 ResourceTiming,立即结束仍在飞行中的 span。\n timing.cancel();\n endSpan();\n };\n activeRequestEnds.add(forceEnd);\n try {\n const response = await originalFetch.call(this, input, requestInit);\n const outcome: BkOTRequestOutcome = {\n ...(response.status > 0 ? { statusCode: response.status } : {}),\n type: 'response',\n };\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { request: requestContext, response, type: 'fetch' },\n outcome,\n details,\n );\n const completedAt = Date.now();\n timing.complete(entry => endSpan(entry, completedAt));\n return response;\n } catch (error) {\n const outcome = getFetchFailureOutcome(requestContext, error);\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { error, request: requestContext, type: 'fetch' },\n outcome,\n details,\n );\n if (outcome.type !== 'abort' && error instanceof Error) span.recordException(error);\n const completedAt = Date.now();\n timing.complete(entry => endSpan(entry, completedAt));\n throw error;\n } finally {\n endActivity();\n }\n };\n window.fetch = patchedFetch;\n teardownCallbacks.push(() => {\n if (window.fetch === patchedFetch) window.fetch = originalFetch;\n });\n }\n\n if (typeof XMLHttpRequest !== 'undefined') {\n const requests = new WeakMap<XMLHttpRequest, RequestDetails>();\n // 浏览器不提供读取 XHR 请求头的 API,只能自己快照业务设置的 header 以便探测既有链路。\n const authorHeaders = new WeakMap<XMLHttpRequest, Record<string, string>>();\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const patchedOpen: typeof XMLHttpRequest.prototype.open = function (\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n async = true,\n username?: null | string,\n password?: null | string,\n ) {\n requests.set(this, { method: method.toUpperCase(), url: toAbsoluteUrl(String(url)) });\n // open() 会清空已设置的请求头,快照必须同步失效。\n authorHeaders.delete(this);\n return originalOpen.call(this, method, String(url), async, username, password);\n };\n const patchedSend: typeof XMLHttpRequest.prototype.send = function (this: XMLHttpRequest, body) {\n const details = requests.get(this) ?? {};\n if (!context.isSampled() || !details.url || shouldIgnoreRequest(context.config, details.url))\n return originalSend.call(this, body);\n details.startedAt = Date.now();\n const tracePlan = shouldPropagateTraceHeader(context.config, details.url)\n ? planRequestTrace(createHeaderReader(authorHeaders.get(this)))\n : undefined;\n const span = startRequestSpan(context, 'xhr', details, tracePlan);\n const timing = trackRequestSpanTiming(context, details.url);\n const endActivity = startRequestActivity(context, details.url);\n if (tracePlan?.traceparent) this.setRequestHeader(TRACEPARENT_HEADER, tracePlan.traceparent);\n let finishing = false;\n let ended = false;\n let removeTerminalListeners = () => {};\n const forceEnd = () => {\n // XHR 可能永不触发终止事件;SDK 停止时必须清理活动计数并结束 span。\n removeTerminalListeners();\n timing.cancel();\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n endActivity();\n span.end();\n };\n activeRequestEnds.add(forceEnd);\n const complete = (completionType: 'abort' | 'error' | 'load' | 'timeout') => {\n if (finishing || ended) return;\n finishing = true;\n removeTerminalListeners();\n const outcome = getXhrOutcome(completionType, this);\n decorateRequestSpan(context, span, 'xhr', { type: 'xhr', xhr: this }, outcome, details);\n endActivity();\n const completedAt = Date.now();\n timing.complete(entry => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) {\n span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end(performance.timeOrigin + entry.startTime + getResourceDuration(entry));\n return;\n }\n span.end(completedAt);\n });\n };\n const onAbort = () => complete('abort');\n const onError = () => complete('error');\n const onLoad = () => complete('load');\n const onTimeout = () => complete('timeout');\n removeTerminalListeners = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onError);\n this.removeEventListener('load', onLoad);\n this.removeEventListener('timeout', onTimeout);\n };\n this.addEventListener('abort', onAbort, { once: true });\n this.addEventListener('error', onError, { once: true });\n this.addEventListener('load', onLoad, { once: true });\n this.addEventListener('timeout', onTimeout, { once: true });\n try {\n return originalSend.call(this, body);\n } catch (error) {\n if (error instanceof Error) span.recordException(error);\n complete('error');\n throw error;\n }\n };\n XMLHttpRequest.prototype.open = patchedOpen;\n XMLHttpRequest.prototype.send = patchedSend;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.open === patchedOpen) XMLHttpRequest.prototype.open = originalOpen;\n if (XMLHttpRequest.prototype.send === patchedSend) XMLHttpRequest.prototype.send = originalSend;\n });\n\n if (hasTracingAllowlist(context) && typeof XMLHttpRequest.prototype.setRequestHeader === 'function') {\n const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n const patchedSetRequestHeader: typeof XMLHttpRequest.prototype.setRequestHeader = function (\n this: XMLHttpRequest,\n name: string,\n value: string,\n ) {\n const key = name.toLowerCase();\n const headers = authorHeaders.get(this) ?? {};\n // 浏览器对重复设置的同名 header 做 \", \" 合并,快照保持一致才能正确解析。\n headers[key] = key in headers ? `${headers[key]}, ${value}` : value;\n authorHeaders.set(this, headers);\n return originalSetRequestHeader.call(this, name, value);\n };\n XMLHttpRequest.prototype.setRequestHeader = patchedSetRequestHeader;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.setRequestHeader === patchedSetRequestHeader)\n XMLHttpRequest.prototype.setRequestHeader = originalSetRequestHeader;\n });\n }\n }\n },\n shutdown() {\n for (const end of [...activeRequestEnds]) end();\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n// blockedURI 除了 URL 还可能是 'inline' / 'eval' / 'wasm-eval' / 'self' 等关键字(CSP spec),\n// 这些不是 URL,不应该走 redactUrl\nconst URL_LIKE_BLOCKED_URI = /^https?:|^\\/\\//i;\n\nconst redactBlockedUri = (redactUrl: (url: string) => string, blockedURI: string | undefined) => {\n if (!blockedURI) return undefined;\n return URL_LIKE_BLOCKED_URI.test(blockedURI) ? redactUrl(blockedURI) : blockedURI;\n};\n\n/**\n * CSP 违规事件采集插件,通过监听 `securitypolicyviolation` 事件捕获被 CSP 策略拦截的脚本/资源,\n * 以 error span 上报,进入统一错误分析口径。\n *\n * URL 处理:\n * - `blockedURI` / `sourceFile` 中的 URL 类值会经 `redactUrl` 脱敏。\n * - `blockedURI` 是 'inline' / 'eval' 等 CSP 关键字时直接保留,不调用 redactUrl。\n */\nexport const createCspViolationPlugin = (): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n if (!context.isSampled()) return;\n const occurredAt = Date.now();\n const blockedUri = redactBlockedUri(context.config.hooks.redactUrl, event.blockedURI);\n const sourceFile = event.sourceFile ? context.config.hooks.redactUrl(event.sourceFile) : undefined;\n\n const span = context.startSpan(\n 'csp.violation',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n outcomeReason: 'csp',\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'csp.blocked_uri': blockedUri,\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.disposition': event.disposition,\n 'csp.source_file': sourceFile,\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n 'csp.original_policy': event.originalPolicy,\n 'error.source': 'csp',\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n document.addEventListener('securitypolicyviolation', handler);\n teardown = () => document.removeEventListener('securitypolicyviolation', handler);\n },\n shutdown() {\n teardown?.();\n teardown = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 浏览器错误采集插件,负责捕获 JS 运行时错误、未处理的 Promise rejection、资源加载失败三类错误。\n *\n * 上报策略:\n * - 错误分析主数据统一使用 `span_type=error` 的 error span。\n * - 每个浏览器错误只生成一条原始 error Span,错误分组和风暴控制由服务端完成。\n *\n * 关键 attribute:\n * - `error.source`:错误来源,取值 `window.error` / `unhandledrejection` / `resource`。\n * - `error.cross_origin`:仅在跨域脚本错误(\"Script error.\")时为 true,便于过滤这类无 stack 噪声。\n * - 资源错误额外带 `url.full`(经 redactUrl 脱敏)与 `html.tag`。\n * - JS 错误额外带 `code.filepath` / `code.lineno` / `code.column`。\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\n\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst getErrorMessage = (value: unknown) => {\n if (value instanceof Error) {\n return value.message;\n }\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n};\n\ninterface EmitErrorOptions {\n context: BkOTRuntimeContext;\n error: unknown;\n exceptionType?: string;\n extra?: Record<string, unknown>;\n source: string;\n spanName: string;\n}\n\nconst emitError = ({ context, error, exceptionType, extra = {}, source, spanName }: EmitErrorOptions) => {\n if (!context.isSampled()) return;\n const occurredAt = Date.now();\n const message = getErrorMessage(error);\n context.notifyActivity({ time: occurredAt, type: 'error' });\n\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...context.config.hooks.getErrorAttributes(),\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'error.message': message,\n 'error.handled': false,\n 'error.source': source,\n ...extra,\n };\n // 错误分析统一沉淀为 error span;若当前存在 active context,OTel 会自动建立父子关系。\n const errorSpan = context.startSpan(spanName, attributes, { startTime: occurredAt });\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.end();\n};\n\nexport const createErrorPlugin = (): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n\n return {\n name: 'error',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const onError = (event: ErrorEvent) => {\n // 跨域脚本错误浏览器会屏蔽 stack/filename,message 通常是 \"Script error.\"\n const isCrossOrigin = !event.error && !event.filename && event.message === 'Script error.';\n emitError({\n context,\n spanName: 'browser.error',\n source: 'window.error',\n error: event.error ?? event.message,\n extra: {\n 'code.filepath': event.filename,\n 'code.lineno': event.lineno,\n 'code.column': event.colno,\n ...(isCrossOrigin ? { 'error.cross_origin': true } : {}),\n },\n });\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n emitError({\n context,\n spanName: 'browser.unhandledrejection',\n source: 'unhandledrejection',\n error: event.reason,\n exceptionType: event.reason instanceof Error ? event.reason.name : 'UnhandledRejection',\n });\n };\n const onResourceError = (event: Event) => {\n // JS 运行时错误是 ErrorEvent,已由 bubble 阶段的 onError 处理;这里只关心资源加载失败\n if (event instanceof ErrorEvent) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const elementWithUrl = target as Element & { href?: string; src?: string };\n const resourceUrl = elementWithUrl.src || elementWithUrl.href || (target as HTMLMediaElement).currentSrc || '';\n emitError({\n context,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName.toLowerCase()} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.hooks.redactUrl(resourceUrl),\n 'html.tag': target.tagName,\n },\n });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('error', onResourceError, true);\n listeners.push(\n () => window.removeEventListener('error', onError),\n () => window.removeEventListener('unhandledrejection', onUnhandledRejection),\n () => window.removeEventListener('error', onResourceError, true),\n );\n },\n shutdown() {\n while (listeners.length) {\n listeners.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 主线程阻塞优先使用 Long Animation Frame API 获取脚本归因,不支持时回退到 Long Tasks API;\n * span 保留单次诊断详情,分布与计数由服务端从原始事件计算。\n */\nexport interface BkOTLongTaskPluginOptions {\n /** 采集阈值,默认 50ms。Performance API 本身通常只产生大于 50ms 的条目。 */\n threshold?: number;\n}\n\ninterface LegacyLongTaskEntry extends PerformanceEntry {\n attribution?: Array<{\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n }>;\n}\n\ninterface LongAnimationFrameEntry extends PerformanceEntry {\n blockingDuration: number;\n firstUIEventTimestamp: number;\n renderStart: number;\n scripts: ScriptTiming[];\n styleAndLayoutStart: number;\n}\n\ninterface ScriptTiming {\n duration: number;\n executionStart: number;\n forcedStyleAndLayoutDuration: number;\n invoker: string;\n invokerType: string;\n pauseDuration: number;\n sourceCharPosition: number;\n sourceFunctionName: string;\n sourceURL: string;\n startTime: number;\n windowAttribution: string;\n}\n\nconst DEFAULT_THRESHOLD = 50;\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst finiteNumber = (value: number | undefined) => (Number.isFinite(value) ? value : undefined);\n\nconst getLegacyAttributionAttributes = (\n attribution: NonNullable<LegacyLongTaskEntry['attribution']>[number],\n redactUrl: (url: string) => string,\n): Attributes => ({\n ...(attribution.containerId ? { 'long_task.attribution.container_id': attribution.containerId } : {}),\n ...(attribution.containerName ? { 'long_task.attribution.container_name': attribution.containerName } : {}),\n ...(attribution.containerSrc ? { 'long_task.attribution.container_src': redactUrl(attribution.containerSrc) } : {}),\n ...(attribution.containerType ? { 'long_task.attribution.container_type': attribution.containerType } : {}),\n ...(attribution.name ? { 'long_task.attribution.name': attribution.name } : {}),\n});\n\nconst getScriptAttributes = (script: ScriptTiming, redactUrl: (url: string) => string): Attributes => ({\n 'long_task.script.duration': script.duration,\n 'long_task.script.execution_start': script.executionStart,\n 'long_task.script.forced_style_and_layout_duration': script.forcedStyleAndLayoutDuration,\n 'long_task.script.invoker': script.invoker,\n 'long_task.script.invoker_type': script.invokerType,\n 'long_task.script.pause_duration': script.pauseDuration,\n 'long_task.script.source_char_position': script.sourceCharPosition,\n 'long_task.script.source_function_name': script.sourceFunctionName,\n 'long_task.script.source_url': script.sourceURL ? redactUrl(script.sourceURL) : '',\n 'long_task.script.start_time': script.startTime,\n 'long_task.script.window_attribution': script.windowAttribution,\n});\n\nexport const createLongTaskPlugin = (options: BkOTLongTaskPluginOptions = {}): BkOTPlugin => {\n const threshold = nonNegativeNumber(options.threshold, DEFAULT_THRESHOLD);\n let observer: PerformanceObserver | undefined;\n let initialBatchTimer: ReturnType<typeof setTimeout> | undefined;\n let initialEntries: PerformanceEntry[] = [];\n\n return {\n name: 'long-task',\n init(context) {\n if (typeof PerformanceObserver === 'undefined' || typeof performance === 'undefined') return;\n const supportedEntryTypes = PerformanceObserver.supportedEntryTypes ?? [];\n // LoAF 提供脚本归因与 blockingDuration,支持时优先使用;旧浏览器回退到 longtask。\n const entryType = supportedEntryTypes.includes('long-animation-frame')\n ? 'long-animation-frame'\n : supportedEntryTypes.includes('longtask')\n ? 'longtask'\n : undefined;\n if (!entryType) return;\n\n const emitEntry = (entry: LegacyLongTaskEntry | LongAnimationFrameEntry) => {\n if (!context.isSampled()) return;\n if (!Number.isFinite(entry.duration) || entry.duration < threshold) return;\n const isLoaf = entry.entryType === 'long-animation-frame';\n const loaf = isLoaf ? (entry as LongAnimationFrameEntry) : undefined;\n const scripts = loaf?.scripts ?? [];\n const id = createRandomId('long-task');\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + entry.duration;\n const entryTypeAttribute = isLoaf ? 'long-animation-frame' : 'long-task';\n const legacyAttributions = isLoaf ? [] : ((entry as LegacyLongTaskEntry).attribution ?? []);\n const span = context.startSpan(\n 'browser.long_task',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.longTask,\n }),\n ...(finiteNumber(loaf?.blockingDuration) !== undefined\n ? { [RUM_ATTRIBUTES.longTaskBlockingDuration]: loaf!.blockingDuration }\n : {}),\n [RUM_ATTRIBUTES.longTaskDuration]: entry.duration,\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n [RUM_ATTRIBUTES.longTaskId]: id,\n 'long_task.name': entry.name,\n ...(finiteNumber(loaf?.firstUIEventTimestamp) !== undefined\n ? { 'long_task.first_ui_event_timestamp': loaf!.firstUIEventTimestamp }\n : {}),\n ...(finiteNumber(loaf?.renderStart) !== undefined ? { 'long_task.render_start': loaf!.renderStart } : {}),\n ...(finiteNumber(loaf?.styleAndLayoutStart) !== undefined\n ? { 'long_task.style_and_layout_start': loaf!.styleAndLayoutStart }\n : {}),\n ...(loaf ? { [RUM_ATTRIBUTES.longTaskStartTime]: loaf.startTime } : {}),\n },\n { startTime },\n );\n scripts.forEach(script => {\n span.addEvent(\n 'long_task.script',\n context.applyRedact(getScriptAttributes(script, context.config.hooks.redactUrl)),\n performance.timeOrigin + script.startTime,\n );\n });\n legacyAttributions.forEach((attribution, index) => {\n span.addEvent(\n 'long_task.attribution',\n context.applyRedact({\n 'long_task.attribution.index': index,\n ...getLegacyAttributionAttributes(attribution, context.config.hooks.redactUrl),\n }),\n startTime,\n );\n });\n span.end(endTime);\n context.notifyActivity({ endTime, startTime, type: 'long_task' });\n };\n\n const emitEntries = (entries: PerformanceEntry[]) =>\n entries.forEach(entry => emitEntry(entry as LegacyLongTaskEntry));\n try {\n let initializing = true;\n observer = new PerformanceObserver(list => {\n const entries = list.getEntries();\n if (initializing) {\n // buffered 条目可能在 observe() 内同步回调,先合并到下一宏任务,等 Session/View 上下文就绪后再归属。\n initialEntries.push(...entries);\n if (!initialBatchTimer) {\n initialBatchTimer = setTimeout(() => {\n const entriesToEmit = initialEntries;\n initialEntries = [];\n initialBatchTimer = undefined;\n emitEntries(entriesToEmit);\n });\n }\n } else {\n emitEntries(entries);\n }\n });\n observer.observe({ type: entryType, buffered: true });\n initializing = false;\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n clearTimeout(initialBatchTimer);\n initialBatchTimer = undefined;\n initialEntries = [];\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport interface RouteChangeEvent {\n fromUrl: string;\n source: RouteChangeSource;\n toUrl: string;\n}\n\nexport type RouteChangeSource = 'hashchange' | 'popstate' | 'pushState' | 'replaceState';\n\ntype RouteChangeHandler = (event: RouteChangeEvent) => void;\n\nconst subscribers = new Set<RouteChangeHandler>();\n\n// History 只 patch 一次,多个插件/实例共用订阅集合;最后一个订阅者退出时恢复原方法。\nlet lastUrl = '';\nlet originalPushState: typeof history.pushState | undefined;\nlet originalReplaceState: typeof history.replaceState | undefined;\nlet patched = false;\n\nconst getCurrentUrl = () => (typeof location === 'undefined' ? '' : location.href);\n\nconst emitRouteChange = (source: RouteChangeSource, fromUrl: string) => {\n const toUrl = getCurrentUrl();\n lastUrl = toUrl;\n const event: RouteChangeEvent = { fromUrl, source, toUrl };\n\n for (const subscriber of Array.from(subscribers)) {\n subscriber(event);\n }\n};\n\nconst onPopState = () => {\n emitRouteChange('popstate', lastUrl || getCurrentUrl());\n};\n\nconst onHashChange = () => {\n emitRouteChange('hashchange', lastUrl || getCurrentUrl());\n};\n\nconst patchHistory = () => {\n if (patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n patched = true;\n lastUrl = getCurrentUrl();\n originalPushState = history.pushState;\n originalReplaceState = history.replaceState;\n\n history.pushState = function patchedPushState(this: History, data: any, title: string, url?: null | string | URL) {\n const fromUrl = lastUrl || getCurrentUrl();\n // 先执行原方法,让订阅者读取到的 location 与浏览器最终状态一致。\n const result = originalPushState?.apply(this, [data, title, url]);\n emitRouteChange('pushState', fromUrl);\n return result;\n };\n history.replaceState = function patchedReplaceState(\n this: History,\n data: any,\n title: string,\n url?: null | string | URL,\n ) {\n const fromUrl = lastUrl || getCurrentUrl();\n const result = originalReplaceState?.apply(this, [data, title, url]);\n emitRouteChange('replaceState', fromUrl);\n return result;\n };\n window.addEventListener('popstate', onPopState);\n window.addEventListener('hashchange', onHashChange);\n};\n\nconst restoreHistory = () => {\n if (!patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n if (originalPushState) {\n history.pushState = originalPushState;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n }\n window.removeEventListener('popstate', onPopState);\n window.removeEventListener('hashchange', onHashChange);\n originalPushState = undefined;\n originalReplaceState = undefined;\n patched = false;\n lastUrl = '';\n};\n\nexport const subscribeRouteChange = (handler: RouteChangeHandler) => {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n patchHistory();\n subscribers.add(handler);\n\n return () => {\n subscribers.delete(handler);\n if (subscribers.size === 0) {\n restoreHistory();\n }\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Page View 插件\n *\n * 自动模式监听初始加载、SPA 路由和 BFCache;手动模式仅由 startView() 划分 View。\n *\n * 上报形态:browser.view start/update/end 原始生命周期事件 span。\n *\n * 自动边界与 Datadog 对齐:pathname 或非锚点 hash path 改变才创建 View,query-only 变化不创建。\n *\n * 关键属性:\n * - view.url:当前页面 URL(经 redactUrl 脱敏)\n * - url.previous:跳转前的 URL(来自 route-observer 的 fromUrl,初始 load 为空字符串)\n * - document.referrer:仅 'load' 事件携带\n */\n\nimport { createRandomId } from '../core/id';\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { subscribeRouteChange } from '../core/route-observer';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { BkOTStartViewOptions, BkOTViewContext } from '../core/runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\n\ninterface ActiveView {\n attributes: Attributes;\n id: string;\n loadingTime?: number;\n loadingTimeSource?: 'auto' | 'manual';\n loadingType: ViewLoadingType;\n name: string;\n rawUrl: string;\n snapshotAttributes: Attributes;\n startedAt: number;\n url: string;\n urlPathGroup: string;\n version: number;\n stopLoadingTime?: () => void;\n}\n\ntype ViewLoadingType = NonNullable<BkOTViewContext['loadingType']>;\n\nconst PROTECTED_ATTRIBUTE_KEYS = new Set<string>(Object.values(RUM_ATTRIBUTES));\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nconst getNavigationTimingAttributes = (): Attributes => {\n if (typeof performance === 'undefined') return {};\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation) return {};\n const attributes: Attributes = {};\n const setTiming = (key: string, value: number) => {\n if (Number.isFinite(value) && value > 0) attributes[key] = value;\n };\n setTiming(RUM_ATTRIBUTES.viewFirstByte, navigation.responseStart);\n setTiming(RUM_ATTRIBUTES.viewDomInteractive, navigation.domInteractive);\n setTiming(RUM_ATTRIBUTES.viewDomContentLoaded, navigation.domContentLoadedEventEnd);\n setTiming(RUM_ATTRIBUTES.viewDomComplete, navigation.domComplete);\n setTiming(RUM_ATTRIBUTES.viewLoadEvent, navigation.loadEventEnd);\n return attributes;\n};\n\nconst getHashPath = (hash: string) => {\n const queryIndex = hash.indexOf('?');\n return queryIndex < 0 ? hash : hash.slice(0, queryIndex);\n};\n\nconst isHashAnchor = (hash: string) => {\n const id = hash.slice(1);\n if (!id || typeof document === 'undefined' || typeof document.getElementById !== 'function') return false;\n try {\n return Boolean(document.getElementById(decodeURIComponent(id)));\n } catch {\n return Boolean(document.getElementById(id));\n }\n};\n\nexport const isViewLocationChange = (fromUrl: string, toUrl: string) => {\n try {\n const base = typeof location === 'undefined' ? 'http://localhost' : location.href;\n const from = new URL(fromUrl, base);\n const to = new URL(toUrl, base);\n return from.pathname !== to.pathname || (!isHashAnchor(to.hash) && getHashPath(from.hash) !== getHashPath(to.hash));\n } catch {\n return fromUrl !== toUrl;\n }\n};\n\nexport const createPageViewPlugin = (): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n let activeView: ActiveView | undefined;\n let bfCacheView: Omit<ActiveView, 'stopLoadingTime'> | undefined;\n let renewalView: Omit<ActiveView, 'stopLoadingTime'> | undefined;\n\n const clearViewContext = (changedAt = Date.now()) => context.setView({}, changedAt);\n\n const emitViewEvent = (\n view: ActiveView,\n phase: 'end' | 'start' | 'update',\n timestamp = Date.now(),\n reason?: string,\n ) => {\n view.version += 1;\n const span = context.startSpan(\n 'browser.view',\n {\n ...context.config.hooks.getPageAttributes(),\n ...view.attributes,\n ...view.snapshotAttributes,\n [RUM_ATTRIBUTES.viewPhase]: phase,\n [RUM_ATTRIBUTES.viewStartedAt]: view.startedAt,\n [RUM_ATTRIBUTES.viewVersion]: view.version,\n ...(reason ? { 'view.end_reason': reason } : {}),\n ...(view.loadingTime === undefined\n ? {}\n : {\n [RUM_ATTRIBUTES.viewLoadingTime]: view.loadingTime,\n [RUM_ATTRIBUTES.viewLoadingTimeSource]: view.loadingTimeSource,\n }),\n },\n {\n contextTime: timestamp,\n startTime: timestamp,\n },\n );\n span.end(timestamp);\n };\n\n const endActiveView = (reason: string, clearContext = true, endedAt = Date.now()) => {\n if (!activeView) {\n if (clearContext) clearViewContext(endedAt);\n return undefined;\n }\n const snapshot = { ...activeView };\n activeView.stopLoadingTime?.();\n emitViewEvent(activeView, 'end', endedAt, reason);\n activeView = undefined;\n if (clearContext) clearViewContext(endedAt);\n const { stopLoadingTime: _stopLoadingTime, ...rest } = snapshot;\n return rest;\n };\n\n const setLoadingTime = (view: ActiveView, duration: number, source: 'auto' | 'manual') => {\n if (activeView !== view || !Number.isFinite(duration) || duration < 0) return;\n const navigationTimingAttributes = view.loadingType === 'initial_load' ? getNavigationTimingAttributes() : {};\n const updatedAttributes = {\n ...navigationTimingAttributes,\n [RUM_ATTRIBUTES.viewLoadingTime]: duration,\n [RUM_ATTRIBUTES.viewLoadingTimeSource]: source,\n };\n Object.assign(view.snapshotAttributes, updatedAttributes);\n view.loadingTime = duration;\n view.loadingTimeSource = source;\n emitViewEvent(view, 'update');\n };\n\n const startLoadingTimeTracking = (view: ActiveView) => {\n if (!context.config.view.loadingTime || document.visibilityState === 'hidden') return;\n let activityResolved = false;\n let activityCandidate: number | undefined;\n let loadResolved = view.loadingType !== 'initial_load';\n let loadCandidate: number | undefined;\n let loadListener: (() => void) | undefined;\n let hidden = false;\n let visibilityListener: (() => void) | undefined;\n\n const tryComplete = () => {\n if (hidden || !activityResolved || !loadResolved || activeView !== view) return;\n const candidates = [activityCandidate, loadCandidate].filter(\n (candidate): candidate is number => candidate !== undefined,\n );\n if (candidates.length) setLoadingTime(view, Math.max(...candidates), 'auto');\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n\n const readLoadEvent = () => {\n const navigation = performance.getEntriesByType?.('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation?.loadEventEnd) return false;\n loadCandidate = Math.max(0, performance.timeOrigin + navigation.loadEventEnd - view.startedAt);\n loadResolved = true;\n return true;\n };\n\n if (!loadResolved && !readLoadEvent()) {\n const onLoad = () => {\n setTimeout(() => {\n if (activeView !== view) return;\n readLoadEvent();\n loadResolved = true;\n tryComplete();\n });\n };\n window.addEventListener('load', onLoad, { once: true });\n loadListener = () => window.removeEventListener('load', onLoad);\n }\n\n const stopActivity = waitForPageActivityEnd(context, view.startedAt, result => {\n activityResolved = true;\n if (result.hadActivity) activityCandidate = Math.max(0, result.endTime - view.startedAt);\n tryComplete();\n });\n const onVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return;\n hidden = true;\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n view.stopLoadingTime = () => {\n stopActivity();\n loadListener?.();\n loadListener = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n };\n tryComplete();\n };\n\n const sanitizeViewAttributes = (attributes: Attributes | undefined) => {\n const redacted = context.applyRedact(attributes ?? {});\n return Object.fromEntries(\n Object.entries(redacted).filter(([key, value]) => value !== undefined && !PROTECTED_ATTRIBUTE_KEYS.has(key)),\n ) as Attributes;\n };\n\n const startView = (\n options: BkOTStartViewOptions,\n source: string,\n loadingType: ViewLoadingType,\n previousUrl = activeView?.rawUrl ?? '',\n ) => {\n if (!context.isSessionActive()) return;\n const currentUrl = options.url ?? getCurrentUrl();\n const pathGroup = context.config.hooks.getViewPathGroup(currentUrl);\n const name = options.name ?? pathGroup;\n const customAttributes = sanitizeViewAttributes(options.attributes);\n endActiveView(source, false);\n\n const viewId = createRandomId('view');\n const redactedCurrentUrl = context.config.hooks.redactUrl(currentUrl);\n const redactedPreviousUrl = context.config.hooks.redactUrl(previousUrl);\n const isInitialLoad = loadingType === 'initial_load';\n const startedAt = isInitialLoad && typeof performance !== 'undefined' ? performance.timeOrigin : Date.now();\n const navigationTimingAttributes = isInitialLoad ? getNavigationTimingAttributes() : {};\n\n context.setView(\n {\n attributes: customAttributes,\n id: viewId,\n loadingType,\n name,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n },\n startedAt,\n );\n\n const viewAttributes = {\n ...customAttributes,\n ...context.config.hooks.getPageAttributes(),\n ...navigationTimingAttributes,\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.view,\n }),\n [RUM_ATTRIBUTES.viewId]: viewId,\n [RUM_ATTRIBUTES.viewName]: name,\n [RUM_ATTRIBUTES.viewUrl]: redactedCurrentUrl,\n [RUM_ATTRIBUTES.viewUrlPathGroup]: pathGroup,\n [RUM_ATTRIBUTES.viewLoadingType]: loadingType,\n 'url.previous': redactedPreviousUrl,\n ...(isInitialLoad ? { 'document.referrer': context.config.hooks.redactUrl(document.referrer || '') } : {}),\n };\n activeView = {\n attributes: customAttributes,\n version: 0,\n id: viewId,\n loadingType,\n name,\n rawUrl: currentUrl,\n snapshotAttributes: viewAttributes,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n };\n emitViewEvent(activeView, 'start', startedAt);\n startLoadingTimeTracking(activeView);\n };\n\n const unsubscribeViewCommands = context.onViewCommand(command => {\n if (command.type === 'start') {\n startView(command.options, 'manual', activeView ? 'route_change' : 'initial_load');\n return;\n }\n if (!activeView) return;\n if (command.type === 'set_loading_time') {\n activeView.stopLoadingTime?.();\n activeView.stopLoadingTime = undefined;\n setLoadingTime(activeView, Math.max(0, command.time - activeView.startedAt), 'manual');\n return;\n }\n if (activeView.name === command.name) return;\n activeView.name = command.name;\n activeView.snapshotAttributes[RUM_ATTRIBUTES.viewName] = command.name;\n context.setView({\n attributes: activeView.attributes,\n id: activeView.id,\n loadingType: activeView.loadingType,\n name: activeView.name,\n startedAt: activeView.startedAt,\n url: activeView.url,\n urlPathGroup: activeView.urlPathGroup,\n });\n emitViewEvent(activeView, 'update');\n });\n\n let unsubscribeRoute = () => {};\n if (context.config.view.tracking === 'auto') {\n if (!activeView) startView({}, 'load', 'initial_load');\n unsubscribeRoute = subscribeRouteChange(event => {\n if (isViewLocationChange(event.fromUrl, event.toUrl)) {\n startView({}, event.source, 'route_change', event.fromUrl);\n }\n });\n }\n\n const onPageHide = (event: PageTransitionEvent) => {\n if (event.persisted) {\n bfCacheView = endActiveView('bfcache');\n return;\n }\n bfCacheView = undefined;\n endActiveView('pagehide');\n };\n const onPageShow = (event: PageTransitionEvent) => {\n if (event.persisted && context.config.view.tracking === 'auto') {\n const restoredView = bfCacheView;\n bfCacheView = undefined;\n if (!context.isSessionActive()) {\n renewalView = restoredView;\n return;\n }\n startView(\n restoredView\n ? { attributes: restoredView.attributes, name: restoredView.name, url: restoredView.rawUrl }\n : {},\n 'bfCache',\n 'bf_cache',\n restoredView?.rawUrl,\n );\n }\n };\n const unsubscribeSessionLifecycle = context.onSessionLifecycle(event => {\n if (event.type === 'expired') {\n renewalView = endActiveView('session_expired', true, event.expiredAt) ?? bfCacheView;\n bfCacheView = undefined;\n return;\n }\n if (renewalView) {\n startView(\n { attributes: renewalView.attributes, name: renewalView.name, url: renewalView.rawUrl },\n 'sessionRenewal',\n 'session_renewal',\n renewalView.rawUrl,\n );\n renewalView = undefined;\n }\n });\n window.addEventListener('pagehide', onPageHide);\n window.addEventListener('pageshow', onPageShow);\n\n teardownCallbacks.push(\n unsubscribeRoute,\n unsubscribeSessionLifecycle,\n unsubscribeViewCommands,\n () => window.removeEventListener('pagehide', onPageHide),\n () => window.removeEventListener('pageshow', onPageShow),\n () => endActiveView('shutdown'),\n );\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","export interface ActiveSessionRecord {\n expiresAt: number;\n id: string;\n startedAt: number;\n status: 'active';\n}\n\nexport interface ExpiredSessionRecord {\n expiredAt: number;\n previousId: string;\n reason: SessionExpireReason;\n status: 'expired';\n}\n\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nexport type SessionExpireReason = 'inactivity' | 'maxLifetime';\n\nexport type SessionRecord = ActiveSessionRecord | ExpiredSessionRecord;\n\nconst FUTURE_CLOCK_TOLERANCE_MS = 5_000;\n\nconst isTimestamp = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0;\n\nexport const createActiveSessionRecord = (\n id: string,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n expiresAt: Math.min(now + idleTimeoutMs, now + maxDurationMs),\n id,\n startedAt: now,\n status: 'active',\n});\n\nexport const getSessionExpireReason = (\n record: ActiveSessionRecord,\n now: number,\n maxDurationMs: number,\n): SessionExpireReason | undefined => {\n if (now >= record.startedAt + maxDurationMs) return 'maxLifetime';\n if (now >= record.expiresAt) return 'inactivity';\n return undefined;\n};\n\nexport const expandActiveSession = (\n record: ActiveSessionRecord,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n ...record,\n expiresAt: Math.min(now + idleTimeoutMs, record.startedAt + maxDurationMs),\n});\n\nexport const parseSessionRecord = (raw: null | string, now: number, maxDurationMs: number): null | SessionRecord => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<SessionRecord>;\n if (\n parsed.status === 'active' &&\n typeof parsed.id === 'string' &&\n parsed.id.length > 0 &&\n isTimestamp(parsed.startedAt) &&\n // 容忍轻微时钟漂移,但拒绝远期时间与超出最大生命周期的记录,避免脏 storage 永久续期。\n parsed.startedAt <= now + FUTURE_CLOCK_TOLERANCE_MS &&\n isTimestamp(parsed.expiresAt) &&\n parsed.expiresAt >= parsed.startedAt &&\n parsed.expiresAt <= parsed.startedAt + maxDurationMs\n ) {\n return parsed as ActiveSessionRecord;\n }\n if (\n parsed.status === 'expired' &&\n typeof parsed.previousId === 'string' &&\n parsed.previousId.length > 0 &&\n (parsed.reason === 'inactivity' || parsed.reason === 'maxLifetime') &&\n isTimestamp(parsed.expiredAt) &&\n parsed.expiredAt <= now + FUTURE_CLOCK_TOLERANCE_MS\n ) {\n return parsed as ExpiredSessionRecord;\n }\n } catch {\n /* JSON 损坏视为无记录 */\n }\n return null;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Session 插件\n *\n * 生命周期:\n * - 首次访问创建 session;用户交互会续期有效 session,或重建已过期 session\n * - 固定 15 分钟无活动 / 4 小时最长生命周期,任一到达时立即清理运行时 session.id\n * - 页面重新可见时只续期有效 session,不会复活已过期 session\n * - 跨 tab 共享持久化状态;本 tab 过期后不会被其他 tab 的新 session 被动复活\n */\n\nimport { hashString } from '../core/hash';\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\nimport {\n createActiveSessionRecord,\n expandActiveSession,\n getSessionExpireReason,\n parseSessionRecord,\n} from './session-state';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { ActiveSessionRecord, ExpiredSessionRecord, SessionExpireReason, SessionRecord } from './session-state';\n\nexport interface BkOTSessionConfig {\n /** 以 0~1 表示的 Session 采样率,默认 1。 */\n sampleRate?: number;\n // 自定义 storage key;不传则按 endpoint host 自动生成隔离后缀\n storageKey?: string;\n}\n\nconst SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;\nconst SESSION_MAX_DURATION_MS = 4 * 60 * 60 * 1000;\nconst ACTIVITY_THROTTLE_MS = 1_000;\nconst VISIBILITY_CHECK_MS = 60_000;\nconst ACTIVITY_EVENTS: Array<keyof WindowEventMap> = ['click', 'keydown', 'scroll', 'touchstart'];\nconst memoryRecords = new Map<string, string>();\n\ninterface SessionStorage {\n isLocalStorage: () => boolean;\n read: () => null | string;\n write: (record: SessionRecord) => void;\n}\n\nconst createSessionStorage = (storageKey: string): SessionStorage => {\n let localStorageAvailable = typeof window !== 'undefined';\n\n const read = () => {\n if (localStorageAvailable) {\n try {\n return window.localStorage.getItem(storageKey);\n } catch {\n localStorageAvailable = false;\n }\n }\n return memoryRecords.get(storageKey) ?? null;\n };\n\n const write = (record: SessionRecord) => {\n const serialized = JSON.stringify(record);\n memoryRecords.set(storageKey, serialized);\n if (!localStorageAvailable) return;\n try {\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n localStorageAvailable = false;\n }\n };\n\n read();\n return { isLocalStorage: () => localStorageAvailable, read, write };\n};\n\nconst buildDefaultStorageKey = (endpoint: string, applicationName: string) => {\n let suffix = '';\n try {\n suffix = new URL(endpoint).host;\n } catch {\n suffix = endpoint;\n }\n return `bk_ot_session__${hashString(`${applicationName}\\0${suffix || 'default'}`)}`;\n};\n\nconst emitSessionEvent = (\n context: BkOTRuntimeContext,\n phase: 'end' | 'rotate' | 'start',\n sessionId: string,\n startedAt: number,\n timestamp: number,\n reason: string,\n previousId?: string,\n) => {\n const span = context.startSpan(\n 'browser.session',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.session,\n }),\n [RUM_ATTRIBUTES.sessionPhase]: phase,\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: sessionId,\n [RUM_ATTRIBUTES.sessionStartTime]: startedAt,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n 'session.lifecycle.reason': reason,\n ...(previousId ? { 'session.previous_id': previousId } : {}),\n },\n {\n contextTime: timestamp,\n startTime: timestamp,\n },\n );\n span.end(timestamp);\n};\n\nexport const createSessionPlugin = (options: BkOTSessionConfig = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'session',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n const now = Date.now();\n const sessionId = createRandomId('session');\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: sessionId,\n [RUM_ATTRIBUTES.sessionStartTime]: now,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n },\n now,\n );\n return;\n }\n\n const storageKey =\n options.storageKey ??\n buildDefaultStorageKey(context.config.transport.endpoint, context.config.application.name);\n const storage = createSessionStorage(storageKey);\n const documentStartedAt =\n typeof performance !== 'undefined' && Number.isFinite(performance.timeOrigin)\n ? performance.timeOrigin\n : Date.now();\n let currentSessionId: string | undefined;\n let currentSessionStartedAt: number | undefined;\n let expirationTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const readRecord = (now = Date.now()) => parseSessionRecord(storage.read(), now, SESSION_MAX_DURATION_MS);\n\n const clearExpirationTimeout = () => {\n if (expirationTimeout !== undefined) {\n clearTimeout(expirationTimeout);\n expirationTimeout = undefined;\n }\n };\n\n const deactivate = (reason: 'external' | SessionExpireReason, expiredAt = Date.now()) => {\n const expiredSessionId = currentSessionId;\n if (!expiredSessionId) return;\n context.notifySessionLifecycle({\n expiredAt,\n id: expiredSessionId,\n reason,\n type: 'expired',\n });\n emitSessionEvent(context, 'end', expiredSessionId, currentSessionStartedAt ?? expiredAt, expiredAt, reason);\n clearExpirationTimeout();\n currentSessionId = undefined;\n currentSessionStartedAt = undefined;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n [RUM_ATTRIBUTES.sessionStartTime]: undefined,\n [RUM_ATTRIBUTES.sessionType]: undefined,\n },\n expiredAt,\n );\n void context.flush();\n };\n\n const expireCurrentSession = () => {\n const now = Date.now();\n const record = readRecord(now);\n if (!record) {\n deactivate('external', now);\n return;\n }\n if (record.status === 'expired') {\n deactivate(record.reason, record.expiredAt);\n return;\n }\n if (record.id !== currentSessionId) {\n deactivate('external', now);\n return;\n }\n const reason = getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS);\n if (!reason) {\n scheduleExpiration(record);\n return;\n }\n const expired: ExpiredSessionRecord = {\n expiredAt: now,\n previousId: record.id,\n reason,\n status: 'expired',\n };\n storage.write(expired);\n deactivate(reason, now);\n };\n\n const scheduleExpiration = (record: ActiveSessionRecord) => {\n clearExpirationTimeout();\n const expiresAt = Math.min(record.expiresAt, record.startedAt + SESSION_MAX_DURATION_MS);\n expirationTimeout = setTimeout(expireCurrentSession, Math.max(0, expiresAt - Date.now()));\n };\n\n const activate = (\n record: ActiveSessionRecord,\n lifecycle?: {\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n },\n ) => {\n const changed = record.id !== currentSessionId;\n currentSessionId = record.id;\n currentSessionStartedAt = record.startedAt;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: record.id,\n [RUM_ATTRIBUTES.sessionStartTime]: record.startedAt,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n },\n Math.min(record.startedAt, documentStartedAt),\n );\n scheduleExpiration(record);\n if (changed && lifecycle) {\n context.notifySessionLifecycle({\n id: record.id,\n previousId: lifecycle.previousId,\n reason: lifecycle.reason,\n startedAt: record.startedAt,\n type: 'renewed',\n });\n }\n };\n\n const expandOrRenew = (isInitialization = false) => {\n const now = Date.now();\n const record = readRecord(now);\n if (record?.status === 'active' && !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)) {\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded, currentSessionId ? undefined : { reason: 'shared' });\n return;\n }\n\n const previousId = record?.status === 'expired' ? record.previousId : record?.id;\n const reason =\n record?.status === 'expired'\n ? record.reason\n : record\n ? getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n : isInitialization\n ? 'init'\n : undefined;\n const created = createActiveSessionRecord(\n createRandomId('session'),\n now,\n SESSION_IDLE_TIMEOUT_MS,\n SESSION_MAX_DURATION_MS,\n );\n storage.write(created);\n activate(created, {\n previousId,\n reason: reason ?? 'init',\n });\n const lifecycleReason = reason ?? 'init';\n emitSessionEvent(\n context,\n lifecycleReason === 'init' ? 'start' : 'rotate',\n created.id,\n created.startedAt,\n created.startedAt,\n lifecycleReason,\n previousId,\n );\n };\n\n const expandOnly = () => {\n if (!currentSessionId) return;\n const now = Date.now();\n const record = readRecord(now);\n if (\n record?.status !== 'active' ||\n record.id !== currentSessionId ||\n getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n expireCurrentSession();\n return;\n }\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded);\n };\n\n expandOrRenew(true);\n\n let lastActivityAt = 0;\n const onActivity = () => {\n const now = Date.now();\n if (now - lastActivityAt < ACTIVITY_THROTTLE_MS) return;\n lastActivityAt = now;\n expandOrRenew();\n };\n const activityListenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n ACTIVITY_EVENTS.forEach(eventName => window.addEventListener(eventName, onActivity, activityListenerOptions));\n teardownCallbacks.push(() => {\n ACTIVITY_EVENTS.forEach(eventName =>\n window.removeEventListener(eventName, onActivity, activityListenerOptions),\n );\n });\n\n const expandWhenVisible = () => {\n if (document.visibilityState === 'visible') expandOnly();\n };\n document.addEventListener('visibilitychange', expandWhenVisible);\n const visibilityInterval = window.setInterval(expandWhenVisible, VISIBILITY_CHECK_MS);\n teardownCallbacks.push(() => {\n document.removeEventListener('visibilitychange', expandWhenVisible);\n window.clearInterval(visibilityInterval);\n });\n\n const onStorage = (event: StorageEvent) => {\n if (!storage.isLocalStorage() || event.storageArea !== window.localStorage || event.key !== storageKey) return;\n const now = Date.now();\n const record = parseSessionRecord(event.newValue, now, SESSION_MAX_DURATION_MS);\n if (\n record?.status === 'active' &&\n record.id === currentSessionId &&\n !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n scheduleExpiration(record);\n return;\n }\n // 当前 tab 一旦失效,不被其他 tab 新建的 session 被动复活;本 tab 发生交互后再接管共享 session。\n deactivate(\n record?.status === 'expired' ? record.reason : 'external',\n record?.status === 'expired' ? record.expiredAt : now,\n );\n };\n window.addEventListener('storage', onStorage);\n teardownCallbacks.push(() => window.removeEventListener('storage', onStorage));\n teardownCallbacks.push(clearExpirationTimeout);\n },\n shutdown() {\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Web Vitals 插件\n *\n * 基于 google/web-vitals 库(attribution 版本)采集 Core Web Vitals:\n * CLS / FCP / INP / LCP / TTFB\n *\n * browser.web_vital span 带 vital.id(唯一)、vital.value 与 attribution 详情\n * (LCP 元素 url / CLS shift target / INP interaction target 等高基数字段),\n * 方便按个例排查\"哪个元素拖累了 LCP\"\n *\n * 数值语义:CLS 是无量纲分数,FCP/INP/LCP/TTFB 的 vital.value 单位为 ms。\n *\n * View 边界:FCP/LCP/TTFB 使用 web-vitals 的条目时间回查历史 View;CLS/INP 按 SDK View 重置,\n * CLS 使用 session window,INP 使用当前 View 的 p98 交互候选。\n *\n * 生命周期保护:\n * - disposed 标志位:shutdown 后所有回调入口 early return,避免向已销毁的 provider 上报\n * - dynamic import 完成后再次检查 disposed:处理\"start() 完成前 shutdown()\"的 race\n * - dynamic import 自身 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n * 即使加载失败也不影响整体启动\n */\n\nimport { type Attributes } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, getVitalOutcome, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type * as WebVitalsAttribution from 'web-vitals/attribution';\nimport type { MetricWithAttribution } from 'web-vitals/attribution';\n\ninterface EventTimingEntry extends PerformanceEntry {\n interactionId?: number;\n processingEnd?: number;\n processingStart?: number;\n target?: EventTarget | null;\n}\n\ninterface InpCandidate {\n duration: number;\n inputDelay: number;\n interactionId: number;\n interactionTarget?: string;\n interactionType: string;\n presentationDelay: number;\n processingDuration: number;\n}\n\ninterface LayoutShiftEntry extends PerformanceEntry {\n hadRecentInput?: boolean;\n sources?: Array<{ node?: Node | null }>;\n value?: number;\n}\n\ninterface ViewVitalState {\n cls: number;\n clsWindowEnd?: number;\n clsWindowLargestShiftTarget?: string;\n clsWindowLargestShiftValue: number;\n clsWindowStart?: number;\n clsWindowValue: number;\n id: string;\n inpCandidates: InpCandidate[];\n interactionCountAtStart?: number;\n interactionIdMax: number;\n interactionIdMin: number;\n largestShiftTarget?: string;\n largestShiftValue: number;\n runtimeAttributes: Attributes;\n startedAt: number;\n}\n\ntype VitalLike = Pick<\n MetricWithAttribution,\n 'attribution' | 'entries' | 'id' | 'name' | 'rating' | 'value'\n>;\n\nconst MAX_INP_CANDIDATES = 10;\nconst MAX_INP_VALUE = 60_000;\nconst MAX_CLS_WINDOW_DURATION = 5_000;\nconst MAX_CLS_UPDATE_GAP = 1_000;\n\nexport const isSameViewId = (currentViewId: string | undefined, nextViewId: string | undefined) =>\n Boolean(nextViewId) && currentViewId === nextViewId;\n\nconst getRating = (name: 'CLS' | 'INP', value: number): VitalLike['rating'] => {\n const [good, poor] = name === 'CLS' ? [0.1, 0.25] : [200, 500];\n return value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n};\n\nconst getTargetName = (target: EventTarget | Node | null | undefined) => {\n if (typeof Element === 'undefined' || !(target instanceof Element)) return undefined;\n return (\n target.getAttribute('data-bk-action-name') ??\n target.getAttribute('aria-label') ??\n target.getAttribute('name') ??\n target.tagName.toLowerCase()\n );\n};\n\nconst getInteractionCount = () => {\n if (typeof performance === 'undefined') return undefined;\n const value = (performance as Performance & { interactionCount?: number }).interactionCount;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n};\n\nexport const updateViewCls = (view: ViewVitalState, entry: LayoutShiftEntry) => {\n const value = entry.value ?? 0;\n if (entry.hadRecentInput || !Number.isFinite(value)) return;\n const shouldCreateWindow =\n view.clsWindowStart === undefined ||\n entry.startTime - (view.clsWindowEnd ?? entry.startTime) >= MAX_CLS_UPDATE_GAP ||\n entry.startTime - view.clsWindowStart >= MAX_CLS_WINDOW_DURATION;\n if (shouldCreateWindow) {\n view.clsWindowStart = entry.startTime;\n view.clsWindowValue = value;\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n } else {\n view.clsWindowValue += value;\n if (value > view.clsWindowLargestShiftValue) {\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n }\n }\n view.clsWindowEnd = entry.startTime;\n if (view.clsWindowValue > view.cls) {\n view.cls = Math.round(view.clsWindowValue * 10_000) / 10_000;\n view.largestShiftValue = view.clsWindowLargestShiftValue;\n view.largestShiftTarget = view.clsWindowLargestShiftTarget;\n }\n};\n\nexport const updateViewInp = (view: ViewVitalState, entry: EventTimingEntry) => {\n if (!entry.interactionId || !Number.isFinite(entry.duration)) return;\n view.interactionIdMin = Math.min(view.interactionIdMin, entry.interactionId);\n view.interactionIdMax = Math.max(view.interactionIdMax, entry.interactionId);\n const processingStart = entry.processingStart ?? entry.startTime;\n const processingEnd = entry.processingEnd ?? processingStart;\n const candidate: InpCandidate = {\n duration: Math.min(entry.duration, MAX_INP_VALUE),\n inputDelay: Math.max(0, processingStart - entry.startTime),\n interactionId: entry.interactionId,\n interactionTarget: getTargetName(entry.target),\n interactionType: entry.name,\n presentationDelay: Math.max(0, entry.startTime + entry.duration - processingEnd),\n processingDuration: Math.max(0, processingEnd - processingStart),\n };\n const index = view.inpCandidates.findIndex(item => item.interactionId === entry.interactionId);\n if (index >= 0) {\n if (candidate.duration > view.inpCandidates[index].duration) view.inpCandidates[index] = candidate;\n } else {\n view.inpCandidates.push(candidate);\n }\n view.inpCandidates.sort((left, right) => right.duration - left.duration).splice(MAX_INP_CANDIDATES);\n};\n\nexport const getViewInp = (view: ViewVitalState): InpCandidate | undefined => {\n const nativeCount = getInteractionCount();\n const interactionCount =\n nativeCount !== undefined && view.interactionCountAtStart !== undefined\n ? Math.max(0, nativeCount - view.interactionCountAtStart)\n : view.interactionIdMin === Infinity\n ? 0\n : (view.interactionIdMax - view.interactionIdMin) / 7 + 1;\n if (!view.inpCandidates.length) return undefined;\n return view.inpCandidates[Math.min(view.inpCandidates.length - 1, Math.floor(interactionCount / 50))];\n};\n\n// 把 attribution 中价值最高的字段抽出来写入 Span。\nconst pickAttribution = (metric: VitalLike, redactUrl: (url: string) => string): Attributes => {\n const attr = metric.attribution ?? {};\n const result: Attributes = {};\n const getAttrValue = (key: string) => (attr as Record<string, unknown>)[key];\n\n const setIfDefined = (key: string, value: unknown, transform?: (value: string) => string) => {\n if (value === undefined || value === null) return;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n result[key] = typeof value === 'string' && transform ? transform(value) : value;\n }\n };\n\n switch (metric.name) {\n case 'LCP':\n setIfDefined('vital.lcp.url', getAttrValue('url'), redactUrl);\n setIfDefined('vital.lcp.target', getAttrValue('target'));\n setIfDefined('vital.lcp.element_render_delay', getAttrValue('elementRenderDelay'));\n setIfDefined('vital.lcp.resource_load_duration', getAttrValue('resourceLoadDuration'));\n setIfDefined('vital.lcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n break;\n case 'CLS':\n setIfDefined('vital.cls.largest_shift_target', getAttrValue('largestShiftTarget'));\n setIfDefined('vital.cls.largest_shift_value', getAttrValue('largestShiftValue'));\n setIfDefined('vital.cls.load_state', getAttrValue('loadState'));\n break;\n case 'INP':\n setIfDefined('vital.inp.interaction_target', getAttrValue('interactionTarget'));\n setIfDefined('vital.inp.interaction_type', getAttrValue('interactionType'));\n setIfDefined('vital.inp.input_delay', getAttrValue('inputDelay'));\n setIfDefined('vital.inp.processing_duration', getAttrValue('processingDuration'));\n setIfDefined('vital.inp.presentation_delay', getAttrValue('presentationDelay'));\n break;\n case 'FCP':\n setIfDefined('vital.fcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n setIfDefined('vital.fcp.load_state', getAttrValue('loadState'));\n break;\n case 'TTFB':\n setIfDefined('vital.ttfb.waiting_duration', getAttrValue('waitingDuration'));\n setIfDefined('vital.ttfb.dns_duration', getAttrValue('dnsDuration'));\n setIfDefined('vital.ttfb.connection_duration', getAttrValue('connectionDuration'));\n setIfDefined('vital.ttfb.request_duration', getAttrValue('requestDuration'));\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (): BkOTPlugin => {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n return {\n name: 'web-vitals',\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // dynamic import 自包 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n // 离线 / chunk 加载失败时不影响其他插件,且 debug 模式留下排查痕迹\n let webVitalsModule: typeof WebVitalsAttribution;\n try {\n webVitalsModule = await import('web-vitals/attribution');\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn('[bk-ot] web-vitals load failed:', error);\n }\n return;\n }\n // import 期间用户可能已经 shutdown:此后再注册回调会向已销毁 provider 上报\n if (disposed) return;\n\n const { onFCP, onLCP, onTTFB } = webVitalsModule;\n const recordSpan = (\n metric: VitalLike,\n runtimeAttributes: Attributes,\n contextTime?: number,\n occurredAt = contextTime ?? Date.now(),\n ) => {\n if (!context.isSampled()) return;\n const metricName = metric.name.toLowerCase();\n const span = context.startSpan(\n 'browser.web_vital',\n {\n ...context.config.hooks.getPageAttributes(),\n ...runtimeAttributes,\n ...buildRumSpanAttributes({\n outcome: getVitalOutcome(metric.rating),\n spanType: RUM_SPAN_TYPE.vital,\n }),\n [RUM_ATTRIBUTES.vitalMetric]: metricName,\n [RUM_ATTRIBUTES.vitalValue]: metric.value,\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n 'vital.id': metric.id,\n ...pickAttribution(metric, context.config.hooks.redactUrl),\n },\n { contextTime, startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n const recordDurationVital = (metric: VitalLike) => {\n // 防御非法值:极端浏览器实现 bug 可能给出 NaN/Infinity,OT 后端会拒收\n if (disposed || !Number.isFinite(metric.value) || !context.isSampled()) return;\n const firstEntry = metric.entries[0];\n const contextTime =\n firstEntry && typeof performance !== 'undefined' ? performance.timeOrigin + firstEntry.startTime : undefined;\n const runtimeAttributes =\n contextTime === undefined ? context.getRuntimeAttributes() : context.getRuntimeAttributesAt(contextTime);\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n let currentView: undefined | ViewVitalState;\n let eventObserver: PerformanceObserver | undefined;\n let layoutShiftObserver: PerformanceObserver | undefined;\n\n const recordViewVital = (metric: VitalLike, runtimeAttributes: Attributes, contextTime: number) => {\n recordSpan(metric, runtimeAttributes, contextTime, Date.now());\n };\n\n const finalizeView = () => {\n const view = currentView;\n if (!view || !context.isSampled()) return;\n recordViewVital(\n {\n attribution: {\n largestShiftTarget: view.largestShiftTarget,\n largestShiftValue: view.largestShiftValue,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'CLS',\n rating: getRating('CLS', view.cls),\n value: view.cls,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n const inp = getViewInp(view);\n if (inp) {\n recordViewVital(\n {\n attribution: {\n inputDelay: inp.inputDelay,\n interactionTarget: inp.interactionTarget,\n interactionType: inp.interactionType,\n presentationDelay: inp.presentationDelay,\n processingDuration: inp.processingDuration,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'INP',\n rating: getRating('INP', inp.duration),\n value: inp.duration,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n }\n };\n\n const unsubscribeView = context.onViewChange(view => {\n const activeVitalView = currentView;\n if (activeVitalView && isSameViewId(activeVitalView.id, view.id)) {\n activeVitalView.runtimeAttributes = {\n ...activeVitalView.runtimeAttributes,\n [RUM_ATTRIBUTES.viewId]: view.id,\n [RUM_ATTRIBUTES.viewName]: view.name,\n [RUM_ATTRIBUTES.viewUrl]: view.url,\n [RUM_ATTRIBUTES.viewUrlPathGroup]: view.urlPathGroup,\n };\n return;\n }\n finalizeView();\n currentView = view.id\n ? {\n cls: 0,\n clsWindowLargestShiftValue: 0,\n clsWindowValue: 0,\n interactionCountAtStart: getInteractionCount(),\n interactionIdMax: 0,\n interactionIdMin: Infinity,\n inpCandidates: [],\n id: view.id,\n largestShiftValue: 0,\n runtimeAttributes: context.getRuntimeAttributes(),\n startedAt: view.startedAt ?? Date.now(),\n }\n : undefined;\n });\n\n if (typeof PerformanceObserver !== 'undefined') {\n if (PerformanceObserver.supportedEntryTypes?.includes('layout-shift')) {\n layoutShiftObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as LayoutShiftEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt) continue;\n updateViewCls(currentView, entry);\n }\n });\n layoutShiftObserver.observe({ buffered: true, type: 'layout-shift' });\n }\n if (PerformanceObserver.supportedEntryTypes?.includes('event')) {\n eventObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as EventTimingEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt || !entry.interactionId || !Number.isFinite(entry.duration))\n continue;\n updateViewInp(currentView, entry);\n }\n });\n eventObserver.observe({ buffered: true, durationThreshold: 40, type: 'event' });\n }\n }\n onFCP(recordDurationVital);\n onLCP(recordDurationVital);\n onTTFB(recordDurationVital);\n\n cleanup = () => {\n finalizeView();\n currentView = undefined;\n unsubscribeView();\n layoutShiftObserver?.disconnect();\n eventObserver?.disconnect();\n };\n },\n shutdown() {\n // web-vitals 的 onXXX 不返回 unsubscribe,无法真正取消订阅;\n // 用 disposed 标志位让回调入口 early return 即可\n disposed = true;\n cleanup?.();\n cleanup = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { SpanKind } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nconst buildWebSocketAttributes = (url: string, redactUrl: (url: string) => string): Attributes => {\n const attributes: Attributes = {\n 'network.protocol.name': 'websocket',\n 'url.full': redactUrl(url),\n };\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n attributes['server.address'] = parsed.hostname;\n if (parsed.port) attributes['server.port'] = Number(parsed.port);\n attributes['url.scheme'] = parsed.protocol.replace(':', '');\n } catch {\n // 非法 URL 仍保留脱敏后的原始字符串。\n }\n return attributes;\n};\n\nconst instrumentSocket = (socket: WebSocket, context: BkOTRuntimeContext, url: string, isDisposed: () => boolean) => {\n const connectionId = createRandomId('websocket');\n const baseAttributes = buildWebSocketAttributes(url, context.config.hooks.redactUrl);\n let opened = false;\n\n const emit = (phase: 'close' | 'connect' | 'error' | 'open', timestamp: number, attributes: Attributes = {}) => {\n if (isDisposed() || !context.isSampled()) return;\n const isError = phase === 'error';\n const span = context.startSpan(\n 'browser.websocket',\n {\n ...baseAttributes,\n ...buildRumSpanAttributes({\n outcome: isError ? RUM_SPAN_OUTCOME.error : RUM_SPAN_OUTCOME.success,\n outcomeReason: isError ? 'network' : undefined,\n spanType: RUM_SPAN_TYPE.websocket,\n }),\n [RUM_ATTRIBUTES.websocketPhase]: phase,\n 'websocket.connection.id': connectionId,\n ...attributes,\n },\n { kind: SpanKind.CLIENT, startTime: timestamp },\n );\n span.end(timestamp);\n };\n\n emit('connect', Date.now());\n socket.addEventListener('open', () => {\n opened = true;\n emit('open', Date.now());\n });\n socket.addEventListener('error', () => {\n emit('error', Date.now(), { 'websocket.error.phase': opened ? 'runtime' : 'connect' });\n });\n socket.addEventListener('close', event => {\n emit('close', Date.now(), {\n 'websocket.close.code': event.code,\n 'websocket.close.reason': event.reason,\n 'websocket.close.was_clean': event.wasClean,\n });\n });\n};\n\nexport const createWebSocketPlugin = (): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n let patchedProxy: typeof WebSocket | undefined;\n let disposed = false;\n\n return {\n name: 'websocket',\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') return;\n const nativeWebSocket = window.WebSocket;\n originalWebSocket = nativeWebSocket;\n patchedProxy = new Proxy(nativeWebSocket, {\n construct(target, args, newTarget) {\n const socket = Reflect.construct(target, args, newTarget) as WebSocket;\n if (disposed || !context.isSampled()) return socket;\n const url = args[0] == null ? '' : String(args[0]);\n if (url && !shouldIgnoreUrl(context.config, url)) {\n instrumentSocket(socket, context, url, () => disposed);\n }\n return socket;\n },\n });\n window.WebSocket = patchedProxy;\n },\n shutdown() {\n disposed = true;\n if (typeof window !== 'undefined' && originalWebSocket && window.WebSocket === patchedProxy) {\n window.WebSocket = originalWebSocket;\n }\n patchedProxy = undefined;\n originalWebSocket = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nexport type BkOTActivityEvent =\n | { endTime: number; startTime: number; type: 'long_task'; url?: never }\n | { endTime: number; startTime: number; type: 'resource'; url?: string }\n | { id: string; time: number; type: 'request_end' }\n | { id: string; time: number; type: 'request_start'; url?: string }\n | { time: number; type: 'dom_mutation' | 'error' | 'window_open' };\n\nexport class RumActivityObserver {\n private readonly listeners = new Set<(event: BkOTActivityEvent) => void>();\n\n public notify = (event: BkOTActivityEvent) => {\n this.listeners.forEach(listener => listener(event));\n };\n\n public subscribe = (listener: (event: BkOTActivityEvent) => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { ResolvedBkOTConfig } from './config';\n\nconst hashToUnitInterval = (input: string) => {\n let hash = 0x811c9dc5;\n for (let index = 0; index < input.length; index += 1) {\n hash = Math.imul(hash ^ input.charCodeAt(index), 0x01000193);\n }\n hash += hash << 13;\n hash ^= hash >>> 7;\n hash += hash << 3;\n hash ^= hash >>> 17;\n hash += hash << 5;\n return (hash >>> 0) / 0x100000000;\n};\n\n/**\n * 按应用和 RUM Session ID 做确定性采样。采样决定随 Session 更新,不再依赖 Tab 级 sessionStorage。\n */\nexport const isBkOTSampled = (\n { application, session }: Pick<ResolvedBkOTConfig, 'application' | 'session'>,\n sessionId: string,\n) => {\n if (session.sampleRate <= 0) {\n return false;\n }\n if (session.sampleRate >= 1) {\n return true;\n }\n return hashToUnitInterval(`${application.name}\\0${sessionId}`) < session.sampleRate;\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { type Attributes, type Span, context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchSpanProcessor, SamplingDecision, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\nimport { createActionPlugin } from '../plugins/action';\nimport { createActivityPlugin } from '../plugins/activity';\nimport { createBlankScreenPlugin } from '../plugins/blank-screen';\nimport { createCommonInstrumentationsPlugin } from '../plugins/common';\nimport { createCspViolationPlugin } from '../plugins/csp-violation';\nimport { createDevicePlugin } from '../plugins/device';\nimport { createErrorPlugin } from '../plugins/error';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createResourcePlugin } from '../plugins/resource';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\nimport { RumActivityObserver } from './action-activity';\nimport {\n type BkOTActionEventPayload,\n type BkOTCustomEventPayload,\n type BkOTOptions,\n type BkOTTrackingConfig,\n type BkOTTrackingConsent,\n type ResolvedBkOTConfig,\n type ResolvedTransportConfig,\n resolveConfig,\n} from './config';\nimport { createRandomId } from './id';\nimport { type BkOTPlugin, type BkOTRuntimeContext, type BkOTStartSpanOptions, PluginManager } from './plugin';\nimport { FilteringSpanProcessor } from './processor';\nimport {\n type BkOTSessionLifecycleEvent,\n type BkOTStartViewOptions,\n type BkOTUserContext,\n RumRuntimeContext,\n} from './runtime-context';\nimport { isBkOTSampled } from './sampling';\n\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport type { Sampler } from '@opentelemetry/sdk-trace-base';\n\nexport interface BkOTInstance extends Omit<\n BkOTRuntimeContext,\n 'notifyActivity' | 'onActivity' | 'onViewChange' | 'onViewCommand' | 'registerInstrumentations' | 'setView'\n> {\n flush: () => Promise<void>;\n reportAction: (payload: BkOTActionEventPayload) => void;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n setTrackingConsent: (consent: BkOTTrackingConsent) => Promise<void>;\n setUser: (user: BkOTUserContext) => void;\n setViewLoadingTime: () => void;\n setViewName: (name: string) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n startView: (options?: BkOTStartViewOptions) => void;\n use: (plugin: BkOTPlugin) => BkOTInstance;\n useInstrumentation: (instrumentation: Instrumentation) => BkOTInstance;\n}\n\nconst createExporterOptions = (config: ResolvedTransportConfig) => ({\n url: config.endpoint,\n headers: config.headers,\n concurrencyLimit: config.concurrencyLimit,\n timeoutMillis: config.timeoutMillis,\n});\n\nconst BK_OT_INSTRUMENTATION_NAME = 'bk-rum';\n\nclass RumSessionSampler implements Sampler {\n public constructor(private readonly isSampled: () => boolean) {}\n\n public shouldSample() {\n return {\n decision: this.isSampled() ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,\n };\n }\n\n public toString() {\n return 'RumSessionSampler';\n }\n}\n\n// OTel context manager 和 propagator 是页面级全局对象,多 SDK 实例通过引用计数共享,最后一个实例退出时释放。\nlet globalRegistration:\n | undefined\n | {\n contextManager?: ZoneContextManager;\n ownsContext: boolean;\n ownsPropagator: boolean;\n users: number;\n };\n\nconst acquireOtelGlobals = () => {\n if (globalRegistration) {\n globalRegistration.users += 1;\n } else {\n const contextManager = new ZoneContextManager().enable();\n const ownsContext = context.setGlobalContextManager(contextManager);\n if (!ownsContext) contextManager.disable();\n globalRegistration = {\n contextManager: ownsContext ? contextManager : undefined,\n ownsContext,\n ownsPropagator: propagation.setGlobalPropagator(new W3CTraceContextPropagator()),\n users: 1,\n };\n }\n let released = false;\n return () => {\n if (released || !globalRegistration) return;\n released = true;\n globalRegistration.users -= 1;\n if (globalRegistration.users > 0) return;\n if (globalRegistration.ownsContext) context.disable();\n if (globalRegistration.ownsPropagator) propagation.disable();\n globalRegistration = undefined;\n };\n};\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n enabled: boolean,\n): TExporter => {\n if (!enabled) {\n return exporter;\n }\n return new Proxy(exporter, {\n get(target, prop, receiver) {\n if (prop === 'export') {\n return (records: unknown, callback: unknown) => {\n globalThis.console?.log('[bk-ot][rum]', records);\n return (target as { export: (...args: unknown[]) => unknown }).export(records, callback);\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n};\n\n// 请求插件统一负责 Fetch/XHR span、Trace Header 与活动计数。\nconst getCorePlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.tracking.request),\n];\n\nconst getRumContextPlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createSessionPlugin(config.session),\n ...createBuiltInPlugin(config.tracking.device, true, createDevicePlugin),\n createActivityPlugin(),\n createPageViewPlugin(),\n createResourcePlugin(isBuiltInPluginEnabled(config.tracking.resource, true)),\n];\n\nconst isBuiltInPluginEnabled = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n) => (option === undefined ? defaultEnabled : option !== false);\n\nconst getBuiltInPluginOptions = <TOptions extends object>(option: boolean | TOptions | undefined) =>\n typeof option === 'object' && option !== null ? option : undefined;\n\nconst createBuiltInPlugin = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n factory: (options?: TOptions) => BkOTPlugin,\n) => (isBuiltInPluginEnabled(option, defaultEnabled) ? [factory(getBuiltInPluginOptions(option))] : []);\n\n// 上下文插件先启动,确保后续 span 能带上关联字段;Session sampler 与 processor 负责统一采样。\nconst getRumPlugins = (tracking: BkOTTrackingConfig): BkOTPlugin[] => [\n ...createBuiltInPlugin(tracking.error, true, createErrorPlugin),\n ...createBuiltInPlugin(tracking.webVitals, true, createWebVitalsPlugin),\n ...createBuiltInPlugin(tracking.blankScreen, true, createBlankScreenPlugin),\n ...createBuiltInPlugin(tracking.action, true, createActionPlugin),\n ...createBuiltInPlugin(tracking.websocket, false, createWebSocketPlugin),\n ...createBuiltInPlugin(tracking.longTask, false, createLongTaskPlugin),\n ...createBuiltInPlugin(tracking.cspViolation, false, createCspViolationPlugin),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'starting' | 'stopped';\n\n/**\n * 浏览器 RUM SDK 的生命周期入口。Provider 在构造时创建,插件和全局 patch 则随 start、授权变更和\n * shutdown 安装或卸载;Session 决定统一作用于所有 RUM span。\n */\nexport class BkOpenTelemetry implements BkOTInstance {\n private readonly activityObserver = new RumActivityObserver();\n private readonly flushProviders = async () => {\n await this.tracerProvider.forceFlush();\n };\n private phase: SdkLifecyclePhase = 'idle';\n private pluginManager?: PluginManager;\n private readonly registeredInstrumentations: Instrumentation[];\n private releaseOtelGlobals?: () => void;\n private readonly runtimeContext: RumRuntimeContext;\n\n private sampled = false;\n private readonly startCollection = async () => {\n // Provider 始终存在以保持公开 API 稳定;采集插件仅在启用且获得授权后安装。\n if (this.pluginManager || !this.config.enabled || this.config.trackingConsent !== 'granted') return;\n this.releaseOtelGlobals = acquireOtelGlobals();\n const pluginManager = this.createPluginManager();\n this.pluginManager = pluginManager;\n try {\n await pluginManager.start(this.createPluginContext());\n this.registerInstrumentations(this.config.extensions.instrumentations ?? []);\n } catch (error) {\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n throw error;\n }\n };\n private readonly stopCollection = async (flush: boolean) => {\n const pluginManager = this.pluginManager;\n if (!pluginManager) return;\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n // 撤销授权后立即清空 Session,防止后续手工 API 继续复用旧采样决定。\n this.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n });\n if (flush) await this.flushProviders();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n };\n private readonly teardownCallbacks: Array<() => void> = [];\n private readonly tracerProvider: WebTracerProvider;\n private assertBeforeStart(method: string) {\n if (this.phase !== 'idle') {\n throw new Error(`[bk-ot] ${method}() must be called before start(); set autoStart: false before registration.`);\n }\n }\n private createPluginContext(): BkOTRuntimeContext {\n return {\n applyRedact: this.applyRedact,\n config: this.config,\n flush: this.flush,\n getRuntimeAttributes: this.getRuntimeAttributes,\n getRuntimeAttributesAt: this.getRuntimeAttributesAt,\n isSampled: this.isSampled,\n isSessionActive: this.isSessionActive,\n isViewActive: this.isViewActive,\n notifySessionLifecycle: this.notifySessionLifecycle,\n notifyActivity: this.activityObserver.notify,\n onActivity: this.activityObserver.subscribe,\n onSessionLifecycle: this.onSessionLifecycle,\n onViewCommand: this.runtimeContext.onViewCommand,\n onViewChange: this.runtimeContext.onViewChange,\n registerInstrumentations: this.registerInstrumentations,\n setRuntimeAttributes: this.setRuntimeAttributes,\n setUser: this.setUser,\n setView: this.runtimeContext.setView,\n startSpan: this.startSpan,\n tracer: this.tracer,\n };\n }\n private createPluginManager() {\n return new PluginManager([\n ...getRumContextPlugins(this.config),\n ...getCorePlugins(this.config),\n ...getRumPlugins(this.config.tracking),\n ...(this.config.extensions.plugins ?? []),\n ]);\n }\n public readonly applyRedact = (attributes: Attributes) => this.config.hooks.redactAttributes(attributes);\n\n public readonly config: ResolvedBkOTConfig;\n\n public readonly flush = async () => {\n await this.pluginManager?.flush();\n await this.flushProviders();\n };\n\n public readonly getRuntimeAttributes = () => this.runtimeContext.getAttributes();\n public readonly getRuntimeAttributesAt = (timestamp: number) => this.runtimeContext.getAttributesAt(timestamp);\n public readonly isSampled = () => this.sampled;\n public readonly isSessionActive = () => this.runtimeContext.isSessionActive();\n public readonly isViewActive = () => this.runtimeContext.isViewActive();\n public readonly notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.runtimeContext.notifySessionLifecycle(event);\n };\n\n public readonly onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) =>\n this.runtimeContext.onSessionLifecycle(listener);\n\n public readonly registerInstrumentations = (instrumentations: Instrumentation[]) => {\n if (!instrumentations.length) return;\n instrumentations.forEach(instrumentation => {\n instrumentation.setTracerProvider(this.tracerProvider);\n if (!instrumentation.getConfig().enabled) instrumentation.enable();\n });\n this.registeredInstrumentations.push(...instrumentations);\n };\n\n public readonly reportAction = ({ attributes = {}, name }: BkOTActionEventPayload) => {\n if (!this.isSampled()) {\n return;\n }\n const actionId = createRandomId('action');\n const occurredAt = Date.now();\n const span = this.startSpan(\n 'action.custom',\n {\n ...this.config.hooks.getPageAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.action,\n }),\n [RUM_ATTRIBUTES.actionId]: actionId,\n [RUM_ATTRIBUTES.actionName]: name,\n [RUM_ATTRIBUTES.actionType]: 'custom',\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n public readonly reportCustomEvent = ({ attributes = {}, error, name }: BkOTCustomEventPayload) => {\n if (!this.isSampled()) {\n // 未采样时静默丢弃;如需调试请把 debug 选项打开\n if (this.config.debug) {\n globalThis.console?.warn('[bk-ot] custom event dropped (not sampled):', name);\n }\n return;\n }\n\n const occurredAt = Date.now();\n const span = this.startSpan(\n `custom.${name}`,\n {\n ...this.config.hooks.getPageAttributes(),\n ...this.config.hooks.getCustomAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n outcome: error ? RUM_SPAN_OUTCOME.error : RUM_SPAN_OUTCOME.success,\n spanType: RUM_SPAN_TYPE.custom,\n }),\n },\n { startTime: occurredAt },\n );\n\n if (error) {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n }\n\n span.end(occurredAt);\n };\n\n public readonly setRuntimeAttributes = (attributes: Attributes, changedAt?: number) => {\n const nextAttributes = { ...attributes };\n const effectiveChangedAt = changedAt ?? Date.now();\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n // 采样以 Session 为边界重新计算,并保存在运行时私有历史中。\n const sessionId = attributes[RUM_ATTRIBUTES.sessionId];\n this.sampled =\n this.config.enabled && typeof sessionId === 'string' && sessionId.length > 0\n ? isBkOTSampled(this.config, sessionId)\n : false;\n this.runtimeContext.setSessionSampled(this.sampled, effectiveChangedAt);\n }\n this.runtimeContext.setAttributes(nextAttributes, effectiveChangedAt);\n };\n\n public readonly setTrackingConsent = async (consent: BkOTTrackingConsent) => {\n if (this.config.trackingConsent === consent) return;\n this.config.trackingConsent = consent;\n if (this.phase !== 'started') return;\n if (consent === 'granted') await this.startCollection();\n else await this.stopCollection(true);\n };\n\n public readonly setUser = (user: BkOTUserContext) => {\n this.runtimeContext.setUser(user);\n };\n\n public readonly setViewLoadingTime = () => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ time: Date.now(), type: 'set_loading_time' });\n };\n\n public readonly setViewName = (name: string) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ name, type: 'set_name' });\n };\n\n public readonly shutdown = async () => {\n if (this.phase === 'stopped') {\n return;\n }\n await this.stopCollection(false);\n await this.flushProviders();\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.tracerProvider.shutdown();\n this.phase = 'stopped';\n };\n\n public readonly start = async () => {\n if (this.phase === 'started' || this.phase === 'starting') {\n return;\n }\n if (this.phase === 'stopped') {\n // shutdown 已经销毁了 provider/processor,此处禁止再次启动\n throw new Error('[bk-ot] instance has been shut down and cannot be restarted; create a new instance instead.');\n }\n if (!this.config.enabled) {\n this.phase = 'started';\n return;\n }\n this.phase = 'starting';\n try {\n await this.startCollection();\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // 页面进入后台或被丢弃前尽力导出批量数据;两个事件并存以覆盖不同浏览器生命周期。\n const flushOnPageHide = () => {\n void this.flush();\n };\n const flushOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n void this.flush();\n }\n };\n window.addEventListener('pagehide', flushOnPageHide);\n document.addEventListener('visibilitychange', flushOnHidden);\n this.teardownCallbacks.push(\n () => window.removeEventListener('pagehide', flushOnPageHide),\n () => document.removeEventListener('visibilitychange', flushOnHidden),\n );\n }\n } catch (error) {\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.stopCollection(false);\n this.phase = 'idle';\n throw error;\n }\n this.phase = 'started';\n };\n\n public readonly startSpan = (name: string, attributes: Attributes = {}, options?: BkOTStartSpanOptions): Span => {\n const contextTime =\n options?.contextTime ?? (typeof options?.startTime === 'number' ? options.startTime : undefined);\n // 返回 provider 创建的 non-recording span,而不是让每个调用方处理 undefined。\n if (!this.isSampled() || (contextTime !== undefined && !this.runtimeContext.isSessionSampledAt(contextTime))) {\n return this.tracer.startSpan(name);\n }\n const runtimeAttributes =\n contextTime === undefined ? this.getRuntimeAttributes() : this.runtimeContext.getAttributesAt(contextTime);\n return this.tracer.startSpan(name, {\n kind: options?.kind ?? SpanKind.INTERNAL,\n links: options?.links,\n startTime: options?.startTime,\n attributes: this.applyRedact({\n ...runtimeAttributes,\n ...attributes,\n }),\n });\n };\n\n public readonly startView = (options: BkOTStartViewOptions = {}) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ options, type: 'start' });\n };\n\n public readonly tracer: BkOTRuntimeContext['tracer'];\n\n public readonly use = (plugin: BkOTPlugin): this => {\n this.assertBeforeStart('use');\n this.config.extensions.plugins?.push(plugin);\n return this;\n };\n\n public readonly useInstrumentation = (instrumentation: Instrumentation): this => {\n this.assertBeforeStart('useInstrumentation');\n if (this.config.enabled) this.config.extensions.instrumentations?.push(instrumentation);\n return this;\n };\n\n constructor(inputConfig: BkOTOptions) {\n this.config = resolveConfig(inputConfig);\n this.runtimeContext = new RumRuntimeContext({}, this.config.user);\n\n const resource = resourceFromAttributes(this.config.resourceAttributes);\n const spanProcessors = (() => {\n const exporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(this.config.transport)),\n this.config.debug,\n );\n const processor =\n this.config.spanProcessor === 'simple'\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, this.config.spanBatch);\n return [\n new FilteringSpanProcessor(processor, this.config, () =>\n this.applyRedact({\n ...this.runtimeContext.getAttributes(),\n ...this.config.hooks.getPageAttributes(),\n }),\n ),\n ];\n })();\n this.tracerProvider = new WebTracerProvider({\n resource,\n sampler: new RumSessionSampler(this.isSampled),\n spanProcessors,\n });\n this.tracer = this.tracerProvider.getTracer(BK_OT_INSTRUMENTATION_NAME);\n this.registeredInstrumentations = [];\n if (this.config.autoStart) {\n void this.start().catch(error => {\n globalThis.console?.warn('[bk-ot] auto start failed:', error);\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../package.json","../src/core/hash.ts","../src/core/id.ts","../src/schema/attributes.ts","../src/plugins/device.ts","../src/core/url.ts","../src/core/config.ts","../src/core/plugin.ts","../src/core/processor.ts","../src/core/runtime-context.ts","../src/schema/span.ts","../src/plugins/action.ts","../src/plugins/activity.ts","../src/core/page-activity.ts","../src/plugins/blank-screen.ts","../src/core/request-timing.ts","../src/core/trace-context.ts","../src/plugins/resource.ts","../src/plugins/common.ts","../src/plugins/csp-violation.ts","../src/plugins/error.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/session-state.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/action-activity.ts","../src/core/sampling.ts","../src/core/sdk.ts"],"sourcesContent":["{\n \"name\": \"@blueking/open-telemetry\",\n \"version\": \"0.0.29\",\n \"description\": \"OpenTelemetry browser SDK for BK RUM\",\n \"author\": \"TencentCloud Real User Monitoring\",\n \"main\": \"dist/index.js\",\n \"unpkg\": \"dist/bk-rum.global.js\",\n \"jsdelivr\": \"dist/bk-rum.global.js\",\n \"license\": \"MIT\",\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"scripts\": {\n \"prepublishOnly\": \"pnpm build && pnpm dts && pnpm skill:refs\",\n \"skill:refs\": \"node scripts/gen-skill-references.mjs\",\n \"test:skill-refs\": \"node --test tests/gen-skill-references.test.mjs\",\n \"build\": \"vite build && vite build --mode cdn && pnpm dts\",\n \"build:cdn\": \"vite build --mode cdn\",\n \"build:esm\": \"vite build\",\n \"docs:build\": \"vitepress build wikis\",\n \"docs:dev\": \"vitepress dev wikis\",\n \"docs:preview\": \"vitepress preview wikis\",\n \"dev\": \"vite dev\",\n \"dts\": \"vue-tsc --project tsconfig.dts.json\",\n \"test:rum-context\": \"node --experimental-default-type=module --experimental-strip-types --test tests/action-plugin.test.mjs tests/consent.test.mjs tests/experience-plugins.test.mjs tests/request-model.test.mjs tests/runtime-context.test.mjs tests/sampling.test.mjs tests/session-state.test.ts tests/session-plugin.test.mjs tests/session-page-view.test.mjs tests/session-processors.test.mjs tests/trace-context.test.mjs tests/web-vitals.test.mjs\"\n },\n \"dependencies\": {\n \"@opentelemetry/api\": \"^1.9.1\",\n \"@opentelemetry/context-zone\": \"2.7.1\",\n \"@opentelemetry/core\": \"^2.7.1\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.217.0\",\n \"@opentelemetry/instrumentation\": \"^0.217.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-trace-base\": \"^2.7.1\",\n \"@opentelemetry/sdk-trace-web\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.38.0\",\n \"web-vitals\": \"^5.0.3\"\n },\n \"devDependencies\": {\n \"@vitejs/plugin-vue\": \"^6.0.6\",\n \"@vue/test-utils\": \"^2.4.6\",\n \"@vue/tsconfig\": \"^0.9.0\",\n \"oxc-minify\": \"^0.133.0\",\n \"vite\": \"^8.0.2\",\n \"vite-bundle-analyzer\": \"^1.3.2\",\n \"vitepress\": \"2.0.0-alpha.16\",\n \"vue\": \"^3.5.34\",\n \"vue-router\": \"^5.0.6\",\n \"vue-tsc\": \"^3.1.4\"\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/** 非密码学稳定 hash,仅用于生成浏览器本地 storage key。 */\nexport const hashString = (input: string): string => {\n let hash = 5381;\n for (let index = 0; index < input.length; index++) {\n hash = (hash * 33) ^ input.charCodeAt(index);\n }\n return (hash >>> 0).toString(36);\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 生成随机 ID。优先用 `crypto.randomUUID`(standards-compliant UUID v4),不可用时回退到\n * `${prefix}-${timestamp}-${rand}` 形式。\n *\n * 注意:fallback 实现使用 Math.random,**不是密码学安全**,仅适用于设备/会话/事件标识等场景,\n * 不要复用此函数生成 token、CSRF 防护等安全相关的值。\n */\nexport const createRandomId = (prefix: string) => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * RUM 上报字段 key 集中定义。\n *\n * 插件里不要散落硬编码字符串,后续字段命名调整只改这里,避免采集端与字段文档逐步漂移。\n */\nexport const RUM_ATTRIBUTES = {\n actionFrustrationType: 'action.frustration.type',\n actionId: 'action.id',\n actionLoadingTime: 'action.loading_time',\n actionName: 'action.name',\n actionTargetName: 'action.target.name',\n actionTargetTag: 'action.target.tag',\n actionType: 'action.type',\n deploymentEnvironmentName: 'deployment.environment.name',\n cacheHit: 'resource.cache.hit',\n deviceMemoryGib: 'device.memory_gib',\n deviceType: 'device.type',\n longTaskBlockingDuration: 'long_task.blocking_duration',\n longTaskDuration: 'long_task.duration',\n longTaskEntryType: 'long_task.entry_type',\n longTaskId: 'long_task.id',\n longTaskStartTime: 'long_task.start_time',\n networkEffectiveType: 'network.effective_type',\n networkStatus: 'network.status',\n nextHopProtocol: 'resource.protocol',\n outcomeReason: 'outcome.reason',\n outcomeType: 'outcome.type',\n serviceName: 'service.name',\n serviceVersion: 'service.version',\n sessionHasReplay: 'session.has_replay',\n sessionId: 'session.id',\n sessionSampleRate: 'session.sample_rate',\n sessionPhase: 'session.phase',\n sessionStartTime: 'session.start_time',\n sessionType: 'session.type',\n spanType: 'span_type',\n userId: 'user.id',\n userAgentName: 'user_agent.name',\n userAgentOsName: 'user_agent.os.name',\n userAgentVersion: 'user_agent.version',\n vitalMetric: 'vital.metric',\n vitalRating: 'vital.rating',\n vitalValue: 'vital.value',\n viewDomComplete: 'view.dom_complete',\n viewDomContentLoaded: 'view.dom_content_loaded',\n viewDomInteractive: 'view.dom_interactive',\n viewFirstByte: 'view.first_byte',\n viewId: 'view.id',\n viewLoadEvent: 'view.load_event',\n viewLoadingTime: 'view.loading_time',\n viewLoadingTimeSource: 'view.loading_time_source',\n viewLoadingType: 'view.loading_type',\n viewName: 'view.name',\n viewPhase: 'view.phase',\n viewStartedAt: 'view.started_at',\n viewUrl: 'view.url',\n viewUrlTemplate: 'view.url_template',\n viewVersion: 'view.version',\n websocketPhase: 'websocket.phase',\n} as const;\n\nexport type RumAttributeKey = (typeof RUM_ATTRIBUTES)[keyof typeof RUM_ATTRIBUTES];\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { hashString } from '../core/hash';\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTDevicePluginOptions {\n /** 是否跨页面持久化匿名设备 ID,默认 false。 */\n persistentId?: boolean;\n storageKey?: string;\n}\n\nconst DEFAULT_DEVICE_STORAGE_KEY = 'BK_RUM_DEVICE_ID';\n// 用户拖窗口时 resize 高频触发,debounce 静默期内只在最后一次更新,避免无谓的 setRuntimeAttributes 风暴\nconst VIEWPORT_DEBOUNCE_MS = 250;\n\ninterface NetworkInformationLike {\n effectiveType?: string;\n type?: string;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ninterface UserAgentDataLike {\n mobile?: boolean;\n platform?: string;\n}\n\nconst getConnection = (): NetworkInformationLike | undefined =>\n (\n navigator as Navigator & {\n connection?: NetworkInformationLike;\n }\n ).connection;\n\nconst getUserAgentData = (): undefined | UserAgentDataLike =>\n (\n navigator as Navigator & {\n userAgentData?: UserAgentDataLike;\n }\n ).userAgentData;\n\nconst getBrowserInfo = (ua: string) => {\n const matchers: Array<[string, RegExp]> = [\n ['Edge', /Edg\\/([\\d.]+)/],\n ['Chrome', /Chrome\\/([\\d.]+)/],\n ['Firefox', /Firefox\\/([\\d.]+)/],\n ['Safari', /Version\\/([\\d.]+).*Safari/],\n ];\n for (const [name, pattern] of matchers) {\n const matched = ua.match(pattern);\n if (matched?.[1]) {\n return { name, version: matched[1].split('.')[0] };\n }\n }\n return { name: 'Other', version: undefined };\n};\n\nconst getOsName = (ua: string, platform?: string) => {\n const value = `${platform ?? ''} ${ua}`;\n if (/iphone|ipad|ipod/i.test(value)) return 'iOS';\n if (/android/i.test(value)) return 'Android';\n if (/mac/i.test(value)) return 'macOS';\n if (/win/i.test(value)) return 'Windows';\n if (/linux/i.test(value)) return 'Linux';\n return 'Other';\n};\n\nconst readDeviceId = (storageKey: string) => {\n let existed: null | string = null;\n try {\n existed = window.localStorage.getItem(storageKey);\n } catch {\n /* localStorage 不可用(隐私模式 / sandbox iframe),下面会创建一个 in-memory id */\n }\n if (existed) {\n return existed;\n }\n const deviceId = createRandomId('device');\n try {\n window.localStorage.setItem(storageKey, deviceId);\n } catch {\n /* 写不进去也行,至少本次 SDK 生命周期内 deviceId 仍然稳定 */\n }\n return deviceId;\n};\n\n// 视口尺寸:随窗口大小 / 屏幕旋转变化,需要在 resize / orientationchange 时刷新\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n return {\n 'browser.viewport.width': window.innerWidth,\n 'browser.viewport.height': window.innerHeight,\n 'browser.screen.width': window.screen?.width,\n 'browser.screen.height': window.screen?.height,\n };\n};\n\n// 网络信息:随 online / offline / connection.change 变化\nconst getNetworkAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const connection = getConnection();\n return {\n [RUM_ATTRIBUTES.networkStatus]: navigator.onLine ? 'connected' : 'not_connected',\n 'network.effective_type': connection?.effectiveType,\n 'network.connection.type': connection?.type,\n };\n};\n\n// 设备与 User Agent 信息:进入页面时一次性读取,运行期不会变化,适合作为 Resource 公共属性。\nexport const getDeviceResourceAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const uaData = getUserAgentData();\n const ua = navigator.userAgent;\n const browser = getBrowserInfo(ua);\n const osName = getOsName(ua, uaData?.platform);\n const deviceType = uaData?.mobile || /mobi|android|iphone|ipad|ipod/i.test(ua) ? 'mobile' : 'desktop';\n return {\n [RUM_ATTRIBUTES.deviceMemoryGib]: (navigator as Navigator & { deviceMemory?: number }).deviceMemory,\n [RUM_ATTRIBUTES.deviceType]: deviceType,\n [RUM_ATTRIBUTES.userAgentName]: browser.name,\n [RUM_ATTRIBUTES.userAgentVersion]: browser.version,\n [RUM_ATTRIBUTES.userAgentOsName]: osName,\n };\n};\n\n// trailing debounce:windowMs 静默期内的多次触发只执行最后一次。\n// 适合 resize 这类\"用户停下来后才需要拿到最终值\"的场景。\nconst createDebounce = (fn: () => void, windowMs: number) => {\n let timer: number | undefined;\n return () => {\n if (timer !== undefined) {\n window.clearTimeout(timer);\n }\n timer = window.setTimeout(() => {\n timer = undefined;\n fn();\n }, windowMs);\n };\n};\n\n/**\n * 设备元数据采集插件。稳定的设备能力由 SDK 写入 Resource;这里只维护设备 ID 和运行期动态属性。\n *\n * 上报字段:\n * - `device.id`:设备级永久标识,存于 localStorage(默认 key 复用旧 session 插件以保留现网用户)。\n * - `device.memory_gib`:硬件能力快照,写入 Resource。\n * - `user_agent.name` / `user_agent.version` / `user_agent.os.name`:User Agent 信息,写入 Resource。\n * - `browser.viewport.*` / `browser.screen.*`:视口与屏幕尺寸。\n * - `network.status` / `network.effective_type` / `network.connection.type`:网络信息。\n *\n * 实时更新:\n * - viewport 监听 `resize` / `orientationchange`,并经 250ms debounce 合并,避免拖窗口时高频触发;\n * 静默期结束后用最终值刷新 runtimeAttributes。\n * - network 监听 `online` / `offline` / `connection.change`(本身低频,不需要 throttle)。\n * - 二者只更新 in-memory 的 runtimeAttributes,不会主动产生新的 Span;\n * 更新后续生成的 Span 会带最新值。\n *\n * 与 session 插件的关系:device 跨会话持久(终身),session 有过期与续期(连续访问语义)。\n */\nexport const createDevicePlugin = (options: BkOTDevicePluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n return {\n name: 'device',\n init(context) {\n const storageKey =\n options.storageKey ?? `${DEFAULT_DEVICE_STORAGE_KEY}_${hashString(context.config.application.name)}`;\n const deviceId =\n typeof window === 'undefined' || options.persistentId !== true\n ? createRandomId('device')\n : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n // createDevicePlugin 作为扩展单独启用时,Resource 没有设备字段,仍回退到原有 runtime attributes 行为。\n ...(context.config.tracking.device === false ? getDeviceResourceAttributes() : {}),\n ...getViewportAttributes(),\n ...getNetworkAttributes(),\n });\n\n if (typeof window === 'undefined') {\n return;\n }\n\n const refreshViewport = createDebounce(() => {\n context.setRuntimeAttributes(getViewportAttributes());\n }, VIEWPORT_DEBOUNCE_MS);\n window.addEventListener('resize', refreshViewport);\n window.addEventListener('orientationchange', refreshViewport);\n teardownCallbacks.push(\n () => window.removeEventListener('resize', refreshViewport),\n () => window.removeEventListener('orientationchange', refreshViewport),\n );\n\n const refreshNetwork = () => {\n context.setRuntimeAttributes(getNetworkAttributes());\n };\n window.addEventListener('online', refreshNetwork);\n window.addEventListener('offline', refreshNetwork);\n teardownCallbacks.push(\n () => window.removeEventListener('online', refreshNetwork),\n () => window.removeEventListener('offline', refreshNetwork),\n );\n\n const connection = getConnection();\n // NetworkInformation 实现了 EventTarget;旧浏览器或某些 polyfill 可能没有 addEventListener,做能力检测\n if (connection && typeof connection.addEventListener === 'function') {\n connection.addEventListener('change', refreshNetwork);\n teardownCallbacks.push(() => {\n connection.removeEventListener?.('change', refreshNetwork);\n });\n }\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTUrlMatcher, ResolvedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nexport const toAbsoluteUrl = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).href;\n } catch {\n return url;\n }\n};\n\nconst DYNAMIC_PATH_SEGMENT = /^(?:\\d+|[0-9a-f]{8}-[0-9a-f-]{27,}|[0-9a-f]{16,})$/i;\n\n/**\n * 常见数字 ID、UUID 和长十六进制段统一折叠为 :id,便于服务端按路由聚合。\n * 同时服务 `view.url_template`(页面路由)与 `url.template`(请求路径)两个维度。\n */\nexport const normalizeUrlPathTemplate = (pathGroup: string) => {\n const path = pathGroup.split(/[?#]/, 1)[0];\n return path\n .split('/')\n .map(segment => (DYNAMIC_PATH_SEGMENT.test(segment) ? ':id' : segment))\n .join('/');\n};\n\n// 去掉 trailing slash,便于 path 边界对齐比较\nconst stripTrailingSlash = (value: string) => (value.length > 1 ? value.replace(/\\/+$/, '') : value);\n\n// 比较两个 URL 是否指向同一资源(忽略 query/fragment 与末尾斜杠)\nconst isSameResource = (left: string, right: string) => {\n const stripped = (value: string) => stripTrailingSlash(value.replace(/[?#].*$/, ''));\n return stripped(left) === stripped(right);\n};\n\nexport const isBkOTEndpoint = (config: ResolvedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n return isSameResource(targetUrl, toAbsoluteUrl(config.transport.endpoint));\n};\n\nexport const shouldIgnoreUrl = (config: ResolvedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nconst matchesUrl = (matcher: BkOTUrlMatcher, url: string) => {\n if (typeof matcher === 'function') return matcher(url);\n if (typeof matcher === 'string') return url.includes(matcher);\n matcher.lastIndex = 0;\n return matcher.test(url);\n};\n\nexport const matchesUrlList = (matchers: BkOTUrlMatcher | BkOTUrlMatcher[] | undefined, url: string) =>\n (Array.isArray(matchers) ? matchers : matchers ? [matchers] : []).some(matcher => matchesUrl(matcher, url));\n\nconst getRequestConfig = (config: ResolvedBkOTConfig) =>\n typeof config.tracking.request === 'object' ? config.tracking.request : undefined;\n\nexport const shouldIgnoreRequest = (config: ResolvedBkOTConfig, url: string) =>\n shouldIgnoreUrl(config, url) || matchesUrlList(getRequestConfig(config)?.excludedUrls, toAbsoluteUrl(url));\n\n// Trace Header 仅向显式白名单注入,避免同源第三方路径被无意注入以及跨域 CORS 预检。\nexport const shouldPropagateTraceHeader = (config: ResolvedBkOTConfig, url: string) =>\n matchesUrlList(getRequestConfig(config)?.allowedTracingUrls, toAbsoluteUrl(url));\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { version as packageVersion } from '../../package.json';\nimport { getDeviceResourceAttributes } from '../plugins/device';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { normalizeUrlPathTemplate } from './url';\n\nimport type { BkOTActionPluginOptions } from '../plugins/action';\nimport type { BkOTBlankScreenPluginOptions } from '../plugins/blank-screen';\nimport type { BkOTDevicePluginOptions } from '../plugins/device';\nimport type { BkOTLongTaskPluginOptions } from '../plugins/long-task';\nimport type { BkOTSessionConfig } from '../plugins/session';\nimport type { RumRequestOutcome } from '../schema/span';\nimport type { BkOTPlugin } from './plugin';\nimport type { BkOTUserContext } from './runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTActionEventPayload {\n attributes?: Attributes;\n name: string;\n}\n\nexport interface BkOTApplicationConfig {\n environment?: string;\n name: string;\n version?: string;\n}\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n page?: () => Attributes;\n}\n\nexport type BkOTAttributeValue = boolean | number | string;\n\nexport interface BkOTBatchConfig {\n exportTimeoutMillis?: number;\n maxExportBatchSize?: number;\n maxQueueSize?: number;\n scheduledDelayMillis?: number;\n}\n\nexport interface BkOTBeforeSendEvent {\n attributes: Record<string, unknown>;\n name: string;\n signal: 'span';\n}\n\nexport interface BkOTContextConfig {\n attributes?: BkOTAttributesConfig;\n user?: BkOTUserContext;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTExtensionsConfig {\n instrumentations?: Instrumentation[];\n plugins?: BkOTPlugin[];\n}\n\nexport interface BkOTOptions {\n application: BkOTApplicationConfig;\n autoStart?: boolean;\n context?: BkOTContextConfig;\n debug?: boolean;\n enabled?: boolean;\n extensions?: BkOTExtensionsConfig;\n privacy?: BkOTPrivacyConfig;\n session?: BkOTSessionConfig;\n tracking?: BkOTTrackingConfig;\n transport: BkOTTransportConfig;\n}\n\nexport interface BkOTPrivacyConfig {\n /** 初始化时的采集授权状态;运行期可通过 setTrackingConsent 更新。 */\n trackingConsent?: BkOTTrackingConsent;\n /** 在所有 RUM 自定义插件 emit 前统一过滤属性。 */\n redactAttributes?: (attributes: Attributes) => Attributes;\n /** 所有写入 attribute 的 URL 都会先经过此函数。 */\n redactUrl?: (url: string) => string;\n}\n\nexport interface BkOTRequestConfig {\n allowedTracingUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n excludedUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n beforeSend?: (event: BkOTRequestEvent, context: BkOTRequestContext) => boolean | void;\n /** 返回请求 URL 的低基数模板,例如 `/api/orders/{id}`;返回空值时回退到默认折叠规则。 */\n getUrlTemplate?: (url: string) => string | undefined;\n}\n\nexport type BkOTRequestContext =\n | { error?: unknown; request: Request | RequestInit; response?: Response; type: 'fetch' }\n | { type: 'xhr'; xhr: XMLHttpRequest };\n\nexport interface BkOTRequestEvent {\n attributes: Attributes;\n type: 'resource';\n resource: {\n method?: string;\n outcome: BkOTRequestOutcome;\n transport: 'fetch' | 'xhr';\n url?: string;\n };\n}\n\nexport type BkOTRequestOutcome = RumRequestOutcome;\n\nexport type BkOTSpanProcessorMode = 'batch' | 'simple';\n\nexport interface BkOTTrackingConfig {\n action?: BkOTActionPluginOptions | boolean;\n blankScreen?: BkOTBlankScreenPluginOptions | boolean;\n cspViolation?: boolean;\n device?: BkOTDevicePluginOptions | boolean;\n error?: boolean;\n longTask?: BkOTLongTaskPluginOptions | boolean;\n request?: BkOTRequestConfig | boolean;\n resource?: boolean;\n view?: BkOTViewConfig;\n websocket?: boolean;\n webVitals?: boolean;\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n}\n\nexport type BkOTTrackingConsent = 'granted' | 'not-granted';\n\nexport interface BkOTTransportConfig {\n batch?: BkOTBatchConfig;\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n processor?: BkOTSpanProcessorMode;\n timeoutMillis?: number;\n token?: string;\n}\n\nexport type BkOTUrlMatcher = ((url: string) => boolean) | RegExp | string;\n\nexport interface BkOTViewConfig {\n /** 不参与 View loading time 的长轮询、流式请求或埋点 URL。 */\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n /** 自动计算初始加载与 SPA 路由的页面稳定耗时,默认开启。 */\n loadingTime?: boolean;\n tracking?: 'auto' | 'manual';\n /** 返回页面路由的低基数模板,例如 `/trace/:id`;返回空值时回退到默认折叠规则。 */\n getUrlTemplate?: (url: string) => string | undefined;\n}\n\nexport interface ResolvedApplicationConfig {\n environment: string;\n name: string;\n version?: string;\n}\n\nexport interface ResolvedBkOTConfig {\n application: ResolvedApplicationConfig;\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n extensions: BkOTExtensionsConfig;\n hooks: ResolvedConfigHooks;\n resourceAttributes: Attributes;\n session: ResolvedSessionConfig;\n spanBatch?: BkOTBatchConfig;\n spanProcessor: BkOTSpanProcessorMode;\n tracking: BkOTTrackingConfig;\n trackingConsent: BkOTTrackingConsent;\n transport: ResolvedTransportConfig;\n user?: BkOTUserContext;\n view: ResolvedViewConfig;\n}\n\nexport interface ResolvedConfigHooks {\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n getRequestUrlTemplate: (url: string) => string;\n getViewUrlTemplate: (url: string) => string;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n}\n\nexport interface ResolvedSessionConfig extends BkOTSessionConfig {\n sampleRate: number;\n}\n\nexport interface ResolvedTransportConfig {\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n}\n\nexport interface ResolvedViewConfig {\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n loadingTime: boolean;\n tracking: 'auto' | 'manual';\n}\n\nconst trimTrailingSlash = (value: string) => value.trim().replace(/\\/+$/, '');\n\nconst resolveTransportConfig = (transport: BkOTTransportConfig): ResolvedTransportConfig => {\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n transport.token || transport.headers\n ? {\n ...(transport.token ? { Authorization: `Bearer ${transport.token}` } : {}),\n ...(transport.headers ?? {}),\n }\n : undefined;\n\n return {\n concurrencyLimit: transport.concurrencyLimit,\n endpoint: `${trimTrailingSlash(transport.endpoint)}/v1/rum`,\n headers: mergedHeaders,\n timeoutMillis: transport.timeoutMillis,\n };\n};\n\nconst clampSampleRate = (sampleRate?: number) => {\n if (typeof sampleRate !== 'number' || !Number.isFinite(sampleRate)) {\n return 1;\n }\n return Math.max(0, Math.min(1, sampleRate));\n};\n\nconst identityRedactAttributes = (attributes: Attributes) => attributes;\n\n// 默认只保留 URL 的定位信息;认证信息、查询参数和 fragment 可能携带用户数据,统一移除。\nconst defaultRedactUrl = (url: string) => {\n if (!url) return url;\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n parsed.username = '';\n parsed.password = '';\n parsed.search = '';\n parsed.hash = '';\n return parsed.href;\n } catch {\n return url.replace(/[?#].*$/, '');\n }\n};\n\nconst getUrlPathname = (url: string) => {\n try {\n return new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href).pathname || '/';\n } catch {\n return url || '/';\n }\n};\n\n/**\n * 低基数路径模板,供 `view.url_template` 与 `url.template` 共用同一套折叠规则。\n * 只保留 path:host 维度由 `server.address` 表达,query 由 `normalizeUrlPathTemplate` 去掉。\n */\nconst templatizeUrlPath = (url: string) => normalizeUrlPathTemplate(getUrlPathname(url));\n\n/** 模板 Hook 在每个请求/导航上都会跑,抛错不能影响追踪本身 */\nconst resolveTemplateHook =\n (config: BkOTOptions, custom: ((url: string) => string | undefined) | undefined) => (url: string) => {\n try {\n const templated = custom?.(url);\n if (templated) return templated;\n } catch (error) {\n if (config.debug) globalThis.console?.warn('[bk-ot] url template hook failed:', error);\n }\n return templatizeUrlPath(url);\n };\n\nconst resolveApplicationConfig = (application: BkOTOptions['application']): ResolvedApplicationConfig => ({\n environment: application.environment ?? 'production',\n name: application.name,\n version: application.version,\n});\n\nconst resolveResourceAttributes = (\n application: ResolvedApplicationConfig,\n device: BkOTTrackingConfig['device'],\n sampleRate: number,\n): Attributes => ({\n [RUM_ATTRIBUTES.deploymentEnvironmentName]: application.environment,\n ...(device === false ? {} : getDeviceResourceAttributes()),\n [RUM_ATTRIBUTES.serviceName]: application.name,\n ...(application.version ? { [RUM_ATTRIBUTES.serviceVersion]: application.version } : {}),\n [RUM_ATTRIBUTES.sessionSampleRate]: sampleRate,\n 'telemetry.sdk.language': 'webjs',\n 'telemetry.sdk.name': '@blueking/open-telemetry',\n 'telemetry.sdk.version': packageVersion,\n});\n\nconst validateTransportConfig = (transport: BkOTTransportConfig | undefined) => {\n if (!transport) throw new Error('[bk-ot] transport is required.');\n if (!transport.endpoint?.trim()) throw new Error('[bk-ot] transport.endpoint is required.');\n};\n\nconst resolveHooks = (config: BkOTOptions): ResolvedConfigHooks => ({\n beforeSend: config.tracking?.beforeSend,\n getPageAttributes: config.context?.attributes?.page ?? (() => ({})),\n getErrorAttributes: config.context?.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.context?.attributes?.custom ?? (() => ({})),\n getRequestUrlTemplate: resolveTemplateHook(\n config,\n typeof config.tracking?.request === 'object' ? config.tracking.request.getUrlTemplate : undefined,\n ),\n getViewUrlTemplate: resolveTemplateHook(\n config,\n typeof config.tracking?.view === 'object' ? config.tracking.view.getUrlTemplate : undefined,\n ),\n redactAttributes: config.privacy?.redactAttributes ?? identityRedactAttributes,\n redactUrl: config.privacy?.redactUrl ?? defaultRedactUrl,\n});\n\nexport const resolveConfig = (config: BkOTOptions): ResolvedBkOTConfig => {\n if (!config.application?.name?.trim()) {\n throw new Error('[bk-ot] application.name is required.');\n }\n validateTransportConfig(config.transport);\n const application = resolveApplicationConfig(config.application);\n const viewConfig = typeof config.tracking?.view === 'object' ? config.tracking.view : undefined;\n const hooks = resolveHooks(config);\n const sampleRate = clampSampleRate(config.session?.sampleRate);\n\n return {\n application,\n autoStart: config.autoStart ?? true,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n extensions: {\n instrumentations: [...(config.extensions?.instrumentations ?? [])],\n plugins: [...(config.extensions?.plugins ?? [])],\n },\n hooks,\n resourceAttributes: hooks.redactAttributes(\n resolveResourceAttributes(application, config.tracking?.device, sampleRate),\n ),\n session: {\n ...config.session,\n sampleRate,\n },\n spanBatch: config.transport.batch,\n spanProcessor: config.transport.processor ?? 'batch',\n tracking: config.tracking ?? {},\n trackingConsent: config.privacy?.trackingConsent ?? 'granted',\n transport: resolveTransportConfig(config.transport),\n user: config.context?.user,\n view: {\n excludedActivityUrls: viewConfig?.excludedActivityUrls,\n loadingTime: viewConfig?.loadingTime !== false,\n tracking: viewConfig?.tracking === 'manual' ? 'manual' : 'auto',\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { ResolvedBkOTConfig } from './config';\nimport type { BkOTSessionLifecycleEvent, BkOTUserContext, BkOTViewCommand, BkOTViewContext } from './runtime-context';\nimport type { Attributes, Link, Span, SpanKind, TimeInput, Tracer } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTPlugin {\n enabled?: ((config: ResolvedBkOTConfig) => boolean) | boolean;\n name: string;\n flush?: () => Promise<void> | void;\n init: (context: BkOTRuntimeContext) => Promise<void> | void;\n shutdown?: () => Promise<void> | void;\n}\n\nexport interface BkOTRuntimeContext {\n config: ResolvedBkOTConfig;\n tracer: Tracer;\n /** 对外提供的属性脱敏入口,所有插件在 emit 前都应过一次 */\n applyRedact: (attributes: Attributes) => Attributes;\n flush: () => Promise<void>;\n getRuntimeAttributes: () => Attributes;\n getRuntimeAttributesAt: (timestamp: number) => Attributes;\n isSampled: () => boolean;\n isSessionActive: () => boolean;\n isViewActive: () => boolean;\n notifyActivity: (event: BkOTActivityEvent) => void;\n notifySessionLifecycle: (event: BkOTSessionLifecycleEvent) => void;\n onActivity: (listener: (event: BkOTActivityEvent) => void) => () => void;\n onSessionLifecycle: (listener: (event: BkOTSessionLifecycleEvent) => void) => () => void;\n onViewChange: (listener: (view: BkOTViewContext) => void) => () => void;\n onViewCommand: (listener: (command: BkOTViewCommand) => void) => () => void;\n registerInstrumentations: (instrumentations: Instrumentation[]) => void;\n setRuntimeAttributes: (attributes: Attributes, changedAt?: number) => void;\n /** 更新用户上下文;user.id 由接入方传入,SDK 原样透传 */\n setUser: (user: BkOTUserContext) => void;\n /** 更新当前 View 上下文,后续 span 会自动带上 view.* 关联字段 */\n setView: (view: BkOTViewContext, changedAt?: number) => void;\n startSpan: (name: string, attributes?: Attributes, options?: BkOTStartSpanOptions) => Span;\n}\n\n/**\n * startSpan 的扩展选项。\n *\n * 用于\"事件实际发生在过去某个时刻,但 span 现在才创建\"的场景:\n * 例如 PerformanceObserver 在条目结束后才回调,但 span 的时间窗仍需反映\n * 条目真实发生的区间,方便 trace 视图与并发请求对齐。\n *\n * startTime 接受 OT TimeInput;常用 epoch 毫秒(performance.timeOrigin + performance.now())。\n * 配合 span.end(endTime?) 可控制结束时间。\n */\nexport interface BkOTStartSpanOptions {\n /** 用于从历史中解析 Session/View/Action 归属;默认复用数字类型 startTime。 */\n contextTime?: number;\n kind?: SpanKind;\n /** 关联其他 trace 的 Span Link;OTel 要求在创建时传入。 */\n links?: Link[];\n startTime?: TimeInput;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: ResolvedBkOTConfig) => {\n if (typeof plugin.enabled === 'function') {\n return plugin.enabled(config);\n }\n return plugin.enabled !== false;\n};\n\nexport const createPlugin = (plugin: BkOTPlugin) => plugin;\n\nexport class PluginManager {\n private readonly plugins: BkOTPlugin[];\n private startedPlugins: BkOTPlugin[] = [];\n\n public constructor(plugins: BkOTPlugin[]) {\n this.plugins = plugins;\n }\n\n public async flush() {\n // 并行 flush,避免单个慢插件拖累整体\n await Promise.all(this.startedPlugins.map(plugin => Promise.resolve().then(() => plugin.flush?.())));\n }\n\n public async shutdown() {\n const plugins = [...this.startedPlugins].reverse();\n for (const plugin of plugins) {\n await plugin.shutdown?.();\n }\n this.startedPlugins = [];\n }\n\n public async start(context: BkOTRuntimeContext) {\n // 每个插件独立 try/catch,单个插件 init 失败不应拖累其他插件:\n // - 失败的插件不会进入 startedPlugins(shutdown 时不会被反向 teardown)\n // - debug 模式打 warn,方便排查;非 debug 静默失败避免污染业务日志\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n try {\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn(`[bk-ot] plugin \"${plugin.name}\" init failed:`, error);\n }\n }\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { isBkOTEndpoint } from './url';\n\nimport type { ResolvedBkOTConfig } from './config';\nimport type { Attributes, Context, Span as ApiSpan } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\nconst discardedSpans = new WeakSet<object>();\n\nexport const markSpanDiscarded = (span: ApiSpan) => {\n discardedSpans.add(span);\n};\n\nconst getSpanUrl = (span: ReadableSpan): string | undefined => {\n for (const key of SELF_TELEMETRY_URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n return value;\n }\n }\n return undefined;\n};\n\nconst hasSessionId = (attributes: Record<string, unknown> | undefined) => {\n const sessionId = attributes?.[RUM_ATTRIBUTES.sessionId];\n return typeof sessionId === 'string' && sessionId.length > 0;\n};\n\nconst hasViewId = (attributes: Record<string, unknown> | undefined) => {\n const viewId = attributes?.[RUM_ATTRIBUTES.viewId];\n return typeof viewId === 'string' && viewId.length > 0;\n};\n\nconst hasRumContext = (attributes: Record<string, unknown> | undefined) =>\n hasSessionId(attributes) && hasViewId(attributes);\n\n/**\n * 给所有 span 固化创建时的运行时上下文,并在 onEnd 阶段过滤自身上报请求和无 RUM 上下文 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: ResolvedBkOTConfig,\n private readonly getRuntimeAttributes: () => Attributes,\n ) {}\n\n public forceFlush(): Promise<void> {\n return this.inner.forceFlush();\n }\n\n public onEnd(span: ReadableSpan): void {\n const url = getSpanUrl(span);\n if (\n discardedSpans.delete(span) ||\n (url && isBkOTEndpoint(this.config, url)) ||\n (!hasRumContext(span.attributes) &&\n !(span.name === 'browser.session' && hasSessionId(span.attributes)))\n ) {\n return;\n }\n try {\n if (\n this.config.hooks?.beforeSend?.({\n attributes: span.attributes,\n name: span.name,\n signal: 'span',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\n const existingAttributes = (span as Span & { attributes?: Attributes }).attributes ?? {};\n const missingRuntimeAttributes: Attributes = {};\n // instrumentation 显式写入的属性优先,只补齐创建时缺失的运行时上下文。\n for (const [key, value] of Object.entries(this.getRuntimeAttributes())) {\n if (existingAttributes[key] === undefined && value !== undefined) missingRuntimeAttributes[key] = value;\n }\n span.setAttributes(missingRuntimeAttributes);\n this.inner.onStart(span, parentContext);\n }\n\n public shutdown(): Promise<void> {\n return this.inner.shutdown();\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nexport type BkOTSessionLifecycleEvent =\n | {\n expiredAt: number;\n id: string;\n reason: 'external' | 'inactivity' | 'maxLifetime';\n type: 'expired';\n }\n | {\n id: string;\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n startedAt: number;\n type: 'renewed';\n };\n\nexport interface BkOTStartViewOptions {\n attributes?: Attributes;\n name?: string;\n url?: string;\n}\n\nexport interface BkOTUserContext {\n /** 由接入方传入并由 SDK 原样透传;SDK 不 hash、不脱敏、不改写。 */\n id?: string;\n}\n\nexport type BkOTViewCommand =\n | { name: string; type: 'set_name' }\n | { options: BkOTStartViewOptions; type: 'start' }\n | { time: number; type: 'set_loading_time' };\n\nexport interface BkOTViewContext {\n attributes?: Attributes;\n id?: string;\n loadingType?: 'bf_cache' | 'initial_load' | 'route_change' | 'session_renewal';\n name?: string;\n startedAt?: number;\n url?: string;\n urlTemplate?: string;\n}\n\nconst PROTECTED_VIEW_ATTRIBUTE_KEYS = new Set<string>(Object.values(RUM_ATTRIBUTES));\n\n/**\n * 管理 SDK 运行期公共属性快照。\n *\n * 这里仅保存“后续事件都需要带上”的低成本上下文,Session/View/用户切换时更新快照,\n * 插件发 Span 时读取当前快照即可,避免每个插件重复维护公共字段。\n */\nexport class RumRuntimeContext {\n private readonly actionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly attributes: Attributes = {};\n private readonly queuedViewCommands: BkOTViewCommand[] = [];\n\n // 保存关联关系的生效区间,供延迟到达的 PerformanceEntry 按事件时间回查原 Session/View/Action。\n private recordAssociation = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n changedAt: number,\n active: boolean,\n ) => {\n const previous = history.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n if (active) {\n const associationAttributes: Attributes = {};\n for (const key of keys) {\n const value = this.attributes[key];\n if (value !== undefined) associationAttributes[key] = value;\n }\n history.push({ attributes: associationAttributes, startedAt: changedAt });\n }\n if (history.length > 100) history.splice(0, history.length - 100);\n };\n private readonly sessionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private sampled = false;\n private readonly samplingHistory: Array<{ endedAt?: number; sampled: boolean; startedAt: number }> = [];\n private readonly sessionLifecycleListeners = new Set<(event: BkOTSessionLifecycleEvent) => void>();\n private readonly setOptionalAttribute = (key: string, value: string | undefined) => {\n if (value === undefined) {\n delete this.attributes[key];\n return;\n }\n this.attributes[key] = value;\n };\n private user: BkOTUserContext = {};\n private view: BkOTViewContext = {};\n private readonly viewAttributeKeys = new Set<string>();\n private readonly viewChangeListeners = new Set<(view: BkOTViewContext) => void>();\n private readonly viewCommandListeners = new Set<(command: BkOTViewCommand) => void>();\n\n private readonly viewHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n\n private readonly viewScopedAttributeKeys = new Set<string>();\n\n public dispatchViewCommand = (command: BkOTViewCommand) => {\n // autoStart 下调用方可能早于 page-view 插件发命令,先排队可避免 startView/setViewName 丢失。\n if (this.viewCommandListeners.size === 0) {\n this.queuedViewCommands.push(command);\n return;\n }\n this.viewCommandListeners.forEach(listener => listener(command));\n };\n\n public getAttributes = (): Attributes => ({ ...this.attributes });\n\n public getAttributesAt = (timestamp: number): Attributes => {\n if (!Number.isFinite(timestamp)) return this.getAttributes();\n const result = this.getAttributes();\n const applyHistory = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n ) => {\n // 先移除当前关联字段,再用目标时间所在的历史快照覆盖,避免把旧事件错误归到当前 View。\n for (const key of keys) delete result[key];\n const matched = [...history]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt));\n if (matched) Object.assign(result, matched.attributes);\n };\n applyHistory(this.sessionHistory, [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay]);\n applyHistory(this.viewHistory, this.viewScopedAttributeKeys);\n applyHistory(this.actionHistory, [RUM_ATTRIBUTES.actionId]);\n return result;\n };\n\n public getUser = (): BkOTUserContext => ({ ...this.user });\n\n public getView = (): BkOTViewContext => ({ ...this.view });\n\n public isSessionActive = () => typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string';\n\n public isSessionSampledAt = (timestamp: number) => {\n if (!Number.isFinite(timestamp)) return this.sampled;\n return (\n [...this.samplingHistory]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt))\n ?.sampled ?? false\n );\n };\n\n public isViewActive = () => typeof this.attributes[RUM_ATTRIBUTES.viewId] === 'string';\n\n public notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.sessionLifecycleListeners.forEach(listener => listener(event));\n };\n\n public onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) => {\n this.sessionLifecycleListeners.add(listener);\n return () => this.sessionLifecycleListeners.delete(listener);\n };\n\n public onViewChange = (listener: (view: BkOTViewContext) => void) => {\n this.viewChangeListeners.add(listener);\n if (this.view.id) listener(this.getView());\n return () => this.viewChangeListeners.delete(listener);\n };\n\n public onViewCommand = (listener: (command: BkOTViewCommand) => void) => {\n this.viewCommandListeners.add(listener);\n while (this.queuedViewCommands.length) {\n listener(this.queuedViewCommands.shift()!);\n }\n return () => this.viewCommandListeners.delete(listener);\n };\n\n public setAttributes = (attributes: Attributes, changedAt = Date.now()) => {\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined) {\n delete this.attributes[key];\n continue;\n }\n this.attributes[key] = value;\n }\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n this.recordAssociation(\n this.sessionHistory,\n [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay],\n changedAt,\n typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string',\n );\n }\n if (RUM_ATTRIBUTES.actionId in attributes) {\n const actionId = this.attributes[RUM_ATTRIBUTES.actionId];\n this.recordAssociation(\n this.actionHistory,\n [RUM_ATTRIBUTES.actionId],\n changedAt,\n Array.isArray(actionId) ? actionId.length > 0 : typeof actionId === 'string',\n );\n }\n };\n\n public setUser = (user: BkOTUserContext) => {\n this.user = {\n ...this.user,\n ...user,\n };\n\n if ('id' in user) {\n if (user.id === undefined) {\n delete this.attributes[RUM_ATTRIBUTES.userId];\n return;\n }\n this.attributes[RUM_ATTRIBUTES.userId] = user.id;\n }\n };\n\n public setSessionSampled = (sampled: boolean, changedAt = Date.now()) => {\n if (sampled === this.sampled) return;\n const previous = this.samplingHistory.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n this.sampled = sampled;\n if (sampled) this.samplingHistory.push({ sampled, startedAt: changedAt });\n if (this.samplingHistory.length > 100) {\n this.samplingHistory.splice(0, this.samplingHistory.length - 100);\n }\n };\n\n public setView = (view: BkOTViewContext, changedAt = view.startedAt ?? Date.now()) => {\n this.viewAttributeKeys.forEach(key => delete this.attributes[key]);\n this.viewAttributeKeys.clear();\n const viewAttributes: Attributes = {};\n for (const [key, value] of Object.entries(view.attributes ?? {})) {\n // 自定义 View 属性不能覆盖 SDK 维护的关联和语义字段。\n if (value === undefined || PROTECTED_VIEW_ATTRIBUTE_KEYS.has(key)) continue;\n viewAttributes[key] = value;\n this.attributes[key] = value;\n this.viewAttributeKeys.add(key);\n this.viewScopedAttributeKeys.add(key);\n }\n this.view = { ...view, attributes: viewAttributes };\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewId, view.id);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrl, view.url);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrlTemplate, view.urlTemplate);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewLoadingType, view.loadingType);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewName, view.name);\n [\n RUM_ATTRIBUTES.viewId,\n RUM_ATTRIBUTES.viewUrl,\n RUM_ATTRIBUTES.viewUrlTemplate,\n RUM_ATTRIBUTES.viewLoadingType,\n RUM_ATTRIBUTES.viewName,\n ].forEach(key => this.viewScopedAttributeKeys.add(key));\n this.recordAssociation(\n this.viewHistory,\n this.viewScopedAttributeKeys,\n changedAt,\n typeof view.id === 'string' && view.id.length > 0,\n );\n this.viewChangeListeners.forEach(listener => listener(this.getView()));\n };\n\n public constructor(initialAttributes: Attributes = {}, user?: BkOTUserContext) {\n this.setAttributes(initialAttributes);\n if (user) {\n this.setUser(user);\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from './attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\n// 所有插件通过同一分类模型生成检索字段,保证错误、资源、Action 等信号可按统一维度聚合。\nexport const RUM_SPAN_OUTCOME = {\n abort: 'abort',\n error: 'error',\n success: 'success',\n timeout: 'timeout',\n warning: 'warning',\n} as const;\n\nexport const RUM_SPAN_TYPE = {\n action: 'action',\n custom: 'custom',\n error: 'error',\n longTask: 'long_task',\n resource: 'resource',\n session: 'session',\n vital: 'vital',\n view: 'view',\n websocket: 'websocket',\n} as const;\n\nexport type RumRequestOutcome =\n | { error?: unknown; type: 'network_error' }\n | { error?: unknown; type: 'timeout' }\n | { statusCode?: number; type: 'response' }\n | { type: 'abort' };\n\nexport interface RumResourceSpanInput {\n initiatorType?: string;\n method?: string;\n outcome?: RumRequestOutcome;\n url: string;\n}\n\nexport interface RumSpanClassification {\n outcome?: RumSpanOutcome;\n outcomeReason?: string;\n spanType: RumSpanType;\n}\n\nexport type RumSpanOutcome = (typeof RUM_SPAN_OUTCOME)[keyof typeof RUM_SPAN_OUTCOME];\n\nexport type RumSpanType = (typeof RUM_SPAN_TYPE)[keyof typeof RUM_SPAN_TYPE];\n\nexport interface RumUrlTarget {\n serverAddress?: string;\n serverPort?: number;\n}\n\nexport const getHttpOutcome = (statusCode: number | undefined): RumSpanOutcome => {\n if (statusCode === 408 || statusCode === 504) return RUM_SPAN_OUTCOME.timeout;\n if (typeof statusCode === 'number' && statusCode >= 400) return RUM_SPAN_OUTCOME.error;\n return RUM_SPAN_OUTCOME.success;\n};\n\nexport const getHttpOutcomeReason = (statusCode: number | undefined) => {\n if (statusCode === 408 || statusCode === 504) return 'network_timeout';\n if (typeof statusCode !== 'number' || statusCode < 400) return undefined;\n return statusCode >= 500 ? 'http_5xx' : 'http_4xx';\n};\n\nexport const getVitalOutcome = (rating: string | undefined): RumSpanOutcome =>\n rating === 'good' ? RUM_SPAN_OUTCOME.success : RUM_SPAN_OUTCOME.warning;\n\nexport const getUrlTarget = (url: string | undefined): RumUrlTarget => {\n if (!url) {\n return {};\n }\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n return {\n serverAddress: parsed.hostname,\n serverPort: parsed.port ? Number(parsed.port) : undefined,\n };\n } catch {\n return {};\n }\n};\n\nexport const buildRumSpanAttributes = ({\n outcome = RUM_SPAN_OUTCOME.success,\n outcomeReason,\n spanType,\n}: RumSpanClassification): Attributes => ({\n [RUM_ATTRIBUTES.spanType]: spanType,\n [RUM_ATTRIBUTES.outcomeType]: outcome,\n ...(outcomeReason ? { [RUM_ATTRIBUTES.outcomeReason]: outcomeReason } : {}),\n});\n\nexport const buildRumResourceSpanAttributes = ({\n initiatorType = 'other',\n method,\n outcome,\n url,\n}: RumResourceSpanInput): Attributes => {\n const target = getUrlTarget(url);\n const responseStatus =\n outcome?.type === 'response' && typeof outcome.statusCode === 'number' && outcome.statusCode > 0\n ? outcome.statusCode\n : undefined;\n const outcomeType =\n outcome?.type === 'abort'\n ? RUM_SPAN_OUTCOME.abort\n : outcome?.type === 'timeout'\n ? RUM_SPAN_OUTCOME.timeout\n : outcome?.type === 'network_error'\n ? RUM_SPAN_OUTCOME.error\n : typeof responseStatus === 'number'\n ? getHttpOutcome(responseStatus)\n : RUM_SPAN_OUTCOME.success;\n const outcomeReason =\n outcome?.type === 'timeout'\n ? 'network_timeout'\n : outcome?.type === 'network_error'\n ? 'network'\n : typeof responseStatus === 'number'\n ? getHttpOutcomeReason(responseStatus)\n : undefined;\n const normalizedMethod = method?.toUpperCase();\n return {\n ...buildRumSpanAttributes({\n outcome: outcomeType,\n outcomeReason,\n spanType: RUM_SPAN_TYPE.resource,\n }),\n ...(target.serverAddress ? { 'server.address': target.serverAddress } : {}),\n ...(target.serverPort !== undefined ? { 'server.port': target.serverPort } : {}),\n 'resource.type': initiatorType,\n ...(normalizedMethod ? { 'http.request.method': normalizedMethod } : {}),\n ...(typeof responseStatus === 'number' ? { 'http.response.status_code': responseStatus } : {}),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createRandomId } from '../core/id';\nimport { matchesUrlList } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTActivityEvent } from '../core/action-activity';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 用户点击采集以“点击后产生的副作用”为 Action 生命周期:关联请求、资源、长任务、DOM 变化和错误,\n * 在活动安静后结束。完成后的 Action 暂缓至连击窗口结束再上报,以便把三次相近点击合并为一次 rage click。\n */\nconst ACTION_VALIDATION_DELAY = 100;\nconst ACTION_END_DELAY = 100;\nconst CLICK_CHAIN_DURATION = 1_000;\nconst CLICK_CHAIN_MAX_DISTANCE = 100;\nconst DEFAULT_ACTION_MAX_DURATION = 10_000;\nconst DEFAULT_ACTION_NAME_ATTRIBUTE = 'data-bk-action-name';\nconst MAX_NAME_LENGTH = 100;\nconst MAX_PARENTS_TO_CONSIDER = 10;\n\nexport interface BkOTActionPluginOptions {\n actionNameAttribute?: string;\n maxDuration?: number;\n /** 默认仅采集显式 data 属性;设为 text 才会读取可访问名称和 DOM 文本。 */\n nameCollection?: 'explicit' | 'text';\n}\n\ntype ActionFrustrationType = 'dead_click' | 'error_click' | 'rage_click';\n\ninterface ActiveAction {\n completed?: { endTime: number; hasLoadingTime: boolean };\n frustrations: Set<ActionFrustrationType>;\n hadActivity: boolean;\n hadError: boolean;\n id: string;\n lastActivityTime: number;\n maxDurationTimer: ReturnType<typeof setTimeout>;\n pendingRequests: Set<string>;\n quietTimer?: ReturnType<typeof setTimeout>;\n reportTimer?: ReturnType<typeof setTimeout>;\n runtimeAttributes: Attributes;\n startTime: number;\n target: Element;\n targetName: string;\n targetTag: string;\n userActivity: { input: boolean; scroll: boolean; selection: boolean };\n validationTimer: ReturnType<typeof setTimeout>;\n}\n\ninterface RecentClick {\n action: ActiveAction;\n target: Element;\n x: number;\n y: number;\n}\n\nconst normalizeText = (value: string) => value.replace(/\\s+/g, ' ').trim().slice(0, MAX_NAME_LENGTH);\n\nconst getParentElement = (element: Element) => {\n if (element.parentElement) {\n return element.parentElement;\n }\n const root = element.getRootNode?.();\n return typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.host : null;\n};\n\nconst findAttributeOnAncestors = (element: Element, attribute: string) => {\n let current: Element | null = element;\n while (current) {\n const value = current.getAttribute(attribute);\n if (value) {\n return normalizeText(value);\n }\n current = getParentElement(current);\n }\n return undefined;\n};\n\nconst getElementText = (element: Element) =>\n (element as HTMLElement).isContentEditable ? '' : normalizeText(element.textContent ?? '');\n\nconst getPriorityName = (element: Element) => {\n const formElement = element as Element & {\n labels?: HTMLLabelElement[] | NodeListOf<HTMLLabelElement>;\n value?: string;\n };\n const label = formElement.labels?.[0];\n if (label) {\n const labelText = getElementText(label);\n if (labelText) return labelText;\n }\n\n const tagName = element.tagName.toLowerCase();\n const inputType = element.getAttribute('type')?.toLowerCase();\n if (tagName === 'input' && ['button', 'reset', 'submit'].includes(inputType ?? '')) {\n const value = normalizeText(formElement.value ?? '');\n if (value) return value;\n }\n\n if (tagName === 'button' || tagName === 'label' || element.getAttribute('role') === 'button') {\n const text = getElementText(element);\n if (text) return text;\n }\n\n for (const attribute of ['aria-label', 'alt', 'name', 'title', 'placeholder']) {\n const value = normalizeText(element.getAttribute(attribute) ?? '');\n if (value) return value;\n }\n\n return undefined;\n};\n\nexport const getActionNameFromElement = (\n target: Element,\n actionNameAttribute = DEFAULT_ACTION_NAME_ATTRIBUTE,\n includeText = false,\n): string => {\n const explicitName = findAttributeOnAncestors(target, actionNameAttribute);\n if (explicitName) {\n return explicitName;\n }\n if (!includeText) return target.tagName.toLowerCase();\n\n let current: Element | null = target;\n let parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const priorityName = getPriorityName(current);\n if (priorityName) return priorityName;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n current = target;\n parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const text = getElementText(current);\n if (text) return text;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n return target.tagName.toLowerCase();\n};\n\nconst getElement = (event: Event) => {\n const composedTarget = event.composedPath?.()[0];\n if (composedTarget instanceof Element) return composedTarget;\n return event.target instanceof Element ? event.target : undefined;\n};\n\nconst toEpochTime = (event: Event) => {\n const eventTime = event.timeStamp;\n if (!Number.isFinite(eventTime) || eventTime <= 0) return Date.now();\n if (eventTime > 1_000_000_000_000) return eventTime;\n return typeof performance === 'undefined' ? Date.now() : performance.timeOrigin + eventTime;\n};\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\n\nexport const isDeadClickExcluded = (target: Element) => {\n let current: Element | null = target;\n if (current.tagName.toLowerCase() === 'label' && current.hasAttribute('for')) {\n current = document.getElementById(current.getAttribute('for') ?? '');\n }\n\n while (current) {\n const tagName = current.tagName.toLowerCase();\n if (\n current.hasAttribute('contenteditable') ||\n tagName === 'canvas' ||\n tagName === 'select' ||\n tagName === 'textarea'\n ) {\n return true;\n }\n if (tagName === 'a' && current.hasAttribute('href')) return true;\n if (tagName === 'input') {\n return !['button', 'checkbox', 'radio', 'range', 'reset', 'submit'].includes(\n current.getAttribute('type')?.toLowerCase() ?? 'text',\n );\n }\n current = getParentElement(current);\n }\n return false;\n};\n\nexport const createActionPlugin = (options: BkOTActionPluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeActions = new Map<string, ActiveAction>();\n const pendingActions = new Set<ActiveAction>();\n const actionNameAttribute = options.actionNameAttribute || DEFAULT_ACTION_NAME_ATTRIBUTE;\n const maxDuration = positiveNumber(options.maxDuration, DEFAULT_ACTION_MAX_DURATION);\n let recentClicks: RecentClick[] = [];\n let runtimeContext: BkOTRuntimeContext | undefined;\n\n const updateRuntimeActionIds = () => {\n runtimeContext?.setRuntimeAttributes({\n [RUM_ATTRIBUTES.actionId]: activeActions.size ? [...activeActions.keys()] : undefined,\n });\n };\n\n const isDeadAction = (action: ActiveAction) =>\n !action.hadActivity &&\n !action.userActivity.input &&\n !action.userActivity.scroll &&\n !action.userActivity.selection &&\n !isDeadClickExcluded(action.target);\n\n const reportAction = (action: ActiveAction) => {\n if (!pendingActions.delete(action) || !runtimeContext || !action.completed) return;\n clearTimeout(action.reportTimer);\n if (action.hadError) action.frustrations.add('error_click');\n if (isDeadAction(action)) action.frustrations.add('dead_click');\n const frustrationTypes = ['rage_click', 'dead_click', 'error_click'].filter(type =>\n action.frustrations.has(type as ActionFrustrationType),\n ) as ActionFrustrationType[];\n const { endTime, hasLoadingTime } = action.completed;\n const loadingTime = Math.max(0, endTime - action.startTime);\n const hasWarning = action.hadError || frustrationTypes.length > 0;\n const span = runtimeContext.startSpan(\n 'action.click',\n {\n ...runtimeContext.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n outcome: hasWarning ? RUM_SPAN_OUTCOME.warning : RUM_SPAN_OUTCOME.success,\n spanType: RUM_SPAN_TYPE.action,\n }),\n ...action.runtimeAttributes,\n [RUM_ATTRIBUTES.actionId]: action.id,\n [RUM_ATTRIBUTES.actionType]: 'click',\n [RUM_ATTRIBUTES.actionName]: `Click on ${action.targetName}`,\n [RUM_ATTRIBUTES.actionTargetName]: action.targetName,\n [RUM_ATTRIBUTES.actionTargetTag]: action.targetTag,\n ...(frustrationTypes.length ? { [RUM_ATTRIBUTES.actionFrustrationType]: frustrationTypes } : {}),\n ...(hasLoadingTime ? { [RUM_ATTRIBUTES.actionLoadingTime]: loadingTime } : {}),\n },\n { startTime: action.startTime },\n );\n span.end(hasLoadingTime ? endTime : action.startTime);\n };\n\n const finishAction = (action: ActiveAction, endTime: number, hasLoadingTime: boolean) => {\n if (!activeActions.delete(action.id) || !runtimeContext) return;\n clearTimeout(action.validationTimer);\n clearTimeout(action.quietTimer);\n clearTimeout(action.maxDurationTimer);\n updateRuntimeActionIds();\n action.completed = { endTime, hasLoadingTime };\n pendingActions.add(action);\n // 等待连击窗口关闭后再落数,否则前两次点击已上报便无法合并为 rage click。\n const reportDelay = Math.max(0, action.startTime + CLICK_CHAIN_DURATION - Date.now());\n action.reportTimer = setTimeout(() => reportAction(action), reportDelay);\n };\n\n const flushPendingActions = () => {\n [...pendingActions].forEach(reportAction);\n };\n\n const scheduleActionEnd = (action: ActiveAction) => {\n clearTimeout(action.quietTimer);\n if (!action.hadActivity || action.pendingRequests.size) return;\n const delay = Math.max(0, action.lastActivityTime + ACTION_END_DELAY - Date.now());\n action.quietTimer = setTimeout(() => finishAction(action, action.lastActivityTime, true), delay);\n };\n\n const markActivity = (action: ActiveAction, time: number) => {\n action.hadActivity = true;\n action.lastActivityTime = Math.max(action.lastActivityTime, time);\n scheduleActionEnd(action);\n };\n\n const isInActionWindow = (action: ActiveAction, time: number) =>\n time >= action.startTime && time <= action.startTime + maxDuration;\n\n const onActivity = (event: BkOTActivityEvent) => {\n for (const action of activeActions.values()) {\n if (event.type === 'request_start') {\n if (event.url && matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)) continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.pendingRequests.add(event.id);\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'request_end') {\n if (!action.pendingRequests.delete(event.id)) continue;\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'long_task' || event.type === 'resource') {\n if (\n event.type === 'resource' &&\n event.url &&\n matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)\n ) {\n continue;\n }\n if (\n event.startTime > action.startTime + maxDuration ||\n (event.type === 'resource' ? event.startTime < action.startTime : event.endTime < action.startTime)\n ) {\n continue;\n }\n markActivity(action, event.endTime);\n continue;\n }\n if (event.type === 'dom_mutation' || event.type === 'window_open') {\n if (isInActionWindow(action, event.time)) markActivity(action, event.time);\n continue;\n }\n if (event.type !== 'error') continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.hadError = true;\n markActivity(action, event.time);\n }\n };\n\n const finishActiveActions = (endTime = Date.now()) => {\n [...activeActions.values()].forEach(action => finishAction(action, endTime, action.hadActivity));\n };\n\n const registerClick = (action: ActiveAction, event: Event) => {\n const x = 'clientX' in event && typeof event.clientX === 'number' ? event.clientX : 0;\n const y = 'clientY' in event && typeof event.clientY === 'number' ? event.clientY : 0;\n recentClicks = recentClicks.filter(click => action.startTime - click.action.startTime <= CLICK_CHAIN_DURATION);\n const lastClick = recentClicks.at(-1);\n const hasInterruptedChain = recentClicks.some(\n click => click.action.userActivity.scroll || click.action.userActivity.selection,\n );\n // 只有同一目标附近、未被滚动或选中文本打断的连续点击才视为同一连击链。\n const isSimilar =\n lastClick?.target === action.target && Math.hypot(lastClick.x - x, lastClick.y - y) <= CLICK_CHAIN_MAX_DISTANCE;\n if (hasInterruptedChain || (lastClick && !isSimilar)) recentClicks = [];\n recentClicks.push({ action, target: action.target, x, y });\n if (\n recentClicks.length >= 3 &&\n action.startTime - recentClicks[recentClicks.length - 3].action.startTime <= CLICK_CHAIN_DURATION\n ) {\n const rageSources = recentClicks.slice(-3).map(click => click.action);\n rageSources.forEach(source => source.frustrations.add('rage_click'));\n recentClicks = [];\n }\n };\n\n return {\n name: 'action',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n runtimeContext = context;\n\n const listenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n\n const startAction = (event: Event) => {\n if (!context.isSampled()) return;\n if ('button' in event && typeof event.button === 'number' && event.button !== 0) return;\n const element = getElement(event);\n if (!element) return;\n const startTime = toEpochTime(event);\n const id = createRandomId('action');\n const action: ActiveAction = {\n frustrations: new Set(),\n hadActivity: false,\n hadError: false,\n id,\n lastActivityTime: startTime,\n maxDurationTimer: setTimeout(() => finishAction(action, startTime + maxDuration, true), maxDuration),\n pendingRequests: new Set(),\n runtimeAttributes: context.getRuntimeAttributes(),\n startTime,\n target: element,\n targetName: getActionNameFromElement(element, actionNameAttribute, options.nameCollection === 'text'),\n targetTag: element.tagName.toLowerCase(),\n userActivity: { input: false, scroll: false, selection: false },\n validationTimer: setTimeout(() => {\n if (!action.hadActivity) finishAction(action, action.startTime, false);\n }, ACTION_VALIDATION_DELAY),\n };\n activeActions.set(id, action);\n registerClick(action, event);\n updateRuntimeActionIds();\n };\n\n window.addEventListener('click', startAction, listenerOptions);\n teardownCallbacks.push(() => window.removeEventListener('click', startAction, listenerOptions));\n\n const unsubscribeActivity = context.onActivity(onActivity);\n const unsubscribeSession = context.onSessionLifecycle(event => {\n if (event.type !== 'expired') return;\n finishActiveActions(event.expiredAt);\n flushPendingActions();\n });\n teardownCallbacks.push(unsubscribeActivity, unsubscribeSession);\n\n const finishOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n finishActiveActions();\n flushPendingActions();\n }\n };\n const finishOnPageHide = () => {\n finishActiveActions();\n flushPendingActions();\n };\n window.addEventListener('pagehide', finishOnPageHide);\n document.addEventListener('visibilitychange', finishOnHidden);\n teardownCallbacks.push(\n () => window.removeEventListener('pagehide', finishOnPageHide),\n () => document.removeEventListener('visibilitychange', finishOnHidden),\n );\n\n const markUserActivity = (type: keyof ActiveAction['userActivity']) => {\n const actions = new Set([\n ...activeActions.values(),\n ...pendingActions,\n ...recentClicks.map(click => click.action),\n ]);\n actions.forEach(action => {\n action.userActivity[type] = true;\n // 滚动/选择表明用户仍在正常交互,撤销尚未上报的 rage click 判定。\n if (type !== 'input') action.frustrations.delete('rage_click');\n });\n };\n const onInput = () => markUserActivity('input');\n const onScroll = () => markUserActivity('scroll');\n const onSelectionChange = () => markUserActivity('selection');\n window.addEventListener('input', onInput, listenerOptions);\n window.addEventListener('scroll', onScroll, listenerOptions);\n document.addEventListener('selectionchange', onSelectionChange, listenerOptions);\n teardownCallbacks.push(\n () => window.removeEventListener('input', onInput, listenerOptions),\n () => window.removeEventListener('scroll', onScroll, listenerOptions),\n () => document.removeEventListener('selectionchange', onSelectionChange, listenerOptions),\n );\n },\n shutdown() {\n finishActiveActions();\n flushPendingActions();\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n recentClicks = [];\n runtimeContext = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nexport const EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE = 'data-bk-excluded-activity-mutations';\n\nconst isExcludedMutation = (mutation: MutationRecord) => {\n const target = mutation.type === 'characterData' ? mutation.target.parentElement : mutation.target;\n return target instanceof Element && Boolean(target.closest(`[${EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE}]`));\n};\n\n/**\n * 页面级 DOM 活动只观察一次,再通过运行时活动总线分发给 View、Action 和白屏检测。\n */\nexport const createActivityPlugin = (): BkOTPlugin => {\n let mutationObserver: MutationObserver | undefined;\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'activity',\n init(context) {\n if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {\n mutationObserver = new MutationObserver(mutations => {\n if (mutations.length && !mutations.every(isExcludedMutation)) {\n context.notifyActivity({ time: Date.now(), type: 'dom_mutation' });\n }\n });\n mutationObserver.observe(document, { attributes: true, characterData: true, childList: true, subtree: true });\n }\n\n if (typeof window === 'undefined') return;\n\n if (typeof window.open === 'function') {\n const originalOpen = window.open;\n const patchedOpen = ((...args: Parameters<typeof window.open>) => {\n context.notifyActivity({ time: Date.now(), type: 'window_open' });\n return Reflect.apply(originalOpen, window, args);\n }) as typeof window.open;\n window.open = patchedOpen;\n teardownCallbacks.push(() => {\n if (window.open === patchedOpen) window.open = originalOpen;\n });\n }\n\n },\n shutdown() {\n mutationObserver?.disconnect();\n mutationObserver = undefined;\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { matchesUrlList } from './url';\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { BkOTRuntimeContext } from './plugin';\n\nexport const PAGE_ACTIVITY_VALIDATION_DELAY = 100;\nexport const PAGE_ACTIVITY_END_DELAY = 100;\n\nexport type PageActivityEnd = { endTime: number; hadActivity: true } | { hadActivity: false };\n\nconst isActivityUrlExcluded = (context: BkOTRuntimeContext, url: string | undefined) =>\n Boolean(url && matchesUrlList(context.config.view.excludedActivityUrls, url));\n\n/**\n * 等待 View/Action 启动后的请求、静态资源和 DOM 变更全部安静 100ms。\n * 请求用 id 精确配对,避免并发请求提前结束等待。\n */\nexport const waitForPageActivityEnd = (\n context: BkOTRuntimeContext,\n startTime: number,\n callback: (result: PageActivityEnd) => void,\n) => {\n const pendingRequests = new Set<string>();\n let completed = false;\n let hadActivity = false;\n let lastActivityTime = startTime;\n let quietTimer: ReturnType<typeof setTimeout> | undefined;\n\n const complete = (result: PageActivityEnd) => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n callback(result);\n };\n\n const scheduleEnd = () => {\n clearTimeout(quietTimer);\n if (!hadActivity || pendingRequests.size) return;\n quietTimer = setTimeout(\n () => complete({ endTime: lastActivityTime, hadActivity: true }),\n Math.max(0, lastActivityTime + PAGE_ACTIVITY_END_DELAY - Date.now()),\n );\n };\n\n const markActivity = (time: number) => {\n hadActivity = true;\n lastActivityTime = Math.max(lastActivityTime, time);\n scheduleEnd();\n };\n\n const onActivity = (event: BkOTActivityEvent) => {\n if (event.type === 'error' || event.type === 'long_task') return;\n if (event.type === 'request_start') {\n if (event.time < startTime || isActivityUrlExcluded(context, event.url)) return;\n pendingRequests.add(event.id);\n markActivity(event.time);\n return;\n }\n if (event.type === 'request_end') {\n if (!pendingRequests.delete(event.id)) return;\n markActivity(event.time);\n return;\n }\n if (event.type === 'resource') {\n if (event.endTime < startTime || isActivityUrlExcluded(context, event.url)) return;\n markActivity(event.endTime);\n return;\n }\n if (event.time >= startTime) markActivity(event.time);\n };\n\n const unsubscribe = context.onActivity(onActivity);\n const validationTimer = setTimeout(() => {\n if (!hadActivity) complete({ hadActivity: false });\n }, PAGE_ACTIVITY_VALIDATION_DELAY);\n\n return () => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 通过视口九宫格采样估算空白比例,并在页面稳定后连续确认两次。每个 View 最多上报一次,\n * 避免首屏渲染、路由切换和 loading 状态造成瞬时误报。\n */\nexport interface BkOTBlankScreenPluginOptions {\n /** 页面活动稳定后首次检查的延迟,默认 1000ms。 */\n checkDelay?: number;\n /** 两次空白结果之间的确认间隔,默认 200ms。 */\n confirmationDelay?: number;\n /** loading、骨架屏等不参与空白比例计算的元素。 */\n ignoreSelectors?: string[];\n /** 应用挂载根节点,默认 body。根节点不存在时回退到 body 检测。 */\n rootSelector?: string;\n /** 空白采样点比例阈值,默认 0.8。 */\n threshold?: number;\n}\n\ninterface DetectionResult {\n centerElement: string;\n emptyCount: number;\n ignoredCount: number;\n reason: 'empty_viewport' | 'missing_root';\n rootFound: boolean;\n score: number;\n validCount: number;\n}\n\nconst DEFAULT_CHECK_DELAY = 1000;\nconst DEFAULT_CONFIRMATION_DELAY = 200;\nconst DEFAULT_ROOT_SELECTOR = 'body';\nconst DEFAULT_THRESHOLD = 0.8;\nconst DEFAULT_IGNORED_SELECTORS = [\n '[data-loading=\"true\"]',\n '[aria-busy=\"true\"]',\n '.loading',\n '.is-loading',\n '.spinner',\n '.skeleton',\n '.bk-loading',\n];\n\nconst SAMPLE_POINTS: ReadonlyArray<readonly [number, number]> = [\n [0.2, 0.2],\n [0.5, 0.2],\n [0.8, 0.2],\n [0.2, 0.5],\n [0.5, 0.5],\n [0.8, 0.5],\n [0.2, 0.8],\n [0.5, 0.8],\n [0.8, 0.8],\n];\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst boundedRatio = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : fallback;\n\nconst getElementSelector = (element: Element | undefined) => {\n if (!element) return '';\n const tag = element.tagName.toLowerCase();\n return element.id ? `${tag}#${element.id}` : tag;\n};\n\nconst matchesAny = (element: Element, selectors: string[]) =>\n selectors.some(selector => {\n try {\n return element.matches(selector) || Boolean(element.closest(selector));\n } catch {\n return false;\n }\n });\n\nconst isVisuallyPresent = (element: Element) => {\n if (typeof getComputedStyle !== 'function') return true;\n const style = getComputedStyle(element);\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity || 1) > 0;\n};\n\nconst getElementsAtPoint = (x: number, y: number): Element[] => {\n if (typeof document.elementsFromPoint === 'function') return document.elementsFromPoint(x, y);\n const element = document.elementFromPoint(x, y);\n return element ? [element] : [];\n};\n\nexport const detectBlankScreen = (rootSelector: string, ignoredSelectors: string[]): DetectionResult => {\n const configuredRoot = document.querySelector(rootSelector);\n const root = configuredRoot ?? document.body ?? document.documentElement;\n const rootFound = Boolean(configuredRoot);\n\n let emptyCount = 0;\n let ignoredCount = 0;\n let centerElement = '';\n // 采样视口九宫格而非遍历 DOM,检测成本固定;被 loading/骨架屏覆盖的点不进入分母。\n for (const [xRatio, yRatio] of SAMPLE_POINTS) {\n const layers = getElementsAtPoint(window.innerWidth * xRatio, window.innerHeight * yRatio);\n if (xRatio === 0.5 && yRatio === 0.5) centerElement = getElementSelector(layers[0]);\n if (layers.some(element => matchesAny(element, ignoredSelectors))) {\n ignoredCount += 1;\n continue;\n }\n const hasContent = layers.some(\n element =>\n element !== root &&\n element !== document.body &&\n element !== document.documentElement &&\n root.contains(element) &&\n isVisuallyPresent(element),\n );\n if (!hasContent) emptyCount += 1;\n }\n\n const validCount = SAMPLE_POINTS.length - ignoredCount;\n return {\n centerElement,\n emptyCount,\n ignoredCount,\n reason: rootFound ? 'empty_viewport' : 'missing_root',\n rootFound,\n score: validCount ? emptyCount / validCount : 0,\n validCount,\n };\n};\n\nexport const createBlankScreenPlugin = (options: BkOTBlankScreenPluginOptions = {}): BkOTPlugin => {\n const checkDelay = nonNegativeNumber(options.checkDelay, DEFAULT_CHECK_DELAY);\n const confirmationDelay = nonNegativeNumber(options.confirmationDelay, DEFAULT_CONFIRMATION_DELAY);\n const rootSelector = options.rootSelector || DEFAULT_ROOT_SELECTOR;\n const threshold = boundedRatio(options.threshold, DEFAULT_THRESHOLD);\n const ignoredSelectors = [...DEFAULT_IGNORED_SELECTORS, ...(options.ignoreSelectors ?? [])];\n let activeViewId: string | undefined;\n let reportedViewId: string | undefined;\n let stopActivityTracking: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeView = () => {};\n let visibilityListener: (() => void) | undefined;\n\n return {\n name: 'blank-screen',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n\n const report = (viewId: string, result: DetectionResult) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (!context.isSampled()) return;\n reportedViewId = viewId;\n const occurredAt = Date.now();\n const span = context.startSpan(\n 'browser.blank_screen',\n {\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n outcomeReason: 'blank_screen',\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'blank_screen.center_element': result.centerElement,\n 'blank_screen.empty_ratio': result.score,\n 'blank_screen.empty_sample_count': result.emptyCount,\n 'blank_screen.ignored_sample_count': result.ignoredCount,\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_found': result.rootFound,\n 'blank_screen.root_selector': rootSelector,\n 'blank_screen.sample_count': SAMPLE_POINTS.length,\n 'blank_screen.valid_sample_count': result.validCount,\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n const detectAndConfirm = (viewId: string) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (document.visibilityState === 'hidden') return;\n let first: DetectionResult;\n try {\n first = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] detection failed:', error);\n return;\n }\n if (!first.validCount || first.score < threshold) return;\n timer = setTimeout(() => {\n if (activeViewId !== viewId || document.visibilityState === 'hidden') return;\n let confirmed: DetectionResult;\n try {\n confirmed = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] confirmation failed:', error);\n return;\n }\n if (confirmed.validCount && confirmed.score >= threshold) report(viewId, confirmed);\n }, confirmationDelay);\n };\n\n const schedule = (viewId: string) => {\n clearTimer();\n if (document.visibilityState === 'hidden' || !context.isSampled()) return;\n timer = setTimeout(() => detectAndConfirm(viewId), checkDelay);\n };\n\n unsubscribeView = context.onViewChange(view => {\n if (view.id === activeViewId) return;\n clearTimer();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n activeViewId = view.id;\n reportedViewId = undefined;\n if (view.id) {\n const viewId = view.id;\n // 页面活动结束后再延迟检测,避免把正常渲染过程中的短暂空白误报为白屏。\n stopActivityTracking = waitForPageActivityEnd(context, Date.now(), () => {\n stopActivityTracking = undefined;\n schedule(viewId);\n });\n }\n });\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n clearTimer();\n } else if (activeViewId && reportedViewId !== activeViewId) {\n schedule(activeViewId);\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n },\n shutdown() {\n clearTimeout(timer);\n timer = undefined;\n unsubscribeView();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n activeViewId = undefined;\n reportedViewId = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\ninterface PendingRequestTiming {\n entry?: PerformanceResourceTiming;\n startTime: number;\n timer?: ReturnType<typeof setTimeout>;\n url: string;\n callback?: (entry?: PerformanceResourceTiming) => void;\n}\n\nconst MAX_PENDING_REQUESTS = 100;\nconst RESOURCE_TIMING_WAIT_MS = 200;\nconst pendingRequests: PendingRequestTiming[] = [];\n\nconst normalizeUrl = (url: string) => url.replace(/#.*$/, '');\n\nconst removePending = (pending: PendingRequestTiming) => {\n const index = pendingRequests.indexOf(pending);\n if (index >= 0) pendingRequests.splice(index, 1);\n if (pending.timer !== undefined) clearTimeout(pending.timer);\n};\n\nconst finishPending = (pending: PendingRequestTiming, entry?: PerformanceResourceTiming) => {\n const callback = pending.callback;\n removePending(pending);\n pending.callback = undefined;\n callback?.(entry);\n};\n\nexport const notifyRequestTiming = (entry: PerformanceResourceTiming) => {\n const entryStartTime = performance.timeOrigin + entry.startTime;\n // 同 URL 并发时选择起始时间最接近的请求;50ms 容差覆盖 JS 调用与浏览器创建 timing entry 的偏差。\n const candidate = pendingRequests\n .filter(\n pending =>\n !pending.entry &&\n normalizeUrl(pending.url) === normalizeUrl(entry.name) &&\n entryStartTime >= pending.startTime - 50,\n )\n .sort((left, right) => Math.abs(left.startTime - entryStartTime) - Math.abs(right.startTime - entryStartTime))[0];\n if (!candidate) return;\n candidate.entry = entry;\n if (candidate.callback) finishPending(candidate, entry);\n};\n\nexport const trackRequestTiming = (url: string, startTime = Date.now()) => {\n const pending: PendingRequestTiming = { startTime, url };\n pendingRequests.push(pending);\n if (pendingRequests.length > MAX_PENDING_REQUESTS) finishPending(pendingRequests[0]);\n return {\n cancel: () => finishPending(pending),\n complete: (callback: (entry?: PerformanceResourceTiming) => void) => {\n pending.callback = callback;\n if (pending.entry) {\n finishPending(pending, pending.entry);\n return;\n }\n // XHR 终止事件/Fetch settle 通常早于 ResourceTiming 回调,短暂等待以补齐 DNS、连接和下载阶段。\n pending.timer = setTimeout(() => finishPending(pending), RESOURCE_TIMING_WAIT_MS);\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * 被观测请求的 Trace Context 规划。\n *\n * 后端 APM 只解析 W3C `traceparent`,并且要求一个后端 trace 只对应一个 API 请求,因此这里不复用\n * RUM Span 自身的 trace context,而是为每个命中白名单的请求生成独立的 trace-id/span-id,再通过\n * Span Link 把后端 trace 关联回 RUM Span。\n *\n * 如果业务已经自行设置了 trace header(`traceparent`、`sentry-trace`、b3、Datadog、sw8),则完全\n * 不注入,避免破坏业务既有的传播方式,只把已有链路建成 Link。\n *\n * Link 结构恒定:`context` 承载 trace-id/span-id,`attributes` 记录命中的格式与原始 header。\n * sw8 这类无法映射为合法 OTel id 的格式会补一对占位 id,让 Link 结构保持统一。\n */\n\nimport { TraceFlags } from '@opentelemetry/api';\n\nimport type { Attributes, Link } from '@opentelemetry/api';\n\n/** 大小写不敏感地读取一个请求 header */\nexport type BkOTHeaderReader = (name: string) => string | undefined;\n\nexport interface BkOTRequestTracePlan {\n /** 关联后端 trace 的 Span Link,恒为一条 */\n links: Link[];\n /** 需要注入的 traceparent;业务已有 trace header 时为 undefined */\n traceparent?: string;\n}\n\nexport type BkOTUpstreamTraceFormat = 'b3' | 'b3-multi' | 'datadog' | 'sentry-trace' | 'sw8' | 'traceparent';\n\nexport const TRACEPARENT_HEADER = 'traceparent';\n\nconst TRACE_ID_LENGTH = 32;\nconst SPAN_ID_LENGTH = 16;\n\nconst HEX_DIGITS = '0123456789abcdef';\nconst HEX_PATTERN = /^[\\da-f]+$/;\nconst ZERO_PATTERN = /^0+$/;\nconst DECIMAL_PATTERN = /^\\d+$/;\n\n// 00-{trace-id}-{parent-id}-{trace-flags};允许未来版本追加字段,但 ff 是规范保留值\nconst TRACEPARENT_PATTERN = /^([\\da-f]{2})-([\\da-f]{32})-([\\da-f]{16})-([\\da-f]{2})(?:-.*)?$/;\nconst SENTRY_TRACE_PATTERN = /^([\\da-f]{32})-([\\da-f]{16})(?:-([01]))?$/;\n// Datadog 128 位 trace-id 的高 64 位放在 x-datadog-tags 的 _dd.p.tid 里\nconst DATADOG_TRACE_ID_HIGH_PATTERN = /(?:^|,)_dd\\.p\\.tid=([\\da-f]{16})(?:,|$)/;\nconst DATADOG_TRACE_ID_HIGH_DEFAULT = '0000000000000000';\n\n/** OTel 要求 id 为定长小写 hex 且不能全零 */\nconst isValidHexId = (value: string, length: number) =>\n value.length === length && HEX_PATTERN.test(value) && !ZERO_PATTERN.test(value);\n\n// b3 与 Datadog 允许 64 位 trace-id,映射到 OTel 的 128 位时按规范左侧补零\nconst normalizeTraceId = (value: string) => {\n const lower = value.toLowerCase();\n if (isValidHexId(lower, TRACE_ID_LENGTH)) return lower;\n return isValidHexId(lower, SPAN_ID_LENGTH) ? lower.padStart(TRACE_ID_LENGTH, '0') : undefined;\n};\n\nconst normalizeSpanId = (value: string) => {\n const lower = value.toLowerCase();\n return isValidHexId(lower, SPAN_ID_LENGTH) ? lower : undefined;\n};\n\nconst createRandomHexId = (length: number) => {\n const bytes = new Uint8Array(length / 2);\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n crypto.getRandomValues(bytes);\n } else {\n for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);\n }\n let hex = '';\n for (const byte of bytes) {\n hex += HEX_DIGITS[byte >> 4] + HEX_DIGITS[byte & 15];\n }\n // 全零是 OTel 的非法 id,概率极低但必须排除\n return ZERO_PATTERN.test(hex) ? `${hex.slice(1)}1` : hex;\n};\n\n/**\n * Datadog 用十进制字符串传 64 位 id。构建目标是 es2015,不能依赖 BigInt,\n * 这里用逐位进位的方式把十进制换算成 16 位 hex。\n */\nconst decimalToHexId = (value: string) => {\n if (!DECIMAL_PATTERN.test(value)) return undefined;\n const nibbles: number[] = [];\n for (let index = 0; index < value.length; index += 1) {\n let carry = value.charCodeAt(index) - 48;\n for (let position = 0; position < nibbles.length; position += 1) {\n const product = nibbles[position] * 10 + carry;\n nibbles[position] = product % 16;\n carry = (product - (product % 16)) / 16;\n }\n while (carry > 0) {\n nibbles.push(carry % 16);\n carry = (carry - (carry % 16)) / 16;\n }\n }\n if (nibbles.length > SPAN_ID_LENGTH) return undefined;\n let hex = '';\n for (let position = nibbles.length - 1; position >= 0; position -= 1) hex += HEX_DIGITS[nibbles[position]];\n return hex.padStart(SPAN_ID_LENGTH, '0');\n};\n\ninterface UpstreamTrace {\n format: BkOTUpstreamTraceFormat;\n /** 命中格式的原始 header,原样写入 Link Attributes */\n headers: Attributes;\n sampled?: boolean;\n /** 规范化后的合法 OTel id;无法映射时两者同时缺失 */\n spanId?: string;\n traceId?: string;\n}\n\n/** 按 header 原名收集实际存在的取值,缺失的不写入 */\nconst collectHeaders = (getHeader: BkOTHeaderReader, names: string[]) => {\n const headers: Attributes = {};\n for (const name of names) {\n const value = getHeader(name)?.trim();\n if (value) headers[name] = value;\n }\n return headers;\n};\n\n/**\n * `traceparent` 与 SDK 注入的是同一个 header,因此格式非法时视作没有既有链路:\n * 这种值后端同样无法接续,覆盖注入比保留它更有价值。\n */\nconst parseTraceparent = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader(TRACEPARENT_HEADER)?.trim().toLowerCase();\n if (!value) return undefined;\n const matched = TRACEPARENT_PATTERN.exec(value);\n if (!matched || matched[1] === 'ff') return undefined;\n const [, , traceId, spanId, flags] = matched;\n if (!isValidHexId(traceId, TRACE_ID_LENGTH) || !isValidHexId(spanId, SPAN_ID_LENGTH)) return undefined;\n return {\n format: 'traceparent',\n headers: collectHeaders(getHeader, [TRACEPARENT_HEADER]),\n sampled: (Number.parseInt(flags, 16) & 1) === 1,\n spanId,\n traceId,\n };\n};\n\nconst parseSentryTrace = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('sentry-trace')?.trim().toLowerCase();\n if (!value) return undefined;\n const headers = collectHeaders(getHeader, ['sentry-trace']);\n const matched = SENTRY_TRACE_PATTERN.exec(value);\n if (!matched) return { format: 'sentry-trace', headers };\n const [, traceId, spanId, sampled] = matched;\n return {\n format: 'sentry-trace',\n headers,\n ...(sampled === undefined ? {} : { sampled: sampled === '1' }),\n ...(isValidHexId(traceId, TRACE_ID_LENGTH) && isValidHexId(spanId, SPAN_ID_LENGTH) ? { spanId, traceId } : {}),\n };\n};\n\n// b3 单头:{trace-id}-{span-id}[-{sampling-state}[-{parent-span-id}]],也允许只有 0 / 1 / d\nconst parseB3Single = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('b3')?.trim().toLowerCase();\n if (!value) return undefined;\n const [rawTraceId, rawSpanId, samplingState] = value.split('-');\n const traceId = rawTraceId ? normalizeTraceId(rawTraceId) : undefined;\n const spanId = rawSpanId ? normalizeSpanId(rawSpanId) : undefined;\n return {\n format: 'b3',\n headers: collectHeaders(getHeader, ['b3']),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingState ? { sampled: samplingState === '1' || samplingState === 'd' } : {}),\n };\n};\n\nconst parseB3Multi = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const rawTraceId = getHeader('x-b3-traceid')?.trim().toLowerCase();\n const rawSpanId = getHeader('x-b3-spanid')?.trim().toLowerCase();\n const samplingState = getHeader('x-b3-sampled')?.trim().toLowerCase();\n if (!rawTraceId && !rawSpanId && !samplingState) return undefined;\n const traceId = rawTraceId ? normalizeTraceId(rawTraceId) : undefined;\n const spanId = rawSpanId ? normalizeSpanId(rawSpanId) : undefined;\n return {\n format: 'b3-multi',\n headers: collectHeaders(getHeader, [\n 'x-b3-traceid',\n 'x-b3-spanid',\n 'x-b3-parentspanid',\n 'x-b3-sampled',\n 'x-b3-flags',\n ]),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingState ? { sampled: samplingState === '1' || samplingState === 'd' } : {}),\n };\n};\n\nconst parseDatadog = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const rawTraceId = getHeader('x-datadog-trace-id')?.trim();\n const rawSpanId = getHeader('x-datadog-parent-id')?.trim();\n const samplingPriority = getHeader('x-datadog-sampling-priority')?.trim();\n if (!rawTraceId && !rawSpanId && !samplingPriority) return undefined;\n const tags = getHeader('x-datadog-tags')?.trim().toLowerCase();\n const traceIdHigh =\n (tags ? DATADOG_TRACE_ID_HIGH_PATTERN.exec(tags)?.[1] : undefined) ?? DATADOG_TRACE_ID_HIGH_DEFAULT;\n const traceIdLow = rawTraceId ? decimalToHexId(rawTraceId) : undefined;\n const traceId = traceIdLow ? normalizeTraceId(`${traceIdHigh}${traceIdLow}`) : undefined;\n const spanIdHex = rawSpanId ? decimalToHexId(rawSpanId) : undefined;\n const spanId = spanIdHex ? normalizeSpanId(spanIdHex) : undefined;\n return {\n format: 'datadog',\n headers: collectHeaders(getHeader, [\n 'x-datadog-trace-id',\n 'x-datadog-parent-id',\n 'x-datadog-sampling-priority',\n 'x-datadog-origin',\n 'x-datadog-tags',\n ]),\n ...(traceId && spanId ? { spanId, traceId } : {}),\n ...(samplingPriority ? { sampled: Number(samplingPriority) > 0 } : {}),\n };\n};\n\n/**\n * sw8:{sample}-{trace-id}-{segment-id}-{span-id}-{service}-{instance}-{endpoint}-{peer},\n * 其中 trace-id 与 segment-id 是 base64 后的 SkyWalking id。它们不是 16 字节 hex,无法映射为\n * 合法的 OTel SpanContext,因此不产出 traceId/spanId,由调用方补占位 id。\n */\nconst parseSw8 = (getHeader: BkOTHeaderReader): undefined | UpstreamTrace => {\n const value = getHeader('sw8')?.trim();\n if (!value) return undefined;\n const samplingState = value.split('-')[0];\n return {\n format: 'sw8',\n headers: collectHeaders(getHeader, ['sw8']),\n ...(samplingState ? { sampled: samplingState === '1' } : {}),\n };\n};\n\n// 按格式明确度排序:先看与 SDK 同名的 traceparent,再看其他厂商格式\nconst UPSTREAM_PARSERS = [parseTraceparent, parseSentryTrace, parseB3Single, parseB3Multi, parseDatadog, parseSw8];\n\nexport const detectUpstreamTrace = (getHeader: BkOTHeaderReader) => {\n for (const parse of UPSTREAM_PARSERS) {\n const upstream = parse(getHeader);\n if (upstream) return upstream;\n }\n return undefined;\n};\n\nconst toLink = (traceId: string, spanId: string, attributes: Attributes, sampled?: boolean): Link => ({\n attributes,\n context: {\n spanId,\n traceFlags: sampled === false ? TraceFlags.NONE : TraceFlags.SAMPLED,\n traceId,\n },\n});\n\nexport const planRequestTrace = (getHeader: BkOTHeaderReader): BkOTRequestTracePlan => {\n const upstream = detectUpstreamTrace(getHeader);\n if (!upstream) {\n // 与 RUM Span 无关的独立 trace,保证后端按 trace-id 检索时只命中这一个 API 请求\n const traceId = createRandomHexId(TRACE_ID_LENGTH);\n const spanId = createRandomHexId(SPAN_ID_LENGTH);\n const traceparent = `00-${traceId}-${spanId}-01`;\n return {\n links: [toLink(traceId, spanId, { format: 'traceparent', injected: true, [TRACEPARENT_HEADER]: traceparent })],\n traceparent,\n };\n }\n // sw8 等格式解析不出合法 OTel id,补占位 id 让 Link 结构统一;此时不注入,后端不会有对应 trace\n return {\n links: [\n toLink(\n upstream.traceId ?? createRandomHexId(TRACE_ID_LENGTH),\n upstream.spanId ?? createRandomHexId(SPAN_ID_LENGTH),\n // injected 恒定写出:业务自带的合法 traceparent 与 SDK 注入的 format 同值,只靠 format 无法区分\n { format: upstream.format, injected: false, ...upstream.headers },\n upstream.sampled,\n ),\n ],\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SpanKind } from '@opentelemetry/api';\n\nimport { notifyRequestTiming } from '../core/request-timing';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * Resource Timing 的统一消费端:静态资源按配置生成 span;Fetch/XHR timing 转交 request 插件,\n * 与其已创建的请求 span 合并,避免同一次网络请求重复计数。\n */\ntype ResourceTimingWithStatus = PerformanceResourceTiming & {\n deliveryType?: string;\n renderBlockingStatus?: string;\n responseStatus?: number;\n};\n\nconst getCacheHit = (entry: ResourceTimingWithStatus) =>\n entry.deliveryType === 'cache' || (entry.transferSize === 0 && entry.decodedBodySize > 0) ? true : undefined;\n\nconst hasUnavailableResourceDetails = (entry: ResourceTimingWithStatus) =>\n entry.transferSize === 0 &&\n entry.encodedBodySize === 0 &&\n entry.decodedBodySize === 0 &&\n entry.requestStart === 0 &&\n entry.responseStart === 0 &&\n !entry.nextHopProtocol;\n\nconst areInOrder = (...values: number[]) => values.every((value, index) => index === 0 || values[index - 1] <= value);\n\nconst setTiming = (attributes: Record<string, number>, name: string, origin: number, start: number, end: number) => {\n if (!areInOrder(origin, start, end)) return;\n attributes[`resource.${name}.start`] = start - origin;\n attributes[`resource.${name}.duration`] = end - start;\n};\n\nexport const getResourceTimingAttributes = (entry: ResourceTimingWithStatus) => {\n const attributes: Record<string, number> = {};\n const commonTimings = [\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 // 浏览器因 TAO 或实现差异可能返回缺失/乱序时间点;整组校验可避免产生负 duration。\n if (!commonTimings.every(Number.isFinite) || !areInOrder(...commonTimings)) return attributes;\n\n setTiming(attributes, 'first_byte', entry.startTime, entry.requestStart, entry.responseStart);\n setTiming(attributes, 'download', entry.startTime, entry.responseStart, entry.responseEnd);\n if (entry.workerStart > 0 && entry.workerStart < entry.fetchStart) {\n setTiming(attributes, 'worker', entry.startTime, entry.workerStart, entry.fetchStart);\n }\n if (entry.fetchStart < entry.domainLookupEnd) {\n setTiming(attributes, 'dns', entry.startTime, entry.domainLookupStart, entry.domainLookupEnd);\n }\n if (entry.fetchStart < entry.connectEnd) {\n setTiming(attributes, 'connect', entry.startTime, entry.connectStart, entry.connectEnd);\n if (entry.connectStart <= entry.secureConnectionStart && entry.secureConnectionStart <= entry.connectEnd) {\n setTiming(attributes, 'ssl', entry.startTime, entry.secureConnectionStart, entry.connectEnd);\n }\n }\n if (\n entry.redirectEnd > entry.startTime &&\n areInOrder(entry.startTime, entry.redirectStart, entry.redirectEnd, entry.fetchStart)\n ) {\n setTiming(attributes, 'redirect', entry.startTime, entry.redirectStart, entry.redirectEnd);\n }\n return attributes;\n};\n\nexport const getResourceDuration = (entry: ResourceTimingWithStatus) =>\n entry.duration === 0 && entry.responseEnd > entry.startTime ? entry.responseEnd - entry.startTime : entry.duration;\n\nexport const getResourcePerformanceAttributes = (entry: ResourceTimingWithStatus) => {\n const detailsUnavailable = hasUnavailableResourceDetails(entry);\n const deliveryType = typeof entry.deliveryType === 'string' && entry.deliveryType ? entry.deliveryType : undefined;\n return {\n [RUM_ATTRIBUTES.cacheHit]: getCacheHit(entry),\n ...(entry.nextHopProtocol ? { [RUM_ATTRIBUTES.nextHopProtocol]: entry.nextHopProtocol } : {}),\n ...getResourceTimingAttributes(entry),\n ...(deliveryType ? { 'resource.delivery_type': deliveryType } : {}),\n ...(entry.renderBlockingStatus ? { 'resource.render_blocking_status': entry.renderBlockingStatus } : {}),\n ...(detailsUnavailable\n ? {}\n : {\n // 对齐 Datadog:resource.size 取解压后 body 体积,缓存命中也能反映真实大小;\n // 网络传输量单独用 resource.transfer_size 表达(受缓存/TAO 影响可能为 0)。\n 'resource.size': entry.decodedBodySize,\n 'resource.encoded_body_size': entry.encodedBodySize,\n 'resource.decoded_body_size': entry.decodedBodySize,\n 'resource.transfer_size': entry.transferSize,\n }),\n };\n};\n\nexport const createResourcePlugin = (emitResourceSpans = true): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'resource',\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n if (!PerformanceObserver.supportedEntryTypes?.includes('resource')) {\n return;\n }\n\n const emitEntry = (entry: ResourceTimingWithStatus) => {\n if (!context.isSampled()) return;\n if (!entry.name || shouldIgnoreUrl(context.config, entry.name)) {\n return;\n }\n if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {\n // Fetch/XHR 已由 request 插件建 span;这里只把 ResourceTiming 回传补齐网络阶段,避免重复上报。\n notifyRequestTiming(entry);\n return;\n }\n if (entry.duration < 0) return;\n const redactedUrl = context.config.hooks.redactUrl(entry.name);\n const duration = getResourceDuration(entry);\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + duration;\n if (emitResourceSpans && context.isSampled()) {\n const span = context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: entry.initiatorType || 'other',\n outcome:\n typeof entry.responseStatus === 'number'\n ? { statusCode: entry.responseStatus, type: 'response' }\n : undefined,\n url: redactedUrl,\n }),\n 'url.full': redactedUrl,\n ...getResourcePerformanceAttributes(entry),\n },\n { kind: SpanKind.CLIENT, startTime },\n );\n span.end(endTime);\n }\n context.notifyActivity({ endTime, startTime, type: 'resource', url: entry.name });\n };\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as ResourceTimingWithStatus[]) {\n emitEntry(entry);\n }\n });\n observer.observe({ type: 'resource', buffered: true });\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/**\n * Fetch/XHR 请求统一采集入口。\n *\n * 同一层 patch 负责请求 span、Trace Header、页面活动与 beforeSend,避免多套 patch\n * 重复计数,也不依赖 OpenTelemetry instrumentation 的私有方法。\n */\n\nimport { SpanKind, SpanStatusCode } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { markSpanDiscarded } from '../core/processor';\nimport { trackRequestTiming } from '../core/request-timing';\nimport { planRequestTrace, TRACEPARENT_HEADER } from '../core/trace-context';\nimport {\n matchesUrlList,\n shouldIgnoreRequest,\n shouldIgnoreUrl,\n shouldPropagateTraceHeader,\n toAbsoluteUrl,\n} from '../core/url';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\nimport { getResourceDuration, getResourcePerformanceAttributes } from './resource';\n\nimport type { BkOTRequestConfig, BkOTRequestContext, BkOTRequestOutcome } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { BkOTHeaderReader, BkOTRequestTracePlan } from '../core/trace-context';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface RequestDetails {\n method?: string;\n startedAt?: number;\n url?: string;\n}\n\nconst getRequestOptions = (context: BkOTRuntimeContext) =>\n typeof context.config.tracking.request === 'object' ? context.config.tracking.request : undefined;\n\n// 没有白名单时不会注入任何 header,也就不需要为探测既有 trace header 付出 patch 成本。\nconst hasTracingAllowlist = (context: BkOTRuntimeContext) => {\n const allowed = getRequestOptions(context)?.allowedTracingUrls;\n return Array.isArray(allowed) ? allowed.length > 0 : allowed !== undefined;\n};\n\nconst getErrorName = (error: unknown) =>\n typeof error === 'object' && error && 'name' in error && typeof error.name === 'string' ? error.name : '';\n\nconst getFetchFailureOutcome = (requestContext: Request | RequestInit, error: unknown): BkOTRequestOutcome => {\n const signal = requestContext.signal;\n if (getErrorName(signal?.reason) === 'TimeoutError' || getErrorName(error) === 'TimeoutError') {\n return { error, type: 'timeout' };\n }\n if (signal?.aborted || getErrorName(error) === 'AbortError') return { type: 'abort' };\n return { error, type: 'network_error' };\n};\n\nconst getXhrOutcome = (\n completionType: 'abort' | 'error' | 'load' | 'timeout',\n xhr: XMLHttpRequest,\n): BkOTRequestOutcome => {\n if (completionType === 'abort') return { type: 'abort' };\n if (completionType === 'timeout') return { type: 'timeout' };\n if (completionType === 'error' || xhr.status === 0) return { type: 'network_error' };\n return { statusCode: xhr.status, type: 'response' };\n};\n\nconst isErrorOutcome = (outcome: BkOTRequestOutcome) =>\n outcome.type === 'network_error' ||\n outcome.type === 'timeout' ||\n (outcome.type === 'response' && typeof outcome.statusCode === 'number' && outcome.statusCode >= 400);\n\nconst getFetchUrl = (input: RequestInfo | URL) => {\n if (typeof input === 'string' || input instanceof URL) return toAbsoluteUrl(String(input));\n return toAbsoluteUrl(input.url);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) =>\n (init?.method ?? (typeof Request !== 'undefined' && input instanceof Request ? input.method : 'GET')).toUpperCase();\n\nconst getFetchRequestContext = (input: RequestInfo | URL, init?: RequestInit): Request | RequestInit =>\n typeof Request !== 'undefined' && input instanceof Request ? input : (init ?? {});\n\n/** 取本次请求最终会发出的 header 集合:init.headers 一旦存在就会整体覆盖 Request 上的 header。 */\nconst getFetchRequestHeaders = (input: RequestInfo | URL, init: RequestInit | undefined) => {\n const requestHeaders = typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined;\n return new Headers(init?.headers ?? requestHeaders);\n};\n\nconst applyFetchTracePlan = (init: RequestInit | undefined, headers: Headers, plan: BkOTRequestTracePlan) => {\n if (!plan.traceparent) return init;\n headers.set(TRACEPARENT_HEADER, plan.traceparent);\n return { ...init, headers };\n};\n\nconst createHeaderReader =\n (headers: Record<string, string> | undefined): BkOTHeaderReader =>\n name =>\n headers?.[name.toLowerCase()];\n\nconst isActivityExcluded = (context: BkOTRuntimeContext, url: string) =>\n shouldIgnoreUrl(context.config, url) || matchesUrlList(context.config.view.excludedActivityUrls, url);\n\nconst startRequestActivity = (context: BkOTRuntimeContext, url: string) => {\n if (isActivityExcluded(context, url)) return () => {};\n const id = createRandomId('request');\n context.notifyActivity({ id, time: Date.now(), type: 'request_start', url });\n let ended = false;\n return () => {\n if (ended) return;\n ended = true;\n context.notifyActivity({ id, time: Date.now(), type: 'request_end' });\n };\n};\n\nconst trackRequestSpanTiming = (context: BkOTRuntimeContext, url: string) =>\n !context.isSampled()\n ? {\n cancel() {},\n complete(callback: (entry?: PerformanceResourceTiming) => void) {\n callback();\n },\n }\n : trackRequestTiming(url);\n\nconst startRequestSpan = (\n context: BkOTRuntimeContext,\n subtype: 'fetch' | 'xhr',\n details: RequestDetails,\n tracePlan?: BkOTRequestTracePlan,\n) => {\n const startedAt = details.startedAt ?? Date.now();\n return context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method: details.method,\n url: details.url ?? '',\n }),\n ...(details.url\n ? {\n 'url.full': context.config.hooks.redactUrl(details.url),\n 'url.template': context.config.hooks.getRequestUrlTemplate(details.url),\n }\n : {}),\n },\n {\n kind: SpanKind.CLIENT,\n startTime: startedAt,\n ...(tracePlan ? { links: tracePlan.links } : {}),\n },\n );\n};\n\nexport const decorateRequestSpan = (\n context: BkOTRuntimeContext,\n span: Span,\n subtype: 'fetch' | 'xhr',\n domainContext: BkOTRequestContext,\n outcome: BkOTRequestOutcome,\n details: RequestDetails = {},\n) => {\n const sourceUrl = details.url;\n const redactedUrl = sourceUrl ? context.config.hooks.redactUrl(sourceUrl) : undefined;\n const method = details.method;\n const attributes: Attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method,\n outcome,\n url: redactedUrl ?? '',\n }),\n ...(redactedUrl ? { 'url.full': redactedUrl } : {}),\n // 模板由原始 URL 推导:Hook 可能需要 query 才能判断路由,产出的模板本身是低基数的\n ...(sourceUrl ? { 'url.template': context.config.hooks.getRequestUrlTemplate(sourceUrl) } : {}),\n };\n\n const event = {\n attributes,\n resource: {\n method,\n outcome,\n transport: subtype,\n url: redactedUrl,\n },\n type: 'resource' as const,\n };\n let shouldSend: boolean | void = true;\n try {\n shouldSend = getRequestOptions(context)?.beforeSend?.(event, domainContext);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot] request.beforeSend failed:', error);\n }\n span.setAttributes(context.applyRedact(event.attributes));\n if (shouldSend === false) {\n // SpanProcessor 在 onEnd 统一识别内部状态,避免这里绕过正常的 span 结束流程。\n markSpanDiscarded(span);\n return;\n }\n if (isErrorOutcome(outcome)) span.setStatus({ code: SpanStatusCode.ERROR });\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTRequestConfig | boolean | undefined): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeRequestEnds = new Set<() => void>();\n\n return {\n name: 'request',\n enabled: option !== false,\n init(context) {\n if (typeof window === 'undefined') return;\n\n if (typeof window.fetch === 'function') {\n const originalFetch = window.fetch;\n const patchedFetch: typeof window.fetch = async function (this: Window, input, init) {\n const url = getFetchUrl(input);\n if (!context.isSampled() || shouldIgnoreRequest(context.config, url))\n return originalFetch.call(this, input, init);\n const method = getFetchMethod(input, init);\n const details = { method, startedAt: Date.now(), url };\n // Span Link 必须在创建 span 时给出,因此先规划 trace context 再建 span。\n const headers = shouldPropagateTraceHeader(context.config, url)\n ? getFetchRequestHeaders(input, init)\n : undefined;\n const tracePlan = headers ? planRequestTrace(name => headers.get(name) ?? undefined) : undefined;\n const span = startRequestSpan(context, 'fetch', details, tracePlan);\n const timing = trackRequestSpanTiming(context, url);\n const endActivity = startRequestActivity(context, url);\n const requestContext = getFetchRequestContext(input, init);\n const requestInit = headers && tracePlan ? applyFetchTracePlan(init, headers, tracePlan) : init;\n let ended = false;\n const endSpan = (entry?: PerformanceResourceTiming, fallbackEndTime = Date.now()) => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) {\n span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end(performance.timeOrigin + entry.startTime + getResourceDuration(entry));\n return;\n }\n span.end(fallbackEndTime);\n };\n const forceEnd = () => {\n // shutdown 不再等待异步 ResourceTiming,立即结束仍在飞行中的 span。\n timing.cancel();\n endSpan();\n };\n activeRequestEnds.add(forceEnd);\n try {\n const response = await originalFetch.call(this, input, requestInit);\n const outcome: BkOTRequestOutcome = {\n ...(response.status > 0 ? { statusCode: response.status } : {}),\n type: 'response',\n };\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { request: requestContext, response, type: 'fetch' },\n outcome,\n details,\n );\n const completedAt = Date.now();\n timing.complete(entry => endSpan(entry, completedAt));\n return response;\n } catch (error) {\n const outcome = getFetchFailureOutcome(requestContext, error);\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { error, request: requestContext, type: 'fetch' },\n outcome,\n details,\n );\n if (outcome.type !== 'abort' && error instanceof Error) span.recordException(error);\n const completedAt = Date.now();\n timing.complete(entry => endSpan(entry, completedAt));\n throw error;\n } finally {\n endActivity();\n }\n };\n window.fetch = patchedFetch;\n teardownCallbacks.push(() => {\n if (window.fetch === patchedFetch) window.fetch = originalFetch;\n });\n }\n\n if (typeof XMLHttpRequest !== 'undefined') {\n const requests = new WeakMap<XMLHttpRequest, RequestDetails>();\n // 浏览器不提供读取 XHR 请求头的 API,只能自己快照业务设置的 header 以便探测既有链路。\n const authorHeaders = new WeakMap<XMLHttpRequest, Record<string, string>>();\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const patchedOpen: typeof XMLHttpRequest.prototype.open = function (\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n async = true,\n username?: null | string,\n password?: null | string,\n ) {\n requests.set(this, { method: method.toUpperCase(), url: toAbsoluteUrl(String(url)) });\n // open() 会清空已设置的请求头,快照必须同步失效。\n authorHeaders.delete(this);\n return originalOpen.call(this, method, String(url), async, username, password);\n };\n const patchedSend: typeof XMLHttpRequest.prototype.send = function (this: XMLHttpRequest, body) {\n const details = requests.get(this) ?? {};\n if (!context.isSampled() || !details.url || shouldIgnoreRequest(context.config, details.url))\n return originalSend.call(this, body);\n details.startedAt = Date.now();\n const tracePlan = shouldPropagateTraceHeader(context.config, details.url)\n ? planRequestTrace(createHeaderReader(authorHeaders.get(this)))\n : undefined;\n const span = startRequestSpan(context, 'xhr', details, tracePlan);\n const timing = trackRequestSpanTiming(context, details.url);\n const endActivity = startRequestActivity(context, details.url);\n if (tracePlan?.traceparent) this.setRequestHeader(TRACEPARENT_HEADER, tracePlan.traceparent);\n let finishing = false;\n let ended = false;\n let removeTerminalListeners = () => {};\n const forceEnd = () => {\n // XHR 可能永不触发终止事件;SDK 停止时必须清理活动计数并结束 span。\n removeTerminalListeners();\n timing.cancel();\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n endActivity();\n span.end();\n };\n activeRequestEnds.add(forceEnd);\n const complete = (completionType: 'abort' | 'error' | 'load' | 'timeout') => {\n if (finishing || ended) return;\n finishing = true;\n removeTerminalListeners();\n const outcome = getXhrOutcome(completionType, this);\n decorateRequestSpan(context, span, 'xhr', { type: 'xhr', xhr: this }, outcome, details);\n endActivity();\n const completedAt = Date.now();\n timing.complete(entry => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) {\n span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end(performance.timeOrigin + entry.startTime + getResourceDuration(entry));\n return;\n }\n span.end(completedAt);\n });\n };\n const onAbort = () => complete('abort');\n const onError = () => complete('error');\n const onLoad = () => complete('load');\n const onTimeout = () => complete('timeout');\n removeTerminalListeners = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onError);\n this.removeEventListener('load', onLoad);\n this.removeEventListener('timeout', onTimeout);\n };\n this.addEventListener('abort', onAbort, { once: true });\n this.addEventListener('error', onError, { once: true });\n this.addEventListener('load', onLoad, { once: true });\n this.addEventListener('timeout', onTimeout, { once: true });\n try {\n return originalSend.call(this, body);\n } catch (error) {\n if (error instanceof Error) span.recordException(error);\n complete('error');\n throw error;\n }\n };\n XMLHttpRequest.prototype.open = patchedOpen;\n XMLHttpRequest.prototype.send = patchedSend;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.open === patchedOpen) XMLHttpRequest.prototype.open = originalOpen;\n if (XMLHttpRequest.prototype.send === patchedSend) XMLHttpRequest.prototype.send = originalSend;\n });\n\n if (hasTracingAllowlist(context) && typeof XMLHttpRequest.prototype.setRequestHeader === 'function') {\n const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n const patchedSetRequestHeader: typeof XMLHttpRequest.prototype.setRequestHeader = function (\n this: XMLHttpRequest,\n name: string,\n value: string,\n ) {\n const key = name.toLowerCase();\n const headers = authorHeaders.get(this) ?? {};\n // 浏览器对重复设置的同名 header 做 \", \" 合并,快照保持一致才能正确解析。\n headers[key] = key in headers ? `${headers[key]}, ${value}` : value;\n authorHeaders.set(this, headers);\n return originalSetRequestHeader.call(this, name, value);\n };\n XMLHttpRequest.prototype.setRequestHeader = patchedSetRequestHeader;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.setRequestHeader === patchedSetRequestHeader)\n XMLHttpRequest.prototype.setRequestHeader = originalSetRequestHeader;\n });\n }\n }\n },\n shutdown() {\n for (const end of [...activeRequestEnds]) end();\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n// blockedURI 除了 URL 还可能是 'inline' / 'eval' / 'wasm-eval' / 'self' 等关键字(CSP spec),\n// 这些不是 URL,不应该走 redactUrl\nconst URL_LIKE_BLOCKED_URI = /^https?:|^\\/\\//i;\n\nconst redactBlockedUri = (redactUrl: (url: string) => string, blockedURI: string | undefined) => {\n if (!blockedURI) return undefined;\n return URL_LIKE_BLOCKED_URI.test(blockedURI) ? redactUrl(blockedURI) : blockedURI;\n};\n\n/**\n * CSP 违规事件采集插件,通过监听 `securitypolicyviolation` 事件捕获被 CSP 策略拦截的脚本/资源,\n * 以 error span 上报,进入统一错误分析口径。\n *\n * URL 处理:\n * - `blockedURI` / `sourceFile` 中的 URL 类值会经 `redactUrl` 脱敏。\n * - `blockedURI` 是 'inline' / 'eval' 等 CSP 关键字时直接保留,不调用 redactUrl。\n */\nexport const createCspViolationPlugin = (): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n if (!context.isSampled()) return;\n const occurredAt = Date.now();\n const blockedUri = redactBlockedUri(context.config.hooks.redactUrl, event.blockedURI);\n const sourceFile = event.sourceFile ? context.config.hooks.redactUrl(event.sourceFile) : undefined;\n\n const span = context.startSpan(\n 'csp.violation',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n outcomeReason: 'csp',\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'csp.blocked_uri': blockedUri,\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.disposition': event.disposition,\n 'csp.source_file': sourceFile,\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n 'csp.original_policy': event.originalPolicy,\n 'error.source': 'csp',\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n document.addEventListener('securitypolicyviolation', handler);\n teardown = () => document.removeEventListener('securitypolicyviolation', handler);\n },\n shutdown() {\n teardown?.();\n teardown = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 浏览器错误采集插件,负责捕获 JS 运行时错误、未处理的 Promise rejection、资源加载失败三类错误。\n *\n * 上报策略:\n * - 错误分析主数据统一使用 `span_type=error` 的 error span。\n * - 每个浏览器错误只生成一条原始 error Span,错误分组和风暴控制由服务端完成。\n *\n * 关键 attribute:\n * - `error.source`:错误来源,取值 `window.error` / `unhandledrejection` / `resource`。\n * - `error.cross_origin`:仅在跨域脚本错误(\"Script error.\")时为 true,便于过滤这类无 stack 噪声。\n * - 资源错误额外带 `url.full`(经 redactUrl 脱敏)与 `html.tag`。\n * - JS 错误额外带 `code.filepath` / `code.lineno` / `code.column`。\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\n\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst getErrorMessage = (value: unknown) => {\n if (value instanceof Error) {\n return value.message;\n }\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n};\n\ninterface EmitErrorOptions {\n context: BkOTRuntimeContext;\n error: unknown;\n exceptionType?: string;\n extra?: Record<string, unknown>;\n source: string;\n spanName: string;\n}\n\nconst emitError = ({ context, error, exceptionType, extra = {}, source, spanName }: EmitErrorOptions) => {\n if (!context.isSampled()) return;\n const occurredAt = Date.now();\n const message = getErrorMessage(error);\n context.notifyActivity({ time: occurredAt, type: 'error' });\n\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...context.config.hooks.getErrorAttributes(),\n ...buildRumSpanAttributes({\n outcome: RUM_SPAN_OUTCOME.error,\n spanType: RUM_SPAN_TYPE.error,\n }),\n 'error.message': message,\n 'error.handled': false,\n 'error.source': source,\n ...extra,\n };\n // 错误分析统一沉淀为 error span;若当前存在 active context,OTel 会自动建立父子关系。\n const errorSpan = context.startSpan(spanName, attributes, { startTime: occurredAt });\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.end();\n};\n\nexport const createErrorPlugin = (): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n\n return {\n name: 'error',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const onError = (event: ErrorEvent) => {\n // 跨域脚本错误浏览器会屏蔽 stack/filename,message 通常是 \"Script error.\"\n const isCrossOrigin = !event.error && !event.filename && event.message === 'Script error.';\n emitError({\n context,\n spanName: 'browser.error',\n source: 'window.error',\n error: event.error ?? event.message,\n extra: {\n 'code.filepath': event.filename,\n 'code.lineno': event.lineno,\n 'code.column': event.colno,\n ...(isCrossOrigin ? { 'error.cross_origin': true } : {}),\n },\n });\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n emitError({\n context,\n spanName: 'browser.unhandledrejection',\n source: 'unhandledrejection',\n error: event.reason,\n exceptionType: event.reason instanceof Error ? event.reason.name : 'UnhandledRejection',\n });\n };\n const onResourceError = (event: Event) => {\n // JS 运行时错误是 ErrorEvent,已由 bubble 阶段的 onError 处理;这里只关心资源加载失败\n if (event instanceof ErrorEvent) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const elementWithUrl = target as Element & { href?: string; src?: string };\n const resourceUrl = elementWithUrl.src || elementWithUrl.href || (target as HTMLMediaElement).currentSrc || '';\n emitError({\n context,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName.toLowerCase()} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.hooks.redactUrl(resourceUrl),\n 'html.tag': target.tagName,\n },\n });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('error', onResourceError, true);\n listeners.push(\n () => window.removeEventListener('error', onError),\n () => window.removeEventListener('unhandledrejection', onUnhandledRejection),\n () => window.removeEventListener('error', onResourceError, true),\n );\n },\n shutdown() {\n while (listeners.length) {\n listeners.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 主线程阻塞优先使用 Long Animation Frame API 获取脚本归因,不支持时回退到 Long Tasks API;\n * span 保留单次诊断详情,分布与计数由服务端从原始事件计算。\n */\nexport interface BkOTLongTaskPluginOptions {\n /** 采集阈值,默认 50ms。Performance API 本身通常只产生大于 50ms 的条目。 */\n threshold?: number;\n}\n\ninterface LegacyLongTaskEntry extends PerformanceEntry {\n attribution?: Array<{\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n }>;\n}\n\ninterface LongAnimationFrameEntry extends PerformanceEntry {\n blockingDuration: number;\n firstUIEventTimestamp: number;\n renderStart: number;\n scripts: ScriptTiming[];\n styleAndLayoutStart: number;\n}\n\ninterface ScriptTiming {\n duration: number;\n executionStart: number;\n forcedStyleAndLayoutDuration: number;\n invoker: string;\n invokerType: string;\n pauseDuration: number;\n sourceCharPosition: number;\n sourceFunctionName: string;\n sourceURL: string;\n startTime: number;\n windowAttribution: string;\n}\n\nconst DEFAULT_THRESHOLD = 50;\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst finiteNumber = (value: number | undefined) => (Number.isFinite(value) ? value : undefined);\n\nconst getLegacyAttributionAttributes = (\n attribution: NonNullable<LegacyLongTaskEntry['attribution']>[number],\n redactUrl: (url: string) => string,\n): Attributes => ({\n ...(attribution.containerId ? { 'long_task.attribution.container_id': attribution.containerId } : {}),\n ...(attribution.containerName ? { 'long_task.attribution.container_name': attribution.containerName } : {}),\n ...(attribution.containerSrc ? { 'long_task.attribution.container_src': redactUrl(attribution.containerSrc) } : {}),\n ...(attribution.containerType ? { 'long_task.attribution.container_type': attribution.containerType } : {}),\n ...(attribution.name ? { 'long_task.attribution.name': attribution.name } : {}),\n});\n\nconst getScriptAttributes = (script: ScriptTiming, redactUrl: (url: string) => string): Attributes => ({\n 'long_task.script.duration': script.duration,\n 'long_task.script.execution_start': script.executionStart,\n 'long_task.script.forced_style_and_layout_duration': script.forcedStyleAndLayoutDuration,\n 'long_task.script.invoker': script.invoker,\n 'long_task.script.invoker_type': script.invokerType,\n 'long_task.script.pause_duration': script.pauseDuration,\n 'long_task.script.source_char_position': script.sourceCharPosition,\n 'long_task.script.source_function_name': script.sourceFunctionName,\n 'long_task.script.source_url': script.sourceURL ? redactUrl(script.sourceURL) : '',\n 'long_task.script.start_time': script.startTime,\n 'long_task.script.window_attribution': script.windowAttribution,\n});\n\nexport const createLongTaskPlugin = (options: BkOTLongTaskPluginOptions = {}): BkOTPlugin => {\n const threshold = nonNegativeNumber(options.threshold, DEFAULT_THRESHOLD);\n let observer: PerformanceObserver | undefined;\n let initialBatchTimer: ReturnType<typeof setTimeout> | undefined;\n let initialEntries: PerformanceEntry[] = [];\n\n return {\n name: 'long-task',\n init(context) {\n if (typeof PerformanceObserver === 'undefined' || typeof performance === 'undefined') return;\n const supportedEntryTypes = PerformanceObserver.supportedEntryTypes ?? [];\n // LoAF 提供脚本归因与 blockingDuration,支持时优先使用;旧浏览器回退到 longtask。\n const entryType = supportedEntryTypes.includes('long-animation-frame')\n ? 'long-animation-frame'\n : supportedEntryTypes.includes('longtask')\n ? 'longtask'\n : undefined;\n if (!entryType) return;\n\n const emitEntry = (entry: LegacyLongTaskEntry | LongAnimationFrameEntry) => {\n if (!context.isSampled()) return;\n if (!Number.isFinite(entry.duration) || entry.duration < threshold) return;\n const isLoaf = entry.entryType === 'long-animation-frame';\n const loaf = isLoaf ? (entry as LongAnimationFrameEntry) : undefined;\n const scripts = loaf?.scripts ?? [];\n const id = createRandomId('long-task');\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + entry.duration;\n const entryTypeAttribute = isLoaf ? 'long-animation-frame' : 'long-task';\n const legacyAttributions = isLoaf ? [] : ((entry as LegacyLongTaskEntry).attribution ?? []);\n const span = context.startSpan(\n 'browser.long_task',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.longTask,\n }),\n ...(finiteNumber(loaf?.blockingDuration) !== undefined\n ? { [RUM_ATTRIBUTES.longTaskBlockingDuration]: loaf!.blockingDuration }\n : {}),\n [RUM_ATTRIBUTES.longTaskDuration]: entry.duration,\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n [RUM_ATTRIBUTES.longTaskId]: id,\n 'long_task.name': entry.name,\n ...(finiteNumber(loaf?.firstUIEventTimestamp) !== undefined\n ? { 'long_task.first_ui_event_timestamp': loaf!.firstUIEventTimestamp }\n : {}),\n ...(finiteNumber(loaf?.renderStart) !== undefined ? { 'long_task.render_start': loaf!.renderStart } : {}),\n ...(finiteNumber(loaf?.styleAndLayoutStart) !== undefined\n ? { 'long_task.style_and_layout_start': loaf!.styleAndLayoutStart }\n : {}),\n ...(loaf ? { [RUM_ATTRIBUTES.longTaskStartTime]: loaf.startTime } : {}),\n },\n { startTime },\n );\n scripts.forEach(script => {\n span.addEvent(\n 'long_task.script',\n context.applyRedact(getScriptAttributes(script, context.config.hooks.redactUrl)),\n performance.timeOrigin + script.startTime,\n );\n });\n legacyAttributions.forEach((attribution, index) => {\n span.addEvent(\n 'long_task.attribution',\n context.applyRedact({\n 'long_task.attribution.index': index,\n ...getLegacyAttributionAttributes(attribution, context.config.hooks.redactUrl),\n }),\n startTime,\n );\n });\n span.end(endTime);\n context.notifyActivity({ endTime, startTime, type: 'long_task' });\n };\n\n const emitEntries = (entries: PerformanceEntry[]) =>\n entries.forEach(entry => emitEntry(entry as LegacyLongTaskEntry));\n try {\n let initializing = true;\n observer = new PerformanceObserver(list => {\n const entries = list.getEntries();\n if (initializing) {\n // buffered 条目可能在 observe() 内同步回调,先合并到下一宏任务,等 Session/View 上下文就绪后再归属。\n initialEntries.push(...entries);\n if (!initialBatchTimer) {\n initialBatchTimer = setTimeout(() => {\n const entriesToEmit = initialEntries;\n initialEntries = [];\n initialBatchTimer = undefined;\n emitEntries(entriesToEmit);\n });\n }\n } else {\n emitEntries(entries);\n }\n });\n observer.observe({ type: entryType, buffered: true });\n initializing = false;\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n clearTimeout(initialBatchTimer);\n initialBatchTimer = undefined;\n initialEntries = [];\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport interface RouteChangeEvent {\n fromUrl: string;\n source: RouteChangeSource;\n toUrl: string;\n}\n\nexport type RouteChangeSource = 'hashchange' | 'popstate' | 'pushState' | 'replaceState';\n\ntype RouteChangeHandler = (event: RouteChangeEvent) => void;\n\nconst subscribers = new Set<RouteChangeHandler>();\n\n// History 只 patch 一次,多个插件/实例共用订阅集合;最后一个订阅者退出时恢复原方法。\nlet lastUrl = '';\nlet originalPushState: typeof history.pushState | undefined;\nlet originalReplaceState: typeof history.replaceState | undefined;\nlet patched = false;\n\nconst getCurrentUrl = () => (typeof location === 'undefined' ? '' : location.href);\n\nconst emitRouteChange = (source: RouteChangeSource, fromUrl: string) => {\n const toUrl = getCurrentUrl();\n lastUrl = toUrl;\n const event: RouteChangeEvent = { fromUrl, source, toUrl };\n\n for (const subscriber of Array.from(subscribers)) {\n subscriber(event);\n }\n};\n\nconst onPopState = () => {\n emitRouteChange('popstate', lastUrl || getCurrentUrl());\n};\n\nconst onHashChange = () => {\n emitRouteChange('hashchange', lastUrl || getCurrentUrl());\n};\n\nconst patchHistory = () => {\n if (patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n patched = true;\n lastUrl = getCurrentUrl();\n originalPushState = history.pushState;\n originalReplaceState = history.replaceState;\n\n history.pushState = function patchedPushState(this: History, data: any, title: string, url?: null | string | URL) {\n const fromUrl = lastUrl || getCurrentUrl();\n // 先执行原方法,让订阅者读取到的 location 与浏览器最终状态一致。\n const result = originalPushState?.apply(this, [data, title, url]);\n emitRouteChange('pushState', fromUrl);\n return result;\n };\n history.replaceState = function patchedReplaceState(\n this: History,\n data: any,\n title: string,\n url?: null | string | URL,\n ) {\n const fromUrl = lastUrl || getCurrentUrl();\n const result = originalReplaceState?.apply(this, [data, title, url]);\n emitRouteChange('replaceState', fromUrl);\n return result;\n };\n window.addEventListener('popstate', onPopState);\n window.addEventListener('hashchange', onHashChange);\n};\n\nconst restoreHistory = () => {\n if (!patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n if (originalPushState) {\n history.pushState = originalPushState;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n }\n window.removeEventListener('popstate', onPopState);\n window.removeEventListener('hashchange', onHashChange);\n originalPushState = undefined;\n originalReplaceState = undefined;\n patched = false;\n lastUrl = '';\n};\n\nexport const subscribeRouteChange = (handler: RouteChangeHandler) => {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n patchHistory();\n subscribers.add(handler);\n\n return () => {\n subscribers.delete(handler);\n if (subscribers.size === 0) {\n restoreHistory();\n }\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Page View 插件\n *\n * 自动模式监听初始加载、SPA 路由和 BFCache;手动模式仅由 startView() 划分 View。\n *\n * 上报形态:browser.view start/update/end 原始生命周期事件 span。\n *\n * 自动边界与 Datadog 对齐:pathname 或非锚点 hash path 改变才创建 View,query-only 变化不创建。\n *\n * 关键属性:\n * - view.url:当前页面 URL(经 redactUrl 脱敏)\n * - url.previous:跳转前的 URL(来自 route-observer 的 fromUrl,初始 load 为空字符串)\n * - document.referrer:仅 'load' 事件携带\n */\n\nimport { createRandomId } from '../core/id';\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { subscribeRouteChange } from '../core/route-observer';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { BkOTStartViewOptions, BkOTViewContext } from '../core/runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\n\ninterface ActiveView {\n attributes: Attributes;\n id: string;\n loadingTime?: number;\n loadingTimeSource?: 'auto' | 'manual';\n loadingType: ViewLoadingType;\n /** 仅业务显式命名时存在;未命名时由后端回退到 view.url_template 展示 */\n name?: string;\n rawUrl: string;\n snapshotAttributes: Attributes;\n startedAt: number;\n url: string;\n urlTemplate: string;\n version: number;\n stopLoadingTime?: () => void;\n}\n\ntype ViewLoadingType = NonNullable<BkOTViewContext['loadingType']>;\n\nconst PROTECTED_ATTRIBUTE_KEYS = new Set<string>(Object.values(RUM_ATTRIBUTES));\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nconst getNavigationTimingAttributes = (): Attributes => {\n if (typeof performance === 'undefined') return {};\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation) return {};\n const attributes: Attributes = {};\n const setTiming = (key: string, value: number) => {\n if (Number.isFinite(value) && value > 0) attributes[key] = value;\n };\n setTiming(RUM_ATTRIBUTES.viewFirstByte, navigation.responseStart);\n setTiming(RUM_ATTRIBUTES.viewDomInteractive, navigation.domInteractive);\n setTiming(RUM_ATTRIBUTES.viewDomContentLoaded, navigation.domContentLoadedEventEnd);\n setTiming(RUM_ATTRIBUTES.viewDomComplete, navigation.domComplete);\n setTiming(RUM_ATTRIBUTES.viewLoadEvent, navigation.loadEventEnd);\n return attributes;\n};\n\nconst getHashPath = (hash: string) => {\n const queryIndex = hash.indexOf('?');\n return queryIndex < 0 ? hash : hash.slice(0, queryIndex);\n};\n\nconst isHashAnchor = (hash: string) => {\n const id = hash.slice(1);\n if (!id || typeof document === 'undefined' || typeof document.getElementById !== 'function') return false;\n try {\n return Boolean(document.getElementById(decodeURIComponent(id)));\n } catch {\n return Boolean(document.getElementById(id));\n }\n};\n\nexport const isViewLocationChange = (fromUrl: string, toUrl: string) => {\n try {\n const base = typeof location === 'undefined' ? 'http://localhost' : location.href;\n const from = new URL(fromUrl, base);\n const to = new URL(toUrl, base);\n return from.pathname !== to.pathname || (!isHashAnchor(to.hash) && getHashPath(from.hash) !== getHashPath(to.hash));\n } catch {\n return fromUrl !== toUrl;\n }\n};\n\nexport const createPageViewPlugin = (): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n let activeView: ActiveView | undefined;\n let bfCacheView: Omit<ActiveView, 'stopLoadingTime'> | undefined;\n let renewalView: Omit<ActiveView, 'stopLoadingTime'> | undefined;\n\n const clearViewContext = (changedAt = Date.now()) => context.setView({}, changedAt);\n\n const emitViewEvent = (\n view: ActiveView,\n phase: 'end' | 'start' | 'update',\n timestamp = Date.now(),\n reason?: string,\n ) => {\n view.version += 1;\n const span = context.startSpan(\n 'browser.view',\n {\n ...context.config.hooks.getPageAttributes(),\n ...view.attributes,\n ...view.snapshotAttributes,\n [RUM_ATTRIBUTES.viewPhase]: phase,\n [RUM_ATTRIBUTES.viewStartedAt]: view.startedAt,\n [RUM_ATTRIBUTES.viewVersion]: view.version,\n ...(reason ? { 'view.end_reason': reason } : {}),\n ...(view.loadingTime === undefined\n ? {}\n : {\n [RUM_ATTRIBUTES.viewLoadingTime]: view.loadingTime,\n [RUM_ATTRIBUTES.viewLoadingTimeSource]: view.loadingTimeSource,\n }),\n },\n {\n contextTime: timestamp,\n startTime: timestamp,\n },\n );\n span.end(timestamp);\n };\n\n const endActiveView = (reason: string, clearContext = true, endedAt = Date.now()) => {\n if (!activeView) {\n if (clearContext) clearViewContext(endedAt);\n return undefined;\n }\n const snapshot = { ...activeView };\n activeView.stopLoadingTime?.();\n emitViewEvent(activeView, 'end', endedAt, reason);\n activeView = undefined;\n if (clearContext) clearViewContext(endedAt);\n const { stopLoadingTime: _stopLoadingTime, ...rest } = snapshot;\n return rest;\n };\n\n const setLoadingTime = (view: ActiveView, duration: number, source: 'auto' | 'manual') => {\n if (activeView !== view || !Number.isFinite(duration) || duration < 0) return;\n const navigationTimingAttributes = view.loadingType === 'initial_load' ? getNavigationTimingAttributes() : {};\n const updatedAttributes = {\n ...navigationTimingAttributes,\n [RUM_ATTRIBUTES.viewLoadingTime]: duration,\n [RUM_ATTRIBUTES.viewLoadingTimeSource]: source,\n };\n Object.assign(view.snapshotAttributes, updatedAttributes);\n view.loadingTime = duration;\n view.loadingTimeSource = source;\n emitViewEvent(view, 'update');\n };\n\n const startLoadingTimeTracking = (view: ActiveView) => {\n if (!context.config.view.loadingTime || document.visibilityState === 'hidden') return;\n let activityResolved = false;\n let activityCandidate: number | undefined;\n let loadResolved = view.loadingType !== 'initial_load';\n let loadCandidate: number | undefined;\n let loadListener: (() => void) | undefined;\n let hidden = false;\n let visibilityListener: (() => void) | undefined;\n\n const tryComplete = () => {\n if (hidden || !activityResolved || !loadResolved || activeView !== view) return;\n const candidates = [activityCandidate, loadCandidate].filter(\n (candidate): candidate is number => candidate !== undefined,\n );\n if (candidates.length) setLoadingTime(view, Math.max(...candidates), 'auto');\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n\n const readLoadEvent = () => {\n const navigation = performance.getEntriesByType?.('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation?.loadEventEnd) return false;\n loadCandidate = Math.max(0, performance.timeOrigin + navigation.loadEventEnd - view.startedAt);\n loadResolved = true;\n return true;\n };\n\n if (!loadResolved && !readLoadEvent()) {\n const onLoad = () => {\n setTimeout(() => {\n if (activeView !== view) return;\n readLoadEvent();\n loadResolved = true;\n tryComplete();\n });\n };\n window.addEventListener('load', onLoad, { once: true });\n loadListener = () => window.removeEventListener('load', onLoad);\n }\n\n const stopActivity = waitForPageActivityEnd(context, view.startedAt, result => {\n activityResolved = true;\n if (result.hadActivity) activityCandidate = Math.max(0, result.endTime - view.startedAt);\n tryComplete();\n });\n const onVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return;\n hidden = true;\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n view.stopLoadingTime = () => {\n stopActivity();\n loadListener?.();\n loadListener = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n };\n tryComplete();\n };\n\n const sanitizeViewAttributes = (attributes: Attributes | undefined) => {\n const redacted = context.applyRedact(attributes ?? {});\n return Object.fromEntries(\n Object.entries(redacted).filter(([key, value]) => value !== undefined && !PROTECTED_ATTRIBUTE_KEYS.has(key)),\n ) as Attributes;\n };\n\n const startView = (\n options: BkOTStartViewOptions,\n source: string,\n loadingType: ViewLoadingType,\n previousUrl = activeView?.rawUrl ?? '',\n ) => {\n if (!context.isSessionActive()) return;\n const currentUrl = options.url ?? getCurrentUrl();\n const urlTemplate = context.config.hooks.getViewUrlTemplate(currentUrl);\n const name = options.name;\n const customAttributes = sanitizeViewAttributes(options.attributes);\n endActiveView(source, false);\n\n const viewId = createRandomId('view');\n const redactedCurrentUrl = context.config.hooks.redactUrl(currentUrl);\n const redactedPreviousUrl = context.config.hooks.redactUrl(previousUrl);\n const isInitialLoad = loadingType === 'initial_load';\n const startedAt = isInitialLoad && typeof performance !== 'undefined' ? performance.timeOrigin : Date.now();\n const navigationTimingAttributes = isInitialLoad ? getNavigationTimingAttributes() : {};\n\n context.setView(\n {\n attributes: customAttributes,\n id: viewId,\n loadingType,\n name,\n startedAt,\n url: redactedCurrentUrl,\n urlTemplate,\n },\n startedAt,\n );\n\n const viewAttributes = {\n ...customAttributes,\n ...context.config.hooks.getPageAttributes(),\n ...navigationTimingAttributes,\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.view,\n }),\n [RUM_ATTRIBUTES.viewId]: viewId,\n ...(name ? { [RUM_ATTRIBUTES.viewName]: name } : {}),\n [RUM_ATTRIBUTES.viewUrl]: redactedCurrentUrl,\n [RUM_ATTRIBUTES.viewUrlTemplate]: urlTemplate,\n [RUM_ATTRIBUTES.viewLoadingType]: loadingType,\n 'url.previous': redactedPreviousUrl,\n ...(isInitialLoad ? { 'document.referrer': context.config.hooks.redactUrl(document.referrer || '') } : {}),\n };\n activeView = {\n attributes: customAttributes,\n version: 0,\n id: viewId,\n loadingType,\n name,\n rawUrl: currentUrl,\n snapshotAttributes: viewAttributes,\n startedAt,\n url: redactedCurrentUrl,\n urlTemplate,\n };\n emitViewEvent(activeView, 'start', startedAt);\n startLoadingTimeTracking(activeView);\n };\n\n const unsubscribeViewCommands = context.onViewCommand(command => {\n if (command.type === 'start') {\n startView(command.options, 'manual', activeView ? 'route_change' : 'initial_load');\n return;\n }\n if (!activeView) return;\n if (command.type === 'set_loading_time') {\n activeView.stopLoadingTime?.();\n activeView.stopLoadingTime = undefined;\n setLoadingTime(activeView, Math.max(0, command.time - activeView.startedAt), 'manual');\n return;\n }\n if (activeView.name === command.name) return;\n activeView.name = command.name;\n activeView.snapshotAttributes[RUM_ATTRIBUTES.viewName] = command.name;\n context.setView({\n attributes: activeView.attributes,\n id: activeView.id,\n loadingType: activeView.loadingType,\n name: activeView.name,\n startedAt: activeView.startedAt,\n url: activeView.url,\n urlTemplate: activeView.urlTemplate,\n });\n emitViewEvent(activeView, 'update');\n });\n\n let unsubscribeRoute = () => {};\n if (context.config.view.tracking === 'auto') {\n if (!activeView) startView({}, 'load', 'initial_load');\n unsubscribeRoute = subscribeRouteChange(event => {\n if (isViewLocationChange(event.fromUrl, event.toUrl)) {\n startView({}, event.source, 'route_change', event.fromUrl);\n }\n });\n }\n\n const onPageHide = (event: PageTransitionEvent) => {\n if (event.persisted) {\n bfCacheView = endActiveView('bfcache');\n return;\n }\n bfCacheView = undefined;\n endActiveView('pagehide');\n };\n const onPageShow = (event: PageTransitionEvent) => {\n if (event.persisted && context.config.view.tracking === 'auto') {\n const restoredView = bfCacheView;\n bfCacheView = undefined;\n if (!context.isSessionActive()) {\n renewalView = restoredView;\n return;\n }\n startView(\n restoredView\n ? { attributes: restoredView.attributes, name: restoredView.name, url: restoredView.rawUrl }\n : {},\n 'bfCache',\n 'bf_cache',\n restoredView?.rawUrl,\n );\n }\n };\n const unsubscribeSessionLifecycle = context.onSessionLifecycle(event => {\n if (event.type === 'expired') {\n renewalView = endActiveView('session_expired', true, event.expiredAt) ?? bfCacheView;\n bfCacheView = undefined;\n return;\n }\n if (renewalView) {\n startView(\n { attributes: renewalView.attributes, name: renewalView.name, url: renewalView.rawUrl },\n 'sessionRenewal',\n 'session_renewal',\n renewalView.rawUrl,\n );\n renewalView = undefined;\n }\n });\n window.addEventListener('pagehide', onPageHide);\n window.addEventListener('pageshow', onPageShow);\n\n teardownCallbacks.push(\n unsubscribeRoute,\n unsubscribeSessionLifecycle,\n unsubscribeViewCommands,\n () => window.removeEventListener('pagehide', onPageHide),\n () => window.removeEventListener('pageshow', onPageShow),\n () => endActiveView('shutdown'),\n );\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","export interface ActiveSessionRecord {\n expiresAt: number;\n id: string;\n startedAt: number;\n status: 'active';\n}\n\nexport interface ExpiredSessionRecord {\n expiredAt: number;\n previousId: string;\n reason: SessionExpireReason;\n status: 'expired';\n}\n\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nexport type SessionExpireReason = 'inactivity' | 'maxLifetime';\n\nexport type SessionRecord = ActiveSessionRecord | ExpiredSessionRecord;\n\nconst FUTURE_CLOCK_TOLERANCE_MS = 5_000;\n\nconst isTimestamp = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0;\n\nexport const createActiveSessionRecord = (\n id: string,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n expiresAt: Math.min(now + idleTimeoutMs, now + maxDurationMs),\n id,\n startedAt: now,\n status: 'active',\n});\n\nexport const getSessionExpireReason = (\n record: ActiveSessionRecord,\n now: number,\n maxDurationMs: number,\n): SessionExpireReason | undefined => {\n if (now >= record.startedAt + maxDurationMs) return 'maxLifetime';\n if (now >= record.expiresAt) return 'inactivity';\n return undefined;\n};\n\nexport const expandActiveSession = (\n record: ActiveSessionRecord,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n ...record,\n expiresAt: Math.min(now + idleTimeoutMs, record.startedAt + maxDurationMs),\n});\n\nexport const parseSessionRecord = (raw: null | string, now: number, maxDurationMs: number): null | SessionRecord => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<SessionRecord>;\n if (\n parsed.status === 'active' &&\n typeof parsed.id === 'string' &&\n parsed.id.length > 0 &&\n isTimestamp(parsed.startedAt) &&\n // 容忍轻微时钟漂移,但拒绝远期时间与超出最大生命周期的记录,避免脏 storage 永久续期。\n parsed.startedAt <= now + FUTURE_CLOCK_TOLERANCE_MS &&\n isTimestamp(parsed.expiresAt) &&\n parsed.expiresAt >= parsed.startedAt &&\n parsed.expiresAt <= parsed.startedAt + maxDurationMs\n ) {\n return parsed as ActiveSessionRecord;\n }\n if (\n parsed.status === 'expired' &&\n typeof parsed.previousId === 'string' &&\n parsed.previousId.length > 0 &&\n (parsed.reason === 'inactivity' || parsed.reason === 'maxLifetime') &&\n isTimestamp(parsed.expiredAt) &&\n parsed.expiredAt <= now + FUTURE_CLOCK_TOLERANCE_MS\n ) {\n return parsed as ExpiredSessionRecord;\n }\n } catch {\n /* JSON 损坏视为无记录 */\n }\n return null;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Session 插件\n *\n * 生命周期:\n * - 首次访问创建 session;用户交互会续期有效 session,或重建已过期 session\n * - 固定 15 分钟无活动 / 4 小时最长生命周期,任一到达时立即清理运行时 session.id\n * - 页面重新可见时只续期有效 session,不会复活已过期 session\n * - 跨 tab 共享持久化状态;本 tab 过期后不会被其他 tab 的新 session 被动复活\n */\n\nimport { hashString } from '../core/hash';\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\nimport {\n createActiveSessionRecord,\n expandActiveSession,\n getSessionExpireReason,\n parseSessionRecord,\n} from './session-state';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { ActiveSessionRecord, ExpiredSessionRecord, SessionExpireReason, SessionRecord } from './session-state';\n\nexport interface BkOTSessionConfig {\n /** 以 0~1 表示的 Session 采样率,默认 1。 */\n sampleRate?: number;\n // 自定义 storage key;不传则按 endpoint host 自动生成隔离后缀\n storageKey?: string;\n}\n\nconst SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;\nconst SESSION_MAX_DURATION_MS = 4 * 60 * 60 * 1000;\nconst ACTIVITY_THROTTLE_MS = 1_000;\nconst VISIBILITY_CHECK_MS = 60_000;\nconst ACTIVITY_EVENTS: Array<keyof WindowEventMap> = ['click', 'keydown', 'scroll', 'touchstart'];\nconst memoryRecords = new Map<string, string>();\n\ninterface SessionStorage {\n isLocalStorage: () => boolean;\n read: () => null | string;\n write: (record: SessionRecord) => void;\n}\n\nconst createSessionStorage = (storageKey: string): SessionStorage => {\n let localStorageAvailable = typeof window !== 'undefined';\n\n const read = () => {\n if (localStorageAvailable) {\n try {\n return window.localStorage.getItem(storageKey);\n } catch {\n localStorageAvailable = false;\n }\n }\n return memoryRecords.get(storageKey) ?? null;\n };\n\n const write = (record: SessionRecord) => {\n const serialized = JSON.stringify(record);\n memoryRecords.set(storageKey, serialized);\n if (!localStorageAvailable) return;\n try {\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n localStorageAvailable = false;\n }\n };\n\n read();\n return { isLocalStorage: () => localStorageAvailable, read, write };\n};\n\nconst buildDefaultStorageKey = (endpoint: string, applicationName: string) => {\n let suffix = '';\n try {\n suffix = new URL(endpoint).host;\n } catch {\n suffix = endpoint;\n }\n return `bk_ot_session__${hashString(`${applicationName}\\0${suffix || 'default'}`)}`;\n};\n\nconst emitSessionEvent = (\n context: BkOTRuntimeContext,\n phase: 'end' | 'rotate' | 'start',\n sessionId: string,\n startedAt: number,\n timestamp: number,\n reason: string,\n previousId?: string,\n) => {\n const span = context.startSpan(\n 'browser.session',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.session,\n }),\n [RUM_ATTRIBUTES.sessionPhase]: phase,\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: sessionId,\n [RUM_ATTRIBUTES.sessionStartTime]: startedAt,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n 'session.lifecycle.reason': reason,\n ...(previousId ? { 'session.previous_id': previousId } : {}),\n },\n {\n contextTime: timestamp,\n startTime: timestamp,\n },\n );\n span.end(timestamp);\n};\n\nexport const createSessionPlugin = (options: BkOTSessionConfig = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'session',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n const now = Date.now();\n const sessionId = createRandomId('session');\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: sessionId,\n [RUM_ATTRIBUTES.sessionStartTime]: now,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n },\n now,\n );\n return;\n }\n\n const storageKey =\n options.storageKey ??\n buildDefaultStorageKey(context.config.transport.endpoint, context.config.application.name);\n const storage = createSessionStorage(storageKey);\n const documentStartedAt =\n typeof performance !== 'undefined' && Number.isFinite(performance.timeOrigin)\n ? performance.timeOrigin\n : Date.now();\n let currentSessionId: string | undefined;\n let currentSessionStartedAt: number | undefined;\n let expirationTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const readRecord = (now = Date.now()) => parseSessionRecord(storage.read(), now, SESSION_MAX_DURATION_MS);\n\n const clearExpirationTimeout = () => {\n if (expirationTimeout !== undefined) {\n clearTimeout(expirationTimeout);\n expirationTimeout = undefined;\n }\n };\n\n const deactivate = (reason: 'external' | SessionExpireReason, expiredAt = Date.now()) => {\n const expiredSessionId = currentSessionId;\n if (!expiredSessionId) return;\n context.notifySessionLifecycle({\n expiredAt,\n id: expiredSessionId,\n reason,\n type: 'expired',\n });\n emitSessionEvent(context, 'end', expiredSessionId, currentSessionStartedAt ?? expiredAt, expiredAt, reason);\n clearExpirationTimeout();\n currentSessionId = undefined;\n currentSessionStartedAt = undefined;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n [RUM_ATTRIBUTES.sessionStartTime]: undefined,\n [RUM_ATTRIBUTES.sessionType]: undefined,\n },\n expiredAt,\n );\n void context.flush();\n };\n\n const expireCurrentSession = () => {\n const now = Date.now();\n const record = readRecord(now);\n if (!record) {\n deactivate('external', now);\n return;\n }\n if (record.status === 'expired') {\n deactivate(record.reason, record.expiredAt);\n return;\n }\n if (record.id !== currentSessionId) {\n deactivate('external', now);\n return;\n }\n const reason = getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS);\n if (!reason) {\n scheduleExpiration(record);\n return;\n }\n const expired: ExpiredSessionRecord = {\n expiredAt: now,\n previousId: record.id,\n reason,\n status: 'expired',\n };\n storage.write(expired);\n deactivate(reason, now);\n };\n\n const scheduleExpiration = (record: ActiveSessionRecord) => {\n clearExpirationTimeout();\n const expiresAt = Math.min(record.expiresAt, record.startedAt + SESSION_MAX_DURATION_MS);\n expirationTimeout = setTimeout(expireCurrentSession, Math.max(0, expiresAt - Date.now()));\n };\n\n const activate = (\n record: ActiveSessionRecord,\n lifecycle?: {\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n },\n ) => {\n const changed = record.id !== currentSessionId;\n currentSessionId = record.id;\n currentSessionStartedAt = record.startedAt;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: record.id,\n [RUM_ATTRIBUTES.sessionStartTime]: record.startedAt,\n [RUM_ATTRIBUTES.sessionType]: 'user',\n },\n Math.min(record.startedAt, documentStartedAt),\n );\n scheduleExpiration(record);\n if (changed && lifecycle) {\n context.notifySessionLifecycle({\n id: record.id,\n previousId: lifecycle.previousId,\n reason: lifecycle.reason,\n startedAt: record.startedAt,\n type: 'renewed',\n });\n }\n };\n\n const expandOrRenew = (isInitialization = false) => {\n const now = Date.now();\n const record = readRecord(now);\n if (record?.status === 'active' && !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)) {\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded, currentSessionId ? undefined : { reason: 'shared' });\n return;\n }\n\n const previousId = record?.status === 'expired' ? record.previousId : record?.id;\n const reason =\n record?.status === 'expired'\n ? record.reason\n : record\n ? getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n : isInitialization\n ? 'init'\n : undefined;\n const created = createActiveSessionRecord(\n createRandomId('session'),\n now,\n SESSION_IDLE_TIMEOUT_MS,\n SESSION_MAX_DURATION_MS,\n );\n storage.write(created);\n activate(created, {\n previousId,\n reason: reason ?? 'init',\n });\n const lifecycleReason = reason ?? 'init';\n emitSessionEvent(\n context,\n lifecycleReason === 'init' ? 'start' : 'rotate',\n created.id,\n created.startedAt,\n created.startedAt,\n lifecycleReason,\n previousId,\n );\n };\n\n const expandOnly = () => {\n if (!currentSessionId) return;\n const now = Date.now();\n const record = readRecord(now);\n if (\n record?.status !== 'active' ||\n record.id !== currentSessionId ||\n getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n expireCurrentSession();\n return;\n }\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded);\n };\n\n expandOrRenew(true);\n\n let lastActivityAt = 0;\n const onActivity = () => {\n const now = Date.now();\n if (now - lastActivityAt < ACTIVITY_THROTTLE_MS) return;\n lastActivityAt = now;\n expandOrRenew();\n };\n const activityListenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n ACTIVITY_EVENTS.forEach(eventName => window.addEventListener(eventName, onActivity, activityListenerOptions));\n teardownCallbacks.push(() => {\n ACTIVITY_EVENTS.forEach(eventName =>\n window.removeEventListener(eventName, onActivity, activityListenerOptions),\n );\n });\n\n const expandWhenVisible = () => {\n if (document.visibilityState === 'visible') expandOnly();\n };\n document.addEventListener('visibilitychange', expandWhenVisible);\n const visibilityInterval = window.setInterval(expandWhenVisible, VISIBILITY_CHECK_MS);\n teardownCallbacks.push(() => {\n document.removeEventListener('visibilitychange', expandWhenVisible);\n window.clearInterval(visibilityInterval);\n });\n\n const onStorage = (event: StorageEvent) => {\n if (!storage.isLocalStorage() || event.storageArea !== window.localStorage || event.key !== storageKey) return;\n const now = Date.now();\n const record = parseSessionRecord(event.newValue, now, SESSION_MAX_DURATION_MS);\n if (\n record?.status === 'active' &&\n record.id === currentSessionId &&\n !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n scheduleExpiration(record);\n return;\n }\n // 当前 tab 一旦失效,不被其他 tab 新建的 session 被动复活;本 tab 发生交互后再接管共享 session。\n deactivate(\n record?.status === 'expired' ? record.reason : 'external',\n record?.status === 'expired' ? record.expiredAt : now,\n );\n };\n window.addEventListener('storage', onStorage);\n teardownCallbacks.push(() => window.removeEventListener('storage', onStorage));\n teardownCallbacks.push(clearExpirationTimeout);\n },\n shutdown() {\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Web Vitals 插件\n *\n * 基于 google/web-vitals 库(attribution 版本)采集 Core Web Vitals:\n * CLS / FCP / INP / LCP / TTFB\n *\n * browser.web_vital span 带 vital.id(唯一)、vital.value 与 attribution 详情\n * (LCP 元素 url / CLS shift target / INP interaction target 等高基数字段),\n * 方便按个例排查\"哪个元素拖累了 LCP\"\n *\n * 数值语义:CLS 是无量纲分数,FCP/INP/LCP/TTFB 的 vital.value 单位为 ms。\n *\n * View 边界:FCP/LCP/TTFB 使用 web-vitals 的条目时间回查历史 View;CLS/INP 按 SDK View 重置,\n * CLS 使用 session window,INP 使用当前 View 的 p98 交互候选。\n *\n * 生命周期保护:\n * - disposed 标志位:shutdown 后所有回调入口 early return,避免向已销毁的 provider 上报\n * - dynamic import 完成后再次检查 disposed:处理\"start() 完成前 shutdown()\"的 race\n * - dynamic import 自身 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n * 即使加载失败也不影响整体启动\n */\n\nimport { type Attributes } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, getVitalOutcome, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type * as WebVitalsAttribution from 'web-vitals/attribution';\nimport type { MetricWithAttribution } from 'web-vitals/attribution';\n\ninterface EventTimingEntry extends PerformanceEntry {\n interactionId?: number;\n processingEnd?: number;\n processingStart?: number;\n target?: EventTarget | null;\n}\n\ninterface InpCandidate {\n duration: number;\n inputDelay: number;\n interactionId: number;\n interactionTarget?: string;\n interactionType: string;\n presentationDelay: number;\n processingDuration: number;\n}\n\ninterface LayoutShiftEntry extends PerformanceEntry {\n hadRecentInput?: boolean;\n sources?: Array<{ node?: Node | null }>;\n value?: number;\n}\n\ninterface ViewVitalState {\n cls: number;\n clsWindowEnd?: number;\n clsWindowLargestShiftTarget?: string;\n clsWindowLargestShiftValue: number;\n clsWindowStart?: number;\n clsWindowValue: number;\n id: string;\n inpCandidates: InpCandidate[];\n interactionCountAtStart?: number;\n interactionIdMax: number;\n interactionIdMin: number;\n largestShiftTarget?: string;\n largestShiftValue: number;\n runtimeAttributes: Attributes;\n startedAt: number;\n}\n\ntype VitalLike = Pick<MetricWithAttribution, 'attribution' | 'entries' | 'id' | 'name' | 'rating' | 'value'>;\n\nconst MAX_INP_CANDIDATES = 10;\nconst MAX_INP_VALUE = 60_000;\nconst MAX_CLS_WINDOW_DURATION = 5_000;\nconst MAX_CLS_UPDATE_GAP = 1_000;\n\nexport const isSameViewId = (currentViewId: string | undefined, nextViewId: string | undefined) =>\n Boolean(nextViewId) && currentViewId === nextViewId;\n\nconst getRating = (name: 'CLS' | 'INP', value: number): VitalLike['rating'] => {\n const [good, poor] = name === 'CLS' ? [0.1, 0.25] : [200, 500];\n return value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n};\n\nconst getTargetName = (target: EventTarget | Node | null | undefined) => {\n if (typeof Element === 'undefined' || !(target instanceof Element)) return undefined;\n return (\n target.getAttribute('data-bk-action-name') ??\n target.getAttribute('aria-label') ??\n target.getAttribute('name') ??\n target.tagName.toLowerCase()\n );\n};\n\nconst getInteractionCount = () => {\n if (typeof performance === 'undefined') return undefined;\n const value = (performance as Performance & { interactionCount?: number }).interactionCount;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n};\n\nexport const updateViewCls = (view: ViewVitalState, entry: LayoutShiftEntry) => {\n const value = entry.value ?? 0;\n if (entry.hadRecentInput || !Number.isFinite(value)) return;\n const shouldCreateWindow =\n view.clsWindowStart === undefined ||\n entry.startTime - (view.clsWindowEnd ?? entry.startTime) >= MAX_CLS_UPDATE_GAP ||\n entry.startTime - view.clsWindowStart >= MAX_CLS_WINDOW_DURATION;\n if (shouldCreateWindow) {\n view.clsWindowStart = entry.startTime;\n view.clsWindowValue = value;\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n } else {\n view.clsWindowValue += value;\n if (value > view.clsWindowLargestShiftValue) {\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n }\n }\n view.clsWindowEnd = entry.startTime;\n if (view.clsWindowValue > view.cls) {\n view.cls = Math.round(view.clsWindowValue * 10_000) / 10_000;\n view.largestShiftValue = view.clsWindowLargestShiftValue;\n view.largestShiftTarget = view.clsWindowLargestShiftTarget;\n }\n};\n\nexport const updateViewInp = (view: ViewVitalState, entry: EventTimingEntry) => {\n if (!entry.interactionId || !Number.isFinite(entry.duration)) return;\n view.interactionIdMin = Math.min(view.interactionIdMin, entry.interactionId);\n view.interactionIdMax = Math.max(view.interactionIdMax, entry.interactionId);\n const processingStart = entry.processingStart ?? entry.startTime;\n const processingEnd = entry.processingEnd ?? processingStart;\n const candidate: InpCandidate = {\n duration: Math.min(entry.duration, MAX_INP_VALUE),\n inputDelay: Math.max(0, processingStart - entry.startTime),\n interactionId: entry.interactionId,\n interactionTarget: getTargetName(entry.target),\n interactionType: entry.name,\n presentationDelay: Math.max(0, entry.startTime + entry.duration - processingEnd),\n processingDuration: Math.max(0, processingEnd - processingStart),\n };\n const index = view.inpCandidates.findIndex(item => item.interactionId === entry.interactionId);\n if (index >= 0) {\n if (candidate.duration > view.inpCandidates[index].duration) view.inpCandidates[index] = candidate;\n } else {\n view.inpCandidates.push(candidate);\n }\n view.inpCandidates.sort((left, right) => right.duration - left.duration).splice(MAX_INP_CANDIDATES);\n};\n\nexport const getViewInp = (view: ViewVitalState): InpCandidate | undefined => {\n const nativeCount = getInteractionCount();\n const interactionCount =\n nativeCount !== undefined && view.interactionCountAtStart !== undefined\n ? Math.max(0, nativeCount - view.interactionCountAtStart)\n : view.interactionIdMin === Infinity\n ? 0\n : (view.interactionIdMax - view.interactionIdMin) / 7 + 1;\n if (!view.inpCandidates.length) return undefined;\n return view.inpCandidates[Math.min(view.inpCandidates.length - 1, Math.floor(interactionCount / 50))];\n};\n\n// 把 attribution 中价值最高的字段抽出来写入 Span。\nconst pickAttribution = (metric: VitalLike, redactUrl: (url: string) => string): Attributes => {\n const attr = metric.attribution ?? {};\n const result: Attributes = {};\n const getAttrValue = (key: string) => (attr as Record<string, unknown>)[key];\n\n const setIfDefined = (key: string, value: unknown, transform?: (value: string) => string) => {\n if (value === undefined || value === null) return;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n result[key] = typeof value === 'string' && transform ? transform(value) : value;\n }\n };\n\n switch (metric.name) {\n case 'LCP':\n setIfDefined('vital.lcp.url', getAttrValue('url'), redactUrl);\n setIfDefined('vital.lcp.target', getAttrValue('target'));\n setIfDefined('vital.lcp.element_render_delay', getAttrValue('elementRenderDelay'));\n setIfDefined('vital.lcp.resource_load_duration', getAttrValue('resourceLoadDuration'));\n setIfDefined('vital.lcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n break;\n case 'CLS':\n setIfDefined('vital.cls.largest_shift_target', getAttrValue('largestShiftTarget'));\n setIfDefined('vital.cls.largest_shift_value', getAttrValue('largestShiftValue'));\n setIfDefined('vital.cls.load_state', getAttrValue('loadState'));\n break;\n case 'INP':\n setIfDefined('vital.inp.interaction_target', getAttrValue('interactionTarget'));\n setIfDefined('vital.inp.interaction_type', getAttrValue('interactionType'));\n setIfDefined('vital.inp.input_delay', getAttrValue('inputDelay'));\n setIfDefined('vital.inp.processing_duration', getAttrValue('processingDuration'));\n setIfDefined('vital.inp.presentation_delay', getAttrValue('presentationDelay'));\n break;\n case 'FCP':\n setIfDefined('vital.fcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n setIfDefined('vital.fcp.load_state', getAttrValue('loadState'));\n break;\n case 'TTFB':\n setIfDefined('vital.ttfb.waiting_duration', getAttrValue('waitingDuration'));\n setIfDefined('vital.ttfb.dns_duration', getAttrValue('dnsDuration'));\n setIfDefined('vital.ttfb.connection_duration', getAttrValue('connectionDuration'));\n setIfDefined('vital.ttfb.request_duration', getAttrValue('requestDuration'));\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (): BkOTPlugin => {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n return {\n name: 'web-vitals',\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // dynamic import 自包 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n // 离线 / chunk 加载失败时不影响其他插件,且 debug 模式留下排查痕迹\n let webVitalsModule: typeof WebVitalsAttribution;\n try {\n webVitalsModule = await import('web-vitals/attribution');\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn('[bk-ot] web-vitals load failed:', error);\n }\n return;\n }\n // import 期间用户可能已经 shutdown:此后再注册回调会向已销毁 provider 上报\n if (disposed) return;\n\n const { onFCP, onLCP, onTTFB } = webVitalsModule;\n const recordSpan = (\n metric: VitalLike,\n runtimeAttributes: Attributes,\n contextTime?: number,\n occurredAt = contextTime ?? Date.now(),\n ) => {\n if (!context.isSampled()) return;\n const metricName = metric.name.toLowerCase();\n const span = context.startSpan(\n 'browser.web_vital',\n {\n ...context.config.hooks.getPageAttributes(),\n ...runtimeAttributes,\n ...buildRumSpanAttributes({\n outcome: getVitalOutcome(metric.rating),\n spanType: RUM_SPAN_TYPE.vital,\n }),\n [RUM_ATTRIBUTES.vitalMetric]: metricName,\n [RUM_ATTRIBUTES.vitalValue]: metric.value,\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n 'vital.id': metric.id,\n ...pickAttribution(metric, context.config.hooks.redactUrl),\n },\n { contextTime, startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n const recordDurationVital = (metric: VitalLike) => {\n // 防御非法值:极端浏览器实现 bug 可能给出 NaN/Infinity,OT 后端会拒收\n if (disposed || !Number.isFinite(metric.value) || !context.isSampled()) return;\n const firstEntry = metric.entries[0];\n const contextTime =\n firstEntry && typeof performance !== 'undefined' ? performance.timeOrigin + firstEntry.startTime : undefined;\n const runtimeAttributes =\n contextTime === undefined ? context.getRuntimeAttributes() : context.getRuntimeAttributesAt(contextTime);\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n let currentView: undefined | ViewVitalState;\n let eventObserver: PerformanceObserver | undefined;\n let layoutShiftObserver: PerformanceObserver | undefined;\n\n const recordViewVital = (metric: VitalLike, runtimeAttributes: Attributes, contextTime: number) => {\n recordSpan(metric, runtimeAttributes, contextTime, Date.now());\n };\n\n const finalizeView = () => {\n const view = currentView;\n if (!view || !context.isSampled()) return;\n recordViewVital(\n {\n attribution: {\n largestShiftTarget: view.largestShiftTarget,\n largestShiftValue: view.largestShiftValue,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'CLS',\n rating: getRating('CLS', view.cls),\n value: view.cls,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n const inp = getViewInp(view);\n if (inp) {\n recordViewVital(\n {\n attribution: {\n inputDelay: inp.inputDelay,\n interactionTarget: inp.interactionTarget,\n interactionType: inp.interactionType,\n presentationDelay: inp.presentationDelay,\n processingDuration: inp.processingDuration,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'INP',\n rating: getRating('INP', inp.duration),\n value: inp.duration,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n }\n };\n\n const unsubscribeView = context.onViewChange(view => {\n const activeVitalView = currentView;\n if (activeVitalView && isSameViewId(activeVitalView.id, view.id)) {\n activeVitalView.runtimeAttributes = {\n ...activeVitalView.runtimeAttributes,\n [RUM_ATTRIBUTES.viewId]: view.id,\n ...(view.name ? { [RUM_ATTRIBUTES.viewName]: view.name } : {}),\n [RUM_ATTRIBUTES.viewUrl]: view.url,\n [RUM_ATTRIBUTES.viewUrlTemplate]: view.urlTemplate,\n };\n return;\n }\n finalizeView();\n currentView = view.id\n ? {\n cls: 0,\n clsWindowLargestShiftValue: 0,\n clsWindowValue: 0,\n interactionCountAtStart: getInteractionCount(),\n interactionIdMax: 0,\n interactionIdMin: Infinity,\n inpCandidates: [],\n id: view.id,\n largestShiftValue: 0,\n runtimeAttributes: context.getRuntimeAttributes(),\n startedAt: view.startedAt ?? Date.now(),\n }\n : undefined;\n });\n\n if (typeof PerformanceObserver !== 'undefined') {\n if (PerformanceObserver.supportedEntryTypes?.includes('layout-shift')) {\n layoutShiftObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as LayoutShiftEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt) continue;\n updateViewCls(currentView, entry);\n }\n });\n layoutShiftObserver.observe({ buffered: true, type: 'layout-shift' });\n }\n if (PerformanceObserver.supportedEntryTypes?.includes('event')) {\n eventObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as EventTimingEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt || !entry.interactionId || !Number.isFinite(entry.duration))\n continue;\n updateViewInp(currentView, entry);\n }\n });\n eventObserver.observe({ buffered: true, durationThreshold: 40, type: 'event' });\n }\n }\n onFCP(recordDurationVital);\n onLCP(recordDurationVital);\n onTTFB(recordDurationVital);\n\n cleanup = () => {\n finalizeView();\n currentView = undefined;\n unsubscribeView();\n layoutShiftObserver?.disconnect();\n eventObserver?.disconnect();\n };\n },\n shutdown() {\n // web-vitals 的 onXXX 不返回 unsubscribe,无法真正取消订阅;\n // 用 disposed 标志位让回调入口 early return 即可\n disposed = true;\n cleanup?.();\n cleanup = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { SpanKind } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nconst buildWebSocketAttributes = (url: string, redactUrl: (url: string) => string): Attributes => {\n const attributes: Attributes = {\n 'network.protocol.name': 'websocket',\n 'url.full': redactUrl(url),\n };\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n attributes['server.address'] = parsed.hostname;\n if (parsed.port) attributes['server.port'] = Number(parsed.port);\n attributes['url.scheme'] = parsed.protocol.replace(':', '');\n } catch {\n // 非法 URL 仍保留脱敏后的原始字符串。\n }\n return attributes;\n};\n\nconst instrumentSocket = (socket: WebSocket, context: BkOTRuntimeContext, url: string, isDisposed: () => boolean) => {\n const connectionId = createRandomId('websocket');\n const baseAttributes = buildWebSocketAttributes(url, context.config.hooks.redactUrl);\n let opened = false;\n\n const emit = (phase: 'close' | 'connect' | 'error' | 'open', timestamp: number, attributes: Attributes = {}) => {\n if (isDisposed() || !context.isSampled()) return;\n const isError = phase === 'error';\n const span = context.startSpan(\n 'browser.websocket',\n {\n ...baseAttributes,\n ...buildRumSpanAttributes({\n outcome: isError ? RUM_SPAN_OUTCOME.error : RUM_SPAN_OUTCOME.success,\n outcomeReason: isError ? 'network' : undefined,\n spanType: RUM_SPAN_TYPE.websocket,\n }),\n [RUM_ATTRIBUTES.websocketPhase]: phase,\n 'websocket.connection.id': connectionId,\n ...attributes,\n },\n { kind: SpanKind.CLIENT, startTime: timestamp },\n );\n span.end(timestamp);\n };\n\n emit('connect', Date.now());\n socket.addEventListener('open', () => {\n opened = true;\n emit('open', Date.now());\n });\n socket.addEventListener('error', () => {\n emit('error', Date.now(), { 'websocket.error.phase': opened ? 'runtime' : 'connect' });\n });\n socket.addEventListener('close', event => {\n emit('close', Date.now(), {\n 'websocket.close.code': event.code,\n 'websocket.close.reason': event.reason,\n 'websocket.close.was_clean': event.wasClean,\n });\n });\n};\n\nexport const createWebSocketPlugin = (): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n let patchedProxy: typeof WebSocket | undefined;\n let disposed = false;\n\n return {\n name: 'websocket',\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') return;\n const nativeWebSocket = window.WebSocket;\n originalWebSocket = nativeWebSocket;\n patchedProxy = new Proxy(nativeWebSocket, {\n construct(target, args, newTarget) {\n const socket = Reflect.construct(target, args, newTarget) as WebSocket;\n if (disposed || !context.isSampled()) return socket;\n const url = args[0] == null ? '' : String(args[0]);\n if (url && !shouldIgnoreUrl(context.config, url)) {\n instrumentSocket(socket, context, url, () => disposed);\n }\n return socket;\n },\n });\n window.WebSocket = patchedProxy;\n },\n shutdown() {\n disposed = true;\n if (typeof window !== 'undefined' && originalWebSocket && window.WebSocket === patchedProxy) {\n window.WebSocket = originalWebSocket;\n }\n patchedProxy = undefined;\n originalWebSocket = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nexport type BkOTActivityEvent =\n | { endTime: number; startTime: number; type: 'long_task'; url?: never }\n | { endTime: number; startTime: number; type: 'resource'; url?: string }\n | { id: string; time: number; type: 'request_end' }\n | { id: string; time: number; type: 'request_start'; url?: string }\n | { time: number; type: 'dom_mutation' | 'error' | 'window_open' };\n\nexport class RumActivityObserver {\n private readonly listeners = new Set<(event: BkOTActivityEvent) => void>();\n\n public notify = (event: BkOTActivityEvent) => {\n this.listeners.forEach(listener => listener(event));\n };\n\n public subscribe = (listener: (event: BkOTActivityEvent) => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { ResolvedBkOTConfig } from './config';\n\nconst hashToUnitInterval = (input: string) => {\n let hash = 0x811c9dc5;\n for (let index = 0; index < input.length; index += 1) {\n hash = Math.imul(hash ^ input.charCodeAt(index), 0x01000193);\n }\n hash += hash << 13;\n hash ^= hash >>> 7;\n hash += hash << 3;\n hash ^= hash >>> 17;\n hash += hash << 5;\n return (hash >>> 0) / 0x100000000;\n};\n\n/**\n * 按应用和 RUM Session ID 做确定性采样。采样决定随 Session 更新,不再依赖 Tab 级 sessionStorage。\n */\nexport const isBkOTSampled = (\n { application, session }: Pick<ResolvedBkOTConfig, 'application' | 'session'>,\n sessionId: string,\n) => {\n if (session.sampleRate <= 0) {\n return false;\n }\n if (session.sampleRate >= 1) {\n return true;\n }\n return hashToUnitInterval(`${application.name}\\0${sessionId}`) < session.sampleRate;\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { type Attributes, type Span, context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchSpanProcessor, SamplingDecision, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\nimport { createActionPlugin } from '../plugins/action';\nimport { createActivityPlugin } from '../plugins/activity';\nimport { createBlankScreenPlugin } from '../plugins/blank-screen';\nimport { createCommonInstrumentationsPlugin } from '../plugins/common';\nimport { createCspViolationPlugin } from '../plugins/csp-violation';\nimport { createDevicePlugin } from '../plugins/device';\nimport { createErrorPlugin } from '../plugins/error';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createResourcePlugin } from '../plugins/resource';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_OUTCOME, RUM_SPAN_TYPE } from '../schema/span';\nimport { RumActivityObserver } from './action-activity';\nimport {\n type BkOTActionEventPayload,\n type BkOTCustomEventPayload,\n type BkOTOptions,\n type BkOTTrackingConfig,\n type BkOTTrackingConsent,\n type ResolvedBkOTConfig,\n type ResolvedTransportConfig,\n resolveConfig,\n} from './config';\nimport { createRandomId } from './id';\nimport { type BkOTPlugin, type BkOTRuntimeContext, type BkOTStartSpanOptions, PluginManager } from './plugin';\nimport { FilteringSpanProcessor } from './processor';\nimport {\n type BkOTSessionLifecycleEvent,\n type BkOTStartViewOptions,\n type BkOTUserContext,\n RumRuntimeContext,\n} from './runtime-context';\nimport { isBkOTSampled } from './sampling';\n\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport type { Sampler } from '@opentelemetry/sdk-trace-base';\n\nexport interface BkOTInstance extends Omit<\n BkOTRuntimeContext,\n 'notifyActivity' | 'onActivity' | 'onViewChange' | 'onViewCommand' | 'registerInstrumentations' | 'setView'\n> {\n flush: () => Promise<void>;\n reportAction: (payload: BkOTActionEventPayload) => void;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n setTrackingConsent: (consent: BkOTTrackingConsent) => Promise<void>;\n setUser: (user: BkOTUserContext) => void;\n setViewLoadingTime: () => void;\n setViewName: (name: string) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n startView: (options?: BkOTStartViewOptions) => void;\n use: (plugin: BkOTPlugin) => BkOTInstance;\n useInstrumentation: (instrumentation: Instrumentation) => BkOTInstance;\n}\n\nconst createExporterOptions = (config: ResolvedTransportConfig) => ({\n url: config.endpoint,\n headers: config.headers,\n concurrencyLimit: config.concurrencyLimit,\n timeoutMillis: config.timeoutMillis,\n});\n\nconst BK_OT_INSTRUMENTATION_NAME = 'bk-rum';\n\nclass RumSessionSampler implements Sampler {\n public constructor(private readonly isSampled: () => boolean) {}\n\n public shouldSample() {\n return {\n decision: this.isSampled() ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,\n };\n }\n\n public toString() {\n return 'RumSessionSampler';\n }\n}\n\n// OTel context manager 和 propagator 是页面级全局对象,多 SDK 实例通过引用计数共享,最后一个实例退出时释放。\nlet globalRegistration:\n | undefined\n | {\n contextManager?: ZoneContextManager;\n ownsContext: boolean;\n ownsPropagator: boolean;\n users: number;\n };\n\nconst acquireOtelGlobals = () => {\n if (globalRegistration) {\n globalRegistration.users += 1;\n } else {\n const contextManager = new ZoneContextManager().enable();\n const ownsContext = context.setGlobalContextManager(contextManager);\n if (!ownsContext) contextManager.disable();\n globalRegistration = {\n contextManager: ownsContext ? contextManager : undefined,\n ownsContext,\n ownsPropagator: propagation.setGlobalPropagator(new W3CTraceContextPropagator()),\n users: 1,\n };\n }\n let released = false;\n return () => {\n if (released || !globalRegistration) return;\n released = true;\n globalRegistration.users -= 1;\n if (globalRegistration.users > 0) return;\n if (globalRegistration.ownsContext) context.disable();\n if (globalRegistration.ownsPropagator) propagation.disable();\n globalRegistration = undefined;\n };\n};\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n enabled: boolean,\n): TExporter => {\n if (!enabled) {\n return exporter;\n }\n return new Proxy(exporter, {\n get(target, prop, receiver) {\n if (prop === 'export') {\n return (records: unknown, callback: unknown) => {\n globalThis.console?.log('[bk-ot][rum]', records);\n return (target as { export: (...args: unknown[]) => unknown }).export(records, callback);\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n};\n\n// 请求插件统一负责 Fetch/XHR span、Trace Header 与活动计数。\nconst getCorePlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.tracking.request),\n];\n\nconst getRumContextPlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createSessionPlugin(config.session),\n ...createBuiltInPlugin(config.tracking.device, true, createDevicePlugin),\n createActivityPlugin(),\n createPageViewPlugin(),\n createResourcePlugin(isBuiltInPluginEnabled(config.tracking.resource, true)),\n];\n\nconst isBuiltInPluginEnabled = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n) => (option === undefined ? defaultEnabled : option !== false);\n\nconst getBuiltInPluginOptions = <TOptions extends object>(option: boolean | TOptions | undefined) =>\n typeof option === 'object' && option !== null ? option : undefined;\n\nconst createBuiltInPlugin = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n factory: (options?: TOptions) => BkOTPlugin,\n) => (isBuiltInPluginEnabled(option, defaultEnabled) ? [factory(getBuiltInPluginOptions(option))] : []);\n\n// 上下文插件先启动,确保后续 span 能带上关联字段;Session sampler 与 processor 负责统一采样。\nconst getRumPlugins = (tracking: BkOTTrackingConfig): BkOTPlugin[] => [\n ...createBuiltInPlugin(tracking.error, true, createErrorPlugin),\n ...createBuiltInPlugin(tracking.webVitals, true, createWebVitalsPlugin),\n ...createBuiltInPlugin(tracking.blankScreen, true, createBlankScreenPlugin),\n ...createBuiltInPlugin(tracking.action, true, createActionPlugin),\n ...createBuiltInPlugin(tracking.websocket, false, createWebSocketPlugin),\n ...createBuiltInPlugin(tracking.longTask, false, createLongTaskPlugin),\n ...createBuiltInPlugin(tracking.cspViolation, false, createCspViolationPlugin),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'starting' | 'stopped';\n\n/**\n * 浏览器 RUM SDK 的生命周期入口。Provider 在构造时创建,插件和全局 patch 则随 start、授权变更和\n * shutdown 安装或卸载;Session 决定统一作用于所有 RUM span。\n */\nexport class BkOpenTelemetry implements BkOTInstance {\n private readonly activityObserver = new RumActivityObserver();\n private readonly flushProviders = async () => {\n await this.tracerProvider.forceFlush();\n };\n private phase: SdkLifecyclePhase = 'idle';\n private pluginManager?: PluginManager;\n private readonly registeredInstrumentations: Instrumentation[];\n private releaseOtelGlobals?: () => void;\n private readonly runtimeContext: RumRuntimeContext;\n\n private sampled = false;\n private readonly startCollection = async () => {\n // Provider 始终存在以保持公开 API 稳定;采集插件仅在启用且获得授权后安装。\n if (this.pluginManager || !this.config.enabled || this.config.trackingConsent !== 'granted') return;\n this.releaseOtelGlobals = acquireOtelGlobals();\n const pluginManager = this.createPluginManager();\n this.pluginManager = pluginManager;\n try {\n await pluginManager.start(this.createPluginContext());\n this.registerInstrumentations(this.config.extensions.instrumentations ?? []);\n } catch (error) {\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n throw error;\n }\n };\n private readonly stopCollection = async (flush: boolean) => {\n const pluginManager = this.pluginManager;\n if (!pluginManager) return;\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n // 撤销授权后立即清空 Session,防止后续手工 API 继续复用旧采样决定。\n this.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n });\n if (flush) await this.flushProviders();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n };\n private readonly teardownCallbacks: Array<() => void> = [];\n private readonly tracerProvider: WebTracerProvider;\n private assertBeforeStart(method: string) {\n if (this.phase !== 'idle') {\n throw new Error(`[bk-ot] ${method}() must be called before start(); set autoStart: false before registration.`);\n }\n }\n private createPluginContext(): BkOTRuntimeContext {\n return {\n applyRedact: this.applyRedact,\n config: this.config,\n flush: this.flush,\n getRuntimeAttributes: this.getRuntimeAttributes,\n getRuntimeAttributesAt: this.getRuntimeAttributesAt,\n isSampled: this.isSampled,\n isSessionActive: this.isSessionActive,\n isViewActive: this.isViewActive,\n notifySessionLifecycle: this.notifySessionLifecycle,\n notifyActivity: this.activityObserver.notify,\n onActivity: this.activityObserver.subscribe,\n onSessionLifecycle: this.onSessionLifecycle,\n onViewCommand: this.runtimeContext.onViewCommand,\n onViewChange: this.runtimeContext.onViewChange,\n registerInstrumentations: this.registerInstrumentations,\n setRuntimeAttributes: this.setRuntimeAttributes,\n setUser: this.setUser,\n setView: this.runtimeContext.setView,\n startSpan: this.startSpan,\n tracer: this.tracer,\n };\n }\n private createPluginManager() {\n return new PluginManager([\n ...getRumContextPlugins(this.config),\n ...getCorePlugins(this.config),\n ...getRumPlugins(this.config.tracking),\n ...(this.config.extensions.plugins ?? []),\n ]);\n }\n public readonly applyRedact = (attributes: Attributes) => this.config.hooks.redactAttributes(attributes);\n\n public readonly config: ResolvedBkOTConfig;\n\n public readonly flush = async () => {\n await this.pluginManager?.flush();\n await this.flushProviders();\n };\n\n public readonly getRuntimeAttributes = () => this.runtimeContext.getAttributes();\n public readonly getRuntimeAttributesAt = (timestamp: number) => this.runtimeContext.getAttributesAt(timestamp);\n public readonly isSampled = () => this.sampled;\n public readonly isSessionActive = () => this.runtimeContext.isSessionActive();\n public readonly isViewActive = () => this.runtimeContext.isViewActive();\n public readonly notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.runtimeContext.notifySessionLifecycle(event);\n };\n\n public readonly onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) =>\n this.runtimeContext.onSessionLifecycle(listener);\n\n public readonly registerInstrumentations = (instrumentations: Instrumentation[]) => {\n if (!instrumentations.length) return;\n instrumentations.forEach(instrumentation => {\n instrumentation.setTracerProvider(this.tracerProvider);\n if (!instrumentation.getConfig().enabled) instrumentation.enable();\n });\n this.registeredInstrumentations.push(...instrumentations);\n };\n\n public readonly reportAction = ({ attributes = {}, name }: BkOTActionEventPayload) => {\n if (!this.isSampled()) {\n return;\n }\n const actionId = createRandomId('action');\n const occurredAt = Date.now();\n const span = this.startSpan(\n 'action.custom',\n {\n ...this.config.hooks.getPageAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n spanType: RUM_SPAN_TYPE.action,\n }),\n [RUM_ATTRIBUTES.actionId]: actionId,\n [RUM_ATTRIBUTES.actionName]: name,\n [RUM_ATTRIBUTES.actionType]: 'custom',\n },\n { startTime: occurredAt },\n );\n span.end(occurredAt);\n };\n\n public readonly reportCustomEvent = ({ attributes = {}, error, name }: BkOTCustomEventPayload) => {\n if (!this.isSampled()) {\n // 未采样时静默丢弃;如需调试请把 debug 选项打开\n if (this.config.debug) {\n globalThis.console?.warn('[bk-ot] custom event dropped (not sampled):', name);\n }\n return;\n }\n\n const occurredAt = Date.now();\n const span = this.startSpan(\n `custom.${name}`,\n {\n ...this.config.hooks.getPageAttributes(),\n ...this.config.hooks.getCustomAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n outcome: error ? RUM_SPAN_OUTCOME.error : RUM_SPAN_OUTCOME.success,\n spanType: RUM_SPAN_TYPE.custom,\n }),\n },\n { startTime: occurredAt },\n );\n\n if (error) {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n }\n\n span.end(occurredAt);\n };\n\n public readonly setRuntimeAttributes = (attributes: Attributes, changedAt?: number) => {\n const nextAttributes = { ...attributes };\n const effectiveChangedAt = changedAt ?? Date.now();\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n // 采样以 Session 为边界重新计算,并保存在运行时私有历史中。\n const sessionId = attributes[RUM_ATTRIBUTES.sessionId];\n this.sampled =\n this.config.enabled && typeof sessionId === 'string' && sessionId.length > 0\n ? isBkOTSampled(this.config, sessionId)\n : false;\n this.runtimeContext.setSessionSampled(this.sampled, effectiveChangedAt);\n }\n this.runtimeContext.setAttributes(nextAttributes, effectiveChangedAt);\n };\n\n public readonly setTrackingConsent = async (consent: BkOTTrackingConsent) => {\n if (this.config.trackingConsent === consent) return;\n this.config.trackingConsent = consent;\n if (this.phase !== 'started') return;\n if (consent === 'granted') await this.startCollection();\n else await this.stopCollection(true);\n };\n\n public readonly setUser = (user: BkOTUserContext) => {\n this.runtimeContext.setUser(user);\n };\n\n public readonly setViewLoadingTime = () => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ time: Date.now(), type: 'set_loading_time' });\n };\n\n public readonly setViewName = (name: string) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ name, type: 'set_name' });\n };\n\n public readonly shutdown = async () => {\n if (this.phase === 'stopped') {\n return;\n }\n await this.stopCollection(false);\n await this.flushProviders();\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.tracerProvider.shutdown();\n this.phase = 'stopped';\n };\n\n public readonly start = async () => {\n if (this.phase === 'started' || this.phase === 'starting') {\n return;\n }\n if (this.phase === 'stopped') {\n // shutdown 已经销毁了 provider/processor,此处禁止再次启动\n throw new Error('[bk-ot] instance has been shut down and cannot be restarted; create a new instance instead.');\n }\n if (!this.config.enabled) {\n this.phase = 'started';\n return;\n }\n this.phase = 'starting';\n try {\n await this.startCollection();\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // 页面进入后台或被丢弃前尽力导出批量数据;两个事件并存以覆盖不同浏览器生命周期。\n const flushOnPageHide = () => {\n void this.flush();\n };\n const flushOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n void this.flush();\n }\n };\n window.addEventListener('pagehide', flushOnPageHide);\n document.addEventListener('visibilitychange', flushOnHidden);\n this.teardownCallbacks.push(\n () => window.removeEventListener('pagehide', flushOnPageHide),\n () => document.removeEventListener('visibilitychange', flushOnHidden),\n );\n }\n } catch (error) {\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.stopCollection(false);\n this.phase = 'idle';\n throw error;\n }\n this.phase = 'started';\n };\n\n public readonly startSpan = (name: string, attributes: Attributes = {}, options?: BkOTStartSpanOptions): Span => {\n const contextTime =\n options?.contextTime ?? (typeof options?.startTime === 'number' ? options.startTime : undefined);\n // 返回 provider 创建的 non-recording span,而不是让每个调用方处理 undefined。\n if (!this.isSampled() || (contextTime !== undefined && !this.runtimeContext.isSessionSampledAt(contextTime))) {\n return this.tracer.startSpan(name);\n }\n const runtimeAttributes =\n contextTime === undefined ? this.getRuntimeAttributes() : this.runtimeContext.getAttributesAt(contextTime);\n return this.tracer.startSpan(name, {\n kind: options?.kind ?? SpanKind.INTERNAL,\n links: options?.links,\n startTime: options?.startTime,\n attributes: this.applyRedact({\n ...runtimeAttributes,\n ...attributes,\n }),\n });\n };\n\n public readonly startView = (options: BkOTStartViewOptions = {}) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ options, type: 'start' });\n };\n\n public readonly tracer: BkOTRuntimeContext['tracer'];\n\n public readonly use = (plugin: BkOTPlugin): this => {\n this.assertBeforeStart('use');\n this.config.extensions.plugins?.push(plugin);\n return this;\n };\n\n public readonly useInstrumentation = (instrumentation: Instrumentation): this => {\n this.assertBeforeStart('useInstrumentation');\n if (this.config.enabled) this.config.extensions.instrumentations?.push(instrumentation);\n return this;\n };\n\n constructor(inputConfig: BkOTOptions) {\n this.config = resolveConfig(inputConfig);\n this.runtimeContext = new RumRuntimeContext({}, this.config.user);\n\n const resource = resourceFromAttributes(this.config.resourceAttributes);\n const spanProcessors = (() => {\n const exporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(this.config.transport)),\n this.config.debug,\n );\n const processor =\n this.config.spanProcessor === 'simple'\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, this.config.spanBatch);\n return [\n new FilteringSpanProcessor(processor, this.config, () =>\n this.applyRedact({\n ...this.runtimeContext.getAttributes(),\n ...this.config.hooks.getPageAttributes(),\n }),\n ),\n ];\n })();\n this.tracerProvider = new WebTracerProvider({\n resource,\n sampler: new RumSessionSampler(this.isSampled),\n spanProcessors,\n });\n this.tracer = this.tracerProvider.getTracer(BK_OT_INSTRUMENTATION_NAME);\n this.registeredInstrumentations = [];\n if (this.config.autoStart) {\n void this.start().catch(error => {\n globalThis.console?.warn('[bk-ot] auto start failed:', error);\n });\n }\n }\n}\n"],"mappings":"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"}
|