@blueking/open-telemetry 0.0.3 → 0.0.5
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 +340 -0
- package/dist/bk-rum.global.js +8 -8
- package/dist/bk-rum.global.js.map +1 -1
- package/dist/browser.d.ts +2 -0
- package/dist/core/config.d.ts +134 -0
- package/dist/core/plugin.d.ts +33 -0
- package/dist/core/processor.d.ts +16 -0
- package/dist/core/route-observer.d.ts +9 -0
- package/dist/core/sampling.d.ts +2 -0
- package/dist/core/sdk.d.ts +44 -0
- package/dist/core/url.d.ts +4 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +374 -365
- package/dist/index.js.map +1 -1
- package/dist/plugins/blank-screen.d.ts +3 -0
- package/dist/plugins/common.d.ts +3 -0
- package/dist/plugins/csp-violation.d.ts +6 -0
- package/dist/plugins/device.d.ts +7 -0
- package/dist/plugins/error.d.ts +3 -0
- package/dist/plugins/http-body.d.ts +20 -0
- package/dist/plugins/long-task.d.ts +7 -0
- package/dist/plugins/page-view.d.ts +3 -0
- package/dist/plugins/route-timing.d.ts +7 -0
- package/dist/plugins/session.d.ts +7 -0
- package/dist/plugins/web-vitals.d.ts +3 -0
- package/dist/plugins/websocket.d.ts +3 -0
- package/package.json +5 -4
- package/src/index.ts +0 -55
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/core/config.ts","../src/core/plugin.ts","../src/core/url.ts","../src/core/processor.ts","../src/plugins/blank-screen.ts","../src/plugins/common.ts","../src/plugins/csp-violation.ts","../src/plugins/device.ts","../src/plugins/error.ts","../src/plugins/http-body.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/route-timing.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/sampling.ts","../src/core/sdk.ts"],"sourcesContent":["/*\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 { BkOTPlugin } from './plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n metric?: () => 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 BkOTConfig {\n attributes?: BkOTAttributesConfig;\n autoStart?: boolean;\n debug?: boolean;\n enabled?: boolean;\n endpoint?: string;\n environment?: string;\n headers?: Record<string, string>;\n plugins?: BkOTPlugin[];\n redact?: BkOTRedactConfig;\n rum?: BkOTRumConfig;\n sampleRate?: number;\n spanBatch?: BkOTBatchConfig;\n token?: string;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTHttpBodyConfig {\n maxBodySize?: number;\n redact?: (payload: BkOTHttpBodyRedactPayload) => string;\n}\n\nexport interface BkOTHttpBodyRedactPayload {\n body: string;\n contentType?: string;\n method: string;\n status?: number;\n truncated: boolean;\n type: 'request' | 'response';\n url: string;\n}\n\nexport interface BkOTInstrumentationsConfig {\n documentLoad?: boolean;\n fetch?: boolean;\n userInteraction?: boolean | { eventNames?: string[] };\n xhr?: boolean;\n}\n\nexport interface BkOTRedactConfig {\n // 用于隐私脱敏:在所有 RUM 自定义插件 emit 之前过滤一次属性\n attributes?: (attributes: Attributes) => Attributes;\n // 用于隐私脱敏:所有上报到 attribute 的 URL 在写入前会经过此函数\n url?: (url: string) => string;\n}\n\nexport interface BkOTRumConfig extends BkOTInstrumentationsConfig {\n cspViolation?: boolean;\n device?: boolean | { storageKey?: string };\n httpBody?: BkOTHttpBodyConfig | boolean;\n pageView?: boolean;\n routeTiming?: boolean;\n websocket?: boolean;\n webVitals?: boolean;\n blankScreen?:\n | {\n checkDelay?: number;\n // 自定义\"页面已渲染\"忽略选择器,命中即认为是 loading 占位、非空白\n ignoreSelectors?: string[];\n rootSelector?: string;\n threshold?: number;\n }\n | boolean;\n error?:\n | {\n // 同 hash 错误窗口内最多上报多少条,默认 5\n maxPerWindow?: number;\n // 节流窗口长度(ms),默认 60_000\n windowMs?: number;\n }\n | boolean;\n longTask?:\n | {\n // 任务时长阈值(ms),低于该值的 longtask 不上报,默认 50\n threshold?: number;\n }\n | boolean;\n session?:\n | {\n // 不活跃多久后视为新会话,默认 30 分钟\n inactivityMs?: number;\n storageKey?: string;\n }\n | boolean;\n}\n\nexport interface NormalizedBkOTConfig extends Omit<\n BkOTConfig,\n 'attributes' | 'autoStart' | 'debug' | 'enabled' | 'endpoint' | 'environment' | 'plugins' | 'redact' | 'token'\n> {\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n endpoint: string;\n environment: string;\n logs: SignalExporterConfig;\n metricIntervalMillis: number;\n metrics: SignalExporterConfig;\n plugins: BkOTPlugin[];\n resourceAttributes: Attributes;\n sampleRate: number;\n traces: SignalExporterConfig;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getMetricAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n instrumentations: Required<Pick<BkOTInstrumentationsConfig, 'documentLoad' | 'fetch' | 'xhr'>> & {\n userInteraction: BkOTInstrumentationsConfig['userInteraction'];\n };\n rum: Omit<Required<BkOTRumConfig>, 'documentLoad' | 'fetch' | 'httpBody' | 'userInteraction' | 'xhr'> & {\n httpBody: false | Required<BkOTHttpBodyConfig>;\n };\n}\n\nexport interface SignalExporterConfig {\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n}\n\nconst DEFAULT_OTLP_ENDPOINT = 'http://localhost:4318'; // 默认上报到本机 OTLP collector\nconst DEFAULT_METRIC_INTERVAL_MILLIS = 10 * 1000; // 10s 上报一次\n\nconst trimTrailingSlash = (value: string) => value.replace(/\\/+$/, '');\n\nconst resolveSignalEndpoint = (endpoint: string | undefined, signalPath: 'logs' | 'metrics' | 'traces') => {\n const normalized = trimTrailingSlash(endpoint || DEFAULT_OTLP_ENDPOINT);\n if (normalized.endsWith(`/v1/${signalPath}`)) {\n return normalized;\n }\n if (normalized.endsWith('/v1')) {\n return `${normalized}/${signalPath}`;\n }\n return `${normalized}/v1/${signalPath}`;\n};\n\nconst resolveSignalConfig = (base: BkOTConfig, signalPath: 'logs' | 'metrics' | 'traces'): SignalExporterConfig => {\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n base.token || base.headers\n ? {\n ...(base.token ? { Authorization: `Bearer ${base.token}` } : {}),\n ...(base.headers ?? {}),\n }\n : undefined;\n\n return {\n endpoint: resolveSignalEndpoint(base.endpoint, signalPath),\n headers: mergedHeaders,\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 positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\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 if (typeof value !== 'number' || !Number.isFinite(value)) {\n return fallback;\n }\n return Math.max(0, Math.min(1, value));\n};\n\nconst identityRedactHttpBody = (payload: BkOTHttpBodyRedactPayload) => payload.body;\n\nconst normalizeHttpBodyConfig = (httpBody: BkOTRumConfig['httpBody']): false | Required<BkOTHttpBodyConfig> => {\n if (!httpBody) {\n return false;\n }\n if (typeof httpBody === 'boolean') {\n return {\n maxBodySize: 10 * 1024,\n redact: identityRedactHttpBody,\n };\n }\n return {\n maxBodySize: positiveNumber(httpBody.maxBodySize, 10 * 1024),\n redact: httpBody.redact ?? identityRedactHttpBody,\n };\n};\n\nconst normalizeRumConfig = (rum: BkOTConfig['rum']): NormalizedBkOTConfig['rum'] => ({\n device: rum?.device ?? true,\n httpBody: normalizeHttpBodyConfig(rum?.httpBody),\n session:\n typeof rum?.session === 'object'\n ? {\n ...rum.session,\n inactivityMs: positiveNumber(rum.session.inactivityMs, 30 * 60 * 1000),\n }\n : (rum?.session ?? true),\n pageView: rum?.pageView ?? true,\n error:\n typeof rum?.error === 'object'\n ? {\n ...rum.error,\n maxPerWindow: positiveNumber(rum.error.maxPerWindow, 5),\n windowMs: positiveNumber(rum.error.windowMs, 60_000),\n }\n : (rum?.error ?? true),\n webVitals: rum?.webVitals ?? true,\n blankScreen:\n typeof rum?.blankScreen === 'object'\n ? {\n ...rum.blankScreen,\n checkDelay: nonNegativeNumber(rum.blankScreen.checkDelay, 3000),\n threshold: boundedRatio(rum.blankScreen.threshold, 0.8),\n }\n : (rum?.blankScreen ?? true),\n websocket: rum?.websocket ?? true,\n longTask:\n typeof rum?.longTask === 'object'\n ? {\n ...rum.longTask,\n threshold: nonNegativeNumber(rum.longTask.threshold, 50),\n }\n : (rum?.longTask ?? false),\n cspViolation: rum?.cspViolation ?? false,\n routeTiming: rum?.routeTiming ?? false,\n});\n\nconst identityRedactAttributes = (attributes: Attributes) => attributes;\nconst identityRedactUrl = (url: string) => url;\n\nexport const normalizeConfig = (config: BkOTConfig): NormalizedBkOTConfig => {\n const resourceAttributes: Attributes = {\n 'deployment.environment.name': config.environment ?? 'production',\n 'rum.provider': 'blueking',\n 'telemetry.sdk.language': 'webjs',\n };\n\n return {\n ...config,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n environment: config.environment ?? 'production',\n endpoint: trimTrailingSlash(config.endpoint || DEFAULT_OTLP_ENDPOINT),\n traces: resolveSignalConfig(config, 'traces'),\n metrics: resolveSignalConfig(config, 'metrics'),\n logs: resolveSignalConfig(config, 'logs'),\n sampleRate: clampSampleRate(config.sampleRate),\n resourceAttributes,\n metricIntervalMillis: DEFAULT_METRIC_INTERVAL_MILLIS,\n instrumentations: {\n documentLoad: config.rum?.documentLoad ?? true,\n fetch: config.rum?.fetch ?? true,\n xhr: config.rum?.xhr ?? true,\n userInteraction: config.rum?.userInteraction ?? true,\n },\n rum: normalizeRumConfig(config.rum),\n plugins: config.plugins ?? [],\n autoStart: config.autoStart ?? true,\n getPageAttributes:\n config.attributes?.page ??\n (() => ({\n 'rum.page.host': typeof window === 'undefined' ? '' : window.location.host,\n 'rum.page.path': typeof window === 'undefined' ? '' : window.location.pathname,\n })),\n getMetricAttributes: config.attributes?.metric ?? (() => ({})),\n getErrorAttributes: config.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.attributes?.custom ?? (() => ({})),\n redactAttributes: config.redact?.attributes ?? identityRedactAttributes,\n redactUrl: config.redact?.url ?? identityRedactUrl,\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 { NormalizedBkOTConfig } from './config';\nimport type { Attributes, Meter, Span, Tracer } from '@opentelemetry/api';\nimport type { Logger, LogRecord } from '@opentelemetry/api-logs';\n\nexport interface BkOTPlugin {\n enabled?: ((config: NormalizedBkOTConfig) => 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: NormalizedBkOTConfig;\n logger: Logger;\n meter: Meter;\n tracer: Tracer;\n /** 对外提供的属性脱敏入口,所有插件在 emit 前都应过一次 */\n applyRedact: (attributes: Attributes) => Attributes;\n /** 包装 logger.emit,自动应用 redact 与 runtime attributes */\n emitLog: (record: LogRecord) => void;\n getRuntimeAttributes: () => Attributes;\n setRuntimeAttributes: (attributes: Attributes) => void;\n startSpan: (name: string, attributes?: Attributes) => Span;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: NormalizedBkOTConfig) => {\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 {\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n }\n } catch (error) {\n await this.shutdown();\n throw error;\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 { NormalizedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nconst 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\nconst isSameOrigin = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).origin === getBaseUrl();\n } catch {\n return false;\n }\n};\n\nexport const isBkOTEndpoint = (config: NormalizedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n\n return [config.traces.endpoint, config.metrics.endpoint, config.logs.endpoint].some(endpoint =>\n isSameResource(targetUrl, toAbsoluteUrl(endpoint))\n );\n};\n\nexport const shouldIgnoreUrl = (config: NormalizedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nexport const shouldPropagateTraceHeader = (_config: NormalizedBkOTConfig, url: string) => isSameOrigin(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 { isBkOTEndpoint } from './url';\n\nimport type { NormalizedBkOTConfig } from './config';\nimport type { Context } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\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\n/**\n * 包装 SpanProcessor,在 onEnd 阶段过滤指向自身上报 endpoint 的 span,\n * 兜底防止 official fetch/xhr 拦截器对 OTel 自身请求产生回环 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: NormalizedBkOTConfig,\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 (url && isBkOTEndpoint(this.config, url)) {\n return;\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\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) 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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_SELECTOR = 'body';\nconst DEFAULT_CHECK_DELAY = 3000;\nconst DEFAULT_THRESHOLD = 0.8;\n// 常见 loading mask / spinner 选择器,命中说明页面正在加载而非空白\nconst DEFAULT_LOADING_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: Array<[number, number]> = [\n [0.5, 0.5],\n [0.25, 0.25],\n [0.75, 0.25],\n [0.25, 0.75],\n [0.75, 0.75],\n];\n\nconst getElementSelector = (element: Element | null) => {\n if (!element) {\n return '';\n }\n const tag = element.tagName.toLowerCase();\n if (element.id) {\n return `${tag}#${element.id}`;\n }\n return tag;\n};\n\nconst matchesAny = (element: Element | null, selectors: string[]) => {\n if (!element) return false;\n return selectors.some(selector => {\n try {\n return element.matches(selector) || !!element.closest(selector);\n } catch {\n return false;\n }\n });\n};\n\n// 页面就绪后再开始计时,避免 SPA / 慢加载场景下检测时机过早\nconst whenDocumentReady = (callback: () => void) => {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'complete' || document.readyState === 'interactive') {\n callback();\n return;\n }\n const handler = () => {\n document.removeEventListener('DOMContentLoaded', handler);\n callback();\n };\n document.addEventListener('DOMContentLoaded', handler);\n};\n\nexport const createBlankScreenPlugin = (option: BkOTRumConfig['blankScreen']): BkOTPlugin => {\n let timer: number | undefined;\n\n return {\n name: 'blank-screen',\n enabled: Boolean(option),\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const optionObj = typeof option === 'object' ? option : {};\n const rootSelector = optionObj.rootSelector ?? DEFAULT_SELECTOR;\n const checkDelay = optionObj.checkDelay ?? DEFAULT_CHECK_DELAY;\n const threshold = optionObj.threshold ?? DEFAULT_THRESHOLD;\n const loadingSelectors = [...DEFAULT_LOADING_SELECTORS, ...(optionObj.ignoreSelectors ?? [])];\n\n // 仅创建一次 counter,避免每次回调都触发一次 meter lookup\n const counter = context.meter.createCounter('browser.blank_screen.count', {\n description: 'Number of suspected blank screen detections',\n });\n\n whenDocumentReady(() => {\n timer = window.setTimeout(() => {\n const root = document.querySelector(rootSelector);\n let emptyCount = 0;\n let loadingCount = 0;\n\n for (const [x, y] of SAMPLE_POINTS) {\n const element = document.elementFromPoint(window.innerWidth * x, window.innerHeight * y);\n if (matchesAny(element, loadingSelectors)) {\n loadingCount += 1;\n continue;\n }\n if (element === root || element === document.body || element === document.documentElement) {\n emptyCount += 1;\n }\n }\n\n const score = emptyCount / SAMPLE_POINTS.length;\n // 触发 loading mask 的样本算\"非空\",但若全部点都是 loading,则视为待定不上报\n if (loadingCount === SAMPLE_POINTS.length) {\n return;\n }\n const isBlank = score >= threshold;\n const attributes = {\n 'bk.rum.blank_screen.score': score,\n 'bk.rum.blank_screen.threshold': threshold,\n 'bk.rum.blank_screen.root': rootSelector,\n 'bk.rum.blank_screen.detected': isBlank,\n 'bk.rum.blank_screen.center_element': getElementSelector(\n document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2),\n ),\n 'bk.rum.blank_screen.dom_node_count': document.body?.getElementsByTagName('*').length ?? 0,\n };\n\n if (isBlank) {\n counter.add(\n 1,\n context.applyRedact({\n // 仅放进低基数维度,center_element 等高基数字段只走 log\n 'bk.rum.blank_screen.root': rootSelector,\n }),\n );\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'browser.blank_screen',\n attributes,\n });\n }\n }, checkDelay);\n });\n },\n shutdown() {\n if (timer !== undefined && typeof window !== 'undefined') {\n window.clearTimeout(timer);\n }\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/naming-convention */\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 Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\nimport { shouldIgnoreUrl, shouldPropagateTraceHeader } from '../core/url';\n\nimport type { BkOTInstrumentationsConfig } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst URL_KEYS = ['http.url', 'url.full'];\n\n// OTel 官方 instrumentation 内部对每个 URL 调用 matcher.test(url),\n// 这里把\"用户函数 + 自身 endpoint 过滤\"包装成具备 .test 的对象,是与 OTel 一致的扩展用法\nconst toUrlPredicateMatcher = (predicate: (url: string) => boolean): RegExp =>\n ({ test: predicate }) as unknown as RegExp;\n\n// 对官方 instrumentation 创建的 span,统一附加 page attributes 与 URL 脱敏\nconst decorateSpan = (\n context: BkOTRuntimeContext,\n span: {\n attributes?: Record<string, any>;\n setAttribute: (key: string, value: any) => void;\n setAttributes: (attrs: Record<string, any>) => void;\n },\n) => {\n span.setAttributes(context.applyRedact(context.config.getPageAttributes()));\n for (const key of URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n const redactedAttributes = context.applyRedact({ [key]: context.config.redactUrl(value) });\n const redactedUrl = redactedAttributes[key];\n if (typeof redactedUrl === 'string') {\n span.setAttribute(key, redactedUrl);\n }\n }\n }\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTInstrumentationsConfig): BkOTPlugin => {\n const instrumentations: Instrumentation[] = [];\n\n return {\n name: 'official-instrumentations',\n enabled: Boolean(option.documentLoad || option.fetch || option.xhr || option.userInteraction),\n async init(context) {\n const loadedInstrumentations: Instrumentation[] = [];\n const ignoreUrls = [toUrlPredicateMatcher(url => shouldIgnoreUrl(context.config, url))];\n const propagateTraceHeaderCorsUrls = [\n toUrlPredicateMatcher(url => shouldPropagateTraceHeader(context.config, url)),\n ];\n\n if (option.documentLoad) {\n const { DocumentLoadInstrumentation } = await import('@opentelemetry/instrumentation-document-load');\n loadedInstrumentations.push(\n new DocumentLoadInstrumentation({\n semconvStabilityOptIn: 'http',\n }),\n );\n }\n\n if (option.fetch) {\n const { FetchInstrumentation } = await import('@opentelemetry/instrumentation-fetch');\n loadedInstrumentations.push(\n new FetchInstrumentation({\n applyCustomAttributesOnSpan: span => decorateSpan(context, span as any),\n ignoreUrls,\n propagateTraceHeaderCorsUrls,\n semconvStabilityOptIn: 'http',\n ignoreNetworkEvents: false,\n }),\n );\n }\n\n if (option.xhr) {\n const { XMLHttpRequestInstrumentation } = await import('@opentelemetry/instrumentation-xml-http-request');\n loadedInstrumentations.push(\n new XMLHttpRequestInstrumentation({\n applyCustomAttributesOnSpan: span => decorateSpan(context, span as any),\n ignoreUrls,\n propagateTraceHeaderCorsUrls,\n semconvStabilityOptIn: 'http',\n ignoreNetworkEvents: false,\n }),\n );\n }\n\n if (option.userInteraction) {\n const { UserInteractionInstrumentation } = await import('@opentelemetry/instrumentation-user-interaction');\n const userInteractionConfig =\n typeof option.userInteraction === 'object'\n ? { eventNames: option.userInteraction.eventNames as Array<keyof HTMLElementEventMap> }\n : undefined;\n loadedInstrumentations.push(new UserInteractionInstrumentation(userInteractionConfig));\n }\n\n instrumentations.push(...loadedInstrumentations);\n registerInstrumentations({ instrumentations: loadedInstrumentations });\n },\n shutdown() {\n while (instrumentations.length) {\n instrumentations.pop()?.disable?.();\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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 监听 CSP 违规事件并以 log 形式上报,便于排查脚本/资源被 CSP 拦截的问题。\n */\nexport const createCspViolationPlugin = (enabled: BkOTRumConfig['cspViolation']): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n context.emitLog({\n severityNumber: SeverityNumber.WARN,\n severityText: 'WARN',\n body: 'csp.violation',\n attributes: {\n ...context.config.getPageAttributes(),\n 'csp.blocked_uri': context.config.redactUrl(event.blockedURI || ''),\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.original_policy': event.originalPolicy,\n 'csp.disposition': event.disposition,\n 'csp.source_file': context.config.redactUrl(event.sourceFile || ''),\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n },\n });\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\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n// 复用旧 session 插件的 storageKey,避免升级后丢失现网累计的设备标识\nconst DEFAULT_DEVICE_STORAGE_KEY = 'bk_ot_session_id';\n\nconst createId = () => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `device-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n\nconst readDeviceId = (storageKey: string) => {\n try {\n const existed = window.localStorage.getItem(storageKey);\n if (existed) {\n return existed;\n }\n const deviceId = createId();\n window.localStorage.setItem(storageKey, deviceId);\n return deviceId;\n } catch {\n return createId();\n }\n};\n\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n const connection = (\n navigator as Navigator & {\n connection?: { effectiveType?: string; type?: string };\n }\n ).connection;\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 'network.effective_type': connection?.effectiveType ?? connection?.type,\n };\n};\n\n/**\n * 设备级永久标识 + 视口 / 网络元数据。\n * 与 session 插件区分:device 跨会话持久,session 有过期与续期。\n */\nexport const createDevicePlugin = (option: BkOTRumConfig['device']): BkOTPlugin => ({\n name: 'device',\n enabled: Boolean(option),\n init(context) {\n const storageKey =\n typeof option === 'object' ? (option.storageKey ?? DEFAULT_DEVICE_STORAGE_KEY) : DEFAULT_DEVICE_STORAGE_KEY;\n const deviceId = typeof window === 'undefined' ? createId() : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n ...getViewportAttributes(),\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 { SpanStatusCode, trace } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_PER_WINDOW = 5;\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\nconst getErrorStack = (value: unknown) => (value instanceof Error ? (value.stack ?? '') : '');\n\n// 简易 djb2 hash,足够区分常见 error 字符串组合\nconst hashString = (input: string): string => {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = (hash * 33) ^ input.charCodeAt(i);\n }\n return (hash >>> 0).toString(36);\n};\n\ninterface ThrottleEntry {\n count: number;\n windowStart: number;\n}\n\nconst createThrottle = (windowMs: number, maxPerWindow: number) => {\n const records = new Map<string, ThrottleEntry>();\n return {\n /** 返回 true 表示允许上报;false 表示触发节流被抛弃 */\n allow(key: string): boolean {\n const now = Date.now();\n const entry = records.get(key);\n if (!entry || now - entry.windowStart > windowMs) {\n records.set(key, { count: 1, windowStart: now });\n return true;\n }\n if (entry.count >= maxPerWindow) {\n return false;\n }\n entry.count += 1;\n return true;\n },\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 throttle: ReturnType<typeof createThrottle>;\n}\n\nconst emitError = ({ context, error, exceptionType, extra = {}, source, spanName, throttle }: EmitErrorOptions) => {\n const message = getErrorMessage(error);\n const stack = getErrorStack(error);\n const throttleKey = hashString(`${source}|${message}|${stack.slice(0, 256)}`);\n\n if (!throttle.allow(throttleKey)) {\n return;\n }\n\n const activeSpan = trace.getActiveSpan();\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.getPageAttributes(),\n ...context.config.getErrorAttributes(),\n 'exception.message': message,\n 'exception.stacktrace': stack,\n 'exception.type': error instanceof Error ? error.name : (exceptionType ?? source),\n 'bk.rum.error.source': source,\n 'bk.rum.error.fingerprint': throttleKey,\n ...extra,\n };\n const errorSpan = context.startSpan(spanName, attributes);\n\n activeSpan?.recordException(exceptionLike);\n activeSpan?.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.end();\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: message,\n attributes,\n });\n};\n\nexport const createErrorPlugin = (option: BkOTRumConfig['error']): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n\n return {\n name: 'error',\n enabled: Boolean(option),\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const windowMs = typeof option === 'object' ? (option.windowMs ?? DEFAULT_WINDOW_MS) : DEFAULT_WINDOW_MS;\n const maxPerWindow =\n typeof option === 'object' ? (option.maxPerWindow ?? DEFAULT_MAX_PER_WINDOW) : DEFAULT_MAX_PER_WINDOW;\n const throttle = createThrottle(windowMs, maxPerWindow);\n\n const onError = (event: ErrorEvent) => {\n emitError({\n context,\n throttle,\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 },\n });\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n emitError({\n context,\n throttle,\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 const target = event.target as EventTarget & {\n href?: string;\n src?: string;\n tagName?: string;\n };\n if (target === window) {\n return;\n }\n const resourceUrl = target.src || target.href || '';\n emitError({\n context,\n throttle,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName?.toLowerCase() || 'unknown'} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.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 * 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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { shouldIgnoreUrl } from '../core/url';\n\nimport type { BkOTHttpBodyConfig, BkOTHttpBodyRedactPayload } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\ninterface BodySnapshot {\n body: string;\n contentType?: string;\n truncated: boolean;\n}\n\ninterface ReportHttpBodyOptions {\n context: BkOTRuntimeContext;\n duration: number;\n error?: unknown;\n method: string;\n request?: BodySnapshot;\n response?: BodySnapshot;\n status?: number;\n url: string;\n}\n\nconst HTTP_BODY_MAX_SIZE = 10 * 1024;\n\ntype HttpBodyInput = BodyInit | Document | null | undefined;\n\nconst truncateBody = (body: string, maxBodySize: number): Pick<BodySnapshot, 'body' | 'truncated'> => {\n if (body.length <= maxBodySize) {\n return {\n body,\n truncated: false,\n };\n }\n return {\n body: body.slice(0, maxBodySize),\n truncated: true,\n };\n};\n\nconst getHeaderValue = (headers: HeadersInit | undefined, key: string) => {\n if (!headers) {\n return undefined;\n }\n if (headers instanceof Headers) {\n return headers.get(key) || undefined;\n }\n const normalizedKey = key.toLowerCase();\n if (Array.isArray(headers)) {\n return headers.find(([itemKey]) => itemKey.toLowerCase() === normalizedKey)?.[1];\n }\n const matchedKey = Object.keys(headers).find(item => item.toLowerCase() === normalizedKey);\n return matchedKey ? headers[matchedKey] : undefined;\n};\n\nconst stringifyFormData = (body: FormData) => {\n const entries: Record<string, string> = {};\n body.forEach((value, key) => {\n entries[key] =\n value instanceof File ? `[File name=${value.name} type=${value.type || 'unknown'} size=${value.size}]` : value;\n });\n return JSON.stringify(entries);\n};\n\nconst stringifyBody = async (body: HttpBodyInput): Promise<string> => {\n if (body == null) {\n return '';\n }\n if (typeof body === 'string') {\n return body;\n }\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n if (body instanceof FormData) {\n return stringifyFormData(body);\n }\n if (body instanceof Blob) {\n return body.text();\n }\n if (body instanceof ArrayBuffer) {\n return new TextDecoder().decode(body);\n }\n if (ArrayBuffer.isView(body)) {\n return new TextDecoder().decode(body);\n }\n if (body instanceof Document) {\n return new XMLSerializer().serializeToString(body);\n }\n return String(body);\n};\n\nconst createBodySnapshot = async (\n body: HttpBodyInput,\n maxBodySize: number,\n contentType?: string,\n): Promise<BodySnapshot | undefined> => {\n const rawBody = await stringifyBody(body);\n if (!rawBody) {\n return undefined;\n }\n return {\n ...truncateBody(rawBody, maxBodySize),\n contentType,\n };\n};\n\nconst safeCreateBodySnapshot = async (body: HttpBodyInput, maxBodySize: number, contentType?: string) => {\n try {\n return await createBodySnapshot(body, maxBodySize, contentType);\n } catch {\n return undefined;\n }\n};\n\nconst getFetchInputUrl = (input: RequestInfo | URL) => {\n if (input instanceof Request) {\n return input.url;\n }\n return String(input);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) => {\n if (init?.method) {\n return init.method;\n }\n if (input instanceof Request) {\n return input.method;\n }\n return 'GET';\n};\n\nconst getFetchRequestBody = async (input: RequestInfo | URL, init: RequestInit | undefined, maxBodySize: number) => {\n if (init?.body) {\n return safeCreateBodySnapshot(\n init.body,\n maxBodySize,\n getHeaderValue(init.headers as Headers | undefined, 'content-type'),\n );\n }\n if (input instanceof Request) {\n try {\n return safeCreateBodySnapshot(\n await input.clone().text(),\n maxBodySize,\n input.headers.get('content-type') || undefined,\n );\n } catch {\n return undefined;\n }\n }\n return undefined;\n};\n\nconst getResponseBody = async (response: Response, maxBodySize: number) => {\n try {\n const clonedResponse = response.clone();\n return safeCreateBodySnapshot(\n clonedResponse.body ? await clonedResponse.text() : '',\n maxBodySize,\n response.headers.get('content-type') || undefined,\n );\n } catch {\n return undefined;\n }\n};\n\nconst getXhrResponseBody = (xhr: XMLHttpRequest, maxBodySize: number): BodySnapshot | undefined => {\n if (xhr.responseType === '' || xhr.responseType === 'text') {\n return {\n ...truncateBody(xhr.responseText || '', maxBodySize),\n contentType: xhr.getResponseHeader('content-type') || undefined,\n };\n }\n if (xhr.responseType === 'json') {\n return {\n ...truncateBody(JSON.stringify(xhr.response), maxBodySize),\n contentType: xhr.getResponseHeader('content-type') || undefined,\n };\n }\n return undefined;\n};\n\nconst redactBody = (\n config: Required<BkOTHttpBodyConfig>,\n payload: Omit<BkOTHttpBodyRedactPayload, 'body' | 'truncated'>,\n snapshot?: BodySnapshot,\n) => {\n if (!snapshot) {\n return undefined;\n }\n return config.redact({\n ...payload,\n body: snapshot.body,\n truncated: snapshot.truncated,\n });\n};\n\nconst reportHttpBody = ({\n context,\n duration,\n error,\n method,\n request,\n response,\n status,\n url,\n}: ReportHttpBodyOptions) => {\n const config = context.config.rum.httpBody;\n if (!config) {\n return;\n }\n\n const isError = Boolean(error) || (typeof status === 'number' && status >= 400);\n const normalizedMethod = method.toUpperCase();\n const requestBody = isError\n ? redactBody(\n config,\n {\n contentType: request?.contentType,\n method: normalizedMethod,\n status,\n type: 'request',\n url,\n },\n request,\n )\n : undefined;\n const responseBody = isError\n ? redactBody(\n config,\n {\n contentType: response?.contentType,\n method: normalizedMethod,\n status,\n type: 'response',\n url,\n },\n response,\n )\n : undefined;\n const attributes: Attributes = {\n ...context.config.getPageAttributes(),\n 'http.duration': duration,\n 'http.request.method': normalizedMethod,\n 'url.full': context.config.redactUrl(url),\n };\n\n if (typeof status === 'number') {\n attributes['http.response.status_code'] = status;\n }\n if (requestBody != null) {\n attributes['http.request.body'] = requestBody;\n }\n if (responseBody != null) {\n attributes['http.response.body'] = responseBody;\n }\n if (isError) {\n attributes['bk.rum.http_body.request.truncated'] = request?.truncated ?? false;\n attributes['bk.rum.http_body.response.truncated'] = response?.truncated ?? false;\n }\n if (error instanceof Error) {\n attributes['exception.message'] = error.message;\n attributes['exception.type'] = error.name;\n attributes['exception.stacktrace'] = error.stack ?? '';\n }\n\n context.emitLog({\n severityNumber: isError ? SeverityNumber.ERROR : SeverityNumber.INFO,\n severityText: isError ? 'ERROR' : 'INFO',\n body: isError ? 'HTTP request completed with error' : 'HTTP request completed',\n attributes,\n });\n};\n\nexport const createHttpBodyPlugin = (option: false | Required<BkOTHttpBodyConfig>): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'http-body',\n enabled: Boolean(option),\n init(context) {\n if (!option || typeof window === 'undefined') {\n return;\n }\n\n const maxBodySize = option.maxBodySize || HTTP_BODY_MAX_SIZE;\n const originalFetch = window.fetch;\n if (typeof originalFetch === 'function') {\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const url = getFetchInputUrl(input);\n if (shouldIgnoreUrl(context.config, url)) {\n return originalFetch(input, init);\n }\n const method = getFetchMethod(input, init);\n const request = await getFetchRequestBody(input, init, maxBodySize);\n const startTime = performance.now();\n try {\n const response = await originalFetch(input, init);\n const duration = performance.now() - startTime;\n const responseBody = response.status >= 400 ? await getResponseBody(response, maxBodySize) : undefined;\n reportHttpBody({\n context,\n duration,\n method,\n request,\n response: responseBody,\n status: response.status,\n url,\n });\n return response;\n } catch (error) {\n reportHttpBody({\n context,\n duration: performance.now() - startTime,\n error,\n method,\n request,\n url,\n });\n throw error;\n }\n };\n teardownCallbacks.push(() => {\n window.fetch = originalFetch;\n });\n }\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n XMLHttpRequest.prototype.open = function open(\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n ...args: [async?: boolean, username?: null | string, password?: null | string]\n ) {\n this.__bkOtHttpBodyMeta__ = {\n method,\n startTime: performance.now(),\n url: String(url),\n };\n return originalOpen.call(this, method, url, ...args);\n };\n XMLHttpRequest.prototype.setRequestHeader = function setRequestHeader(\n this: XMLHttpRequest,\n name: string,\n value: string,\n ) {\n this.__bkOtHttpBodyRequestHeaders__ = {\n ...(this.__bkOtHttpBodyRequestHeaders__ ?? {}),\n [name]: value,\n };\n return originalSetRequestHeader.call(this, name, value);\n };\n XMLHttpRequest.prototype.send = function send(\n this: XMLHttpRequest,\n body?: Document | null | XMLHttpRequestBodyInit,\n ) {\n void safeCreateBodySnapshot(\n body,\n maxBodySize,\n getHeaderValue(this.__bkOtHttpBodyRequestHeaders__, 'content-type'),\n ).then(request => {\n this.__bkOtHttpBodyRequest__ = request;\n });\n this.addEventListener('loadend', () => {\n const meta = this.__bkOtHttpBodyMeta__;\n if (!meta || shouldIgnoreUrl(context.config, meta.url)) {\n return;\n }\n const responseBody = this.status >= 400 ? getXhrResponseBody(this, maxBodySize) : undefined;\n reportHttpBody({\n context,\n duration: performance.now() - meta.startTime,\n method: meta.method,\n request: this.__bkOtHttpBodyRequest__,\n response: responseBody,\n status: this.status,\n url: meta.url,\n });\n });\n return originalSend.call(this, body);\n };\n teardownCallbacks.push(() => {\n XMLHttpRequest.prototype.open = originalOpen;\n XMLHttpRequest.prototype.send = originalSend;\n XMLHttpRequest.prototype.setRequestHeader = originalSetRequestHeader;\n });\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n\ndeclare global {\n interface XMLHttpRequest {\n __bkOtHttpBodyRequest__?: BodySnapshot;\n __bkOtHttpBodyRequestHeaders__?: Record<string, string>;\n __bkOtHttpBodyMeta__?: {\n method: string;\n startTime: number;\n url: string;\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 { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_THRESHOLD_MS = 50;\n\ninterface LongTaskAttribution {\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n}\n\ninterface LongTaskEntry extends PerformanceEntry {\n attribution?: LongTaskAttribution[];\n}\n\n/**\n * Long Task 监控:通过 PerformanceObserver type=longtask 捕获 >= threshold 的主线程长任务。\n * 默认关闭,建议在性能敏感模块按需开启。\n */\nexport const createLongTaskPlugin = (option: BkOTRumConfig['longTask']): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'long-task',\n enabled: Boolean(option),\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n const entryTypes = (PerformanceObserver as unknown as { supportedEntryTypes?: string[] }).supportedEntryTypes;\n if (!entryTypes?.includes('longtask')) {\n return;\n }\n\n const threshold = typeof option === 'object' ? (option.threshold ?? DEFAULT_THRESHOLD_MS) : DEFAULT_THRESHOLD_MS;\n const counter = context.meter.createCounter('browser.long_task.count', {\n description: 'Number of long tasks observed (duration >= threshold)',\n });\n const durationHistogram = context.meter.createHistogram('browser.long_task.duration', {\n unit: 'ms',\n description: 'Duration of long tasks',\n });\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as LongTaskEntry[]) {\n if (entry.duration < threshold) {\n continue;\n }\n const attribution = entry.attribution?.[0];\n const dimensions = context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n 'long_task.name': entry.name,\n 'long_task.attribution': attribution?.name ?? 'unknown',\n });\n counter.add(1, dimensions);\n durationHistogram.record(entry.duration, dimensions);\n }\n });\n observer.observe({ type: 'longtask', 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 * 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\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 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\nimport { subscribeRouteChange } from '../core/route-observer';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nexport const createPageViewPlugin = (enabled: BkOTRumConfig['pageView']): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // 同 URL 去重:路由库常常因 query/hash 微调反复 push 同一 URL,去重避免重复上报\n let lastUrl = '';\n\n const emitPageView = (source: string, previousUrl = lastUrl) => {\n const currentUrl = getCurrentUrl();\n if (currentUrl === lastUrl) {\n return;\n }\n lastUrl = currentUrl;\n\n const span = context.startSpan('browser.page_view', {\n ...context.config.getPageAttributes(),\n 'url.full': context.config.redactUrl(currentUrl),\n 'url.previous': context.config.redactUrl(previousUrl),\n 'document.referrer': context.config.redactUrl(document.referrer || ''),\n 'bk.rum.event.source': source,\n });\n span.end();\n };\n\n const unsubscribe = subscribeRouteChange(event => {\n emitPageView(event.source, event.fromUrl);\n });\n emitPageView('load');\n\n teardownCallbacks.push(unsubscribe);\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 { subscribeRouteChange } from '../core/route-observer';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 估算 SPA 路由切换耗时:路由变更 → 双 raf + 一个宏任务后视为\"渲染完成\"。\n * 不依赖具体框架,但仅是粗粒度估算;如需精确,业务可走自定义 plugin 在框架钩子内 startSpan/end。\n */\nexport const createRouteTimingPlugin = (enabled: BkOTRumConfig['routeTiming']): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'route-timing',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n const histogram = context.meter.createHistogram('browser.route_change.duration', {\n unit: 'ms',\n description: 'Estimated SPA route change duration (route start to next idle)',\n });\n\n const measure = (source: string, fromUrl: string, toUrl: string) => {\n const startedAt = performance.now();\n const finalize = () => {\n const duration = performance.now() - startedAt;\n const dimensions = context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n 'route.change.source': source,\n });\n histogram.record(duration, dimensions);\n const span = context.startSpan('browser.route_change', {\n ...dimensions,\n 'url.full': context.config.redactUrl(toUrl),\n 'url.previous': context.config.redactUrl(fromUrl),\n 'route.change.duration_ms': duration,\n });\n span.end();\n };\n // 双 raf 等到下一帧渲染后,再补一个宏任务,覆盖大多数同步组件挂载场景\n requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(finalize, 0)));\n };\n\n const unsubscribe = subscribeRouteChange(event => {\n if (event.fromUrl === event.toUrl) {\n return;\n }\n measure(event.source, event.fromUrl, event.toUrl);\n });\n\n teardownCallbacks.push(unsubscribe);\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 { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_SESSION_STORAGE_KEY = 'bk_ot_session';\nconst DEFAULT_INACTIVITY_MS = 30 * 60 * 1000;\n\ninterface SessionRecord {\n id: string;\n // 会话上次活跃时间戳\n ts: number;\n}\n\nconst createId = () => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `session-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n\nconst readRecord = (storageKey: string): null | SessionRecord => {\n try {\n const raw = window.sessionStorage.getItem(storageKey) ?? window.localStorage.getItem(storageKey);\n if (!raw) {\n return null;\n }\n const parsed = JSON.parse(raw) as SessionRecord;\n if (typeof parsed?.id === 'string' && typeof parsed?.ts === 'number') {\n return parsed;\n }\n return null;\n } catch {\n return null;\n }\n};\n\nconst writeRecord = (storageKey: string, record: SessionRecord) => {\n try {\n const serialized = JSON.stringify(record);\n window.sessionStorage.setItem(storageKey, serialized);\n // 同时写到 localStorage,让多 tab / 关闭页后短期内复用同一个 session\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n /* storage 不可用时忽略,运行期仍能继续 */\n }\n};\n\nconst resolveSessionId = (storageKey: string, inactivityMs: number) => {\n const now = Date.now();\n const existed = readRecord(storageKey);\n if (existed && now - existed.ts < inactivityMs) {\n const record: SessionRecord = { id: existed.id, ts: now };\n writeRecord(storageKey, record);\n return record.id;\n }\n const record: SessionRecord = { id: createId(), ts: now };\n writeRecord(storageKey, record);\n return record.id;\n};\n\n/**\n * 会话级标识:带不活跃过期时间,超过 inactivityMs 视为新会话。\n * 与 device 插件互补:device 是设备终身标识,session 反映\"用户连续访问\"语义。\n */\nexport const createSessionPlugin = (option: BkOTRumConfig['session']): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'session',\n enabled: Boolean(option),\n init(context) {\n const storageKey =\n typeof option === 'object' ? (option.storageKey ?? DEFAULT_SESSION_STORAGE_KEY) : DEFAULT_SESSION_STORAGE_KEY;\n const inactivityMs =\n typeof option === 'object' ? (option.inactivityMs ?? DEFAULT_INACTIVITY_MS) : DEFAULT_INACTIVITY_MS;\n\n const refresh = () => {\n const sessionId = typeof window === 'undefined' ? createId() : resolveSessionId(storageKey, inactivityMs);\n context.setRuntimeAttributes({ 'session.id': sessionId });\n };\n\n refresh();\n\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n // 用户回到页面时检查是否过期,过期则轮换 sessionId\n const onVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n refresh();\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n teardown = () => document.removeEventListener('visibilitychange', onVisibilityChange);\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\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\ninterface VitalLike {\n attribution?: Record<string, unknown>;\n id?: string;\n name: string;\n rating?: string;\n value: number;\n}\n\nconst getNavigationType = () => {\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n return navigation?.type || 'unknown';\n};\n\n// 仅挑选低基数、可枚举的字段进 metric attributes,避免指标维度爆炸\nconst getMetricDimensions = (metric: VitalLike): Attributes => ({\n 'rum.navigation.type': getNavigationType(),\n 'web_vital.name': metric.name,\n 'web_vital.rating': metric.rating,\n});\n\n// 把 attribution 中价值最高的字段抽出来(仅放进 span/log,不放进 metric)\nconst pickAttribution = (metric: VitalLike, redactUrl: (url: string) => string): Attributes => {\n const attr = metric.attribution ?? {};\n const result: Attributes = {};\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('web_vital.lcp.url', attr.url, redactUrl);\n setIfDefined('web_vital.lcp.target', attr.target);\n setIfDefined('web_vital.lcp.element_render_delay', attr.elementRenderDelay);\n setIfDefined('web_vital.lcp.resource_load_duration', attr.resourceLoadDuration);\n setIfDefined('web_vital.lcp.time_to_first_byte', attr.timeToFirstByte);\n break;\n case 'CLS':\n setIfDefined('web_vital.cls.largest_shift_target', attr.largestShiftTarget);\n setIfDefined('web_vital.cls.largest_shift_value', attr.largestShiftValue);\n setIfDefined('web_vital.cls.load_state', attr.loadState);\n break;\n case 'INP':\n setIfDefined('web_vital.inp.interaction_target', attr.interactionTarget);\n setIfDefined('web_vital.inp.interaction_type', attr.interactionType);\n setIfDefined('web_vital.inp.input_delay', attr.inputDelay);\n setIfDefined('web_vital.inp.processing_duration', attr.processingDuration);\n setIfDefined('web_vital.inp.presentation_delay', attr.presentationDelay);\n break;\n case 'FCP':\n setIfDefined('web_vital.fcp.time_to_first_byte', attr.timeToFirstByte);\n setIfDefined('web_vital.fcp.load_state', attr.loadState);\n break;\n case 'TTFB':\n setIfDefined('web_vital.ttfb.waiting_duration', attr.waitingDuration);\n setIfDefined('web_vital.ttfb.dns_duration', attr.dnsDuration);\n setIfDefined('web_vital.ttfb.connection_duration', attr.connectionDuration);\n setIfDefined('web_vital.ttfb.request_duration', attr.requestDuration);\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (enabled: BkOTRumConfig['webVitals']): BkOTPlugin => ({\n name: 'web-vitals',\n enabled: Boolean(enabled),\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const { onCLS, onFCP, onINP, onLCP, onTTFB } = await import('web-vitals/attribution');\n const clsHistogram = context.meter.createHistogram('browser.web_vital.cls', {\n unit: '1',\n description: 'Cumulative Layout Shift',\n });\n const durationHistogram = context.meter.createHistogram('browser.web_vital.duration', {\n unit: 'ms',\n description: 'Web Vitals duration metrics, including FCP, INP, LCP and TTFB',\n });\n\n const recordSpan = (metric: VitalLike) => {\n const span = context.startSpan('browser.web_vital', {\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n // 单条度量唯一 ID 仅放进 span,避免污染 metric 维度\n 'web_vital.id': metric.id,\n 'web_vital.value': metric.value,\n ...pickAttribution(metric, context.config.redactUrl),\n });\n span.end();\n };\n\n const recordDurationMetric = (metric: VitalLike) => {\n durationHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n })\n );\n recordSpan(metric);\n };\n\n onCLS(metric => {\n clsHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n })\n );\n recordSpan(metric);\n });\n onFCP(recordDurationMetric);\n onINP(recordDurationMetric);\n onLCP(recordDurationMetric);\n onTTFB(recordDurationMetric);\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 { SpanStatusCode } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { shouldIgnoreUrl } from '../core/url';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst getMessageByteLength = (data: unknown): number => {\n if (data == null) return 0;\n if (typeof data === 'string') {\n return typeof TextEncoder === 'undefined' ? data.length : new TextEncoder().encode(data).byteLength;\n }\n if (data instanceof ArrayBuffer) return data.byteLength;\n if (ArrayBuffer.isView(data)) return data.byteLength;\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.size;\n return 0;\n};\n\nconst getWebSocketAttributes = (url: string, redactUrl: (url: string) => string) => {\n const spanAttributes: Record<string, string> = {\n 'url.full': redactUrl(url),\n 'network.protocol.name': 'websocket',\n };\n\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n spanAttributes['server.address'] = parsed.host;\n } catch {\n /* ignore malformed URL and keep the raw, redacted URL only */\n }\n\n return {\n spanAttributes,\n metricAttributes: {\n 'network.protocol.name': 'websocket',\n },\n };\n};\n\nexport const createWebSocketPlugin = (enabled: BkOTRumConfig['websocket']): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n\n return {\n name: 'websocket',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {\n return;\n }\n\n const NativeWebSocket = window.WebSocket;\n originalWebSocket = NativeWebSocket;\n\n const messageCounter = context.meter.createCounter('browser.websocket.message.count', {\n description: 'Total number of WebSocket messages observed',\n });\n const bytesCounter = context.meter.createCounter('browser.websocket.message.bytes', {\n unit: 'By',\n description: 'Total bytes transferred over WebSocket (best-effort)',\n });\n const errorCounter = context.meter.createCounter('browser.websocket.error.count', {\n description: 'Total number of WebSocket error events',\n });\n\n const PatchedWebSocket = function (this: WebSocket, url: string | URL, protocols?: string | string[]) {\n const urlValue = url.toString();\n\n // 用户主动 ignore 或上报 endpoint,避免回环监控\n if (shouldIgnoreUrl(context.config, urlValue)) {\n return protocols === undefined ? new NativeWebSocket(url) : new NativeWebSocket(url, protocols);\n }\n\n const { metricAttributes, spanAttributes } = getWebSocketAttributes(urlValue, context.config.redactUrl);\n // connect span 只覆盖\"建立连接\"阶段,避免长连接导致 span 永远不结束\n const connectSpan = context.startSpan('websocket.connect', spanAttributes);\n const startTime = performance.now();\n const socket = protocols === undefined ? new NativeWebSocket(url) : new NativeWebSocket(url, protocols);\n let connectEnded = false;\n\n const endConnectSpan = (status: 'error' | 'opened') => {\n if (connectEnded) return;\n connectEnded = true;\n if (status === 'error') {\n connectSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'websocket connect failed' });\n }\n connectSpan.setAttribute('websocket.connect.duration_ms', performance.now() - startTime);\n connectSpan.end();\n };\n\n socket.addEventListener('open', () => endConnectSpan('opened'));\n\n socket.addEventListener('message', event => {\n messageCounter.add(1, { ...metricAttributes, 'websocket.direction': 'in' });\n bytesCounter.add(getMessageByteLength(event.data), {\n ...metricAttributes,\n 'websocket.direction': 'in',\n });\n });\n\n socket.addEventListener('error', () => {\n errorCounter.add(1, metricAttributes);\n endConnectSpan('error');\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'websocket.error',\n attributes: spanAttributes,\n });\n });\n\n socket.addEventListener('close', event => {\n endConnectSpan('opened');\n context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: 'websocket.close',\n attributes: {\n ...spanAttributes,\n 'websocket.close.code': event.code,\n 'websocket.close.reason': event.reason,\n 'websocket.close.was_clean': event.wasClean,\n },\n });\n });\n\n // 计量发送方向(无法拦截 send 的回调,所以包一层 send 方法)\n const originalSend = socket.send.bind(socket);\n socket.send = (data: ArrayBufferLike | ArrayBufferView | Blob | string) => {\n messageCounter.add(1, { ...metricAttributes, 'websocket.direction': 'out' });\n bytesCounter.add(getMessageByteLength(data), {\n ...metricAttributes,\n 'websocket.direction': 'out',\n });\n return originalSend(data);\n };\n\n return socket;\n } as unknown as typeof WebSocket;\n\n // 复制原型与静态常量(CONNECTING / OPEN / CLOSING / CLOSED 等),避免业务侧 WebSocket.OPEN 失效\n PatchedWebSocket.prototype = NativeWebSocket.prototype;\n Object.assign(PatchedWebSocket, NativeWebSocket);\n\n window.WebSocket = PatchedWebSocket;\n },\n shutdown() {\n if (originalWebSocket && typeof window !== 'undefined') {\n window.WebSocket = originalWebSocket;\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 { NormalizedBkOTConfig } from './config';\n\nconst SAMPLE_STORAGE_KEY = '__bk_ot_sampled__';\n\n// 在 sessionStorage 不可用时降级到模块内单例缓存,避免同会话内多次结果不一致\nconst memoryCache = new Map<string, boolean>();\n\nconst readMemory = (key: string) => memoryCache.get(key);\nconst writeMemory = (key: string, value: boolean) => {\n memoryCache.set(key, value);\n return value;\n};\n\nexport const isBkOTSampled = ({ sampleRate }: Pick<NormalizedBkOTConfig, 'sampleRate'>) => {\n if (sampleRate <= 0) {\n return false;\n }\n if (sampleRate >= 1) {\n return true;\n }\n\n try {\n const cached = sessionStorage.getItem(SAMPLE_STORAGE_KEY);\n if (cached) {\n return cached === '1';\n }\n const sampled = Math.random() < sampleRate;\n sessionStorage.setItem(SAMPLE_STORAGE_KEY, sampled ? '1' : '0');\n writeMemory(SAMPLE_STORAGE_KEY, sampled);\n return sampled;\n } catch {\n const cached = readMemory(SAMPLE_STORAGE_KEY);\n if (typeof cached === 'boolean') {\n return cached;\n }\n return writeMemory(SAMPLE_STORAGE_KEY, Math.random() < sampleRate);\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\nimport {\n type Attributes,\n type Span,\n context,\n metrics,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace,\n} from '@opentelemetry/api';\nimport { logs } from '@opentelemetry/api-logs';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';\nimport { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { BatchSpanProcessor, ParentBasedSampler, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\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 { createHttpBodyPlugin } from '../plugins/http-body';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createRouteTimingPlugin } from '../plugins/route-timing';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { type BkOTConfig, type BkOTCustomEventPayload, type NormalizedBkOTConfig, normalizeConfig } from './config';\nimport { type BkOTPlugin, type BkOTRuntimeContext, PluginManager } from './plugin';\nimport { FilteringSpanProcessor } from './processor';\nimport { isBkOTSampled } from './sampling';\n\nimport type { LogRecord } from '@opentelemetry/api-logs';\n\nexport interface BkOTInstance extends BkOTRuntimeContext {\n flush: () => Promise<void>;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n}\n\nconst createExporterOptions = (config: NormalizedBkOTConfig['traces']) => ({\n url: config.endpoint,\n headers: config.headers,\n concurrencyLimit: config.concurrencyLimit,\n timeoutMillis: config.timeoutMillis,\n});\n\ntype DebugSignal = 'logs' | 'metrics' | 'traces';\n\nconst BK_OT_INSTRUMENTATION_NAME = 'bk-rum';\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n signal: DebugSignal,\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][${signal}]`, 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 注入 W3C traceparent 头;仅 SDK 启用时装载,避免 disabled 状态仍 patch 全局 API\nconst getCorePlugins = (config: NormalizedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.instrumentations),\n];\n\n// 仅在采样命中时启用的\"会主动产生数据\"的 RUM 插件\nconst getRumPlugins = (config: NormalizedBkOTConfig): BkOTPlugin[] => [\n createDevicePlugin(config.rum.device),\n createHttpBodyPlugin(config.rum.httpBody),\n createSessionPlugin(config.rum.session),\n createPageViewPlugin(config.rum.pageView),\n createErrorPlugin(config.rum.error),\n createWebVitalsPlugin(config.rum.webVitals),\n createBlankScreenPlugin(config.rum.blankScreen),\n createWebSocketPlugin(config.rum.websocket),\n createLongTaskPlugin(config.rum.longTask),\n createCspViolationPlugin(config.rum.cspViolation),\n createRouteTimingPlugin(config.rum.routeTiming),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'stopped';\n\nexport const createBkOT = (inputConfig: BkOTConfig): BkOTInstance => {\n const config = normalizeConfig(inputConfig);\n const sampled = config.enabled && isBkOTSampled(config);\n const resource = resourceFromAttributes(config.resourceAttributes);\n const spanExporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(config.traces)),\n 'traces',\n config.debug,\n );\n const metricExporter = wrapWithDebug(\n new OTLPMetricExporter(createExporterOptions(config.metrics)),\n 'metrics',\n config.debug,\n );\n const logExporter = wrapWithDebug(new OTLPLogExporter(createExporterOptions(config.logs)), 'logs', config.debug);\n const batchSpanProcessor = new BatchSpanProcessor(spanExporter, config.spanBatch);\n const spanProcessor = new FilteringSpanProcessor(batchSpanProcessor, config);\n const metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: config.metricIntervalMillis,\n });\n const logProcessor = new BatchLogRecordProcessor(logExporter);\n const tracerProvider = new WebTracerProvider({\n resource,\n sampler: new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(sampled ? 1 : 0),\n }),\n spanProcessors: [spanProcessor],\n });\n const meterProvider = new MeterProvider({\n resource,\n readers: [metricReader],\n });\n const loggerProvider = new LoggerProvider({\n resource,\n processors: [logProcessor],\n });\n const tracer = tracerProvider.getTracer(BK_OT_INSTRUMENTATION_NAME);\n const meter = meterProvider.getMeter(BK_OT_INSTRUMENTATION_NAME);\n const logger = loggerProvider.getLogger(BK_OT_INSTRUMENTATION_NAME);\n const runtimeAttributes: Attributes = {\n 'bk.rum.sampled': sampled,\n };\n // 核心插件在 SDK 启用时装载;RUM 数据上报类插件仅在采样命中时装载\n const pluginManager = new PluginManager([\n ...(config.enabled ? getCorePlugins(config) : []),\n ...(sampled ? [...getRumPlugins(config), ...config.plugins] : []),\n ]);\n const teardownCallbacks: Array<() => void> = [];\n let phase: SdkLifecyclePhase = 'idle';\n\n const getRuntimeAttributes = () => ({ ...runtimeAttributes });\n const setRuntimeAttributes = (attributes: Attributes) => {\n Object.assign(runtimeAttributes, attributes);\n };\n const applyRedact = (attributes: Attributes) => config.redactAttributes(attributes);\n const startSpan = (name: string, attributes: Attributes = {}): Span =>\n tracer.startSpan(name, {\n kind: SpanKind.INTERNAL,\n attributes: applyRedact({\n ...getRuntimeAttributes(),\n ...attributes,\n }),\n });\n const emitLog = (record: LogRecord) => {\n const merged: LogRecord = {\n ...record,\n attributes: applyRedact({\n ...getRuntimeAttributes(),\n ...((record.attributes ?? {}) as Attributes),\n }),\n };\n logger.emit(merged);\n };\n\n const instance: BkOTInstance = {\n config,\n tracer,\n meter,\n logger,\n applyRedact,\n emitLog,\n getRuntimeAttributes,\n setRuntimeAttributes,\n startSpan,\n reportCustomEvent({ attributes = {}, error, name }) {\n if (!sampled) {\n // 未采样时静默丢弃;如需调试请把 debug 选项打开\n if (config.debug) {\n globalThis.console?.warn('[bk-ot] custom event dropped (not sampled):', name);\n }\n return;\n }\n\n const span = startSpan(`custom.${name}`, {\n ...config.getPageAttributes(),\n ...config.getCustomAttributes(),\n ...attributes,\n 'rum.custom.name': name,\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();\n },\n async start() {\n if (phase === 'started') {\n return;\n }\n if (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 (!config.enabled) {\n phase = 'started';\n return;\n }\n propagation.setGlobalPropagator(new W3CTraceContextPropagator());\n tracerProvider.register({\n contextManager: new ZoneContextManager(),\n });\n metrics.setGlobalMeterProvider(meterProvider);\n logs.setGlobalLoggerProvider(loggerProvider);\n try {\n await pluginManager.start(instance);\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const flushOnPageHide = () => {\n void instance.flush();\n };\n const flushOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n void instance.flush();\n }\n };\n window.addEventListener('pagehide', flushOnPageHide);\n document.addEventListener('visibilitychange', flushOnHidden);\n teardownCallbacks.push(\n () => window.removeEventListener('pagehide', flushOnPageHide),\n () => document.removeEventListener('visibilitychange', flushOnHidden),\n );\n }\n } catch (error) {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n await pluginManager.shutdown();\n trace.disable();\n metrics.disable();\n context.disable();\n throw error;\n }\n phase = 'started';\n },\n async flush() {\n await pluginManager.flush();\n await tracerProvider.forceFlush();\n await meterProvider.forceFlush();\n await loggerProvider.forceFlush();\n },\n async shutdown() {\n if (phase === 'stopped') {\n return;\n }\n await instance.flush();\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n await pluginManager.shutdown();\n await tracerProvider.shutdown();\n await meterProvider.shutdown();\n await loggerProvider.shutdown();\n trace.disable();\n metrics.disable();\n context.disable();\n phase = 'stopped';\n },\n };\n\n if (config.autoStart) {\n void instance.start().catch(error => {\n globalThis.console?.warn('[bk-ot] auto start failed:', error);\n });\n }\n\n return instance;\n};\n\nexport const initBkOT = createBkOT;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/core/config.ts","../src/core/plugin.ts","../src/core/url.ts","../src/core/processor.ts","../src/plugins/blank-screen.ts","../src/plugins/common.ts","../src/plugins/csp-violation.ts","../src/plugins/device.ts","../src/plugins/error.ts","../src/plugins/http-body.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/route-timing.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/sampling.ts","../src/core/sdk.ts"],"sourcesContent":["/*\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 { BkOTPlugin } from './plugin';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n metric?: () => 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 BkOTConfig {\n attributes?: BkOTAttributesConfig;\n autoStart?: boolean;\n debug?: boolean;\n enabled?: boolean;\n endpoint?: string;\n environment?: string;\n headers?: Record<string, string>;\n instrumentations?: Instrumentation[];\n logs?: SignalExporterInputConfig;\n metrics?: SignalExporterInputConfig;\n plugins?: BkOTPlugin[];\n redact?: BkOTRedactConfig;\n rum?: BkOTRumConfig;\n sampleRate?: number;\n signalEndpoints?: SignalEndpointsConfig;\n spanBatch?: BkOTBatchConfig;\n token?: string;\n traces?: SignalExporterInputConfig;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTHttpBodyConfig {\n maxBodySize?: number;\n redact?: (payload: BkOTHttpBodyRedactPayload) => string;\n}\n\nexport interface BkOTHttpBodyRedactPayload {\n body: string;\n contentType?: string;\n method: string;\n status?: number;\n truncated: boolean;\n type: 'request' | 'response';\n url: string;\n}\n\nexport interface BkOTInstrumentationsConfig {\n documentLoad?: boolean;\n fetch?: boolean;\n userInteraction?: boolean | { eventNames?: string[] };\n xhr?: boolean;\n}\n\nexport interface BkOTRedactConfig {\n // 用于隐私脱敏:在所有 RUM 自定义插件 emit 之前过滤一次属性\n attributes?: (attributes: Attributes) => Attributes;\n // 用于隐私脱敏:所有上报到 attribute 的 URL 在写入前会经过此函数\n url?: (url: string) => string;\n}\n\nexport interface BkOTRumConfig extends BkOTInstrumentationsConfig {\n cspViolation?: boolean;\n device?: boolean | { storageKey?: string };\n httpBody?: BkOTHttpBodyConfig | boolean;\n pageView?: boolean;\n routeTiming?: boolean;\n websocket?: boolean;\n webVitals?: boolean;\n blankScreen?:\n | {\n checkDelay?: number;\n // 自定义\"页面已渲染\"忽略选择器,命中即认为是 loading 占位、非空白\n ignoreSelectors?: string[];\n rootSelector?: string;\n threshold?: number;\n }\n | boolean;\n error?:\n | {\n // 同 hash 错误窗口内最多上报多少条,默认 5\n maxPerWindow?: number;\n // 节流窗口长度(ms),默认 60_000\n windowMs?: number;\n }\n | boolean;\n longTask?:\n | {\n // 任务时长阈值(ms),低于该值的 longtask 不上报,默认 50\n threshold?: number;\n }\n | boolean;\n session?:\n | {\n // 不活跃多久后视为新会话,默认 30 分钟\n inactivityMs?: number;\n storageKey?: string;\n }\n | boolean;\n}\n\nexport interface NormalizedBkOTConfig extends Omit<\n BkOTConfig,\n | 'attributes'\n | 'autoStart'\n | 'debug'\n | 'enabled'\n | 'endpoint'\n | 'environment'\n | 'instrumentations'\n | 'logs'\n | 'metrics'\n | 'plugins'\n | 'redact'\n | 'signalEndpoints'\n | 'token'\n | 'traces'\n> {\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n endpoint: string;\n environment: string;\n instrumentations: Instrumentation[];\n logs: SignalExporterConfig;\n metricIntervalMillis: number;\n metrics: SignalExporterConfig;\n plugins: BkOTPlugin[];\n resourceAttributes: Attributes;\n sampleRate: number;\n traces: SignalExporterConfig;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getMetricAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n commonInstrumentations: Required<Pick<BkOTInstrumentationsConfig, 'documentLoad' | 'fetch' | 'xhr'>> & {\n userInteraction: BkOTInstrumentationsConfig['userInteraction'];\n };\n rum: Omit<Required<BkOTRumConfig>, 'documentLoad' | 'fetch' | 'httpBody' | 'userInteraction' | 'xhr'> & {\n httpBody: false | Required<BkOTHttpBodyConfig>;\n };\n}\n\nexport interface SignalEndpointsConfig {\n logs?: string;\n metrics?: string;\n traces?: string;\n}\n\nexport interface SignalExporterConfig {\n concurrencyLimit?: number;\n endpoint: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n}\n\nexport type SignalExporterInputConfig = Partial<SignalExporterConfig>;\n\nconst DEFAULT_OTLP_ENDPOINT = 'http://localhost:4318'; // 默认上报到本机 OTLP collector\nconst DEFAULT_METRIC_INTERVAL_MILLIS = 60 * 1000; // 10s 上报一次\n\nconst trimTrailingSlash = (value: string) => value.replace(/\\/+$/, '');\n\nconst resolveSignalEndpoint = (endpoint: string | undefined, signalPath: 'logs' | 'metrics' | 'traces') => {\n const normalized = trimTrailingSlash(endpoint || DEFAULT_OTLP_ENDPOINT);\n if (normalized.endsWith(`/v1/${signalPath}`)) {\n return normalized;\n }\n if (normalized.endsWith('/v1')) {\n return `${normalized}/${signalPath}`;\n }\n return `${normalized}/v1/${signalPath}`;\n};\n\nconst resolveSignalConfig = (base: BkOTConfig, signalPath: 'logs' | 'metrics' | 'traces'): SignalExporterConfig => {\n const signalConfig = base[signalPath];\n const signalEndpoint = base.signalEndpoints?.[signalPath];\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n base.token || base.headers || signalConfig?.headers\n ? {\n ...(base.token ? { Authorization: `Bearer ${base.token}` } : {}),\n ...(base.headers ?? {}),\n ...(signalConfig?.headers ?? {}),\n }\n : undefined;\n\n return {\n concurrencyLimit: signalConfig?.concurrencyLimit,\n endpoint: signalEndpoint\n ? trimTrailingSlash(signalEndpoint)\n : resolveSignalEndpoint(signalConfig?.endpoint ?? base.endpoint, signalPath),\n headers: mergedHeaders,\n timeoutMillis: signalConfig?.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 positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\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 if (typeof value !== 'number' || !Number.isFinite(value)) {\n return fallback;\n }\n return Math.max(0, Math.min(1, value));\n};\n\nconst identityRedactHttpBody = (payload: BkOTHttpBodyRedactPayload) => payload.body;\n\nconst normalizeHttpBodyConfig = (httpBody: BkOTRumConfig['httpBody']): false | Required<BkOTHttpBodyConfig> => {\n if (!httpBody) {\n return false;\n }\n if (typeof httpBody === 'boolean') {\n return {\n maxBodySize: 10 * 1024,\n redact: identityRedactHttpBody,\n };\n }\n return {\n maxBodySize: positiveNumber(httpBody.maxBodySize, 10 * 1024),\n redact: httpBody.redact ?? identityRedactHttpBody,\n };\n};\n\nconst normalizeRumConfig = (rum: BkOTConfig['rum']): NormalizedBkOTConfig['rum'] => ({\n device: rum?.device ?? true,\n httpBody: normalizeHttpBodyConfig(rum?.httpBody),\n session:\n typeof rum?.session === 'object'\n ? {\n ...rum.session,\n inactivityMs: positiveNumber(rum.session.inactivityMs, 30 * 60 * 1000),\n }\n : (rum?.session ?? true),\n pageView: rum?.pageView ?? true,\n error:\n typeof rum?.error === 'object'\n ? {\n ...rum.error,\n maxPerWindow: positiveNumber(rum.error.maxPerWindow, 5),\n windowMs: positiveNumber(rum.error.windowMs, 60_000),\n }\n : (rum?.error ?? true),\n webVitals: rum?.webVitals ?? true,\n blankScreen:\n typeof rum?.blankScreen === 'object'\n ? {\n ...rum.blankScreen,\n checkDelay: nonNegativeNumber(rum.blankScreen.checkDelay, 3000),\n threshold: boundedRatio(rum.blankScreen.threshold, 0.8),\n }\n : (rum?.blankScreen ?? true),\n websocket: rum?.websocket ?? true,\n longTask:\n typeof rum?.longTask === 'object'\n ? {\n ...rum.longTask,\n threshold: nonNegativeNumber(rum.longTask.threshold, 50),\n }\n : (rum?.longTask ?? false),\n cspViolation: rum?.cspViolation ?? false,\n routeTiming: rum?.routeTiming ?? false,\n});\n\nconst identityRedactAttributes = (attributes: Attributes) => attributes;\nconst identityRedactUrl = (url: string) => url;\n\nexport const normalizeConfig = (config: BkOTConfig): NormalizedBkOTConfig => {\n const resourceAttributes: Attributes = {\n 'deployment.environment.name': config.environment ?? 'production',\n 'rum.provider': 'blueking',\n 'telemetry.sdk.language': 'webjs',\n };\n\n return {\n ...config,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n environment: config.environment ?? 'production',\n endpoint: trimTrailingSlash(config.endpoint || DEFAULT_OTLP_ENDPOINT),\n traces: resolveSignalConfig(config, 'traces'),\n metrics: resolveSignalConfig(config, 'metrics'),\n logs: resolveSignalConfig(config, 'logs'),\n sampleRate: clampSampleRate(config.sampleRate),\n resourceAttributes,\n metricIntervalMillis: DEFAULT_METRIC_INTERVAL_MILLIS,\n instrumentations: config.instrumentations ?? [],\n commonInstrumentations: {\n documentLoad: config.rum?.documentLoad ?? true,\n fetch: config.rum?.fetch ?? true,\n xhr: config.rum?.xhr ?? true,\n userInteraction: config.rum?.userInteraction ?? true,\n },\n rum: normalizeRumConfig(config.rum),\n plugins: config.plugins ?? [],\n autoStart: config.autoStart ?? true,\n getPageAttributes:\n config.attributes?.page ??\n (() => ({\n 'rum.page.host': typeof window === 'undefined' ? '' : window.location.host,\n 'rum.page.path': typeof window === 'undefined' ? '' : window.location.pathname,\n })),\n getMetricAttributes: config.attributes?.metric ?? (() => ({})),\n getErrorAttributes: config.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.attributes?.custom ?? (() => ({})),\n redactAttributes: config.redact?.attributes ?? identityRedactAttributes,\n redactUrl: config.redact?.url ?? identityRedactUrl,\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 { NormalizedBkOTConfig } from './config';\nimport type { Attributes, Meter, Span, Tracer } from '@opentelemetry/api';\nimport type { Logger, LogRecord } from '@opentelemetry/api-logs';\n\nexport interface BkOTPlugin {\n enabled?: ((config: NormalizedBkOTConfig) => 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: NormalizedBkOTConfig;\n logger: Logger;\n meter: Meter;\n tracer: Tracer;\n /** 对外提供的属性脱敏入口,所有插件在 emit 前都应过一次 */\n applyRedact: (attributes: Attributes) => Attributes;\n /** 包装 logger.emit,自动应用 redact 与 runtime attributes */\n emitLog: (record: LogRecord) => void;\n getRuntimeAttributes: () => Attributes;\n setRuntimeAttributes: (attributes: Attributes) => void;\n startSpan: (name: string, attributes?: Attributes) => Span;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: NormalizedBkOTConfig) => {\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 {\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n }\n } catch (error) {\n await this.shutdown();\n throw error;\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 { NormalizedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nconst 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\nconst isSameOrigin = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).origin === getBaseUrl();\n } catch {\n return false;\n }\n};\n\nexport const isBkOTEndpoint = (config: NormalizedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n\n return [config.traces.endpoint, config.metrics.endpoint, config.logs.endpoint].some(endpoint =>\n isSameResource(targetUrl, toAbsoluteUrl(endpoint))\n );\n};\n\nexport const shouldIgnoreUrl = (config: NormalizedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nexport const shouldPropagateTraceHeader = (_config: NormalizedBkOTConfig, url: string) => isSameOrigin(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 { isBkOTEndpoint } from './url';\n\nimport type { NormalizedBkOTConfig } from './config';\nimport type { Context } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\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\n/**\n * 包装 SpanProcessor,在 onEnd 阶段过滤指向自身上报 endpoint 的 span,\n * 兜底防止 official fetch/xhr 拦截器对 OTel 自身请求产生回环 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: NormalizedBkOTConfig,\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 (url && isBkOTEndpoint(this.config, url)) {\n return;\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\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) 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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_SELECTOR = 'body';\nconst DEFAULT_CHECK_DELAY = 3000;\nconst DEFAULT_THRESHOLD = 0.8;\n// 常见 loading mask / spinner 选择器,命中说明页面正在加载而非空白\nconst DEFAULT_LOADING_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: Array<[number, number]> = [\n [0.5, 0.5],\n [0.25, 0.25],\n [0.75, 0.25],\n [0.25, 0.75],\n [0.75, 0.75],\n];\n\nconst getElementSelector = (element: Element | null) => {\n if (!element) {\n return '';\n }\n const tag = element.tagName.toLowerCase();\n if (element.id) {\n return `${tag}#${element.id}`;\n }\n return tag;\n};\n\nconst matchesAny = (element: Element | null, selectors: string[]) => {\n if (!element) return false;\n return selectors.some(selector => {\n try {\n return element.matches(selector) || !!element.closest(selector);\n } catch {\n return false;\n }\n });\n};\n\n// 页面就绪后再开始计时,避免 SPA / 慢加载场景下检测时机过早\nconst whenDocumentReady = (callback: () => void) => {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'complete' || document.readyState === 'interactive') {\n callback();\n return;\n }\n const handler = () => {\n document.removeEventListener('DOMContentLoaded', handler);\n callback();\n };\n document.addEventListener('DOMContentLoaded', handler);\n};\n\nexport const createBlankScreenPlugin = (option: BkOTRumConfig['blankScreen']): BkOTPlugin => {\n let timer: number | undefined;\n\n return {\n name: 'blank-screen',\n enabled: Boolean(option),\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const optionObj = typeof option === 'object' ? option : {};\n const rootSelector = optionObj.rootSelector ?? DEFAULT_SELECTOR;\n const checkDelay = optionObj.checkDelay ?? DEFAULT_CHECK_DELAY;\n const threshold = optionObj.threshold ?? DEFAULT_THRESHOLD;\n const loadingSelectors = [...DEFAULT_LOADING_SELECTORS, ...(optionObj.ignoreSelectors ?? [])];\n\n // 仅创建一次 counter,避免每次回调都触发一次 meter lookup\n const counter = context.meter.createCounter('browser.blank_screen.count', {\n description: 'Number of suspected blank screen detections',\n });\n\n whenDocumentReady(() => {\n timer = window.setTimeout(() => {\n const root = document.querySelector(rootSelector);\n let emptyCount = 0;\n let loadingCount = 0;\n\n for (const [x, y] of SAMPLE_POINTS) {\n const element = document.elementFromPoint(window.innerWidth * x, window.innerHeight * y);\n if (matchesAny(element, loadingSelectors)) {\n loadingCount += 1;\n continue;\n }\n if (element === root || element === document.body || element === document.documentElement) {\n emptyCount += 1;\n }\n }\n\n const score = emptyCount / SAMPLE_POINTS.length;\n // 触发 loading mask 的样本算\"非空\",但若全部点都是 loading,则视为待定不上报\n if (loadingCount === SAMPLE_POINTS.length) {\n return;\n }\n const isBlank = score >= threshold;\n const attributes = {\n 'bk.rum.blank_screen.score': score,\n 'bk.rum.blank_screen.threshold': threshold,\n 'bk.rum.blank_screen.root': rootSelector,\n 'bk.rum.blank_screen.detected': isBlank,\n 'bk.rum.blank_screen.center_element': getElementSelector(\n document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2),\n ),\n 'bk.rum.blank_screen.dom_node_count': document.body?.getElementsByTagName('*').length ?? 0,\n };\n\n if (isBlank) {\n counter.add(\n 1,\n context.applyRedact({\n // 仅放进低基数维度,center_element 等高基数字段只走 log\n 'bk.rum.blank_screen.root': rootSelector,\n }),\n );\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'browser.blank_screen',\n attributes,\n });\n }\n }, checkDelay);\n });\n },\n shutdown() {\n if (timer !== undefined && typeof window !== 'undefined') {\n window.clearTimeout(timer);\n }\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/naming-convention */\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 Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\nimport { shouldIgnoreUrl, shouldPropagateTraceHeader } from '../core/url';\n\nimport type { BkOTInstrumentationsConfig } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst URL_KEYS = ['http.url', 'url.full'];\n\n// OTel 官方 instrumentation 内部对每个 URL 调用 matcher.test(url),\n// 这里把\"用户函数 + 自身 endpoint 过滤\"包装成具备 .test 的对象,是与 OTel 一致的扩展用法\nconst toUrlPredicateMatcher = (predicate: (url: string) => boolean): RegExp =>\n ({ test: predicate }) as unknown as RegExp;\n\n// 对官方 instrumentation 创建的 span,统一附加 page attributes 与 URL 脱敏\nconst decorateSpan = (\n context: BkOTRuntimeContext,\n span: {\n attributes?: Record<string, any>;\n setAttribute: (key: string, value: any) => void;\n setAttributes: (attrs: Record<string, any>) => void;\n },\n) => {\n span.setAttributes(context.applyRedact(context.config.getPageAttributes()));\n for (const key of URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n const redactedAttributes = context.applyRedact({ [key]: context.config.redactUrl(value) });\n const redactedUrl = redactedAttributes[key];\n if (typeof redactedUrl === 'string') {\n span.setAttribute(key, redactedUrl);\n }\n }\n }\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTInstrumentationsConfig): BkOTPlugin => {\n const instrumentations: Instrumentation[] = [];\n\n return {\n name: 'official-instrumentations',\n enabled: Boolean(option.documentLoad || option.fetch || option.xhr || option.userInteraction),\n async init(context) {\n const loadedInstrumentations: Instrumentation[] = [];\n const ignoreUrls = [toUrlPredicateMatcher(url => shouldIgnoreUrl(context.config, url))];\n const propagateTraceHeaderCorsUrls = [\n toUrlPredicateMatcher(url => shouldPropagateTraceHeader(context.config, url)),\n ];\n\n if (option.documentLoad) {\n const { DocumentLoadInstrumentation } = await import('@opentelemetry/instrumentation-document-load');\n loadedInstrumentations.push(\n new DocumentLoadInstrumentation({\n semconvStabilityOptIn: 'http',\n }),\n );\n }\n\n if (option.fetch) {\n const { FetchInstrumentation } = await import('@opentelemetry/instrumentation-fetch');\n loadedInstrumentations.push(\n new FetchInstrumentation({\n applyCustomAttributesOnSpan: span => decorateSpan(context, span as any),\n ignoreUrls,\n propagateTraceHeaderCorsUrls,\n semconvStabilityOptIn: 'http',\n ignoreNetworkEvents: false,\n }),\n );\n }\n\n if (option.xhr) {\n const { XMLHttpRequestInstrumentation } = await import('@opentelemetry/instrumentation-xml-http-request');\n loadedInstrumentations.push(\n new XMLHttpRequestInstrumentation({\n applyCustomAttributesOnSpan: span => decorateSpan(context, span as any),\n ignoreUrls,\n propagateTraceHeaderCorsUrls,\n semconvStabilityOptIn: 'http',\n ignoreNetworkEvents: false,\n }),\n );\n }\n\n if (option.userInteraction) {\n const { UserInteractionInstrumentation } = await import('@opentelemetry/instrumentation-user-interaction');\n const userInteractionConfig =\n typeof option.userInteraction === 'object'\n ? { eventNames: option.userInteraction.eventNames as Array<keyof HTMLElementEventMap> }\n : undefined;\n loadedInstrumentations.push(new UserInteractionInstrumentation(userInteractionConfig));\n }\n\n instrumentations.push(...loadedInstrumentations);\n registerInstrumentations({ instrumentations: loadedInstrumentations });\n },\n shutdown() {\n while (instrumentations.length) {\n instrumentations.pop()?.disable?.();\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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 监听 CSP 违规事件并以 log 形式上报,便于排查脚本/资源被 CSP 拦截的问题。\n */\nexport const createCspViolationPlugin = (enabled: BkOTRumConfig['cspViolation']): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n context.emitLog({\n severityNumber: SeverityNumber.WARN,\n severityText: 'WARN',\n body: 'csp.violation',\n attributes: {\n ...context.config.getPageAttributes(),\n 'csp.blocked_uri': context.config.redactUrl(event.blockedURI || ''),\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.original_policy': event.originalPolicy,\n 'csp.disposition': event.disposition,\n 'csp.source_file': context.config.redactUrl(event.sourceFile || ''),\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n },\n });\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\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n// 复用旧 session 插件的 storageKey,避免升级后丢失现网累计的设备标识\nconst DEFAULT_DEVICE_STORAGE_KEY = 'bk_ot_session_id';\n\nconst createId = () => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `device-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n\nconst readDeviceId = (storageKey: string) => {\n try {\n const existed = window.localStorage.getItem(storageKey);\n if (existed) {\n return existed;\n }\n const deviceId = createId();\n window.localStorage.setItem(storageKey, deviceId);\n return deviceId;\n } catch {\n return createId();\n }\n};\n\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n const connection = (\n navigator as Navigator & {\n connection?: { effectiveType?: string; type?: string };\n }\n ).connection;\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 'network.effective_type': connection?.effectiveType ?? connection?.type,\n };\n};\n\n/**\n * 设备级永久标识 + 视口 / 网络元数据。\n * 与 session 插件区分:device 跨会话持久,session 有过期与续期。\n */\nexport const createDevicePlugin = (option: BkOTRumConfig['device']): BkOTPlugin => ({\n name: 'device',\n enabled: Boolean(option),\n init(context) {\n const storageKey =\n typeof option === 'object' ? (option.storageKey ?? DEFAULT_DEVICE_STORAGE_KEY) : DEFAULT_DEVICE_STORAGE_KEY;\n const deviceId = typeof window === 'undefined' ? createId() : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n ...getViewportAttributes(),\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 { context as otelContext, SpanStatusCode, trace } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_PER_WINDOW = 5;\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\nconst getErrorStack = (value: unknown) => (value instanceof Error ? (value.stack ?? '') : '');\n\n// 简易 djb2 hash,足够区分常见 error 字符串组合\nconst hashString = (input: string): string => {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = (hash * 33) ^ input.charCodeAt(i);\n }\n return (hash >>> 0).toString(36);\n};\n\ninterface ThrottleEntry {\n count: number;\n windowStart: number;\n}\n\nconst createThrottle = (windowMs: number, maxPerWindow: number) => {\n const records = new Map<string, ThrottleEntry>();\n return {\n /** 返回 true 表示允许上报;false 表示触发节流被抛弃 */\n allow(key: string): boolean {\n const now = Date.now();\n const entry = records.get(key);\n if (!entry || now - entry.windowStart > windowMs) {\n records.set(key, { count: 1, windowStart: now });\n return true;\n }\n if (entry.count >= maxPerWindow) {\n return false;\n }\n entry.count += 1;\n return true;\n },\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 throttle: ReturnType<typeof createThrottle>;\n}\n\nconst emitError = ({ context, error, exceptionType, extra = {}, source, spanName, throttle }: EmitErrorOptions) => {\n const message = getErrorMessage(error);\n const stack = getErrorStack(error);\n const throttleKey = hashString(`${source}|${message}|${stack.slice(0, 256)}`);\n\n if (!throttle.allow(throttleKey)) {\n return;\n }\n\n const activeSpan = trace.getActiveSpan();\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.getPageAttributes(),\n ...context.config.getErrorAttributes(),\n 'exception.message': message,\n 'exception.stacktrace': stack,\n 'exception.type': error instanceof Error ? error.name : (exceptionType ?? source),\n 'bk.rum.error.source': source,\n 'bk.rum.error.fingerprint': throttleKey,\n ...extra,\n };\n const emitErrorLog = () =>\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: message,\n attributes,\n });\n\n // 操作链路内的错误优先记录到当前 active span,符合 OT 对 exception event 与 span status 的语义。\n if (activeSpan) {\n activeSpan.recordException(exceptionLike);\n activeSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n emitErrorLog();\n return;\n }\n\n // 全局孤立错误没有可靠父 span 时,创建独立 error span,并在该 span 上下文中发 log 以保留 trace/log 关联。\n const errorSpan = context.startSpan(spanName, attributes);\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n otelContext.with(trace.setSpan(otelContext.active(), errorSpan), emitErrorLog);\n errorSpan.end();\n};\n\nexport const createErrorPlugin = (option: BkOTRumConfig['error']): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n\n return {\n name: 'error',\n enabled: Boolean(option),\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const windowMs = typeof option === 'object' ? (option.windowMs ?? DEFAULT_WINDOW_MS) : DEFAULT_WINDOW_MS;\n const maxPerWindow =\n typeof option === 'object' ? (option.maxPerWindow ?? DEFAULT_MAX_PER_WINDOW) : DEFAULT_MAX_PER_WINDOW;\n const throttle = createThrottle(windowMs, maxPerWindow);\n\n const onError = (event: ErrorEvent) => {\n emitError({\n context,\n throttle,\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 },\n });\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n emitError({\n context,\n throttle,\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 const target = event.target as EventTarget & {\n href?: string;\n src?: string;\n tagName?: string;\n };\n if (target === window) {\n return;\n }\n const resourceUrl = target.src || target.href || '';\n emitError({\n context,\n throttle,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName?.toLowerCase() || 'unknown'} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.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 * 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 { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { shouldIgnoreUrl } from '../core/url';\n\nimport type { BkOTHttpBodyConfig, BkOTHttpBodyRedactPayload } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\ninterface BodySnapshot {\n body: string;\n contentType?: string;\n truncated: boolean;\n}\n\ninterface ReportHttpBodyOptions {\n context: BkOTRuntimeContext;\n duration: number;\n error?: unknown;\n method: string;\n request?: BodySnapshot;\n response?: BodySnapshot;\n status?: number;\n url: string;\n}\n\nconst HTTP_BODY_MAX_SIZE = 10 * 1024;\n\ntype HttpBodyInput = BodyInit | Document | null | undefined;\n\nconst truncateBody = (body: string, maxBodySize: number): Pick<BodySnapshot, 'body' | 'truncated'> => {\n if (body.length <= maxBodySize) {\n return {\n body,\n truncated: false,\n };\n }\n return {\n body: body.slice(0, maxBodySize),\n truncated: true,\n };\n};\n\nconst getHeaderValue = (headers: HeadersInit | undefined, key: string) => {\n if (!headers) {\n return undefined;\n }\n if (headers instanceof Headers) {\n return headers.get(key) || undefined;\n }\n const normalizedKey = key.toLowerCase();\n if (Array.isArray(headers)) {\n return headers.find(([itemKey]) => itemKey.toLowerCase() === normalizedKey)?.[1];\n }\n const matchedKey = Object.keys(headers).find(item => item.toLowerCase() === normalizedKey);\n return matchedKey ? headers[matchedKey] : undefined;\n};\n\nconst stringifyFormData = (body: FormData) => {\n const entries: Record<string, string> = {};\n body.forEach((value, key) => {\n entries[key] =\n value instanceof File ? `[File name=${value.name} type=${value.type || 'unknown'} size=${value.size}]` : value;\n });\n return JSON.stringify(entries);\n};\n\nconst stringifyBody = async (body: HttpBodyInput): Promise<string> => {\n if (body == null) {\n return '';\n }\n if (typeof body === 'string') {\n return body;\n }\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n if (body instanceof FormData) {\n return stringifyFormData(body);\n }\n if (body instanceof Blob) {\n return body.text();\n }\n if (body instanceof ArrayBuffer) {\n return new TextDecoder().decode(body);\n }\n if (ArrayBuffer.isView(body)) {\n return new TextDecoder().decode(body);\n }\n if (body instanceof Document) {\n return new XMLSerializer().serializeToString(body);\n }\n return String(body);\n};\n\nconst createBodySnapshot = async (\n body: HttpBodyInput,\n maxBodySize: number,\n contentType?: string,\n): Promise<BodySnapshot | undefined> => {\n const rawBody = await stringifyBody(body);\n if (!rawBody) {\n return undefined;\n }\n return {\n ...truncateBody(rawBody, maxBodySize),\n contentType,\n };\n};\n\nconst safeCreateBodySnapshot = async (body: HttpBodyInput, maxBodySize: number, contentType?: string) => {\n try {\n return await createBodySnapshot(body, maxBodySize, contentType);\n } catch {\n return undefined;\n }\n};\n\nconst getFetchInputUrl = (input: RequestInfo | URL) => {\n if (input instanceof Request) {\n return input.url;\n }\n return String(input);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) => {\n if (init?.method) {\n return init.method;\n }\n if (input instanceof Request) {\n return input.method;\n }\n return 'GET';\n};\n\nconst getFetchRequestBody = async (input: RequestInfo | URL, init: RequestInit | undefined, maxBodySize: number) => {\n if (init?.body) {\n return safeCreateBodySnapshot(\n init.body,\n maxBodySize,\n getHeaderValue(init.headers as Headers | undefined, 'content-type'),\n );\n }\n if (input instanceof Request) {\n try {\n return safeCreateBodySnapshot(\n await input.clone().text(),\n maxBodySize,\n input.headers.get('content-type') || undefined,\n );\n } catch {\n return undefined;\n }\n }\n return undefined;\n};\n\nconst getResponseBody = async (response: Response, maxBodySize: number) => {\n try {\n const clonedResponse = response.clone();\n return safeCreateBodySnapshot(\n clonedResponse.body ? await clonedResponse.text() : '',\n maxBodySize,\n response.headers.get('content-type') || undefined,\n );\n } catch {\n return undefined;\n }\n};\n\nconst getXhrResponseBody = (xhr: XMLHttpRequest, maxBodySize: number): BodySnapshot | undefined => {\n if (xhr.responseType === '' || xhr.responseType === 'text') {\n return {\n ...truncateBody(xhr.responseText || '', maxBodySize),\n contentType: xhr.getResponseHeader('content-type') || undefined,\n };\n }\n if (xhr.responseType === 'json') {\n return {\n ...truncateBody(JSON.stringify(xhr.response), maxBodySize),\n contentType: xhr.getResponseHeader('content-type') || undefined,\n };\n }\n return undefined;\n};\n\nconst redactBody = (\n config: Required<BkOTHttpBodyConfig>,\n payload: Omit<BkOTHttpBodyRedactPayload, 'body' | 'truncated'>,\n snapshot?: BodySnapshot,\n) => {\n if (!snapshot) {\n return undefined;\n }\n return config.redact({\n ...payload,\n body: snapshot.body,\n truncated: snapshot.truncated,\n });\n};\n\nconst reportHttpBody = ({\n context,\n duration,\n error,\n method,\n request,\n response,\n status,\n url,\n}: ReportHttpBodyOptions) => {\n const config = context.config.rum.httpBody;\n if (!config) {\n return;\n }\n\n const isError = Boolean(error) || (typeof status === 'number' && status >= 400);\n const normalizedMethod = method.toUpperCase();\n const requestBody = isError\n ? redactBody(\n config,\n {\n contentType: request?.contentType,\n method: normalizedMethod,\n status,\n type: 'request',\n url,\n },\n request,\n )\n : undefined;\n const responseBody = isError\n ? redactBody(\n config,\n {\n contentType: response?.contentType,\n method: normalizedMethod,\n status,\n type: 'response',\n url,\n },\n response,\n )\n : undefined;\n const attributes: Attributes = {\n ...context.config.getPageAttributes(),\n 'http.duration': duration,\n 'http.request.method': normalizedMethod,\n 'url.full': context.config.redactUrl(url),\n };\n\n if (typeof status === 'number') {\n attributes['http.response.status_code'] = status;\n }\n if (requestBody != null) {\n attributes['http.request.body'] = requestBody;\n }\n if (responseBody != null) {\n attributes['http.response.body'] = responseBody;\n }\n if (isError) {\n attributes['bk.rum.http_body.request.truncated'] = request?.truncated ?? false;\n attributes['bk.rum.http_body.response.truncated'] = response?.truncated ?? false;\n }\n if (error instanceof Error) {\n attributes['exception.message'] = error.message;\n attributes['exception.type'] = error.name;\n attributes['exception.stacktrace'] = error.stack ?? '';\n }\n\n context.emitLog({\n severityNumber: isError ? SeverityNumber.ERROR : SeverityNumber.INFO,\n severityText: isError ? 'ERROR' : 'INFO',\n body: isError ? 'HTTP request completed with error' : 'HTTP request completed',\n attributes,\n });\n};\n\nexport const createHttpBodyPlugin = (option: false | Required<BkOTHttpBodyConfig>): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'http-body',\n enabled: Boolean(option),\n init(context) {\n if (!option || typeof window === 'undefined') {\n return;\n }\n\n const maxBodySize = option.maxBodySize || HTTP_BODY_MAX_SIZE;\n const originalFetch = window.fetch;\n if (typeof originalFetch === 'function') {\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const url = getFetchInputUrl(input);\n if (shouldIgnoreUrl(context.config, url)) {\n return originalFetch(input, init);\n }\n const method = getFetchMethod(input, init);\n const request = await getFetchRequestBody(input, init, maxBodySize);\n const startTime = performance.now();\n try {\n const response = await originalFetch(input, init);\n const duration = performance.now() - startTime;\n const responseBody = response.status >= 400 ? await getResponseBody(response, maxBodySize) : undefined;\n reportHttpBody({\n context,\n duration,\n method,\n request,\n response: responseBody,\n status: response.status,\n url,\n });\n return response;\n } catch (error) {\n reportHttpBody({\n context,\n duration: performance.now() - startTime,\n error,\n method,\n request,\n url,\n });\n throw error;\n }\n };\n teardownCallbacks.push(() => {\n window.fetch = originalFetch;\n });\n }\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n XMLHttpRequest.prototype.open = function open(\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n ...args: [async?: boolean, username?: null | string, password?: null | string]\n ) {\n this.__bkOtHttpBodyMeta__ = {\n method,\n startTime: performance.now(),\n url: String(url),\n };\n return originalOpen.call(this, method, url, args[0] ?? true, args[1], args[2]);\n };\n XMLHttpRequest.prototype.setRequestHeader = function setRequestHeader(\n this: XMLHttpRequest,\n name: string,\n value: string,\n ) {\n this.__bkOtHttpBodyRequestHeaders__ = {\n ...(this.__bkOtHttpBodyRequestHeaders__ ?? {}),\n [name]: value,\n };\n return originalSetRequestHeader.call(this, name, value);\n };\n XMLHttpRequest.prototype.send = function send(\n this: XMLHttpRequest,\n body?: Document | null | XMLHttpRequestBodyInit,\n ) {\n void safeCreateBodySnapshot(\n body,\n maxBodySize,\n getHeaderValue(this.__bkOtHttpBodyRequestHeaders__, 'content-type'),\n ).then(request => {\n this.__bkOtHttpBodyRequest__ = request;\n });\n this.addEventListener('loadend', () => {\n const meta = this.__bkOtHttpBodyMeta__;\n if (!meta || shouldIgnoreUrl(context.config, meta.url)) {\n return;\n }\n const responseBody = this.status >= 400 ? getXhrResponseBody(this, maxBodySize) : undefined;\n reportHttpBody({\n context,\n duration: performance.now() - meta.startTime,\n method: meta.method,\n request: this.__bkOtHttpBodyRequest__,\n response: responseBody,\n status: this.status,\n url: meta.url,\n });\n });\n return originalSend.call(this, body);\n };\n teardownCallbacks.push(() => {\n XMLHttpRequest.prototype.open = originalOpen;\n XMLHttpRequest.prototype.send = originalSend;\n XMLHttpRequest.prototype.setRequestHeader = originalSetRequestHeader;\n });\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n\ndeclare global {\n interface XMLHttpRequest {\n __bkOtHttpBodyRequest__?: BodySnapshot;\n __bkOtHttpBodyRequestHeaders__?: Record<string, string>;\n __bkOtHttpBodyMeta__?: {\n method: string;\n startTime: number;\n url: string;\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 { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_THRESHOLD_MS = 50;\n\ninterface LongTaskAttribution {\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n}\n\ninterface LongTaskEntry extends PerformanceEntry {\n attribution?: LongTaskAttribution[];\n}\n\n/**\n * Long Task 监控:通过 PerformanceObserver type=longtask 捕获 >= threshold 的主线程长任务。\n * 默认关闭,建议在性能敏感模块按需开启。\n */\nexport const createLongTaskPlugin = (option: BkOTRumConfig['longTask']): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'long-task',\n enabled: Boolean(option),\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n const entryTypes = (PerformanceObserver as unknown as { supportedEntryTypes?: string[] }).supportedEntryTypes;\n if (!entryTypes?.includes('longtask')) {\n return;\n }\n\n const threshold = typeof option === 'object' ? (option.threshold ?? DEFAULT_THRESHOLD_MS) : DEFAULT_THRESHOLD_MS;\n const counter = context.meter.createCounter('browser.long_task.count', {\n description: 'Number of long tasks observed (duration >= threshold)',\n });\n const durationHistogram = context.meter.createHistogram('browser.long_task.duration', {\n unit: 'ms',\n description: 'Duration of long tasks',\n });\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as LongTaskEntry[]) {\n if (entry.duration < threshold) {\n continue;\n }\n const attribution = entry.attribution?.[0];\n const dimensions = context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n 'long_task.name': entry.name,\n 'long_task.attribution': attribution?.name ?? 'unknown',\n });\n counter.add(1, dimensions);\n durationHistogram.record(entry.duration, dimensions);\n }\n });\n observer.observe({ type: 'longtask', 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 * 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\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 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\nimport { subscribeRouteChange } from '../core/route-observer';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nexport const createPageViewPlugin = (enabled: BkOTRumConfig['pageView']): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // 同 URL 去重:路由库常常因 query/hash 微调反复 push 同一 URL,去重避免重复上报\n let lastUrl = '';\n\n const emitPageView = (source: string, previousUrl = lastUrl) => {\n const currentUrl = getCurrentUrl();\n if (currentUrl === lastUrl) {\n return;\n }\n lastUrl = currentUrl;\n\n const span = context.startSpan('browser.page_view', {\n ...context.config.getPageAttributes(),\n 'url.full': context.config.redactUrl(currentUrl),\n 'url.previous': context.config.redactUrl(previousUrl),\n 'document.referrer': context.config.redactUrl(document.referrer || ''),\n 'bk.rum.event.source': source,\n });\n span.end();\n };\n\n const unsubscribe = subscribeRouteChange(event => {\n emitPageView(event.source, event.fromUrl);\n });\n emitPageView('load');\n\n teardownCallbacks.push(unsubscribe);\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 { subscribeRouteChange } from '../core/route-observer';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 估算 SPA 路由切换耗时:路由变更 → 双 raf + 一个宏任务后视为\"渲染完成\"。\n * 不依赖具体框架,但仅是粗粒度估算;如需精确,业务可走自定义 plugin 在框架钩子内 startSpan/end。\n */\nexport const createRouteTimingPlugin = (enabled: BkOTRumConfig['routeTiming']): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'route-timing',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n const histogram = context.meter.createHistogram('browser.route_change.duration', {\n unit: 'ms',\n description: 'Estimated SPA route change duration (route start to next idle)',\n });\n\n const measure = (source: string, fromUrl: string, toUrl: string) => {\n const startedAt = performance.now();\n const finalize = () => {\n const duration = performance.now() - startedAt;\n const dimensions = context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n 'route.change.source': source,\n });\n histogram.record(duration, dimensions);\n const span = context.startSpan('browser.route_change', {\n ...dimensions,\n 'url.full': context.config.redactUrl(toUrl),\n 'url.previous': context.config.redactUrl(fromUrl),\n 'route.change.duration_ms': duration,\n });\n span.end();\n };\n // 双 raf 等到下一帧渲染后,再补一个宏任务,覆盖大多数同步组件挂载场景\n requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(finalize, 0)));\n };\n\n const unsubscribe = subscribeRouteChange(event => {\n if (event.fromUrl === event.toUrl) {\n return;\n }\n measure(event.source, event.fromUrl, event.toUrl);\n });\n\n teardownCallbacks.push(unsubscribe);\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 { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst DEFAULT_SESSION_STORAGE_KEY = 'bk_ot_session';\nconst DEFAULT_INACTIVITY_MS = 30 * 60 * 1000;\n\ninterface SessionRecord {\n id: string;\n // 会话上次活跃时间戳\n ts: number;\n}\n\nconst createId = () => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `session-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n\nconst readRecord = (storageKey: string): null | SessionRecord => {\n try {\n const raw = window.sessionStorage.getItem(storageKey) ?? window.localStorage.getItem(storageKey);\n if (!raw) {\n return null;\n }\n const parsed = JSON.parse(raw) as SessionRecord;\n if (typeof parsed?.id === 'string' && typeof parsed?.ts === 'number') {\n return parsed;\n }\n return null;\n } catch {\n return null;\n }\n};\n\nconst writeRecord = (storageKey: string, record: SessionRecord) => {\n try {\n const serialized = JSON.stringify(record);\n window.sessionStorage.setItem(storageKey, serialized);\n // 同时写到 localStorage,让多 tab / 关闭页后短期内复用同一个 session\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n /* storage 不可用时忽略,运行期仍能继续 */\n }\n};\n\nconst resolveSessionId = (storageKey: string, inactivityMs: number) => {\n const now = Date.now();\n const existed = readRecord(storageKey);\n if (existed && now - existed.ts < inactivityMs) {\n const record: SessionRecord = { id: existed.id, ts: now };\n writeRecord(storageKey, record);\n return record.id;\n }\n const record: SessionRecord = { id: createId(), ts: now };\n writeRecord(storageKey, record);\n return record.id;\n};\n\n/**\n * 会话级标识:带不活跃过期时间,超过 inactivityMs 视为新会话。\n * 与 device 插件互补:device 是设备终身标识,session 反映\"用户连续访问\"语义。\n */\nexport const createSessionPlugin = (option: BkOTRumConfig['session']): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'session',\n enabled: Boolean(option),\n init(context) {\n const storageKey =\n typeof option === 'object' ? (option.storageKey ?? DEFAULT_SESSION_STORAGE_KEY) : DEFAULT_SESSION_STORAGE_KEY;\n const inactivityMs =\n typeof option === 'object' ? (option.inactivityMs ?? DEFAULT_INACTIVITY_MS) : DEFAULT_INACTIVITY_MS;\n\n const refresh = () => {\n const sessionId = typeof window === 'undefined' ? createId() : resolveSessionId(storageKey, inactivityMs);\n context.setRuntimeAttributes({ 'session.id': sessionId });\n };\n\n refresh();\n\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n // 用户回到页面时检查是否过期,过期则轮换 sessionId\n const onVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n refresh();\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n teardown = () => document.removeEventListener('visibilitychange', onVisibilityChange);\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\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { MetricWithAttribution } from 'web-vitals/attribution';\n\ntype VitalLike = Pick<MetricWithAttribution, 'attribution' | 'id' | 'name' | 'rating' | 'value'>;\n\nconst getNavigationType = () => {\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n return navigation?.type || 'unknown';\n};\n\n// 仅挑选低基数、可枚举的字段进 metric attributes,避免指标维度爆炸\nconst getMetricDimensions = (metric: VitalLike): Attributes => ({\n 'rum.navigation.type': getNavigationType(),\n 'web_vital.name': metric.name,\n 'web_vital.rating': metric.rating,\n});\n\n// 把 attribution 中价值最高的字段抽出来(仅放进 span/log,不放进 metric)\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('web_vital.lcp.url', getAttrValue('url'), redactUrl);\n setIfDefined('web_vital.lcp.target', getAttrValue('target'));\n setIfDefined('web_vital.lcp.element_render_delay', getAttrValue('elementRenderDelay'));\n setIfDefined('web_vital.lcp.resource_load_duration', getAttrValue('resourceLoadDuration'));\n setIfDefined('web_vital.lcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n break;\n case 'CLS':\n setIfDefined('web_vital.cls.largest_shift_target', getAttrValue('largestShiftTarget'));\n setIfDefined('web_vital.cls.largest_shift_value', getAttrValue('largestShiftValue'));\n setIfDefined('web_vital.cls.load_state', getAttrValue('loadState'));\n break;\n case 'INP':\n setIfDefined('web_vital.inp.interaction_target', getAttrValue('interactionTarget'));\n setIfDefined('web_vital.inp.interaction_type', getAttrValue('interactionType'));\n setIfDefined('web_vital.inp.input_delay', getAttrValue('inputDelay'));\n setIfDefined('web_vital.inp.processing_duration', getAttrValue('processingDuration'));\n setIfDefined('web_vital.inp.presentation_delay', getAttrValue('presentationDelay'));\n break;\n case 'FCP':\n setIfDefined('web_vital.fcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n setIfDefined('web_vital.fcp.load_state', getAttrValue('loadState'));\n break;\n case 'TTFB':\n setIfDefined('web_vital.ttfb.waiting_duration', getAttrValue('waitingDuration'));\n setIfDefined('web_vital.ttfb.dns_duration', getAttrValue('dnsDuration'));\n setIfDefined('web_vital.ttfb.connection_duration', getAttrValue('connectionDuration'));\n setIfDefined('web_vital.ttfb.request_duration', getAttrValue('requestDuration'));\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (enabled: BkOTRumConfig['webVitals']): BkOTPlugin => ({\n name: 'web-vitals',\n enabled: Boolean(enabled),\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const { onCLS, onFCP, onINP, onLCP, onTTFB } = await import('web-vitals/attribution');\n const clsHistogram = context.meter.createHistogram('browser.web_vital.cls', {\n unit: '1',\n description: 'Cumulative Layout Shift',\n });\n const durationHistogram = context.meter.createHistogram('browser.web_vital.duration', {\n unit: 'ms',\n description: 'Web Vitals duration metrics, including FCP, INP, LCP and TTFB',\n });\n\n const recordSpan = (metric: VitalLike) => {\n const span = context.startSpan('browser.web_vital', {\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n // 单条度量唯一 ID 仅放进 span,避免污染 metric 维度\n 'web_vital.id': metric.id,\n 'web_vital.value': metric.value,\n ...pickAttribution(metric, context.config.redactUrl),\n });\n span.end();\n };\n\n const recordDurationMetric = (metric: VitalLike) => {\n durationHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n }),\n );\n recordSpan(metric);\n };\n\n onCLS(metric => {\n clsHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.getPageAttributes(),\n ...context.config.getMetricAttributes(),\n ...getMetricDimensions(metric),\n }),\n );\n recordSpan(metric);\n });\n onFCP(recordDurationMetric);\n onINP(recordDurationMetric);\n onLCP(recordDurationMetric);\n onTTFB(recordDurationMetric);\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 { SpanStatusCode } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { shouldIgnoreUrl } from '../core/url';\n\nimport type { BkOTRumConfig } from '../core/config';\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst getMessageByteLength = (data: unknown): number => {\n if (data == null) return 0;\n if (typeof data === 'string') {\n return typeof TextEncoder === 'undefined' ? data.length : new TextEncoder().encode(data).byteLength;\n }\n if (data instanceof ArrayBuffer) return data.byteLength;\n if (ArrayBuffer.isView(data)) return data.byteLength;\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.size;\n return 0;\n};\n\nconst getWebSocketAttributes = (url: string, redactUrl: (url: string) => string) => {\n const spanAttributes: Record<string, string> = {\n 'url.full': redactUrl(url),\n 'network.protocol.name': 'websocket',\n };\n\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n spanAttributes['server.address'] = parsed.host;\n } catch {\n /* ignore malformed URL and keep the raw, redacted URL only */\n }\n\n return {\n spanAttributes,\n metricAttributes: {\n 'network.protocol.name': 'websocket',\n },\n };\n};\n\nexport const createWebSocketPlugin = (enabled: BkOTRumConfig['websocket']): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n\n return {\n name: 'websocket',\n enabled: Boolean(enabled),\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {\n return;\n }\n\n const NativeWebSocket = window.WebSocket;\n originalWebSocket = NativeWebSocket;\n\n const messageCounter = context.meter.createCounter('browser.websocket.message.count', {\n description: 'Total number of WebSocket messages observed',\n });\n const bytesCounter = context.meter.createCounter('browser.websocket.message.bytes', {\n unit: 'By',\n description: 'Total bytes transferred over WebSocket (best-effort)',\n });\n const errorCounter = context.meter.createCounter('browser.websocket.error.count', {\n description: 'Total number of WebSocket error events',\n });\n\n const PatchedWebSocket = function (this: WebSocket, url: string | URL, protocols?: string | string[]) {\n const urlValue = url.toString();\n\n // 用户主动 ignore 或上报 endpoint,避免回环监控\n if (shouldIgnoreUrl(context.config, urlValue)) {\n return protocols === undefined ? new NativeWebSocket(url) : new NativeWebSocket(url, protocols);\n }\n\n const { metricAttributes, spanAttributes } = getWebSocketAttributes(urlValue, context.config.redactUrl);\n // connect span 只覆盖\"建立连接\"阶段,避免长连接导致 span 永远不结束\n const connectSpan = context.startSpan('websocket.connect', spanAttributes);\n const startTime = performance.now();\n const socket = protocols === undefined ? new NativeWebSocket(url) : new NativeWebSocket(url, protocols);\n let connectEnded = false;\n\n const endConnectSpan = (status: 'error' | 'opened') => {\n if (connectEnded) return;\n connectEnded = true;\n if (status === 'error') {\n connectSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'websocket connect failed' });\n }\n connectSpan.setAttribute('websocket.connect.duration_ms', performance.now() - startTime);\n connectSpan.end();\n };\n\n socket.addEventListener('open', () => endConnectSpan('opened'));\n\n socket.addEventListener('message', event => {\n messageCounter.add(1, { ...metricAttributes, 'websocket.direction': 'in' });\n bytesCounter.add(getMessageByteLength(event.data), {\n ...metricAttributes,\n 'websocket.direction': 'in',\n });\n });\n\n socket.addEventListener('error', () => {\n errorCounter.add(1, metricAttributes);\n endConnectSpan('error');\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'websocket.error',\n attributes: spanAttributes,\n });\n });\n\n socket.addEventListener('close', event => {\n endConnectSpan('opened');\n context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: 'websocket.close',\n attributes: {\n ...spanAttributes,\n 'websocket.close.code': event.code,\n 'websocket.close.reason': event.reason,\n 'websocket.close.was_clean': event.wasClean,\n },\n });\n });\n\n // 计量发送方向(无法拦截 send 的回调,所以包一层 send 方法)\n const originalSend = socket.send.bind(socket);\n socket.send = (data: ArrayBufferLike | ArrayBufferView | Blob | string) => {\n messageCounter.add(1, { ...metricAttributes, 'websocket.direction': 'out' });\n bytesCounter.add(getMessageByteLength(data), {\n ...metricAttributes,\n 'websocket.direction': 'out',\n });\n return originalSend(data);\n };\n\n return socket;\n } as unknown as typeof WebSocket;\n\n // 复制原型与静态常量(CONNECTING / OPEN / CLOSING / CLOSED 等),避免业务侧 WebSocket.OPEN 失效\n PatchedWebSocket.prototype = NativeWebSocket.prototype;\n Object.assign(PatchedWebSocket, NativeWebSocket);\n\n window.WebSocket = PatchedWebSocket;\n },\n shutdown() {\n if (originalWebSocket && typeof window !== 'undefined') {\n window.WebSocket = originalWebSocket;\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 { NormalizedBkOTConfig } from './config';\n\nconst SAMPLE_STORAGE_KEY = '__bk_ot_sampled__';\n\n// 在 sessionStorage 不可用时降级到模块内单例缓存,避免同会话内多次结果不一致\nconst memoryCache = new Map<string, boolean>();\n\nconst readMemory = (key: string) => memoryCache.get(key);\nconst writeMemory = (key: string, value: boolean) => {\n memoryCache.set(key, value);\n return value;\n};\n\nexport const isBkOTSampled = ({ sampleRate }: Pick<NormalizedBkOTConfig, 'sampleRate'>) => {\n if (sampleRate <= 0) {\n return false;\n }\n if (sampleRate >= 1) {\n return true;\n }\n\n try {\n const cached = sessionStorage.getItem(SAMPLE_STORAGE_KEY);\n if (cached) {\n return cached === '1';\n }\n const sampled = Math.random() < sampleRate;\n sessionStorage.setItem(SAMPLE_STORAGE_KEY, sampled ? '1' : '0');\n writeMemory(SAMPLE_STORAGE_KEY, sampled);\n return sampled;\n } catch {\n const cached = readMemory(SAMPLE_STORAGE_KEY);\n if (typeof cached === 'boolean') {\n return cached;\n }\n return writeMemory(SAMPLE_STORAGE_KEY, Math.random() < sampleRate);\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\nimport {\n type Attributes,\n type Span,\n context,\n metrics,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace,\n} from '@opentelemetry/api';\nimport { logs } from '@opentelemetry/api-logs';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { type Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';\nimport { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { BatchSpanProcessor, ParentBasedSampler, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\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 { createHttpBodyPlugin } from '../plugins/http-body';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createRouteTimingPlugin } from '../plugins/route-timing';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { type BkOTConfig, type BkOTCustomEventPayload, type NormalizedBkOTConfig, normalizeConfig } from './config';\nimport { type BkOTPlugin, type BkOTRuntimeContext, PluginManager } from './plugin';\nimport { FilteringSpanProcessor } from './processor';\nimport { isBkOTSampled } from './sampling';\n\nimport type { LogRecord } from '@opentelemetry/api-logs';\n\nexport interface BkOTInstance extends BkOTRuntimeContext {\n flush: () => Promise<void>;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n use: (plugin: BkOTPlugin) => BkOTInstance;\n useInstrumentation: (instrumentation: Instrumentation) => BkOTInstance;\n}\n\nconst createExporterOptions = (config: NormalizedBkOTConfig['traces']) => ({\n url: config.endpoint,\n headers: config.headers,\n concurrencyLimit: config.concurrencyLimit,\n timeoutMillis: config.timeoutMillis,\n});\n\ntype DebugSignal = 'logs' | 'metrics' | 'traces';\n\nconst BK_OT_INSTRUMENTATION_NAME = 'bk-rum';\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n signal: DebugSignal,\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][${signal}]`, 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 注入 W3C traceparent 头;仅 SDK 启用时装载,避免 disabled 状态仍 patch 全局 API\nconst getCorePlugins = (config: NormalizedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.commonInstrumentations),\n];\n\n// 仅在采样命中时启用的\"会主动产生数据\"的 RUM 插件\nconst getRumPlugins = (config: NormalizedBkOTConfig): BkOTPlugin[] => [\n createDevicePlugin(config.rum.device),\n createHttpBodyPlugin(config.rum.httpBody),\n createSessionPlugin(config.rum.session),\n createPageViewPlugin(config.rum.pageView),\n createErrorPlugin(config.rum.error),\n createWebVitalsPlugin(config.rum.webVitals),\n createBlankScreenPlugin(config.rum.blankScreen),\n createWebSocketPlugin(config.rum.websocket),\n createLongTaskPlugin(config.rum.longTask),\n createCspViolationPlugin(config.rum.cspViolation),\n createRouteTimingPlugin(config.rum.routeTiming),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'starting' | 'stopped';\n\nexport class BkOpenTelemetry implements BkOTInstance {\n private readonly loggerProvider: LoggerProvider;\n private readonly meterProvider: MeterProvider;\n private phase: SdkLifecyclePhase = 'idle';\n private readonly pluginManager: PluginManager;\n private readonly plugins: BkOTPlugin[];\n private readonly registeredInstrumentations: Instrumentation[];\n private readonly runtimeAttributes: Attributes;\n private readonly sampled: boolean;\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 public readonly applyRedact = (attributes: Attributes) => this.config.redactAttributes(attributes);\n\n public readonly config: NormalizedBkOTConfig;\n\n public readonly emitLog = (record: LogRecord) => {\n const merged: LogRecord = {\n ...record,\n attributes: this.applyRedact({\n ...this.getRuntimeAttributes(),\n ...((record.attributes ?? {}) as Attributes),\n }),\n };\n this.logger.emit(merged);\n };\n public readonly flush = async () => {\n await this.pluginManager.flush();\n await this.tracerProvider.forceFlush();\n await this.meterProvider.forceFlush();\n await this.loggerProvider.forceFlush();\n };\n\n public readonly getRuntimeAttributes = () => ({ ...this.runtimeAttributes });\n public readonly logger: BkOTRuntimeContext['logger'];\n\n public readonly meter: BkOTRuntimeContext['meter'];\n\n public readonly reportCustomEvent = ({ attributes = {}, error, name }: BkOTCustomEventPayload) => {\n if (!this.sampled) {\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 span = this.startSpan(`custom.${name}`, {\n ...this.config.getPageAttributes(),\n ...this.config.getCustomAttributes(),\n ...attributes,\n 'rum.custom.name': name,\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();\n };\n\n public readonly setRuntimeAttributes = (attributes: Attributes) => {\n Object.assign(this.runtimeAttributes, attributes);\n };\n\n public readonly shutdown = async () => {\n if (this.phase === 'stopped') {\n return;\n }\n await this.flush();\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.pluginManager.shutdown();\n while (this.registeredInstrumentations.length) {\n this.registeredInstrumentations.pop()?.disable?.();\n }\n await this.tracerProvider.shutdown();\n await this.meterProvider.shutdown();\n await this.loggerProvider.shutdown();\n trace.disable();\n metrics.disable();\n context.disable();\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 propagation.setGlobalPropagator(new W3CTraceContextPropagator());\n this.tracerProvider.register({\n contextManager: new ZoneContextManager(),\n });\n metrics.setGlobalMeterProvider(this.meterProvider);\n logs.setGlobalLoggerProvider(this.loggerProvider);\n try {\n if (this.registeredInstrumentations.length) {\n registerInstrumentations({\n instrumentations: this.registeredInstrumentations,\n meterProvider: this.meterProvider,\n tracerProvider: this.tracerProvider,\n });\n }\n await this.pluginManager.start(this);\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\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.pluginManager.shutdown();\n for (const instrumentation of this.registeredInstrumentations) {\n instrumentation.disable?.();\n }\n trace.disable();\n metrics.disable();\n context.disable();\n this.phase = 'idle';\n throw error;\n }\n this.phase = 'started';\n };\n\n public readonly startSpan = (name: string, attributes: Attributes = {}): Span =>\n this.tracer.startSpan(name, {\n kind: SpanKind.INTERNAL,\n attributes: this.applyRedact({\n ...this.getRuntimeAttributes(),\n ...attributes,\n }),\n });\n\n public readonly tracer: BkOTRuntimeContext['tracer'];\n\n public readonly use = (plugin: BkOTPlugin): this => {\n this.assertBeforeStart('use');\n if (this.sampled) {\n this.plugins.push(plugin);\n }\n return this;\n };\n\n public readonly useInstrumentation = (instrumentation: Instrumentation): this => {\n this.assertBeforeStart('useInstrumentation');\n if (this.config.enabled) {\n this.registeredInstrumentations.push(instrumentation);\n }\n return this;\n };\n\n constructor(inputConfig: BkOTConfig) {\n this.config = normalizeConfig(inputConfig);\n this.sampled = this.config.enabled && isBkOTSampled(this.config);\n\n const resource = resourceFromAttributes(this.config.resourceAttributes);\n const spanExporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(this.config.traces)),\n 'traces',\n this.config.debug,\n );\n const metricExporter = wrapWithDebug(\n new OTLPMetricExporter(createExporterOptions(this.config.metrics)),\n 'metrics',\n this.config.debug,\n );\n const logExporter = wrapWithDebug(\n new OTLPLogExporter(createExporterOptions(this.config.logs)),\n 'logs',\n this.config.debug,\n );\n const batchSpanProcessor = new BatchSpanProcessor(spanExporter, this.config.spanBatch);\n const spanProcessor = new FilteringSpanProcessor(batchSpanProcessor, this.config);\n const metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: this.config.metricIntervalMillis,\n });\n const logProcessor = new BatchLogRecordProcessor(logExporter);\n\n this.tracerProvider = new WebTracerProvider({\n resource,\n sampler: new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(this.sampled ? 1 : 0),\n }),\n spanProcessors: [spanProcessor],\n });\n this.meterProvider = new MeterProvider({\n resource,\n readers: [metricReader],\n });\n this.loggerProvider = new LoggerProvider({\n resource,\n processors: [logProcessor],\n });\n this.tracer = this.tracerProvider.getTracer(BK_OT_INSTRUMENTATION_NAME);\n this.meter = this.meterProvider.getMeter(BK_OT_INSTRUMENTATION_NAME);\n this.logger = this.loggerProvider.getLogger(BK_OT_INSTRUMENTATION_NAME);\n this.runtimeAttributes = {\n 'bk.rum.sampled': this.sampled,\n };\n this.registeredInstrumentations = this.config.enabled ? [...this.config.instrumentations] : [];\n // 核心插件在 SDK 启用时装载;RUM 数据上报类插件仅在采样命中时装载\n this.plugins = [\n ...(this.config.enabled ? getCorePlugins(this.config) : []),\n ...(this.sampled ? [...getRumPlugins(this.config), ...this.config.plugins] : []),\n ];\n this.pluginManager = new PluginManager(this.plugins);\n\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyMA,IAAM,IAAwB,yBACxB,KAAiC,KAAK,KAEtC,KAAqB,MAAkB,EAAM,QAAQ,QAAQ,GAAG,EAEhE,KAAyB,GAA8B,MAA8C;CACzG,IAAM,IAAa,EAAkB,KAAY,EAAsB;CAOvE,OANI,EAAW,SAAS,OAAO,IAAa,GACnC,IAEL,EAAW,SAAS,MAAM,GACrB,GAAG,EAAW,GAAG,MAEnB,GAAG,EAAW,MAAM;GAGvB,KAAuB,GAAkB,MAAoE;;CACjH,IAAM,IAAe,EAAK,IACpB,KAAA,IAAiB,EAAK,oBAAA,OAAA,KAAA,IAAA,EAAkB,IAExC,IACJ,EAAK,SAAS,EAAK,WAAA,KAAA,QAAW,EAAc,UAAA,EAAA,EAAA,EAAA,EAAA,EAElC,EAAK,QAAQ,EAAE,eAAe,UAAU,EAAK,SAAS,GAAG,EAAE,CAAA,GAAA,IAC3D,EAAK,YAAA,OAAW,EAAE,GAAb,EAAa,GAAA,IAAA,KAAA,OAAA,KAAA,IAClB,EAAc,YAAA,OAAW,EAAE,GAAb,EACnB,GACD,KAAA;CAEN,OAAO;EACL,kBAAA,KAAA,OAAA,KAAA,IAAkB,EAAc;EAChC,UAAU,IACN,EAAkB,EAAe,GACjC,GAAA,IAAA,KAAA,OAAA,KAAA,IAAsB,EAAc,aAAA,OAAY,EAAK,WAAjB,GAA2B,EAAW;EAC9E,SAAS;EACT,eAAA,KAAA,OAAA,KAAA,IAAe,EAAc;EAC9B;GAGG,MAAmB,MACnB,OAAO,KAAe,YAAY,CAAC,OAAO,SAAS,EAAW,GACzD,IAEF,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAW,CAAC,EAGvC,KAAkB,GAA2B,MACjD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,IAAQ,IAAI,IAAQ,GAEvE,MAAqB,GAA2B,MACpD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,KAAS,IAAI,IAAQ,GAExE,MAAgB,GAA2B,MAC3C,OAAO,KAAU,YAAY,CAAC,OAAO,SAAS,EAAM,GAC/C,IAEF,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAM,CAAC,EAGlC,MAA0B,MAAuC,EAAQ,MAEzE,MAA2B,MAA8E;;CAU7G,OATK,IAGD,OAAO,KAAa,YACf;EACL,aAAa,KAAK;EAClB,QAAQ;EACT,GAEI;EACL,aAAa,EAAe,EAAS,aAAa,KAAK,KAAK;EAC5D,SAAA,IAAQ,EAAS,WAAA,OAAU,KAAV;EAClB,GAXQ;GAcL,MAAsB,MAAA;;QAAyD;EACnF,SAAA,IAAA,KAAA,OAAA,KAAA,IAAQ,EAAK,WAAA,OAAU,KAAV;EACb,UAAU,GAAA,KAAA,OAAA,KAAA,IAAwB,EAAK,SAAS;EAChD,SACE,QAAA,KAAA,OAAA,KAAA,IAAO,EAAK,YAAY,WAAA,EAAA,EAAA,EAAA,EAEf,EAAI,QAAA,EAAA,EAAA,EAAA,EACP,cAAc,EAAe,EAAI,QAAQ,cAAc,OAAU,IAAK,EAAA,CACvE,IAAA,IAAA,KAAA,OAAA,KAAA,IACA,EAAK,YAAA,OAAW,KAAX;EACZ,WAAA,IAAA,KAAA,OAAA,KAAA,IAAU,EAAK,aAAA,OAAY,KAAZ;EACf,OACE,QAAA,KAAA,OAAA,KAAA,IAAO,EAAK,UAAU,WAAA,EAAA,EAAA,EAAA,EAEb,EAAI,MAAA,EAAA,EAAA,EAAA;GACP,cAAc,EAAe,EAAI,MAAM,cAAc,EAAE;GACvD,UAAU,EAAe,EAAI,MAAM,UAAU,IAAO;IACrD,IAAA,IAAA,KAAA,OAAA,KAAA,IACA,EAAK,UAAA,OAAS,KAAT;EACZ,YAAA,IAAA,KAAA,OAAA,KAAA,IAAW,EAAK,cAAA,OAAa,KAAb;EAChB,aACE,QAAA,KAAA,OAAA,KAAA,IAAO,EAAK,gBAAgB,WAAA,EAAA,EAAA,EAAA,EAEnB,EAAI,YAAA,EAAA,EAAA,EAAA;GACP,YAAY,GAAkB,EAAI,YAAY,YAAY,IAAK;GAC/D,WAAW,GAAa,EAAI,YAAY,WAAW,GAAI;IACxD,IAAA,IAAA,KAAA,OAAA,KAAA,IACA,EAAK,gBAAA,OAAe,KAAf;EACZ,YAAA,IAAA,KAAA,OAAA,KAAA,IAAW,EAAK,cAAA,OAAa,KAAb;EAChB,UACE,QAAA,KAAA,OAAA,KAAA,IAAO,EAAK,aAAa,WAAA,EAAA,EAAA,EAAA,EAEhB,EAAI,SAAA,EAAA,EAAA,EAAA,EACP,WAAW,GAAkB,EAAI,SAAS,WAAW,GAAG,EAAA,CACzD,IAAA,IAAA,KAAA,OAAA,KAAA,IACA,EAAK,aAAA,OAAY,KAAZ;EACZ,eAAA,IAAA,KAAA,OAAA,KAAA,IAAc,EAAK,iBAAA,OAAgB,KAAhB;EACnB,cAAA,IAAA,KAAA,OAAA,KAAA,IAAa,EAAK,gBAAA,OAAe,KAAf;EACnB;GAEK,MAA4B,MAA2B,GACvD,MAAqB,MAAgB,GAE9B,MAAmB,MAA6C;;CAC3E,IAAM,IAAiC;EACrC,gCAAA,IAA+B,EAAO,gBAAA,OAAe,eAAf;EACtC,gBAAgB;EAChB,0BAA0B;EAC3B;CAED,OAAA,EAAA,EAAA,EAAA,EACK,EAAA,EAAA,EAAA,EAAA;EACH,QAAA,IAAO,EAAO,UAAA,OAAS,KAAT;EACd,UAAA,IAAS,EAAO,YAAA,OAAW,KAAX;EAChB,cAAA,IAAa,EAAO,gBAAA,OAAe,eAAf;EACpB,UAAU,EAAkB,EAAO,YAAY,EAAsB;EACrE,QAAQ,EAAoB,GAAQ,SAAS;EAC7C,SAAS,EAAoB,GAAQ,UAAU;EAC/C,MAAM,EAAoB,GAAQ,OAAO;EACzC,YAAY,GAAgB,EAAO,WAAW;EAC9C;EACA,sBAAsB;EACtB,mBAAA,IAAkB,EAAO,qBAAA,OAAoB,EAAE,GAAtB;EACzB,wBAAwB;GACtB,eAAA,KAAA,IAAc,EAAO,QAAA,OAAA,KAAA,IAAA,EAAK,iBAAA,OAAgB,KAAhB;GAC1B,QAAA,KAAA,IAAO,EAAO,QAAA,OAAA,KAAA,IAAA,EAAK,UAAA,OAAS,KAAT;GACnB,MAAA,KAAA,IAAK,EAAO,QAAA,OAAA,KAAA,IAAA,EAAK,QAAA,OAAO,KAAP;GACjB,kBAAA,KAAA,IAAiB,EAAO,QAAA,OAAA,KAAA,IAAA,EAAK,oBAAA,OAAmB,KAAnB;GAC9B;EACD,KAAK,GAAmB,EAAO,IAAI;EACnC,UAAA,IAAS,EAAO,YAAA,OAAW,EAAE,GAAb;EAChB,YAAA,IAAW,EAAO,cAAA,OAAa,KAAb;EAClB,oBAAA,KAAA,IACE,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,SAAA,eACX;GACN,iBAAiB,OAAO,SAAW,MAAc,KAAK,OAAO,SAAS;GACtE,iBAAiB,OAAO,SAAW,MAAc,KAAK,OAAO,SAAS;GACvE,KAJkB;EAKrB,sBAAA,KAAA,IAAqB,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,WAAA,eAAkB,EAAE,KAApB;EACxC,qBAAA,KAAA,IAAoB,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,UAAA,eAAiB,EAAE,KAAnB;EACvC,sBAAA,KAAA,IAAqB,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,WAAA,eAAkB,EAAE,KAApB;EACxC,mBAAA,KAAA,IAAkB,EAAO,WAAA,OAAA,KAAA,IAAA,EAAQ,eAAA,OAAc,KAAd;EACjC,YAAA,KAAA,IAAW,EAAO,WAAA,OAAA,KAAA,IAAA,EAAQ,QAAA,OAAO,KAAP;GAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrTH,IAAa,MAAmB,GAAoB,MAC9C,OAAO,EAAO,WAAY,aACrB,EAAO,QAAQ,EAAO,GAExB,EAAO,YAAY,IAGf,MAAgB,MAAuB,GAEvC,KAAb,MAA2B;CAIzB,YAAmB,GAAuB;EACxC,QAJF,WAAA,KAAA,EAAA,UACA,kBAAuC,EAAE,CAAA,EAGvC,KAAK,UAAU;;CAGjB,QAAa;;wBAAQ;GAEnB,MAAM,QAAQ,IAAI,EAAK,eAAe,KAAI,MAAU,QAAQ,SAAS,CAAC,WAAW;;kBAAO,UAAA,OAAA,KAAA,IAAA,EAAA,KAAA,EAAS;KAAC,CAAC,CAAC;;;CAGtG,WAAa;;wBAAW;GACtB,IAAM,IAAU,CAAC,GAAG,EAAK,eAAe,CAAC,SAAS;GAClD,KAAK,IAAM,KAAU,GAAS;;IAC5B,OAAA,IAAM,EAAO,aAAA,OAAA,KAAA,IAAA,EAAA,KAAA,EAAY;;GAE3B,EAAK,iBAAiB,EAAE;;;CAG1B,MAAmB,GAAA;;wBAA6B;GAC9C,IAAI;IACF,KAAK,IAAM,KAAU,EAAK,SACnB,GAAgB,GAAQ,EAAQ,OAAO,KAG5C,MAAM,EAAO,KAAK,EAAQ,EAC1B,EAAK,eAAe,KAAK,EAAO;YAE3B,GAAO;IAEd,MADA,MAAM,EAAK,UAAU,EACf;;;;GCjEN,UAAoB,OAAO,SAAW,MAAc,qBAAqB,OAAO,SAAS,QAEzF,MAAiB,MAAgB;CACrC,IAAI;EACF,OAAO,IAAI,IAAI,GAAK,GAAY,CAAC,CAAC;aAC5B;EACN,OAAO;;GAKL,MAAsB,MAAmB,EAAM,SAAS,IAAI,EAAM,QAAQ,QAAQ,GAAG,GAAG,GAGxF,MAAkB,GAAc,MAAkB;CACtD,IAAM,KAAY,MAAkB,GAAmB,EAAM,QAAQ,WAAW,GAAG,CAAC;CACpF,OAAO,EAAS,EAAK,KAAK,EAAS,EAAM;GAGrC,MAAgB,MAAgB;CACpC,IAAI;EACF,OAAO,IAAI,IAAI,GAAK,GAAY,CAAC,CAAC,WAAW,GAAY;aACnD;EACN,OAAO;;GAIE,MAAkB,GAA8B,MAAgB;CAC3E,IAAM,IAAY,GAAc,EAAI;CAEpC,OAAO;EAAC,EAAO,OAAO;EAAU,EAAO,QAAQ;EAAU,EAAO,KAAK;EAAS,CAAC,MAAK,MAClF,GAAe,GAAW,GAAc,EAAS,CAAC,CACnD;GAGU,KAAmB,GAA8B,MAAgB,GAAe,GAAQ,EAAI,EAE5F,MAA8B,GAA+B,MAAgB,GAAa,EAAI,ECjCrG,KAA0B,CAAC,YAAY,WAAW,EAElD,MAAc,MAA2C;CAC7D,KAAK,IAAM,KAAO,IAAyB;;EACzC,IAAM,KAAA,IAAQ,EAAK,eAAA,OAAA,KAAA,IAAA,EAAa;EAChC,IAAI,OAAO,KAAU,UACnB,OAAO;;GAUA,KAAb,MAA6D;CAC3D,YACE,GACA,GACA;EADiB,AADA,KAAA,QAAA,GACA,KAAA,SAAA;;CAGnB,aAAmC;EACjC,OAAO,KAAK,MAAM,YAAY;;CAGhC,MAAa,GAA0B;EACrC,IAAM,IAAM,GAAW,EAAK;EACxB,KAAO,GAAe,KAAK,QAAQ,EAAI,IAG3C,KAAK,MAAM,MAAM,EAAK;;CAGxB,QAAe,GAAY,GAA8B;EACvD,KAAK,MAAM,QAAQ,GAAM,EAAc;;CAGzC,WAAiC;EAC/B,OAAO,KAAK,MAAM,UAAU;;GCxC1B,KAAmB,QACnB,KAAsB,KACtB,KAAoB,IAEpB,KAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAyC;CAC7C,CAAC,IAAK,GAAI;CACV,CAAC,KAAM,IAAK;CACZ,CAAC,KAAM,IAAK;CACZ,CAAC,KAAM,IAAK;CACZ,CAAC,KAAM,IAAK;CACb,EAEK,MAAsB,MAA4B;CACtD,IAAI,CAAC,GACH,OAAO;CAET,IAAM,IAAM,EAAQ,QAAQ,aAAa;CAIzC,OAHI,EAAQ,KACH,GAAG,EAAI,GAAG,EAAQ,OAEpB;GAGH,MAAc,GAAyB,MACtC,IACE,EAAU,MAAK,MAAY;CAChC,IAAI;EACF,OAAO,EAAQ,QAAQ,EAAS,IAAI,CAAC,CAAC,EAAQ,QAAQ,EAAS;aACzD;EACN,OAAO;;EAET,GAPmB,IAWjB,MAAqB,MAAyB;CAClD,IAAI,OAAO,WAAa,KAAa;CACrC,IAAI,SAAS,eAAe,cAAc,SAAS,eAAe,eAAe;EAC/E,GAAU;EACV;;CAEF,IAAM,UAAgB;EAEpB,AADA,SAAS,oBAAoB,oBAAoB,EAAQ,EACzD,GAAU;;CAEZ,SAAS,iBAAiB,oBAAoB,EAAQ;GAG3C,MAA2B,MAAqD;CAC3F,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACvD;GAGF,IAAM,IAAY,OAAO,KAAW,WAAW,IAAS,EAAE,EACpD,KAAA,IAAe,EAAU,iBAAA,OAAgB,KAAhB,GACzB,KAAA,IAAa,EAAU,eAAA,OAAc,KAAd,GACvB,KAAA,IAAY,EAAU,cAAA,OAAa,KAAb,GACtB,IAAmB,CAAC,GAAG,IAA2B,IAAA,IAAI,EAAU,oBAAA,OAAmB,EAAE,GAArB,EAAuB,EAGvF,IAAU,EAAQ,MAAM,cAAc,8BAA8B,EACxE,aAAa,+CACd,CAAC;GAEF,SAAwB;IACtB,IAAQ,OAAO,iBAAiB;;KAC9B,IAAM,IAAO,SAAS,cAAc,EAAa,EAC7C,IAAa,GACb,IAAe;KAEnB,KAAK,IAAM,CAAC,GAAG,MAAM,GAAe;MAClC,IAAM,IAAU,SAAS,iBAAiB,OAAO,aAAa,GAAG,OAAO,cAAc,EAAE;MACxF,IAAI,GAAW,GAAS,EAAiB,EAAE;OACzC,KAAgB;OAChB;;MAEF,CAAI,MAAY,KAAQ,MAAY,SAAS,QAAQ,MAAY,SAAS,qBACxE,KAAc;;KAIlB,IAAM,IAAQ,IAAa,EAAc;KAEzC,IAAI,MAAiB,EAAc,QACjC;KAEF,IAAM,IAAU,KAAS,GACnB,IAAa;MACjB,6BAA6B;MAC7B,iCAAiC;MACjC,4BAA4B;MAC5B,gCAAgC;MAChC,sCAAsC,GACpC,SAAS,iBAAiB,OAAO,aAAa,GAAG,OAAO,cAAc,EAAE,CACzE;MACD,uCAAA,KAAA,IAAsC,SAAS,SAAA,OAAA,KAAA,IAAA,EAAM,qBAAqB,IAAI,CAAC,WAAA,OAAU,IAAV;MAChF;KAED,AAAI,MACF,EAAQ,IACN,GACA,EAAQ,YAAY,EAElB,4BAA4B,GAC7B,CAAC,CACH,EACD,EAAQ,QAAQ;MACd,gBAAgB,EAAe;MAC/B,cAAc;MACd,MAAM;MACN;MACD,CAAC;OAEH,EAAW;KACd;;EAEJ,WAAW;GACT,AAAI,MAAU,KAAA,KAAa,OAAO,SAAW,OAC3C,OAAO,aAAa,EAAM;;EAG/B;GCrIG,KAAW,CAAC,YAAY,WAAW,EAInC,MAAyB,OAC5B,EAAE,MAAM,GAAW,GAGhB,MACJ,GACA,MAKG;CACH,EAAK,cAAc,EAAQ,YAAY,EAAQ,OAAO,mBAAmB,CAAC,CAAC;CAC3E,KAAK,IAAM,KAAO,IAAU;;EAC1B,IAAM,KAAA,IAAQ,EAAK,eAAA,OAAA,KAAA,IAAA,EAAa;EAChC,IAAI,OAAO,KAAU,UAAU;GAE7B,IAAM,IADqB,EAAQ,YAAY,GAAG,IAAM,EAAQ,OAAO,UAAU,EAAM,EAAE,CACrE,CAAmB;GACvC,AAAI,OAAO,KAAgB,YACzB,EAAK,aAAa,GAAK,EAAY;;;GAM9B,MAAsC,MAAmD;CACpG,IAAM,IAAsC,EAAE;CAE9C,OAAO;EACL,MAAM;EACN,SAAS,GAAQ,EAAO,gBAAgB,EAAO,SAAS,EAAO,OAAO,EAAO;EAC7E,KAAW,GAAA;yBAAS;IAClB,IAAM,IAA4C,EAAE,EAC9C,IAAa,CAAC,IAAsB,MAAO,EAAgB,EAAQ,QAAQ,EAAI,CAAC,CAAC,EACjF,IAA+B,CACnC,IAAsB,MAAO,GAA2B,EAAQ,QAAQ,EAAI,CAAC,CAC9E;IAED,IAAI,EAAO,cAAc;KACvB,IAAM,EAAE,mCAAgC,MAAM,OAAO;KACrD,EAAuB,KACrB,IAAI,EAA4B,EAC9B,uBAAuB,QACxB,CAAC,CACH;;IAGH,IAAI,EAAO,OAAO;KAChB,IAAM,EAAE,4BAAyB,MAAM,OAAO;KAC9C,EAAuB,KACrB,IAAI,EAAqB;MACvB,8BAA6B,MAAQ,GAAa,GAAS,EAAY;MACvE;MACA;MACA,uBAAuB;MACvB,qBAAqB;MACtB,CAAC,CACH;;IAGH,IAAI,EAAO,KAAK;KACd,IAAM,EAAE,qCAAkC,MAAM,OAAO;KACvD,EAAuB,KACrB,IAAI,EAA8B;MAChC,8BAA6B,MAAQ,GAAa,GAAS,EAAY;MACvE;MACA;MACA,uBAAuB;MACvB,qBAAqB;MACtB,CAAC,CACH;;IAGH,IAAI,EAAO,iBAAiB;KAC1B,IAAM,EAAE,sCAAmC,MAAM,OAAO,oDAClD,IACJ,OAAO,EAAO,mBAAoB,WAC9B,EAAE,YAAY,EAAO,gBAAgB,YAAgD,GACrF,KAAA;KACN,EAAuB,KAAK,IAAI,EAA+B,EAAsB,CAAC;;IAIxF,AADA,EAAiB,KAAK,GAAG,EAAuB,EAChD,EAAyB,EAAE,kBAAkB,GAAwB,CAAC;;;EAExE,WAAW;GACT,OAAO,EAAiB,SAAQ;;IAC9B,CAAA,IAAA,EAAiB,KAAK,KAAA,SAAA,IAAA,EAAE,YAAA,QAAA,EAAA,KAAA,EAAW;;;EAGxC;GC/FU,MAA4B,MAAuD;CAC9F,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACvD;GAGF,IAAM,KAAW,MAAwC;IACvD,EAAQ,QAAQ;KACd,gBAAgB,EAAe;KAC/B,cAAc;KACd,MAAM;KACN,YAAA,EAAA,EAAA,EAAA,EACK,EAAQ,OAAO,mBAAmB,CAAA,EAAA,EAAA,EAAA;MACrC,mBAAmB,EAAQ,OAAO,UAAU,EAAM,cAAc,GAAG;MACnE,0BAA0B,EAAM;MAChC,2BAA2B,EAAM;MACjC,uBAAuB,EAAM;MAC7B,mBAAmB,EAAM;MACzB,mBAAmB,EAAQ,OAAO,UAAU,EAAM,cAAc,GAAG;MACnE,mBAAmB,EAAM;MACzB,qBAAqB,EAAM;MAC3B,mBAAmB,EAAM;OAC1B;KACF,CAAC;;GAIJ,AADA,SAAS,iBAAiB,2BAA2B,EAAQ,EAC7D,UAAiB,SAAS,oBAAoB,2BAA2B,EAAQ;;EAEnF,WAAW;GAET,AADA,KAAA,QAAA,GAAY,EACZ,IAAW,KAAA;;EAEd;GCzCG,KAA6B,oBAE7B,UACA,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,aACzD,OAAO,YAAY,GAErB,UAAU,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,IAG9D,MAAgB,MAAuB;CAC3C,IAAI;EACF,IAAM,IAAU,OAAO,aAAa,QAAQ,EAAW;EACvD,IAAI,GACF,OAAO;EAET,IAAM,IAAW,GAAU;EAE3B,OADA,OAAO,aAAa,QAAQ,GAAY,EAAS,EAC1C;aACD;EACN,OAAO,GAAU;;GAIf,WAA0C;;CAC9C,IAAI,OAAO,SAAW,KACpB,OAAO,EAAE;CAEX,IAAM,IACJ,UAGA;CAEF,OAAO;EACL,0BAA0B,OAAO;EACjC,2BAA2B,OAAO;EAClC,yBAAA,IAAwB,OAAO,WAAA,OAAA,KAAA,IAAA,EAAQ;EACvC,0BAAA,IAAyB,OAAO,WAAA,OAAA,KAAA,IAAA,EAAQ;EACxC,2BAAA,IAAA,KAAA,OAAA,KAAA,IAA0B,EAAY,kBAAA,OAAA,KAAA,OAAA,KAAA,IAAiB,EAAY,OAA7B;EACvC;GAOU,MAAsB,OAAiD;CAClF,MAAM;CACN,SAAS,EAAQ;CACjB,KAAK,GAAS;;EACZ,IAAM,IACJ,OAAO,KAAW,YAAA,IAAY,EAAO,eAAA,OAAc,KAAd,IAA4C,IAC7E,IAAW,OAAO,SAAW,MAAc,GAAU,GAAG,GAAa,EAAW;EAEtF,EAAQ,qBAAA,EAAA,EACN,aAAa,GAAA,EACV,IAAuB,CAC3B,CAAC;;CAEL,GC1DK,KAAoB,KACpB,KAAyB,GAEzB,MAAmB,MAAmB;CAC1C,IAAI,aAAiB,OACnB,OAAO,EAAM;CAEf,IAAI,OAAO,KAAU,UACnB,OAAO;CAET,IAAI;EACF,OAAO,KAAK,UAAU,EAAM;aACtB;EACN,OAAO,OAAO,EAAM;;GAIlB,MAAiB,MAAoB;;qBAAiB,SAAA,IAAS,EAAM,UAAA,OAAS,KAAT,IAAe;GAGpF,MAAc,MAA0B;CAC5C,IAAI,IAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAChC,IAAQ,IAAO,KAAM,EAAM,WAAW,EAAE;CAE1C,QAAQ,MAAS,GAAG,SAAS,GAAG;GAQ5B,MAAkB,GAAkB,MAAyB;CACjE,IAAM,oBAAU,IAAI,KAA4B;CAChD,OAAO,EAEL,MAAM,GAAsB;EAC1B,IAAM,IAAM,KAAK,KAAK,EAChB,IAAQ,EAAQ,IAAI,EAAI;EAS9B,OARI,CAAC,KAAS,IAAM,EAAM,cAAc,KACtC,EAAQ,IAAI,GAAK;GAAE,OAAO;GAAG,aAAa;GAAK,CAAC,EACzC,MAEL,EAAM,SAAS,IACV,MAET,EAAM,SAAS,GACR;IAEV;GAaG,KAAa,EAAE,SAAA,GAAS,UAAO,kBAAe,WAAQ,EAAE,EAAE,WAAQ,aAAU,kBAAiC;CACjH,IAAM,IAAU,GAAgB,EAAM,EAChC,IAAQ,GAAc,EAAM,EAC5B,IAAc,GAAW,GAAG,EAAO,GAAG,EAAQ,GAAG,EAAM,MAAM,GAAG,IAAI,GAAG;CAE7E,IAAI,CAAC,EAAS,MAAM,EAAY,EAC9B;CAGF,IAAM,IAAa,EAAM,eAAe,EAClC,IAAgB,aAAiB,QAAQ,IAAQ;EAAE;EAAS,MAAM,KAAA,OAAiB,IAAjB;EAAyB,EAC3F,IAAA,EAAA,EAAA,EAAA,EAAA,EACD,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,oBAAoB,CAAA,EAAA,EAAA,EAAA;EACtC,qBAAqB;EACrB,wBAAwB;EACxB,kBAAkB,aAAiB,QAAQ,EAAM,OAAQ,KAAA,OAAiB,IAAjB;EACzD,uBAAuB;EACvB,4BAA4B;IACzB,EACJ,EACK,UACJ,EAAQ,QAAQ;EACd,gBAAgB,EAAe;EAC/B,cAAc;EACd,MAAM;EACN;EACD,CAAC;CAGJ,IAAI,GAAY;EAGd,AAFA,EAAW,gBAAgB,EAAc,EACzC,EAAW,UAAU;GAAE,MAAM,EAAe;GAAO;GAAS,CAAC,EAC7D,GAAc;EACd;;CAIF,IAAM,IAAY,EAAQ,UAAU,GAAU,EAAW;CAIzD,AAHA,EAAU,gBAAgB,EAAc,EACxC,EAAU,UAAU;EAAE,MAAM,EAAe;EAAO;EAAS,CAAC,EAC5D,EAAY,KAAK,EAAM,QAAQ,EAAY,QAAQ,EAAE,EAAU,EAAE,EAAa,EAC9E,EAAU,KAAK;GAGJ,MAAqB,MAA+C;CAC/E,IAAM,IAA+B,EAAE;CAEvC,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;;GACZ,IAAI,OAAO,SAAW,KACpB;GAMF,IAAM,IAAW,GAHA,OAAO,KAAW,YAAA,IAAY,EAAO,aAAA,OAAY,KAAZ,IAAiC,IAErF,OAAO,KAAW,YAAA,IAAY,EAAO,iBAAA,OAAgB,KAAhB,IAA0C,GAC1B,EAEjD,KAAW,MAAsB;;IACrC,EAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,QAAA,IAAO,EAAM,UAAA,OAAS,EAAM,UAAf;KACb,OAAO;MACL,iBAAiB,EAAM;MACvB,eAAe,EAAM;MACrB,eAAe,EAAM;MACtB;KACF,CAAC;MAEE,KAAwB,MAAiC;IAC7D,EAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,OAAO,EAAM;KACb,eAAe,EAAM,kBAAkB,QAAQ,EAAM,OAAO,OAAO;KACpE,CAAC;MAEE,KAAmB,MAAiB;;IACxC,IAAM,IAAS,EAAM;IAKrB,IAAI,MAAW,QACb;IAEF,IAAM,IAAc,EAAO,OAAO,EAAO,QAAQ;IACjD,EAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,OAAO,gBAAI,MAAM,2BAAA,IAAyB,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS,aAAa,KAAI,UAAU,GAAG,IAAc;KACtG,eAAe;KACf,OAAO;MACL,YAAY,EAAQ,OAAO,UAAU,EAAY;MACjD,YAAY,EAAO,WAAW;MAC/B;KACF,CAAC;;GAMJ,AAHA,OAAO,iBAAiB,SAAS,EAAQ,EACzC,OAAO,iBAAiB,sBAAsB,EAAqB,EACnE,OAAO,iBAAiB,SAAS,GAAiB,GAAK,EACvD,EAAU,WACF,OAAO,oBAAoB,SAAS,EAAQ,QAC5C,OAAO,oBAAoB,sBAAsB,EAAqB,QACtE,OAAO,oBAAoB,SAAS,GAAiB,GAAK,CACjE;;EAEH,WAAW;GACT,OAAO,EAAU,SAAQ;;IACvB,CAAA,IAAA,EAAU,KAAK,KAAA,QAAA,GAAI;;;EAGxB;GCvKG,KAAqB,KAAK,MAI1B,KAAgB,GAAc,MAC9B,EAAK,UAAU,IACV;CACL;CACA,WAAW;CACZ,GAEI;CACL,MAAM,EAAK,MAAM,GAAG,EAAY;CAChC,WAAW;CACZ,EAGG,MAAkB,GAAkC,MAAgB;CACxE,IAAI,CAAC,GACH;CAEF,IAAI,aAAmB,SACrB,OAAO,EAAQ,IAAI,EAAI,IAAI,KAAA;CAE7B,IAAM,IAAgB,EAAI,aAAa;CACvC,IAAI,MAAM,QAAQ,EAAQ,EAAE;;EAC1B,QAAA,IAAO,EAAQ,MAAM,CAAC,OAAa,EAAQ,aAAa,KAAK,EAAc,KAAA,OAAA,KAAA,IAAA,EAAG;;CAEhF,IAAM,IAAa,OAAO,KAAK,EAAQ,CAAC,MAAK,MAAQ,EAAK,aAAa,KAAK,EAAc;CAC1F,OAAO,IAAa,EAAQ,KAAc,KAAA;GAGtC,MAAqB,MAAmB;CAC5C,IAAM,IAAkC,EAAE;CAK1C,OAJA,EAAK,SAAS,GAAO,MAAQ;EAC3B,EAAQ,KACN,aAAiB,OAAO,cAAc,EAAM,KAAK,QAAQ,EAAM,QAAQ,UAAU,QAAQ,EAAM,KAAK,KAAK;GAC3G,EACK,KAAK,UAAU,EAAQ;GAG1B,KAAA,WAAA;sBAAuB,GAAyC;EAyBpE,OAxBI,KAAQ,OACH,KAEL,OAAO,KAAS,WACX,IAEL,aAAgB,kBACX,EAAK,UAAU,GAEpB,aAAgB,WACX,GAAkB,EAAK,GAE5B,aAAgB,OACX,EAAK,MAAM,GAEhB,aAAgB,eAGhB,YAAY,OAAO,EAAK,GACnB,IAAI,aAAa,CAAC,OAAO,EAAK,GAEnC,aAAgB,WACX,IAAI,eAAe,CAAC,kBAAkB,EAAK,GAE7C,OAAO,EAAK;;iBAzBQ,GAAA;;;KA4BvB,KAAA,WAAA;sBACJ,GACA,GACA,GACsC;EACtC,IAAM,IAAU,MAAM,GAAc,EAAK;EACpC,OAGL,OAAA,EAAA,EAAA,EAAA,EACK,EAAa,GAAS,EAAY,CAAA,EAAA,EAAA,EAAA,EACrC,gBAAA,CACD;;iBAXD,GACA,GACA,GAAA;;;KAYI,IAAA,WAAA;sBAAgC,GAAqB,GAAqB,GAAyB;EACvG,IAAI;GACF,OAAO,MAAM,GAAmB,GAAM,GAAa,EAAY;cACzD;GACN;;;iBAJkC,GAAqB,GAAqB,GAAA;;;KAQ1E,MAAoB,MACpB,aAAiB,UACZ,EAAM,MAER,OAAO,EAAM,EAGhB,MAAkB,GAA0B,MAChD,KAAA,QAAI,EAAM,SACD,EAAK,SAEV,aAAiB,UACZ,EAAM,SAER,OAGH,KAAA,WAAA;sBAA6B,GAA0B,GAA+B,GAAwB;EAClH,IAAA,KAAA,QAAI,EAAM,MACR,OAAO,EACL,EAAK,MACL,GACA,GAAe,EAAK,SAAgC,eAAe,CACpE;EAEH,IAAI,aAAiB,SACnB,IAAI;GACF,OAAO,EACL,MAAM,EAAM,OAAO,CAAC,MAAM,EAC1B,GACA,EAAM,QAAQ,IAAI,eAAe,IAAI,KAAA,EACtC;cACK;GACN;;;iBAhB6B,GAA0B,GAA+B,GAAA;;;KAsBtF,KAAA,WAAA;sBAAyB,GAAoB,GAAwB;EACzE,IAAI;GACF,IAAM,IAAiB,EAAS,OAAO;GACvC,OAAO,EACL,EAAe,OAAO,MAAM,EAAe,MAAM,GAAG,IACpD,GACA,EAAS,QAAQ,IAAI,eAAe,IAAI,KAAA,EACzC;cACK;GACN;;;iBAT2B,GAAoB,GAAA;;;KAa7C,MAAsB,GAAqB,MAAkD;CACjG,IAAI,EAAI,iBAAiB,MAAM,EAAI,iBAAiB,QAClD,OAAA,EAAA,EAAA,EAAA,EACK,EAAa,EAAI,gBAAgB,IAAI,EAAY,CAAA,EAAA,EAAA,EAAA,EACpD,aAAa,EAAI,kBAAkB,eAAe,IAAI,KAAA,GAAA,CACvD;CAEH,IAAI,EAAI,iBAAiB,QACvB,OAAA,EAAA,EAAA,EAAA,EACK,EAAa,KAAK,UAAU,EAAI,SAAS,EAAE,EAAY,CAAA,EAAA,EAAA,EAAA,EAC1D,aAAa,EAAI,kBAAkB,eAAe,IAAI,KAAA,GAAA,CACvD;GAKC,MACJ,GACA,GACA,MACG;CACE,OAGL,OAAO,EAAO,OAAA,EAAA,EAAA,EAAA,EACT,EAAA,EAAA,EAAA,EAAA;EACH,MAAM,EAAS;EACf,WAAW,EAAS;GACrB,CAAC;GAGE,KAAkB,EACtB,YACA,aACA,UACA,WACA,YACA,aACA,WACA,aAC2B;CAC3B,IAAM,IAAS,EAAQ,OAAO,IAAI;CAClC,IAAI,CAAC,GACH;CAGF,IAAM,IAAU,EAAQ,KAAW,OAAO,KAAW,YAAY,KAAU,KACrE,IAAmB,EAAO,aAAa,EACvC,IAAc,IAChB,GACE,GACA;EACE,aAAA,KAAA,OAAA,KAAA,IAAa,EAAS;EACtB,QAAQ;EACR;EACA,MAAM;EACN;EACD,EACD,EACD,GACD,KAAA,GACE,IAAe,IACjB,GACE,GACA;EACE,aAAA,KAAA,OAAA,KAAA,IAAa,EAAU;EACvB,QAAQ;EACR;EACA,MAAM;EACN;EACD,EACD,EACD,GACD,KAAA,GACE,IAAA,EAAA,EAAA,EAAA,EACD,EAAQ,OAAO,mBAAmB,CAAA,EAAA,EAAA,EAAA;EACrC,iBAAiB;EACjB,uBAAuB;EACvB,YAAY,EAAQ,OAAO,UAAU,EAAI;GAC1C;CAWD,IATI,OAAO,KAAW,aACpB,EAAW,+BAA+B,IAExC,KAAe,SACjB,EAAW,uBAAuB,IAEhC,KAAgB,SAClB,EAAW,wBAAwB,IAEjC,GAAS;;EAEX,AADA,EAAW,yCAAA,IAAA,KAAA,OAAA,KAAA,IAAwC,EAAS,cAAA,OAAa,KAAb,GAC5D,EAAW,0CAAA,IAAA,KAAA,OAAA,KAAA,IAAyC,EAAU,cAAA,OAAa,KAAb;;CAEhE,IAAI,aAAiB,OAAO;;EAG1B,AAFA,EAAW,uBAAuB,EAAM,SACxC,EAAW,oBAAoB,EAAM,MACrC,EAAW,2BAAA,IAA0B,EAAM,UAAA,OAAS,KAAT;;CAG7C,EAAQ,QAAQ;EACd,gBAAgB,IAAU,EAAe,QAAQ,EAAe;EAChE,cAAc,IAAU,UAAU;EAClC,MAAM,IAAU,sCAAsC;EACtD;EACD,CAAC;GAGS,MAAwB,MAA6D;CAChG,IAAM,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;GACZ,IAAI,CAAC,KAAU,OAAO,SAAW,KAC/B;GAGF,IAAM,IAAc,EAAO,eAAe,IACpC,IAAgB,OAAO;GAC7B,AAAI,OAAO,KAAkB,eAC3B,OAAO,QAAA,WAAA;yBAAe,GAA0B,GAAuB;KACrE,IAAM,IAAM,GAAiB,EAAM;KACnC,IAAI,EAAgB,EAAQ,QAAQ,EAAI,EACtC,OAAO,EAAc,GAAO,EAAK;KAEnC,IAAM,IAAS,GAAe,GAAO,EAAK,EACpC,IAAU,MAAM,GAAoB,GAAO,GAAM,EAAY,EAC7D,IAAY,YAAY,KAAK;KACnC,IAAI;MACF,IAAM,IAAW,MAAM,EAAc,GAAO,EAAK;MAYjD,OATA,EAAe;OACb;OACA,UAJe,YAAY,KAAK,GAAG;OAKnC;OACA;OACA,UANmB,EAAS,UAAU,MAAM,MAAM,GAAgB,GAAU,EAAY,GAAG,KAAA;OAO3F,QAAQ,EAAS;OACjB;OACD,CAAC,EACK;cACA,GAAO;MASd,MARA,EAAe;OACb;OACA,UAAU,YAAY,KAAK,GAAG;OAC9B;OACA;OACA;OACA;OACD,CAAC,EACI;;;oBA/BY,GAA0B,GAAA;;;QAkChD,EAAkB,WAAW;IAC3B,OAAO,QAAQ;KACf;GAGJ,IAAM,IAAe,eAAe,UAAU,MACxC,IAAe,eAAe,UAAU,MACxC,IAA2B,eAAe,UAAU;GAsD1D,AArDA,eAAe,UAAU,OAAO,SAE9B,GACA,GACA,GAAG,GACH;;IAMA,OALA,KAAK,uBAAuB;KAC1B;KACA,WAAW,YAAY,KAAK;KAC5B,KAAK,OAAO,EAAI;KACjB,EACM,EAAa,KAAK,MAAM,GAAQ,IAAA,IAAK,EAAK,OAAA,OAAM,KAAN,GAAY,EAAK,IAAI,EAAK,GAAG;MAEhF,eAAe,UAAU,mBAAmB,SAE1C,GACA,GACA;;IAKA,OAJA,KAAK,iCAAA,EAAA,EAAA,EAAA,GAAA,IACC,KAAK,mCAAA,OAAkC,EAAE,GAApC,EAAoC,EAAA,EAAA,EAAA,GAC5C,IAAO,GAAA,CACT,EACM,EAAyB,KAAK,MAAM,GAAM,EAAM;MAEzD,eAAe,UAAU,OAAO,SAE9B,GACA;IAwBA,OAvBA,EACE,GACA,GACA,GAAe,KAAK,gCAAgC,eAAe,CACpE,CAAC,MAAK,MAAW;KAChB,KAAK,0BAA0B;MAC/B,EACF,KAAK,iBAAiB,iBAAiB;KACrC,IAAM,IAAO,KAAK;KAClB,IAAI,CAAC,KAAQ,EAAgB,EAAQ,QAAQ,EAAK,IAAI,EACpD;KAEF,IAAM,IAAe,KAAK,UAAU,MAAM,GAAmB,MAAM,EAAY,GAAG,KAAA;KAClF,EAAe;MACb;MACA,UAAU,YAAY,KAAK,GAAG,EAAK;MACnC,QAAQ,EAAK;MACb,SAAS,KAAK;MACd,UAAU;MACV,QAAQ,KAAK;MACb,KAAK,EAAK;MACX,CAAC;MACF,EACK,EAAa,KAAK,MAAM,EAAK;MAEtC,EAAkB,WAAW;IAG3B,AAFA,eAAe,UAAU,OAAO,GAChC,eAAe,UAAU,OAAO,GAChC,eAAe,UAAU,mBAAmB;KAC5C;;EAEJ,WAAW;GACT,OAAO,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;;EAGhC;GC1YG,KAAuB,IAkBhB,MAAwB,MAAkD;CACrF,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;;GACZ,IAAI,OAAO,sBAAwB,KACjC;GAEF,IAAM,IAAc,oBAAsE;GAC1F,IAAI,EAAA,KAAA,QAAC,EAAY,SAAS,WAAW,GACnC;GAGF,IAAM,IAAY,OAAO,KAAW,YAAA,IAAY,EAAO,cAAA,OAAa,KAAb,IAAqC,IACtF,IAAU,EAAQ,MAAM,cAAc,2BAA2B,EACrE,aAAa,yDACd,CAAC,EACI,IAAoB,EAAQ,MAAM,gBAAgB,8BAA8B;IACpF,MAAM;IACN,aAAa;IACd,CAAC;GAEF,IAAI;IAiBF,AAhBA,IAAW,IAAI,qBAAoB,MAAQ;KACzC,KAAK,IAAM,KAAS,EAAK,YAAY,EAAqB;;MACxD,IAAI,EAAM,WAAW,GACnB;MAEF,IAAM,KAAA,IAAc,EAAM,gBAAA,OAAA,KAAA,IAAA,EAAc,IAClC,IAAa,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACtB,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,qBAAqB,CAAA,EAAA,EAAA,EAAA;OACvC,kBAAkB,EAAM;OACxB,0BAAA,IAAA,KAAA,OAAA,KAAA,IAAyB,EAAa,SAAA,OAAQ,YAAR;QACvC,CAAC;MAEF,AADA,EAAQ,IAAI,GAAG,EAAW,EAC1B,EAAkB,OAAO,EAAM,UAAU,EAAW;;MAEtD,EACF,EAAS,QAAQ;KAAE,MAAM;KAAY,UAAU;KAAM,CAAC;eAChD;IACN,IAAW,KAAA;;;EAGf,WAAW;GAET,AADA,KAAA,QAAA,EAAU,YAAY,EACtB,IAAW,KAAA;;EAEd;GC7DG,oBAAc,IAAI,KAAyB,EAE7C,IAAU,IACV,GACA,GACA,IAAU,IAER,UAAuB,OAAO,WAAa,MAAc,KAAK,SAAS,MAEvE,KAAmB,GAA2B,MAAoB;CACtE,IAAM,IAAQ,GAAe;CAC7B,IAAU;CACV,IAAM,IAA0B;EAAE;EAAS;EAAQ;EAAO;CAE1D,KAAK,IAAM,KAAc,MAAM,KAAK,EAAY,EAC9C,EAAW,EAAM;GAIf,WAAmB;CACvB,EAAgB,YAAY,KAAW,GAAe,CAAC;GAGnD,WAAqB;CACzB,EAAgB,cAAc,KAAW,GAAe,CAAC;GAGrD,WAAqB;CACrB,KAAW,OAAO,SAAW,OAAe,OAAO,UAAY,QAInE,IAAU,IACV,IAAU,GAAe,EACzB,IAAoB,QAAQ,WAC5B,IAAuB,QAAQ,cAE/B,QAAQ,YAAY,SAAyC,GAAW,GAAe,GAA2B;EAChH,IAAM,IAAU,KAAW,GAAe,EACpC,IAAA,KAAA,OAAA,KAAA,IAAS,EAAmB,MAAM,MAAM;GAAC;GAAM;GAAO;GAAI,CAAC;EAEjE,OADA,EAAgB,aAAa,EAAQ,EAC9B;IAET,QAAQ,eAAe,SAErB,GACA,GACA,GACA;EACA,IAAM,IAAU,KAAW,GAAe,EACpC,IAAA,KAAA,OAAA,KAAA,IAAS,EAAsB,MAAM,MAAM;GAAC;GAAM;GAAO;GAAI,CAAC;EAEpE,OADA,EAAgB,gBAAgB,EAAQ,EACjC;IAET,OAAO,iBAAiB,YAAY,GAAW,EAC/C,OAAO,iBAAiB,cAAc,GAAa;GAG/C,WAAuB;CACvB,CAAC,KAAW,OAAO,SAAW,OAAe,OAAO,UAAY,QAIhE,MACF,QAAQ,YAAY,IAElB,MACF,QAAQ,eAAe,IAEzB,OAAO,oBAAoB,YAAY,GAAW,EAClD,OAAO,oBAAoB,cAAc,GAAa,EACtD,IAAoB,KAAA,GACpB,IAAuB,KAAA,GACvB,IAAU,IACV,IAAU;GAGC,MAAwB,MAC/B,OAAO,SAAW,OAAe,OAAO,UAAY,YACzC,MAGf,IAAc,EACd,EAAY,IAAI,EAAQ,QAEX;CAEX,AADA,EAAY,OAAO,EAAQ,EACvB,EAAY,SAAS,KACvB,IAAgB;IC7FhB,WACA,OAAO,WAAa,MACf,KAEF,SAAS,MAGL,MAAwB,MAAmD;CACtF,IAAM,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,KACpB;GAIF,IAAI,IAAU,IAER,KAAgB,GAAgB,IAAc,MAAY;IAC9D,IAAM,IAAa,IAAe;IAC9B,MAAe,MAGnB,IAAU,GASV,EAPqB,UAAU,qBAAA,EAAA,EAAA,EAAA,EAC1B,EAAQ,OAAO,mBAAmB,CAAA,EAAA,EAAA,EAAA;KACrC,YAAY,EAAQ,OAAO,UAAU,EAAW;KAChD,gBAAgB,EAAQ,OAAO,UAAU,EAAY;KACrD,qBAAqB,EAAQ,OAAO,UAAU,SAAS,YAAY,GAAG;KACtE,uBAAuB;MACxB,CACD,CAAK,KAAK;MAGN,IAAc,IAAqB,MAAS;IAChD,EAAa,EAAM,QAAQ,EAAM,QAAQ;KACzC;GAGF,AAFA,EAAa,OAAO,EAEpB,EAAkB,KAAK,EAAY;;EAErC,WAAW;GACT,OAAO,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;;EAGhC;GC9CU,MAA2B,MAAsD;CAC5F,IAAM,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,UAAY,KACtD;GAGF,IAAM,IAAY,EAAQ,MAAM,gBAAgB,iCAAiC;IAC/E,MAAM;IACN,aAAa;IACd,CAAC,EAEI,KAAW,GAAgB,GAAiB,MAAkB;IAClE,IAAM,IAAY,YAAY,KAAK,EAC7B,UAAiB;KACrB,IAAM,IAAW,YAAY,KAAK,GAAG,GAC/B,IAAa,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACtB,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,qBAAqB,CAAA,EAAA,EAAA,EAAA,EACvC,uBAAuB,GAAA,CACxB,CAAC;KAQF,AAPA,EAAU,OAAO,GAAU,EAAW,EAOtC,EANqB,UAAU,wBAAA,EAAA,EAAA,EAAA,EAC1B,EAAA,EAAA,EAAA,EAAA;MACH,YAAY,EAAQ,OAAO,UAAU,EAAM;MAC3C,gBAAgB,EAAQ,OAAO,UAAU,EAAQ;MACjD,4BAA4B;OAC7B,CACD,CAAK,KAAK;;IAGZ,4BAA4B,4BAA4B,WAAW,GAAU,EAAE,CAAC,CAAC;MAG7E,IAAc,IAAqB,MAAS;IAC5C,EAAM,YAAY,EAAM,SAG5B,EAAQ,EAAM,QAAQ,EAAM,SAAS,EAAM,MAAM;KACjD;GAEF,EAAkB,KAAK,EAAY;;EAErC,WAAW;GACT,OAAO,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;;EAGhC;GC1DG,KAA8B,iBAC9B,KAAwB,OAAU,KAQlC,WACA,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,aACzD,OAAO,YAAY,GAErB,WAAW,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,IAG/D,MAAc,MAA6C;CAC/D,IAAI;;EACF,IAAM,KAAA,IAAM,OAAO,eAAe,QAAQ,EAAW,KAAA,OAAI,OAAO,aAAa,QAAQ,EAAW,GAA3C;EACrD,IAAI,CAAC,GACH,OAAO;EAET,IAAM,IAAS,KAAK,MAAM,EAAI;EAI9B,OAHI,QAAA,KAAA,OAAA,KAAA,IAAO,EAAQ,OAAO,YAAY,QAAA,KAAA,OAAA,KAAA,IAAO,EAAQ,OAAO,WACnD,IAEF;aACD;EACN,OAAO;;GAIL,MAAe,GAAoB,MAA0B;CACjE,IAAI;EACF,IAAM,IAAa,KAAK,UAAU,EAAO;EAGzC,AAFA,OAAO,eAAe,QAAQ,GAAY,EAAW,EAErD,OAAO,aAAa,QAAQ,GAAY,EAAW;aAC7C;GAKJ,MAAoB,GAAoB,MAAyB;CACrE,IAAM,IAAM,KAAK,KAAK,EAChB,IAAU,GAAW,EAAW;CACtC,IAAI,KAAW,IAAM,EAAQ,KAAK,GAAc;EAC9C,IAAM,IAAwB;GAAE,IAAI,EAAQ;GAAI,IAAI;GAAK;EAEzD,OADA,GAAY,GAAY,EAAO,EACxB,EAAO;;CAEhB,IAAM,IAAwB;EAAE,IAAI,IAAU;EAAE,IAAI;EAAK;CAEzD,OADA,GAAY,GAAY,EAAO,EACxB,EAAO;GAOH,MAAuB,MAAiD;CACnF,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;;GACZ,IAAM,IACJ,OAAO,KAAW,YAAA,IAAY,EAAO,eAAA,OAAc,KAAd,IAA6C,IAC9E,IACJ,OAAO,KAAW,YAAA,IAAY,EAAO,iBAAA,OAAgB,KAAhB,IAAyC,IAE1E,UAAgB;IACpB,IAAM,IAAY,OAAO,SAAW,MAAc,IAAU,GAAG,GAAiB,GAAY,EAAa;IACzG,EAAQ,qBAAqB,EAAE,cAAc,GAAW,CAAC;;GAK3D,IAFA,GAAS,EAEL,OAAO,SAAW,OAAe,OAAO,WAAa,KACvD;GAIF,IAAM,UAA2B;IAC/B,AAAI,SAAS,oBAAoB,aAC/B,GAAS;;GAIb,AADA,SAAS,iBAAiB,oBAAoB,EAAmB,EACjE,UAAiB,SAAS,oBAAoB,oBAAoB,EAAmB;;EAEvF,WAAW;GAET,AADA,KAAA,QAAA,GAAY,EACZ,IAAW,KAAA;;EAEd;GC5FG,WAA0B;CAC9B,IAAM,IAAa,YAAY,iBAAiB,aAAa,CAAC;CAE9D,QAAA,KAAA,OAAA,KAAA,IAAO,EAAY,SAAQ;GAIvB,KAAuB,OAAmC;CAC9D,uBAAuB,IAAmB;CAC1C,kBAAkB,EAAO;CACzB,oBAAoB,EAAO;CAC5B,GAGK,MAAmB,GAAmB,MAAmD;;CAC7F,IAAM,KAAA,IAAO,EAAO,gBAAA,OAAe,EAAE,GAAjB,GACd,IAAqB,EAAE,EACvB,KAAgB,MAAiB,EAAiC,IAElE,KAAgB,GAAa,GAAgB,MAA0C;EACvF,KAAiC,SACjC,OAAO,KAAU,YAAY,OAAO,KAAU,YAAY,OAAO,KAAU,eAC7E,EAAO,KAAO,OAAO,KAAU,YAAY,IAAY,EAAU,EAAM,GAAG;;CAI9E,QAAQ,EAAO,MAAf;EACE,KAAK;GAKH,AAJA,EAAa,qBAAqB,EAAa,MAAM,EAAE,EAAU,EACjE,EAAa,wBAAwB,EAAa,SAAS,CAAC,EAC5D,EAAa,sCAAsC,EAAa,qBAAqB,CAAC,EACtF,EAAa,wCAAwC,EAAa,uBAAuB,CAAC,EAC1F,EAAa,oCAAoC,EAAa,kBAAkB,CAAC;GACjF;EACF,KAAK;GAGH,AAFA,EAAa,sCAAsC,EAAa,qBAAqB,CAAC,EACtF,EAAa,qCAAqC,EAAa,oBAAoB,CAAC,EACpF,EAAa,4BAA4B,EAAa,YAAY,CAAC;GACnE;EACF,KAAK;GAKH,AAJA,EAAa,oCAAoC,EAAa,oBAAoB,CAAC,EACnF,EAAa,kCAAkC,EAAa,kBAAkB,CAAC,EAC/E,EAAa,6BAA6B,EAAa,aAAa,CAAC,EACrE,EAAa,qCAAqC,EAAa,qBAAqB,CAAC,EACrF,EAAa,oCAAoC,EAAa,oBAAoB,CAAC;GACnF;EACF,KAAK;GAEH,AADA,EAAa,oCAAoC,EAAa,kBAAkB,CAAC,EACjF,EAAa,4BAA4B,EAAa,YAAY,CAAC;GACnE;EACF,KAAK;GAIH,AAHA,EAAa,mCAAmC,EAAa,kBAAkB,CAAC,EAChF,EAAa,+BAA+B,EAAa,cAAc,CAAC,EACxE,EAAa,sCAAsC,EAAa,qBAAqB,CAAC,EACtF,EAAa,mCAAmC,EAAa,kBAAkB,CAAC;GAChF;EACF,SACE;;CAEJ,OAAO;GAGI,MAAyB,OAAqD;CACzF,MAAM;CACN,SAAS,EAAQ;CACjB,KAAW,GAAA;wBAAS;GAClB,IAAI,OAAO,SAAW,KACpB;GAGF,IAAM,EAAE,UAAO,UAAO,UAAO,UAAO,cAAW,MAAM,OAAO,2BACtD,IAAe,EAAQ,MAAM,gBAAgB,yBAAyB;IAC1E,MAAM;IACN,aAAa;IACd,CAAC,EACI,IAAoB,EAAQ,MAAM,gBAAgB,8BAA8B;IACpF,MAAM;IACN,aAAa;IACd,CAAC,EAEI,KAAc,MAAsB;IAUxC,EATqB,UAAU,qBAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAC1B,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,qBAAqB,CAAA,EACpC,EAAoB,EAAO,CAAA,EAAA,EAAA,EAAA;KAE9B,gBAAgB,EAAO;KACvB,mBAAmB,EAAO;OACvB,GAAgB,GAAQ,EAAQ,OAAO,UAAU,CACrD,CACD,CAAK,KAAK;MAGN,KAAwB,MAAsB;IASlD,AARA,EAAkB,OAChB,EAAO,OACP,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACH,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,qBAAqB,CAAA,EACpC,EAAoB,EAAO,CAC/B,CAAC,CACH,EACD,EAAW,EAAO;;GAiBpB,AAdA,GAAM,MAAU;IASd,AARA,EAAa,OACX,EAAO,OACP,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACH,EAAQ,OAAO,mBAAmB,CAAA,EAClC,EAAQ,OAAO,qBAAqB,CAAA,EACpC,EAAoB,EAAO,CAC/B,CAAC,CACH,EACD,EAAW,EAAO;KAClB,EACF,EAAM,EAAqB,EAC3B,EAAM,EAAqB,EAC3B,EAAM,EAAqB,EAC3B,EAAO,EAAqB;;;CAE/B,GCxHK,MAAwB,MACxB,KAAQ,OAAa,IACrB,OAAO,KAAS,WACX,OAAO,cAAgB,MAAc,EAAK,SAAS,IAAI,aAAa,CAAC,OAAO,EAAK,CAAC,aAEvF,aAAgB,eAChB,YAAY,OAAO,EAAK,GAAS,EAAK,aACtC,OAAO,OAAS,OAAe,aAAgB,OAAa,EAAK,OAC9D,GAGH,MAA0B,GAAa,MAAuC;CAClF,IAAM,IAAyC;EAC7C,YAAY,EAAU,EAAI;EAC1B,yBAAyB;EAC1B;CAED,IAAI;EAEF,EAAe,oBAAoB,IADhB,IAAI,GAAK,OAAO,WAAa,MAAc,qBAAqB,SAAS,KACzD,CAAO;aACpC;CAIR,OAAO;EACL;EACA,kBAAkB,EAChB,yBAAyB,aAC1B;EACF;GAGU,MAAyB,MAAoD;CACxF,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,EAAQ;EACjB,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAsB,OAAO,cAAc,QAC/D;GAGF,IAAM,IAAkB,OAAO;GAC/B,IAAoB;GAEpB,IAAM,IAAiB,EAAQ,MAAM,cAAc,mCAAmC,EACpF,aAAa,+CACd,CAAC,EACI,IAAe,EAAQ,MAAM,cAAc,mCAAmC;IAClF,MAAM;IACN,aAAa;IACd,CAAC,EACI,IAAe,EAAQ,MAAM,cAAc,iCAAiC,EAChF,aAAa,0CACd,CAAC,EAEI,IAAmB,SAA2B,GAAmB,GAA+B;IACpG,IAAM,IAAW,EAAI,UAAU;IAG/B,IAAI,EAAgB,EAAQ,QAAQ,EAAS,EAC3C,OAAO,MAAc,KAAA,IAAY,IAAI,EAAgB,EAAI,GAAG,IAAI,EAAgB,GAAK,EAAU;IAGjG,IAAM,EAAE,qBAAkB,sBAAmB,GAAuB,GAAU,EAAQ,OAAO,UAAU,EAEjG,IAAc,EAAQ,UAAU,qBAAqB,EAAe,EACpE,IAAY,YAAY,KAAK,EAC7B,IAAS,MAAc,KAAA,IAAY,IAAI,EAAgB,EAAI,GAAG,IAAI,EAAgB,GAAK,EAAU,EACnG,IAAe,IAEb,KAAkB,MAA+B;KACjD,MACJ,IAAe,IACX,MAAW,WACb,EAAY,UAAU;MAAE,MAAM,EAAe;MAAO,SAAS;MAA4B,CAAC,EAE5F,EAAY,aAAa,iCAAiC,YAAY,KAAK,GAAG,EAAU,EACxF,EAAY,KAAK;;IAwBnB,AArBA,EAAO,iBAAiB,cAAc,EAAe,SAAS,CAAC,EAE/D,EAAO,iBAAiB,YAAW,MAAS;KAE1C,AADA,EAAe,IAAI,GAAA,EAAA,EAAA,EAAA,EAAQ,EAAA,EAAA,EAAA,EAAA,EAAkB,uBAAuB,MAAA,CAAM,CAAC,EAC3E,EAAa,IAAI,GAAqB,EAAM,KAAK,EAAA,EAAA,EAAA,EAAA,EAC5C,EAAA,EAAA,EAAA,EAAA,EACH,uBAAuB,MAAA,CACxB,CAAC;MACF,EAEF,EAAO,iBAAiB,eAAe;KAGrC,AAFA,EAAa,IAAI,GAAG,EAAiB,EACrC,EAAe,QAAQ,EACvB,EAAQ,QAAQ;MACd,gBAAgB,EAAe;MAC/B,cAAc;MACd,MAAM;MACN,YAAY;MACb,CAAC;MACF,EAEF,EAAO,iBAAiB,UAAS,MAAS;KAExC,AADA,EAAe,SAAS,EACxB,EAAQ,QAAQ;MACd,gBAAgB,EAAe;MAC/B,cAAc;MACd,MAAM;MACN,YAAA,EAAA,EAAA,EAAA,EACK,EAAA,EAAA,EAAA,EAAA;OACH,wBAAwB,EAAM;OAC9B,0BAA0B,EAAM;OAChC,6BAA6B,EAAM;QACpC;MACF,CAAC;MACF;IAGF,IAAM,IAAe,EAAO,KAAK,KAAK,EAAO;IAU7C,OATA,EAAO,QAAQ,OACb,EAAe,IAAI,GAAA,EAAA,EAAA,EAAA,EAAQ,EAAA,EAAA,EAAA,EAAA,EAAkB,uBAAuB,OAAA,CAAO,CAAC,EAC5E,EAAa,IAAI,GAAqB,EAAK,EAAA,EAAA,EAAA,EAAA,EACtC,EAAA,EAAA,EAAA,EAAA,EACH,uBAAuB,OAAA,CACxB,CAAC,EACK,EAAa,EAAK,GAGpB;;GAOT,AAHA,EAAiB,YAAY,EAAgB,WAC7C,OAAO,OAAO,GAAkB,EAAgB,EAEhD,OAAO,YAAY;;EAErB,WAAW;GACT,AAAI,KAAqB,OAAO,SAAW,QACzC,OAAO,YAAY;;EAGxB;GCrJG,IAAqB,qBAGrB,qBAAc,IAAI,KAAsB,EAExC,MAAc,MAAgB,GAAY,IAAI,EAAI,EAClD,MAAe,GAAa,OAChC,GAAY,IAAI,GAAK,EAAM,EACpB,IAGI,MAAiB,EAAE,oBAA2D;CACzF,IAAI,KAAc,GAChB,OAAO;CAET,IAAI,KAAc,GAChB,OAAO;CAGT,IAAI;EACF,IAAM,IAAS,eAAe,QAAQ,EAAmB;EACzD,IAAI,GACF,OAAO,MAAW;EAEpB,IAAM,IAAU,KAAK,QAAQ,GAAG;EAGhC,OAFA,eAAe,QAAQ,GAAoB,IAAU,MAAM,IAAI,EAC/D,GAAY,GAAoB,EAAQ,EACjC;aACD;EACN,IAAM,IAAS,GAAW,EAAmB;EAI7C,OAHI,OAAO,KAAW,YACb,IAEF,GAAY,GAAoB,KAAK,QAAQ,GAAG,EAAW;;GCiBhE,KAAyB,OAA4C;CACzE,KAAK,EAAO;CACZ,SAAS,EAAO;CAChB,kBAAkB,EAAO;CACzB,eAAe,EAAO;CACvB,GAIK,IAA6B,UAK7B,MACJ,GACA,GACA,MAEK,IAGE,IAAI,MAAM,GAAU,EACzB,IAAI,GAAQ,GAAM,GAAU;CAC1B,IAAI,MAAS,UACX,QAAQ,GAAkB,MAAsB;;EAE9C,QADA,IAAA,WAAW,YAAA,QAAA,EAAS,IAAI,WAAW,EAAO,IAAI,EAAQ,EAC9C,EAAuD,OAAO,GAAS,EAAS;;CAG5F,IAAM,IAAQ,QAAQ,IAAI,GAAQ,GAAM,EAAS;CACjD,OAAO,OAAO,KAAU,aAAa,EAAM,KAAK,EAAO,GAAG;GAE7D,CAAC,GAbO,GAiBL,MAAkB,MAA+C,CACrE,GAAmC,EAAO,uBAAuB,CAClE,EAGK,MAAiB,MAA+C;CACpE,GAAmB,EAAO,IAAI,OAAO;CACrC,GAAqB,EAAO,IAAI,SAAS;CACzC,GAAoB,EAAO,IAAI,QAAQ;CACvC,GAAqB,EAAO,IAAI,SAAS;CACzC,GAAkB,EAAO,IAAI,MAAM;CACnC,GAAsB,EAAO,IAAI,UAAU;CAC3C,GAAwB,EAAO,IAAI,YAAY;CAC/C,GAAsB,EAAO,IAAI,UAAU;CAC3C,GAAqB,EAAO,IAAI,SAAS;CACzC,GAAyB,EAAO,IAAI,aAAa;CACjD,GAAwB,EAAO,IAAI,YAAY;CAChD,EAIY,KAAb,MAAqD;CAYnD,kBAA0B,GAAgB;EACxC,IAAI,KAAK,UAAU,QACjB,MAAU,MAAM,WAAW,EAAO,6EAA6E;;CAyKnH,YAAY,GAAyB;;EAEnC,QAxLF,kBAAA,KAAA,EAAA,UACA,iBAAA,KAAA,EAAA,UACA,SAAmC,OAAA,UACnC,iBAAA,KAAA,EAAA,UACA,WAAA,KAAA,EAAA,UACA,8BAAA,KAAA,EAAA,UACA,qBAAA,KAAA,EAAA,UACA,WAAA,KAAA,EAAA,UAEA,qBAAwD,EAAE,CAAA,UAC1D,kBAAA,KAAA,EAAA,UAMA,gBAA+B,MAA2B,KAAK,OAAO,iBAAiB,EAAW,CAAA,UAElG,UAAA,KAAA,EAAA,UAEA,YAA2B,MAAsB;;GAC/C,IAAM,IAAA,EAAA,EAAA,EAAA,EACD,EAAA,EAAA,EAAA,EAAA,EACH,YAAY,KAAK,YAAA,EAAA,EAAA,EAAA,EACZ,KAAK,sBAAsB,CAAA,GAAA,IACzB,EAAO,eAAA,OAAc,EAAE,GAAhB,EACb,CAAC,EAAA,CACH;GACD,KAAK,OAAO,KAAK,EAAO;cAE1B,SAAA,EAAA,aAAoC;GAIlC,AAHA,MAAM,EAAK,cAAc,OAAO,EAChC,MAAM,EAAK,eAAe,YAAY,EACtC,MAAM,EAAK,cAAc,YAAY,EACrC,MAAM,EAAK,eAAe,YAAY;eAGxC,8BAAA,EAAA,EAAA,EAAmD,KAAK,kBAAmB,CAAA,UAC3E,UAAA,KAAA,EAAA,UAEA,SAAA,KAAA,EAAA,UAEA,sBAAqC,EAAE,gBAAa,EAAE,EAAE,UAAO,cAAmC;GAChG,IAAI,CAAC,KAAK,SAAS;IAEjB,IAAI,KAAK,OAAO,OAAO;;KACrB,CAAA,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,+CAA+C,EAAK;;IAE/E;;GAGF,IAAM,IAAO,KAAK,UAAU,UAAU,KAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACjC,KAAK,OAAO,mBAAmB,CAAA,EAC/B,KAAK,OAAO,qBAAqB,CAAA,EACjC,EAAA,EAAA,EAAA,EAAA,EACH,mBAAmB,GAAA,CACpB,CAAC;GAUF,AARI,MACF,EAAK,gBAAgB,EAAM,EAC3B,EAAK,UAAU;IACb,MAAM,EAAe;IACrB,SAAS,EAAM;IAChB,CAAC,GAGJ,EAAK,KAAK;cAGZ,yBAAwC,MAA2B;GACjE,OAAO,OAAO,KAAK,mBAAmB,EAAW;cAGnD,YAAA,EAAA,aAAuC;GACjC,MAAK,UAAU,WAInB;SADA,MAAM,EAAK,OAAO,EACX,EAAK,kBAAkB,SAAQ;;KACpC,CAAA,IAAA,EAAK,kBAAkB,KAAK,KAAA,QAAA,GAAI;;IAGlC,KADA,MAAM,EAAK,cAAc,UAAU,EAC5B,EAAK,2BAA2B,SAAQ;;KAC7C,CAAA,IAAA,EAAK,2BAA2B,KAAK,KAAA,SAAA,IAAA,EAAE,YAAA,QAAA,EAAA,KAAA,EAAW;;IAQpD,AANA,MAAM,EAAK,eAAe,UAAU,EACpC,MAAM,EAAK,cAAc,UAAU,EACnC,MAAM,EAAK,eAAe,UAAU,EACpC,EAAM,SAAS,EACf,EAAQ,SAAS,EACjB,EAAQ,SAAS,EACjB,EAAK,QAAQ;;eAGf,SAAA,EAAA,aAAoC;GAC9B,QAAK,UAAU,aAAa,EAAK,UAAU,aAG/C;QAAI,EAAK,UAAU,WAEjB,MAAU,MAAM,8FAA8F;IAEhH,IAAI,CAAC,EAAK,OAAO,SAAS;KACxB,EAAK,QAAQ;KACb;;IAQF,AANA,EAAK,QAAQ,YACb,EAAY,oBAAoB,IAAI,GAA2B,CAAC,EAChE,EAAK,eAAe,SAAS,EAC3B,gBAAgB,IAAI,GAAoB,EACzC,CAAC,EACF,EAAQ,uBAAuB,EAAK,cAAc,EAClD,EAAK,wBAAwB,EAAK,eAAe;IACjD,IAAI;KASF,IARI,EAAK,2BAA2B,UAClC,EAAyB;MACvB,kBAAkB,EAAK;MACvB,eAAe,EAAK;MACpB,gBAAgB,EAAK;MACtB,CAAC,EAEJ,MAAM,EAAK,cAAc,MAAM,EAAK,EAChC,OAAO,SAAW,OAAe,OAAO,WAAa,KAAa;MACpE,IAAM,UAAwB;OAC5B,EAAU,OAAO;SAEb,UAAsB;OAC1B,AAAI,SAAS,oBAAoB,YAC/B,EAAU,OAAO;;MAKrB,AAFA,OAAO,iBAAiB,YAAY,EAAgB,EACpD,SAAS,iBAAiB,oBAAoB,EAAc,EAC5D,EAAK,kBAAkB,WACf,OAAO,oBAAoB,YAAY,EAAgB,QACvD,SAAS,oBAAoB,oBAAoB,EAAc,CACtE;;aAEI,GAAO;KACd,OAAO,EAAK,kBAAkB,SAAQ;;MACpC,CAAA,IAAA,EAAK,kBAAkB,KAAK,KAAA,QAAA,GAAI;;KAElC,MAAM,EAAK,cAAc,UAAU;KACnC,KAAK,IAAM,KAAmB,EAAK,4BAA4B;;MAC7D,CAAA,IAAA,EAAgB,YAAA,QAAA,EAAA,KAAA,EAAW;;KAM7B,MAJA,EAAM,SAAS,EACf,EAAQ,SAAS,EACjB,EAAQ,SAAS,EACjB,EAAK,QAAQ,QACP;;IAER,EAAK,QAAQ;;eAGf,cAA6B,GAAc,IAAyB,EAAE,KACpE,KAAK,OAAO,UAAU,GAAM;GAC1B,MAAM,EAAS;GACf,YAAY,KAAK,YAAA,EAAA,EAAA,EAAA,EACZ,KAAK,sBAAsB,CAAA,EAC3B,EACJ,CAAC;GACH,CAAC,CAAA,UAEJ,UAAA,KAAA,EAAA,UAEA,QAAuB,OACrB,KAAK,kBAAkB,MAAM,EACzB,KAAK,WACP,KAAK,QAAQ,KAAK,EAAO,EAEpB,gBAGT,uBAAsC,OACpC,KAAK,kBAAkB,qBAAqB,EACxC,KAAK,OAAO,WACd,KAAK,2BAA2B,KAAK,EAAgB,EAEhD,QAIP,KAAK,SAAS,GAAgB,EAAY,EAC1C,KAAK,UAAU,KAAK,OAAO,WAAW,GAAc,KAAK,OAAO;EAEhE,IAAM,IAAW,EAAuB,KAAK,OAAO,mBAAmB,EACjE,IAAe,GACnB,IAAI,EAAkB,EAAsB,KAAK,OAAO,OAAO,CAAC,EAChE,UACA,KAAK,OAAO,MACb,EACK,IAAiB,GACrB,IAAI,EAAmB,EAAsB,KAAK,OAAO,QAAQ,CAAC,EAClE,WACA,KAAK,OAAO,MACb,EACK,IAAc,GAClB,IAAI,EAAgB,EAAsB,KAAK,OAAO,KAAK,CAAC,EAC5D,QACA,KAAK,OAAO,MACb,EAEK,KAAgB,IAAI,GAAuB,IADlB,EAAmB,GAAc,KAAK,OAAO,UAC3B,EAAoB,KAAK,OAAO,EAC3E,IAAe,IAAI,EAA8B;GACrD,UAAU;GACV,sBAAsB,KAAK,OAAO;GACnC,CAAC,EACI,IAAe,IAAI,EAAwB,EAAY;EA+B7D,AA7BA,KAAK,iBAAiB,IAAI,EAAkB;GAC1C;GACA,SAAS,IAAI,EAAmB,EAC9B,MAAM,IAAI,EAAyB,QAAK,QAAgB,EACzD,CAAC;GACF,gBAAgB,CAAC,GAAc;GAChC,CAAC,EACF,KAAK,gBAAgB,IAAI,EAAc;GACrC;GACA,SAAS,CAAC,EAAa;GACxB,CAAC,EACF,KAAK,iBAAiB,IAAI,EAAe;GACvC;GACA,YAAY,CAAC,EAAa;GAC3B,CAAC,EACF,KAAK,SAAS,KAAK,eAAe,UAAU,EAA2B,EACvE,KAAK,QAAQ,KAAK,cAAc,SAAS,EAA2B,EACpE,KAAK,SAAS,KAAK,eAAe,UAAU,EAA2B,EACvE,KAAK,oBAAoB,EACvB,kBAAkB,KAAK,SACxB,EACD,KAAK,6BAA6B,KAAK,OAAO,UAAU,CAAC,GAAG,KAAK,OAAO,iBAAiB,GAAG,EAAE,EAE9F,KAAK,UAAU,CACb,GAAI,KAAK,OAAO,UAAU,GAAe,KAAK,OAAO,GAAG,EAAE,EAC1D,GAAI,KAAK,UAAU,CAAC,GAAG,GAAc,KAAK,OAAO,EAAE,GAAG,KAAK,OAAO,QAAQ,GAAG,EAAE,CAChF,EACD,KAAK,gBAAgB,IAAI,GAAc,KAAK,QAAQ,EAEhD,KAAK,OAAO,aACd,KAAU,OAAO,CAAC,OAAM,MAAS;;GAC/B,CAAA,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,8BAA8B,EAAM;IAC7D"}
|