@blueking/open-telemetry 0.0.16 → 0.0.17
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/dist/bk-rum.global.js +2 -2
- package/dist/bk-rum.global.js.map +1 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/metric.d.ts +2 -0
- package/dist/core/sdk.d.ts +4 -0
- package/dist/core/signal.d.ts +3 -0
- package/dist/core/telemetry-attributes.d.ts +1 -0
- package/dist/core/throttle.d.ts +2 -2
- package/dist/index.js +525 -456
- package/dist/index.js.map +1 -1
- package/dist/plugins/blank-screen.d.ts +4 -0
- package/dist/plugins/common.d.ts +1 -0
- package/dist/plugins/csp-violation.d.ts +1 -1
- package/dist/plugins/long-task.d.ts +4 -0
- package/dist/plugins/request-body.d.ts +2 -1
- package/dist/plugins/resource.d.ts +4 -0
- package/dist/plugins/web-vitals.d.ts +2 -0
- package/package.json +1 -1
- package/skills/bk-rum-otele/references/data-model.md +1 -1
- package/skills/bk-rum-otele/references/plugins.md +1 -1
- package/skills/bk-rum-otele/references/telemetry-reference.md +12 -10
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/schema/attributes.ts","../src/core/config.ts","../src/core/plugin.ts","../src/core/sampling.ts","../src/core/url.ts","../src/core/processor.ts","../src/core/runtime-context.ts","../src/core/id.ts","../src/schema/span.ts","../src/plugins/action.ts","../src/plugins/activity.ts","../src/core/page-activity.ts","../src/core/telemetry-attributes.ts","../src/plugins/blank-screen.ts","../src/core/request-timing.ts","../src/plugins/request-body.ts","../src/plugins/resource.ts","../src/plugins/common.ts","../src/core/throttle.ts","../src/plugins/csp-violation.ts","../src/plugins/device.ts","../src/plugins/error.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/session-state.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/action-activity.ts","../src/core/sdk.ts"],"sourcesContent":["/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * RUM 上报字段 key 集中定义。\n *\n * 插件里不要散落硬编码字符串,后续字段命名调整只改这里,避免采集端与字段文档逐步漂移。\n */\nexport const RUM_ATTRIBUTES = {\n actionErrorCount: 'action.error.count',\n actionFrustrationCount: 'action.frustration.count',\n actionFrustrationType: 'action.frustration.type',\n actionId: 'action.id',\n actionLoadingTime: 'action.loading_time',\n actionLongTaskCount: 'action.long_task.count',\n actionName: 'action.name',\n actionResourceCount: 'action.resource.count',\n actionTargetName: 'action.target.name',\n actionTargetTag: 'action.target.tag',\n actionType: 'action.type',\n deploymentEnvironmentName: 'deployment.environment.name',\n browserName: 'browser.name',\n browserVersion: 'browser.version',\n cacheHit: 'resource.cache.hit',\n deviceType: 'device.type',\n durationBucket: 'duration_bucket',\n errorType: 'error_type',\n eventLabel: 'event_label',\n exceptionFingerprint: 'exception.fingerprint',\n exceptionMessageShort: 'exception.message_short',\n exceptionStackTopFrame: 'exception.stack_top_frame',\n longTaskBlockingDuration: 'long_task.blocking_duration',\n longTaskCulprit: 'long_task.culprit',\n longTaskDuration: 'long_task.duration',\n longTaskEntryType: 'long_task.entry_type',\n longTaskId: 'long_task.id',\n longTaskIsFrozenFrame: 'long_task.is_frozen_frame',\n longTaskScriptCount: 'long_task.script.count',\n longTaskStartTime: 'long_task.start_time',\n networkEffectiveType: 'network.effective_type',\n nextHopProtocol: 'resource.protocol',\n result: 'result',\n serviceName: 'service.name',\n serviceVersion: 'service.version',\n osName: 'os.name',\n sessionHasReplay: 'session.has_replay',\n sessionId: 'session.id',\n sessionStartTime: 'session.start_time',\n spanSubtype: 'span_subtype',\n spanType: 'span_type',\n statusClass: 'status_class',\n targetDomain: 'target_domain',\n targetLabel: 'target_label',\n targetPathTemplate: 'target_path_template',\n targetValue: 'target_value',\n traceScene: 'trace_scene',\n transferSize: 'transfer_size',\n userId: 'user.id',\n vitalMetric: 'vital.metric',\n vitalRating: 'vital.rating',\n vitalValue: 'vital.value',\n viewDomComplete: 'view.dom_complete',\n viewDomContentLoaded: 'view.dom_content_loaded',\n viewDomInteractive: 'view.dom_interactive',\n viewFirstByte: 'view.first_byte',\n viewId: 'view.id',\n viewLoadEvent: 'view.load_event',\n viewLoadingTime: 'view.loading_time',\n viewLoadingType: 'view.loading_type',\n viewName: 'view.name',\n viewUrl: 'view.url',\n viewUrlPathGroup: 'view.url_path_group',\n} as const;\n\nexport type RumAttributeKey = (typeof RUM_ATTRIBUTES)[keyof typeof RUM_ATTRIBUTES];\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTActionPluginOptions } from '../plugins/action';\nimport type { BkOTBlankScreenPluginOptions } from '../plugins/blank-screen';\nimport type { BkOTDevicePluginOptions } from '../plugins/device';\nimport type { BkOTErrorPluginOptions } from '../plugins/error';\nimport type { BkOTLongTaskPluginOptions } from '../plugins/long-task';\nimport type { BkOTSessionConfig } from '../plugins/session';\nimport type { BkOTPlugin } from './plugin';\nimport type { BkOTUserContext } from './runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTActionEventPayload {\n attributes?: Attributes;\n name: string;\n}\n\nexport interface BkOTApplicationConfig {\n environment?: string;\n name: string;\n version?: string;\n}\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n 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 BkOTBeforeSendEvent {\n attributes: Record<string, unknown>;\n name: string;\n signal: 'log' | 'span';\n}\n\nexport interface BkOTContextConfig {\n attributes?: BkOTAttributesConfig;\n user?: BkOTUserContext;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTExtensionsConfig {\n instrumentations?: Instrumentation[];\n plugins?: BkOTPlugin[];\n}\n\nexport interface BkOTOptions {\n application: BkOTApplicationConfig;\n autoStart?: boolean;\n context?: BkOTContextConfig;\n debug?: boolean;\n enabled?: boolean;\n extensions?: BkOTExtensionsConfig;\n privacy?: BkOTPrivacyConfig;\n session?: BkOTSessionConfig;\n tracking?: BkOTTrackingConfig;\n transport: BkOTTransportConfig;\n}\n\nexport interface BkOTPrivacyConfig {\n /** 初始化时的采集授权状态;运行期可通过 setTrackingConsent 更新。 */\n trackingConsent?: BkOTTrackingConsent;\n // 用于隐私脱敏:在所有 RUM 自定义插件 emit 之前过滤一次属性\n redactAttributes?: (attributes: Attributes) => Attributes;\n // 用于隐私脱敏:所有上报到 attribute 的 URL 在写入前会经过此函数\n redactUrl?: (url: string) => string;\n}\n\nexport interface BkOTRequestBodyConfig {\n allowedContentTypes?: Array<RegExp | string>;\n capture?: 'all' | 'errors';\n maxSize?: number;\n redact: (payload: BkOTRequestBodyRedactPayload) => string | undefined;\n}\n\nexport interface BkOTRequestBodyRedactPayload {\n body: string;\n contentType?: string;\n kind: 'request' | 'response';\n method?: string;\n statusCode?: number;\n truncated: boolean;\n type: 'fetch' | 'xhr';\n url?: string;\n}\n\nexport interface BkOTRequestConfig {\n allowedTracingUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n body?: BkOTRequestBodyConfig;\n excludedUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n beforeSend?: (event: BkOTRequestEvent, context: BkOTRequestContext) => boolean | void;\n}\n\nexport type BkOTRequestContext =\n | { error?: unknown; request: Request | RequestInit; response?: Response; type: 'fetch' }\n | { requestBody?: Document | null | XMLHttpRequestBodyInit; type: 'xhr'; xhr: XMLHttpRequest };\n\nexport interface BkOTRequestEvent {\n attributes: Attributes;\n type: 'resource';\n resource: {\n method?: string;\n statusCode?: number;\n type: 'fetch' | 'xhr';\n url?: string;\n };\n}\n\nexport type BkOTSpanProcessorMode = 'batch' | 'simple';\n\nexport interface BkOTTrackingConfig {\n action?: BkOTActionPluginOptions | boolean;\n blankScreen?: BkOTBlankScreenPluginOptions | boolean;\n cspViolation?: boolean;\n device?: BkOTDevicePluginOptions | boolean;\n error?: BkOTErrorPluginOptions | boolean;\n longTask?: BkOTLongTaskPluginOptions | boolean;\n request?: BkOTRequestConfig | boolean;\n resource?: boolean;\n view?: BkOTViewConfig;\n websocket?: boolean;\n webVitals?: boolean;\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n}\n\nexport type BkOTTrackingConsent = 'granted' | 'not-granted';\n\nexport interface BkOTTransportConfig {\n endpoint?: string;\n headers?: Record<string, string>;\n signals?: SignalExportersInputConfig;\n token?: string;\n}\n\nexport type BkOTUrlMatcher = ((url: string) => boolean) | RegExp | string;\n\nexport interface BkOTViewConfig {\n /** 不参与 View loading time 的长轮询、流式请求或埋点 URL。 */\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n /** 自动计算初始加载与 SPA 路由的页面稳定耗时,默认开启。 */\n loadingTime?: boolean;\n tracking?: 'auto' | 'manual';\n /** 返回低基数路由分组,例如 `/trace/:id`。 */\n getPathGroup?: (url: string) => string | undefined;\n}\n\nexport interface MetricExporterInputConfig extends SignalExporterInputConfig {\n intervalMillis?: number;\n}\n\nexport interface ResolvedApplicationConfig {\n environment: string;\n name: string;\n version?: string;\n}\n\nexport interface ResolvedBkOTConfig {\n application: ResolvedApplicationConfig;\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n extensions: BkOTExtensionsConfig;\n hooks: ResolvedConfigHooks;\n metricIntervalMillis: number;\n resourceAttributes: Attributes;\n session: ResolvedSessionConfig;\n spanBatch?: BkOTBatchConfig;\n spanProcessor: BkOTSpanProcessorMode;\n tracking: BkOTTrackingConfig;\n trackingConsent: BkOTTrackingConsent;\n transport: ResolvedTransportConfig;\n user?: BkOTUserContext;\n view: ResolvedViewConfig;\n}\n\nexport interface ResolvedConfigHooks {\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getMetricAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n getViewPathGroup: (url: string) => string;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n}\n\nexport interface ResolvedSessionConfig extends BkOTSessionConfig {\n sampleRate: number;\n}\n\nexport interface ResolvedTransportConfig {\n endpoint: string;\n logs: false | SignalExporterConfig;\n metrics: false | SignalExporterConfig;\n traces: false | SignalExporterConfig;\n}\n\nexport interface ResolvedViewConfig {\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n loadingTime: boolean;\n tracking: 'auto' | 'manual';\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\nexport interface SignalExportersInputConfig {\n logs?: false | SignalExporterInputConfig;\n metrics?: false | MetricExporterInputConfig;\n traces?: false | TraceExporterInputConfig;\n}\n\nexport interface TraceExporterInputConfig extends SignalExporterInputConfig {\n batch?: BkOTBatchConfig;\n processor?: BkOTSpanProcessorMode;\n}\n\nconst DEFAULT_METRIC_INTERVAL_MILLIS = 60 * 1000; // 60s 上报一次\n\nconst trimTrailingSlash = (value: string) => value.trim().replace(/\\/+$/, '');\n\nconst SIGNAL_PATH_SEGMENT: Record<'logs' | 'metrics' | 'traces', string> = {\n traces: 'rum',\n metrics: 'metrics',\n logs: 'logs',\n};\n\n// 统一 endpoint 自动拼 /v1/{rum|metrics|logs};单信号自定义 URL 原样使用,不再拼接。\nconst resolveSignalEndpoint = (endpoint: string | undefined, signal: 'logs' | 'metrics' | 'traces') => {\n const normalized = trimTrailingSlash(endpoint ?? '');\n return `${normalized}/v1/${SIGNAL_PATH_SEGMENT[signal]}`;\n};\n\nconst resolveSignalConfig = (\n transport: BkOTTransportConfig,\n signalPath: 'logs' | 'metrics' | 'traces',\n): false | SignalExporterConfig => {\n const signalConfig = transport.signals?.[signalPath];\n if (signalConfig === false) return false;\n // signals.{signal}.endpoint 是完整 URL,不再拼接默认路径。\n const customEndpoint = signalConfig?.endpoint;\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n transport.token || transport.headers || signalConfig?.headers\n ? {\n ...(transport.token ? { Authorization: `Bearer ${transport.token}` } : {}),\n ...(transport.headers ?? {}),\n ...(signalConfig?.headers ?? {}),\n }\n : undefined;\n\n return {\n concurrencyLimit: signalConfig?.concurrencyLimit,\n endpoint: customEndpoint\n ? trimTrailingSlash(customEndpoint)\n : resolveSignalEndpoint(transport.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 identityRedactAttributes = (attributes: Attributes) => attributes;\nconst defaultRedactUrl = (url: string) => {\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n parsed.username = '';\n parsed.password = '';\n parsed.search = '';\n parsed.hash = '';\n return parsed.href;\n } catch {\n return url.replace(/[?#].*$/, '');\n }\n};\n\nconst getDefaultPathGroup = (url: string) => {\n try {\n return new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href).pathname || '/';\n } catch {\n return url || '/';\n }\n};\n\nconst resolveApplicationConfig = (application: BkOTOptions['application']): ResolvedApplicationConfig => ({\n environment: application.environment ?? 'production',\n name: application.name,\n version: application.version,\n});\n\nconst resolveResourceAttributes = (application: ResolvedApplicationConfig): Attributes => ({\n [RUM_ATTRIBUTES.deploymentEnvironmentName]: application.environment,\n 'rum.provider': 'blueking',\n 'rum.schema.version': '1',\n [RUM_ATTRIBUTES.serviceName]: application.name,\n ...(application.version ? { [RUM_ATTRIBUTES.serviceVersion]: application.version } : {}),\n 'telemetry.sdk.language': 'webjs',\n 'telemetry.sdk.name': '@blueking/open-telemetry',\n});\n\nconst resolveTransportConfig = (transport: BkOTOptions['transport']): ResolvedTransportConfig => {\n const traces = resolveSignalConfig(transport, 'traces');\n const metrics = resolveSignalConfig(transport, 'metrics');\n const logs = resolveSignalConfig(transport, 'logs');\n const endpoint = trimTrailingSlash(transport.endpoint ?? '');\n return {\n endpoint: endpoint || [traces, metrics, logs].find(signal => signal !== false)?.endpoint || '',\n traces,\n metrics,\n logs,\n };\n};\n\nconst validateTransportConfig = (transport: BkOTTransportConfig | undefined) => {\n if (!transport) throw new Error('[bk-ot] transport is required.');\n const enabledSignals = (['traces', 'metrics', 'logs'] as const).filter(\n signal => transport.signals?.[signal] !== false,\n );\n if (!enabledSignals.length) {\n throw new Error('[bk-ot] at least one transport signal must be enabled.');\n }\n for (const signal of enabledSignals) {\n const signalConfig = transport.signals?.[signal];\n const signalEndpoint = signalConfig ? signalConfig.endpoint : undefined;\n if (!signalEndpoint?.trim() && !transport.endpoint?.trim()) {\n throw new Error(`[bk-ot] transport.signals.${signal}.endpoint or transport.endpoint is required.`);\n }\n }\n};\n\nconst resolveHooks = (config: BkOTOptions): ResolvedConfigHooks => ({\n beforeSend: config.tracking?.beforeSend,\n getPageAttributes:\n config.context?.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.context?.attributes?.metric ?? (() => ({})),\n getErrorAttributes: config.context?.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.context?.attributes?.custom ?? (() => ({})),\n getViewPathGroup: url =>\n (typeof config.tracking?.view === 'object' ? config.tracking.view.getPathGroup?.(url) : undefined) ??\n getDefaultPathGroup(url),\n redactAttributes: config.privacy?.redactAttributes ?? identityRedactAttributes,\n redactUrl: config.privacy?.redactUrl ?? defaultRedactUrl,\n});\n\nexport const resolveConfig = (config: BkOTOptions): ResolvedBkOTConfig => {\n if (!config.application?.name?.trim()) {\n throw new Error('[bk-ot] application.name is required.');\n }\n validateTransportConfig(config.transport);\n const application = resolveApplicationConfig(config.application);\n const viewConfig = typeof config.tracking?.view === 'object' ? config.tracking.view : undefined;\n const traceConfig = config.transport.signals?.traces;\n const metricConfig = config.transport.signals?.metrics;\n\n return {\n application,\n autoStart: config.autoStart ?? true,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n extensions: {\n instrumentations: [...(config.extensions?.instrumentations ?? [])],\n plugins: [...(config.extensions?.plugins ?? [])],\n },\n hooks: resolveHooks(config),\n metricIntervalMillis: metricConfig\n ? (metricConfig.intervalMillis ?? DEFAULT_METRIC_INTERVAL_MILLIS)\n : DEFAULT_METRIC_INTERVAL_MILLIS,\n resourceAttributes: resolveResourceAttributes(application),\n session: {\n ...config.session,\n sampleRate: clampSampleRate(config.session?.sampleRate),\n },\n spanBatch: traceConfig ? traceConfig.batch : undefined,\n spanProcessor: traceConfig ? (traceConfig.processor ?? 'batch') : 'batch',\n tracking: config.tracking ?? {},\n trackingConsent: config.privacy?.trackingConsent ?? 'granted',\n transport: resolveTransportConfig(config.transport),\n user: config.context?.user,\n view: {\n excludedActivityUrls: viewConfig?.excludedActivityUrls,\n loadingTime: viewConfig?.loadingTime !== false,\n tracking: viewConfig?.tracking === 'manual' ? 'manual' : 'auto',\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { ResolvedBkOTConfig } from './config';\nimport type { BkOTSessionLifecycleEvent, BkOTUserContext, BkOTViewCommand, BkOTViewContext } from './runtime-context';\nimport type { Attributes, Meter, Span, SpanKind, TimeInput, Tracer } from '@opentelemetry/api';\nimport type { Logger, LogRecord } from '@opentelemetry/api-logs';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTPlugin {\n enabled?: ((config: ResolvedBkOTConfig) => boolean) | boolean;\n name: string;\n flush?: () => Promise<void> | void;\n init: (context: BkOTRuntimeContext) => Promise<void> | void;\n shutdown?: () => Promise<void> | void;\n}\n\nexport interface BkOTRuntimeContext {\n config: ResolvedBkOTConfig;\n 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 flush: () => Promise<void>;\n getRuntimeAttributes: () => Attributes;\n getRuntimeAttributesAt: (timestamp: number) => Attributes;\n isSampled: () => boolean;\n isSessionActive: () => boolean;\n isViewActive: () => boolean;\n notifyActivity: (event: BkOTActivityEvent) => void;\n notifySessionLifecycle: (event: BkOTSessionLifecycleEvent) => void;\n onActivity: (listener: (event: BkOTActivityEvent) => void) => () => void;\n onSessionLifecycle: (listener: (event: BkOTSessionLifecycleEvent) => void) => () => void;\n onViewChange: (listener: (view: BkOTViewContext) => void) => () => void;\n onViewCommand: (listener: (command: BkOTViewCommand) => void) => () => void;\n registerInstrumentations: (instrumentations: Instrumentation[]) => void;\n setRuntimeAttributes: (attributes: Attributes, changedAt?: number) => void;\n /** 更新用户上下文;user.id 由接入方传入,SDK 原样透传 */\n setUser: (user: BkOTUserContext) => void;\n /** 更新当前 View 上下文,后续 span/log 会自动带上 view.* 关联字段 */\n setView: (view: BkOTViewContext, changedAt?: number) => void;\n startSpan: (name: string, attributes?: Attributes, options?: BkOTStartSpanOptions) => Span;\n}\n\n/**\n * startSpan 的扩展选项。\n *\n * 用于\"事件实际发生在过去某个时刻,但 span 现在才创建\"的场景:\n * 例如 PerformanceObserver 在条目结束后才回调,但 span 的时间窗仍需反映\n * 条目真实发生的区间,方便 trace 视图与并发请求对齐。\n *\n * startTime 接受 OT TimeInput;常用 epoch 毫秒(performance.timeOrigin + performance.now())。\n * 配合 span.end(endTime?) 可控制结束时间。\n */\nexport interface BkOTStartSpanOptions {\n /** 用于从历史中解析 Session/View/Action 归属;默认复用数字类型 startTime。 */\n contextTime?: number;\n kind?: SpanKind;\n startTime?: TimeInput;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: ResolvedBkOTConfig) => {\n if (typeof plugin.enabled === 'function') {\n return plugin.enabled(config);\n }\n return plugin.enabled !== false;\n};\n\nexport const createPlugin = (plugin: BkOTPlugin) => plugin;\n\nexport class PluginManager {\n private readonly plugins: BkOTPlugin[];\n private startedPlugins: BkOTPlugin[] = [];\n\n public constructor(plugins: BkOTPlugin[]) {\n this.plugins = plugins;\n }\n\n public async flush() {\n // 并行 flush,避免单个慢插件拖累整体\n await Promise.all(this.startedPlugins.map(plugin => Promise.resolve().then(() => plugin.flush?.())));\n }\n\n public async shutdown() {\n const plugins = [...this.startedPlugins].reverse();\n for (const plugin of plugins) {\n await plugin.shutdown?.();\n }\n this.startedPlugins = [];\n }\n\n public async start(context: BkOTRuntimeContext) {\n // 每个插件独立 try/catch,单个插件 init 失败不应拖累其他插件:\n // - 失败的插件不会进入 startedPlugins(shutdown 时不会被反向 teardown)\n // - debug 模式打 warn,方便排查;非 debug 静默失败避免污染业务日志\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n try {\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn(`[bk-ot] plugin \"${plugin.name}\" init failed:`, error);\n }\n }\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { ResolvedBkOTConfig } from './config';\n\nexport const RUM_SAMPLED_ATTRIBUTE = 'bk.rum.sampled';\n\nconst hashToUnitInterval = (input: string) => {\n let hash = 0x811c9dc5;\n for (let index = 0; index < input.length; index += 1) {\n hash = Math.imul(hash ^ input.charCodeAt(index), 0x01000193);\n }\n hash += hash << 13;\n hash ^= hash >>> 7;\n hash += hash << 3;\n hash ^= hash >>> 17;\n hash += hash << 5;\n return (hash >>> 0) / 0x100000000;\n};\n\n/**\n * 按应用和 RUM Session ID 做确定性采样。采样决定随 Session 更新,不再依赖 Tab 级 sessionStorage。\n */\nexport const isBkOTSampled = (\n { application, session }: Pick<ResolvedBkOTConfig, 'application' | 'session'>,\n sessionId: string,\n) => {\n if (session.sampleRate <= 0) {\n return false;\n }\n if (session.sampleRate >= 1) {\n return true;\n }\n return hashToUnitInterval(`${application.name}\\0${sessionId}`) < session.sampleRate;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTUrlMatcher, ResolvedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nexport const toAbsoluteUrl = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).href;\n } catch {\n return url;\n }\n};\n\n// 去掉 trailing slash,便于 path 边界对齐比较\nconst stripTrailingSlash = (value: string) => (value.length > 1 ? value.replace(/\\/+$/, '') : value);\n\n// 比较两个 URL 是否指向同一资源(忽略 query/fragment 与末尾斜杠)\nconst isSameResource = (left: string, right: string) => {\n const stripped = (value: string) => stripTrailingSlash(value.replace(/[?#].*$/, ''));\n return stripped(left) === stripped(right);\n};\n\nexport const isBkOTEndpoint = (config: ResolvedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n\n return [config.transport.traces, config.transport.metrics, config.transport.logs]\n .filter(endpoint => endpoint !== false)\n .some(endpoint => isSameResource(targetUrl, toAbsoluteUrl(endpoint.endpoint)));\n};\n\nexport const shouldIgnoreUrl = (config: ResolvedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nconst matchesUrl = (matcher: BkOTUrlMatcher, url: string) => {\n if (typeof matcher === 'function') return matcher(url);\n if (typeof matcher === 'string') return url.includes(matcher);\n matcher.lastIndex = 0;\n return matcher.test(url);\n};\n\nexport const matchesUrlList = (matchers: BkOTUrlMatcher | BkOTUrlMatcher[] | undefined, url: string) =>\n (Array.isArray(matchers) ? matchers : matchers ? [matchers] : []).some(matcher => matchesUrl(matcher, url));\n\nconst getRequestConfig = (config: ResolvedBkOTConfig) =>\n typeof config.tracking.request === 'object' ? config.tracking.request : undefined;\n\nexport const shouldIgnoreRequest = (config: ResolvedBkOTConfig, url: string) =>\n shouldIgnoreUrl(config, url) || matchesUrlList(getRequestConfig(config)?.excludedUrls, toAbsoluteUrl(url));\n\n// Trace Header 仅向显式白名单注入,避免同源第三方路径被无意注入以及跨域 CORS 预检。\nexport const shouldPropagateTraceHeader = (config: ResolvedBkOTConfig, url: string) =>\n config.transport.traces !== false && matchesUrlList(getRequestConfig(config)?.allowedTracingUrls, toAbsoluteUrl(url));\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ExportResultCode } from '@opentelemetry/core';\nimport { AggregationTemporality } from '@opentelemetry/sdk-metrics';\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\nimport { isBkOTEndpoint } from './url';\n\nimport type { ResolvedBkOTConfig } from './config';\nimport type { Attributes, Context } from '@opentelemetry/api';\nimport type { LogRecordProcessor, SdkLogRecord } from '@opentelemetry/sdk-logs';\nimport type { InstrumentType, MetricData, PushMetricExporter, ResourceMetrics } from '@opentelemetry/sdk-metrics';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\nexport const REQUEST_DISCARD_ATTRIBUTE = 'bk.rum.request.discard';\n\nconst getSpanUrl = (span: ReadableSpan): string | undefined => {\n for (const key of SELF_TELEMETRY_URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n return value;\n }\n }\n return undefined;\n};\n\nconst hasSessionId = (attributes: Record<string, unknown> | undefined) => {\n const sessionId = attributes?.[RUM_ATTRIBUTES.sessionId];\n return typeof sessionId === 'string' && sessionId.length > 0;\n};\n\nconst hasViewId = (attributes: Record<string, unknown> | undefined) => {\n const viewId = attributes?.[RUM_ATTRIBUTES.viewId];\n return typeof viewId === 'string' && viewId.length > 0;\n};\n\nconst hasRumContext = (attributes: Record<string, unknown> | undefined) =>\n attributes?.[RUM_SAMPLED_ATTRIBUTE] === true && hasSessionId(attributes) && hasViewId(attributes);\n\nexport class FilteringLogRecordProcessor implements LogRecordProcessor {\n public constructor(\n private readonly inner: LogRecordProcessor,\n private readonly config?: ResolvedBkOTConfig,\n ) {}\n\n public enabled(options: Parameters<NonNullable<LogRecordProcessor['enabled']>>[0]) {\n return this.inner.enabled?.(options) ?? true;\n }\n\n public forceFlush() {\n return this.inner.forceFlush();\n }\n\n public onEmit(logRecord: SdkLogRecord, context?: Context) {\n const isSessionLifecycleLog = logRecord.body === 'session.start' || logRecord.body === 'session.rotate';\n const accepted =\n hasRumContext(logRecord.attributes) ||\n (isSessionLifecycleLog &&\n logRecord.attributes?.[RUM_SAMPLED_ATTRIBUTE] === true &&\n hasSessionId(logRecord.attributes));\n if (!accepted) return;\n delete logRecord.attributes[RUM_SAMPLED_ATTRIBUTE];\n try {\n if (\n this.config?.hooks.beforeSend?.({\n attributes: logRecord.attributes,\n name: logRecord.eventName ?? String(logRecord.body ?? ''),\n signal: 'log',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config?.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEmit(logRecord, context);\n }\n\n public shutdown() {\n return this.inner.shutdown();\n }\n}\n\n/**\n * 给所有 span 固化创建时的运行时上下文,并在 onEnd 阶段过滤自身上报请求和无 RUM 上下文 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: ResolvedBkOTConfig,\n private readonly getRuntimeAttributes: () => Attributes,\n ) {}\n\n public forceFlush(): Promise<void> {\n return this.inner.forceFlush();\n }\n\n public onEnd(span: ReadableSpan): void {\n const url = getSpanUrl(span);\n if (\n span.attributes[REQUEST_DISCARD_ATTRIBUTE] === true ||\n (url && isBkOTEndpoint(this.config, url)) ||\n !hasRumContext(span.attributes)\n ) {\n return;\n }\n delete (span.attributes as Attributes)[RUM_SAMPLED_ATTRIBUTE];\n try {\n if (\n this.config.hooks?.beforeSend?.({\n attributes: span.attributes,\n name: span.name,\n signal: 'span',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\n const existingAttributes = (span as Span & { attributes?: Attributes }).attributes ?? {};\n const missingRuntimeAttributes: Attributes = {};\n for (const [key, value] of Object.entries(this.getRuntimeAttributes())) {\n if (existingAttributes[key] === undefined && value !== undefined) missingRuntimeAttributes[key] = value;\n }\n span.setAttributes(missingRuntimeAttributes);\n this.inner.onStart(span, parentContext);\n }\n\n public shutdown(): Promise<void> {\n return this.inner.shutdown();\n }\n}\n\nconst filterMetric = (metric: MetricData): MetricData =>\n ({\n ...metric,\n dataPoints: metric.dataPoints\n .filter(point => point.attributes[RUM_SAMPLED_ATTRIBUTE] === true)\n .map(point => {\n const { [RUM_SAMPLED_ATTRIBUTE]: _sampled, ...attributes } = point.attributes;\n return { ...point, attributes };\n }),\n }) as MetricData;\n\nexport class FilteringMetricExporter implements PushMetricExporter {\n public constructor(private readonly inner: PushMetricExporter) {}\n\n public export(metrics: ResourceMetrics, resultCallback: Parameters<PushMetricExporter['export']>[1]) {\n const filtered: ResourceMetrics = {\n ...metrics,\n scopeMetrics: metrics.scopeMetrics\n .map(scopeMetric => ({\n ...scopeMetric,\n metrics: scopeMetric.metrics.map(filterMetric).filter(metric => metric.dataPoints.length > 0),\n }))\n .filter(scopeMetric => scopeMetric.metrics.length > 0),\n };\n if (filtered.scopeMetrics.length === 0) {\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n this.inner.export(filtered, resultCallback);\n }\n\n public forceFlush() {\n return this.inner.forceFlush();\n }\n\n public selectAggregationTemporality(instrumentType: InstrumentType) {\n return this.inner.selectAggregationTemporality?.(instrumentType) ?? AggregationTemporality.CUMULATIVE;\n }\n\n public shutdown() {\n return this.inner.shutdown();\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nexport type BkOTSessionLifecycleEvent =\n | {\n expiredAt: number;\n id: string;\n reason: 'external' | 'inactivity' | 'maxLifetime';\n type: 'expired';\n }\n | {\n id: string;\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n startedAt: number;\n type: 'renewed';\n };\n\nexport interface BkOTStartViewOptions {\n attributes?: Attributes;\n name?: string;\n url?: string;\n}\n\nexport interface BkOTUserContext {\n /** 由接入方传入并由 SDK 原样透传;SDK 不 hash、不脱敏、不改写。 */\n id?: string;\n}\n\nexport type BkOTViewCommand =\n | { name: string; type: 'set_name' }\n | { options: BkOTStartViewOptions; type: 'start' }\n | { time: number; type: 'set_loading_time' };\n\nexport interface BkOTViewContext {\n attributes?: Attributes;\n id?: string;\n loadingType?: 'bf_cache' | 'initial_load' | 'route_change' | 'session_renewal';\n name?: string;\n startedAt?: number;\n url?: string;\n urlPathGroup?: string;\n}\n\nconst PROTECTED_VIEW_ATTRIBUTE_KEYS = new Set<string>([...Object.values(RUM_ATTRIBUTES), RUM_SAMPLED_ATTRIBUTE]);\n\n/**\n * 管理 SDK 运行期公共属性快照。\n *\n * 这里仅保存“后续事件都需要带上”的低成本上下文,Session/View/用户切换时更新快照,\n * 插件发 span/log 时读取当前快照即可,避免每个插件重复维护公共字段。\n */\nexport class RumRuntimeContext {\n private readonly actionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly attributes: Attributes = {};\n private readonly queuedViewCommands: BkOTViewCommand[] = [];\n private recordAssociation = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n changedAt: number,\n active: boolean,\n ) => {\n const previous = history.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n if (active) {\n const associationAttributes: Attributes = {};\n for (const key of keys) {\n const value = this.attributes[key];\n if (value !== undefined) associationAttributes[key] = value;\n }\n history.push({ attributes: associationAttributes, startedAt: changedAt });\n }\n if (history.length > 100) history.splice(0, history.length - 100);\n };\n private readonly sessionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly sessionLifecycleListeners = new Set<(event: BkOTSessionLifecycleEvent) => void>();\n private readonly setOptionalAttribute = (key: string, value: string | undefined) => {\n if (value === undefined) {\n delete this.attributes[key];\n return;\n }\n this.attributes[key] = value;\n };\n private user: BkOTUserContext = {};\n private view: BkOTViewContext = {};\n private readonly viewAttributeKeys = new Set<string>();\n private readonly viewChangeListeners = new Set<(view: BkOTViewContext) => void>();\n private readonly viewCommandListeners = new Set<(command: BkOTViewCommand) => void>();\n\n private readonly viewHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n\n private readonly viewScopedAttributeKeys = new Set<string>();\n\n public dispatchViewCommand = (command: BkOTViewCommand) => {\n if (this.viewCommandListeners.size === 0) {\n this.queuedViewCommands.push(command);\n return;\n }\n this.viewCommandListeners.forEach(listener => listener(command));\n };\n\n public getAttributes = (): Attributes => ({ ...this.attributes });\n\n public getAttributesAt = (timestamp: number): Attributes => {\n if (!Number.isFinite(timestamp)) return this.getAttributes();\n const result = this.getAttributes();\n const applyHistory = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n ) => {\n for (const key of keys) delete result[key];\n const matched = [...history]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt));\n if (matched) Object.assign(result, matched.attributes);\n };\n applyHistory(this.sessionHistory, [\n RUM_ATTRIBUTES.sessionId,\n RUM_ATTRIBUTES.sessionHasReplay,\n RUM_SAMPLED_ATTRIBUTE,\n ]);\n applyHistory(this.viewHistory, this.viewScopedAttributeKeys);\n applyHistory(this.actionHistory, [RUM_ATTRIBUTES.actionId]);\n return result;\n };\n\n public getUser = (): BkOTUserContext => ({ ...this.user });\n\n public getView = (): BkOTViewContext => ({ ...this.view });\n\n public isSessionActive = () => typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string';\n\n public isViewActive = () => typeof this.attributes[RUM_ATTRIBUTES.viewId] === 'string';\n\n public notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.sessionLifecycleListeners.forEach(listener => listener(event));\n };\n\n public onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) => {\n this.sessionLifecycleListeners.add(listener);\n return () => this.sessionLifecycleListeners.delete(listener);\n };\n\n public onViewChange = (listener: (view: BkOTViewContext) => void) => {\n this.viewChangeListeners.add(listener);\n if (this.view.id) listener(this.getView());\n return () => this.viewChangeListeners.delete(listener);\n };\n\n public onViewCommand = (listener: (command: BkOTViewCommand) => void) => {\n this.viewCommandListeners.add(listener);\n while (this.queuedViewCommands.length) {\n listener(this.queuedViewCommands.shift()!);\n }\n return () => this.viewCommandListeners.delete(listener);\n };\n\n public setAttributes = (attributes: Attributes, changedAt = Date.now()) => {\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined) {\n delete this.attributes[key];\n continue;\n }\n this.attributes[key] = value;\n }\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n this.recordAssociation(\n this.sessionHistory,\n [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay, RUM_SAMPLED_ATTRIBUTE],\n changedAt,\n typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string',\n );\n }\n if (RUM_ATTRIBUTES.actionId in attributes) {\n const actionId = this.attributes[RUM_ATTRIBUTES.actionId];\n this.recordAssociation(\n this.actionHistory,\n [RUM_ATTRIBUTES.actionId],\n changedAt,\n Array.isArray(actionId) ? actionId.length > 0 : typeof actionId === 'string',\n );\n }\n };\n\n public setUser = (user: BkOTUserContext) => {\n this.user = {\n ...this.user,\n ...user,\n };\n\n if ('id' in user) {\n if (user.id === undefined) {\n delete this.attributes[RUM_ATTRIBUTES.userId];\n return;\n }\n this.attributes[RUM_ATTRIBUTES.userId] = user.id;\n }\n };\n\n public setView = (view: BkOTViewContext, changedAt = view.startedAt ?? Date.now()) => {\n this.viewAttributeKeys.forEach(key => delete this.attributes[key]);\n this.viewAttributeKeys.clear();\n const viewAttributes: Attributes = {};\n for (const [key, value] of Object.entries(view.attributes ?? {})) {\n if (value === undefined || PROTECTED_VIEW_ATTRIBUTE_KEYS.has(key)) continue;\n viewAttributes[key] = value;\n this.attributes[key] = value;\n this.viewAttributeKeys.add(key);\n this.viewScopedAttributeKeys.add(key);\n }\n this.view = { ...view, attributes: viewAttributes };\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewId, view.id);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrl, view.url);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrlPathGroup, view.urlPathGroup);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewLoadingType, view.loadingType);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewName, view.name);\n [\n RUM_ATTRIBUTES.viewId,\n RUM_ATTRIBUTES.viewUrl,\n RUM_ATTRIBUTES.viewUrlPathGroup,\n RUM_ATTRIBUTES.viewLoadingType,\n RUM_ATTRIBUTES.viewName,\n ].forEach(key => this.viewScopedAttributeKeys.add(key));\n this.recordAssociation(\n this.viewHistory,\n this.viewScopedAttributeKeys,\n changedAt,\n typeof view.id === 'string' && view.id.length > 0,\n );\n this.viewChangeListeners.forEach(listener => listener(this.getView()));\n };\n\n public constructor(initialAttributes: Attributes = {}, user?: BkOTUserContext) {\n this.setAttributes(initialAttributes);\n if (user) {\n this.setUser(user);\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 生成随机 ID。优先用 `crypto.randomUUID`(standards-compliant UUID v4),不可用时回退到\n * `${prefix}-${timestamp}-${rand}` 形式。\n *\n * 注意:fallback 实现使用 Math.random,**不是密码学安全**,仅适用于设备/会话/事件标识等场景,\n * 不要复用此函数生成 token、CSRF 防护等安全相关的值。\n */\nexport const createRandomId = (prefix: string) => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from './attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nexport const RUM_SPAN_RESULT = {\n abort: 'abort',\n error: 'error',\n success: 'success',\n timeout: 'timeout',\n warning: 'warning',\n} as const;\n\nexport const RUM_SPAN_TYPE = {\n action: 'action',\n custom: 'custom',\n document: 'document',\n error: 'error',\n longtask: 'longtask',\n resource: 'resource',\n route: 'route',\n vital: 'vital',\n} as const;\n\nexport interface RumResourceSpanInput {\n duration?: number;\n initiatorType?: string;\n method?: string;\n networkError?: boolean;\n responseStatus?: number;\n transferSize?: number;\n url: string;\n urlPathTemplate?: string;\n}\n\nexport interface RumSpanClassification {\n duration?: number;\n errorType?: string;\n eventLabel?: string;\n result?: RumSpanResult;\n spanSubtype: string;\n spanType: RumSpanType;\n targetLabel?: string;\n targetValue?: boolean | number | string;\n traceScene?: string;\n}\n\nexport type RumSpanResult = (typeof RUM_SPAN_RESULT)[keyof typeof RUM_SPAN_RESULT];\n\nexport type RumSpanType = (typeof RUM_SPAN_TYPE)[keyof typeof RUM_SPAN_TYPE];\n\nexport interface RumUrlTarget {\n domain?: string;\n label?: string;\n pathTemplate?: string;\n}\n\nexport const getDurationBucket = (duration: number) => {\n if (duration < 100) return '<100ms';\n if (duration < 500) return '100~500ms';\n if (duration < 2000) return '500ms~2s';\n return '>2s';\n};\n\nexport const getStatusClass = (statusCode: number) => `${Math.trunc(statusCode / 100)}xx`;\n\nexport const getHttpResult = (statusCode: number | undefined): RumSpanResult => {\n if (statusCode === 408 || statusCode === 504) return RUM_SPAN_RESULT.timeout;\n if (typeof statusCode === 'number' && statusCode >= 400) return RUM_SPAN_RESULT.error;\n return RUM_SPAN_RESULT.success;\n};\n\nexport const getHttpErrorType = (statusCode: number | undefined) => {\n if (statusCode === 408 || statusCode === 504) return 'network_timeout';\n if (typeof statusCode !== 'number' || statusCode < 400) return 'none';\n return statusCode >= 500 ? 'http_5xx' : 'http_4xx';\n};\n\nexport const getVitalResult = (rating: string | undefined): RumSpanResult =>\n rating === 'good' ? RUM_SPAN_RESULT.success : RUM_SPAN_RESULT.warning;\n\nexport const getUrlTarget = (url: string | undefined, pathTemplate?: string): RumUrlTarget => {\n if (!url) {\n return {};\n }\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n const normalizedPath = pathTemplate ?? parsed.pathname;\n return {\n domain: parsed.host,\n label: `${parsed.host}${normalizedPath}`,\n pathTemplate: normalizedPath,\n };\n } catch {\n return {\n label: url,\n pathTemplate: pathTemplate ?? url,\n };\n }\n};\n\nexport const buildRumSpanAttributes = ({\n duration,\n errorType = 'none',\n eventLabel,\n result = RUM_SPAN_RESULT.success,\n spanSubtype,\n spanType,\n targetLabel,\n targetValue,\n traceScene,\n}: RumSpanClassification): Attributes => ({\n [RUM_ATTRIBUTES.spanType]: spanType,\n [RUM_ATTRIBUTES.spanSubtype]: spanSubtype,\n [RUM_ATTRIBUTES.result]: result,\n [RUM_ATTRIBUTES.errorType]: errorType,\n ...(duration !== undefined ? { [RUM_ATTRIBUTES.durationBucket]: getDurationBucket(duration) } : {}),\n ...(eventLabel !== undefined ? { [RUM_ATTRIBUTES.eventLabel]: eventLabel } : {}),\n ...(targetLabel !== undefined ? { [RUM_ATTRIBUTES.targetLabel]: targetLabel } : {}),\n ...(targetValue !== undefined ? { [RUM_ATTRIBUTES.targetValue]: targetValue } : {}),\n ...(traceScene !== undefined ? { [RUM_ATTRIBUTES.traceScene]: traceScene } : {}),\n});\n\nexport const buildRumResourceSpanAttributes = ({\n duration,\n initiatorType = 'other',\n method,\n networkError = false,\n responseStatus,\n transferSize,\n url,\n urlPathTemplate,\n}: RumResourceSpanInput): Attributes => {\n const target = getUrlTarget(url, urlPathTemplate);\n const isRequest = initiatorType === 'fetch' || initiatorType === 'xhr';\n const result = networkError\n ? RUM_SPAN_RESULT.error\n : typeof responseStatus === 'number'\n ? getHttpResult(responseStatus)\n : RUM_SPAN_RESULT.success;\n const normalizedMethod = method?.toUpperCase();\n return {\n ...buildRumSpanAttributes({\n duration,\n errorType: networkError\n ? 'network'\n : typeof responseStatus === 'number'\n ? getHttpErrorType(responseStatus)\n : 'none',\n eventLabel: isRequest ? '请求资源' : '静态资源',\n result,\n spanSubtype: initiatorType,\n spanType: RUM_SPAN_TYPE.resource,\n targetLabel: target.label ?? url,\n targetValue: isRequest ? responseStatus : transferSize,\n }),\n ...(target.domain ? { [RUM_ATTRIBUTES.targetDomain]: target.domain } : {}),\n ...(target.pathTemplate ? { [RUM_ATTRIBUTES.targetPathTemplate]: target.pathTemplate } : {}),\n 'resource.type': initiatorType,\n 'resource.url': url,\n ...(duration !== undefined ? { 'resource.duration': duration } : {}),\n ...(normalizedMethod\n ? {\n 'http.request.method': normalizedMethod,\n 'resource.method': normalizedMethod,\n }\n : {}),\n ...(typeof transferSize === 'number' ? { 'resource.size': transferSize } : {}),\n ...(typeof responseStatus === 'number'\n ? {\n 'http.response.status_code': responseStatus,\n 'resource.status_code': responseStatus,\n ...(responseStatus > 0 ? { [RUM_ATTRIBUTES.statusClass]: getStatusClass(responseStatus) } : {}),\n }\n : {}),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createRandomId } from '../core/id';\nimport { matchesUrlList } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTActivityEvent } from '../core/action-activity';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nconst ACTION_VALIDATION_DELAY = 100;\nconst ACTION_END_DELAY = 100;\nconst CLICK_CHAIN_DURATION = 1_000;\nconst CLICK_CHAIN_MAX_DISTANCE = 100;\nconst DEFAULT_ACTION_MAX_DURATION = 10_000;\nconst DEFAULT_ACTION_NAME_ATTRIBUTE = 'data-bk-action-name';\nconst MAX_NAME_LENGTH = 100;\nconst MAX_PARENTS_TO_CONSIDER = 10;\n\nexport interface BkOTActionPluginOptions {\n actionNameAttribute?: string;\n maxDuration?: number;\n /** 默认仅采集显式 data 属性;设为 text 才会读取可访问名称和 DOM 文本。 */\n nameCollection?: 'explicit' | 'text';\n}\n\ninterface ActionCounts {\n error: number;\n longTask: number;\n resource: number;\n}\n\ntype ActionFrustrationType = 'dead_click' | 'error_click' | 'rage_click';\n\ninterface ActiveAction {\n completed?: { endTime: number; hasLoadingTime: boolean };\n counts: ActionCounts;\n discardedByRage?: boolean;\n frustrations: Set<ActionFrustrationType>;\n hadActivity: boolean;\n id: string;\n lastActivityTime: number;\n maxDurationTimer: ReturnType<typeof setTimeout>;\n pendingRequests: Set<string>;\n quietTimer?: ReturnType<typeof setTimeout>;\n rageSources?: ActiveAction[];\n reportTimer?: ReturnType<typeof setTimeout>;\n runtimeAttributes: Attributes;\n startTime: number;\n target: Element;\n targetName: string;\n targetTag: string;\n userActivity: { input: boolean; scroll: boolean; selection: boolean };\n validationTimer: ReturnType<typeof setTimeout>;\n}\n\ninterface RecentClick {\n action: ActiveAction;\n target: Element;\n x: number;\n y: number;\n}\n\nconst normalizeText = (value: string) => value.replace(/\\s+/g, ' ').trim().slice(0, MAX_NAME_LENGTH);\n\nconst getParentElement = (element: Element) => {\n if (element.parentElement) {\n return element.parentElement;\n }\n const root = element.getRootNode?.();\n return typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.host : null;\n};\n\nconst findAttributeOnAncestors = (element: Element, attribute: string) => {\n let current: Element | null = element;\n while (current) {\n const value = current.getAttribute(attribute);\n if (value) {\n return normalizeText(value);\n }\n current = getParentElement(current);\n }\n return undefined;\n};\n\nconst getElementText = (element: Element) =>\n (element as HTMLElement).isContentEditable ? '' : normalizeText(element.textContent ?? '');\n\nconst getPriorityName = (element: Element) => {\n const formElement = element as Element & {\n labels?: HTMLLabelElement[] | NodeListOf<HTMLLabelElement>;\n value?: string;\n };\n const label = formElement.labels?.[0];\n if (label) {\n const labelText = getElementText(label);\n if (labelText) return labelText;\n }\n\n const tagName = element.tagName.toLowerCase();\n const inputType = element.getAttribute('type')?.toLowerCase();\n if (tagName === 'input' && ['button', 'reset', 'submit'].includes(inputType ?? '')) {\n const value = normalizeText(formElement.value ?? '');\n if (value) return value;\n }\n\n if (tagName === 'button' || tagName === 'label' || element.getAttribute('role') === 'button') {\n const text = getElementText(element);\n if (text) return text;\n }\n\n for (const attribute of ['aria-label', 'alt', 'name', 'title', 'placeholder']) {\n const value = normalizeText(element.getAttribute(attribute) ?? '');\n if (value) return value;\n }\n\n return undefined;\n};\n\nexport const getActionNameFromElement = (\n target: Element,\n actionNameAttribute = DEFAULT_ACTION_NAME_ATTRIBUTE,\n includeText = false,\n): string => {\n const explicitName = findAttributeOnAncestors(target, actionNameAttribute);\n if (explicitName) {\n return explicitName;\n }\n if (!includeText) return target.tagName.toLowerCase();\n\n let current: Element | null = target;\n let parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const priorityName = getPriorityName(current);\n if (priorityName) return priorityName;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n current = target;\n parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const text = getElementText(current);\n if (text) return text;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n return target.tagName.toLowerCase();\n};\n\nconst getElement = (event: Event) => {\n const composedTarget = event.composedPath?.()[0];\n if (composedTarget instanceof Element) return composedTarget;\n return event.target instanceof Element ? event.target : undefined;\n};\n\nconst toEpochTime = (event: Event) => {\n const eventTime = event.timeStamp;\n if (!Number.isFinite(eventTime) || eventTime <= 0) return Date.now();\n if (eventTime > 1_000_000_000_000) return eventTime;\n return typeof performance === 'undefined' ? Date.now() : performance.timeOrigin + eventTime;\n};\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\n\nexport const isDeadClickExcluded = (target: Element) => {\n let current: Element | null = target;\n if (current.tagName.toLowerCase() === 'label' && current.hasAttribute('for')) {\n current = document.getElementById(current.getAttribute('for') ?? '');\n }\n\n while (current) {\n const tagName = current.tagName.toLowerCase();\n if (\n current.hasAttribute('contenteditable') ||\n tagName === 'canvas' ||\n tagName === 'select' ||\n tagName === 'textarea'\n ) {\n return true;\n }\n if (tagName === 'a' && current.hasAttribute('href')) return true;\n if (tagName === 'input') {\n return !['button', 'checkbox', 'radio', 'range', 'reset', 'submit'].includes(\n current.getAttribute('type')?.toLowerCase() ?? 'text',\n );\n }\n current = getParentElement(current);\n }\n return false;\n};\n\nexport const createActionPlugin = (options: BkOTActionPluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeActions = new Map<string, ActiveAction>();\n const pendingActions = new Set<ActiveAction>();\n const actionNameAttribute = options.actionNameAttribute || DEFAULT_ACTION_NAME_ATTRIBUTE;\n const maxDuration = positiveNumber(options.maxDuration, DEFAULT_ACTION_MAX_DURATION);\n let recentClicks: RecentClick[] = [];\n let runtimeContext: BkOTRuntimeContext | undefined;\n\n const updateRuntimeActionIds = () => {\n runtimeContext?.setRuntimeAttributes({\n [RUM_ATTRIBUTES.actionId]: activeActions.size ? [...activeActions.keys()] : undefined,\n });\n };\n\n const isDeadAction = (action: ActiveAction) =>\n !action.hadActivity &&\n !action.userActivity.input &&\n !action.userActivity.scroll &&\n !action.userActivity.selection &&\n !isDeadClickExcluded(action.target);\n\n const reportAction = (action: ActiveAction) => {\n if (!pendingActions.delete(action) || !runtimeContext || !action.completed || action.discardedByRage) return;\n clearTimeout(action.reportTimer);\n if (action.counts.error) action.frustrations.add('error_click');\n if ((action.rageSources ?? [action]).some(isDeadAction)) action.frustrations.add('dead_click');\n const frustrationTypes = ['rage_click', 'dead_click', 'error_click'].filter(type =>\n action.frustrations.has(type as ActionFrustrationType),\n ) as ActionFrustrationType[];\n const { endTime, hasLoadingTime } = action.completed;\n const loadingTime = Math.max(0, endTime - action.startTime);\n const hasWarning = action.counts.error > 0 || frustrationTypes.length > 0;\n const span = runtimeContext.startSpan(\n 'action.click',\n {\n ...runtimeContext.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n duration: hasLoadingTime ? loadingTime : undefined,\n errorType: action.counts.error ? 'js' : frustrationTypes.length ? 'frustration' : 'none',\n eventLabel: '用户操作',\n result: hasWarning ? RUM_SPAN_RESULT.warning : RUM_SPAN_RESULT.success,\n spanSubtype: 'click',\n spanType: RUM_SPAN_TYPE.action,\n targetLabel: action.targetName,\n targetValue: hasLoadingTime ? loadingTime : undefined,\n traceScene: 'user_action',\n }),\n ...action.runtimeAttributes,\n [RUM_ATTRIBUTES.actionId]: action.id,\n [RUM_ATTRIBUTES.actionType]: 'click',\n [RUM_ATTRIBUTES.actionName]: `Click on ${action.targetName}`,\n [RUM_ATTRIBUTES.actionTargetName]: action.targetName,\n [RUM_ATTRIBUTES.actionTargetTag]: action.targetTag,\n [RUM_ATTRIBUTES.actionResourceCount]: action.counts.resource,\n [RUM_ATTRIBUTES.actionErrorCount]: action.counts.error,\n [RUM_ATTRIBUTES.actionLongTaskCount]: action.counts.longTask,\n [RUM_ATTRIBUTES.actionFrustrationCount]: frustrationTypes.length,\n ...(frustrationTypes.length ? { [RUM_ATTRIBUTES.actionFrustrationType]: frustrationTypes } : {}),\n ...(hasLoadingTime ? { [RUM_ATTRIBUTES.actionLoadingTime]: loadingTime } : {}),\n },\n { startTime: action.startTime },\n );\n span.setAttributes({ ...action.runtimeAttributes, [RUM_ATTRIBUTES.actionId]: action.id });\n span.end(hasLoadingTime ? endTime : action.startTime);\n };\n\n const finishAction = (action: ActiveAction, endTime: number, hasLoadingTime: boolean) => {\n if (!activeActions.delete(action.id) || !runtimeContext) return;\n clearTimeout(action.validationTimer);\n clearTimeout(action.quietTimer);\n clearTimeout(action.maxDurationTimer);\n updateRuntimeActionIds();\n action.completed = { endTime, hasLoadingTime };\n pendingActions.add(action);\n const reportDelay = Math.max(0, action.startTime + CLICK_CHAIN_DURATION - Date.now());\n action.reportTimer = setTimeout(() => reportAction(action), reportDelay);\n };\n\n const flushPendingActions = () => {\n [...pendingActions].forEach(reportAction);\n };\n\n const scheduleActionEnd = (action: ActiveAction) => {\n clearTimeout(action.quietTimer);\n if (!action.hadActivity || action.pendingRequests.size) return;\n const delay = Math.max(0, action.lastActivityTime + ACTION_END_DELAY - Date.now());\n action.quietTimer = setTimeout(() => finishAction(action, action.lastActivityTime, true), delay);\n };\n\n const markActivity = (action: ActiveAction, time: number) => {\n action.hadActivity = true;\n action.lastActivityTime = Math.max(action.lastActivityTime, time);\n scheduleActionEnd(action);\n };\n\n const isInActionWindow = (action: ActiveAction, time: number) =>\n time >= action.startTime && time <= action.startTime + maxDuration;\n\n const onActivity = (event: BkOTActivityEvent) => {\n for (const action of activeActions.values()) {\n if (event.type === 'request_start') {\n if (event.url && matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)) continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.pendingRequests.add(event.id);\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'request_end') {\n if (!action.pendingRequests.delete(event.id)) continue;\n action.counts.resource += 1;\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'long_task' || event.type === 'resource') {\n if (\n event.type === 'resource' &&\n event.url &&\n matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)\n ) {\n continue;\n }\n if (\n event.startTime > action.startTime + maxDuration ||\n (event.type === 'resource' ? event.startTime < action.startTime : event.endTime < action.startTime)\n ) {\n continue;\n }\n action.counts[event.type === 'long_task' ? 'longTask' : 'resource'] += 1;\n markActivity(action, event.endTime);\n continue;\n }\n if (event.type === 'dom_mutation' || event.type === 'window_open') {\n if (isInActionWindow(action, event.time)) markActivity(action, event.time);\n continue;\n }\n if (event.type !== 'error') continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.counts.error += 1;\n markActivity(action, event.time);\n }\n };\n\n const finishActiveActions = (endTime = Date.now()) => {\n [...activeActions.values()].forEach(action => finishAction(action, endTime, action.hadActivity));\n };\n\n const registerClick = (action: ActiveAction, event: Event) => {\n const x = 'clientX' in event && typeof event.clientX === 'number' ? event.clientX : 0;\n const y = 'clientY' in event && typeof event.clientY === 'number' ? event.clientY : 0;\n recentClicks = recentClicks.filter(click => action.startTime - click.action.startTime <= CLICK_CHAIN_DURATION);\n const lastClick = recentClicks.at(-1);\n const hasInterruptedChain = recentClicks.some(\n click => click.action.userActivity.scroll || click.action.userActivity.selection,\n );\n const isSimilar =\n lastClick?.target === action.target && Math.hypot(lastClick.x - x, lastClick.y - y) <= CLICK_CHAIN_MAX_DISTANCE;\n if (hasInterruptedChain || (lastClick && !isSimilar)) recentClicks = [];\n recentClicks.push({ action, target: action.target, x, y });\n if (\n recentClicks.length >= 3 &&\n action.startTime - recentClicks[recentClicks.length - 3].action.startTime <= CLICK_CHAIN_DURATION\n ) {\n const rageSources = recentClicks.slice(-3).map(click => click.action);\n rageSources.slice(0, -1).forEach(source => {\n source.discardedByRage = true;\n });\n action.frustrations.add('rage_click');\n action.rageSources = rageSources;\n recentClicks = [];\n }\n };\n\n return {\n name: 'action',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n runtimeContext = context;\n\n const listenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n\n const startAction = (event: Event) => {\n if ('button' in event && typeof event.button === 'number' && event.button !== 0) return;\n const element = getElement(event);\n if (!element) return;\n const startTime = toEpochTime(event);\n const id = createRandomId('action');\n const action: ActiveAction = {\n counts: { error: 0, longTask: 0, resource: 0 },\n frustrations: new Set(),\n hadActivity: false,\n id,\n lastActivityTime: startTime,\n maxDurationTimer: setTimeout(() => finishAction(action, startTime + maxDuration, true), maxDuration),\n pendingRequests: new Set(),\n runtimeAttributes: context.getRuntimeAttributes(),\n startTime,\n target: element,\n targetName: getActionNameFromElement(element, actionNameAttribute, options.nameCollection === 'text'),\n targetTag: element.tagName.toLowerCase(),\n userActivity: { input: false, scroll: false, selection: false },\n validationTimer: setTimeout(() => {\n if (!action.hadActivity) finishAction(action, action.startTime, false);\n }, ACTION_VALIDATION_DELAY),\n };\n activeActions.set(id, action);\n registerClick(action, event);\n updateRuntimeActionIds();\n };\n\n window.addEventListener('click', startAction, listenerOptions);\n teardownCallbacks.push(() => window.removeEventListener('click', startAction, listenerOptions));\n\n const unsubscribeActivity = context.onActivity(onActivity);\n const unsubscribeSession = context.onSessionLifecycle(event => {\n if (event.type !== 'expired') return;\n finishActiveActions(event.expiredAt);\n flushPendingActions();\n });\n teardownCallbacks.push(unsubscribeActivity, unsubscribeSession);\n\n const finishOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n finishActiveActions();\n flushPendingActions();\n }\n };\n const finishOnPageHide = () => {\n finishActiveActions();\n flushPendingActions();\n };\n window.addEventListener('pagehide', finishOnPageHide);\n document.addEventListener('visibilitychange', finishOnHidden);\n teardownCallbacks.push(\n () => window.removeEventListener('pagehide', finishOnPageHide),\n () => document.removeEventListener('visibilitychange', finishOnHidden),\n );\n\n const markUserActivity = (type: keyof ActiveAction['userActivity']) => {\n const actions = new Set([\n ...activeActions.values(),\n ...pendingActions,\n ...recentClicks.map(click => click.action),\n ]);\n actions.forEach(action => {\n action.userActivity[type] = true;\n if (type !== 'input' && action.frustrations.delete('rage_click')) {\n action.rageSources?.slice(0, -1).forEach(source => {\n source.discardedByRage = false;\n });\n action.rageSources = undefined;\n }\n });\n };\n const onInput = () => markUserActivity('input');\n const onScroll = () => markUserActivity('scroll');\n const onSelectionChange = () => markUserActivity('selection');\n window.addEventListener('input', onInput, listenerOptions);\n window.addEventListener('scroll', onScroll, listenerOptions);\n document.addEventListener('selectionchange', onSelectionChange, listenerOptions);\n teardownCallbacks.push(\n () => window.removeEventListener('input', onInput, listenerOptions),\n () => window.removeEventListener('scroll', onScroll, listenerOptions),\n () => document.removeEventListener('selectionchange', onSelectionChange, listenerOptions),\n );\n },\n shutdown() {\n finishActiveActions();\n flushPendingActions();\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n recentClicks = [];\n runtimeContext = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nexport const EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE = 'data-bk-excluded-activity-mutations';\n\nconst isExcludedMutation = (mutation: MutationRecord) => {\n const target = mutation.type === 'characterData' ? mutation.target.parentElement : mutation.target;\n return target instanceof Element && Boolean(target.closest(`[${EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE}]`));\n};\n\n/**\n * 页面级 DOM 活动只观察一次,再通过运行时活动总线分发给 View、Action 和白屏检测。\n */\nexport const createActivityPlugin = (): BkOTPlugin => {\n let mutationObserver: MutationObserver | undefined;\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'activity',\n init(context) {\n if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {\n mutationObserver = new MutationObserver(mutations => {\n if (mutations.length && !mutations.every(isExcludedMutation)) {\n context.notifyActivity({ time: Date.now(), type: 'dom_mutation' });\n }\n });\n mutationObserver.observe(document, { attributes: true, characterData: true, childList: true, subtree: true });\n }\n\n if (typeof window === 'undefined') return;\n\n if (typeof window.open === 'function') {\n const originalOpen = window.open;\n const patchedOpen = ((...args: Parameters<typeof window.open>) => {\n context.notifyActivity({ time: Date.now(), type: 'window_open' });\n return Reflect.apply(originalOpen, window, args);\n }) as typeof window.open;\n window.open = patchedOpen;\n teardownCallbacks.push(() => {\n if (window.open === patchedOpen) window.open = originalOpen;\n });\n }\n\n },\n shutdown() {\n mutationObserver?.disconnect();\n mutationObserver = undefined;\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { matchesUrlList } from './url';\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { BkOTRuntimeContext } from './plugin';\n\nexport const PAGE_ACTIVITY_VALIDATION_DELAY = 100;\nexport const PAGE_ACTIVITY_END_DELAY = 100;\n\nexport type PageActivityEnd = { endTime: number; hadActivity: true } | { hadActivity: false };\n\nconst isActivityUrlExcluded = (context: BkOTRuntimeContext, url: string | undefined) =>\n Boolean(url && matchesUrlList(context.config.view.excludedActivityUrls, url));\n\n/**\n * 等待 View/Action 启动后的请求、静态资源和 DOM 变更全部安静 100ms。\n * 请求用 id 精确配对,避免并发请求提前结束等待。\n */\nexport const waitForPageActivityEnd = (\n context: BkOTRuntimeContext,\n startTime: number,\n callback: (result: PageActivityEnd) => void,\n) => {\n const pendingRequests = new Set<string>();\n let completed = false;\n let hadActivity = false;\n let lastActivityTime = startTime;\n let quietTimer: ReturnType<typeof setTimeout> | undefined;\n\n const complete = (result: PageActivityEnd) => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n callback(result);\n };\n\n const scheduleEnd = () => {\n clearTimeout(quietTimer);\n if (!hadActivity || pendingRequests.size) return;\n quietTimer = setTimeout(\n () => complete({ endTime: lastActivityTime, hadActivity: true }),\n Math.max(0, lastActivityTime + PAGE_ACTIVITY_END_DELAY - Date.now()),\n );\n };\n\n const markActivity = (time: number) => {\n hadActivity = true;\n lastActivityTime = Math.max(lastActivityTime, time);\n scheduleEnd();\n };\n\n const onActivity = (event: BkOTActivityEvent) => {\n if (event.type === 'error' || event.type === 'long_task') return;\n if (event.type === 'request_start') {\n if (event.time < startTime || isActivityUrlExcluded(context, event.url)) return;\n pendingRequests.add(event.id);\n markActivity(event.time);\n return;\n }\n if (event.type === 'request_end') {\n if (!pendingRequests.delete(event.id)) return;\n markActivity(event.time);\n return;\n }\n if (event.type === 'resource') {\n if (event.endTime < startTime || isActivityUrlExcluded(context, event.url)) return;\n markActivity(event.endTime);\n return;\n }\n if (event.time >= startTime) markActivity(event.time);\n };\n\n const unsubscribe = context.onActivity(onActivity);\n const validationTimer = setTimeout(() => {\n if (!hadActivity) complete({ hadActivity: false });\n }, PAGE_ACTIVITY_VALIDATION_DELAY);\n\n return () => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nconst getViewPathGroup = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined;\n\nconst buildViewPathGroupAttribute = (viewPathGroup: unknown): Attributes => {\n const normalized = getViewPathGroup(viewPathGroup);\n return normalized ? { [RUM_ATTRIBUTES.viewUrlPathGroup]: normalized } : {};\n};\n\nexport const getMetricViewAttributes = (runtimeAttributes: Attributes): Attributes => ({\n ...buildViewPathGroupAttribute(runtimeAttributes[RUM_ATTRIBUTES.viewUrlPathGroup]),\n [RUM_SAMPLED_ATTRIBUTE]: runtimeAttributes[RUM_SAMPLED_ATTRIBUTE] === true,\n});\n\nexport const getInstrumentationSpanRuntimeAttributes = (\n runtimeAttributes: Attributes,\n fallbackViewPathGroup?: string,\n): Attributes => {\n const { [RUM_ATTRIBUTES.viewUrlPathGroup]: rawViewPathGroup, ...restAttributes } = runtimeAttributes;\n return {\n ...restAttributes,\n ...buildViewPathGroupAttribute(getViewPathGroup(rawViewPathGroup) ?? fallbackViewPathGroup),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nexport interface BkOTBlankScreenPluginOptions {\n /** 页面活动稳定后首次检查的延迟,默认 1000ms。 */\n checkDelay?: number;\n /** 两次空白结果之间的确认间隔,默认 200ms。 */\n confirmationDelay?: number;\n /** loading、骨架屏等不参与空白比例计算的元素。 */\n ignoreSelectors?: string[];\n /** 应用挂载根节点,默认 body。根节点不存在本身会被判为候选白屏。 */\n rootSelector?: string;\n /** 空白采样点比例阈值,默认 0.8。 */\n threshold?: number;\n}\n\ninterface DetectionResult {\n centerElement: string;\n emptyCount: number;\n ignoredCount: number;\n reason: 'empty_viewport' | 'missing_root';\n rootFound: boolean;\n score: number;\n validCount: number;\n}\n\nconst DEFAULT_CHECK_DELAY = 1000;\nconst DEFAULT_CONFIRMATION_DELAY = 200;\nconst DEFAULT_ROOT_SELECTOR = 'body';\nconst DEFAULT_THRESHOLD = 0.8;\nconst DEFAULT_IGNORED_SELECTORS = [\n '[data-loading=\"true\"]',\n '[aria-busy=\"true\"]',\n '.loading',\n '.is-loading',\n '.spinner',\n '.skeleton',\n '.bk-loading',\n];\n\nconst SAMPLE_POINTS: ReadonlyArray<readonly [number, number]> = [\n [0.2, 0.2],\n [0.5, 0.2],\n [0.8, 0.2],\n [0.2, 0.5],\n [0.5, 0.5],\n [0.8, 0.5],\n [0.2, 0.8],\n [0.5, 0.8],\n [0.8, 0.8],\n];\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst boundedRatio = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : fallback;\n\nconst getElementSelector = (element: Element | undefined) => {\n if (!element) return '';\n const tag = element.tagName.toLowerCase();\n return element.id ? `${tag}#${element.id}` : tag;\n};\n\nconst matchesAny = (element: Element, selectors: string[]) =>\n selectors.some(selector => {\n try {\n return element.matches(selector) || Boolean(element.closest(selector));\n } catch {\n return false;\n }\n });\n\nconst isVisuallyPresent = (element: Element) => {\n if (typeof getComputedStyle !== 'function') return true;\n const style = getComputedStyle(element);\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity || 1) > 0;\n};\n\nconst getElementsAtPoint = (x: number, y: number): Element[] => {\n if (typeof document.elementsFromPoint === 'function') return document.elementsFromPoint(x, y);\n const element = document.elementFromPoint(x, y);\n return element ? [element] : [];\n};\n\nexport const detectBlankScreen = (rootSelector: string, ignoredSelectors: string[]): DetectionResult => {\n const root = document.querySelector(rootSelector);\n if (!root) {\n return {\n centerElement: getElementSelector(getElementsAtPoint(window.innerWidth / 2, window.innerHeight / 2)[0]),\n emptyCount: SAMPLE_POINTS.length,\n ignoredCount: 0,\n reason: 'missing_root',\n rootFound: false,\n score: 1,\n validCount: SAMPLE_POINTS.length,\n };\n }\n\n let emptyCount = 0;\n let ignoredCount = 0;\n let centerElement = '';\n for (const [xRatio, yRatio] of SAMPLE_POINTS) {\n const layers = getElementsAtPoint(window.innerWidth * xRatio, window.innerHeight * yRatio);\n if (xRatio === 0.5 && yRatio === 0.5) centerElement = getElementSelector(layers[0]);\n if (layers.some(element => matchesAny(element, ignoredSelectors))) {\n ignoredCount += 1;\n continue;\n }\n const hasContent = layers.some(\n element =>\n element !== root &&\n element !== document.body &&\n element !== document.documentElement &&\n root.contains(element) &&\n isVisuallyPresent(element),\n );\n if (!hasContent) emptyCount += 1;\n }\n\n const validCount = SAMPLE_POINTS.length - ignoredCount;\n return {\n centerElement,\n emptyCount,\n ignoredCount,\n reason: 'empty_viewport',\n rootFound: true,\n score: validCount ? emptyCount / validCount : 0,\n validCount,\n };\n};\n\nexport const createBlankScreenPlugin = (options: BkOTBlankScreenPluginOptions = {}): BkOTPlugin => {\n const checkDelay = nonNegativeNumber(options.checkDelay, DEFAULT_CHECK_DELAY);\n const confirmationDelay = nonNegativeNumber(options.confirmationDelay, DEFAULT_CONFIRMATION_DELAY);\n const rootSelector = options.rootSelector || DEFAULT_ROOT_SELECTOR;\n const threshold = boundedRatio(options.threshold, DEFAULT_THRESHOLD);\n const ignoredSelectors = [...DEFAULT_IGNORED_SELECTORS, ...(options.ignoreSelectors ?? [])];\n let activeViewId: string | undefined;\n let reportedViewId: string | undefined;\n let stopActivityTracking: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeView = () => {};\n let visibilityListener: (() => void) | undefined;\n\n return {\n name: 'blank-screen',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const counter = context.meter.createCounter('browser.blank_screen.count', {\n description: 'Number of confirmed blank screens',\n });\n\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n\n const report = (viewId: string, result: DetectionResult) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n reportedViewId = viewId;\n counter.add(\n 1,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_selector': rootSelector,\n }),\n );\n context\n .startSpan('browser.blank_screen', {\n ...buildRumSpanAttributes({\n errorType: 'blank_screen',\n eventLabel: '白屏',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: result.reason,\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: rootSelector,\n targetValue: result.score,\n }),\n 'blank_screen.center_element': result.centerElement,\n 'blank_screen.empty_ratio': result.score,\n 'blank_screen.empty_sample_count': result.emptyCount,\n 'blank_screen.ignored_sample_count': result.ignoredCount,\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_found': result.rootFound,\n 'blank_screen.root_selector': rootSelector,\n 'blank_screen.sample_count': SAMPLE_POINTS.length,\n 'blank_screen.valid_sample_count': result.validCount,\n })\n .end();\n };\n\n const detectAndConfirm = (viewId: string) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (document.visibilityState === 'hidden') return;\n let first: DetectionResult;\n try {\n first = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] detection failed:', error);\n return;\n }\n if (!first.validCount || first.score < threshold) return;\n timer = setTimeout(() => {\n if (activeViewId !== viewId || document.visibilityState === 'hidden') return;\n let confirmed: DetectionResult;\n try {\n confirmed = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] confirmation failed:', error);\n return;\n }\n if (confirmed.validCount && confirmed.score >= threshold) report(viewId, confirmed);\n }, confirmationDelay);\n };\n\n const schedule = (viewId: string) => {\n clearTimer();\n if (document.visibilityState === 'hidden') return;\n timer = setTimeout(() => detectAndConfirm(viewId), checkDelay);\n };\n\n unsubscribeView = context.onViewChange(view => {\n if (view.id === activeViewId) return;\n clearTimer();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n activeViewId = view.id;\n reportedViewId = undefined;\n if (view.id) {\n const viewId = view.id;\n stopActivityTracking = waitForPageActivityEnd(context, Date.now(), () => {\n stopActivityTracking = undefined;\n schedule(viewId);\n });\n }\n });\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n clearTimer();\n } else if (activeViewId && reportedViewId !== activeViewId) {\n schedule(activeViewId);\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n },\n shutdown() {\n clearTimeout(timer);\n timer = undefined;\n unsubscribeView();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n activeViewId = undefined;\n reportedViewId = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\ninterface PendingRequestTiming {\n callback?: (entry?: PerformanceResourceTiming) => void;\n entry?: PerformanceResourceTiming;\n startTime: number;\n timer?: ReturnType<typeof setTimeout>;\n url: string;\n}\n\nconst MAX_PENDING_REQUESTS = 100;\nconst RESOURCE_TIMING_WAIT_MS = 200;\nconst pendingRequests: PendingRequestTiming[] = [];\n\nconst normalizeUrl = (url: string) => url.replace(/#.*$/, '');\n\nconst removePending = (pending: PendingRequestTiming) => {\n const index = pendingRequests.indexOf(pending);\n if (index >= 0) pendingRequests.splice(index, 1);\n if (pending.timer !== undefined) clearTimeout(pending.timer);\n};\n\nconst finishPending = (pending: PendingRequestTiming, entry?: PerformanceResourceTiming) => {\n const callback = pending.callback;\n removePending(pending);\n pending.callback = undefined;\n callback?.(entry);\n};\n\nexport const notifyRequestTiming = (entry: PerformanceResourceTiming) => {\n const entryStartTime = performance.timeOrigin + entry.startTime;\n const candidate = pendingRequests\n .filter(\n pending =>\n !pending.entry &&\n normalizeUrl(pending.url) === normalizeUrl(entry.name) &&\n entryStartTime >= pending.startTime - 50,\n )\n .sort((left, right) => Math.abs(left.startTime - entryStartTime) - Math.abs(right.startTime - entryStartTime))[0];\n if (!candidate) return;\n candidate.entry = entry;\n if (candidate.callback) finishPending(candidate, entry);\n};\n\nexport const trackRequestTiming = (url: string, startTime = Date.now()) => {\n const pending: PendingRequestTiming = { startTime, url };\n pendingRequests.push(pending);\n if (pendingRequests.length > MAX_PENDING_REQUESTS) finishPending(pendingRequests[0]);\n return {\n cancel: () => finishPending(pending),\n complete: (callback: (entry?: PerformanceResourceTiming) => void) => {\n pending.callback = callback;\n if (pending.entry) {\n finishPending(pending, pending.entry);\n return;\n }\n pending.timer = setTimeout(() => finishPending(pending), RESOURCE_TIMING_WAIT_MS);\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 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, trace } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type {\n BkOTRequestBodyConfig,\n BkOTRequestBodyRedactPayload,\n BkOTRequestContext,\n BkOTRequestEvent,\n} from '../core/config';\nimport type { BkOTRuntimeContext } from '../core/plugin';\nimport type { Span } from '@opentelemetry/api';\n\ninterface BodySnapshot {\n body: string;\n contentType?: string;\n truncated: boolean;\n}\n\ninterface CaptureRequestBodyLogOptions {\n bodyConfig: BkOTRequestBodyConfig;\n domainContext: BkOTRequestContext;\n event: BkOTRequestEvent;\n fetchRequest?: Request | RequestInit;\n runtimeContext: Pick<BkOTRuntimeContext, 'emitLog'>;\n span: Span;\n}\n\nconst DEFAULT_MAX_SIZE = 10 * 1024;\nconst DEFAULT_ALLOWED_CONTENT_TYPES: Array<RegExp | string> = [\n /^text\\//i,\n /json/i,\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nconst resolveMaxSize = (value: number | undefined) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : DEFAULT_MAX_SIZE;\n\nconst isAllowedContentType = (\n contentType: string | undefined,\n matchers: Array<RegExp | string>,\n isRequest: boolean,\n) => {\n if (!contentType) return isRequest;\n return matchers.some(matcher => {\n if (typeof matcher === 'string') return contentType.toLowerCase().includes(matcher.toLowerCase());\n matcher.lastIndex = 0;\n return matcher.test(contentType);\n });\n};\n\nconst getHeadersContentType = (headers: HeadersInit | undefined) => {\n try {\n return headers ? (new Headers(headers).get('content-type') ?? undefined) : undefined;\n } catch {\n return undefined;\n }\n};\n\nconst readStream = async (stream: ReadableStream<Uint8Array>, readLimit: number) => {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let body = '';\n try {\n while (body.length <= readLimit) {\n const { done, value } = await reader.read();\n if (done) {\n body += decoder.decode();\n break;\n }\n body += decoder.decode(value, { stream: true });\n }\n if (body.length > readLimit) {\n await reader.cancel().catch(() => undefined);\n }\n } finally {\n reader.releaseLock();\n }\n return body;\n};\n\nconst bodyValueToText = async (body: BodyInit | Document | null | undefined, readLimit: number) => {\n if (typeof body === 'string') return body;\n if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) return body.toString();\n if (typeof Blob !== 'undefined' && body instanceof Blob) return readStream(body.stream(), readLimit);\n return undefined;\n};\n\nconst readFetchRequest = async (\n request: Request | RequestInit,\n readLimit: number,\n): Promise<{ contentType?: string; text?: string }> => {\n if (typeof Request !== 'undefined' && request instanceof Request) {\n const contentType = request.headers.get('content-type') ?? undefined;\n const clone = request.clone();\n const text = clone.body ? await readStream(clone.body, readLimit) : '';\n return { contentType, text };\n }\n return {\n contentType: getHeadersContentType(request.headers),\n text: await bodyValueToText(request.body, readLimit),\n };\n};\n\nexport const prepareFetchRequestBodySource = (request: Request | RequestInit) => {\n if (typeof Request === 'undefined' || !(request instanceof Request)) return request;\n try {\n return request.clone();\n } catch {\n return undefined;\n }\n};\n\nconst readFetchResponse = async (\n response: Response | undefined,\n readLimit: number,\n): Promise<{ contentType?: string; text?: string }> => {\n if (!response) return {};\n const contentType = response.headers.get('content-type') ?? undefined;\n const clone = response.clone();\n const text = clone.body ? await readStream(clone.body, readLimit) : '';\n return { contentType, text };\n};\n\nconst readXhrResponse = (xhr: XMLHttpRequest) => {\n const contentType = xhr.getResponseHeader('content-type') ?? undefined;\n if (xhr.responseType === '' || xhr.responseType === 'text') {\n return { contentType, text: xhr.responseText };\n }\n if (xhr.responseType === 'json') {\n return { contentType, text: JSON.stringify(xhr.response) };\n }\n return { contentType };\n};\n\nconst createSnapshot = (\n bodyConfig: BkOTRequestBodyConfig,\n event: BkOTRequestEvent,\n kind: 'request' | 'response',\n contentType: string | undefined,\n text: string | undefined,\n): BodySnapshot | undefined => {\n if (text === undefined) return undefined;\n const maxSize = resolveMaxSize(bodyConfig.maxSize);\n const truncated = text.length > maxSize;\n const payload: BkOTRequestBodyRedactPayload = {\n body: text.slice(0, maxSize * 2),\n contentType,\n kind,\n method: event.resource.method,\n statusCode: event.resource.statusCode,\n truncated,\n type: event.resource.type,\n url: event.resource.url,\n };\n const redacted = bodyConfig.redact(payload);\n if (redacted === undefined) return undefined;\n return {\n body: redacted.slice(0, maxSize),\n contentType,\n truncated: truncated || redacted.length > maxSize,\n };\n};\n\nexport const captureRequestBodyLog = async ({\n bodyConfig,\n domainContext,\n event,\n fetchRequest,\n runtimeContext,\n span,\n}: CaptureRequestBodyLogOptions) => {\n const statusCode = event.resource.statusCode;\n const isError = statusCode === undefined || statusCode === 0 || statusCode >= 400;\n if ((bodyConfig.capture ?? 'errors') === 'errors' && !isError) return;\n\n const maxSize = resolveMaxSize(bodyConfig.maxSize);\n const readLimit = maxSize * 2;\n const allowedContentTypes = bodyConfig.allowedContentTypes ?? DEFAULT_ALLOWED_CONTENT_TYPES;\n let requestInput: { contentType?: string; text?: string } = {};\n let responseInput: { contentType?: string; text?: string } = {};\n\n try {\n if (domainContext.type === 'fetch') {\n [requestInput, responseInput] = await Promise.all([\n readFetchRequest(fetchRequest ?? domainContext.request, readLimit),\n readFetchResponse(domainContext.response, readLimit),\n ]);\n } else {\n requestInput = { text: await bodyValueToText(domainContext.requestBody, readLimit) };\n responseInput = readXhrResponse(domainContext.xhr);\n }\n } catch {\n return;\n }\n\n let request: BodySnapshot | undefined;\n let response: BodySnapshot | undefined;\n try {\n request = isAllowedContentType(requestInput.contentType, allowedContentTypes, true)\n ? createSnapshot(bodyConfig, event, 'request', requestInput.contentType, requestInput.text)\n : undefined;\n response = isAllowedContentType(responseInput.contentType, allowedContentTypes, false)\n ? createSnapshot(bodyConfig, event, 'response', responseInput.contentType, responseInput.text)\n : undefined;\n } catch {\n return;\n }\n if (!request && !response) return;\n\n const spanContext = span.spanContext();\n runtimeContext.emitLog({\n attributes: {\n ...event.attributes,\n 'span.id': spanContext.spanId,\n 'trace.id': spanContext.traceId,\n ...(request\n ? {\n 'request.body': request.body,\n 'request.body.content_type': request.contentType,\n 'request.body.truncated': request.truncated,\n }\n : {}),\n ...(response\n ? {\n 'response.body': response.body,\n 'response.body.content_type': response.contentType,\n 'response.body.truncated': response.truncated,\n }\n : {}),\n },\n body: 'browser.request.body',\n context: trace.setSpan(otelContext.active(), span),\n eventName: 'browser.request.body',\n severityNumber: isError ? SeverityNumber.ERROR : SeverityNumber.INFO,\n severityText: isError ? 'ERROR' : 'INFO',\n });\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { shouldIgnoreUrl } from '../core/url';\nimport { notifyRequestTiming } from '../core/request-timing';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\ntype ResourceTimingWithStatus = PerformanceResourceTiming & {\n deliveryType?: string;\n renderBlockingStatus?: string;\n responseStatus?: number;\n};\n\nconst getCacheHit = (entry: ResourceTimingWithStatus) =>\n entry.deliveryType === 'cache' || (entry.transferSize === 0 && entry.decodedBodySize > 0) ? true : undefined;\n\nconst areInOrder = (...values: number[]) => values.every((value, index) => index === 0 || values[index - 1] <= value);\n\nconst setTiming = (attributes: Record<string, number>, name: string, origin: number, start: number, end: number) => {\n if (!areInOrder(origin, start, end)) return;\n attributes[`resource.${name}.start`] = start - origin;\n attributes[`resource.${name}.duration`] = end - start;\n};\n\nexport const getResourceTimingAttributes = (entry: ResourceTimingWithStatus) => {\n const attributes: Record<string, number> = {};\n const commonTimings = [\n entry.startTime,\n entry.fetchStart,\n entry.domainLookupStart,\n entry.domainLookupEnd,\n entry.connectStart,\n entry.connectEnd,\n entry.requestStart,\n entry.responseStart,\n entry.responseEnd,\n ];\n if (!commonTimings.every(Number.isFinite) || !areInOrder(...commonTimings)) return attributes;\n\n setTiming(attributes, 'first_byte', entry.startTime, entry.requestStart, entry.responseStart);\n setTiming(attributes, 'download', entry.startTime, entry.responseStart, entry.responseEnd);\n if (entry.workerStart > 0 && entry.workerStart < entry.fetchStart) {\n setTiming(attributes, 'worker', entry.startTime, entry.workerStart, entry.fetchStart);\n }\n if (entry.fetchStart < entry.domainLookupEnd) {\n setTiming(attributes, 'dns', entry.startTime, entry.domainLookupStart, entry.domainLookupEnd);\n }\n if (entry.fetchStart < entry.connectEnd) {\n setTiming(attributes, 'connect', entry.startTime, entry.connectStart, entry.connectEnd);\n if (entry.connectStart <= entry.secureConnectionStart && entry.secureConnectionStart <= entry.connectEnd) {\n setTiming(attributes, 'ssl', entry.startTime, entry.secureConnectionStart, entry.connectEnd);\n }\n }\n if (\n entry.redirectEnd > entry.startTime &&\n areInOrder(entry.startTime, entry.redirectStart, entry.redirectEnd, entry.fetchStart)\n ) {\n setTiming(attributes, 'redirect', entry.startTime, entry.redirectStart, entry.redirectEnd);\n }\n return attributes;\n};\n\nconst getResourceDuration = (entry: ResourceTimingWithStatus) =>\n entry.duration === 0 && entry.responseEnd > entry.startTime ? entry.responseEnd - entry.startTime : entry.duration;\n\nexport const getResourcePerformanceAttributes = (entry: ResourceTimingWithStatus) => {\n const deliveryType = typeof entry.deliveryType === 'string' ? entry.deliveryType || 'other' : undefined;\n return {\n [RUM_ATTRIBUTES.transferSize]: entry.transferSize,\n [RUM_ATTRIBUTES.cacheHit]: getCacheHit(entry),\n [RUM_ATTRIBUTES.nextHopProtocol]: entry.nextHopProtocol,\n ...getResourceTimingAttributes(entry),\n ...(deliveryType ? { 'resource.delivery_type': deliveryType } : {}),\n ...(entry.renderBlockingStatus ? { 'resource.render_blocking_status': entry.renderBlockingStatus } : {}),\n 'resource.encoded_body_size': entry.encodedBodySize,\n 'resource.decoded_body_size': entry.decodedBodySize,\n };\n};\n\nexport const createResourcePlugin = (emitResourceSpans = true): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'resource',\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n if (!PerformanceObserver.supportedEntryTypes?.includes('resource')) {\n return;\n }\n\n const emitEntry = (entry: ResourceTimingWithStatus) => {\n if (!entry.name || shouldIgnoreUrl(context.config, entry.name)) {\n return;\n }\n if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {\n notifyRequestTiming(entry);\n return;\n }\n if (entry.duration < 0) return;\n const redactedUrl = context.config.hooks.redactUrl(entry.name);\n const duration = getResourceDuration(entry);\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + duration;\n if (emitResourceSpans) {\n const span = context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n duration,\n initiatorType: entry.initiatorType || 'other',\n responseStatus: entry.responseStatus,\n transferSize: entry.transferSize,\n url: redactedUrl,\n urlPathTemplate: context.config.hooks.getViewPathGroup(entry.name),\n }),\n 'url.full': redactedUrl,\n initiator_type: entry.initiatorType || 'other',\n ...getResourcePerformanceAttributes(entry),\n },\n { startTime },\n );\n span.end(endTime);\n }\n context.notifyActivity({ endTime, startTime, type: 'resource', url: entry.name });\n };\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as ResourceTimingWithStatus[]) {\n emitEntry(entry);\n }\n });\n observer.observe({ type: 'resource', buffered: true });\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/**\n * Fetch/XHR 请求统一采集入口。\n *\n * 同一层 patch 负责请求 span、Trace Header、页面活动、body 与 beforeSend,避免多套 patch\n * 重复计数,也不依赖 OpenTelemetry instrumentation 的私有方法。\n */\n\nimport { context as otelContext, propagation, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { REQUEST_DISCARD_ATTRIBUTE } from '../core/processor';\nimport { trackRequestTiming } from '../core/request-timing';\nimport {\n matchesUrlList,\n shouldIgnoreRequest,\n shouldIgnoreUrl,\n shouldPropagateTraceHeader,\n toAbsoluteUrl,\n} from '../core/url';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\nimport { captureRequestBodyLog, prepareFetchRequestBodySource } from './request-body';\nimport { getResourcePerformanceAttributes } from './resource';\n\nimport type { BkOTRequestConfig, BkOTRequestContext } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface RequestDetails {\n method?: string;\n url?: string;\n}\n\nconst getRequestOptions = (context: BkOTRuntimeContext) =>\n typeof context.config.tracking.request === 'object' ? context.config.tracking.request : undefined;\n\nconst getDomainStatus = (domainContext: BkOTRequestContext) => {\n if (domainContext.type === 'xhr') return domainContext.xhr.status;\n if (domainContext.response) return domainContext.response.status;\n if (typeof domainContext.error === 'object' && domainContext.error && 'status' in domainContext.error) {\n const status = (domainContext.error as { status?: unknown }).status;\n return typeof status === 'number' ? status : undefined;\n }\n return undefined;\n};\n\nconst getFetchUrl = (input: RequestInfo | URL) => {\n if (typeof input === 'string' || input instanceof URL) return toAbsoluteUrl(String(input));\n return toAbsoluteUrl(input.url);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) =>\n (init?.method ?? (typeof Request !== 'undefined' && input instanceof Request ? input.method : 'GET')).toUpperCase();\n\nconst getFetchRequestContext = (input: RequestInfo | URL, init?: RequestInit): Request | RequestInit =>\n typeof Request !== 'undefined' && input instanceof Request ? input : (init ?? {});\n\nconst injectFetchTraceHeaders = (input: RequestInfo | URL, init: RequestInit | undefined, span: Span): RequestInit => {\n const requestHeaders = typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined;\n const headers = new Headers(init?.headers ?? requestHeaders);\n propagation.inject(trace.setSpan(otelContext.active(), span), headers, {\n set: (carrier, key, value) => carrier.set(key, value),\n });\n return { ...init, headers };\n};\n\nconst injectXhrTraceHeaders = (xhr: XMLHttpRequest, span: Span) => {\n const headers: Record<string, string> = {};\n propagation.inject(trace.setSpan(otelContext.active(), span), headers);\n for (const [key, value] of Object.entries(headers)) xhr.setRequestHeader(key, value);\n};\n\nconst isActivityExcluded = (context: BkOTRuntimeContext, url: string) =>\n shouldIgnoreUrl(context.config, url) || matchesUrlList(context.config.view.excludedActivityUrls, url);\n\nconst startRequestActivity = (context: BkOTRuntimeContext, url: string) => {\n if (isActivityExcluded(context, url)) return () => {};\n const id = createRandomId('request');\n context.notifyActivity({ id, time: Date.now(), type: 'request_start', url });\n let ended = false;\n return () => {\n if (ended) return;\n ended = true;\n context.notifyActivity({ id, time: Date.now(), type: 'request_end' });\n };\n};\n\nconst trackRequestSpanTiming = (context: BkOTRuntimeContext, url: string) =>\n context.config.transport.traces === false\n ? {\n cancel() {},\n complete(callback: (entry?: PerformanceResourceTiming) => void) {\n callback();\n },\n }\n : trackRequestTiming(url);\n\nconst startRequestSpan = (context: BkOTRuntimeContext, subtype: 'fetch' | 'xhr', details: RequestDetails) =>\n context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method: details.method,\n url: details.url ?? '',\n urlPathTemplate: details.url ? context.config.hooks.getViewPathGroup(details.url) : undefined,\n }),\n ...(details.url ? { 'resource.url': context.config.hooks.redactUrl(details.url) } : {}),\n },\n { kind: SpanKind.CLIENT },\n );\n\nexport { REQUEST_DISCARD_ATTRIBUTE };\n\nexport const decorateRequestSpan = (\n context: BkOTRuntimeContext,\n span: Span,\n subtype: 'fetch' | 'xhr',\n domainContext: BkOTRequestContext,\n details: RequestDetails = {},\n fetchRequest?: Request | RequestInit,\n) => {\n const sourceUrl = details.url;\n const redactedUrl = sourceUrl ? context.config.hooks.redactUrl(sourceUrl) : undefined;\n const statusCode = getDomainStatus(domainContext);\n const method = details.method;\n const attributes: Attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method,\n networkError: domainContext.type === 'fetch' ? !domainContext.response : domainContext.xhr.status === 0,\n responseStatus: statusCode,\n url: redactedUrl ?? '',\n urlPathTemplate: sourceUrl ? context.config.hooks.getViewPathGroup(sourceUrl) : undefined,\n }),\n ...(redactedUrl ? { 'resource.url': redactedUrl, 'url.full': redactedUrl } : {}),\n };\n\n const event = {\n attributes,\n resource: {\n method,\n statusCode,\n type: subtype,\n url: redactedUrl,\n },\n type: 'resource' as const,\n };\n let shouldSend: boolean | void = true;\n try {\n shouldSend = getRequestOptions(context)?.beforeSend?.(event, domainContext);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot] request.beforeSend failed:', error);\n }\n span.setAttributes(context.applyRedact(event.attributes));\n if (shouldSend === false) {\n span.setAttribute(REQUEST_DISCARD_ATTRIBUTE, true);\n return;\n }\n if ((statusCode ?? 0) >= 400 || statusCode === 0 || (domainContext.type === 'fetch' && domainContext.error)) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n const bodyConfig = context.config.transport.logs === false ? undefined : getRequestOptions(context)?.body;\n if (bodyConfig) {\n void captureRequestBodyLog({ bodyConfig, domainContext, event, fetchRequest, runtimeContext: context, span });\n }\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTRequestConfig | boolean | undefined): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeRequestEnds = new Set<() => void>();\n\n return {\n name: 'request',\n enabled: option !== false,\n init(context) {\n if (typeof window === 'undefined') return;\n\n if (typeof window.fetch === 'function') {\n const originalFetch = window.fetch;\n const patchedFetch: typeof window.fetch = async function (this: Window, input, init) {\n const url = getFetchUrl(input);\n if (shouldIgnoreRequest(context.config, url)) return originalFetch.call(this, input, init);\n const method = getFetchMethod(input, init);\n const details = { method, url };\n const span = startRequestSpan(context, 'fetch', details);\n const timing = trackRequestSpanTiming(context, url);\n const endActivity = startRequestActivity(context, url);\n const requestContext = getFetchRequestContext(input, init);\n const fetchRequest =\n context.config.transport.logs !== false && getRequestOptions(context)?.body\n ? prepareFetchRequestBodySource(requestContext)\n : undefined;\n const requestInit = shouldPropagateTraceHeader(context.config, url)\n ? injectFetchTraceHeaders(input, init, span)\n : init;\n let ended = false;\n const endSpan = (entry?: PerformanceResourceTiming) => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end();\n };\n const forceEnd = () => {\n timing.cancel();\n endSpan();\n };\n activeRequestEnds.add(forceEnd);\n try {\n const response = await originalFetch.call(this, input, requestInit);\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { request: requestContext, response, type: 'fetch' },\n details,\n fetchRequest,\n );\n timing.complete(endSpan);\n return response;\n } catch (error) {\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { error, request: requestContext, type: 'fetch' },\n details,\n fetchRequest,\n );\n if (error instanceof Error) span.recordException(error);\n timing.complete(endSpan);\n throw error;\n } finally {\n endActivity();\n }\n };\n window.fetch = patchedFetch;\n teardownCallbacks.push(() => {\n if (window.fetch === patchedFetch) window.fetch = originalFetch;\n });\n }\n\n if (typeof XMLHttpRequest !== 'undefined') {\n const requests = new WeakMap<XMLHttpRequest, RequestDetails>();\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const patchedOpen: typeof XMLHttpRequest.prototype.open = function (\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n async = true,\n username?: null | string,\n password?: null | string,\n ) {\n requests.set(this, { method: method.toUpperCase(), url: toAbsoluteUrl(String(url)) });\n return originalOpen.call(this, method, String(url), async, username, password);\n };\n const patchedSend: typeof XMLHttpRequest.prototype.send = function (this: XMLHttpRequest, body) {\n const details = requests.get(this) ?? {};\n if (!details.url || shouldIgnoreRequest(context.config, details.url)) return originalSend.call(this, body);\n const span = startRequestSpan(context, 'xhr', details);\n const timing = trackRequestSpanTiming(context, details.url);\n const endActivity = startRequestActivity(context, details.url);\n if (shouldPropagateTraceHeader(context.config, details.url)) injectXhrTraceHeaders(this, span);\n let ended = false;\n const forceEnd = () => {\n timing.cancel();\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n endActivity();\n span.end();\n };\n activeRequestEnds.add(forceEnd);\n const end = () => {\n if (ended) return;\n decorateRequestSpan(context, span, 'xhr', { requestBody: body, type: 'xhr', xhr: this }, details);\n endActivity();\n timing.complete(entry => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end();\n });\n };\n this.addEventListener('loadend', end, { once: true });\n try {\n return originalSend.call(this, body);\n } catch (error) {\n if (error instanceof Error) span.recordException(error);\n end();\n throw error;\n }\n };\n XMLHttpRequest.prototype.open = patchedOpen;\n XMLHttpRequest.prototype.send = patchedSend;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.open === patchedOpen) XMLHttpRequest.prototype.open = originalOpen;\n if (XMLHttpRequest.prototype.send === patchedSend) XMLHttpRequest.prototype.send = originalSend;\n });\n }\n },\n shutdown() {\n for (const end of [...activeRequestEnds]) end();\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 简易 djb2 字符串 hash。结果稳定、短(base36),足以区分常见 fingerprint 字符串组合。\n * 不是密码学安全 hash,仅用于事件聚合分组场景。\n */\nexport const 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\nexport interface ThrottleResult {\n /** 是否允许本次上报 */\n allowed: boolean;\n /** 当前窗口内同 fingerprint 的总触发次数(含本次,含被丢弃次数),便于下游感知是否被节流 */\n count: number;\n}\n\ninterface ThrottleEntry {\n count: number;\n windowStart: number;\n}\n\n/**\n * 时间窗节流:同一 key 在 windowMs 窗口内最多允许 maxPerWindow 次通过;\n * 超出则被拦截,但 `count` 仍累加,便于下游感知风暴规模。\n *\n * 长会话场景下避免 records 无限累积——按窗口长度节奏轻量清理一次过期 entry。\n */\nexport const createThrottle = (windowMs: number, maxPerWindow: number) => {\n const records = new Map<string, ThrottleEntry>();\n let lastSweep = 0;\n const sweep = (now: number) => {\n if (now - lastSweep < windowMs) return;\n lastSweep = now;\n for (const [key, entry] of records) {\n if (now - entry.windowStart > windowMs) records.delete(key);\n }\n };\n return {\n allow(key: string): ThrottleResult {\n const now = Date.now();\n sweep(now);\n const entry = records.get(key);\n if (!entry || now - entry.windowStart > windowMs) {\n records.set(key, { count: 1, windowStart: now });\n return { allowed: true, count: 1 };\n }\n entry.count += 1;\n return { allowed: entry.count <= maxPerWindow, count: entry.count };\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 { createThrottle, hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst CSP_THROTTLE_WINDOW_MS = 60_000;\nconst CSP_THROTTLE_MAX_PER_WINDOW = 5;\n\n// blockedURI 除了 URL 还可能是 'inline' / 'eval' / 'wasm-eval' / 'self' 等关键字(CSP spec),\n// 这些不是 URL,不应该走 redactUrl\nconst URL_LIKE_BLOCKED_URI = /^https?:|^\\/\\//i;\n\nconst redactBlockedUri = (redactUrl: (url: string) => string, blockedURI: string | undefined) => {\n if (!blockedURI) return undefined;\n return URL_LIKE_BLOCKED_URI.test(blockedURI) ? redactUrl(blockedURI) : blockedURI;\n};\n\n/**\n * CSP 违规事件采集插件,通过监听 `securitypolicyviolation` 事件捕获被 CSP 策略拦截的脚本/资源,\n * 以 error span 上报,进入统一错误分析口径。\n *\n * 节流策略:\n * - 同 fingerprint(`violatedDirective + blockedURI + sourceFile + lineNumber`)\n * 在 60s 窗口内最多上报 5 条,避免风暴打爆 collector(如某段被拦截的 inline 脚本被反复渲染)。\n * - 每条 attribute 带 `csp.window_count`,反映当前窗口内同 fingerprint 的总触发次数(含被丢弃的)。\n *\n * 体积优化:\n * - `csp.original_policy`(完整 CSP 策略,可能数 KB)仅在窗口首条上报,后续同 fingerprint 不再重复携带。\n *\n * URL 处理:\n * - `blockedURI` / `sourceFile` 中的 URL 类值会经 `redactUrl` 脱敏。\n * - `blockedURI` 是 'inline' / 'eval' 等 CSP 关键字时直接保留,不调用 redactUrl。\n */\nexport const createCspViolationPlugin = (): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const throttle = createThrottle(CSP_THROTTLE_WINDOW_MS, CSP_THROTTLE_MAX_PER_WINDOW);\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n const fingerprint = hashString(\n `${event.violatedDirective}|${event.blockedURI}|${event.sourceFile}|${event.lineNumber}`,\n );\n const result = throttle.allow(fingerprint);\n if (!result.allowed) return;\n\n const blockedUri = redactBlockedUri(context.config.hooks.redactUrl, event.blockedURI);\n const sourceFile = event.sourceFile ? context.config.hooks.redactUrl(event.sourceFile) : undefined;\n // 窗口首条带完整 originalPolicy 用于排查策略本身;后续同 fingerprint 省略,避免数 KB 字段反复传输\n const includeOriginalPolicy = result.count === 1;\n\n context\n .startSpan('csp.violation', {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n errorType: 'csp',\n eventLabel: 'CSP 错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: 'csp',\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: blockedUri ?? event.effectiveDirective,\n targetValue: event.statusCode,\n }),\n 'csp.blocked_uri': blockedUri,\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.disposition': event.disposition,\n 'csp.source_file': sourceFile,\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n 'csp.fingerprint': fingerprint,\n 'csp.window_count': result.count,\n [RUM_ATTRIBUTES.exceptionFingerprint]: fingerprint,\n 'error.source': 'csp',\n ...(includeOriginalPolicy ? { 'csp.original_policy': event.originalPolicy } : {}),\n })\n .end();\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 { createRandomId } from '../core/id';\nimport { hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTDevicePluginOptions {\n /** 是否跨页面持久化匿名设备 ID,默认 false。 */\n persistentId?: boolean;\n storageKey?: string;\n}\n\nconst DEFAULT_DEVICE_STORAGE_KEY = 'BK_RUM_DEVICE_ID';\n// 用户拖窗口时 resize 高频触发,debounce 静默期内只在最后一次更新,避免无谓的 setRuntimeAttributes 风暴\nconst VIEWPORT_DEBOUNCE_MS = 250;\n\ninterface NetworkInformationLike {\n downlink?: number;\n effectiveType?: string;\n rtt?: number;\n saveData?: boolean;\n type?: string;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ninterface UserAgentDataLike {\n mobile?: boolean;\n platform?: string;\n}\n\nconst getConnection = (): NetworkInformationLike | undefined =>\n (\n navigator as Navigator & {\n connection?: NetworkInformationLike;\n }\n ).connection;\n\nconst getUserAgentData = (): undefined | UserAgentDataLike =>\n (\n navigator as Navigator & {\n userAgentData?: UserAgentDataLike;\n }\n ).userAgentData;\n\nconst getBrowserInfo = (ua: string) => {\n const matchers: Array<[string, RegExp]> = [\n ['Edge', /Edg\\/([\\d.]+)/],\n ['Chrome', /Chrome\\/([\\d.]+)/],\n ['Firefox', /Firefox\\/([\\d.]+)/],\n ['Safari', /Version\\/([\\d.]+).*Safari/],\n ];\n for (const [name, pattern] of matchers) {\n const matched = ua.match(pattern);\n if (matched?.[1]) {\n return { name, version: matched[1].split('.')[0] };\n }\n }\n return { name: 'Other', version: undefined };\n};\n\nconst getOsName = (ua: string, platform?: string) => {\n const value = `${platform ?? ''} ${ua}`;\n if (/iphone|ipad|ipod/i.test(value)) return 'iOS';\n if (/android/i.test(value)) return 'Android';\n if (/mac/i.test(value)) return 'macOS';\n if (/win/i.test(value)) return 'Windows';\n if (/linux/i.test(value)) return 'Linux';\n return 'Other';\n};\n\nconst readDeviceId = (storageKey: string) => {\n let existed: null | string = null;\n try {\n existed = window.localStorage.getItem(storageKey);\n } catch {\n /* localStorage 不可用(隐私模式 / sandbox iframe),下面会创建一个 in-memory id */\n }\n if (existed) {\n return existed;\n }\n const deviceId = createRandomId('device');\n try {\n window.localStorage.setItem(storageKey, deviceId);\n } catch {\n /* 写不进去也行,至少本次 SDK 生命周期内 deviceId 仍然稳定 */\n }\n return deviceId;\n};\n\n// 视口尺寸:随窗口大小 / 屏幕旋转变化,需要在 resize / orientationchange 时刷新\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n return {\n 'browser.viewport.width': window.innerWidth,\n 'browser.viewport.height': window.innerHeight,\n 'browser.screen.width': window.screen?.width,\n 'browser.screen.height': window.screen?.height,\n };\n};\n\n// 网络信息:随 connection.change 变化\nconst getNetworkAttributes = (): Attributes => {\n const connection = getConnection();\n if (!connection) {\n return {};\n }\n return {\n 'network.effective_type': connection.effectiveType,\n 'network.connection_type': connection.type,\n 'network.downlink': connection.downlink,\n 'network.rtt': connection.rtt,\n 'network.save_data': connection.saveData,\n };\n};\n\n// 设备硬件能力:进入页面时一次性读取,运行期不会变化\nconst getHardwareAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const uaData = getUserAgentData();\n const ua = navigator.userAgent;\n const browser = getBrowserInfo(ua);\n const osName = getOsName(ua, uaData?.platform);\n const deviceType = uaData?.mobile || /mobi|android|iphone|ipad|ipod/i.test(ua) ? 'mobile' : 'desktop';\n return {\n 'device.memory': (navigator as Navigator & { deviceMemory?: number }).deviceMemory,\n 'device.cpu_cores': navigator.hardwareConcurrency,\n 'device.platform': uaData?.platform,\n 'device.mobile': uaData?.mobile,\n [RUM_ATTRIBUTES.browserName]: browser.name,\n [RUM_ATTRIBUTES.browserVersion]: browser.version,\n [RUM_ATTRIBUTES.deviceType]: deviceType,\n [RUM_ATTRIBUTES.osName]: osName,\n };\n};\n\n// trailing debounce:windowMs 静默期内的多次触发只执行最后一次。\n// 适合 resize 这类\"用户停下来后才需要拿到最终值\"的场景。\nconst createDebounce = (fn: () => void, windowMs: number) => {\n let timer: number | undefined;\n return () => {\n if (timer !== undefined) {\n window.clearTimeout(timer);\n }\n timer = window.setTimeout(() => {\n timer = undefined;\n fn();\n }, windowMs);\n };\n};\n\n/**\n * 设备元数据采集插件,把\"设备级\"信息写入 SDK 的 runtimeAttributes,\n * 让所有 log/span 都自动带上这些维度。\n *\n * 上报字段:\n * - `device.id`:设备级永久标识,存于 localStorage(默认 key 复用旧 session 插件以保留现网用户)。\n * - `device.memory` / `device.cpu_cores` / `device.platform` / `device.mobile`:硬件能力快照(运行期不变)。\n * - `browser.viewport.*` / `browser.screen.*`:视口与屏幕尺寸。\n * - `network.effective_type` / `network.connection_type` / `network.downlink` / `network.rtt` / `network.save_data`:\n * 网络信息。\n *\n * 实时更新:\n * - viewport 监听 `resize` / `orientationchange`,并经 250ms debounce 合并,避免拖窗口时高频触发;\n * 静默期结束后用最终值刷新 runtimeAttributes。\n * - network 监听 `connection.change`(本身低频,不需要 throttle)。\n * - 二者只更新 in-memory 的 runtimeAttributes,不会主动产生新的 span/log,cardinality 安全;\n * 但更新后续生成的 log/span 会带最新值。\n *\n * 与 session 插件的关系:device 跨会话持久(终身),session 有过期与续期(连续访问语义)。\n */\nexport const createDevicePlugin = (options: BkOTDevicePluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n return {\n name: 'device',\n init(context) {\n const storageKey =\n options.storageKey ?? `${DEFAULT_DEVICE_STORAGE_KEY}_${hashString(context.config.application.name)}`;\n const deviceId =\n typeof window === 'undefined' || options.persistentId !== true\n ? createRandomId('device')\n : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n ...getHardwareAttributes(),\n ...getViewportAttributes(),\n ...getNetworkAttributes(),\n });\n\n if (typeof window === 'undefined') {\n return;\n }\n\n const refreshViewport = createDebounce(() => {\n context.setRuntimeAttributes(getViewportAttributes());\n }, VIEWPORT_DEBOUNCE_MS);\n window.addEventListener('resize', refreshViewport);\n window.addEventListener('orientationchange', refreshViewport);\n teardownCallbacks.push(\n () => window.removeEventListener('resize', refreshViewport),\n () => window.removeEventListener('orientationchange', refreshViewport),\n );\n\n const connection = getConnection();\n // NetworkInformation 实现了 EventTarget;旧浏览器或某些 polyfill 可能没有 addEventListener,做能力检测\n if (connection && typeof connection.addEventListener === 'function') {\n const refreshNetwork = () => {\n context.setRuntimeAttributes(getNetworkAttributes());\n };\n connection.addEventListener('change', refreshNetwork);\n teardownCallbacks.push(() => {\n connection.removeEventListener?.('change', refreshNetwork);\n });\n }\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 浏览器错误采集插件,负责捕获 JS 运行时错误、未处理的 Promise rejection、资源加载失败三类错误。\n *\n * 上报策略:\n * - 错误分析主数据统一使用 `span_type=error` 的 error span。\n * - 不再为同一个错误额外 emit error log,避免后端错误数按 span/log 混算时重复。\n *\n * 节流策略:\n * - 以 source + message + stack 前 256 字符做 djb2 hash 作为 fingerprint。\n * - 同 fingerprint 在 windowMs(默认 60s)内最多上报 maxPerWindow(默认 5)条;超过的会被丢弃。\n * - 每条上报的 attribute 都带 `error.window_count`,反映当前窗口内同 fingerprint 的总触发次数(含被丢弃次数),\n * 用于下游识别错误风暴。\n *\n * 关键 attribute:\n * - `error.source`:错误来源,取值 `window.error` / `unhandledrejection` / `resource`。\n * - `exception.fingerprint`:节流分组用的指纹 hash,可用于聚合同类错误。\n * - `error.window_count`:当前节流窗口内同指纹的总触发次数。\n * - `error.cross_origin`:仅在跨域脚本错误(\"Script error.\")时为 true,便于过滤这类无 stack 噪声。\n * - 资源错误额外带 `url.full`(经 redactUrl 脱敏)与 `html.tag`。\n * - JS 错误额外带 `code.filepath` / `code.lineno` / `code.column`。\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\n\nimport { createThrottle, hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nexport interface BkOTErrorPluginOptions {\n // 同 hash 错误窗口内最多上报多少条,默认 5\n maxPerWindow?: number;\n // 节流窗口长度(ms),默认 60_000\n windowMs?: number;\n}\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_PER_WINDOW = 5;\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\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\nconst getMessageShort = (message: string) => message.slice(0, 80);\n\nconst getStackTopFrame = (stack: string) =>\n stack\n .split('\\n')\n .find(line => line.trim().startsWith('at '))\n ?.trim() ?? '';\n\nconst getErrorSubtype = (source: string) => {\n if (source === 'unhandledrejection') return 'promise';\n if (source === 'resource') return 'resource_load';\n return 'js';\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 const throttleResult = throttle.allow(throttleKey);\n if (!throttleResult.allowed) {\n return;\n }\n context.notifyActivity({ time: Date.now(), type: 'error' });\n\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...context.config.hooks.getErrorAttributes(),\n ...buildRumSpanAttributes({\n errorType: getErrorSubtype(source),\n eventLabel: '错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: getErrorSubtype(source),\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: error instanceof Error ? error.name : (exceptionType ?? source),\n }),\n 'exception.message': message,\n [RUM_ATTRIBUTES.exceptionMessageShort]: getMessageShort(message),\n 'exception.stacktrace': stack,\n [RUM_ATTRIBUTES.exceptionStackTopFrame]: getStackTopFrame(stack),\n 'exception.type': error instanceof Error ? error.name : (exceptionType ?? source),\n [RUM_ATTRIBUTES.exceptionFingerprint]: throttleKey,\n 'error.handled': false,\n 'error.source': source,\n // 当前窗口内同 fingerprint 的总触发次数(含被节流丢弃的次数),用于下游识别错误风暴\n 'error.window_count': throttleResult.count,\n ...extra,\n };\n // 错误分析统一沉淀为 error span;若当前存在 active context,OTel 会自动建立父子关系。\n const errorSpan = context.startSpan(spanName, attributes);\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.end();\n};\n\nexport const createErrorPlugin = (options: BkOTErrorPluginOptions = {}): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n const windowMs = positiveNumber(options.windowMs, DEFAULT_WINDOW_MS);\n const maxPerWindow = positiveNumber(options.maxPerWindow, DEFAULT_MAX_PER_WINDOW);\n\n return {\n name: 'error',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const throttle = createThrottle(windowMs, maxPerWindow);\n\n const onError = (event: ErrorEvent) => {\n // 跨域脚本错误浏览器会屏蔽 stack/filename,message 通常是 \"Script error.\"\n const isCrossOrigin = !event.error && !event.filename && event.message === 'Script error.';\n emitError({\n context,\n 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 ...(isCrossOrigin ? { 'error.cross_origin': true } : {}),\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 // JS 运行时错误是 ErrorEvent,已由 bubble 阶段的 onError 处理;这里只关心资源加载失败\n if (event instanceof ErrorEvent) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const elementWithUrl = target as Element & { href?: string; src?: string };\n const resourceUrl = elementWithUrl.src || elementWithUrl.href || (target as HTMLMediaElement).currentSrc || '';\n emitError({\n context,\n throttle,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName.toLowerCase()} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.hooks.redactUrl(resourceUrl),\n 'html.tag': target.tagName,\n },\n });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('error', onResourceError, true);\n listeners.push(\n () => window.removeEventListener('error', onError),\n () => window.removeEventListener('unhandledrejection', onUnhandledRejection),\n () => window.removeEventListener('error', onResourceError, true),\n );\n },\n shutdown() {\n while (listeners.length) {\n listeners.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { createRandomId } from '../core/id';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTLongTaskPluginOptions {\n /** 采集阈值,默认 50ms。Performance API 本身通常只产生大于 50ms 的条目。 */\n threshold?: number;\n /** 标记 warning 的阈值,默认 100ms。 */\n warningThreshold?: number;\n}\n\ninterface LegacyLongTaskEntry extends PerformanceEntry {\n attribution?: Array<{\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n }>;\n}\n\ninterface LongAnimationFrameEntry extends PerformanceEntry {\n blockingDuration: number;\n firstUIEventTimestamp: number;\n renderStart: number;\n scripts: ScriptTiming[];\n styleAndLayoutStart: number;\n}\n\ninterface ScriptTiming {\n duration: number;\n executionStart: number;\n forcedStyleAndLayoutDuration: number;\n invoker: string;\n invokerType: string;\n pauseDuration: number;\n sourceCharPosition: number;\n sourceFunctionName: string;\n sourceURL: string;\n startTime: number;\n windowAttribution: string;\n}\n\nconst DEFAULT_THRESHOLD = 50;\nconst DEFAULT_WARNING_THRESHOLD = 100;\nconst FROZEN_FRAME_THRESHOLD = 700;\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst finiteNumber = (value: number | undefined) => (Number.isFinite(value) ? value : undefined);\n\nconst getLegacyAttribution = (entry: LegacyLongTaskEntry, redactUrl: (url: string) => string) => {\n const attribution = entry.attribution?.[0];\n return attribution?.containerSrc\n ? redactUrl(attribution.containerSrc)\n : attribution?.containerName || attribution?.name || entry.name || 'unknown';\n};\n\nconst getScriptAttributes = (script: ScriptTiming, redactUrl: (url: string) => string): Attributes => ({\n 'long_task.script.duration': script.duration,\n 'long_task.script.execution_start': script.executionStart,\n 'long_task.script.forced_style_and_layout_duration': script.forcedStyleAndLayoutDuration,\n 'long_task.script.invoker': script.invoker,\n 'long_task.script.invoker_type': script.invokerType,\n 'long_task.script.pause_duration': script.pauseDuration,\n 'long_task.script.source_char_position': script.sourceCharPosition,\n 'long_task.script.source_function_name': script.sourceFunctionName,\n 'long_task.script.source_url': script.sourceURL ? redactUrl(script.sourceURL) : '',\n 'long_task.script.start_time': script.startTime,\n 'long_task.script.window_attribution': script.windowAttribution,\n});\n\nexport const createLongTaskPlugin = (options: BkOTLongTaskPluginOptions = {}): BkOTPlugin => {\n const threshold = nonNegativeNumber(options.threshold, DEFAULT_THRESHOLD);\n const warningThreshold = Math.max(threshold, nonNegativeNumber(options.warningThreshold, DEFAULT_WARNING_THRESHOLD));\n let observer: PerformanceObserver | undefined;\n let initialBatchTimer: ReturnType<typeof setTimeout> | undefined;\n let initialEntries: PerformanceEntry[] = [];\n\n return {\n name: 'long-task',\n init(context) {\n if (typeof PerformanceObserver === 'undefined' || typeof performance === 'undefined') return;\n const supportedEntryTypes = PerformanceObserver.supportedEntryTypes ?? [];\n const entryType = supportedEntryTypes.includes('long-animation-frame')\n ? 'long-animation-frame'\n : supportedEntryTypes.includes('longtask')\n ? 'longtask'\n : undefined;\n if (!entryType) return;\n\n const counter = context.meter.createCounter('browser.long_task.count', {\n description: 'Number of long tasks and long animation frames',\n });\n const durationHistogram = context.meter.createHistogram('browser.long_task.duration', {\n description: 'Duration of long tasks and long animation frames',\n unit: 'ms',\n });\n const blockingHistogram = context.meter.createHistogram('browser.long_task.blocking_duration', {\n description: 'Blocking duration of long animation frames',\n unit: 'ms',\n });\n\n const emitEntry = (entry: LegacyLongTaskEntry | LongAnimationFrameEntry) => {\n if (!Number.isFinite(entry.duration) || entry.duration < threshold) return;\n const isLoaf = entry.entryType === 'long-animation-frame';\n const loaf = isLoaf ? (entry as LongAnimationFrameEntry) : undefined;\n const scripts = loaf?.scripts ?? [];\n const culprit = scripts.reduce<ScriptTiming | undefined>(\n (selected, script) => (!selected || script.duration > selected.duration ? script : selected),\n undefined,\n );\n const id = createRandomId('long-task');\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + entry.duration;\n const result = entry.duration >= warningThreshold ? RUM_SPAN_RESULT.warning : RUM_SPAN_RESULT.success;\n const entryTypeAttribute = isLoaf ? 'long-animation-frame' : 'long-task';\n const culpritUrl = culprit?.sourceURL ? context.config.hooks.redactUrl(culprit.sourceURL) : undefined;\n const legacyAttribution = isLoaf\n ? undefined\n : getLegacyAttribution(entry as LegacyLongTaskEntry, context.config.hooks.redactUrl);\n const target = culpritUrl || culprit?.sourceFunctionName || legacyAttribution || 'unknown';\n const span = context.startSpan(\n 'browser.long_task',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n duration: entry.duration,\n errorType: result === RUM_SPAN_RESULT.warning ? 'main_thread_blocked' : 'none',\n eventLabel: isLoaf ? '长动画帧' : '长任务',\n result,\n spanSubtype: entryTypeAttribute,\n spanType: RUM_SPAN_TYPE.longtask,\n targetLabel: target,\n targetValue: loaf?.blockingDuration ?? entry.duration,\n }),\n [RUM_ATTRIBUTES.longTaskBlockingDuration]: loaf?.blockingDuration ?? entry.duration,\n [RUM_ATTRIBUTES.longTaskDuration]: entry.duration,\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n [RUM_ATTRIBUTES.longTaskId]: id,\n [RUM_ATTRIBUTES.longTaskIsFrozenFrame]: entry.duration >= FROZEN_FRAME_THRESHOLD,\n [RUM_ATTRIBUTES.longTaskScriptCount]: scripts.length,\n ...(legacyAttribution ? { [RUM_ATTRIBUTES.longTaskCulprit]: legacyAttribution } : {}),\n ...(culprit\n ? {\n [RUM_ATTRIBUTES.longTaskCulprit]: target,\n 'long_task.culprit.duration': culprit.duration,\n 'long_task.culprit.forced_style_and_layout_duration': culprit.forcedStyleAndLayoutDuration,\n 'long_task.culprit.function_name': culprit.sourceFunctionName,\n 'long_task.culprit.invoker_type': culprit.invokerType,\n }\n : {}),\n ...(finiteNumber(loaf?.firstUIEventTimestamp) !== undefined\n ? { 'long_task.first_ui_event_timestamp': loaf!.firstUIEventTimestamp }\n : {}),\n ...(finiteNumber(loaf?.renderStart) !== undefined ? { 'long_task.render_start': loaf!.renderStart } : {}),\n ...(finiteNumber(loaf?.styleAndLayoutStart) !== undefined\n ? { 'long_task.style_and_layout_start': loaf!.styleAndLayoutStart }\n : {}),\n ...(loaf ? { [RUM_ATTRIBUTES.longTaskStartTime]: loaf.startTime } : {}),\n },\n { startTime },\n );\n scripts.forEach(script => {\n span.addEvent(\n 'long_task.script',\n context.applyRedact(getScriptAttributes(script, context.config.hooks.redactUrl)),\n performance.timeOrigin + script.startTime,\n );\n });\n span.end(endTime);\n context.notifyActivity({ endTime, startTime, type: 'long_task' });\n\n const dimensions = context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n });\n counter.add(1, dimensions);\n durationHistogram.record(entry.duration, dimensions);\n if (loaf) blockingHistogram.record(loaf.blockingDuration, dimensions);\n };\n\n const emitEntries = (entries: PerformanceEntry[]) =>\n entries.forEach(entry => emitEntry(entry as LegacyLongTaskEntry));\n try {\n let initializing = true;\n observer = new PerformanceObserver(list => {\n const entries = list.getEntries();\n if (initializing) {\n initialEntries.push(...entries);\n if (!initialBatchTimer) {\n initialBatchTimer = setTimeout(() => {\n const entriesToEmit = initialEntries;\n initialEntries = [];\n initialBatchTimer = undefined;\n emitEntries(entriesToEmit);\n });\n }\n } else {\n emitEntries(entries);\n }\n });\n observer.observe({ type: entryType, buffered: true });\n initializing = false;\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n clearTimeout(initialBatchTimer);\n initialBatchTimer = undefined;\n initialEntries = [];\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport interface RouteChangeEvent {\n fromUrl: string;\n source: RouteChangeSource;\n toUrl: string;\n}\n\nexport type RouteChangeSource = 'hashchange' | 'popstate' | 'pushState' | 'replaceState';\n\ntype RouteChangeHandler = (event: RouteChangeEvent) => void;\n\nconst subscribers = new Set<RouteChangeHandler>();\n\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\n/**\n * Page View 插件\n *\n * 自动模式监听初始加载、SPA 路由和 BFCache;手动模式仅由 startView() 划分 View。\n *\n * 上报形态:一个覆盖完整生命周期的 browser.view span,加 create/update/final View Snapshot log。\n *\n * 自动边界与 Datadog 对齐:pathname 或非锚点 hash path 改变才创建 View,query-only 变化不创建。\n *\n * 关键属性:\n * - view.url:当前页面 URL(经 redactUrl 脱敏)\n * - url.previous:跳转前的 URL(来自 route-observer 的 fromUrl,初始 load 为空字符串)\n * - document.referrer:仅 'load' 事件携带\n * - event.source:触发源(load / pushState / replaceState / popstate / hashchange)\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { createRandomId } from '../core/id';\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { subscribeRouteChange } from '../core/route-observer';\nimport { RUM_SAMPLED_ATTRIBUTE } from '../core/sampling';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { BkOTStartViewOptions, BkOTViewContext } from '../core/runtime-context';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface ActiveView {\n attributes: Attributes;\n documentVersion: number;\n id: string;\n loadingTime?: number;\n loadingTimeSource?: 'auto' | 'manual';\n loadingType: ViewLoadingType;\n name: string;\n rawUrl: string;\n snapshotAttributes: Attributes;\n span: Span;\n startedAt: number;\n url: string;\n urlPathGroup: string;\n stopLoadingTime?: () => void;\n}\n\ntype ViewLoadingType = NonNullable<BkOTViewContext['loadingType']>;\n\nconst PROTECTED_ATTRIBUTE_KEYS = new Set<string>([...Object.values(RUM_ATTRIBUTES), RUM_SAMPLED_ATTRIBUTE]);\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nconst getNavigationTimingAttributes = (): Attributes => {\n if (typeof performance === 'undefined') return {};\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation) return {};\n const attributes: Attributes = {};\n const setTiming = (key: string, value: number) => {\n if (Number.isFinite(value) && value > 0) attributes[key] = value;\n };\n setTiming(RUM_ATTRIBUTES.viewFirstByte, navigation.responseStart);\n setTiming(RUM_ATTRIBUTES.viewDomInteractive, navigation.domInteractive);\n setTiming(RUM_ATTRIBUTES.viewDomContentLoaded, navigation.domContentLoadedEventEnd);\n setTiming(RUM_ATTRIBUTES.viewDomComplete, navigation.domComplete);\n setTiming(RUM_ATTRIBUTES.viewLoadEvent, navigation.loadEventEnd);\n return attributes;\n};\n\nconst getHashPath = (hash: string) => {\n const queryIndex = hash.indexOf('?');\n return queryIndex < 0 ? hash : hash.slice(0, queryIndex);\n};\n\nconst isHashAnchor = (hash: string) => {\n const id = hash.slice(1);\n if (!id || typeof document === 'undefined' || typeof document.getElementById !== 'function') return false;\n try {\n return Boolean(document.getElementById(decodeURIComponent(id)));\n } catch {\n return Boolean(document.getElementById(id));\n }\n};\n\nexport const isViewLocationChange = (fromUrl: string, toUrl: string) => {\n try {\n const base = typeof location === 'undefined' ? 'http://localhost' : location.href;\n const from = new URL(fromUrl, base);\n const to = new URL(toUrl, base);\n return from.pathname !== to.pathname || (!isHashAnchor(to.hash) && getHashPath(from.hash) !== getHashPath(to.hash));\n } catch {\n return fromUrl !== toUrl;\n }\n};\n\nexport const createPageViewPlugin = (): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n let activeView: ActiveView | undefined;\n let renewalView: Omit<ActiveView, 'span' | 'stopLoadingTime'> | undefined;\n const loadingTimeHistogram = context.config.view.loadingTime\n ? context.meter.createHistogram('browser.view.loading_time', {\n description: 'Time from view start until network and DOM activity become idle',\n unit: 'ms',\n })\n : undefined;\n\n const clearViewContext = (changedAt = Date.now()) => context.setView({}, changedAt);\n\n const emitViewSnapshot = (\n view: ActiveView,\n state: 'create' | 'final' | 'update',\n timestamp = Date.now(),\n reason?: string,\n ) => {\n view.documentVersion += 1;\n context.emitLog({\n attributes: {\n ...context.config.hooks.getPageAttributes(),\n ...view.attributes,\n ...view.snapshotAttributes,\n 'event.type': 'view',\n 'view.document_version': view.documentVersion,\n 'view.duration': Math.max(0, timestamp - view.startedAt),\n 'view.is_active': state !== 'final',\n 'view.snapshot.type': state,\n ...(reason ? { 'view.end_reason': reason } : {}),\n ...(view.loadingTime === undefined\n ? {}\n : {\n [RUM_ATTRIBUTES.viewLoadingTime]: view.loadingTime,\n 'view.loading_time.source': view.loadingTimeSource,\n }),\n },\n body: 'browser.view',\n eventName: 'browser.view',\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n timestamp,\n });\n };\n\n const endActiveView = (reason: string, clearContext = true, endedAt = Date.now()) => {\n if (!activeView) {\n if (clearContext) clearViewContext(endedAt);\n return undefined;\n }\n const snapshot = { ...activeView };\n activeView.stopLoadingTime?.();\n if (activeView.loadingTime !== undefined) {\n loadingTimeHistogram?.record(\n activeView.loadingTime,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'view.loading_type': activeView.loadingType,\n }),\n );\n }\n emitViewSnapshot(activeView, 'final', endedAt, reason);\n activeView.span.setAttribute('view.end_reason', reason);\n activeView.span.end(endedAt);\n activeView = undefined;\n if (clearContext) clearViewContext(endedAt);\n const { span: _span, stopLoadingTime: _stopLoadingTime, ...rest } = snapshot;\n return rest;\n };\n\n const setLoadingTime = (view: ActiveView, duration: number, source: 'auto' | 'manual') => {\n if (activeView !== view || !Number.isFinite(duration) || duration < 0) return;\n const navigationTimingAttributes =\n view.loadingType === 'initial_load' ? getNavigationTimingAttributes() : {};\n const updatedAttributes = {\n ...navigationTimingAttributes,\n [RUM_ATTRIBUTES.viewLoadingTime]: duration,\n 'view.loading_time.source': source,\n };\n view.span.setAttributes(updatedAttributes);\n Object.assign(view.snapshotAttributes, updatedAttributes);\n view.loadingTime = duration;\n view.loadingTimeSource = source;\n emitViewSnapshot(view, 'update');\n };\n\n const startLoadingTimeTracking = (view: ActiveView) => {\n if (!context.config.view.loadingTime || document.visibilityState === 'hidden') return;\n let activityResolved = false;\n let activityCandidate: number | undefined;\n let loadResolved = view.loadingType !== 'initial_load';\n let loadCandidate: number | undefined;\n let loadListener: (() => void) | undefined;\n let hidden = false;\n let visibilityListener: (() => void) | undefined;\n\n const tryComplete = () => {\n if (hidden || !activityResolved || !loadResolved || activeView !== view) return;\n const candidates = [activityCandidate, loadCandidate].filter(\n (candidate): candidate is number => candidate !== undefined,\n );\n if (candidates.length) setLoadingTime(view, Math.max(...candidates), 'auto');\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n\n const readLoadEvent = () => {\n const navigation = performance.getEntriesByType?.('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation?.loadEventEnd) return false;\n loadCandidate = Math.max(0, performance.timeOrigin + navigation.loadEventEnd - view.startedAt);\n loadResolved = true;\n return true;\n };\n\n if (!loadResolved && !readLoadEvent()) {\n const onLoad = () => {\n setTimeout(() => {\n if (activeView !== view) return;\n readLoadEvent();\n loadResolved = true;\n tryComplete();\n });\n };\n window.addEventListener('load', onLoad, { once: true });\n loadListener = () => window.removeEventListener('load', onLoad);\n }\n\n const stopActivity = waitForPageActivityEnd(context, view.startedAt, result => {\n activityResolved = true;\n if (result.hadActivity) activityCandidate = Math.max(0, result.endTime - view.startedAt);\n tryComplete();\n });\n const onVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return;\n hidden = true;\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n view.stopLoadingTime = () => {\n stopActivity();\n loadListener?.();\n loadListener = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n };\n tryComplete();\n };\n\n const sanitizeViewAttributes = (attributes: Attributes | undefined) => {\n const redacted = context.applyRedact(attributes ?? {});\n return Object.fromEntries(\n Object.entries(redacted).filter(([key, value]) => value !== undefined && !PROTECTED_ATTRIBUTE_KEYS.has(key)),\n ) as Attributes;\n };\n\n const startView = (\n options: BkOTStartViewOptions,\n source: string,\n loadingType: ViewLoadingType,\n previousUrl = activeView?.rawUrl ?? '',\n ) => {\n if (!context.isSessionActive()) return;\n const currentUrl = options.url ?? getCurrentUrl();\n const pathGroup = context.config.hooks.getViewPathGroup(currentUrl);\n const name = options.name ?? pathGroup;\n const customAttributes = sanitizeViewAttributes(options.attributes);\n endActiveView(source, false);\n\n const viewId = createRandomId('view');\n const redactedCurrentUrl = context.config.hooks.redactUrl(currentUrl);\n const redactedPreviousUrl = context.config.hooks.redactUrl(previousUrl);\n const isInitialLoad = loadingType === 'initial_load';\n const startedAt = isInitialLoad && typeof performance !== 'undefined' ? performance.timeOrigin : Date.now();\n const navigationTimingAttributes = isInitialLoad ? getNavigationTimingAttributes() : {};\n\n context.setView(\n {\n attributes: customAttributes,\n id: viewId,\n loadingType,\n name,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n },\n startedAt,\n );\n\n const viewAttributes = {\n ...customAttributes,\n ...context.config.hooks.getPageAttributes(),\n ...navigationTimingAttributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: isInitialLoad ? '文档加载' : '路由切换',\n spanSubtype: isInitialLoad ? 'navigate' : source,\n spanType: isInitialLoad ? RUM_SPAN_TYPE.document : RUM_SPAN_TYPE.route,\n targetLabel: pathGroup,\n traceScene: isInitialLoad ? 'page_load' : 'route_change',\n }),\n [RUM_ATTRIBUTES.viewId]: viewId,\n [RUM_ATTRIBUTES.viewName]: name,\n [RUM_ATTRIBUTES.viewUrl]: redactedCurrentUrl,\n [RUM_ATTRIBUTES.viewUrlPathGroup]: pathGroup,\n [RUM_ATTRIBUTES.viewLoadingType]: loadingType,\n 'url.previous': redactedPreviousUrl,\n 'event.source': source,\n ...(isInitialLoad ? { 'document.referrer': context.config.hooks.redactUrl(document.referrer || '') } : {}),\n };\n const activeViewSpan = context.startSpan('browser.view', viewAttributes, { startTime: startedAt });\n activeView = {\n attributes: customAttributes,\n documentVersion: 0,\n id: viewId,\n loadingType,\n name,\n rawUrl: currentUrl,\n snapshotAttributes: viewAttributes,\n span: activeViewSpan,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n };\n emitViewSnapshot(activeView, 'create', startedAt);\n startLoadingTimeTracking(activeView);\n };\n\n const unsubscribeViewCommands = context.onViewCommand(command => {\n if (command.type === 'start') {\n startView(command.options, 'manual', activeView ? 'route_change' : 'initial_load');\n return;\n }\n if (!activeView) return;\n if (command.type === 'set_loading_time') {\n activeView.stopLoadingTime?.();\n activeView.stopLoadingTime = undefined;\n setLoadingTime(activeView, Math.max(0, command.time - activeView.startedAt), 'manual');\n return;\n }\n activeView.name = command.name;\n activeView.span.setAttribute(RUM_ATTRIBUTES.viewName, command.name);\n activeView.snapshotAttributes[RUM_ATTRIBUTES.viewName] = command.name;\n context.setView({\n attributes: activeView.attributes,\n id: activeView.id,\n loadingType: activeView.loadingType,\n name: activeView.name,\n startedAt: activeView.startedAt,\n url: activeView.url,\n urlPathGroup: activeView.urlPathGroup,\n });\n emitViewSnapshot(activeView, 'update');\n });\n\n let unsubscribeRoute = () => {};\n if (context.config.view.tracking === 'auto') {\n if (!activeView) startView({}, 'load', 'initial_load');\n unsubscribeRoute = subscribeRouteChange(event => {\n if (isViewLocationChange(event.fromUrl, event.toUrl)) {\n startView({}, event.source, 'route_change', event.fromUrl);\n }\n });\n }\n\n const onPageHide = (event: PageTransitionEvent) => {\n if (!event.persisted) endActiveView('pagehide');\n };\n const onPageShow = (event: PageTransitionEvent) => {\n if (event.persisted && context.config.view.tracking === 'auto') {\n startView({}, 'bfCache', 'bf_cache', activeView?.rawUrl);\n }\n };\n const unsubscribeSessionLifecycle = context.onSessionLifecycle(event => {\n if (event.type === 'expired') {\n renewalView = endActiveView('session_expired', true, event.expiredAt);\n return;\n }\n if (renewalView) {\n startView(\n { attributes: renewalView.attributes, name: renewalView.name, url: renewalView.rawUrl },\n 'sessionRenewal',\n 'session_renewal',\n renewalView.rawUrl,\n );\n renewalView = undefined;\n }\n });\n window.addEventListener('pagehide', onPageHide);\n window.addEventListener('pageshow', onPageShow);\n\n teardownCallbacks.push(\n unsubscribeRoute,\n unsubscribeSessionLifecycle,\n unsubscribeViewCommands,\n () => window.removeEventListener('pagehide', onPageHide),\n () => window.removeEventListener('pageshow', onPageShow),\n () => endActiveView('shutdown'),\n );\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","export interface ActiveSessionRecord {\n expiresAt: number;\n id: string;\n startedAt: number;\n status: 'active';\n}\n\nexport interface ExpiredSessionRecord {\n expiredAt: number;\n previousId: string;\n reason: SessionExpireReason;\n status: 'expired';\n}\n\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nexport type SessionExpireReason = 'inactivity' | 'maxLifetime';\n\nexport type SessionRecord = ActiveSessionRecord | ExpiredSessionRecord;\n\nconst FUTURE_CLOCK_TOLERANCE_MS = 5_000;\n\nconst isTimestamp = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0;\n\nexport const createActiveSessionRecord = (\n id: string,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n expiresAt: Math.min(now + idleTimeoutMs, now + maxDurationMs),\n id,\n startedAt: now,\n status: 'active',\n});\n\nexport const getSessionExpireReason = (\n record: ActiveSessionRecord,\n now: number,\n maxDurationMs: number,\n): SessionExpireReason | undefined => {\n if (now >= record.startedAt + maxDurationMs) return 'maxLifetime';\n if (now >= record.expiresAt) return 'inactivity';\n return undefined;\n};\n\nexport const expandActiveSession = (\n record: ActiveSessionRecord,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n ...record,\n expiresAt: Math.min(now + idleTimeoutMs, record.startedAt + maxDurationMs),\n});\n\nexport const parseSessionRecord = (raw: null | string, now: number, maxDurationMs: number): null | SessionRecord => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<SessionRecord>;\n if (\n parsed.status === 'active' &&\n typeof parsed.id === 'string' &&\n parsed.id.length > 0 &&\n isTimestamp(parsed.startedAt) &&\n parsed.startedAt <= now + FUTURE_CLOCK_TOLERANCE_MS &&\n isTimestamp(parsed.expiresAt) &&\n parsed.expiresAt >= parsed.startedAt &&\n parsed.expiresAt <= parsed.startedAt + maxDurationMs\n ) {\n return parsed as ActiveSessionRecord;\n }\n if (\n parsed.status === 'expired' &&\n typeof parsed.previousId === 'string' &&\n parsed.previousId.length > 0 &&\n (parsed.reason === 'inactivity' || parsed.reason === 'maxLifetime') &&\n isTimestamp(parsed.expiredAt) &&\n parsed.expiredAt <= now + FUTURE_CLOCK_TOLERANCE_MS\n ) {\n return parsed as ExpiredSessionRecord;\n }\n } catch {\n /* JSON 损坏视为无记录 */\n }\n return null;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Session 插件\n *\n * 生命周期:\n * - 首次访问创建 session;用户交互会续期有效 session,或重建已过期 session\n * - 固定 15 分钟无活动 / 4 小时最长生命周期,任一到达时立即清理运行时 session.id\n * - 页面重新可见时只续期有效 session,不会复活已过期 session\n * - 跨 tab 共享持久化状态;本 tab 过期后不会被其他 tab 的新 session 被动复活\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { createRandomId } from '../core/id';\nimport { hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport {\n createActiveSessionRecord,\n expandActiveSession,\n getSessionExpireReason,\n parseSessionRecord,\n} from './session-state';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { ActiveSessionRecord, ExpiredSessionRecord, SessionExpireReason, SessionRecord } from './session-state';\n\nexport interface BkOTSessionConfig {\n /** 以 0~1 表示的 Session 采样率,默认 1。 */\n sampleRate?: number;\n // 自定义 storage key;不传则按 endpoint host 自动生成隔离后缀\n storageKey?: string;\n}\n\nconst SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;\nconst SESSION_MAX_DURATION_MS = 4 * 60 * 60 * 1000;\nconst ACTIVITY_THROTTLE_MS = 1_000;\nconst VISIBILITY_CHECK_MS = 60_000;\nconst ACTIVITY_EVENTS: Array<keyof WindowEventMap> = ['click', 'keydown', 'scroll', 'touchstart'];\nconst memoryRecords = new Map<string, string>();\n\ninterface SessionRenewResult {\n previousId?: string;\n reason?: 'init' | SessionExpireReason;\n record: ActiveSessionRecord;\n}\n\ninterface SessionStorage {\n isLocalStorage: () => boolean;\n read: () => null | string;\n write: (record: SessionRecord) => void;\n}\n\nconst createSessionStorage = (storageKey: string): SessionStorage => {\n let localStorageAvailable = typeof window !== 'undefined';\n\n const read = () => {\n if (localStorageAvailable) {\n try {\n return window.localStorage.getItem(storageKey);\n } catch {\n localStorageAvailable = false;\n }\n }\n return memoryRecords.get(storageKey) ?? null;\n };\n\n const write = (record: SessionRecord) => {\n const serialized = JSON.stringify(record);\n memoryRecords.set(storageKey, serialized);\n if (!localStorageAvailable) return;\n try {\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n localStorageAvailable = false;\n }\n };\n\n read();\n return { isLocalStorage: () => localStorageAvailable, read, write };\n};\n\nconst buildDefaultStorageKey = (endpoint: string, applicationName: string) => {\n let suffix = '';\n try {\n suffix = new URL(endpoint).host;\n } catch {\n suffix = endpoint;\n }\n return `bk_ot_session__${hashString(`${applicationName}\\0${suffix || 'default'}`)}`;\n};\n\nconst emitSessionEvent = (context: BkOTRuntimeContext, result: SessionRenewResult) => {\n if (!result.reason) return;\n context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: result.reason === 'init' ? 'session.start' : 'session.rotate',\n attributes: {\n ...context.config.hooks.getPageAttributes(),\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: result.record.id,\n [RUM_ATTRIBUTES.sessionStartTime]: result.record.startedAt,\n 'session.rotate.reason': result.reason,\n ...(result.previousId ? { 'session.previous_id': result.previousId } : {}),\n },\n });\n};\n\nexport const createSessionPlugin = (options: BkOTSessionConfig = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'session',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n context.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: createRandomId('session'),\n });\n return;\n }\n\n const storageKey =\n options.storageKey ??\n buildDefaultStorageKey(context.config.transport.endpoint, context.config.application.name);\n const storage = createSessionStorage(storageKey);\n const documentStartedAt =\n typeof performance !== 'undefined' && Number.isFinite(performance.timeOrigin)\n ? performance.timeOrigin\n : Date.now();\n let currentSessionId: string | undefined;\n let expirationTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const readRecord = (now = Date.now()) => parseSessionRecord(storage.read(), now, SESSION_MAX_DURATION_MS);\n\n const clearExpirationTimeout = () => {\n if (expirationTimeout !== undefined) {\n clearTimeout(expirationTimeout);\n expirationTimeout = undefined;\n }\n };\n\n const deactivate = (reason: 'external' | SessionExpireReason, expiredAt = Date.now()) => {\n const expiredSessionId = currentSessionId;\n if (!expiredSessionId) return;\n context.notifySessionLifecycle({\n expiredAt,\n id: expiredSessionId,\n reason,\n type: 'expired',\n });\n clearExpirationTimeout();\n currentSessionId = undefined;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n },\n expiredAt,\n );\n void context.flush();\n };\n\n const expireCurrentSession = () => {\n const now = Date.now();\n const record = readRecord(now);\n if (!record) {\n deactivate('external', now);\n return;\n }\n if (record.status === 'expired') {\n deactivate(record.reason, record.expiredAt);\n return;\n }\n if (record.id !== currentSessionId) {\n deactivate('external', now);\n return;\n }\n const reason = getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS);\n if (!reason) {\n scheduleExpiration(record);\n return;\n }\n const expired: ExpiredSessionRecord = {\n expiredAt: now,\n previousId: record.id,\n reason,\n status: 'expired',\n };\n storage.write(expired);\n deactivate(reason, now);\n };\n\n const scheduleExpiration = (record: ActiveSessionRecord) => {\n clearExpirationTimeout();\n const expiresAt = Math.min(record.expiresAt, record.startedAt + SESSION_MAX_DURATION_MS);\n expirationTimeout = setTimeout(expireCurrentSession, Math.max(0, expiresAt - Date.now()));\n };\n\n const activate = (\n record: ActiveSessionRecord,\n lifecycle?: {\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n },\n ) => {\n const changed = record.id !== currentSessionId;\n currentSessionId = record.id;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: record.id,\n },\n Math.min(record.startedAt, documentStartedAt),\n );\n scheduleExpiration(record);\n if (changed && lifecycle) {\n context.notifySessionLifecycle({\n id: record.id,\n previousId: lifecycle.previousId,\n reason: lifecycle.reason,\n startedAt: record.startedAt,\n type: 'renewed',\n });\n }\n };\n\n const expandOrRenew = (isInitialization = false) => {\n const now = Date.now();\n const record = readRecord(now);\n if (record?.status === 'active' && !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)) {\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded, currentSessionId ? undefined : { reason: 'shared' });\n return;\n }\n\n const previousId = record?.status === 'expired' ? record.previousId : record?.id;\n const reason =\n record?.status === 'expired'\n ? record.reason\n : record\n ? getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n : isInitialization\n ? 'init'\n : undefined;\n const created = createActiveSessionRecord(\n createRandomId('session'),\n now,\n SESSION_IDLE_TIMEOUT_MS,\n SESSION_MAX_DURATION_MS,\n );\n storage.write(created);\n activate(created, {\n previousId,\n reason: reason ?? 'init',\n });\n emitSessionEvent(context, {\n previousId,\n reason: reason ?? 'init',\n record: created,\n });\n };\n\n const expandOnly = () => {\n if (!currentSessionId) return;\n const now = Date.now();\n const record = readRecord(now);\n if (\n record?.status !== 'active' ||\n record.id !== currentSessionId ||\n getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n expireCurrentSession();\n return;\n }\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded);\n };\n\n expandOrRenew(true);\n\n let lastActivityAt = 0;\n const onActivity = () => {\n const now = Date.now();\n if (now - lastActivityAt < ACTIVITY_THROTTLE_MS) return;\n lastActivityAt = now;\n expandOrRenew();\n };\n const activityListenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n ACTIVITY_EVENTS.forEach(eventName => window.addEventListener(eventName, onActivity, activityListenerOptions));\n teardownCallbacks.push(() => {\n ACTIVITY_EVENTS.forEach(eventName =>\n window.removeEventListener(eventName, onActivity, activityListenerOptions),\n );\n });\n\n const expandWhenVisible = () => {\n if (document.visibilityState === 'visible') expandOnly();\n };\n document.addEventListener('visibilitychange', expandWhenVisible);\n const visibilityInterval = window.setInterval(expandWhenVisible, VISIBILITY_CHECK_MS);\n teardownCallbacks.push(() => {\n document.removeEventListener('visibilitychange', expandWhenVisible);\n window.clearInterval(visibilityInterval);\n });\n\n const onStorage = (event: StorageEvent) => {\n if (!storage.isLocalStorage() || event.storageArea !== window.localStorage || event.key !== storageKey) return;\n const now = Date.now();\n const record = parseSessionRecord(event.newValue, now, SESSION_MAX_DURATION_MS);\n if (\n record?.status === 'active' &&\n record.id === currentSessionId &&\n !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n scheduleExpiration(record);\n return;\n }\n // 当前 tab 一旦失效,不被其他 tab 新建的 session 被动复活;本 tab 发生交互后再接管共享 session。\n deactivate(\n record?.status === 'expired' ? record.reason : 'external',\n record?.status === 'expired' ? record.expiredAt : now,\n );\n };\n window.addEventListener('storage', onStorage);\n teardownCallbacks.push(() => window.removeEventListener('storage', onStorage));\n teardownCallbacks.push(clearExpirationTimeout);\n },\n shutdown() {\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Web Vitals 插件\n *\n * 基于 google/web-vitals 库(attribution 版本)采集 Core Web Vitals:\n * CLS / FCP / INP / LCP / TTFB\n *\n * 维度分层(关键设计):\n * - metric (histogram):仅带低基数维度(`view.url_path_group` / navigation.type / vital.name / vital.rating),\n * 适合做 P50 / P75 / P99 聚合统计,避免维度爆炸\n * - span (browser.web_vital):带 vital.id(唯一)、vital.value 与 attribution 详情\n * (LCP 元素 url / CLS shift target / INP interaction target 等高基数字段),\n * 方便按个例排查\"哪个元素拖累了 LCP\"\n *\n * Histogram 设计:\n * - browser.web_vital.cls:单位 '1'(无量纲),CLS 是累积分数,不能与 ms 混用\n * - browser.web_vital.duration:单位 'ms',FCP/INP/LCP/TTFB 共享,靠 vital.metric 维度区分\n *\n * Navigation Type:\n * 优先使用 metric.navigationType(web-vitals 内部计算,含 'back-forward-cache' 等准确值),\n * fallback 到 PerformanceNavigationTiming.type(注意 web-vitals 用连字符 'back-forward',\n * PerformanceNavigationTiming 用下划线 'back_forward',统一成连字符)。\n *\n * View 边界:FCP/LCP/TTFB 使用 web-vitals 的条目时间回查历史 View;CLS/INP 按 SDK View 重置,\n * CLS 使用 session window,INP 使用当前 View 的 p98 交互候选。\n *\n * 生命周期保护:\n * - disposed 标志位:shutdown 后所有回调入口 early return,避免向已销毁的 provider 上报\n * - dynamic import 完成后再次检查 disposed:处理\"start() 完成前 shutdown()\"的 race\n * - dynamic import 自身 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n * 即使加载失败也不影响整体启动\n */\n\nimport { type Attributes } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, getVitalResult, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type * as WebVitalsAttribution from 'web-vitals/attribution';\nimport type { MetricWithAttribution } from 'web-vitals/attribution';\n\ninterface EventTimingEntry extends PerformanceEntry {\n interactionId?: number;\n processingEnd?: number;\n processingStart?: number;\n target?: EventTarget | null;\n}\n\ninterface LayoutShiftEntry extends PerformanceEntry {\n hadRecentInput?: boolean;\n sources?: Array<{ node?: Node | null }>;\n value?: number;\n}\n\ntype NavigationTypeName =\n | 'back-forward'\n | 'back-forward-cache'\n | 'navigate'\n | 'prerender'\n | 'reload'\n | 'restore'\n | 'unknown';\n\ninterface ViewVitalState {\n cls: number;\n clsWindowEnd?: number;\n clsWindowLargestShiftTarget?: string;\n clsWindowLargestShiftValue: number;\n clsWindowStart?: number;\n clsWindowValue: number;\n interactionCountAtStart?: number;\n interactionIdMax: number;\n interactionIdMin: number;\n inpCandidates: InpCandidate[];\n largestShiftTarget?: string;\n largestShiftValue: number;\n runtimeAttributes: Attributes;\n startedAt: number;\n}\n\ninterface InpCandidate {\n duration: number;\n inputDelay: number;\n interactionId: number;\n interactionTarget?: string;\n interactionType: string;\n presentationDelay: number;\n processingDuration: number;\n}\n\ntype VitalLike = Pick<\n MetricWithAttribution,\n 'attribution' | 'entries' | 'id' | 'name' | 'navigationType' | 'rating' | 'value'\n>;\n\nconst FALLBACK_NAVIGATION_TYPE: NavigationTypeName = 'unknown';\nconst MAX_INP_CANDIDATES = 10;\nconst MAX_INP_VALUE = 60_000;\nconst MAX_CLS_WINDOW_DURATION = 5_000;\nconst MAX_CLS_UPDATE_GAP = 1_000;\n\n// PerformanceNavigationTiming.type 用下划线('back_forward'),web-vitals 用连字符('back-forward'),\n// 统一成 web-vitals 的连字符风格作为 fallback,下游聚合时维度值一致\nconst normalizeNavigationType = (raw: string | undefined): NavigationTypeName => {\n if (!raw) return FALLBACK_NAVIGATION_TYPE;\n return raw.replace(/_/g, '-') as NavigationTypeName;\n};\n\nconst computeFallbackNavigationType = (): NavigationTypeName => {\n if (typeof performance === 'undefined') return FALLBACK_NAVIGATION_TYPE;\n const entry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n return normalizeNavigationType(entry?.type);\n};\n\nconst getRating = (name: 'CLS' | 'INP', value: number): VitalLike['rating'] => {\n const [good, poor] = name === 'CLS' ? [0.1, 0.25] : [200, 500];\n return value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n};\n\nconst getTargetName = (target: EventTarget | Node | null | undefined) => {\n if (typeof Element === 'undefined' || !(target instanceof Element)) return undefined;\n return (\n target.getAttribute('data-bk-action-name') ??\n target.getAttribute('aria-label') ??\n target.getAttribute('name') ??\n target.tagName.toLowerCase()\n );\n};\n\nconst getInteractionCount = () => {\n if (typeof performance === 'undefined') return undefined;\n const value = (performance as Performance & { interactionCount?: number }).interactionCount;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n};\n\nexport const updateViewCls = (view: ViewVitalState, entry: LayoutShiftEntry) => {\n const value = entry.value ?? 0;\n if (entry.hadRecentInput || !Number.isFinite(value)) return;\n const shouldCreateWindow =\n view.clsWindowStart === undefined ||\n entry.startTime - (view.clsWindowEnd ?? entry.startTime) >= MAX_CLS_UPDATE_GAP ||\n entry.startTime - view.clsWindowStart >= MAX_CLS_WINDOW_DURATION;\n if (shouldCreateWindow) {\n view.clsWindowStart = entry.startTime;\n view.clsWindowValue = value;\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n } else {\n view.clsWindowValue += value;\n if (value > view.clsWindowLargestShiftValue) {\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n }\n }\n view.clsWindowEnd = entry.startTime;\n if (view.clsWindowValue > view.cls) {\n view.cls = Math.round(view.clsWindowValue * 10_000) / 10_000;\n view.largestShiftValue = view.clsWindowLargestShiftValue;\n view.largestShiftTarget = view.clsWindowLargestShiftTarget;\n }\n};\n\nexport const updateViewInp = (view: ViewVitalState, entry: EventTimingEntry) => {\n if (!entry.interactionId || !Number.isFinite(entry.duration)) return;\n view.interactionIdMin = Math.min(view.interactionIdMin, entry.interactionId);\n view.interactionIdMax = Math.max(view.interactionIdMax, entry.interactionId);\n const processingStart = entry.processingStart ?? entry.startTime;\n const processingEnd = entry.processingEnd ?? processingStart;\n const candidate: InpCandidate = {\n duration: Math.min(entry.duration, MAX_INP_VALUE),\n inputDelay: Math.max(0, processingStart - entry.startTime),\n interactionId: entry.interactionId,\n interactionTarget: getTargetName(entry.target),\n interactionType: entry.name,\n presentationDelay: Math.max(0, entry.startTime + entry.duration - processingEnd),\n processingDuration: Math.max(0, processingEnd - processingStart),\n };\n const index = view.inpCandidates.findIndex(item => item.interactionId === entry.interactionId);\n if (index >= 0) {\n if (candidate.duration > view.inpCandidates[index].duration) view.inpCandidates[index] = candidate;\n } else {\n view.inpCandidates.push(candidate);\n }\n view.inpCandidates.sort((left, right) => right.duration - left.duration).splice(MAX_INP_CANDIDATES);\n};\n\nexport const getViewInp = (view: ViewVitalState): InpCandidate | undefined => {\n const nativeCount = getInteractionCount();\n const interactionCount =\n nativeCount !== undefined && view.interactionCountAtStart !== undefined\n ? Math.max(0, nativeCount - view.interactionCountAtStart)\n : view.interactionIdMin === Infinity\n ? 0\n : (view.interactionIdMax - view.interactionIdMin) / 7 + 1;\n if (!view.inpCandidates.length) return undefined;\n return view.inpCandidates[Math.min(view.inpCandidates.length - 1, Math.floor(interactionCount / 50))];\n};\n\n// 仅挑选低基数、可枚举的字段进 metric attributes,避免指标维度爆炸\nconst buildMetricDimensions = (metric: VitalLike, fallbackNavType: NavigationTypeName): Attributes => ({\n // 优先使用 metric.navigationType(含 bfcache 等准确值),fallback 到首次 navigation entry\n 'rum.navigation.type': metric.navigationType ?? fallbackNavType,\n [RUM_ATTRIBUTES.vitalMetric]: metric.name.toLowerCase(),\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n});\n\n// 把 attribution 中价值最高的字段抽出来(仅放进 span,不放进 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('vital.lcp.url', getAttrValue('url'), redactUrl);\n setIfDefined('vital.lcp.target', getAttrValue('target'));\n setIfDefined('vital.lcp.element_render_delay', getAttrValue('elementRenderDelay'));\n setIfDefined('vital.lcp.resource_load_duration', getAttrValue('resourceLoadDuration'));\n setIfDefined('vital.lcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n break;\n case 'CLS':\n setIfDefined('vital.cls.largest_shift_target', getAttrValue('largestShiftTarget'));\n setIfDefined('vital.cls.largest_shift_value', getAttrValue('largestShiftValue'));\n setIfDefined('vital.cls.load_state', getAttrValue('loadState'));\n break;\n case 'INP':\n setIfDefined('vital.inp.interaction_target', getAttrValue('interactionTarget'));\n setIfDefined('vital.inp.interaction_type', getAttrValue('interactionType'));\n setIfDefined('vital.inp.input_delay', getAttrValue('inputDelay'));\n setIfDefined('vital.inp.processing_duration', getAttrValue('processingDuration'));\n setIfDefined('vital.inp.presentation_delay', getAttrValue('presentationDelay'));\n break;\n case 'FCP':\n setIfDefined('vital.fcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n setIfDefined('vital.fcp.load_state', getAttrValue('loadState'));\n break;\n case 'TTFB':\n setIfDefined('vital.ttfb.waiting_duration', getAttrValue('waitingDuration'));\n setIfDefined('vital.ttfb.dns_duration', getAttrValue('dnsDuration'));\n setIfDefined('vital.ttfb.connection_duration', getAttrValue('connectionDuration'));\n setIfDefined('vital.ttfb.request_duration', getAttrValue('requestDuration'));\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (): BkOTPlugin => {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n return {\n name: 'web-vitals',\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // dynamic import 自包 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n // 离线 / chunk 加载失败时不影响其他插件,且 debug 模式留下排查痕迹\n let webVitalsModule: typeof WebVitalsAttribution;\n try {\n webVitalsModule = await import('web-vitals/attribution');\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn('[bk-ot] web-vitals load failed:', error);\n }\n return;\n }\n // import 期间用户可能已经 shutdown:此后再注册回调会向已销毁 provider 上报\n if (disposed) return;\n\n const { onFCP, onLCP, onTTFB } = webVitalsModule;\n const fallbackNavType = computeFallbackNavigationType();\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, runtimeAttributes: Attributes, contextTime?: number) => {\n const metricName = metric.name.toLowerCase();\n const span = context.startSpan(\n 'browser.web_vital',\n {\n ...context.config.hooks.getPageAttributes(),\n ...runtimeAttributes,\n ...buildMetricDimensions(metric, fallbackNavType),\n ...buildRumSpanAttributes({\n duration: metric.name === 'CLS' ? undefined : metric.value,\n errorType: metric.rating === 'good' ? 'none' : 'slow',\n eventLabel: 'Web 指标',\n result: getVitalResult(metric.rating),\n spanSubtype: metricName,\n spanType: RUM_SPAN_TYPE.vital,\n targetLabel: metricName,\n targetValue: metric.value,\n }),\n [RUM_ATTRIBUTES.vitalMetric]: metricName,\n [RUM_ATTRIBUTES.vitalValue]: metric.value,\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n // 单条度量唯一 ID 仅放进 span,避免污染 metric 维度\n 'vital.id': metric.id,\n ...pickAttribution(metric, context.config.hooks.redactUrl),\n },\n contextTime === undefined ? undefined : { contextTime },\n );\n span.end();\n };\n\n const recordDurationMetric = (metric: VitalLike) => {\n // 防御非法值:极端浏览器实现 bug 可能给出 NaN/Infinity,OT 后端会拒收\n if (disposed || !Number.isFinite(metric.value)) return;\n const firstEntry = metric.entries[0];\n const contextTime =\n firstEntry && typeof performance !== 'undefined' ? performance.timeOrigin + firstEntry.startTime : undefined;\n const runtimeAttributes =\n contextTime === undefined ? context.getRuntimeAttributes() : context.getRuntimeAttributesAt(contextTime);\n durationHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(runtimeAttributes),\n ...buildMetricDimensions(metric, fallbackNavType),\n }),\n );\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n let currentView: undefined | ViewVitalState;\n let eventObserver: PerformanceObserver | undefined;\n let layoutShiftObserver: PerformanceObserver | undefined;\n\n const recordViewMetric = (metric: VitalLike, runtimeAttributes: Attributes, contextTime: number) => {\n const metricAttributes = context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(runtimeAttributes),\n ...buildMetricDimensions(metric, fallbackNavType),\n });\n if (metric.name === 'CLS') clsHistogram.record(metric.value, metricAttributes);\n else durationHistogram.record(metric.value, metricAttributes);\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n const finalizeView = () => {\n const view = currentView;\n if (!view) return;\n recordViewMetric(\n {\n attribution: {\n largestShiftTarget: view.largestShiftTarget,\n largestShiftValue: view.largestShiftValue,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'CLS',\n navigationType: fallbackNavType,\n rating: getRating('CLS', view.cls),\n value: view.cls,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n const inp = getViewInp(view);\n if (inp) {\n recordViewMetric(\n {\n attribution: {\n inputDelay: inp.inputDelay,\n interactionTarget: inp.interactionTarget,\n interactionType: inp.interactionType,\n presentationDelay: inp.presentationDelay,\n processingDuration: inp.processingDuration,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'INP',\n navigationType: fallbackNavType,\n rating: getRating('INP', inp.duration),\n value: inp.duration,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n }\n };\n\n const unsubscribeView = context.onViewChange(view => {\n finalizeView();\n currentView = view.id\n ? {\n cls: 0,\n clsWindowLargestShiftValue: 0,\n clsWindowValue: 0,\n interactionCountAtStart: getInteractionCount(),\n interactionIdMax: 0,\n interactionIdMin: Infinity,\n inpCandidates: [],\n largestShiftValue: 0,\n runtimeAttributes: context.getRuntimeAttributes(),\n startedAt: view.startedAt ?? Date.now(),\n }\n : undefined;\n });\n\n if (typeof PerformanceObserver !== 'undefined') {\n if (PerformanceObserver.supportedEntryTypes?.includes('layout-shift')) {\n layoutShiftObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as LayoutShiftEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt) continue;\n updateViewCls(currentView, entry);\n }\n });\n layoutShiftObserver.observe({ buffered: true, type: 'layout-shift' });\n }\n if (PerformanceObserver.supportedEntryTypes?.includes('event')) {\n eventObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as EventTimingEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt || !entry.interactionId || !Number.isFinite(entry.duration))\n continue;\n updateViewInp(currentView, entry);\n }\n });\n eventObserver.observe({ buffered: true, durationThreshold: 40, type: 'event' });\n }\n }\n onFCP(recordDurationMetric);\n onLCP(recordDurationMetric);\n onTTFB(recordDurationMetric);\n\n cleanup = () => {\n finalizeView();\n currentView = undefined;\n unsubscribeView();\n layoutShiftObserver?.disconnect();\n eventObserver?.disconnect();\n };\n },\n shutdown() {\n // web-vitals 的 onXXX 不返回 unsubscribe,无法真正取消订阅;\n // 用 disposed 标志位让回调入口 early return 即可\n disposed = true;\n cleanup?.();\n cleanup = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * WebSocket 插件\n *\n * 通过 Proxy 包装 window.WebSocket,对每个 ws 实例采集:\n * - span (`websocket.connect`):仅覆盖建连阶段,避免长连接 span 永远不结束\n * span attributes: url.full / url.scheme / server.address / network.protocol.name\n * - histogram (`browser.websocket.connect.duration`):成功建连耗时(ms),便于聚合 P99\n * - counter (`browser.websocket.message.count` / `.bytes`):双向消息数与字节数,\n * 按 websocket.direction (in/out) 维度区分\n * - counter (`browser.websocket.error.count`):错误次数,按 websocket.error.phase\n * (connect / runtime) 区分建连失败与运行时中断\n * - log (`websocket.error` / `websocket.close`):错误与关闭事件详情;error 按 url\n * 做 throttle 防 reconnect 风暴\n *\n * Patch 策略(关键设计):\n * 使用 Proxy(NativeWebSocket, { construct }),相比传统 monkey-patch 优势:\n * - 自动透传静态常量(WebSocket.OPEN / CONNECTING 等),无需手动复制;\n * Object.assign 复制会失败,因为这些常量是 non-enumerable\n * - 完美支持 `class MyWS extends WebSocket` 子类化场景:\n * Reflect.construct 用 newTarget=MyWS 创建对象,原型链与 instanceof 都正确\n * - 透明的 Symbol.hasInstance 行为,业务侧 instanceof 检查正常\n *\n * 已知行为与限制:\n * - send 方法在每个实例上覆盖(不在 prototype),原因是 native ws 没有 send 回调,\n * 只能包装实例方法计数 outbound;副作用是若业务 `delete socket.send` 会失去统计\n * - shutdown 后已存在的 socket 实例仍带着我们的事件监听与 send 包装;\n * 用 disposed 标志位让回调入口 early return,新建的 ws 走原生路径\n * - 大消息(> 64KB string)字节计算降级为 string.length 估算(CJK 等多字节字符会偏小),\n * 避免 TextEncoder().encode() 的 O(n) 开销在 IM / 流式场景拖累主线程\n * - error throttle:默认 60s 窗口最多 5 条同 url 错误日志,counter 仍精确累计不受影响\n *\n * 多 patch 兼容:\n * shutdown 时检查 window.WebSocket 是否仍是我们的 Proxy,若被其他库覆盖则不强行恢复,\n * 避免抹掉后来者的 patch\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { createThrottle } from '../core/throttle';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Span } from '@opentelemetry/api';\n\n// 64KB 以下精确计算 UTF-8 字节,超过则用 string.length 粗估,避免 IM 大文本拖慢主线程\nconst BYTES_PRECISE_THRESHOLD = 64 * 1024;\n// 错误日志节流:60s / 5 条同 url,防 reconnect 风暴\nconst ERROR_THROTTLE_WINDOW_MS = 60_000;\nconst ERROR_THROTTLE_MAX_PER_WINDOW = 5;\n\nconst getMessageByteLength = (data: unknown): number => {\n if (data == null) return 0;\n if (typeof data === 'string') {\n if (data.length > BYTES_PRECISE_THRESHOLD || typeof TextEncoder === 'undefined') {\n // 粗估:纯 ASCII 准确,CJK / emoji 会偏小;权衡性能与准确性\n return data.length;\n }\n return 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\ninterface WebSocketAttributeBundle {\n metricAttributes: Record<string, string>;\n spanAttributes: Record<string, string>;\n}\n\nconst buildWebSocketAttributes = (url: string, redactUrl: (url: string) => string): WebSocketAttributeBundle => {\n const spanAttributes: Record<string, string> = {\n 'network.protocol.name': 'websocket',\n 'url.full': redactUrl(url),\n };\n const metricAttributes: Record<string, string> = {\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 // url.scheme 区分 ws / wss,下游能按加密/非加密分组\n spanAttributes['url.scheme'] = parsed.protocol.replace(':', '');\n // server.address 进 metric 是中等基数(按域名维度通常 < 10),对 metric 系列影响可控\n metricAttributes['server.address'] = parsed.host;\n metricAttributes['url.scheme'] = spanAttributes['url.scheme'];\n } catch {\n /* malformed URL:保留 redacted 原 url,跳过 host / scheme 解析 */\n }\n\n return { metricAttributes, spanAttributes };\n};\n\ninterface InstrumentDeps {\n bytesCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n connectDurationHistogram: ReturnType<BkOTRuntimeContext['meter']['createHistogram']>;\n context: BkOTRuntimeContext;\n errorCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n errorThrottle: ReturnType<typeof createThrottle>;\n messageCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n url: string;\n isDisposed: () => boolean;\n}\n\nconst instrumentSocket = (socket: WebSocket, deps: InstrumentDeps) => {\n const {\n bytesCounter,\n connectDurationHistogram,\n context,\n errorCounter,\n errorThrottle,\n isDisposed,\n messageCounter,\n url,\n } = deps;\n const { metricAttributes, spanAttributes } = buildWebSocketAttributes(url, context.config.hooks.redactUrl);\n // 补上业务自定义 metric 维度(context.attributes.metric hook),与 web-vitals / longTask 保持一致。\n // 每个 socket 只计算一次,避免消息热路径反复调用用户 hook。\n const metricBaseAttributes = context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n ...metricAttributes,\n });\n const targetLabel = spanAttributes['server.address'] ?? spanAttributes['url.full'];\n\n // connect span 仅覆盖建连阶段,长连接生命周期不绑定 span,避免\"幽灵 span\"\n const connectSpan: Span = context.startSpan('websocket.connect', {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: 'WebSocket',\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n }),\n ...(spanAttributes['server.address'] ? { [RUM_ATTRIBUTES.targetDomain]: spanAttributes['server.address'] } : {}),\n });\n const startTime = performance.now();\n let connectEnded = false;\n\n const endConnectSpan = (status: 'error' | 'opened') => {\n if (connectEnded) return;\n connectEnded = true;\n const duration = performance.now() - startTime;\n if (status === 'error') {\n connectSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'websocket connect failed' });\n }\n connectSpan.setAttributes(\n buildRumSpanAttributes({\n duration,\n errorType: status === 'error' ? 'network' : 'none',\n eventLabel: 'WebSocket',\n result: status === 'error' ? RUM_SPAN_RESULT.error : RUM_SPAN_RESULT.success,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n targetValue: duration,\n }),\n );\n connectSpan.end();\n // 仅成功建连记入 histogram;错误的 duration 多为 timeout 固定值,统计意义有限\n if (status === 'opened' && !isDisposed()) {\n connectDurationHistogram.record(duration, metricBaseAttributes);\n }\n };\n\n socket.addEventListener('open', () => {\n if (isDisposed()) return;\n endConnectSpan('opened');\n });\n\n socket.addEventListener('message', event => {\n if (isDisposed()) return;\n const inAttrs = { ...metricBaseAttributes, 'websocket.direction': 'in' };\n messageCounter.add(1, inAttrs);\n bytesCounter.add(getMessageByteLength(event.data), inAttrs);\n });\n\n socket.addEventListener('error', () => {\n if (isDisposed()) return;\n // 用 connectEnded 区分阶段:未 open 即出错为 connect 阶段,已 open 后为 runtime\n const phase = connectEnded ? 'runtime' : 'connect';\n errorCounter.add(1, { ...metricBaseAttributes, 'websocket.error.phase': phase });\n endConnectSpan('error');\n // 同 url 错误节流:reconnect 失败循环不至于产生日志风暴;counter 不受影响(已在上方累计)\n const throttleResult = errorThrottle.allow(`ws:error:${url}`);\n if (!throttleResult.allowed) return;\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'websocket.error',\n attributes: {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: 'network',\n eventLabel: 'WebSocket 错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n }),\n 'websocket.error.phase': phase,\n 'websocket.error.window_count': throttleResult.count,\n },\n });\n });\n\n socket.addEventListener('close', event => {\n if (isDisposed()) return;\n // 从未 open 就 close → 建连失败(部分浏览器只触发 close 不触发 error 的边界)\n if (!connectEnded) {\n endConnectSpan('error');\n }\n context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: 'websocket.close',\n attributes: {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: event.wasClean ? 'none' : 'network',\n eventLabel: 'WebSocket 关闭',\n result: event.wasClean ? RUM_SPAN_RESULT.success : RUM_SPAN_RESULT.warning,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n targetValue: event.code,\n }),\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 统计 outbound:原生 WebSocket 没有 send 回调或事件,只能逐实例覆盖方法\n const originalSend = socket.send.bind(socket);\n socket.send = (data: ArrayBufferLike | ArrayBufferView | Blob | string) => {\n if (!isDisposed()) {\n const outAttrs = { ...metricBaseAttributes, 'websocket.direction': 'out' };\n messageCounter.add(1, outAttrs);\n bytesCounter.add(getMessageByteLength(data), outAttrs);\n }\n return originalSend(data);\n };\n};\n\nexport const createWebSocketPlugin = (): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n let patchedProxy: typeof WebSocket | undefined;\n let disposed = false;\n\n return {\n name: 'websocket',\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {\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 const connectDurationHistogram = context.meter.createHistogram('browser.websocket.connect.duration', {\n unit: 'ms',\n description: 'WebSocket connection establishment duration (only successful connects)',\n });\n const errorThrottle = createThrottle(ERROR_THROTTLE_WINDOW_MS, ERROR_THROTTLE_MAX_PER_WINDOW);\n const isDisposed = () => disposed;\n\n // Proxy 而非 function 替换:自动透传静态常量、原型与 Symbol.hasInstance;\n // 同时 Reflect.construct 用 newTarget 让 `class extends WebSocket` 子类化正确工作\n patchedProxy = new Proxy(nativeWebSocket, {\n construct(target, args, newTarget) {\n const socket = Reflect.construct(target, args, newTarget) as WebSocket;\n if (disposed) return socket;\n const url = args[0] != null ? String(args[0]) : '';\n // 用户主动 ignore 或上报 endpoint,避免回环监控\n if (!url || shouldIgnoreUrl(context.config, url)) {\n return socket;\n }\n instrumentSocket(socket, {\n bytesCounter,\n connectDurationHistogram,\n context,\n errorCounter,\n errorThrottle,\n isDisposed,\n messageCounter,\n url,\n });\n return socket;\n },\n });\n\n window.WebSocket = patchedProxy;\n },\n shutdown() {\n disposed = true;\n // 多 patch 兼容:仅当当前 window.WebSocket 还是我们的 Proxy 时才恢复,\n // 避免抹掉其他库后来者的 patch\n if (typeof window !== 'undefined' && originalWebSocket && window.WebSocket === patchedProxy) {\n window.WebSocket = originalWebSocket;\n }\n patchedProxy = undefined;\n originalWebSocket = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nexport type BkOTActivityEvent =\n | { endTime: number; startTime: number; type: 'long_task'; url?: never }\n | { endTime: number; startTime: number; type: 'resource'; url?: string }\n | { id: string; time: number; type: 'request_end' }\n | { id: string; time: number; type: 'request_start'; url?: string }\n | { time: number; type: 'dom_mutation' | 'error' | 'window_open' };\n\nexport class RumActivityObserver {\n private readonly listeners = new Set<(event: BkOTActivityEvent) => void>();\n\n public notify = (event: BkOTActivityEvent) => {\n this.listeners.forEach(listener => listener(event));\n };\n\n public subscribe = (listener: (event: BkOTActivityEvent) => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { type Attributes, type Span, context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { hrTimeToMilliseconds, timeInputToHrTime, W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { AggregationTemporalityPreference, 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, SamplingDecision, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\nimport { createActionPlugin } from '../plugins/action';\nimport { createActivityPlugin } from '../plugins/activity';\nimport { createBlankScreenPlugin } from '../plugins/blank-screen';\nimport { createCommonInstrumentationsPlugin } from '../plugins/common';\nimport { createCspViolationPlugin } from '../plugins/csp-violation';\nimport { createDevicePlugin } from '../plugins/device';\nimport { createErrorPlugin } from '../plugins/error';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createResourcePlugin } from '../plugins/resource';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\nimport { RumActivityObserver } from './action-activity';\nimport {\n type BkOTActionEventPayload,\n type BkOTCustomEventPayload,\n type BkOTOptions,\n type BkOTTrackingConfig,\n type BkOTTrackingConsent,\n type ResolvedBkOTConfig,\n type SignalExporterConfig,\n resolveConfig,\n} from './config';\nimport { createRandomId } from './id';\nimport { type BkOTPlugin, type BkOTRuntimeContext, type BkOTStartSpanOptions, PluginManager } from './plugin';\nimport { FilteringLogRecordProcessor, FilteringMetricExporter, FilteringSpanProcessor } from './processor';\nimport {\n type BkOTSessionLifecycleEvent,\n type BkOTStartViewOptions,\n type BkOTUserContext,\n RumRuntimeContext,\n} from './runtime-context';\nimport { isBkOTSampled, RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { LogRecord } from '@opentelemetry/api-logs';\nimport type { Sampler } from '@opentelemetry/sdk-trace-base';\n\nexport interface BkOTInstance extends Omit<\n BkOTRuntimeContext,\n 'notifyActivity' | 'onActivity' | 'onViewChange' | 'onViewCommand' | 'registerInstrumentations' | 'setView'\n> {\n flush: () => Promise<void>;\n reportAction: (payload: BkOTActionEventPayload) => void;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n setTrackingConsent: (consent: BkOTTrackingConsent) => Promise<void>;\n setUser: (user: BkOTUserContext) => void;\n setViewLoadingTime: () => void;\n setViewName: (name: string) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n startView: (options?: BkOTStartViewOptions) => void;\n use: (plugin: BkOTPlugin) => BkOTInstance;\n useInstrumentation: (instrumentation: Instrumentation) => BkOTInstance;\n}\n\nconst createExporterOptions = (config: SignalExporterConfig) => ({\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\nclass RumSessionSampler implements Sampler {\n public constructor(private readonly isSampled: () => boolean) {}\n\n public shouldSample() {\n return {\n decision: this.isSampled() ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,\n };\n }\n\n public toString() {\n return 'RumSessionSampler';\n }\n}\n\nlet globalRegistration:\n | undefined\n | {\n contextManager?: ZoneContextManager;\n ownsContext: boolean;\n ownsPropagator: boolean;\n users: number;\n };\n\nconst acquireOtelGlobals = () => {\n if (globalRegistration) {\n globalRegistration.users += 1;\n } else {\n const contextManager = new ZoneContextManager().enable();\n const ownsContext = context.setGlobalContextManager(contextManager);\n if (!ownsContext) contextManager.disable();\n globalRegistration = {\n contextManager: ownsContext ? contextManager : undefined,\n ownsContext,\n ownsPropagator: propagation.setGlobalPropagator(new W3CTraceContextPropagator()),\n users: 1,\n };\n }\n let released = false;\n return () => {\n if (released || !globalRegistration) return;\n released = true;\n globalRegistration.users -= 1;\n if (globalRegistration.users > 0) return;\n if (globalRegistration.ownsContext) context.disable();\n if (globalRegistration.ownsPropagator) propagation.disable();\n globalRegistration = undefined;\n };\n};\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n 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 span、Trace Header 与活动计数。\nconst getCorePlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.tracking.request),\n];\n\nconst getRumContextPlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createSessionPlugin(config.session),\n ...createBuiltInPlugin(config.tracking.device, true, createDevicePlugin),\n createActivityPlugin(),\n createPageViewPlugin(),\n createResourcePlugin(isBuiltInPluginEnabled(config.tracking.resource, true)),\n];\n\nconst isBuiltInPluginEnabled = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n) => (option === undefined ? defaultEnabled : option !== false);\n\nconst getBuiltInPluginOptions = <TOptions extends object>(option: boolean | TOptions | undefined) =>\n typeof option === 'object' && option !== null ? option : undefined;\n\nconst createBuiltInPlugin = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n factory: (options?: TOptions) => BkOTPlugin,\n) => (isBuiltInPluginEnabled(option, defaultEnabled) ? [factory(getBuiltInPluginOptions(option))] : []);\n\n// 上下文插件先启动,确保后续 span/log 能带上关联字段;Session sampler 与 processor 负责统一采样。\nconst getRumPlugins = (tracking: BkOTTrackingConfig): BkOTPlugin[] => [\n ...createBuiltInPlugin(tracking.error, true, createErrorPlugin),\n ...createBuiltInPlugin(tracking.webVitals, true, createWebVitalsPlugin),\n ...createBuiltInPlugin(tracking.blankScreen, true, createBlankScreenPlugin),\n ...createBuiltInPlugin(tracking.action, true, createActionPlugin),\n ...createBuiltInPlugin(tracking.websocket, false, createWebSocketPlugin),\n ...createBuiltInPlugin(tracking.longTask, false, createLongTaskPlugin),\n ...createBuiltInPlugin(tracking.cspViolation, false, createCspViolationPlugin),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'starting' | 'stopped';\n\nexport class BkOpenTelemetry implements BkOTInstance {\n private readonly activityObserver = new RumActivityObserver();\n private readonly flushProviders = async () => {\n await this.tracerProvider.forceFlush();\n await this.meterProvider.forceFlush();\n await this.loggerProvider.forceFlush();\n };\n private readonly loggerProvider: LoggerProvider;\n private readonly meterProvider: MeterProvider;\n private phase: SdkLifecyclePhase = 'idle';\n private pluginManager?: PluginManager;\n private readonly registeredInstrumentations: Instrumentation[];\n private releaseOtelGlobals?: () => void;\n private readonly runtimeContext: RumRuntimeContext;\n\n private sampled = false;\n private readonly startCollection = async () => {\n if (this.pluginManager || !this.config.enabled || this.config.trackingConsent !== 'granted') return;\n if (this.config.transport.traces !== false) this.releaseOtelGlobals = acquireOtelGlobals();\n const pluginManager = this.createPluginManager();\n this.pluginManager = pluginManager;\n try {\n await pluginManager.start(this.createPluginContext());\n this.registerInstrumentations(this.config.extensions.instrumentations ?? []);\n } catch (error) {\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n throw error;\n }\n };\n private readonly stopCollection = async (flush: boolean) => {\n const pluginManager = this.pluginManager;\n if (!pluginManager) return;\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n this.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n });\n if (flush) await this.flushProviders();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n };\n private readonly teardownCallbacks: Array<() => void> = [];\n private readonly tracerProvider: WebTracerProvider;\n private assertBeforeStart(method: string) {\n if (this.phase !== 'idle') {\n throw new Error(`[bk-ot] ${method}() must be called before start(); set autoStart: false before registration.`);\n }\n }\n private createPluginContext(): BkOTRuntimeContext {\n return {\n applyRedact: this.applyRedact,\n config: this.config,\n emitLog: this.emitLog,\n flush: this.flush,\n getRuntimeAttributes: this.getRuntimeAttributes,\n getRuntimeAttributesAt: this.getRuntimeAttributesAt,\n isSampled: this.isSampled,\n isSessionActive: this.isSessionActive,\n isViewActive: this.isViewActive,\n logger: this.logger,\n meter: this.meter,\n notifySessionLifecycle: this.notifySessionLifecycle,\n notifyActivity: this.activityObserver.notify,\n onActivity: this.activityObserver.subscribe,\n onSessionLifecycle: this.onSessionLifecycle,\n onViewCommand: this.runtimeContext.onViewCommand,\n onViewChange: this.runtimeContext.onViewChange,\n registerInstrumentations: this.registerInstrumentations,\n setRuntimeAttributes: this.setRuntimeAttributes,\n setUser: this.setUser,\n setView: this.runtimeContext.setView,\n startSpan: this.startSpan,\n tracer: this.tracer,\n };\n }\n private createPluginManager() {\n return new PluginManager([\n ...getRumContextPlugins(this.config),\n ...getCorePlugins(this.config),\n ...getRumPlugins(this.config.tracking),\n ...(this.config.extensions.plugins ?? []),\n ]);\n }\n public readonly applyRedact = (attributes: Attributes) => this.config.hooks.redactAttributes(attributes);\n\n public readonly config: ResolvedBkOTConfig;\n\n public readonly emitLog = (record: LogRecord) => {\n const contextTime = record.timestamp ? hrTimeToMilliseconds(timeInputToHrTime(record.timestamp)) : undefined;\n const merged: LogRecord = {\n ...record,\n attributes: this.applyRedact({\n ...(contextTime === undefined ? this.getRuntimeAttributes() : this.getRuntimeAttributesAt(contextTime)),\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.flushProviders();\n };\n\n public readonly getRuntimeAttributes = () => this.runtimeContext.getAttributes();\n public readonly getRuntimeAttributesAt = (timestamp: number) => this.runtimeContext.getAttributesAt(timestamp);\n public readonly isSampled = () => this.sampled;\n public readonly isSessionActive = () => this.runtimeContext.isSessionActive();\n public readonly isViewActive = () => this.runtimeContext.isViewActive();\n public readonly logger: BkOTRuntimeContext['logger'];\n\n public readonly meter: BkOTRuntimeContext['meter'];\n\n public readonly notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.runtimeContext.notifySessionLifecycle(event);\n };\n\n public readonly onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) =>\n this.runtimeContext.onSessionLifecycle(listener);\n\n public readonly registerInstrumentations = (instrumentations: Instrumentation[]) => {\n if (!instrumentations.length) return;\n registerInstrumentations({\n instrumentations,\n meterProvider: this.meterProvider,\n tracerProvider: this.tracerProvider,\n });\n this.registeredInstrumentations.push(...instrumentations);\n };\n\n public readonly reportAction = ({ attributes = {}, name }: BkOTActionEventPayload) => {\n if (!this.isSampled()) {\n return;\n }\n const actionId = createRandomId('action');\n const span = this.startSpan('action.custom', {\n ...this.config.hooks.getPageAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: '用户操作',\n spanSubtype: 'custom',\n spanType: RUM_SPAN_TYPE.action,\n targetLabel: name,\n traceScene: 'user_action',\n }),\n [RUM_ATTRIBUTES.actionId]: actionId,\n [RUM_ATTRIBUTES.actionName]: name,\n [RUM_ATTRIBUTES.actionType]: 'custom',\n });\n span.end();\n };\n\n public readonly reportCustomEvent = ({ attributes = {}, error, name }: BkOTCustomEventPayload) => {\n if (!this.isSampled()) {\n // 未采样时静默丢弃;如需调试请把 debug 选项打开\n if (this.config.debug) {\n globalThis.console?.warn('[bk-ot] custom event dropped (not sampled):', name);\n }\n return;\n }\n\n const span = this.startSpan(`custom.${name}`, {\n ...this.config.hooks.getPageAttributes(),\n ...this.config.hooks.getCustomAttributes(),\n ...buildRumSpanAttributes({\n errorType: error ? 'custom' : 'none',\n eventLabel: '自定义事件',\n result: error ? RUM_SPAN_RESULT.error : RUM_SPAN_RESULT.success,\n spanSubtype: name,\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel: name,\n }),\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, changedAt?: number) => {\n const nextAttributes = { ...attributes };\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n const sessionId = attributes[RUM_ATTRIBUTES.sessionId];\n this.sampled =\n this.config.enabled && typeof sessionId === 'string' && sessionId.length > 0\n ? isBkOTSampled(this.config, sessionId)\n : false;\n nextAttributes[RUM_SAMPLED_ATTRIBUTE] = this.sampled;\n }\n this.runtimeContext.setAttributes(nextAttributes, changedAt);\n };\n\n public readonly setTrackingConsent = async (consent: BkOTTrackingConsent) => {\n if (this.config.trackingConsent === consent) return;\n this.config.trackingConsent = consent;\n if (this.phase !== 'started') return;\n if (consent === 'granted') await this.startCollection();\n else await this.stopCollection(true);\n };\n\n public readonly setUser = (user: BkOTUserContext) => {\n this.runtimeContext.setUser(user);\n };\n\n public readonly setViewLoadingTime = () => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ time: Date.now(), type: 'set_loading_time' });\n };\n\n public readonly setViewName = (name: string) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ name, type: 'set_name' });\n };\n\n public readonly shutdown = async () => {\n if (this.phase === 'stopped') {\n return;\n }\n await this.stopCollection(false);\n await this.flushProviders();\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.tracerProvider.shutdown();\n await this.meterProvider.shutdown();\n await this.loggerProvider.shutdown();\n this.phase = 'stopped';\n };\n\n public readonly start = async () => {\n if (this.phase === 'started' || this.phase === 'starting') {\n return;\n }\n if (this.phase === 'stopped') {\n // shutdown 已经销毁了 provider/processor,此处禁止再次启动\n throw new Error('[bk-ot] instance has been shut down and cannot be restarted; create a new instance instead.');\n }\n if (!this.config.enabled) {\n this.phase = 'started';\n return;\n }\n this.phase = 'starting';\n try {\n await this.startCollection();\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const flushOnPageHide = () => {\n void this.flush();\n };\n const flushOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n void this.flush();\n }\n };\n window.addEventListener('pagehide', flushOnPageHide);\n document.addEventListener('visibilitychange', flushOnHidden);\n this.teardownCallbacks.push(\n () => window.removeEventListener('pagehide', flushOnPageHide),\n () => document.removeEventListener('visibilitychange', flushOnHidden),\n );\n }\n } catch (error) {\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.stopCollection(false);\n this.phase = 'idle';\n throw error;\n }\n this.phase = 'started';\n };\n\n public readonly startSpan = (name: string, attributes: Attributes = {}, options?: BkOTStartSpanOptions): Span => {\n const contextTime =\n options?.contextTime ?? (typeof options?.startTime === 'number' ? options.startTime : undefined);\n const runtimeAttributes =\n contextTime === undefined ? this.getRuntimeAttributes() : this.runtimeContext.getAttributesAt(contextTime);\n return this.tracer.startSpan(name, {\n kind: options?.kind ?? SpanKind.INTERNAL,\n startTime: options?.startTime,\n attributes: this.applyRedact({\n ...runtimeAttributes,\n ...attributes,\n }),\n });\n };\n\n public readonly startView = (options: BkOTStartViewOptions = {}) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ options, type: 'start' });\n };\n\n public readonly tracer: BkOTRuntimeContext['tracer'];\n\n public readonly use = (plugin: BkOTPlugin): this => {\n this.assertBeforeStart('use');\n this.config.extensions.plugins?.push(plugin);\n return this;\n };\n\n public readonly useInstrumentation = (instrumentation: Instrumentation): this => {\n this.assertBeforeStart('useInstrumentation');\n if (this.config.enabled) this.config.extensions.instrumentations?.push(instrumentation);\n return this;\n };\n\n constructor(inputConfig: BkOTOptions) {\n this.config = resolveConfig(inputConfig);\n this.runtimeContext = new RumRuntimeContext(\n {\n [RUM_SAMPLED_ATTRIBUTE]: false,\n },\n this.config.user,\n );\n\n const resource = resourceFromAttributes(this.config.resourceAttributes);\n const spanProcessors = (() => {\n if (this.config.transport.traces === false) return [];\n const exporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(this.config.transport.traces)),\n 'traces',\n this.config.debug,\n );\n const processor =\n this.config.spanProcessor === 'simple'\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, this.config.spanBatch);\n return [\n new FilteringSpanProcessor(processor, this.config, () =>\n this.applyRedact({\n ...this.runtimeContext.getAttributes(),\n ...this.config.hooks.getPageAttributes(),\n }),\n ),\n ];\n })();\n const metricReaders =\n this.config.transport.metrics === false\n ? []\n : [\n new PeriodicExportingMetricReader({\n exporter: new FilteringMetricExporter(\n wrapWithDebug(\n new OTLPMetricExporter({\n ...createExporterOptions(this.config.transport.metrics),\n temporalityPreference: AggregationTemporalityPreference.DELTA,\n }),\n 'metrics',\n this.config.debug,\n ),\n ),\n exportIntervalMillis: this.config.metricIntervalMillis,\n }),\n ];\n const logProcessors =\n this.config.transport.logs === false\n ? []\n : [\n new FilteringLogRecordProcessor(\n new BatchLogRecordProcessor(\n wrapWithDebug(\n new OTLPLogExporter(createExporterOptions(this.config.transport.logs)),\n 'logs',\n this.config.debug,\n ),\n ),\n this.config,\n ),\n ];\n\n this.tracerProvider = new WebTracerProvider({\n resource,\n sampler: new RumSessionSampler(() => this.config.transport.traces !== false && this.isSampled()),\n spanProcessors,\n });\n this.meterProvider = new MeterProvider({\n resource,\n readers: metricReaders,\n });\n this.loggerProvider = new LoggerProvider({\n resource,\n processors: logProcessors,\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.registeredInstrumentations = [];\n if (this.config.autoStart) {\n void this.start().catch(error => {\n globalThis.console?.warn('[bk-ot] auto start failed:', error);\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAa,IAAiB;CAC5B,kBAAkB;CAClB,wBAAwB;CACxB,uBAAuB;CACvB,UAAU;CACV,mBAAmB;CACnB,qBAAqB;CACrB,YAAY;CACZ,qBAAqB;CACrB,kBAAkB;CAClB,iBAAiB;CACjB,YAAY;CACZ,2BAA2B;CAC3B,aAAa;CACb,gBAAgB;CAChB,UAAU;CACV,YAAY;CACZ,gBAAgB;CAChB,WAAW;CACX,YAAY;CACZ,sBAAsB;CACtB,uBAAuB;CACvB,wBAAwB;CACxB,0BAA0B;CAC1B,iBAAiB;CACjB,kBAAkB;CAClB,mBAAmB;CACnB,YAAY;CACZ,uBAAuB;CACvB,qBAAqB;CACrB,mBAAmB;CACnB,sBAAsB;CACtB,iBAAiB;CACjB,QAAQ;CACR,aAAa;CACb,gBAAgB;CAChB,QAAQ;CACR,kBAAkB;CAClB,WAAW;CACX,kBAAkB;CAClB,aAAa;CACb,UAAU;CACV,aAAa;CACb,cAAc;CACd,aAAa;CACb,oBAAoB;CACpB,aAAa;CACb,YAAY;CACZ,cAAc;CACd,QAAQ;CACR,aAAa;CACb,aAAa;CACb,YAAY;CACZ,iBAAiB;CACjB,sBAAsB;CACtB,oBAAoB;CACpB,eAAe;CACf,QAAQ;CACR,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,UAAU;CACV,SAAS;CACT,kBAAkB;CACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC2KD,IAAM,KAAiC,KAAK,KAEtC,MAAqB,MAAkB,EAAM,MAAM,CAAC,QAAQ,QAAQ,GAAG,EAEvE,KAAqE;CACzE,QAAQ;CACR,SAAS;CACT,MAAM;CACP,EAGK,MAAyB,GAA8B,MAEpD,GADY,GAAkB,KAAA,OAAY,KAAZ,EAC3B,CAAW,MAAM,GAAoB,MAG3C,MACJ,GACA,MACiC;;CACjC,IAAM,KAAA,IAAe,EAAU,YAAA,OAAA,KAAA,IAAA,EAAU;CACzC,IAAI,MAAiB,IAAO,OAAO;CAEnC,IAAM,IAAA,KAAA,OAAA,KAAA,IAAiB,EAAc,UAE/B,IACJ,EAAU,SAAS,EAAU,WAAA,KAAA,QAAW,EAAc,UAAA,EAAA,EAAA,EAAA,EAAA,EAE5C,EAAU,QAAQ,EAAE,eAAe,UAAU,EAAU,SAAS,GAAG,EAAE,CAAA,GAAA,IACrE,EAAU,YAAA,OAAW,EAAE,GAAb,EAAa,GAAA,IAAA,KAAA,OAAA,KAAA,IACvB,EAAc,YAAA,OAAW,EAAE,GAAb,EACnB,GACD,KAAA;CAEN,OAAO;EACL,kBAAA,KAAA,OAAA,KAAA,IAAkB,EAAc;EAChC,UAAU,IACN,GAAkB,EAAe,GACjC,GAAsB,EAAU,UAAU,EAAW;EACzD,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,MAA4B,MAA2B,GACvD,MAAoB,MAAgB;CACxC,IAAI;EACF,IAAM,IAAS,IAAI,IAAI,GAAK,OAAO,WAAa,MAAc,qBAAqB,SAAS,KAAK;EAKjG,OAJA,EAAO,WAAW,IAClB,EAAO,WAAW,IAClB,EAAO,SAAS,IAChB,EAAO,OAAO,IACP,EAAO;aACR;EACN,OAAO,EAAI,QAAQ,WAAW,GAAG;;GAI/B,MAAuB,MAAgB;CAC3C,IAAI;EACF,OAAO,IAAI,IAAI,GAAK,OAAO,WAAa,MAAc,qBAAqB,SAAS,KAAK,CAAC,YAAY;aAChG;EACN,OAAO,KAAO;;GAIZ,MAA4B,MAAA;;QAAwE;EACxG,cAAA,IAAa,EAAY,gBAAA,OAAe,eAAf;EACzB,MAAM,EAAY;EAClB,SAAS,EAAY;EACtB;GAEK,MAA6B,MAAA,EAAA,EAAA;EAChC,EAAe,4BAA4B,EAAY;CACxD,gBAAgB;CAChB,sBAAsB;EACrB,EAAe,cAAc,EAAY;GACtC,EAAY,UAAU,GAAG,EAAe,iBAAiB,EAAY,SAAS,GAAG,EAAE,CAAA,EAAA,EAAA,EAAA;CACvF,0BAA0B;CAC1B,sBAAsB;EACvB,EAEK,MAA0B,MAAiE;;CAC/F,IAAM,IAAS,GAAoB,GAAW,SAAS,EACjD,IAAU,GAAoB,GAAW,UAAU,EACnD,IAAO,GAAoB,GAAW,OAAO;CAEnD,OAAO;EACL,UAFe,IAAA,IAAkB,EAAU,aAAA,OAAY,KAAZ,EAEjC,MAAA,IAAY;GAAC;GAAQ;GAAS;GAAK,CAAC,MAAK,MAAU,MAAW,GAAM,KAAA,OAAA,KAAA,IAAA,EAAE,aAAY;EAC5F;EACA;EACA;EACD;GAGG,MAA2B,MAA+C;CAC9E,IAAI,CAAC,GAAW,MAAU,MAAM,iCAAiC;CACjE,IAAM,IAAkB;EAAC;EAAU;EAAW;EAAO,CAAW,QAC9D,MAAU;;iBAAU,YAAA,OAAA,KAAA,IAAA,EAAU,QAAY;GAC3C;CACD,IAAI,CAAC,EAAe,QAClB,MAAU,MAAM,yDAAyD;CAE3E,KAAK,IAAM,KAAU,GAAgB;;EACnC,IAAM,KAAA,IAAe,EAAU,YAAA,OAAA,KAAA,IAAA,EAAU,IACnC,IAAiB,IAAe,EAAa,WAAW,KAAA;EAC9D,IAAI,EAAA,KAAA,QAAC,EAAgB,MAAM,KAAI,GAAA,IAAC,EAAU,aAAA,QAAA,EAAU,MAAM,GACxD,MAAU,MAAM,6BAA6B,EAAO,8CAA8C;;GAKlG,MAAgB,MAAA;;QAA8C;EAClE,aAAA,IAAY,EAAO,aAAA,OAAA,KAAA,IAAA,EAAU;EAC7B,oBAAA,KAAA,IACE,EAAO,YAAA,SAAA,IAAA,EAAS,eAAA,OAAA,KAAA,IAAA,EAAY,SAAA,eACpB;GACN,iBAAiB,OAAO,SAAW,MAAc,KAAK,OAAO,SAAS;GACtE,iBAAiB,OAAO,SAAW,MAAc,KAAK,OAAO,SAAS;GACvE,KAJ2B;EAK9B,sBAAA,KAAA,IAAqB,EAAO,YAAA,SAAA,IAAA,EAAS,eAAA,OAAA,KAAA,IAAA,EAAY,WAAA,eAAkB,EAAE,KAApB;EACjD,qBAAA,KAAA,IAAoB,EAAO,YAAA,SAAA,IAAA,EAAS,eAAA,OAAA,KAAA,IAAA,EAAY,UAAA,eAAiB,EAAE,KAAnB;EAChD,sBAAA,KAAA,IAAqB,EAAO,YAAA,SAAA,IAAA,EAAS,eAAA,OAAA,KAAA,IAAA,EAAY,WAAA,eAAkB,EAAE,KAApB;EACjD,mBAAkB,MAAA;;eACf,SAAA,IAAO,EAAO,aAAA,OAAA,KAAA,IAAA,EAAU,SAAS,YAAA,KAAA,IAAW,EAAO,SAAS,MAAK,iBAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAe,EAAI,GAAG,KAAA,MAAA,OACxF,GAAoB,EAAI,GADgE;;EAE1F,mBAAA,KAAA,IAAkB,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS,qBAAA,OAAoB,KAApB;EAClC,YAAA,KAAA,IAAW,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS,cAAA,OAAa,KAAb;EAC5B;GAEY,MAAiB,MAA4C;;CACxE,IAAI,EAAA,GAAA,IAAC,EAAO,gBAAA,SAAA,IAAA,EAAa,SAAA,SAAA,EAAM,MAAM,GACnC,MAAU,MAAM,wCAAwC;CAE1D,GAAwB,EAAO,UAAU;CACzC,IAAM,IAAc,GAAyB,EAAO,YAAY,EAC1D,IAAa,SAAA,IAAO,EAAO,aAAA,OAAA,KAAA,IAAA,EAAU,SAAS,WAAW,EAAO,SAAS,OAAO,KAAA,GAChF,KAAA,IAAc,EAAO,UAAU,YAAA,OAAA,KAAA,IAAA,EAAS,QACxC,KAAA,IAAe,EAAO,UAAU,YAAA,OAAA,KAAA,IAAA,EAAS;CAE/C,OAAO;EACL;EACA,YAAA,IAAW,EAAO,cAAA,OAAa,KAAb;EAClB,QAAA,IAAO,EAAO,UAAA,OAAS,KAAT;EACd,UAAA,IAAS,EAAO,YAAA,OAAW,KAAX;EAChB,YAAY;GACV,kBAAkB,CAAC,IAAA,KAAA,IAAI,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,qBAAA,OAAoB,EAAE,GAAtB,EAAwB;GAClE,SAAS,CAAC,IAAA,KAAA,IAAI,EAAO,eAAA,OAAA,KAAA,IAAA,EAAY,YAAA,OAAW,EAAE,GAAb,EAAe;GACjD;EACD,OAAO,GAAa,EAAO;EAC3B,sBAAsB,KAAA,IACjB,EAAa,mBAAA,OAAkB,KAAlB,IACd;EACJ,oBAAoB,GAA0B,EAAY;EAC1D,SAAA,EAAA,EAAA,EAAA,EACK,EAAO,QAAA,EAAA,EAAA,EAAA,EACV,YAAY,IAAA,IAAgB,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS,WAAW,EAAA,CACxD;EACD,WAAW,IAAc,EAAY,QAAQ,KAAA;EAC7C,eAAe,KAAA,IAAe,EAAY,cAAA,OAAa,UAAb,IAAwB;EAClE,WAAA,IAAU,EAAO,aAAA,OAAY,EAAE,GAAd;EACjB,kBAAA,KAAA,IAAiB,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS,oBAAA,OAAmB,YAAnB;EACjC,WAAW,GAAuB,EAAO,UAAU;EACnD,OAAA,IAAM,EAAO,YAAA,OAAA,KAAA,IAAA,EAAS;EACtB,MAAM;GACJ,sBAAA,KAAA,OAAA,KAAA,IAAsB,EAAY;GAClC,cAAA,KAAA,OAAA,KAAA,IAAa,EAAY,iBAAgB;GACzC,WAAA,KAAA,OAAA,KAAA,IAAU,EAAY,cAAa,WAAW,WAAW;GAC1D;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjWH,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;GAI9C,KAAK,IAAM,KAAU,EAAK,SACnB,OAAgB,GAAQ,EAAQ,OAAO,EAG5C,IAAI;IAEF,AADA,MAAM,EAAO,KAAK,EAAQ,EAC1B,EAAK,eAAe,KAAK,EAAO;YACzB,GAAO;IACd,IAAI,EAAQ,OAAO,OAAO;;KACxB,CAAA,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,mBAAmB,EAAO,KAAK,iBAAiB,EAAM;;;;;GCvG5E,IAAwB,kBAE/B,MAAsB,MAAkB;CAC5C,IAAI,IAAO;CACX,KAAK,IAAI,IAAQ,GAAG,IAAQ,EAAM,QAAQ,KAAS,GACjD,IAAO,KAAK,KAAK,IAAO,EAAM,WAAW,EAAM,EAAE,SAAW;CAO9D,OALA,KAAQ,KAAQ,IAChB,KAAQ,MAAS,GACjB,KAAQ,KAAQ,GAChB,KAAQ,MAAS,IACjB,KAAQ,KAAQ,IACR,MAAS,KAAK;GAMX,MACX,EAAE,gBAAa,cACf,MAEI,EAAQ,cAAc,IACjB,KAEL,EAAQ,cAAc,IACjB,KAEF,GAAmB,GAAG,EAAY,KAAK,IAAI,IAAY,GAAG,EAAQ,YC5BrE,WAAoB,OAAO,SAAW,MAAc,qBAAqB,OAAO,SAAS,QAElF,KAAiB,MAAgB;CAC5C,IAAI;EACF,OAAO,IAAI,IAAI,GAAK,IAAY,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;GAG9B,MAAkB,GAA4B,MAAgB;CACzE,IAAM,IAAY,EAAc,EAAI;CAEpC,OAAO;EAAC,EAAO,UAAU;EAAQ,EAAO,UAAU;EAAS,EAAO,UAAU;EAAK,CAC9E,QAAO,MAAY,MAAa,GAAM,CACtC,MAAK,MAAY,GAAe,GAAW,EAAc,EAAS,SAAS,CAAC,CAAC;GAGrE,MAAmB,GAA4B,MAAgB,GAAe,GAAQ,EAAI,EAEjG,MAAc,GAAyB,MACvC,OAAO,KAAY,aAAmB,EAAQ,EAAI,GAClD,OAAO,KAAY,WAAiB,EAAI,SAAS,EAAQ,IAC7D,EAAQ,YAAY,GACb,EAAQ,KAAK,EAAI,GAGb,KAAkB,GAAyD,OACrF,MAAM,QAAQ,EAAS,GAAG,IAAW,IAAW,CAAC,EAAS,GAAG,EAAE,EAAE,MAAK,MAAW,GAAW,GAAS,EAAI,CAAC,EAEvG,MAAoB,MACxB,OAAO,EAAO,SAAS,WAAY,WAAW,EAAO,SAAS,UAAU,KAAA,GAE7D,MAAuB,GAA4B,MAC9D;;WAAgB,GAAQ,EAAI,IAAI,GAAA,IAAe,GAAiB,EAAO,KAAA,OAAA,KAAA,IAAA,EAAE,cAAc,EAAc,EAAI,CAAC;GAG/F,MAA8B,GAA4B,MACrE;;UAAO,UAAU,WAAW,MAAS,GAAA,IAAe,GAAiB,EAAO,KAAA,OAAA,KAAA,IAAA,EAAE,oBAAoB,EAAc,EAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;ACpCvH,IAAM,KAA0B,CAAC,YAAY,WAAW,EAC3C,KAA4B,0BAEnC,MAAc,MAA2C;CAC7D,KAAK,IAAM,KAAO,IAAyB;;EACzC,IAAM,KAAA,IAAQ,EAAK,eAAA,OAAA,KAAA,IAAA,EAAa;EAChC,IAAI,OAAO,KAAU,UACnB,OAAO;;GAMP,MAAgB,MAAoD;CACxE,IAAM,IAAA,KAAA,OAAA,KAAA,IAAY,EAAa,EAAe;CAC9C,OAAO,OAAO,KAAc,YAAY,EAAU,SAAS;GAGvD,MAAa,MAAoD;CACrE,IAAM,IAAA,KAAA,OAAA,KAAA,IAAS,EAAa,EAAe;CAC3C,OAAO,OAAO,KAAW,YAAY,EAAO,SAAS;GAGjD,MAAiB,OAAA,KAAA,OAAA,KAAA,IACrB,EAAA,uBAAwC,MAAQ,GAAa,EAAW,IAAI,GAAU,EAAW,EAEtF,KAAb,MAAuE;CACrE,YACE,GACA,GACA;EADiB,AADA,KAAA,QAAA,GACA,KAAA,SAAA;;CAGnB,QAAe,GAAoE;;EACjF,QAAA,KAAA,KAAA,IAAO,KAAK,OAAM,YAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAU,EAAQ,KAAA,OAAI,KAAJ;;CAGtC,aAAoB;EAClB,OAAO,KAAK,MAAM,YAAY;;CAGhC,OAAc,GAAyB,GAAmB;;EACxD,IAAM,IAAwB,EAAU,SAAS,mBAAmB,EAAU,SAAS;EAErF,OAAc,EAAU,WAAW,IAClC,OAAA,IACC,EAAU,eAAA,OAAA,KAAA,IAAA,EAAA,uBAAwC,MAClD,GAAa,EAAU,WAAW,EAEtC;UAAO,EAAU,WAAW;GAC5B,IAAI;;IACF,MAAA,IACE,KAAK,WAAA,SAAA,KAAA,IAAA,EAAQ,OAAM,eAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAa;KAC9B,YAAY,EAAU;KACtB,OAAA,IAAM,EAAU,cAAA,OAAa,QAAA,IAAO,EAAU,SAAA,OAAQ,KAAR,EAAW,GAAzC;KAChB,QAAQ;KACT,CAAC,MAAK,IAEP;YAEK,GAAO;;IACd,CAAA,IAAI,KAAK,WAAA,QAAA,EAAQ,WAAO,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,uCAAuC,EAAM;;GAEhG,KAAK,MAAM,OAAO,GAAW,EAAQ;;;CAGvC,WAAkB;EAChB,OAAO,KAAK,MAAM,UAAU;;GAOnB,KAAb,MAA6D;CAC3D,YACE,GACA,GACA,GACA;EADiB,AAFA,KAAA,QAAA,GACA,KAAA,SAAA,GACA,KAAA,uBAAA;;CAGnB,aAAmC;EACjC,OAAO,KAAK,MAAM,YAAY;;CAGhC,MAAa,GAA0B;EACrC,IAAM,IAAM,GAAW,EAAK;EAE1B,QAAK,WAAA,8BAA0C,MAC9C,KAAO,GAAe,KAAK,QAAQ,EAAI,IACxC,CAAC,GAAc,EAAK,WAAW,GAIjC;UAAQ,EAAK,WAA0B;GACvC,IAAI;;IACF,MAAA,IACE,KAAK,OAAO,UAAA,SAAA,IAAA,EAAO,eAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAa;KAC9B,YAAY,EAAK;KACjB,MAAM,EAAK;KACX,QAAQ;KACT,CAAC,MAAK,IAEP;YAEK,GAAO;;IACd,AAAI,KAAK,OAAO,WAAO,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,uCAAuC,EAAM;;GAE/F,KAAK,MAAM,MAAM,EAAK;;;CAGxB,QAAe,GAAY,GAA8B;;EACvD,IAAM,KAAA,IAAsB,EAA4C,eAAA,OAAc,EAAE,GAAhB,GAClE,IAAuC,EAAE;EAC/C,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,KAAK,sBAAsB,CAAC,EACpE,AAAI,EAAmB,OAAS,KAAA,KAAa,MAAU,KAAA,MAAW,EAAyB,KAAO;EAGpG,AADA,EAAK,cAAc,EAAyB,EAC5C,KAAK,MAAM,QAAQ,GAAM,EAAc;;CAGzC,WAAiC;EAC/B,OAAO,KAAK,MAAM,UAAU;;GAI1B,MAAgB,MAAA,EAAA,EAAA,EAAA,EAEf,EAAA,EAAA,EAAA,EAAA,EACH,YAAY,EAAO,WAChB,QAAO,MAAS,EAAM,WAAW,OAA2B,GAAK,CACjE,KAAI,MAAS;CACZ,IAAA,IAA6D,EAAM,YAA7D,GAAG,IAAwB,MAAA,GAAa,IAAA,GAAA,GAAA,CAAA,EAAA,CAAA,IAAA,GAAA,CAAA;CAC9C,OAAA,EAAA,EAAA,EAAA,EAAY,EAAA,EAAA,EAAA,EAAA,EAAO,eAAA,CAAY;EAC/B,EAAA,CACL,EAEU,KAAb,MAAmE;CACjE,YAAmB,GAA4C;EAA3B,KAAA,QAAA;;CAEpC,OAAc,GAA0B,GAA6D;EACnG,IAAM,IAAA,EAAA,EAAA,EAAA,EACD,EAAA,EAAA,EAAA,EAAA,EACH,cAAc,EAAQ,aACnB,KAAI,MAAA,EAAA,EAAA,EAAA,EACA,EAAA,EAAA,EAAA,EAAA,EACH,SAAS,EAAY,QAAQ,IAAI,GAAa,CAAC,QAAO,MAAU,EAAO,WAAW,SAAS,EAAE,EAAA,CAC9F,CAAE,CACF,QAAO,MAAe,EAAY,QAAQ,SAAS,EAAE,EAAA,CACzD;EACD,IAAI,EAAS,aAAa,WAAW,GAAG;GACtC,EAAe,EAAE,MAAM,EAAiB,SAAS,CAAC;GAClD;;EAEF,KAAK,MAAM,OAAO,GAAU,EAAe;;CAG7C,aAAoB;EAClB,OAAO,KAAK,MAAM,YAAY;;CAGhC,6BAAoC,GAAgC;;EAClE,QAAA,KAAA,KAAA,IAAO,KAAK,OAAM,iCAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAA+B,EAAe,KAAA,OAAI,EAAuB,aAA3B;;CAGlE,WAAkB;EAChB,OAAO,KAAK,MAAM,UAAU;;GCrI1B,KAAgC,IAAI,IAAY,CAAC,GAAG,OAAO,OAAO,EAAe,EAAE,EAAsB,CAAC,EAQnG,KAAb,MAA+B;CAoL7B,YAAmB,IAAgC,EAAE,EAAE,GAAwB;EAE7E,QArLF,iBAAyG,EAAE,CAAA,UAC3G,cAA0C,EAAE,CAAA,UAC5C,sBAAyD,EAAE,CAAA,UAC3D,sBACE,GACA,GACA,GACA,MACG;GACH,IAAM,IAAW,EAAQ,GAAG,GAAG;GAE/B,IADI,KAAY,EAAS,YAAY,KAAA,MAAW,EAAS,UAAU,KAAK,IAAI,EAAS,WAAW,EAAU,GACtG,GAAQ;IACV,IAAM,IAAoC,EAAE;IAC5C,KAAK,IAAM,KAAO,GAAM;KACtB,IAAM,IAAQ,KAAK,WAAW;KAC9B,AAAI,MAAU,KAAA,MAAW,EAAsB,KAAO;;IAExD,EAAQ,KAAK;KAAE,YAAY;KAAuB,WAAW;KAAW,CAAC;;GAE3E,AAAI,EAAQ,SAAS,OAAK,EAAQ,OAAO,GAAG,EAAQ,SAAS,IAAI;cAEnE,kBAA0G,EAAE,CAAA,UAC5G,6CAA6C,IAAI,KAAiD,CAAA,UAClG,yBAAyC,GAAa,MAA8B;GAClF,IAAI,MAAU,KAAA,GAAW;IACvB,OAAO,KAAK,WAAW;IACvB;;GAEF,KAAK,WAAW,KAAO;cAEzB,QAAgC,EAAE,CAAA,UAClC,QAAgC,EAAE,CAAA,UAClC,qCAAqC,IAAI,KAAa,CAAA,UACtD,uCAAuC,IAAI,KAAsC,CAAA,UACjF,wCAAwC,IAAI,KAAyC,CAAA,UAErF,eAAuG,EAAE,CAAA,UAEzG,2CAA2C,IAAI,KAAa,CAAA,UAE5D,wBAA8B,MAA6B;GACzD,IAAI,KAAK,qBAAqB,SAAS,GAAG;IACxC,KAAK,mBAAmB,KAAK,EAAQ;IACrC;;GAEF,KAAK,qBAAqB,SAAQ,MAAY,EAAS,EAAQ,CAAC;cAGlE,uBAAA,EAAA,EAAA,EAA+C,KAAK,WAAY,CAAA,UAEhE,oBAA0B,MAAkC;GAC1D,IAAI,CAAC,OAAO,SAAS,EAAU,EAAE,OAAO,KAAK,eAAe;GAC5D,IAAM,IAAS,KAAK,eAAe,EAC7B,KACJ,GACA,MACG;IACH,KAAK,IAAM,KAAO,GAAM,OAAO,EAAO;IACtC,IAAM,IAAU,CAAC,GAAG,EAAQ,CACzB,SAAS,CACT,MAAK,MAAS,EAAM,aAAa,MAAc,EAAM,YAAY,KAAA,KAAa,IAAY,EAAM,SAAS;IAC5G,AAAI,KAAS,OAAO,OAAO,GAAQ,EAAQ,WAAW;;GASxD,OAPA,EAAa,KAAK,gBAAgB;IAChC,EAAe;IACf,EAAe;IACf;IACD,CAAC,EACF,EAAa,KAAK,aAAa,KAAK,wBAAwB,EAC5D,EAAa,KAAK,eAAe,CAAC,EAAe,SAAS,CAAC,EACpD;cAGT,iBAAA,EAAA,EAAA,EAA8C,KAAK,KAAM,CAAA,UAEzD,iBAAA,EAAA,EAAA,EAA8C,KAAK,KAAM,CAAA,UAEzD,yBAA+B,OAAO,KAAK,WAAW,EAAe,cAAe,SAAA,UAEpF,sBAA4B,OAAO,KAAK,WAAW,EAAe,WAAY,SAAA,UAE9E,2BAAiC,MAAqC;GACpE,KAAK,0BAA0B,SAAQ,MAAY,EAAS,EAAM,CAAC;cAGrE,uBAA6B,OAC3B,KAAK,0BAA0B,IAAI,EAAS,QAC/B,KAAK,0BAA0B,OAAO,EAAS,YAG9D,iBAAuB,OACrB,KAAK,oBAAoB,IAAI,EAAS,EAClC,KAAK,KAAK,MAAI,EAAS,KAAK,SAAS,CAAC,QAC7B,KAAK,oBAAoB,OAAO,EAAS,YAGxD,kBAAwB,MAAiD;GAEvE,KADA,KAAK,qBAAqB,IAAI,EAAS,EAChC,KAAK,mBAAmB,SAC7B,EAAS,KAAK,mBAAmB,OAAO,CAAE;GAE5C,aAAa,KAAK,qBAAqB,OAAO,EAAS;cAGzD,kBAAwB,GAAwB,IAAY,KAAK,KAAK,KAAK;GACzE,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAW,EAAE;IACrD,IAAI,MAAU,KAAA,GAAW;KACvB,OAAO,KAAK,WAAW;KACvB;;IAEF,KAAK,WAAW,KAAO;;GAUzB,IARI,EAAe,aAAa,KAC9B,KAAK,kBACH,KAAK,gBACL;IAAC,EAAe;IAAW,EAAe;IAAkB;IAAsB,EAClF,GACA,OAAO,KAAK,WAAW,EAAe,cAAe,SACtD,EAEC,EAAe,YAAY,GAAY;IACzC,IAAM,IAAW,KAAK,WAAW,EAAe;IAChD,KAAK,kBACH,KAAK,eACL,CAAC,EAAe,SAAS,EACzB,GACA,MAAM,QAAQ,EAAS,GAAG,EAAS,SAAS,IAAI,OAAO,KAAa,SACrE;;cAIL,YAAkB,MAA0B;GAM1C,IALA,KAAK,OAAA,EAAA,EAAA,EAAA,EACA,KAAK,KAAA,EACL,EACJ,EAEG,QAAQ,GAAM;IAChB,IAAI,EAAK,OAAO,KAAA,GAAW;KACzB,OAAO,KAAK,WAAW,EAAe;KACtC;;IAEF,KAAK,WAAW,EAAe,UAAU,EAAK;;cAIlD,YAAkB,GAAuB,MAAA,OAAA,IAAY,EAAK,cAAA,OAAa,KAAK,KAAK,GAAvB,IAAuB,KAAK;;GAEpF,AADA,KAAK,kBAAkB,SAAQ,MAAO,OAAO,KAAK,WAAW,GAAK,EAClE,KAAK,kBAAkB,OAAO;GAC9B,IAAM,IAA6B,EAAE;GACrC,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,SAAA,IAAQ,EAAK,eAAA,OAAc,EAAE,GAAhB,EAAiB,EAC1D,MAAU,KAAA,KAAa,GAA8B,IAAI,EAAI,KACjE,EAAe,KAAO,GACtB,KAAK,WAAW,KAAO,GACvB,KAAK,kBAAkB,IAAI,EAAI,EAC/B,KAAK,wBAAwB,IAAI,EAAI;GAqBvC,AAnBA,KAAK,OAAA,EAAA,EAAA,EAAA,EAAY,EAAA,EAAA,EAAA,EAAA,EAAM,YAAY,GAAA,CAAgB,EACnD,KAAK,qBAAqB,EAAe,QAAQ,EAAK,GAAG,EACzD,KAAK,qBAAqB,EAAe,SAAS,EAAK,IAAI,EAC3D,KAAK,qBAAqB,EAAe,kBAAkB,EAAK,aAAa,EAC7E,KAAK,qBAAqB,EAAe,iBAAiB,EAAK,YAAY,EAC3E,KAAK,qBAAqB,EAAe,UAAU,EAAK,KAAK,EAC7D;IACE,EAAe;IACf,EAAe;IACf,EAAe;IACf,EAAe;IACf,EAAe;IAChB,CAAC,SAAQ,MAAO,KAAK,wBAAwB,IAAI,EAAI,CAAC,EACvD,KAAK,kBACH,KAAK,aACL,KAAK,yBACL,GACA,OAAO,EAAK,MAAO,YAAY,EAAK,GAAG,SAAS,EACjD,EACD,KAAK,oBAAoB,SAAQ,MAAY,EAAS,KAAK,SAAS,CAAC,CAAC;MAItE,KAAK,cAAc,EAAkB,EACjC,KACF,KAAK,QAAQ,EAAK;;GCrOX,KAAkB,MACzB,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,aACzD,OAAO,YAAY,GAErB,GAAG,EAAO,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,ICR1D,IAAkB;CAC7B,OAAO;CACP,OAAO;CACP,SAAS;CACT,SAAS;CACT,SAAS;CACV,EAEY,IAAgB;CAC3B,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,OAAO;CACP,UAAU;CACV,UAAU;CACV,OAAO;CACP,OAAO;CACR,EAmCY,MAAqB,MAC5B,IAAW,MAAY,WACvB,IAAW,MAAY,cACvB,IAAW,MAAa,aACrB,OAGI,MAAkB,MAAuB,GAAG,KAAK,MAAM,IAAa,IAAI,CAAC,KAEzE,MAAiB,MACxB,MAAe,OAAO,MAAe,MAAY,EAAgB,UACjE,OAAO,KAAe,YAAY,KAAc,MAAY,EAAgB,QACzE,EAAgB,SAGZ,MAAoB,MAC3B,MAAe,OAAO,MAAe,MAAY,oBACjD,OAAO,KAAe,YAAY,IAAa,MAAY,SACxD,KAAc,MAAM,aAAa,YAG7B,MAAkB,MAC7B,MAAW,SAAS,EAAgB,UAAU,EAAgB,SAEnD,MAAgB,GAAyB,MAAwC;CAC5F,IAAI,CAAC,GACH,OAAO,EAAE;CAEX,IAAI;EACF,IAAM,IAAS,IAAI,IAAI,GAAK,OAAO,WAAa,MAAc,qBAAqB,SAAS,KAAK,EAC3F,IAAiB,KAAA,OAAgB,EAAO,WAAvB;EACvB,OAAO;GACL,QAAQ,EAAO;GACf,OAAO,GAAG,EAAO,OAAO;GACxB,cAAc;GACf;aACK;EACN,OAAO;GACL,OAAO;GACP,cAAc,KAAA,OAAgB,IAAhB;GACf;;GAIQ,KAA0B,EACrC,aACA,eAAY,QACZ,eACA,YAAS,EAAgB,SACzB,gBACA,aACA,gBACA,gBACA,oBAAA,EAAA,EAAA,EAAA,EAAA,EAAA;EAEC,EAAe,WAAW;EAC1B,EAAe,cAAc;EAC7B,EAAe,SAAS;EACxB,EAAe,YAAY;GACxB,MAAa,KAAA,IAA+E,EAAE,GAArE,GAAG,EAAe,iBAAiB,GAAkB,EAAS,EAAE,CAAK,EAC9F,MAAe,KAAA,IAA0D,EAAE,GAAhD,GAAG,EAAe,aAAa,GAAY,CAAK,EAC3E,MAAgB,KAAA,IAA4D,EAAE,GAAlD,GAAG,EAAe,cAAc,GAAa,CAAK,EAC9E,MAAgB,KAAA,IAA4D,EAAE,GAAlD,GAAG,EAAe,cAAc,GAAa,CAAK,EAC9E,MAAe,KAAA,IAA0D,EAAE,GAAhD,GAAG,EAAe,aAAa,GAAY,CAC3E,EAEY,MAAkC,EAC7C,aACA,mBAAgB,SAChB,WACA,kBAAe,IACf,mBACA,iBACA,QACA,yBACsC;;CACtC,IAAM,IAAS,GAAa,GAAK,EAAgB,EAC3C,IAAY,MAAkB,WAAW,MAAkB,OAC3D,IAAS,IACX,EAAgB,QAChB,OAAO,KAAmB,WACxB,GAAc,EAAe,GAC7B,EAAgB,SAChB,IAAA,KAAA,OAAA,KAAA,IAAmB,EAAQ,aAAa;CAC9C,OAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACK,EAAuB;EACxB;EACA,WAAW,IACP,YACA,OAAO,KAAmB,WACxB,GAAiB,EAAe,GAChC;EACN,YAAY,IAAY,SAAS;EACjC;EACA,aAAa;EACb,UAAU,EAAc;EACxB,cAAA,IAAa,EAAO,UAAA,OAAS,IAAT;EACpB,aAAa,IAAY,IAAiB;EAC3C,CAAC,CAAA,EACE,EAAO,SAAS,GAAG,EAAe,eAAe,EAAO,QAAQ,GAAG,EAAE,CAAA,EACrE,EAAO,eAAe,GAAG,EAAe,qBAAqB,EAAO,cAAc,GAAG,EAAE,CAAA,EAAA,EAAA,EAAA;EAC3F,iBAAiB;EACjB,gBAAgB;IACZ,MAAa,KAAA,IAAgD,EAAE,GAAtC,EAAE,qBAAqB,GAAU,CAAK,EAC/D,IACA;EACE,uBAAuB;EACvB,mBAAmB;EACpB,GACD,EAAE,CAAA,EACF,OAAO,KAAiB,WAAW,EAAE,iBAAiB,GAAc,GAAG,EAAE,CAAA,EACzE,OAAO,KAAmB,WAAA,EAAA;EAExB,6BAA6B;EAC7B,wBAAwB;IACpB,IAAiB,IAAI,GAAG,EAAe,cAAc,GAAe,EAAe,EAAE,GAAG,EAAE,CAC/F,GACD,EAAE,CACP;GCpKG,KAA0B,KAC1B,KAAmB,KACnB,KAAuB,KACvB,KAA2B,KAC3B,KAA8B,KAC9B,KAAgC,uBAChC,KAAkB,KAClB,KAA0B,IA8C1B,KAAiB,MAAkB,EAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,GAAgB,EAE9F,KAAoB,MAAqB;;CAC7C,IAAI,EAAQ,eACV,OAAO,EAAQ;CAEjB,IAAM,KAAA,IAAO,EAAQ,gBAAA,OAAA,KAAA,IAAA,EAAA,KAAA,EAAe;CACpC,OAAO,OAAO,aAAe,OAAe,aAAgB,aAAa,EAAK,OAAO;GAGjF,MAA4B,GAAkB,MAAsB;CACxE,IAAI,IAA0B;CAC9B,OAAO,IAAS;EACd,IAAM,IAAQ,EAAQ,aAAa,EAAU;EAC7C,IAAI,GACF,OAAO,EAAc,EAAM;EAE7B,IAAU,EAAiB,EAAQ;;GAKjC,MAAkB,MACrB;;UAAwB,oBAAoB,KAAK,GAAA,IAAc,EAAQ,gBAAA,OAAe,KAAf,EAAkB;GAEtF,MAAmB,MAAqB;;CAC5C,IAAM,IAAc,GAId,KAAA,IAAQ,EAAY,WAAA,OAAA,KAAA,IAAA,EAAS;CACnC,IAAI,GAAO;EACT,IAAM,IAAY,GAAe,EAAM;EACvC,IAAI,GAAW,OAAO;;CAGxB,IAAM,IAAU,EAAQ,QAAQ,aAAa,EACvC,KAAA,IAAY,EAAQ,aAAa,OAAO,KAAA,OAAA,KAAA,IAAA,EAAE,aAAa;CAC7D,IAAI,MAAY,WAAW;EAAC;EAAU;EAAS;EAAS,CAAC,SAAS,KAAA,OAAa,KAAb,EAAgB,EAAE;;EAClF,IAAM,IAAQ,GAAA,IAAc,EAAY,UAAA,OAAS,KAAT,EAAY;EACpD,IAAI,GAAO,OAAO;;CAGpB,IAAI,MAAY,YAAY,MAAY,WAAW,EAAQ,aAAa,OAAO,KAAK,UAAU;EAC5F,IAAM,IAAO,GAAe,EAAQ;EACpC,IAAI,GAAM,OAAO;;CAGnB,KAAK,IAAM,KAAa;EAAC;EAAc;EAAO;EAAQ;EAAS;EAAc,EAAE;;EAC7E,IAAM,IAAQ,GAAA,IAAc,EAAQ,aAAa,EAAU,KAAA,OAAI,KAAJ,EAAO;EAClE,IAAI,GAAO,OAAO;;GAMT,MACX,GACA,IAAsB,IACtB,IAAc,OACH;CACX,IAAM,IAAe,GAAyB,GAAQ,EAAoB;CAC1E,IAAI,GACF,OAAO;CAET,IAAI,CAAC,GAAa,OAAO,EAAO,QAAQ,aAAa;CAErD,IAAI,IAA0B,GAC1B,IAAc;CAClB,OAAO,KAAW,KAAe,KAAyB;EACxD,IAAM,IAAU,EAAQ,QAAQ,aAAa;EAC7C,IAAI,MAAY,UAAU,MAAY,UAAU,MAAY,QAAQ;EACpE,IAAM,IAAe,GAAgB,EAAQ;EAC7C,IAAI,GAAc,OAAO;EACzB,IAAI,MAAY,QAAQ;EAExB,AADA,IAAU,EAAiB,EAAQ,EACnC,KAAe;;CAKjB,KAFA,IAAU,GACV,IAAc,GACP,KAAW,KAAe,KAAyB;EACxD,IAAM,IAAU,EAAQ,QAAQ,aAAa;EAC7C,IAAI,MAAY,UAAU,MAAY,UAAU,MAAY,QAAQ;EACpE,IAAM,IAAO,GAAe,EAAQ;EACpC,IAAI,GAAM,OAAO;EACjB,IAAI,MAAY,QAAQ;EAExB,AADA,IAAU,EAAiB,EAAQ,EACnC,KAAe;;CAGjB,OAAO,EAAO,QAAQ,aAAa;GAG/B,MAAc,MAAiB;;CACnC,IAAM,KAAA,IAAiB,EAAM,iBAAA,OAAA,KAAA,IAAA,EAAA,KAAA,EAAgB,CAAC;CAE9C,OADI,aAA0B,UAAgB,IACvC,EAAM,kBAAkB,UAAU,EAAM,SAAS,KAAA;GAGpD,MAAe,MAAiB;CACpC,IAAM,IAAY,EAAM;CAGxB,OAFI,CAAC,OAAO,SAAS,EAAU,IAAI,KAAa,IAAU,KAAK,KAAK,GAChE,IAAY,eAA0B,IACnC,OAAO,cAAgB,MAAc,KAAK,KAAK,GAAG,YAAY,aAAa;GAG9E,MAAkB,GAA2B,MACjD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,IAAQ,IAAI,IAAQ,GAEhE,MAAuB,MAAoB;CACtD,IAAI,IAA0B;CAC9B,IAAI,EAAQ,QAAQ,aAAa,KAAK,WAAW,EAAQ,aAAa,MAAM,EAAE;;EAC5E,IAAU,SAAS,gBAAA,IAAe,EAAQ,aAAa,MAAM,KAAA,OAAI,KAAJ,EAAO;;CAGtE,OAAO,IAAS;EACd,IAAM,IAAU,EAAQ,QAAQ,aAAa;EAS7C,IAPE,EAAQ,aAAa,kBAAkB,IACvC,MAAY,YACZ,MAAY,YACZ,MAAY,cAIV,MAAY,OAAO,EAAQ,aAAa,OAAO,EAAE,OAAO;EAC5D,IAAI,MAAY,SAAS;;GACvB,OAAO,CAAC;IAAC;IAAU;IAAY;IAAS;IAAS;IAAS;IAAS,CAAC,UAAA,KAAA,IAClE,EAAQ,aAAa,OAAO,KAAA,OAAA,KAAA,IAAA,EAAE,aAAa,KAAA,OAAI,SAAJ,EAC5C;;EAEH,IAAU,EAAiB,EAAQ;;CAErC,OAAO;GAGI,MAAsB,IAAmC,EAAE,KAAiB;CACvF,IAAM,IAAuC,EAAE,EACzC,oBAAgB,IAAI,KAA2B,EAC/C,oBAAiB,IAAI,KAAmB,EACxC,IAAsB,EAAQ,uBAAuB,IACrD,IAAc,GAAe,EAAQ,aAAa,GAA4B,EAChF,IAA8B,EAAE,EAChC,GAEE,UAA+B;EACnC,KAAA,QAAA,EAAgB,qBAAqB,GAClC,EAAe,WAAW,EAAc,OAAO,CAAC,GAAG,EAAc,MAAM,CAAC,GAAG,KAAA,GAC7E,CAAC;IAGE,KAAgB,MACpB,CAAC,EAAO,eACR,CAAC,EAAO,aAAa,SACrB,CAAC,EAAO,aAAa,UACrB,CAAC,EAAO,aAAa,aACrB,CAAC,GAAoB,EAAO,OAAO,EAE/B,KAAgB,MAAyB;;EAC7C,IAAI,CAAC,EAAe,OAAO,EAAO,IAAI,CAAC,KAAkB,CAAC,EAAO,aAAa,EAAO,iBAAiB;EAGtG,AAFA,aAAa,EAAO,YAAY,EAC5B,EAAO,OAAO,SAAO,EAAO,aAAa,IAAI,cAAc,IAC/D,IAAK,EAAO,gBAAA,OAAe,CAAC,EAAO,GAAvB,GAAyB,KAAK,EAAa,IAAE,EAAO,aAAa,IAAI,aAAa;EAC9F,IAAM,IAAmB;GAAC;GAAc;GAAc;GAAc,CAAC,QAAO,MAC1E,EAAO,aAAa,IAAI,EAA8B,CACvD,EACK,EAAE,YAAS,sBAAmB,EAAO,WACrC,IAAc,KAAK,IAAI,GAAG,IAAU,EAAO,UAAU,EACrD,IAAa,EAAO,OAAO,QAAQ,KAAK,EAAiB,SAAS,GAClE,IAAO,EAAe,UAC1B,gBAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAEK,EAAe,OAAO,MAAM,mBAAmB,CAAA,EAC/C,EAAuB;GACxB,UAAU,IAAiB,IAAc,KAAA;GACzC,WAAW,EAAO,OAAO,QAAQ,OAAO,EAAiB,SAAS,gBAAgB;GAClF,YAAY;GACZ,QAAQ,IAAa,EAAgB,UAAU,EAAgB;GAC/D,aAAa;GACb,UAAU,EAAc;GACxB,aAAa,EAAO;GACpB,aAAa,IAAiB,IAAc,KAAA;GAC5C,YAAY;GACb,CAAC,CAAA,EACC,EAAO,kBAAA,EAAA,EAAA,EAAA;IACT,EAAe,WAAW,EAAO;IACjC,EAAe,aAAa;IAC5B,EAAe,aAAa,YAAY,EAAO;IAC/C,EAAe,mBAAmB,EAAO;IACzC,EAAe,kBAAkB,EAAO;IACxC,EAAe,sBAAsB,EAAO,OAAO;IACnD,EAAe,mBAAmB,EAAO,OAAO;IAChD,EAAe,sBAAsB,EAAO,OAAO;IACnD,EAAe,yBAAyB,EAAiB;KACtD,EAAiB,SAAS,GAAG,EAAe,wBAAwB,GAAkB,GAAG,EAAE,CAAA,EAC3F,IAAiB,GAAG,EAAe,oBAAoB,GAAa,GAAG,EAAE,CAC9E,EACD,EAAE,WAAW,EAAO,WAAW,CAChC;EAED,AADA,EAAK,cAAA,EAAA,EAAA,EAAA,EAAmB,EAAO,kBAAA,EAAA,EAAA,EAAA,GAAoB,EAAe,WAAW,EAAO,IAAA,CAAI,CAAC,EACzF,EAAK,IAAI,IAAiB,IAAU,EAAO,UAAU;IAGjD,KAAgB,GAAsB,GAAiB,MAA4B;EACvF,IAAI,CAAC,EAAc,OAAO,EAAO,GAAG,IAAI,CAAC,GAAgB;EAMzD,AALA,aAAa,EAAO,gBAAgB,EACpC,aAAa,EAAO,WAAW,EAC/B,aAAa,EAAO,iBAAiB,EACrC,GAAwB,EACxB,EAAO,YAAY;GAAE;GAAS;GAAgB,EAC9C,EAAe,IAAI,EAAO;EAC1B,IAAM,IAAc,KAAK,IAAI,GAAG,EAAO,YAAY,KAAuB,KAAK,KAAK,CAAC;EACrF,EAAO,cAAc,iBAAiB,EAAa,EAAO,EAAE,EAAY;IAGpE,UAA4B;EAChC,CAAC,GAAG,EAAe,CAAC,QAAQ,EAAa;IAGrC,KAAqB,MAAyB;EAElD,IADA,aAAa,EAAO,WAAW,EAC3B,CAAC,EAAO,eAAe,EAAO,gBAAgB,MAAM;EACxD,IAAM,IAAQ,KAAK,IAAI,GAAG,EAAO,mBAAmB,KAAmB,KAAK,KAAK,CAAC;EAClF,EAAO,aAAa,iBAAiB,EAAa,GAAQ,EAAO,kBAAkB,GAAK,EAAE,EAAM;IAG5F,KAAgB,GAAsB,MAAiB;EAG3D,AAFA,EAAO,cAAc,IACrB,EAAO,mBAAmB,KAAK,IAAI,EAAO,kBAAkB,EAAK,EACjE,EAAkB,EAAO;IAGrB,KAAoB,GAAsB,MAC9C,KAAQ,EAAO,aAAa,KAAQ,EAAO,YAAY,GAEnD,KAAc,MAA6B;EAC/C,KAAK,IAAM,KAAU,EAAc,QAAQ,EAAE;GAC3C,IAAI,EAAM,SAAS,iBAAiB;;IAElC,IADI,EAAM,OAAO,EAAA,KAAA,SAAA,IAAe,EAAgB,OAAO,SAAA,OAAA,KAAA,IAAA,EAAM,sBAAsB,EAAM,IAAI,IACzF,CAAC,EAAiB,GAAQ,EAAM,KAAK,EAAE;IAE3C,AADA,EAAO,gBAAgB,IAAI,EAAM,GAAG,EACpC,EAAa,GAAQ,EAAM,KAAK;IAChC;;GAEF,IAAI,EAAM,SAAS,eAAe;IAChC,IAAI,CAAC,EAAO,gBAAgB,OAAO,EAAM,GAAG,EAAE;IAE9C,AADA,EAAO,OAAO,YAAY,GAC1B,EAAa,GAAQ,EAAM,KAAK;IAChC;;GAEF,IAAI,EAAM,SAAS,eAAe,EAAM,SAAS,YAAY;;IAQ3D,IANE,EAAM,SAAS,cACf,EAAM,OACN,EAAA,KAAA,SAAA,IAAe,EAAgB,OAAO,SAAA,OAAA,KAAA,IAAA,EAAM,sBAAsB,EAAM,IAAI,IAK5E,EAAM,YAAY,EAAO,YAAY,MACpC,EAAM,SAAS,aAAa,EAAM,YAAY,EAAO,YAAY,EAAM,UAAU,EAAO,YAEzF;IAGF,AADA,EAAO,OAAO,EAAM,SAAS,cAAc,aAAa,eAAe,GACvE,EAAa,GAAQ,EAAM,QAAQ;IACnC;;GAEF,IAAI,EAAM,SAAS,kBAAkB,EAAM,SAAS,eAAe;IACjE,AAAI,EAAiB,GAAQ,EAAM,KAAK,IAAE,EAAa,GAAQ,EAAM,KAAK;IAC1E;;GAEE,EAAM,SAAS,WACd,EAAiB,GAAQ,EAAM,KAAK,KACzC,EAAO,OAAO,SAAS,GACvB,EAAa,GAAQ,EAAM,KAAK;;IAI9B,KAAuB,IAAU,KAAK,KAAK,KAAK;EACpD,CAAC,GAAG,EAAc,QAAQ,CAAC,CAAC,SAAQ,MAAU,EAAa,GAAQ,GAAS,EAAO,YAAY,CAAC;IAG5F,KAAiB,GAAsB,MAAiB;EAC5D,IAAM,IAAI,aAAa,KAAS,OAAO,EAAM,WAAY,WAAW,EAAM,UAAU,GAC9E,IAAI,aAAa,KAAS,OAAO,EAAM,WAAY,WAAW,EAAM,UAAU;EACpF,IAAe,EAAa,QAAO,MAAS,EAAO,YAAY,EAAM,OAAO,aAAa,GAAqB;EAC9G,IAAM,IAAY,EAAa,GAAG,GAAG,EAC/B,IAAsB,EAAa,MACvC,MAAS,EAAM,OAAO,aAAa,UAAU,EAAM,OAAO,aAAa,UACxE,EACK,KAAA,KAAA,OAAA,KAAA,IACJ,EAAW,YAAW,EAAO,UAAU,KAAK,MAAM,EAAU,IAAI,GAAG,EAAU,IAAI,EAAE,IAAI;EAGzF,KAFI,KAAwB,KAAa,CAAC,OAAY,IAAe,EAAE,GACvE,EAAa,KAAK;GAAE;GAAQ,QAAQ,EAAO;GAAQ;GAAG;GAAG,CAAC,EAExD,EAAa,UAAU,KACvB,EAAO,YAAY,EAAa,EAAa,SAAS,GAAG,OAAO,aAAa,IAC7E;GACA,IAAM,IAAc,EAAa,MAAM,GAAG,CAAC,KAAI,MAAS,EAAM,OAAO;GAMrE,AALA,EAAY,MAAM,GAAG,GAAG,CAAC,SAAQ,MAAU;IACzC,EAAO,kBAAkB;KACzB,EACF,EAAO,aAAa,IAAI,aAAa,EACrC,EAAO,cAAc,GACrB,IAAe,EAAE;;;CAIrB,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACvD;GAEF,IAAiB;GAEjB,IAAM,IAA2C;IAAE,SAAS;IAAM,SAAS;IAAM,EAE3E,KAAe,MAAiB;IACpC,IAAI,YAAY,KAAS,OAAO,EAAM,UAAW,YAAY,EAAM,WAAW,GAAG;IACjF,IAAM,IAAU,GAAW,EAAM;IACjC,IAAI,CAAC,GAAS;IACd,IAAM,IAAY,GAAY,EAAM,EAC9B,IAAK,EAAe,SAAS,EAC7B,IAAuB;KAC3B,QAAQ;MAAE,OAAO;MAAG,UAAU;MAAG,UAAU;MAAG;KAC9C,8BAAc,IAAI,KAAK;KACvB,aAAa;KACb;KACA,kBAAkB;KAClB,kBAAkB,iBAAiB,EAAa,GAAQ,IAAY,GAAa,GAAK,EAAE,EAAY;KACpG,iCAAiB,IAAI,KAAK;KAC1B,mBAAmB,EAAQ,sBAAsB;KACjD;KACA,QAAQ;KACR,YAAY,GAAyB,GAAS,GAAqB,EAAQ,mBAAmB,OAAO;KACrG,WAAW,EAAQ,QAAQ,aAAa;KACxC,cAAc;MAAE,OAAO;MAAO,QAAQ;MAAO,WAAW;MAAO;KAC/D,iBAAiB,iBAAiB;MAChC,AAAK,EAAO,eAAa,EAAa,GAAQ,EAAO,WAAW,GAAM;QACrE,GAAwB;KAC5B;IAGD,AAFA,EAAc,IAAI,GAAI,EAAO,EAC7B,EAAc,GAAQ,EAAM,EAC5B,GAAwB;;GAI1B,AADA,OAAO,iBAAiB,SAAS,GAAa,EAAgB,EAC9D,EAAkB,WAAW,OAAO,oBAAoB,SAAS,GAAa,EAAgB,CAAC;GAE/F,IAAM,IAAsB,EAAQ,WAAW,EAAW,EACpD,IAAqB,EAAQ,oBAAmB,MAAS;IACzD,EAAM,SAAS,cACnB,EAAoB,EAAM,UAAU,EACpC,GAAqB;KACrB;GACF,EAAkB,KAAK,GAAqB,EAAmB;GAE/D,IAAM,UAAuB;IAC3B,AAAI,SAAS,oBAAoB,aAC/B,GAAqB,EACrB,GAAqB;MAGnB,UAAyB;IAE7B,AADA,GAAqB,EACrB,GAAqB;;GAIvB,AAFA,OAAO,iBAAiB,YAAY,EAAiB,EACrD,SAAS,iBAAiB,oBAAoB,EAAe,EAC7D,EAAkB,WACV,OAAO,oBAAoB,YAAY,EAAiB,QACxD,SAAS,oBAAoB,oBAAoB,EAAe,CACvE;GAED,IAAM,KAAoB,MAA6C;IAMrE,IALoB,IAAI;KACtB,GAAG,EAAc,QAAQ;KACzB,GAAG;KACH,GAAG,EAAa,KAAI,MAAS,EAAM,OAAO;KAC3C,CACD,CAAQ,SAAQ,MAAU;KAExB,IADA,EAAO,aAAa,KAAQ,IACxB,MAAS,WAAW,EAAO,aAAa,OAAO,aAAa,EAAE;;MAIhE,CAHA,IAAA,EAAO,gBAAA,QAAA,EAAa,MAAM,GAAG,GAAG,CAAC,SAAQ,MAAU;OACjD,EAAO,kBAAkB;QACzB,EACF,EAAO,cAAc,KAAA;;MAEvB;MAEE,UAAgB,EAAiB,QAAQ,EACzC,UAAiB,EAAiB,SAAS,EAC3C,WAA0B,EAAiB,YAAY;GAI7D,AAHA,OAAO,iBAAiB,SAAS,GAAS,EAAgB,EAC1D,OAAO,iBAAiB,UAAU,GAAU,EAAgB,EAC5D,SAAS,iBAAiB,mBAAmB,IAAmB,EAAgB,EAChF,EAAkB,WACV,OAAO,oBAAoB,SAAS,GAAS,EAAgB,QAC7D,OAAO,oBAAoB,UAAU,GAAU,EAAgB,QAC/D,SAAS,oBAAoB,mBAAmB,IAAmB,EAAgB,CAC1F;;EAEH,WAAW;GAGT,KAFA,GAAqB,EACrB,GAAqB,EACd,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;GAG7B,AADA,IAAe,EAAE,EACjB,IAAiB,KAAA;;EAEpB;GC3eU,KAAwC,uCAE/C,MAAsB,MAA6B;CACvD,IAAM,IAAS,EAAS,SAAS,kBAAkB,EAAS,OAAO,gBAAgB,EAAS;CAC5F,OAAO,aAAkB,WAAW,EAAQ,EAAO,QAAQ,wCAA6C;GAM7F,WAAyC;CACpD,IAAI,GACE,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,WAAa,OAAe,OAAO,mBAAqB,QACjE,IAAmB,IAAI,kBAAiB,MAAa;IACnD,AAAI,EAAU,UAAU,CAAC,EAAU,MAAM,GAAmB,IAC1D,EAAQ,eAAe;KAAE,MAAM,KAAK,KAAK;KAAE,MAAM;KAAgB,CAAC;KAEpE,EACF,EAAiB,QAAQ,UAAU;IAAE,YAAY;IAAM,eAAe;IAAM,WAAW;IAAM,SAAS;IAAM,CAAC,GAG3G,SAAO,SAAW,QAElB,OAAO,OAAO,QAAS,YAAY;IACrC,IAAM,IAAe,OAAO,MACtB,MAAgB,GAAG,OACvB,EAAQ,eAAe;KAAE,MAAM,KAAK,KAAK;KAAE,MAAM;KAAe,CAAC,EAC1D,QAAQ,MAAM,GAAc,QAAQ,EAAK;IAGlD,AADA,OAAO,OAAO,GACd,EAAkB,WAAW;KAC3B,AAAI,OAAO,SAAS,MAAa,OAAO,OAAO;MAC/C;;;EAIN,WAAW;;GAGT,KAFA,KAAA,QAAA,EAAkB,YAAY,EAC9B,IAAmB,KAAA,GACZ,EAAkB,SAAQ,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;EAE/D;GCtCG,MAAyB,GAA6B,MAC1D,GAAQ,KAAO,EAAe,EAAQ,OAAO,KAAK,sBAAsB,EAAI,GAMjE,MACX,GACA,GACA,MACG;CACH,IAAM,oBAAkB,IAAI,KAAa,EACrC,IAAY,IACZ,IAAc,IACd,IAAmB,GACnB,GAEE,KAAY,MAA4B;EACxC,MACJ,IAAY,IACZ,aAAa,EAAgB,EAC7B,aAAa,EAAW,EACxB,GAAa,EACb,EAAS,EAAO;IAGZ,UAAoB;EACxB,aAAa,EAAW,EACpB,GAAC,KAAe,EAAgB,UACpC,IAAa,iBACL,EAAS;GAAE,SAAS;GAAkB,aAAa;GAAM,CAAC,EAChE,KAAK,IAAI,GAAG,IAAA,MAA6C,KAAK,KAAK,CAAC,CACrE;IAGG,KAAgB,MAAiB;EAGrC,AAFA,IAAc,IACd,IAAmB,KAAK,IAAI,GAAkB,EAAK,EACnD,GAAa;IAwBT,IAAc,EAAQ,YArBR,MAA6B;EAC3C,QAAM,SAAS,WAAW,EAAM,SAAS,cAC7C;OAAI,EAAM,SAAS,iBAAiB;IAClC,IAAI,EAAM,OAAO,KAAa,GAAsB,GAAS,EAAM,IAAI,EAAE;IAEzE,AADA,EAAgB,IAAI,EAAM,GAAG,EAC7B,EAAa,EAAM,KAAK;IACxB;;GAEF,IAAI,EAAM,SAAS,eAAe;IAChC,IAAI,CAAC,EAAgB,OAAO,EAAM,GAAG,EAAE;IACvC,EAAa,EAAM,KAAK;IACxB;;GAEF,IAAI,EAAM,SAAS,YAAY;IAC7B,IAAI,EAAM,UAAU,KAAa,GAAsB,GAAS,EAAM,IAAI,EAAE;IAC5E,EAAa,EAAM,QAAQ;IAC3B;;GAEF,AAAI,EAAM,QAAQ,KAAW,EAAa,EAAM,KAAK;;GAGL,EAC5C,IAAkB,iBAAiB;EACvC,AAAK,KAAa,EAAS,EAAE,aAAa,IAAO,CAAC;QAClB;CAElC,aAAa;EACP,MACJ,IAAY,IACZ,aAAa,EAAgB,EAC7B,aAAa,EAAW,EACxB,GAAa;;GC9DX,MAAoB,MACxB,OAAO,KAAU,YAAY,EAAM,SAAS,IAAI,IAAQ,KAAA,GAEpD,MAA+B,MAAuC;CAC1E,IAAM,IAAa,GAAiB,EAAc;CAClD,OAAO,IAAa,GAAG,EAAe,mBAAmB,GAAY,GAAG,EAAE;GAG/D,KAA2B,MAAA,EAAA,EAAA,EAAA,EACnC,GAA4B,EAAkB,EAAe,kBAAkB,CAAA,EAAA,EAAA,EAAA,GACjF,IAAwB,EAAkB,OAA2B,IAAA,CACvE,ECHK,KAAsB,KACtB,KAA6B,KAC7B,KAAwB,QACxB,KAAoB,IACpB,KAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAA0D;CAC9D,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACV,CAAC,IAAK,GAAI;CACX,EAEK,MAAqB,GAA2B,MACpD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,KAAS,IAAI,IAAQ,GAExE,MAAgB,GAA2B,MAC/C,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAM,CAAC,GAAG,GAEpF,MAAsB,MAAiC;CAC3D,IAAI,CAAC,GAAS,OAAO;CACrB,IAAM,IAAM,EAAQ,QAAQ,aAAa;CACzC,OAAO,EAAQ,KAAK,GAAG,EAAI,GAAG,EAAQ,OAAO;GAGzC,MAAc,GAAkB,MACpC,EAAU,MAAK,MAAY;CACzB,IAAI;EACF,OAAO,EAAQ,QAAQ,EAAS,IAAI,EAAQ,EAAQ,QAAQ,EAAS;aAC/D;EACN,OAAO;;EAET,EAEE,MAAqB,MAAqB;CAC9C,IAAI,OAAO,oBAAqB,YAAY,OAAO;CACnD,IAAM,IAAQ,iBAAiB,EAAQ;CACvC,OAAO,EAAM,YAAY,UAAU,EAAM,eAAe,YAAY,OAAO,EAAM,WAAW,EAAE,GAAG;GAG7F,MAAsB,GAAW,MAAyB;CAC9D,IAAI,OAAO,SAAS,qBAAsB,YAAY,OAAO,SAAS,kBAAkB,GAAG,EAAE;CAC7F,IAAM,IAAU,SAAS,iBAAiB,GAAG,EAAE;CAC/C,OAAO,IAAU,CAAC,EAAQ,GAAG,EAAE;GAGpB,MAAqB,GAAsB,MAAgD;CACtG,IAAM,IAAO,SAAS,cAAc,EAAa;CACjD,IAAI,CAAC,GACH,OAAO;EACL,eAAe,GAAmB,GAAmB,OAAO,aAAa,GAAG,OAAO,cAAc,EAAE,CAAC,GAAG;EACvG,YAAY,EAAc;EAC1B,cAAc;EACd,QAAQ;EACR,WAAW;EACX,OAAO;EACP,YAAY,EAAc;EAC3B;CAGH,IAAI,IAAa,GACb,IAAe,GACf,IAAgB;CACpB,KAAK,IAAM,CAAC,GAAQ,MAAW,GAAe;EAC5C,IAAM,IAAS,GAAmB,OAAO,aAAa,GAAQ,OAAO,cAAc,EAAO;EAE1F,IADI,MAAW,MAAO,MAAW,OAAK,IAAgB,GAAmB,EAAO,GAAG,GAC/E,EAAO,MAAK,MAAW,GAAW,GAAS,EAAiB,CAAC,EAAE;GACjE,KAAgB;GAChB;;EAUF,AARmB,EAAO,MACxB,MACE,MAAY,KACZ,MAAY,SAAS,QACrB,MAAY,SAAS,mBACrB,EAAK,SAAS,EAAQ,IACtB,GAAkB,EAAQ,CAEzB,KAAY,KAAc;;CAGjC,IAAM,IAAa,EAAc,SAAS;CAC1C,OAAO;EACL;EACA;EACA;EACA,QAAQ;EACR,WAAW;EACX,OAAO,IAAa,IAAa,IAAa;EAC9C;EACD;GAGU,MAA2B,IAAwC,EAAE,KAAiB;;CACjG,IAAM,IAAa,GAAkB,EAAQ,YAAY,GAAoB,EACvE,IAAoB,GAAkB,EAAQ,mBAAmB,GAA2B,EAC5F,IAAe,EAAQ,gBAAgB,IACvC,IAAY,GAAa,EAAQ,WAAW,GAAkB,EAC9D,IAAmB,CAAC,GAAG,IAA2B,IAAA,IAAI,EAAQ,oBAAA,OAAmB,EAAE,GAArB,EAAuB,EACvF,GACA,GACA,GACA,GACA,UAAwB,IACxB;CAEJ,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KAAa;GACtE,IAAM,IAAU,EAAQ,MAAM,cAAc,8BAA8B,EACxE,aAAa,qCACd,CAAC,EAEI,UAAmB;IAEvB,AADA,aAAa,EAAM,EACnB,IAAQ,KAAA;MAGJ,KAAU,GAAgB,MAA4B;IACtD,MAAiB,KAAU,MAAmB,MAClD,IAAiB,GACjB,EAAQ,IACN,GACA,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACH,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAQ,sBAAsB,CAAC,CAAA,EAAA,EAAA,EAAA;KAC1D,uBAAuB,EAAO;KAC9B,8BAA8B;MAC/B,CAAC,CACH,EACD,EACG,UAAU,wBAAA,EAAA,EAAA,EAAA,EACN,EAAuB;KACxB,WAAW;KACX,YAAY;KACZ,QAAQ,EAAgB;KACxB,aAAa,EAAO;KACpB,UAAU,EAAc;KACxB,aAAa;KACb,aAAa,EAAO;KACrB,CAAC,CAAA,EAAA,EAAA,EAAA;KACF,+BAA+B,EAAO;KACtC,4BAA4B,EAAO;KACnC,mCAAmC,EAAO;KAC1C,qCAAqC,EAAO;KAC5C,uBAAuB,EAAO;KAC9B,2BAA2B,EAAO;KAClC,8BAA8B;KAC9B,6BAA6B,EAAc;KAC3C,mCAAmC,EAAO;MAC3C,CAAC,CACD,KAAK;MAGJ,KAAoB,MAAmB;IAE3C,IADI,MAAiB,KAAU,MAAmB,KAC9C,SAAS,oBAAoB,UAAU;IAC3C,IAAI;IACJ,IAAI;KACF,IAAQ,GAAkB,GAAc,EAAiB;aAClD,GAAO;;KACd,AAAI,EAAQ,OAAO,WAAO,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,2CAA2C,EAAM;KACpG;;IAEE,CAAC,EAAM,cAAc,EAAM,QAAQ,MACvC,IAAQ,iBAAiB;KACvB,IAAI,MAAiB,KAAU,SAAS,oBAAoB,UAAU;KACtE,IAAI;KACJ,IAAI;MACF,IAAY,GAAkB,GAAc,EAAiB;cACtD,GAAO;;MACd,AAAI,EAAQ,OAAO,WAAO,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,8CAA8C,EAAM;MACvG;;KAEF,AAAI,EAAU,cAAc,EAAU,SAAS,KAAW,EAAO,GAAQ,EAAU;OAClF,EAAkB;MAGjB,KAAY,MAAmB;IACnC,GAAY,EACR,SAAS,oBAAoB,aACjC,IAAQ,iBAAiB,EAAiB,EAAO,EAAE,EAAW;;GAGhE,IAAkB,EAAQ,cAAa,MAAQ;IACzC,MAAK,OAAO,MAChB,GAAY,EACZ,KAAA,QAAA,GAAwB,EACxB,IAAuB,KAAA,GACvB,IAAe,EAAK,IACpB,IAAiB,KAAA,GACb,EAAK,KAAI;KACX,IAAM,IAAS,EAAK;KACpB,IAAuB,GAAuB,GAAS,KAAK,KAAK,QAAQ;MAEvE,AADA,IAAuB,KAAA,GACvB,EAAS,EAAO;OAChB;;KAEJ;GACF,IAAM,UAA2B;IAC/B,AAAI,SAAS,oBAAoB,WAC/B,GAAY,GACH,KAAgB,MAAmB,KAC5C,EAAS,EAAa;;GAI1B,AADA,SAAS,iBAAiB,oBAAoB,EAAmB,EACjE,UAA2B,SAAS,oBAAoB,oBAAoB,EAAmB;;EAEjG,WAAW;GAST,AARA,aAAa,EAAM,EACnB,IAAQ,KAAA,GACR,GAAiB,EACjB,KAAA,QAAA,GAAwB,EACxB,IAAuB,KAAA,GACvB,KAAA,QAAA,GAAsB,EACtB,IAAqB,KAAA,GACrB,IAAe,KAAA,GACf,IAAiB,KAAA;;EAEpB;GC/PG,KAAuB,KACvB,KAA0B,KAC1B,IAA0C,EAAE,EAE5C,MAAgB,MAAgB,EAAI,QAAQ,QAAQ,GAAG,EAEvD,MAAiB,MAAkC;CACvD,IAAM,IAAQ,EAAgB,QAAQ,EAAQ;CAE9C,AADI,KAAS,KAAG,EAAgB,OAAO,GAAO,EAAE,EAC5C,EAAQ,UAAU,KAAA,KAAW,aAAa,EAAQ,MAAM;GAGxD,KAAiB,GAA+B,MAAsC;CAC1F,IAAM,IAAW,EAAQ;CAGzB,AAFA,GAAc,EAAQ,EACtB,EAAQ,WAAW,KAAA,GACnB,KAAA,QAAA,EAAW,EAAM;GAGN,MAAuB,MAAqC;CACvE,IAAM,IAAiB,YAAY,aAAa,EAAM,WAChD,IAAY,EACf,QACC,MACE,CAAC,EAAQ,SACT,GAAa,EAAQ,IAAI,KAAK,GAAa,EAAM,KAAK,IACtD,KAAkB,EAAQ,YAAY,GACzC,CACA,MAAM,GAAM,MAAU,KAAK,IAAI,EAAK,YAAY,EAAe,GAAG,KAAK,IAAI,EAAM,YAAY,EAAe,CAAC,CAAC;CAC5G,MACL,EAAU,QAAQ,GACd,EAAU,YAAU,EAAc,GAAW,EAAM;GAG5C,MAAsB,GAAa,IAAY,KAAK,KAAK,KAAK;CACzE,IAAM,IAAgC;EAAE;EAAW;EAAK;CAGxD,OAFA,EAAgB,KAAK,EAAQ,EACzB,EAAgB,SAAS,MAAsB,EAAc,EAAgB,GAAG,EAC7E;EACL,cAAc,EAAc,EAAQ;EACpC,WAAW,MAA0D;GAEnE,IADA,EAAQ,WAAW,GACf,EAAQ,OAAO;IACjB,EAAc,GAAS,EAAQ,MAAM;IACrC;;GAEF,EAAQ,QAAQ,iBAAiB,EAAc,EAAQ,EAAE,GAAwB;;EAEpF;GCZG,KAAmB,KAAK,MACxB,KAAwD;CAC5D;CACA;CACA;CACA;CACD,EAEK,MAAkB,MACtB,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,IAAQ,IAAI,KAAK,MAAM,EAAM,GAAG,IAEnF,MACJ,GACA,GACA,MAEK,IACE,EAAS,MAAK,MACf,OAAO,KAAY,WAAiB,EAAY,aAAa,CAAC,SAAS,EAAQ,aAAa,CAAC,IACjG,EAAQ,YAAY,GACb,EAAQ,KAAK,EAAY,EAChC,GALuB,GAQrB,MAAyB,MAAqC;CAClE,IAAI;;EACF,OAAO,KAAA,IAAW,IAAI,QAAQ,EAAQ,CAAC,IAAI,eAAe,KAAA,OAAI,KAAA,IAAJ,IAAiB,KAAA;aACrE;EACN;;GAIE,KAAA,WAAA;sBAAoB,GAAoC,GAAsB;EAClF,IAAM,IAAS,EAAO,WAAW,EAC3B,IAAU,IAAI,aAAa,EAC7B,IAAO;EACX,IAAI;GACF,OAAO,EAAK,UAAU,IAAW;IAC/B,IAAM,EAAE,SAAM,aAAU,MAAM,EAAO,MAAM;IAC3C,IAAI,GAAM;KACR,KAAQ,EAAQ,QAAQ;KACxB;;IAEF,KAAQ,EAAQ,OAAO,GAAO,EAAE,QAAQ,IAAM,CAAC;;GAEjD,AAAI,EAAK,SAAS,MAChB,MAAM,EAAO,QAAQ,CAAC,YAAY,KAAA,EAAU;YAEtC;GACR,EAAO,aAAa;;EAEtB,OAAO;;iBAnBiB,GAAoC,GAAA;;;KAsBxD,KAAA,WAAA;sBAAyB,GAA8C,GAAsB;EACjG,IAAI,OAAO,KAAS,UAAU,OAAO;EACrC,IAAI,OAAO,kBAAoB,OAAe,aAAgB,iBAAiB,OAAO,EAAK,UAAU;EACrG,IAAI,OAAO,OAAS,OAAe,aAAgB,MAAM,OAAO,GAAW,EAAK,QAAQ,EAAE,EAAU;;iBAHvE,GAA8C,GAAA;;;KAOvE,KAAA,WAAA;sBACJ,GACA,GACqD;EACrD,IAAI,OAAO,UAAY,OAAe,aAAmB,SAAS;;GAChE,IAAM,KAAA,IAAc,EAAQ,QAAQ,IAAI,eAAe,KAAA,OAAI,KAAA,IAAJ,GACjD,IAAQ,EAAQ,OAAO;GAE7B,OAAO;IAAE;IAAa,MADT,EAAM,OAAO,MAAM,GAAW,EAAM,MAAM,EAAU,GAAG;IACxC;;EAE9B,OAAO;GACL,aAAa,GAAsB,EAAQ,QAAQ;GACnD,MAAM,MAAM,GAAgB,EAAQ,MAAM,EAAU;GACrD;;iBAZD,GACA,GAAA;;;KAcW,MAAiC,MAAmC;CAC/E,IAAI,OAAO,UAAY,OAAe,EAAE,aAAmB,UAAU,OAAO;CAC5E,IAAI;EACF,OAAO,EAAQ,OAAO;aAChB;EACN;;GAIE,KAAA,WAAA;sBACJ,GACA,GACqD;;EACrD,IAAI,CAAC,GAAU,OAAO,EAAE;EACxB,IAAM,KAAA,IAAc,EAAS,QAAQ,IAAI,eAAe,KAAA,OAAI,KAAA,IAAJ,GAClD,IAAQ,EAAS,OAAO;EAE9B,OAAO;GAAE;GAAa,MADT,EAAM,OAAO,MAAM,GAAW,EAAM,MAAM,EAAU,GAAG;GACxC;;iBAP5B,GACA,GAAA;;;KASI,MAAmB,MAAwB;;CAC/C,IAAM,KAAA,IAAc,EAAI,kBAAkB,eAAe,KAAA,OAAI,KAAA,IAAJ;CAOzD,OANI,EAAI,iBAAiB,MAAM,EAAI,iBAAiB,SAC3C;EAAE;EAAa,MAAM,EAAI;EAAc,GAE5C,EAAI,iBAAiB,SAChB;EAAE;EAAa,MAAM,KAAK,UAAU,EAAI,SAAS;EAAE,GAErD,EAAE,gBAAa;GAGlB,MACJ,GACA,GACA,GACA,GACA,MAC6B;CAC7B,IAAI,MAAS,KAAA,GAAW;CACxB,IAAM,IAAU,GAAe,EAAW,QAAQ,EAC5C,IAAY,EAAK,SAAS,GAC1B,IAAwC;EAC5C,MAAM,EAAK,MAAM,GAAG,IAAU,EAAE;EAChC;EACA;EACA,QAAQ,EAAM,SAAS;EACvB,YAAY,EAAM,SAAS;EAC3B;EACA,MAAM,EAAM,SAAS;EACrB,KAAK,EAAM,SAAS;EACrB,EACK,IAAW,EAAW,OAAO,EAAQ;CACvC,UAAa,KAAA,GACjB,OAAO;EACL,MAAM,EAAS,MAAM,GAAG,EAAQ;EAChC;EACA,WAAW,KAAa,EAAS,SAAS;EAC3C;GAGU,KAAA,WAAA;sBAA+B,EAC1C,eACA,kBACA,UACA,iBACA,mBACA,WACkC;;EAClC,IAAM,IAAa,EAAM,SAAS,YAC5B,IAAU,MAAe,KAAA,KAAa,MAAe,KAAK,KAAc;EAC9E,MAAA,IAAK,EAAW,YAAA,OAAW,WAAX,OAAyB,YAAY,CAAC,GAAS;EAG/D,IAAM,IADU,GAAe,EAAW,QACxB,GAAU,GACtB,KAAA,IAAsB,EAAW,wBAAA,OAAuB,KAAvB,GACnC,IAAwD,EAAE,EAC1D,IAAyD,EAAE;EAE/D,IAAI;GACF,AAAI,EAAc,SAAS,UACzB,CAAC,GAAc,KAAiB,MAAM,QAAQ,IAAI,CAChD,GAAiB,KAAA,OAAgB,EAAc,UAA9B,GAAuC,EAAU,EAClE,GAAkB,EAAc,UAAU,EAAU,CACrD,CAAC,IAEF,IAAe,EAAE,MAAM,MAAM,GAAgB,EAAc,aAAa,EAAU,EAAE,EACpF,IAAgB,GAAgB,EAAc,IAAI;cAE9C;GACN;;EAGF,IAAI,GACA;EACJ,IAAI;GAIF,AAHA,IAAU,GAAqB,EAAa,aAAa,GAAqB,GAAK,GAC/E,GAAe,GAAY,GAAO,WAAW,EAAa,aAAa,EAAa,KAAK,GACzF,KAAA,GACJ,IAAW,GAAqB,EAAc,aAAa,GAAqB,GAAM,GAClF,GAAe,GAAY,GAAO,YAAY,EAAc,aAAa,EAAc,KAAK,GAC5F,KAAA;cACE;GACN;;EAEF,IAAI,CAAC,KAAW,CAAC,GAAU;EAE3B,IAAM,IAAc,EAAK,aAAa;EACtC,EAAe,QAAQ;GACrB,YAAA,EAAA,EAAA,EAAA,EAAA,EACK,EAAM,WAAA,EAAA,EAAA,EAAA;IACT,WAAW,EAAY;IACvB,YAAY,EAAY;MACpB,IACA;IACE,gBAAgB,EAAQ;IACxB,6BAA6B,EAAQ;IACrC,0BAA0B,EAAQ;IACnC,GACD,EAAE,CAAA,EACF,IACA;IACE,iBAAiB,EAAS;IAC1B,8BAA8B,EAAS;IACvC,2BAA2B,EAAS;IACrC,GACD,EAAE,CACP;GACD,MAAM;GACN,SAAS,EAAM,QAAQ,EAAY,QAAQ,EAAE,EAAK;GAClD,WAAW;GACX,gBAAgB,IAAU,EAAe,QAAQ,EAAe;GAChE,cAAc,IAAU,UAAU;GACnC,CAAC;;iBAxEwC,GAAA;;;KCvJtC,MAAe,MACnB,EAAM,iBAAiB,WAAY,EAAM,iBAAiB,KAAK,EAAM,kBAAkB,IAAK,KAAO,KAAA,GAE/F,MAAc,GAAG,MAAqB,EAAO,OAAO,GAAO,MAAU,MAAU,KAAK,EAAO,IAAQ,MAAM,EAAM,EAE/G,KAAa,GAAoC,GAAc,GAAgB,GAAe,MAAgB;CAC7G,GAAW,GAAQ,GAAO,EAAI,KACnC,EAAW,YAAY,EAAK,WAAW,IAAQ,GAC/C,EAAW,YAAY,EAAK,cAAc,IAAM;GAGrC,MAA+B,MAAoC;CAC9E,IAAM,IAAqC,EAAE,EACvC,IAAgB;EACpB,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACN,EAAM;EACP;CAuBD,OAtBI,CAAC,EAAc,MAAM,OAAO,SAAS,IAAI,CAAC,GAAW,GAAG,EAAc,GAAS,KAEnF,EAAU,GAAY,cAAc,EAAM,WAAW,EAAM,cAAc,EAAM,cAAc,EAC7F,EAAU,GAAY,YAAY,EAAM,WAAW,EAAM,eAAe,EAAM,YAAY,EACtF,EAAM,cAAc,KAAK,EAAM,cAAc,EAAM,cACrD,EAAU,GAAY,UAAU,EAAM,WAAW,EAAM,aAAa,EAAM,WAAW,EAEnF,EAAM,aAAa,EAAM,mBAC3B,EAAU,GAAY,OAAO,EAAM,WAAW,EAAM,mBAAmB,EAAM,gBAAgB,EAE3F,EAAM,aAAa,EAAM,eAC3B,EAAU,GAAY,WAAW,EAAM,WAAW,EAAM,cAAc,EAAM,WAAW,EACnF,EAAM,gBAAgB,EAAM,yBAAyB,EAAM,yBAAyB,EAAM,cAC5F,EAAU,GAAY,OAAO,EAAM,WAAW,EAAM,uBAAuB,EAAM,WAAW,GAI9F,EAAM,cAAc,EAAM,aAC1B,GAAW,EAAM,WAAW,EAAM,eAAe,EAAM,aAAa,EAAM,WAAW,IAErF,EAAU,GAAY,YAAY,EAAM,WAAW,EAAM,eAAe,EAAM,YAAY,EAErF;GAGH,MAAuB,MAC3B,EAAM,aAAa,KAAK,EAAM,cAAc,EAAM,YAAY,EAAM,cAAc,EAAM,YAAY,EAAM,UAE/F,MAAoC,MAAoC;CACnF,IAAM,IAAe,OAAO,EAAM,gBAAiB,WAAW,EAAM,gBAAgB,UAAU,KAAA;CAC9F,OAAA,EAAA,EAAA,EAAA,EAAA;GACG,EAAe,eAAe,EAAM;GACpC,EAAe,WAAW,GAAY,EAAM;GAC5C,EAAe,kBAAkB,EAAM;IACrC,GAA4B,EAAM,CAAA,EACjC,IAAe,EAAE,0BAA0B,GAAc,GAAG,EAAE,CAAA,EAC9D,EAAM,uBAAuB,EAAE,mCAAmC,EAAM,sBAAsB,GAAG,EAAE,CAAA,EAAA,EAAA,EAAA;EACvG,8BAA8B,EAAM;EACpC,8BAA8B,EAAM;GACrC;GAGU,MAAwB,IAAoB,OAAqB;CAC5E,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,KAAK,GAAS;;GAIZ,IAHI,OAAO,sBAAwB,OAG/B,GAAA,IAAC,oBAAoB,wBAAA,QAAA,EAAqB,SAAS,WAAW,GAChE;GAGF,IAAM,KAAa,MAAoC;IACrD,IAAI,CAAC,EAAM,QAAQ,GAAgB,EAAQ,QAAQ,EAAM,KAAK,EAC5D;IAEF,IAAI,EAAM,kBAAkB,WAAW,EAAM,kBAAkB,kBAAkB;KAC/E,GAAoB,EAAM;KAC1B;;IAEF,IAAI,EAAM,WAAW,GAAG;IACxB,IAAM,IAAc,EAAQ,OAAO,MAAM,UAAU,EAAM,KAAK,EACxD,IAAW,GAAoB,EAAM,EACrC,IAAY,YAAY,aAAa,EAAM,WAC3C,IAAU,IAAY;IAsB5B,AArBI,KAmBF,EAlBqB,UACnB,oBAAA,EAAA,EAAA,EAAA,EAAA,EAEK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,GAA+B;KAChC;KACA,eAAe,EAAM,iBAAiB;KACtC,gBAAgB,EAAM;KACtB,cAAc,EAAM;KACpB,KAAK;KACL,iBAAiB,EAAQ,OAAO,MAAM,iBAAiB,EAAM,KAAK;KACnE,CAAC,CAAA,EAAA,EAAA,EAAA;KACF,YAAY;KACZ,gBAAgB,EAAM,iBAAiB;OACpC,GAAiC,EAAM,CAC3C,EACD,EAAE,cAAW,CAEf,CAAK,IAAI,EAAQ,EAEnB,EAAQ,eAAe;KAAE;KAAS;KAAW,MAAM;KAAY,KAAK,EAAM;KAAM,CAAC;;GAGnF,IAAI;IAMF,AALA,IAAW,IAAI,qBAAoB,MAAQ;KACzC,KAAK,IAAM,KAAS,EAAK,YAAY,EACnC,EAAU,EAAM;MAElB,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;GCjIG,MAAqB,MACzB,OAAO,EAAQ,OAAO,SAAS,WAAY,WAAW,EAAQ,OAAO,SAAS,UAAU,KAAA,GAEpF,MAAmB,MAAsC;CAC7D,IAAI,EAAc,SAAS,OAAO,OAAO,EAAc,IAAI;CAC3D,IAAI,EAAc,UAAU,OAAO,EAAc,SAAS;CAC1D,IAAI,OAAO,EAAc,SAAU,YAAY,EAAc,SAAS,YAAY,EAAc,OAAO;EACrG,IAAM,IAAU,EAAc,MAA+B;EAC7D,OAAO,OAAO,KAAW,WAAW,IAAS,KAAA;;GAK3C,MAAe,MACf,OAAO,KAAU,YAAY,aAAiB,MAAY,EAAc,OAAO,EAAM,CAAC,GACnF,EAAc,EAAM,IAAI,EAG3B,MAAkB,GAA0B,MAAA;;mCAC/C,EAAM,WAAA,OAAW,OAAO,UAAY,OAAe,aAAiB,UAAU,EAAM,SAAS,QAAvF,GAA+F,aAAa;GAE/G,MAA0B,GAA0B,MACxD,OAAO,UAAY,OAAe,aAAiB,UAAU,IAAS,KAAA,OAAQ,EAAE,GAAV,GAElE,MAA2B,GAA0B,GAA+B,MAA4B;;CACpH,IAAM,IAAiB,OAAO,UAAY,OAAe,aAAiB,UAAU,EAAM,UAAU,KAAA,GAC9F,IAAU,IAAI,SAAA,IAAA,KAAA,OAAA,KAAA,IAAQ,EAAM,YAAA,OAAW,IAAX,EAA0B;CAI5D,OAHA,EAAY,OAAO,EAAM,QAAQ,EAAY,QAAQ,EAAE,EAAK,EAAE,GAAS,EACrE,MAAM,GAAS,GAAK,MAAU,EAAQ,IAAI,GAAK,EAAM,EACtD,CAAC,EACF,EAAA,EAAA,EAAA,EAAY,EAAA,EAAA,EAAA,EAAA,EAAM,YAAA,CAAS;GAGvB,MAAyB,GAAqB,MAAe;CACjE,IAAM,IAAkC,EAAE;CAC1C,EAAY,OAAO,EAAM,QAAQ,EAAY,QAAQ,EAAE,EAAK,EAAE,EAAQ;CACtE,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAQ,EAAE,EAAI,iBAAiB,GAAK,EAAM;GAGhF,MAAsB,GAA6B,MACvD,GAAgB,EAAQ,QAAQ,EAAI,IAAI,EAAe,EAAQ,OAAO,KAAK,sBAAsB,EAAI,EAEjG,MAAwB,GAA6B,MAAgB;CACzE,IAAI,GAAmB,GAAS,EAAI,EAAE,aAAa;CACnD,IAAM,IAAK,EAAe,UAAU;CACpC,EAAQ,eAAe;EAAE;EAAI,MAAM,KAAK,KAAK;EAAE,MAAM;EAAiB;EAAK,CAAC;CAC5E,IAAI,IAAQ;CACZ,aAAa;EACP,MACJ,IAAQ,IACR,EAAQ,eAAe;GAAE;GAAI,MAAM,KAAK,KAAK;GAAE,MAAM;GAAe,CAAC;;GAInE,MAA0B,GAA6B,MAC3D,EAAQ,OAAO,UAAU,WAAW,KAChC;CACE,SAAS;CACT,SAAS,GAAuD;EAC9D,GAAU;;CAEb,GACD,GAAmB,EAAI,EAEvB,MAAoB,GAA6B,GAA0B,MAC/E;;UAAQ,UACN,oBAAA,EAAA,EAAA,EAAA,EAAA,EAEK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,GAA+B;EAChC,eAAe;EACf,QAAQ,EAAQ;EAChB,MAAA,IAAK,EAAQ,QAAA,OAAO,KAAP;EACb,iBAAiB,EAAQ,MAAM,EAAQ,OAAO,MAAM,iBAAiB,EAAQ,IAAI,GAAG,KAAA;EACrF,CAAC,CAAA,EACE,EAAQ,MAAM,EAAE,gBAAgB,EAAQ,OAAO,MAAM,UAAU,EAAQ,IAAI,EAAE,GAAG,EAAE,CACvF,EACD,EAAE,MAAM,EAAS,QAAQ,CAC1B;GAIU,MACX,GACA,GACA,GACA,GACA,IAA0B,EAAE,EAC5B,MACG;;CACH,IAAM,IAAY,EAAQ,KACpB,IAAc,IAAY,EAAQ,OAAO,MAAM,UAAU,EAAU,GAAG,KAAA,GACtE,IAAa,GAAgB,EAAc,EAC3C,IAAS,EAAQ,QAcjB,IAAQ;EACZ,YAAA,EAAA,EAAA,EAAA,EAAA,EAbG,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,GAA+B;GAChC,eAAe;GACf;GACA,cAAc,EAAc,SAAS,UAAU,CAAC,EAAc,WAAW,EAAc,IAAI,WAAW;GACtG,gBAAgB;GAChB,KAAK,KAAA,OAAe,KAAf;GACL,iBAAiB,IAAY,EAAQ,OAAO,MAAM,iBAAiB,EAAU,GAAG,KAAA;GACjF,CAAC,CAAA,EACE,IAAc;GAAE,gBAAgB;GAAa,YAAY;GAAa,GAAG,EAAE,CAI/E;EACA,UAAU;GACR;GACA;GACA,MAAM;GACN,KAAK;GACN;EACD,MAAM;EACP,EACG,IAA6B;CACjC,IAAI;;EACF,KAAA,IAAa,GAAkB,EAAQ,KAAA,SAAA,IAAA,EAAE,eAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAa,GAAO,EAAc;UACpE,GAAO;;EACd,AAAI,EAAQ,OAAO,WAAO,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,sCAAsC,EAAM;;CAGjG,IADA,EAAK,cAAc,EAAQ,YAAY,EAAM,WAAW,CAAC,EACrD,MAAe,IAAO;EACxB,EAAK,aAAa,IAA2B,GAAK;EAClD;;CAEF,EAAK,KAAA,OAAc,IAAd,MAAoB,OAAO,MAAe,KAAM,EAAc,SAAS,WAAW,EAAc,UACnG,EAAK,UAAU,EAAE,MAAM,EAAe,OAAO,CAAC;CAEhD,IAAM,IAAa,EAAQ,OAAO,UAAU,SAAS,OAAQ,IAAY,GAAkB,EAAQ,KAAA,OAAtC,KAAA,IAAsC,EAAE;CACrG,AAAI,KACF,GAA2B;EAAE;EAAY;EAAe;EAAO;EAAc,gBAAgB;EAAS;EAAM,CAAC;GAIpG,MAAsC,MAAgE;CACjH,IAAM,IAAuC,EAAE,EACzC,oBAAoB,IAAI,KAAiB;CAE/C,OAAO;EACL,MAAM;EACN,SAAS,MAAW;EACpB,KAAK,GAAS;GACR,aAAO,SAAW,MAEtB;QAAI,OAAO,OAAO,SAAU,YAAY;KACtC,IAAM,IAAgB,OAAO,OACvB,IAAoC,2BAAA;2BAA8B,GAAO,GAAM;;OACnF,IAAM,IAAM,GAAY,EAAM;OAC9B,IAAI,GAAoB,EAAQ,QAAQ,EAAI,EAAE,OAAO,EAAc,KAAK,MAAM,GAAO,EAAK;OAE1F,IAAM,IAAU;QAAE,QADH,GAAe,GAAO,EACnB;QAAQ;QAAK,EACzB,IAAO,GAAiB,GAAS,SAAS,EAAQ,EAClD,IAAS,GAAuB,GAAS,EAAI,EAC7C,IAAc,GAAqB,GAAS,EAAI,EAChD,IAAiB,GAAuB,GAAO,EAAK,EACpD,IACJ,EAAQ,OAAO,UAAU,SAAS,OAAA,IAAS,GAAkB,EAAQ,KAAA,QAAA,EAAE,OACnE,GAA8B,EAAe,GAC7C,KAAA,GACA,IAAc,GAA2B,EAAQ,QAAQ,EAAI,GAC/D,GAAwB,GAAO,GAAM,EAAK,GAC1C,GACA,IAAQ,IACN,KAAW,MAAsC;QACjD,MACJ,IAAQ,IACR,EAAkB,OAAO,EAAS,EAC9B,KAAO,EAAK,cAAc,EAAQ,YAAY,GAAiC,EAAM,CAAC,CAAC,EAC3F,EAAK,KAAK;UAEN,UAAiB;QAErB,AADA,EAAO,QAAQ,EACf,GAAS;;OAEX,EAAkB,IAAI,EAAS;OAC/B,IAAI;QACF,IAAM,IAAW,MAAM,EAAc,KAAK,MAAM,GAAO,EAAY;QAUnE,OATA,GACE,GACA,GACA,SACA;SAAE,SAAS;SAAgB;SAAU,MAAM;SAAS,EACpD,GACA,EACD,EACD,EAAO,SAAS,EAAQ,EACjB;gBACA,GAAO;QAWd,MAVA,GACE,GACA,GACA,SACA;SAAE;SAAO,SAAS;SAAgB,MAAM;SAAS,EACjD,GACA,EACD,EACG,aAAiB,SAAO,EAAK,gBAAgB,EAAM,EACvD,EAAO,SAAS,EAAQ,EAClB;iBACE;QACR,GAAa;;;sBAtDuD,GAAO,GAAA;;;;KA0D/E,AADA,OAAO,QAAQ,GACf,EAAkB,WAAW;MAC3B,AAAI,OAAO,UAAU,MAAc,OAAO,QAAQ;OAClD;;IAGJ,IAAI,OAAO,iBAAmB,KAAa;KACzC,IAAM,oBAAW,IAAI,SAAyC,EACxD,IAAe,eAAe,UAAU,MACxC,IAAe,eAAe,UAAU,MACxC,IAAoD,SAExD,GACA,GACA,IAAQ,IACR,GACA,GACA;MAEA,OADA,EAAS,IAAI,MAAM;OAAE,QAAQ,EAAO,aAAa;OAAE,KAAK,EAAc,OAAO,EAAI,CAAC;OAAE,CAAC,EAC9E,EAAa,KAAK,MAAM,GAAQ,OAAO,EAAI,EAAE,GAAO,GAAU,EAAS;QAE1E,IAAoD,SAAgC,GAAM;;MAC9F,IAAM,KAAA,IAAU,EAAS,IAAI,KAAK,KAAA,OAAI,EAAE,GAAN;MAClC,IAAI,CAAC,EAAQ,OAAO,GAAoB,EAAQ,QAAQ,EAAQ,IAAI,EAAE,OAAO,EAAa,KAAK,MAAM,EAAK;MAC1G,IAAM,IAAO,GAAiB,GAAS,OAAO,EAAQ,EAChD,IAAS,GAAuB,GAAS,EAAQ,IAAI,EACrD,IAAc,GAAqB,GAAS,EAAQ,IAAI;MAC9D,AAAI,GAA2B,EAAQ,QAAQ,EAAQ,IAAI,IAAE,GAAsB,MAAM,EAAK;MAC9F,IAAI,IAAQ,IACN,UAAiB;OACrB,EAAO,QAAQ,EACX,OACJ,IAAQ,IACR,EAAkB,OAAO,EAAS,EAClC,GAAa,EACb,EAAK,KAAK;;MAEZ,EAAkB,IAAI,EAAS;MAC/B,IAAM,UAAY;OACZ,MACJ,GAAoB,GAAS,GAAM,OAAO;QAAE,aAAa;QAAM,MAAM;QAAO,KAAK;QAAM,EAAE,EAAQ,EACjG,GAAa,EACb,EAAO,UAAS,MAAS;QACnB,MACJ,IAAQ,IACR,EAAkB,OAAO,EAAS,EAC9B,KAAO,EAAK,cAAc,EAAQ,YAAY,GAAiC,EAAM,CAAC,CAAC,EAC3F,EAAK,KAAK;SACV;;MAEJ,KAAK,iBAAiB,WAAW,GAAK,EAAE,MAAM,IAAM,CAAC;MACrD,IAAI;OACF,OAAO,EAAa,KAAK,MAAM,EAAK;eAC7B,GAAO;OAGd,MAFI,aAAiB,SAAO,EAAK,gBAAgB,EAAM,EACvD,GAAK,EACC;;;KAKV,AAFA,eAAe,UAAU,OAAO,GAChC,eAAe,UAAU,OAAO,GAChC,EAAkB,WAAW;MAE3B,AADI,eAAe,UAAU,SAAS,MAAa,eAAe,UAAU,OAAO,IAC/E,eAAe,UAAU,SAAS,MAAa,eAAe,UAAU,OAAO;OACnF;;;;EAGN,WAAW;;GACT,KAAK,IAAM,KAAO,CAAC,GAAG,EAAkB,EAAE,GAAK;GAC/C,OAAO,EAAkB,SAAQ,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;EAE/D;GChSU,KAAc,MAA0B;CACnD,IAAI,IAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAChC,IAAQ,IAAO,KAAM,EAAM,WAAW,EAAE;CAE1C,QAAQ,MAAS,GAAG,SAAS,GAAG;GAqBrB,MAAkB,GAAkB,MAAyB;CACxE,IAAM,oBAAU,IAAI,KAA4B,EAC5C,IAAY,GACV,KAAS,MAAgB;EACzB,UAAM,IAAY,IACtB;OAAY;GACZ,KAAK,IAAM,CAAC,GAAK,MAAU,GACzB,AAAI,IAAM,EAAM,cAAc,KAAU,EAAQ,OAAO,EAAI;;;CAG/D,OAAO,EACL,MAAM,GAA6B;EACjC,IAAM,IAAM,KAAK,KAAK;EACtB,EAAM,EAAI;EACV,IAAM,IAAQ,EAAQ,IAAI,EAAI;EAM9B,OALI,CAAC,KAAS,IAAM,EAAM,cAAc,KACtC,EAAQ,IAAI,GAAK;GAAE,OAAO;GAAG,aAAa;GAAK,CAAC,EACzC;GAAE,SAAS;GAAM,OAAO;GAAG,KAEpC,EAAM,SAAS,GACR;GAAE,SAAS,EAAM,SAAS;GAAc,OAAO,EAAM;GAAO;IAEtE;GC9CG,KAAyB,KACzB,KAA8B,GAI9B,KAAuB,mBAEvB,MAAoB,GAAoC,MAAmC;CAC1F,OACL,OAAO,GAAqB,KAAK,EAAW,GAAG,EAAU,EAAW,GAAG;GAmB5D,WAA6C;CACxD,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACvD;GAGF,IAAM,IAAW,GAAe,IAAwB,GAA4B,EAE9E,KAAW,MAAwC;IACvD,IAAM,IAAc,EAClB,GAAG,EAAM,kBAAkB,GAAG,EAAM,WAAW,GAAG,EAAM,WAAW,GAAG,EAAM,aAC7E,EACK,IAAS,EAAS,MAAM,EAAY;IAC1C,IAAI,CAAC,EAAO,SAAS;IAErB,IAAM,IAAa,GAAiB,EAAQ,OAAO,MAAM,WAAW,EAAM,WAAW,EAC/E,IAAa,EAAM,aAAa,EAAQ,OAAO,MAAM,UAAU,EAAM,WAAW,GAAG,KAAA,GAEnF,IAAwB,EAAO,UAAU;IAE/C,EACG,UAAU,iBAAA,EAAA,EAAA,EAAA,EAAA,EACN,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAuB;KACxB,WAAW;KACX,YAAY;KACZ,QAAQ,EAAgB;KACxB,aAAa;KACb,UAAU,EAAc;KACxB,aAAa,KAAA,OAAc,EAAM,qBAApB;KACb,aAAa,EAAM;KACpB,CAAC,CAAA,EAAA,EAAA,EAAA;KACF,mBAAmB;KACnB,0BAA0B,EAAM;KAChC,2BAA2B,EAAM;KACjC,mBAAmB,EAAM;KACzB,mBAAmB;KACnB,mBAAmB,EAAM;KACzB,qBAAqB,EAAM;KAC3B,mBAAmB,EAAM;KACzB,mBAAmB;KACnB,oBAAoB,EAAO;MAC1B,EAAe,uBAAuB;KACvC,gBAAgB;OACZ,IAAwB,EAAE,uBAAuB,EAAM,gBAAgB,GAAG,EAAE,CACjF,CAAC,CACD,KAAK;;GAIV,AADA,SAAS,iBAAiB,2BAA2B,EAAQ,EAC7D,UAAiB,SAAS,oBAAoB,2BAA2B,EAAQ;;EAEnF,WAAW;GAET,AADA,KAAA,QAAA,GAAY,EACZ,IAAW,KAAA;;EAEd;GCjFG,KAA6B,oBAE7B,KAAuB,KAiBvB,WAEF,UAGA,YAEE,WAEF,UAGA,eAEE,MAAkB,MAAe;CAOrC,KAAK,IAAM,CAAC,GAAM,MAAY;EAL5B,CAAC,QAAQ,gBAAgB;EACzB,CAAC,UAAU,mBAAmB;EAC9B,CAAC,WAAW,oBAAoB;EAChC,CAAC,UAAU,4BAA4B;EAEX,EAAU;EACtC,IAAM,IAAU,EAAG,MAAM,EAAQ;EACjC,IAAA,KAAA,QAAI,EAAU,IACZ,OAAO;GAAE;GAAM,SAAS,EAAQ,GAAG,MAAM,IAAI,CAAC;GAAI;;CAGtD,OAAO;EAAE,MAAM;EAAS,SAAS,KAAA;EAAW;GAGxC,MAAa,GAAY,MAAsB;CACnD,IAAM,IAAQ,GAAG,KAAA,OAAY,KAAZ,EAAe,GAAG;CAMnC,OALI,oBAAoB,KAAK,EAAM,GAAS,QACxC,WAAW,KAAK,EAAM,GAAS,YAC/B,OAAO,KAAK,EAAM,GAAS,UAC3B,OAAO,KAAK,EAAM,GAAS,YAC3B,SAAS,KAAK,EAAM,GAAS,UAC1B;GAGH,MAAgB,MAAuB;CAC3C,IAAI,IAAyB;CAC7B,IAAI;EACF,IAAU,OAAO,aAAa,QAAQ,EAAW;aAC3C;CAGR,IAAI,GACF,OAAO;CAET,IAAM,IAAW,EAAe,SAAS;CACzC,IAAI;EACF,OAAO,aAAa,QAAQ,GAAY,EAAS;aAC3C;CAGR,OAAO;GAIH,WAA0C;;CAI9C,OAHI,OAAO,SAAW,MACb,EAAE,GAEJ;EACL,0BAA0B,OAAO;EACjC,2BAA2B,OAAO;EAClC,yBAAA,IAAwB,OAAO,WAAA,OAAA,KAAA,IAAA,EAAQ;EACvC,0BAAA,IAAyB,OAAO,WAAA,OAAA,KAAA,IAAA,EAAQ;EACzC;GAIG,WAAyC;CAC7C,IAAM,IAAa,IAAe;CAIlC,OAHK,IAGE;EACL,0BAA0B,EAAW;EACrC,2BAA2B,EAAW;EACtC,oBAAoB,EAAW;EAC/B,eAAe,EAAW;EAC1B,qBAAqB,EAAW;EACjC,GARQ,EAAE;GAYP,WAA0C;CAC9C,IAAI,OAAO,YAAc,KACvB,OAAO,EAAE;CAEX,IAAM,IAAS,IAAkB,EAC3B,IAAK,UAAU,WACf,IAAU,GAAe,EAAG,EAC5B,IAAS,GAAU,GAAA,KAAA,OAAA,KAAA,IAAI,EAAQ,SAAS,EACxC,IAAA,KAAA,QAAa,EAAQ,UAAU,iCAAiC,KAAK,EAAG,GAAG,WAAW;CAC5F,OAAO;EACL,iBAAkB,UAAoD;EACtE,oBAAoB,UAAU;EAC9B,mBAAA,KAAA,OAAA,KAAA,IAAmB,EAAQ;EAC3B,iBAAA,KAAA,OAAA,KAAA,IAAiB,EAAQ;GACxB,EAAe,cAAc,EAAQ;GACrC,EAAe,iBAAiB,EAAQ;GACxC,EAAe,aAAa;GAC5B,EAAe,SAAS;EAC1B;GAKG,MAAkB,GAAgB,MAAqB;CAC3D,IAAI;CACJ,aAAa;EAIX,AAHI,MAAU,KAAA,KACZ,OAAO,aAAa,EAAM,EAE5B,IAAQ,OAAO,iBAAiB;GAE9B,AADA,IAAQ,KAAA,GACR,GAAI;KACH,EAAS;;GAwBH,MAAsB,IAAmC,EAAE,KAAiB;CACvF,IAAM,IAAuC,EAAE;CAC/C,OAAO;EACL,MAAM;EACN,KAAK,GAAS;;GACZ,IAAM,KAAA,IACJ,EAAQ,eAAA,OAAc,GAAG,GAA2B,GAAG,EAAW,EAAQ,OAAO,YAAY,KAAK,KAA1F,GACJ,IACJ,OAAO,SAAW,OAAe,EAAQ,iBAAiB,KACtD,EAAe,SAAS,GACxB,GAAa,EAAW;GAS9B,IAPA,EAAQ,qBAAA,EAAA,EAAA,EAAA,EACN,aAAa,GAAA,EACV,IAAuB,CAAA,EACvB,IAAuB,CAAA,EACvB,IAAsB,CAC1B,CAAC,EAEE,OAAO,SAAW,KACpB;GAGF,IAAM,IAAkB,SAAqB;IAC3C,EAAQ,qBAAqB,IAAuB,CAAC;MACpD,GAAqB;GAGxB,AAFA,OAAO,iBAAiB,UAAU,EAAgB,EAClD,OAAO,iBAAiB,qBAAqB,EAAgB,EAC7D,EAAkB,WACV,OAAO,oBAAoB,UAAU,EAAgB,QACrD,OAAO,oBAAoB,qBAAqB,EAAgB,CACvE;GAED,IAAM,IAAa,IAAe;GAElC,IAAI,KAAc,OAAO,EAAW,oBAAqB,YAAY;IACnE,IAAM,UAAuB;KAC3B,EAAQ,qBAAqB,IAAsB,CAAC;;IAGtD,AADA,EAAW,iBAAiB,UAAU,EAAe,EACrD,EAAkB,WAAW;;KAC3B,CAAA,IAAA,EAAW,wBAAA,QAAA,EAAA,KAAA,GAAsB,UAAU,EAAe;MAC1D;;;EAGN,WAAW;GACT,OAAO,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;;EAGhC;GC7LG,KAAoB,KACpB,KAAyB,GAEzB,MAAkB,GAA2B,MACjD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,IAAQ,IAAI,IAAQ,GAEvE,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;GAEpF,MAAmB,MAAoB,EAAQ,MAAM,GAAG,GAAG,EAE3D,MAAoB,MACxB;;oBACG,MAAM,KAAK,CACX,MAAK,MAAQ,EAAK,MAAM,CAAC,WAAW,MAAM,CAAC,KAAA,OAAA,KAAA,IAAA,EAC1C,MAAM,KAAA,OAAI,KAAJ;GAEN,MAAmB,MACnB,MAAW,uBAA6B,YACxC,MAAW,aAAmB,kBAC3B,MAaH,MAAa,EAAE,YAAS,UAAO,kBAAe,WAAQ,EAAE,EAAE,WAAQ,aAAU,kBAAiC;CACjH,IAAM,IAAU,GAAgB,EAAM,EAChC,IAAQ,GAAc,EAAM,EAC5B,IAAc,EAAW,GAAG,EAAO,GAAG,EAAQ,GAAG,EAAM,MAAM,GAAG,IAAI,GAAG,EAEvE,IAAiB,EAAS,MAAM,EAAY;CAClD,IAAI,CAAC,EAAe,SAClB;CAEF,EAAQ,eAAe;EAAE,MAAM,KAAK,KAAK;EAAE,MAAM;EAAS,CAAC;CAE3D,IAAM,IAAgB,aAAiB,QAAQ,IAAQ;EAAE;EAAS,MAAM,KAAA,OAAiB,IAAjB;EAAyB,EAC3F,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACD,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAQ,OAAO,MAAM,oBAAoB,CAAA,EACzC,EAAuB;EACxB,WAAW,GAAgB,EAAO;EAClC,YAAY;EACZ,QAAQ,EAAgB;EACxB,aAAa,GAAgB,EAAO;EACpC,UAAU,EAAc;EACxB,aAAa,aAAiB,QAAQ,EAAM,OAAQ,KAAA,OAAiB,IAAjB;EACrD,CAAC,CAAA,EAAA,EAAA,EAAA;EACF,qBAAqB;GACpB,EAAe,wBAAwB,GAAgB,EAAQ;EAChE,wBAAwB;GACvB,EAAe,yBAAyB,GAAiB,EAAM;EAChE,kBAAkB,aAAiB,QAAQ,EAAM,OAAQ,KAAA,OAAiB,IAAjB;GACxD,EAAe,uBAAuB;EACvC,iBAAiB;EACjB,gBAAgB;EAEhB,sBAAsB,EAAe;IAClC,EACJ,EAEK,IAAY,EAAQ,UAAU,GAAU,EAAW;CAGzD,AAFA,EAAU,gBAAgB,EAAc,EACxC,EAAU,UAAU;EAAE,MAAM,EAAe;EAAO;EAAS,CAAC,EAC5D,EAAU,KAAK;GAGJ,MAAqB,IAAkC,EAAE,KAAiB;CACrF,IAAM,IAA+B,EAAE,EACjC,IAAW,GAAe,EAAQ,UAAU,GAAkB,EAC9D,IAAe,GAAe,EAAQ,cAAc,GAAuB;CAEjF,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,KACpB;GAGF,IAAM,IAAW,GAAe,GAAU,EAAa,EAEjD,KAAW,MAAsB;;IAErC,IAAM,IAAgB,CAAC,EAAM,SAAS,CAAC,EAAM,YAAY,EAAM,YAAY;IAC3E,GAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,QAAA,IAAO,EAAM,UAAA,OAAS,EAAM,UAAf;KACb,OAAA,EAAA;MACE,iBAAiB,EAAM;MACvB,eAAe,EAAM;MACrB,eAAe,EAAM;QACjB,IAAgB,EAAE,sBAAsB,IAAM,GAAG,EAAE,CACxD;KACF,CAAC;MAEE,KAAwB,MAAiC;IAC7D,GAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,OAAO,EAAM;KACb,eAAe,EAAM,kBAAkB,QAAQ,EAAM,OAAO,OAAO;KACpE,CAAC;MAEE,KAAmB,MAAiB;IAExC,IAAI,aAAiB,YAAY;IACjC,IAAM,IAAS,EAAM;IACrB,IAAI,EAAE,aAAkB,UAAU;IAClC,IAAM,IAAiB,GACjB,IAAc,EAAe,OAAO,EAAe,QAAS,EAA4B,cAAc;IAC5G,GAAU;KACR;KACA;KACA,UAAU;KACV,QAAQ;KACR,OAAO,gBAAI,MAAM,yBAAyB,EAAO,QAAQ,aAAa,CAAC,GAAG,IAAc;KACxF,eAAe;KACf,OAAO;MACL,YAAY,EAAQ,OAAO,MAAM,UAAU,EAAY;MACvD,YAAY,EAAO;MACpB;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;GC3KG,KAAoB,IACpB,KAA4B,KAC5B,KAAyB,KAEzB,MAAqB,GAA2B,MACpD,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,KAAS,IAAI,IAAQ,GAExE,MAAgB,MAA+B,OAAO,SAAS,EAAM,GAAG,IAAQ,KAAA,GAEhF,MAAwB,GAA4B,MAAuC;;CAC/F,IAAM,KAAA,IAAc,EAAM,gBAAA,OAAA,KAAA,IAAA,EAAc;CACxC,OAAA,KAAA,QAAO,EAAa,eAChB,EAAU,EAAY,aAAa,IAAA,KAAA,OAAA,KAAA,IACnC,EAAa,mBAAA,KAAA,OAAA,KAAA,IAAiB,EAAa,SAAQ,EAAM,QAAQ;GAGjE,MAAuB,GAAsB,OAAoD;CACrG,6BAA6B,EAAO;CACpC,oCAAoC,EAAO;CAC3C,qDAAqD,EAAO;CAC5D,4BAA4B,EAAO;CACnC,iCAAiC,EAAO;CACxC,mCAAmC,EAAO;CAC1C,yCAAyC,EAAO;CAChD,yCAAyC,EAAO;CAChD,+BAA+B,EAAO,YAAY,EAAU,EAAO,UAAU,GAAG;CAChF,+BAA+B,EAAO;CACtC,uCAAuC,EAAO;CAC/C,GAEY,MAAwB,IAAqC,EAAE,KAAiB;CAC3F,IAAM,IAAY,GAAkB,EAAQ,WAAW,GAAkB,EACnE,IAAmB,KAAK,IAAI,GAAW,GAAkB,EAAQ,kBAAkB,GAA0B,CAAC,EAChH,GACA,GACA,IAAqC,EAAE;CAE3C,OAAO;EACL,MAAM;EACN,KAAK,GAAS;;GACZ,IAAI,OAAO,sBAAwB,OAAe,OAAO,cAAgB,KAAa;GACtF,IAAM,KAAA,IAAsB,oBAAoB,wBAAA,OAAuB,EAAE,GAAzB,GAC1C,IAAY,EAAoB,SAAS,uBAAuB,GAClE,yBACA,EAAoB,SAAS,WAAW,GACtC,aACA,KAAA;GACN,IAAI,CAAC,GAAW;GAEhB,IAAM,IAAU,EAAQ,MAAM,cAAc,2BAA2B,EACrE,aAAa,kDACd,CAAC,EACI,IAAoB,EAAQ,MAAM,gBAAgB,8BAA8B;IACpF,aAAa;IACb,MAAM;IACP,CAAC,EACI,IAAoB,EAAQ,MAAM,gBAAgB,uCAAuC;IAC7F,aAAa;IACb,MAAM;IACP,CAAC,EAEI,KAAa,MAAyD;;IAC1E,IAAI,CAAC,OAAO,SAAS,EAAM,SAAS,IAAI,EAAM,WAAW,GAAW;IACpE,IAAM,IAAS,EAAM,cAAc,wBAC7B,IAAO,IAAU,IAAoC,KAAA,GACrD,KAAA,IAAA,KAAA,OAAA,KAAA,IAAU,EAAM,YAAA,OAAW,EAAE,GAAb,GAChB,IAAU,EAAQ,QACrB,GAAU,MAAY,CAAC,KAAY,EAAO,WAAW,EAAS,WAAW,IAAS,GACnF,KAAA,EACD,EACK,IAAK,EAAe,YAAY,EAChC,IAAY,YAAY,aAAa,EAAM,WAC3C,IAAU,IAAY,EAAM,UAC5B,IAAS,EAAM,YAAY,IAAmB,EAAgB,UAAU,EAAgB,SACxF,IAAqB,IAAS,yBAAyB,aACvD,IAAA,KAAA,QAAa,EAAS,YAAY,EAAQ,OAAO,MAAM,UAAU,EAAQ,UAAU,GAAG,KAAA,GACtF,IAAoB,IACtB,KAAA,IACA,GAAqB,GAA8B,EAAQ,OAAO,MAAM,UAAU,EAChF,IAAS,MAAA,KAAA,OAAA,KAAA,IAAc,EAAS,uBAAsB,KAAqB,WAC3E,IAAO,EAAQ,UACnB,qBAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAEK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAuB;KACxB,UAAU,EAAM;KAChB,WAAW,MAAW,EAAgB,UAAU,wBAAwB;KACxE,YAAY,IAAS,SAAS;KAC9B;KACA,aAAa;KACb,UAAU,EAAc;KACxB,aAAa;KACb,cAAA,IAAA,KAAA,OAAA,KAAA,IAAa,EAAM,qBAAA,OAAoB,EAAM,WAA1B;KACpB,CAAC,CAAA,EAAA,EAAA,EAAA;MACD,EAAe,4BAAA,IAAA,KAAA,OAAA,KAAA,IAA2B,EAAM,qBAAA,OAAoB,EAAM,WAA1B;MAChD,EAAe,mBAAmB,EAAM;MACxC,EAAe,oBAAoB;MACnC,EAAe,aAAa;MAC5B,EAAe,wBAAwB,EAAM,YAAY;MACzD,EAAe,sBAAsB,EAAQ;OAC1C,IAAoB,GAAG,EAAe,kBAAkB,GAAmB,GAAG,EAAE,CAAA,EAChF,IACA;MACG,EAAe,kBAAkB;KAClC,8BAA8B,EAAQ;KACtC,sDAAsD,EAAQ;KAC9D,mCAAmC,EAAQ;KAC3C,kCAAkC,EAAQ;KAC3C,GACD,EAAE,CAAA,EACF,GAAA,KAAA,OAAA,KAAA,IAAa,EAAM,sBAAsB,KAAK,KAAA,IAE9C,EAAE,GADF,EAAE,sCAAsC,EAAM,uBAAuB,CACnE,EACF,GAAA,KAAA,OAAA,KAAA,IAAa,EAAM,YAAY,KAAK,KAAA,IAA8D,EAAE,GAApD,EAAE,0BAA0B,EAAM,aAAa,CAAK,EACpG,GAAA,KAAA,OAAA,KAAA,IAAa,EAAM,oBAAoB,KAAK,KAAA,IAE5C,EAAE,GADF,EAAE,oCAAoC,EAAM,qBAAqB,CAC/D,EACF,IAAO,GAAG,EAAe,oBAAoB,EAAK,WAAW,GAAG,EAAE,CACvE,EACD,EAAE,cAAW,CACd;IASD,AARA,EAAQ,SAAQ,MAAU;KACxB,EAAK,SACH,oBACA,EAAQ,YAAY,GAAoB,GAAQ,EAAQ,OAAO,MAAM,UAAU,CAAC,EAChF,YAAY,aAAa,EAAO,UACjC;MACD,EACF,EAAK,IAAI,EAAQ,EACjB,EAAQ,eAAe;KAAE;KAAS;KAAW,MAAM;KAAa,CAAC;IAEjE,IAAM,IAAa,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACtB,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAQ,sBAAsB,CAAC,CAAA,EAAA,EAAA,EAAA,GACzD,EAAe,oBAAoB,GAAA,CACrC,CAAC;IAGF,AAFA,EAAQ,IAAI,GAAG,EAAW,EAC1B,EAAkB,OAAO,EAAM,UAAU,EAAW,EAChD,KAAM,EAAkB,OAAO,EAAK,kBAAkB,EAAW;MAGjE,KAAe,MACnB,EAAQ,SAAQ,MAAS,EAAU,EAA6B,CAAC;GACnE,IAAI;IACF,IAAI,IAAe;IAkBnB,AAjBA,IAAW,IAAI,qBAAoB,MAAQ;KACzC,IAAM,IAAU,EAAK,YAAY;KACjC,AAAI,KACF,EAAe,KAAK,GAAG,EAAQ,EAC1B,MACH,IAAoB,iBAAiB;MACnC,IAAM,IAAgB;MAGtB,AAFA,IAAiB,EAAE,EACnB,IAAoB,KAAA,GACpB,EAAY,EAAc;OAC1B,KAGJ,EAAY,EAAQ;MAEtB,EACF,EAAS,QAAQ;KAAE,MAAM;KAAW,UAAU;KAAM,CAAC,EACrD,IAAe;eACT;IACN,IAAW,KAAA;;;EAGf,WAAW;GAKT,AAJA,KAAA,QAAA,EAAU,YAAY,EACtB,IAAW,KAAA,GACX,aAAa,EAAkB,EAC/B,IAAoB,KAAA,GACpB,IAAiB,EAAE;;EAEtB;GCjMG,oBAAc,IAAI,KAAyB,EAE7C,IAAU,IACV,GACA,GACA,KAAU,IAER,UAAuB,OAAO,WAAa,MAAc,KAAK,SAAS,MAEvE,MAAmB,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,GAAgB,YAAY,KAAW,GAAe,CAAC;GAGnD,WAAqB;CACzB,GAAgB,cAAc,KAAW,GAAe,CAAC;GAGrD,WAAqB;CACrB,MAAW,OAAO,SAAW,OAAe,OAAO,UAAY,QAInE,KAAU,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,GAAgB,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,GAAgB,gBAAgB,EAAQ,EACjC;IAET,OAAO,iBAAiB,YAAY,GAAW,EAC/C,OAAO,iBAAiB,cAAc,GAAa;GAG/C,WAAuB;CACvB,CAAC,MAAW,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,KAAU,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;UC6EN,QAAa,kBAAA,EA/HvB,KAA2B,IAAI,IAAY,CAAC,GAAG,OAAO,OAAO,EAAe,EAAE,EAAsB,CAAC,EAErG,WACA,OAAO,WAAa,MACf,KAEF,SAAS,MAGZ,WAAkD;CACtD,IAAI,OAAO,cAAgB,KAAa,OAAO,EAAE;CACjD,IAAM,IAAa,YAAY,iBAAiB,aAAa,CAAC;CAC9D,IAAI,CAAC,GAAY,OAAO,EAAE;CAC1B,IAAM,IAAyB,EAAE,EAC3B,KAAa,GAAa,MAAkB;EAChD,AAAI,OAAO,SAAS,EAAM,IAAI,IAAQ,MAAG,EAAW,KAAO;;CAO7D,OALA,EAAU,EAAe,eAAe,EAAW,cAAc,EACjE,EAAU,EAAe,oBAAoB,EAAW,eAAe,EACvE,EAAU,EAAe,sBAAsB,EAAW,yBAAyB,EACnF,EAAU,EAAe,iBAAiB,EAAW,YAAY,EACjE,EAAU,EAAe,eAAe,EAAW,aAAa,EACzD;GAGH,MAAe,MAAiB;CACpC,IAAM,IAAa,EAAK,QAAQ,IAAI;CACpC,OAAO,IAAa,IAAI,IAAO,EAAK,MAAM,GAAG,EAAW;GAGpD,MAAgB,MAAiB;CACrC,IAAM,IAAK,EAAK,MAAM,EAAE;CACxB,IAAI,CAAC,KAAM,OAAO,WAAa,OAAe,OAAO,SAAS,kBAAmB,YAAY,OAAO;CACpG,IAAI;EACF,OAAO,EAAQ,SAAS,eAAe,mBAAmB,EAAG,CAAC;aACxD;EACN,OAAO,EAAQ,SAAS,eAAe,EAAG;;GAIjC,MAAwB,GAAiB,MAAkB;CACtE,IAAI;EACF,IAAM,IAAO,OAAO,WAAa,MAAc,qBAAqB,SAAS,MACvE,IAAO,IAAI,IAAI,GAAS,EAAK,EAC7B,IAAK,IAAI,IAAI,GAAO,EAAK;EAC/B,OAAO,EAAK,aAAa,EAAG,YAAa,CAAC,GAAa,EAAG,KAAK,IAAI,GAAY,EAAK,KAAK,KAAK,GAAY,EAAG,KAAK;aAC5G;EACN,OAAO,MAAY;;GAIV,WAAyC;CACpD,IAAM,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,KAAK,GAAS;GACZ,IAAI,OAAO,SAAW,KACpB;GAGF,IAAI,GACA,GACE,IAAuB,EAAQ,OAAO,KAAK,cAC7C,EAAQ,MAAM,gBAAgB,6BAA6B;IACzD,aAAa;IACb,MAAM;IACP,CAAC,GACF,KAAA,GAEE,KAAoB,IAAY,KAAK,KAAK,KAAK,EAAQ,QAAQ,EAAE,EAAE,EAAU,EAE7E,KACJ,GACA,GACA,IAAY,KAAK,KAAK,EACtB,MACG;IAEH,AADA,EAAK,mBAAmB,GACxB,EAAQ,QAAQ;KACd,YAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAK,WAAA,EACL,EAAK,mBAAA,EAAA,EAAA,EAAA;MACR,cAAc;MACd,yBAAyB,EAAK;MAC9B,iBAAiB,KAAK,IAAI,GAAG,IAAY,EAAK,UAAU;MACxD,kBAAkB,MAAU;MAC5B,sBAAsB;QAClB,IAAS,EAAE,mBAAmB,GAAQ,GAAG,EAAE,CAAA,EAC3C,EAAK,gBAAgB,KAAA,IACrB,EAAE,GACF;OACG,EAAe,kBAAkB,EAAK;MACvC,4BAA4B,EAAK;MAClC,CACN;KACD,MAAM;KACN,WAAW;KACX,gBAAgB,EAAe;KAC/B,cAAc;KACd;KACD,CAAC;MAGE,KAAiB,GAAgB,IAAe,IAAM,IAAU,KAAK,KAAK,KAAK;;IACnF,IAAI,CAAC,GAAY;KACf,AAAI,KAAc,EAAiB,EAAQ;KAC3C;;IAEF,IAAM,IAAA,EAAA,EAAA,EAAgB,EAAY;IAgBlC,CAfA,IAAA,EAAW,oBAAA,QAAA,EAAA,KAAA,EAAmB,EAC1B,EAAW,gBAAgB,KAAA,MAC7B,KAAA,QAAA,EAAsB,OACpB,EAAW,aACX,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACH,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAQ,sBAAsB,CAAC,CAAA,EAAA,EAAA,EAAA,EAC1D,qBAAqB,EAAW,aAAA,CACjC,CAAC,CACH,GAEH,EAAiB,GAAY,SAAS,GAAS,EAAO,EACtD,EAAW,KAAK,aAAa,mBAAmB,EAAO,EACvD,EAAW,KAAK,IAAI,EAAQ,EAC5B,IAAa,KAAA,GACT,KAAc,EAAiB,EAAQ;IAC3C,IAAM,EAAE,MAAM,GAAO,iBAAiB,MAAA;IACtC,OAAA,GADoE,GAAA,GAC7D;MAGH,KAAkB,GAAkB,GAAkB,MAA8B;IACxF,IAAI,MAAe,KAAQ,CAAC,OAAO,SAAS,EAAS,IAAI,IAAW,GAAG;IAGvE,IAAM,IAAA,EAAA,EAAA,EAAA,EADJ,EAAK,gBAAgB,iBAAiB,IAA+B,GAAG,EAAE,CAAA,EAAA,EAAA,EAAA;MAGzE,EAAe,kBAAkB;KAClC,4BAA4B;MAC7B;IAKD,AAJA,EAAK,KAAK,cAAc,EAAkB,EAC1C,OAAO,OAAO,EAAK,oBAAoB,EAAkB,EACzD,EAAK,cAAc,GACnB,EAAK,oBAAoB,GACzB,EAAiB,GAAM,SAAS;MAG5B,KAA4B,MAAqB;IACrD,IAAI,CAAC,EAAQ,OAAO,KAAK,eAAe,SAAS,oBAAoB,UAAU;IAC/E,IAAI,IAAmB,IACnB,GACA,IAAe,EAAK,gBAAgB,gBACpC,GACA,GACA,IAAS,IACT,GAEE,UAAoB;;KACxB,IAAI,KAAU,CAAC,KAAoB,CAAC,KAAgB,MAAe,GAAM;KACzE,IAAM,IAAa,CAAC,GAAmB,EAAc,CAAC,QACnD,MAAmC,MAAc,KAAA,EACnD;KAGD,AAFI,EAAW,UAAQ,EAAe,GAAM,KAAK,IAAI,GAAG,EAAW,EAAE,OAAO,GAC5E,IAAA,EAAK,oBAAA,QAAA,EAAA,KAAA,EAAmB,EACxB,EAAK,kBAAkB,KAAA;OAGnB,UAAsB;;KAC1B,IAAM,KAAA,KAAA,IAAa,aAAY,qBAAA,OAAA,KAAA,IAAA,EAAA,KAAA,GAAmB,aAAa,CAAC;KAIhE,OAHI,KAAA,QAAC,EAAY,gBACjB,IAAgB,KAAK,IAAI,GAAG,YAAY,aAAa,EAAW,eAAe,EAAK,UAAU,EAC9F,IAAe,IACR,MAH+B;;IAMxC,IAAI,CAAC,KAAgB,CAAC,GAAe,EAAE;KACrC,IAAM,UAAe;MACnB,iBAAiB;OACX,MAAe,MACnB,GAAe,EACf,IAAe,IACf,GAAa;QACb;;KAGJ,AADA,OAAO,iBAAiB,QAAQ,GAAQ,EAAE,MAAM,IAAM,CAAC,EACvD,UAAqB,OAAO,oBAAoB,QAAQ,EAAO;;IAGjE,IAAM,IAAe,GAAuB,GAAS,EAAK,YAAW,MAAU;KAG7E,AAFA,IAAmB,IACf,EAAO,gBAAa,IAAoB,KAAK,IAAI,GAAG,EAAO,UAAU,EAAK,UAAU,GACxF,GAAa;MACb,EACI,UAA2B;;KAC3B,SAAS,oBAAoB,aACjC,IAAS,KACT,IAAA,EAAK,oBAAA,QAAA,EAAA,KAAA,EAAmB,EACxB,EAAK,kBAAkB,KAAA;;IAWzB,AATA,SAAS,iBAAiB,oBAAoB,EAAmB,EACjE,UAA2B,SAAS,oBAAoB,oBAAoB,EAAmB,EAC/F,EAAK,wBAAwB;KAK3B,AAJA,GAAc,EACd,KAAA,QAAA,GAAgB,EAChB,IAAe,KAAA,GACf,KAAA,QAAA,GAAsB,EACtB,IAAqB,KAAA;OAEvB,GAAa;MAGT,KAA0B,MAAuC;IACrE,IAAM,IAAW,EAAQ,YAAY,KAAA,OAAc,EAAE,GAAhB,EAAiB;IACtD,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAS,CAAC,QAAQ,CAAC,GAAK,OAAW,MAAU,KAAA,KAAa,CAAC,GAAyB,IAAI,EAAI,CAAC,CAC7G;MAGG,KACJ,GACA,GACA,GACA,MAAA,OAAA,IAAA,KAAA,OAAA,KAAA,IAAc,EAAY,WAAA,OAAU,KAAV,IAAU,KACjC;;IACH,IAAI,CAAC,EAAQ,iBAAiB,EAAE;IAChC,IAAM,KAAA,IAAa,EAAQ,QAAA,OAAO,IAAe,GAAtB,GACrB,IAAY,EAAQ,OAAO,MAAM,iBAAiB,EAAW,EAC7D,KAAA,IAAO,EAAQ,SAAA,OAAQ,IAAR,GACf,IAAmB,EAAuB,EAAQ,WAAW;IACnE,EAAc,GAAQ,GAAM;IAE5B,IAAM,IAAS,EAAe,OAAO,EAC/B,IAAqB,EAAQ,OAAO,MAAM,UAAU,EAAW,EAC/D,IAAsB,EAAQ,OAAO,MAAM,UAAU,EAAY,EACjE,IAAgB,MAAgB,gBAChC,IAAY,KAAiB,OAAO,cAAgB,MAAc,YAAY,aAAa,KAAK,KAAK,EACrG,IAA6B,IAAgB,IAA+B,GAAG,EAAE;IAEvF,EAAQ,QACN;KACE,YAAY;KACZ,IAAI;KACJ;KACA;KACA;KACA,KAAK;KACL,cAAc;KACf,EACD,EACD;IAED,IAAM,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACD,EAAA,EACA,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAA,EACA,EAAuB;KACxB,WAAW;KACX,YAAY,IAAgB,SAAS;KACrC,aAAa,IAAgB,aAAa;KAC1C,UAAU,IAAgB,EAAc,WAAW,EAAc;KACjE,aAAa;KACb,YAAY,IAAgB,cAAc;KAC3C,CAAC,CAAA,EAAA,EAAA,EAAA;MACD,EAAe,SAAS;MACxB,EAAe,WAAW;MAC1B,EAAe,UAAU;MACzB,EAAe,mBAAmB;MAClC,EAAe,kBAAkB;KAClC,gBAAgB;KAChB,gBAAgB;OACZ,IAAgB,EAAE,qBAAqB,EAAQ,OAAO,MAAM,UAAU,SAAS,YAAY,GAAG,EAAE,GAAG,EAAE,CAC1G;IAgBD,AAdA,IAAa;KACX,YAAY;KACZ,iBAAiB;KACjB,IAAI;KACJ;KACA;KACA,QAAQ;KACR,oBAAoB;KACpB,MATqB,EAAQ,UAAU,gBAAgB,GAAgB,EAAE,WAAW,GAAW,CASzF;KACN;KACA,KAAK;KACL,cAAc;KACf,EACD,EAAiB,GAAY,UAAU,EAAU,EACjD,EAAyB,EAAW;MAGhC,IAA0B,EAAQ,eAAc,MAAW;IAC/D,IAAI,EAAQ,SAAS,SAAS;KAC5B,EAAU,EAAQ,SAAS,UAAU,IAAa,iBAAiB,eAAe;KAClF;;IAEG,OACL;SAAI,EAAQ,SAAS,oBAAoB;;MAGvC,CAFA,IAAA,EAAW,oBAAA,QAAA,EAAA,KAAA,EAAmB,EAC9B,EAAW,kBAAkB,KAAA,GAC7B,EAAe,GAAY,KAAK,IAAI,GAAG,EAAQ,OAAO,EAAW,UAAU,EAAE,SAAS;MACtF;;KAcF,AAZA,EAAW,OAAO,EAAQ,MAC1B,EAAW,KAAK,aAAa,EAAe,UAAU,EAAQ,KAAK,EACnE,EAAW,mBAAmB,EAAe,YAAY,EAAQ,MACjE,EAAQ,QAAQ;MACd,YAAY,EAAW;MACvB,IAAI,EAAW;MACf,aAAa,EAAW;MACxB,MAAM,EAAW;MACjB,WAAW,EAAW;MACtB,KAAK,EAAW;MAChB,cAAc,EAAW;MAC1B,CAAC,EACF,EAAiB,GAAY,SAAS;;KACtC,EAEE,UAAyB;GAC7B,AAAI,EAAQ,OAAO,KAAK,aAAa,WAC9B,KAAY,EAAU,EAAE,EAAE,QAAQ,eAAe,EACtD,IAAmB,IAAqB,MAAS;IAC/C,AAAI,GAAqB,EAAM,SAAS,EAAM,MAAM,IAClD,EAAU,EAAE,EAAE,EAAM,QAAQ,gBAAgB,EAAM,QAAQ;KAE5D;GAGJ,IAAM,KAAc,MAA+B;IACjD,AAAK,EAAM,aAAW,EAAc,WAAW;MAE3C,KAAc,MAA+B;IACjD,AAAI,EAAM,aAAa,EAAQ,OAAO,KAAK,aAAa,UACtD,EAAU,EAAE,EAAE,WAAW,YAAA,KAAA,OAAA,KAAA,IAAY,EAAY,OAAO;MAGtD,IAA8B,EAAQ,oBAAmB,MAAS;IACtE,IAAI,EAAM,SAAS,WAAW;KAC5B,IAAc,EAAc,mBAAmB,IAAM,EAAM,UAAU;KACrE;;IAEF,AAAI,MACF,EACE;KAAE,YAAY,EAAY;KAAY,MAAM,EAAY;KAAM,KAAK,EAAY;KAAQ,EACvF,kBACA,mBACA,EAAY,OACb,EACD,IAAc,KAAA;KAEhB;GAIF,AAHA,OAAO,iBAAiB,YAAY,EAAW,EAC/C,OAAO,iBAAiB,YAAY,EAAW,EAE/C,EAAkB,KAChB,GACA,GACA,SACM,OAAO,oBAAoB,YAAY,EAAW,QAClD,OAAO,oBAAoB,YAAY,EAAW,QAClD,EAAc,WAAW,CAChC;;EAEH,WAAW;GACT,OAAO,EAAkB,SAAQ;;IAC/B,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;;EAGhC;GC/YG,KAA4B,KAE5B,MAAe,MACnB,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,IAAI,IAAQ,GAEpD,MACX,GACA,GACA,GACA,OACyB;CACzB,WAAW,KAAK,IAAI,IAAM,GAAe,IAAM,EAAc;CAC7D;CACA,WAAW;CACX,QAAQ;CACT,GAEY,KACX,GACA,GACA,MACoC;CACpC,IAAI,KAAO,EAAO,YAAY,GAAe,OAAO;CACpD,IAAI,KAAO,EAAO,WAAW,OAAO;GAIzB,MACX,GACA,GACA,GACA,MAAA,EAAA,EAAA,EAAA,EAEG,EAAA,EAAA,EAAA,EAAA,EACH,WAAW,KAAK,IAAI,IAAM,GAAe,EAAO,YAAY,EAAc,EAAA,CAC3E,EAEY,MAAsB,GAAoB,GAAa,MAAgD;CAClH,IAAI,CAAC,GAAK,OAAO;CACjB,IAAI;EACF,IAAM,IAAS,KAAK,MAAM,EAAI;EAa9B,IAXE,EAAO,WAAW,YAClB,OAAO,EAAO,MAAO,YACrB,EAAO,GAAG,SAAS,KACnB,GAAY,EAAO,UAAU,IAC7B,EAAO,aAAa,IAAM,MAC1B,GAAY,EAAO,UAAU,IAC7B,EAAO,aAAa,EAAO,aAC3B,EAAO,aAAa,EAAO,YAAY,KAKvC,EAAO,WAAW,aAClB,OAAO,EAAO,cAAe,YAC7B,EAAO,WAAW,SAAS,MAC1B,EAAO,WAAW,gBAAgB,EAAO,WAAW,kBACrD,GAAY,EAAO,UAAU,IAC7B,EAAO,aAAa,IAAM,IAE1B,OAAO;aAEH;CAGR,OAAO;GCnDH,KAA0B,MAAU,KACpC,IAA0B,QAAc,KACxC,KAAuB,KACvB,KAAsB,KACtB,KAA+C;CAAC;CAAS;CAAW;CAAU;CAAa,EAC3F,qBAAgB,IAAI,KAAqB,EAczC,MAAwB,MAAuC;CACnE,IAAI,IAAwB,OAAO,SAAW,KAExC,UAAa;;EACjB,IAAI,GACF,IAAI;GACF,OAAO,OAAO,aAAa,QAAQ,EAAW;cACxC;GACN,IAAwB;;EAG5B,QAAA,IAAO,GAAc,IAAI,EAAW,KAAA,OAAI,OAAJ;;CAetC,OADA,GAAM,EACC;EAAE,sBAAsB;EAAuB;EAAM,QAZ7C,MAA0B;GACvC,IAAM,IAAa,KAAK,UAAU,EAAO;GACzC,OAAc,IAAI,GAAY,EAAW,EACpC,GACL,IAAI;IACF,OAAO,aAAa,QAAQ,GAAY,EAAW;eAC7C;IACN,IAAwB;;;EAKuC;GAG/D,MAA0B,GAAkB,MAA4B;CAC5E,IAAI,IAAS;CACb,IAAI;EACF,IAAS,IAAI,IAAI,EAAS,CAAC;aACrB;EACN,IAAS;;CAEX,OAAO,kBAAkB,EAAW,GAAG,EAAgB,IAAI,KAAU,YAAY;GAG7E,MAAoB,GAA6B,MAA+B;CAC/E,EAAO,UACZ,EAAQ,QAAQ;EACd,gBAAgB,EAAe;EAC/B,cAAc;EACd,MAAM,EAAO,WAAW,SAAS,kBAAkB;EACnD,YAAA,EAAA,EAAA,EAAA,EACK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EAAA,EAAA,EAAA;IAC1C,EAAe,mBAAmB;IAClC,EAAe,YAAY,EAAO,OAAO;IACzC,EAAe,mBAAmB,EAAO,OAAO;GACjD,yBAAyB,EAAO;KAC5B,EAAO,aAAa,EAAE,uBAAuB,EAAO,YAAY,GAAG,EAAE,CAC1E;EACF,CAAC;GAGS,MAAuB,IAA6B,EAAE,KAAiB;CAClF,IAAM,IAAuC,EAAE;CAE/C,OAAO;EACL,MAAM;EACN,KAAK,GAAS;;GACZ,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KAAa;IACpE,EAAQ,qBAAqB;MAC1B,EAAe,mBAAmB;MAClC,EAAe,YAAY,EAAe,UAAU;KACtD,CAAC;IACF;;GAGF,IAAM,KAAA,IACJ,EAAQ,eAAA,OACR,GAAuB,EAAQ,OAAO,UAAU,UAAU,EAAQ,OAAO,YAAY,KAAK,GADlF,GAEJ,IAAU,GAAqB,EAAW,EAC1C,IACJ,OAAO,cAAgB,OAAe,OAAO,SAAS,YAAY,WAAW,GACzE,YAAY,aACZ,KAAK,KAAK,EACZ,GACA,GAEE,KAAc,IAAM,KAAK,KAAK,KAAK,GAAmB,EAAQ,MAAM,EAAE,GAAK,EAAwB,EAEnG,UAA+B;IACnC,AAAI,MAAsB,KAAA,MACxB,aAAa,EAAkB,EAC/B,IAAoB,KAAA;MAIlB,KAAc,GAA0C,IAAY,KAAK,KAAK,KAAK;IACvF,IAAM,IAAmB;IACpB,MACL,EAAQ,uBAAuB;KAC7B;KACA,IAAI;KACJ;KACA,MAAM;KACP,CAAC,EACF,GAAwB,EACxB,IAAmB,KAAA,GACnB,EAAQ,qBACN;MACG,EAAe,mBAAmB,KAAA;MAClC,EAAe,YAAY,KAAA;KAC7B,EACD,EACD,EACD,EAAa,OAAO;MAGhB,UAA6B;IACjC,IAAM,IAAM,KAAK,KAAK,EAChB,IAAS,EAAW,EAAI;IAC9B,IAAI,CAAC,GAAQ;KACX,EAAW,YAAY,EAAI;KAC3B;;IAEF,IAAI,EAAO,WAAW,WAAW;KAC/B,EAAW,EAAO,QAAQ,EAAO,UAAU;KAC3C;;IAEF,IAAI,EAAO,OAAO,GAAkB;KAClC,EAAW,YAAY,EAAI;KAC3B;;IAEF,IAAM,IAAS,EAAuB,GAAQ,GAAK,EAAwB;IAC3E,IAAI,CAAC,GAAQ;KACX,EAAmB,EAAO;KAC1B;;IAEF,IAAM,IAAgC;KACpC,WAAW;KACX,YAAY,EAAO;KACnB;KACA,QAAQ;KACT;IAED,AADA,EAAQ,MAAM,EAAQ,EACtB,EAAW,GAAQ,EAAI;MAGnB,KAAsB,MAAgC;IAC1D,GAAwB;IACxB,IAAM,IAAY,KAAK,IAAI,EAAO,WAAW,EAAO,YAAY,EAAwB;IACxF,IAAoB,WAAW,GAAsB,KAAK,IAAI,GAAG,IAAY,KAAK,KAAK,CAAC,CAAC;MAGrF,KACJ,GACA,MAIG;IACH,IAAM,IAAU,EAAO,OAAO;IAU9B,AATA,IAAmB,EAAO,IAC1B,EAAQ,qBACN;MACG,EAAe,mBAAmB;MAClC,EAAe,YAAY,EAAO;KACpC,EACD,KAAK,IAAI,EAAO,WAAW,EAAkB,CAC9C,EACD,EAAmB,EAAO,EACtB,KAAW,KACb,EAAQ,uBAAuB;KAC7B,IAAI,EAAO;KACX,YAAY,EAAU;KACtB,QAAQ,EAAU;KAClB,WAAW,EAAO;KAClB,MAAM;KACP,CAAC;MAIA,KAAiB,IAAmB,OAAU;IAClD,IAAM,IAAM,KAAK,KAAK,EAChB,IAAS,EAAW,EAAI;IAC9B,KAAA,KAAA,OAAA,KAAA,IAAI,EAAQ,YAAW,YAAY,CAAC,EAAuB,GAAQ,GAAK,EAAwB,EAAE;KAChG,IAAM,IAAW,GAAoB,GAAQ,GAAK,IAAyB,EAAwB;KAEnG,AADA,EAAQ,MAAM,EAAS,EACvB,EAAS,GAAU,IAAmB,KAAA,IAAY,EAAE,QAAQ,UAAU,CAAC;KACvE;;IAGF,IAAM,KAAA,KAAA,OAAA,KAAA,IAAa,EAAQ,YAAW,YAAY,EAAO,aAAA,KAAA,OAAA,KAAA,IAAa,EAAQ,IACxE,KAAA,KAAA,OAAA,KAAA,IACJ,EAAQ,YAAW,YACf,EAAO,SACP,IACE,EAAuB,GAAQ,GAAK,EAAwB,GAC5D,IACE,SACA,KAAA,GACJ,IAAU,GACd,EAAe,UAAU,EACzB,GACA,IACA,EACD;IAMD,AALA,EAAQ,MAAM,EAAQ,EACtB,EAAS,GAAS;KAChB;KACA,QAAQ,KAAA,OAAU,SAAV;KACT,CAAC,EACF,GAAiB,GAAS;KACxB;KACA,QAAQ,KAAA,OAAU,SAAV;KACR,QAAQ;KACT,CAAC;MAGE,UAAmB;IACvB,IAAI,CAAC,GAAkB;IACvB,IAAM,IAAM,KAAK,KAAK,EAChB,IAAS,EAAW,EAAI;IAC9B,KAAA,KAAA,OAAA,KAAA,IACE,EAAQ,YAAW,YACnB,EAAO,OAAO,KACd,EAAuB,GAAQ,GAAK,EAAwB,EAC5D;KACA,GAAsB;KACtB;;IAEF,IAAM,IAAW,GAAoB,GAAQ,GAAK,IAAyB,EAAwB;IAEnG,AADA,EAAQ,MAAM,EAAS,EACvB,EAAS,EAAS;;GAGpB,EAAc,GAAK;GAEnB,IAAI,IAAiB,GACf,UAAmB;IACvB,IAAM,IAAM,KAAK,KAAK;IAClB,IAAM,IAAiB,OAC3B,IAAiB,GACjB,GAAe;MAEX,IAAmD;IAAE,SAAS;IAAM,SAAS;IAAM;GAEzF,AADA,GAAgB,SAAQ,MAAa,OAAO,iBAAiB,GAAW,GAAY,EAAwB,CAAC,EAC7G,EAAkB,WAAW;IAC3B,GAAgB,SAAQ,MACtB,OAAO,oBAAoB,GAAW,GAAY,EAAwB,CAC3E;KACD;GAEF,IAAM,UAA0B;IAC9B,AAAI,SAAS,oBAAoB,aAAW,GAAY;;GAE1D,SAAS,iBAAiB,oBAAoB,EAAkB;GAChE,IAAM,IAAqB,OAAO,YAAY,GAAmB,GAAoB;GACrF,EAAkB,WAAW;IAE3B,AADA,SAAS,oBAAoB,oBAAoB,EAAkB,EACnE,OAAO,cAAc,EAAmB;KACxC;GAEF,IAAM,KAAa,MAAwB;IACzC,IAAI,CAAC,EAAQ,gBAAgB,IAAI,EAAM,gBAAgB,OAAO,gBAAgB,EAAM,QAAQ,GAAY;IACxG,IAAM,IAAM,KAAK,KAAK,EAChB,IAAS,GAAmB,EAAM,UAAU,GAAK,EAAwB;IAC/E,KAAA,KAAA,OAAA,KAAA,IACE,EAAQ,YAAW,YACnB,EAAO,OAAO,KACd,CAAC,EAAuB,GAAQ,GAAK,EAAwB,EAC7D;KACA,EAAmB,EAAO;KAC1B;;IAGF,GAAA,KAAA,OAAA,KAAA,IACE,EAAQ,YAAW,YAAY,EAAO,SAAS,aAAA,KAAA,OAAA,KAAA,IAC/C,EAAQ,YAAW,YAAY,EAAO,YAAY,EACnD;;GAIH,AAFA,OAAO,iBAAiB,WAAW,EAAU,EAC7C,EAAkB,WAAW,OAAO,oBAAoB,WAAW,EAAU,CAAC,EAC9E,EAAkB,KAAK,EAAuB;;EAEhD,WAAW;GACT,YAAO,EAAkB,SAAQ,CAAA,IAAA,EAAkB,KAAK,KAAA,QAAA,GAAI;;EAE/D;GC3OG,KAA+C,WAC/C,KAAqB,IACrB,KAAgB,KAChB,KAA0B,KAC1B,KAAqB,KAIrB,MAA2B,MAC1B,IACE,EAAI,QAAQ,MAAM,IAAI,GADZ,IAIb,WAA0D;CAC9D,IAAI,OAAO,cAAgB,KAAa,OAAO;CAC/C,IAAM,IAAQ,YAAY,iBAAiB,aAAa,CAAC;CACzD,OAAO,GAAA,KAAA,OAAA,KAAA,IAAwB,EAAO,KAAK;GAGvC,MAAa,GAAqB,MAAuC;CAC7E,IAAM,CAAC,GAAM,KAAQ,MAAS,QAAQ,CAAC,IAAK,IAAK,GAAG,CAAC,KAAK,IAAI;CAC9D,OAAO,KAAS,IAAO,SAAS,KAAS,IAAO,sBAAsB;GAGlE,MAAiB,MAAkD;;CACnE,aAAO,UAAY,OAAe,EAAE,aAAkB,WAC1D,QAAA,KAAA,KAAA,IACE,EAAO,aAAa,sBAAsB,KAAA,OAC1C,EAAO,aAAa,aAAa,GADS,MACT,OACjC,EAAO,aAAa,OAAO,GADM,MACN,OAC3B,EAAO,QAAQ,aAAa,GADD;GAKzB,WAA4B;CAChC,IAAI,OAAO,cAAgB,KAAa;CACxC,IAAM,IAAS,YAA4D;CAC3E,OAAO,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,GAAG,IAAQ,KAAA;GAG1D,MAAiB,GAAsB,MAA4B;;CAC9E,IAAM,KAAA,IAAQ,EAAM,UAAA,OAAS,IAAT;CAChB,QAAM,kBAAkB,CAAC,OAAO,SAAS,EAAM,GAKnD;MAHE,EAAK,mBAAmB,KAAA,KACxB,EAAM,cAAA,IAAa,EAAK,iBAAA,OAAgB,EAAM,YAAtB,MAAoC,MAC5D,EAAM,YAAY,EAAK,kBAAkB,IACnB;;GAItB,AAHA,EAAK,iBAAiB,EAAM,WAC5B,EAAK,iBAAiB,GACtB,EAAK,6BAA6B,GAClC,EAAK,8BAA8B,IAAA,IAAc,EAAM,YAAA,SAAA,IAAA,EAAU,OAAA,OAAA,KAAA,IAAA,EAAI,KAAK;SAG1E,IADA,EAAK,kBAAkB,GACnB,IAAQ,EAAK,4BAA4B;;GAE3C,AADA,EAAK,6BAA6B,GAClC,EAAK,8BAA8B,IAAA,IAAc,EAAM,YAAA,SAAA,IAAA,EAAU,OAAA,OAAA,KAAA,IAAA,EAAI,KAAK;;EAI9E,AADA,EAAK,eAAe,EAAM,WACtB,EAAK,iBAAiB,EAAK,QAC7B,EAAK,MAAM,KAAK,MAAM,EAAK,iBAAiB,IAAO,GAAG,KACtD,EAAK,oBAAoB,EAAK,4BAC9B,EAAK,qBAAqB,EAAK;;GAItB,MAAiB,GAAsB,MAA4B;;CAC9E,IAAI,CAAC,EAAM,iBAAiB,CAAC,OAAO,SAAS,EAAM,SAAS,EAAE;CAE9D,AADA,EAAK,mBAAmB,KAAK,IAAI,EAAK,kBAAkB,EAAM,cAAc,EAC5E,EAAK,mBAAmB,KAAK,IAAI,EAAK,kBAAkB,EAAM,cAAc;CAC5E,IAAM,KAAA,IAAkB,EAAM,oBAAA,OAAmB,EAAM,YAAzB,GACxB,KAAA,IAAgB,EAAM,kBAAA,OAAiB,IAAjB,GACtB,IAA0B;EAC9B,UAAU,KAAK,IAAI,EAAM,UAAU,GAAc;EACjD,YAAY,KAAK,IAAI,GAAG,IAAkB,EAAM,UAAU;EAC1D,eAAe,EAAM;EACrB,mBAAmB,GAAc,EAAM,OAAO;EAC9C,iBAAiB,EAAM;EACvB,mBAAmB,KAAK,IAAI,GAAG,EAAM,YAAY,EAAM,WAAW,EAAc;EAChF,oBAAoB,KAAK,IAAI,GAAG,IAAgB,EAAgB;EACjE,EACK,IAAQ,EAAK,cAAc,WAAU,MAAQ,EAAK,kBAAkB,EAAM,cAAc;CAM9F,AALI,KAAS,IACP,EAAU,WAAW,EAAK,cAAc,GAAO,aAAU,EAAK,cAAc,KAAS,KAEzF,EAAK,cAAc,KAAK,EAAU,EAEpC,EAAK,cAAc,MAAM,GAAM,MAAU,EAAM,WAAW,EAAK,SAAS,CAAC,OAAO,GAAmB;GAGxF,MAAc,MAAmD;CAC5E,IAAM,IAAc,IAAqB,EACnC,IACJ,MAAgB,KAAA,KAAa,EAAK,4BAA4B,KAAA,IAC1D,KAAK,IAAI,GAAG,IAAc,EAAK,wBAAwB,GACvD,EAAK,qBAAqB,WACxB,KACC,EAAK,mBAAmB,EAAK,oBAAoB,IAAI;CACzD,MAAK,cAAc,QACxB,OAAO,EAAK,cAAc,KAAK,IAAI,EAAK,cAAc,SAAS,GAAG,KAAK,MAAM,IAAmB,GAAG,CAAC;GAIhG,MAAyB,GAAmB,MAAA;;QAAqD;EAErG,wBAAA,IAAuB,EAAO,mBAAA,OAAkB,IAAlB;GAC7B,EAAe,cAAc,EAAO,KAAK,aAAa;GACtD,EAAe,cAAc,EAAO;EACtC;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,iBAAiB,EAAa,MAAM,EAAE,EAAU,EAC7D,EAAa,oBAAoB,EAAa,SAAS,CAAC,EACxD,EAAa,kCAAkC,EAAa,qBAAqB,CAAC,EAClF,EAAa,oCAAoC,EAAa,uBAAuB,CAAC,EACtF,EAAa,gCAAgC,EAAa,kBAAkB,CAAC;GAC7E;EACF,KAAK;GAGH,AAFA,EAAa,kCAAkC,EAAa,qBAAqB,CAAC,EAClF,EAAa,iCAAiC,EAAa,oBAAoB,CAAC,EAChF,EAAa,wBAAwB,EAAa,YAAY,CAAC;GAC/D;EACF,KAAK;GAKH,AAJA,EAAa,gCAAgC,EAAa,oBAAoB,CAAC,EAC/E,EAAa,8BAA8B,EAAa,kBAAkB,CAAC,EAC3E,EAAa,yBAAyB,EAAa,aAAa,CAAC,EACjE,EAAa,iCAAiC,EAAa,qBAAqB,CAAC,EACjF,EAAa,gCAAgC,EAAa,oBAAoB,CAAC;GAC/E;EACF,KAAK;GAEH,AADA,EAAa,gCAAgC,EAAa,kBAAkB,CAAC,EAC7E,EAAa,wBAAwB,EAAa,YAAY,CAAC;GAC/D;EACF,KAAK;GAIH,AAHA,EAAa,+BAA+B,EAAa,kBAAkB,CAAC,EAC5E,EAAa,2BAA2B,EAAa,cAAc,CAAC,EACpE,EAAa,kCAAkC,EAAa,qBAAqB,CAAC,EAClF,EAAa,+BAA+B,EAAa,kBAAkB,CAAC;GAC5E;EACF,SACE;;CAEJ,OAAO;GAGI,WAA0C;CACrD,IAAI,GACA,IAAW;CAEf,OAAO;EACL,MAAM;EACN,KAAW,GAAA;yBAAS;IAClB,IAAI,OAAO,SAAW,KACpB;IAKF,IAAI;IACJ,IAAI;KACF,IAAkB,MAAM,OAAO;aACxB,GAAO;KACd,IAAI,EAAQ,OAAO,OAAO;;MACxB,CAAA,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,mCAAmC,EAAM;;KAEpE;;IAGF,IAAI,GAAU;IAEd,IAAM,EAAE,UAAO,UAAO,cAAW,GAC3B,IAAkB,IAA+B,EACjD,IAAe,EAAQ,MAAM,gBAAgB,yBAAyB;KAC1E,MAAM;KACN,aAAa;KACd,CAAC,EACI,IAAoB,EAAQ,MAAM,gBAAgB,8BAA8B;KACpF,MAAM;KACN,aAAa;KACd,CAAC,EAEI,KAAc,GAAmB,GAA+B,MAAyB;KAC7F,IAAM,IAAa,EAAO,KAAK,aAAa;KA0B5C,EAzBqB,UACnB,qBAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAEK,EAAQ,OAAO,MAAM,mBAAmB,CAAA,EACxC,EAAA,EACA,GAAsB,GAAQ,EAAgB,CAAA,EAC9C,EAAuB;MACxB,UAAU,EAAO,SAAS,QAAQ,KAAA,IAAY,EAAO;MACrD,WAAW,EAAO,WAAW,SAAS,SAAS;MAC/C,YAAY;MACZ,QAAQ,GAAe,EAAO,OAAO;MACrC,aAAa;MACb,UAAU,EAAc;MACxB,aAAa;MACb,aAAa,EAAO;MACrB,CAAC,CAAA,EAAA,EAAA,EAAA;OACD,EAAe,cAAc;OAC7B,EAAe,aAAa,EAAO;OACnC,EAAe,cAAc,EAAO;MAErC,YAAY,EAAO;QAChB,GAAgB,GAAQ,EAAQ,OAAO,MAAM,UAAU,CAC3D,EACD,MAAgB,KAAA,IAAY,KAAA,IAAY,EAAE,gBAAa,CAEzD,CAAK,KAAK;OAGN,KAAwB,MAAsB;KAElD,IAAI,KAAY,CAAC,OAAO,SAAS,EAAO,MAAM,EAAE;KAChD,IAAM,IAAa,EAAO,QAAQ,IAC5B,IACJ,KAAc,OAAO,cAAgB,MAAc,YAAY,aAAa,EAAW,YAAY,KAAA,GAC/F,IACJ,MAAgB,KAAA,IAAY,EAAQ,sBAAsB,GAAG,EAAQ,uBAAuB,EAAY;KAS1G,AARA,EAAkB,OAChB,EAAO,OACP,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EACH,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAkB,CAAA,EAC1C,GAAsB,GAAQ,EAAgB,CAClD,CAAC,CACH,EACD,EAAW,GAAQ,GAAmB,EAAY;OAGhD,GACA,GACA,GAEE,KAAoB,GAAmB,GAA+B,MAAwB;KAClG,IAAM,IAAmB,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EAC5B,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAkB,CAAA,EAC1C,GAAsB,GAAQ,EAAgB,CAClD,CAAC;KAGF,AAFI,EAAO,SAAS,QAAO,EAAa,OAAO,EAAO,OAAO,EAAiB,GACzE,EAAkB,OAAO,EAAO,OAAO,EAAiB,EAC7D,EAAW,GAAQ,GAAmB,EAAY;OAG9C,UAAqB;KACzB,IAAM,IAAO;KACb,IAAI,CAAC,GAAM;KACX,EACE;MACE,aAAa;OACX,oBAAoB,EAAK;OACzB,mBAAmB,EAAK;OACzB;MACD,SAAS,EAAE;MACX,IAAI,EAAe,QAAQ;MAC3B,MAAM;MACN,gBAAgB;MAChB,QAAQ,GAAU,OAAO,EAAK,IAAI;MAClC,OAAO,EAAK;MACb,EACD,EAAK,mBACL,EAAK,UACN;KACD,IAAM,IAAM,GAAW,EAAK;KAC5B,AAAI,KACF,EACE;MACE,aAAa;OACX,YAAY,EAAI;OAChB,mBAAmB,EAAI;OACvB,iBAAiB,EAAI;OACrB,mBAAmB,EAAI;OACvB,oBAAoB,EAAI;OACzB;MACD,SAAS,EAAE;MACX,IAAI,EAAe,QAAQ;MAC3B,MAAM;MACN,gBAAgB;MAChB,QAAQ,GAAU,OAAO,EAAI,SAAS;MACtC,OAAO,EAAI;MACZ,EACD,EAAK,mBACL,EAAK,UACN;OAIC,IAAkB,EAAQ,cAAa,MAAQ;;KAEnD,AADA,GAAc,EACd,IAAc,EAAK,KACf;MACE,KAAK;MACL,4BAA4B;MAC5B,gBAAgB;MAChB,yBAAyB,IAAqB;MAC9C,kBAAkB;MAClB,kBAAkB;MAClB,eAAe,EAAE;MACjB,mBAAmB;MACnB,mBAAmB,EAAQ,sBAAsB;MACjD,YAAA,IAAW,EAAK,cAAA,OAAa,KAAK,KAAK,GAAvB;MACjB,GACD,KAAA;MACJ;IAEF,IAAI,OAAO,sBAAwB,KAAa;;KAY9C,CAXA,IAAI,oBAAoB,wBAAA,QAAA,EAAqB,SAAS,eAAe,KACnE,IAAsB,IAAI,qBAAoB,MAAQ;MAC/C,OACL,KAAK,IAAM,KAAS,EAAK,YAAY,EACjB,YAAY,aAAa,EAAM,YACjC,EAAY,aAC5B,GAAc,GAAa,EAAM;OAEnC,EACF,EAAoB,QAAQ;MAAE,UAAU;MAAM,MAAM;MAAgB,CAAC,IAEvE,IAAI,oBAAoB,wBAAA,QAAA,EAAqB,SAAS,QAAQ,KAC5D,IAAgB,IAAI,qBAAoB,MAAQ;MACzC,OACL,KAAK,IAAM,KAAS,EAAK,YAAY,EACjB,YAAY,aAAa,EAAM,YACjC,EAAY,aAAa,CAAC,EAAM,iBAAiB,CAAC,OAAO,SAAS,EAAM,SAAS,IAEjG,GAAc,GAAa,EAAM;OAEnC,EACF,EAAc,QAAQ;MAAE,UAAU;MAAM,mBAAmB;MAAI,MAAM;MAAS,CAAC;;IAOnF,AAJA,EAAM,EAAqB,EAC3B,EAAM,EAAqB,EAC3B,EAAO,EAAqB,EAE5B,UAAgB;KAKd,AAJA,GAAc,EACd,IAAc,KAAA,GACd,GAAiB,EACjB,KAAA,QAAA,EAAqB,YAAY,EACjC,KAAA,QAAA,EAAe,YAAY;;;;EAG/B,WAAW;GAKT,AAFA,IAAW,IACX,KAAA,QAAA,GAAW,EACX,IAAU,KAAA;;EAEb;GC7ZG,KAA0B,KAAK,MAE/B,KAA2B,KAC3B,KAAgC,GAEhC,MAAwB,MACxB,KAAQ,OAAa,IACrB,OAAO,KAAS,WACd,EAAK,SAAS,MAA2B,OAAO,cAAgB,MAE3D,EAAK,SAEP,IAAI,aAAa,CAAC,OAAO,EAAK,CAAC,aAEpC,aAAgB,eAChB,YAAY,OAAO,EAAK,GAAS,EAAK,aACtC,OAAO,OAAS,OAAe,aAAgB,OAAa,EAAK,OAC9D,GAQH,MAA4B,GAAa,MAAiE;CAC9G,IAAM,IAAyC;EAC7C,yBAAyB;EACzB,YAAY,EAAU,EAAI;EAC3B,EACK,IAA2C,EAC/C,yBAAyB,aAC1B;CAED,IAAI;EACF,IAAM,IAAS,IAAI,IAAI,GAAK,OAAO,WAAa,MAAc,qBAAqB,SAAS,KAAK;EAMjG,AALA,EAAe,oBAAoB,EAAO,MAE1C,EAAe,gBAAgB,EAAO,SAAS,QAAQ,KAAK,GAAG,EAE/D,EAAiB,oBAAoB,EAAO,MAC5C,EAAiB,gBAAgB,EAAe;aAC1C;CAIR,OAAO;EAAE;EAAkB;EAAgB;GAcvC,MAAoB,GAAmB,MAAyB;;CACpE,IAAM,EACJ,iBACA,6BACA,YACA,iBACA,kBACA,eACA,mBACA,WACE,GACE,EAAE,qBAAkB,sBAAmB,GAAyB,GAAK,EAAQ,OAAO,MAAM,UAAU,EAGpG,IAAuB,EAAQ,YAAA,EAAA,EAAA,EAAA,EAAA,EAChC,EAAQ,OAAO,MAAM,qBAAqB,CAAA,EAC1C,EAAwB,EAAQ,sBAAsB,CAAC,CAAA,EACvD,EACJ,CAAC,EACI,KAAA,IAAc,EAAe,sBAAA,OAAqB,EAAe,cAApC,GAG7B,IAAoB,EAAQ,UAAU,qBAAA,EAAA,EAAA,EAAA,EAAA,EACvC,EAAA,EACA,EAAuB;EACxB,WAAW;EACX,YAAY;EACZ,aAAa;EACb,UAAU,EAAc;EACxB;EACD,CAAC,CAAA,EACE,EAAe,oBAAoB,GAAG,EAAe,eAAe,EAAe,mBAAmB,GAAG,EAAE,CAChH,CAAC,EACI,IAAY,YAAY,KAAK,EAC/B,IAAe,IAEb,KAAkB,MAA+B;EACrD,IAAI,GAAc;EAClB,IAAe;EACf,IAAM,IAAW,YAAY,KAAK,GAAG;EAkBrC,AAjBI,MAAW,WACb,EAAY,UAAU;GAAE,MAAM,EAAe;GAAO,SAAS;GAA4B,CAAC,EAE5F,EAAY,cACV,EAAuB;GACrB;GACA,WAAW,MAAW,UAAU,YAAY;GAC5C,YAAY;GACZ,QAAQ,MAAW,UAAU,EAAgB,QAAQ,EAAgB;GACrE,aAAa;GACb,UAAU,EAAc;GACxB;GACA,aAAa;GACd,CAAC,CACH,EACD,EAAY,KAAK,EAEb,MAAW,YAAY,CAAC,GAAY,IACtC,EAAyB,OAAO,GAAU,EAAqB;;CA6CnE,AAzCA,EAAO,iBAAiB,cAAc;EAChC,GAAY,IAChB,EAAe,SAAS;GACxB,EAEF,EAAO,iBAAiB,YAAW,MAAS;EAC1C,IAAI,GAAY,EAAE;EAClB,IAAM,IAAA,EAAA,EAAA,EAAA,EAAe,EAAA,EAAA,EAAA,EAAA,EAAsB,uBAAuB,MAAA,CAAM;EAExE,AADA,EAAe,IAAI,GAAG,EAAQ,EAC9B,EAAa,IAAI,GAAqB,EAAM,KAAK,EAAE,EAAQ;GAC3D,EAEF,EAAO,iBAAiB,eAAe;EACrC,IAAI,GAAY,EAAE;EAElB,IAAM,IAAQ,IAAe,YAAY;EAEzC,AADA,EAAa,IAAI,GAAA,EAAA,EAAA,EAAA,EAAQ,EAAA,EAAA,EAAA,EAAA,EAAsB,yBAAyB,GAAA,CAAO,CAAC,EAChF,EAAe,QAAQ;EAEvB,IAAM,IAAiB,EAAc,MAAM,YAAY,IAAM;EACxD,EAAe,WACpB,EAAQ,QAAQ;GACd,gBAAgB,EAAe;GAC/B,cAAc;GACd,MAAM;GACN,YAAA,EAAA,EAAA,EAAA,EAAA,EACK,EAAA,EACA,EAAuB;IACxB,WAAW;IACX,YAAY;IACZ,QAAQ,EAAgB;IACxB,aAAa;IACb,UAAU,EAAc;IACxB;IACD,CAAC,CAAA,EAAA,EAAA,EAAA;IACF,yBAAyB;IACzB,gCAAgC,EAAe;KAChD;GACF,CAAC;GACF,EAEF,EAAO,iBAAiB,UAAS,MAAS;EACpC,GAAY,KAEX,KACH,EAAe,QAAQ,EAEzB,EAAQ,QAAQ;GACd,gBAAgB,EAAe;GAC/B,cAAc;GACd,MAAM;GACN,YAAA,EAAA,EAAA,EAAA,EAAA,EACK,EAAA,EACA,EAAuB;IACxB,WAAW,EAAM,WAAW,SAAS;IACrC,YAAY;IACZ,QAAQ,EAAM,WAAW,EAAgB,UAAU,EAAgB;IACnE,aAAa;IACb,UAAU,EAAc;IACxB;IACA,aAAa,EAAM;IACpB,CAAC,CAAA,EAAA,EAAA,EAAA;IACF,wBAAwB,EAAM;IAC9B,0BAA0B,EAAM;IAChC,6BAA6B,EAAM;KACpC;GACF,CAAC;GACF;CAGF,IAAM,IAAe,EAAO,KAAK,KAAK,EAAO;CAC7C,EAAO,QAAQ,MAA4D;EACzE,IAAI,CAAC,GAAY,EAAE;GACjB,IAAM,IAAA,EAAA,EAAA,EAAA,EAAgB,EAAA,EAAA,EAAA,EAAA,EAAsB,uBAAuB,OAAA,CAAO;GAE1E,AADA,EAAe,IAAI,GAAG,EAAS,EAC/B,EAAa,IAAI,GAAqB,EAAK,EAAE,EAAS;;EAExD,OAAO,EAAa,EAAK;;GAIhB,WAA0C;CACrD,IAAI,GACA,GACA,IAAW;CAEf,OAAO;EACL,MAAM;EACN,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,EACI,IAA2B,EAAQ,MAAM,gBAAgB,sCAAsC;IACnG,MAAM;IACN,aAAa;IACd,CAAC,EACI,IAAgB,GAAe,IAA0B,GAA8B,EACvF,UAAmB;GA2BzB,AAvBA,IAAe,IAAI,MAAM,GAAiB,EACxC,UAAU,GAAQ,GAAM,GAAW;IACjC,IAAM,IAAS,QAAQ,UAAU,GAAQ,GAAM,EAAU;IACzD,IAAI,GAAU,OAAO;IACrB,IAAM,IAAM,EAAK,MAAM,OAAyB,KAAlB,OAAO,EAAK,GAAG;IAe7C,OAbI,CAAC,KAAO,GAAgB,EAAQ,QAAQ,EAAI,IAGhD,GAAiB,GAAQ;KACvB;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD,CAAC,EAXO;MAcZ,CAAC,EAEF,OAAO,YAAY;;EAErB,WAAW;GAQT,AAPA,IAAW,IAGP,OAAO,SAAW,OAAe,KAAqB,OAAO,cAAc,MAC7E,OAAO,YAAY,IAErB,IAAe,KAAA,GACf,IAAoB,KAAA;;EAEvB;GC3UU,KAAb,MAAiC;;UAC/B,6BAA6B,IAAI,KAAyC,CAAA,UAE1E,WAAiB,MAA6B;GAC5C,KAAK,UAAU,SAAQ,MAAY,EAAS,EAAM,CAAC;cAGrD,cAAoB,OAClB,KAAK,UAAU,IAAI,EAAS,QACf,KAAK,UAAU,OAAO,EAAS;;GCyE1C,MAAyB,OAAkC;CAC/D,KAAK,EAAO;CACZ,SAAS,EAAO;CAChB,kBAAkB,EAAO;CACzB,eAAe,EAAO;CACvB,GAIK,KAA6B,UAE7B,KAAN,MAA2C;CACzC,YAAmB,GAA2C;EAA1B,KAAA,YAAA;;CAEpC,eAAsB;EACpB,OAAO,EACL,UAAU,KAAK,WAAW,GAAG,EAAiB,qBAAqB,EAAiB,YACrF;;CAGH,WAAkB;EAChB,OAAO;;GAIP,GASE,WAA2B;CAC/B,IAAI,GACF,EAAmB,SAAS;MACvB;EACL,IAAM,IAAiB,IAAI,GAAoB,CAAC,QAAQ,EAClD,IAAc,EAAQ,wBAAwB,EAAe;EAEnE,AADK,KAAa,EAAe,SAAS,EAC1C,IAAqB;GACnB,gBAAgB,IAAc,IAAiB,KAAA;GAC/C;GACA,gBAAgB,EAAY,oBAAoB,IAAI,GAA2B,CAAC;GAChF,OAAO;GACR;;CAEH,IAAI,IAAW;CACf,aAAa;EACP,KAAY,CAAC,MACjB,IAAW,IACX,IAAmB,OACf,IAAmB,QAAQ,OAC3B,EAAmB,eAAa,EAAQ,SAAS,EACjD,EAAmB,kBAAgB,EAAY,SAAS,EAC5D,IAAqB,KAAA;;GAOnB,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,MAA6C,CACnE,GAAmC,EAAO,SAAS,QAAQ,CAC5D,EAEK,MAAwB,MAA6C;CACzE,GAAoB,EAAO,QAAQ;CACnC,GAAG,EAAoB,EAAO,SAAS,QAAQ,IAAM,GAAmB;CACxE,IAAsB;CACtB,IAAsB;CACtB,GAAqB,GAAuB,EAAO,SAAS,UAAU,GAAK,CAAC;CAC7E,EAEK,MACJ,GACA,MACI,MAAW,KAAA,IAAY,IAAiB,MAAW,IAEnD,MAAoD,MACxD,OAAO,KAAW,YAAY,IAAkB,IAAS,KAAA,GAErD,KACJ,GACA,GACA,MACI,GAAuB,GAAQ,EAAe,GAAG,CAAC,EAAQ,GAAwB,EAAO,CAAC,CAAC,GAAG,EAAE,EAGhG,MAAiB,MAA+C;CACpE,GAAG,EAAoB,EAAS,OAAO,IAAM,GAAkB;CAC/D,GAAG,EAAoB,EAAS,WAAW,IAAM,GAAsB;CACvE,GAAG,EAAoB,EAAS,aAAa,IAAM,GAAwB;CAC3E,GAAG,EAAoB,EAAS,QAAQ,IAAM,GAAmB;CACjE,GAAG,EAAoB,EAAS,WAAW,IAAO,GAAsB;CACxE,GAAG,EAAoB,EAAS,UAAU,IAAO,GAAqB;CACtE,GAAG,EAAoB,EAAS,cAAc,IAAO,GAAyB;CAC/E,EAIY,KAAb,MAAqD;CAiDnD,kBAA0B,GAAgB;EACxC,IAAI,KAAK,UAAU,QACjB,MAAU,MAAM,WAAW,EAAO,6EAA6E;;CAGnH,sBAAkD;EAChD,OAAO;GACL,aAAa,KAAK;GAClB,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,sBAAsB,KAAK;GAC3B,wBAAwB,KAAK;GAC7B,WAAW,KAAK;GAChB,iBAAiB,KAAK;GACtB,cAAc,KAAK;GACnB,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,wBAAwB,KAAK;GAC7B,gBAAgB,KAAK,iBAAiB;GACtC,YAAY,KAAK,iBAAiB;GAClC,oBAAoB,KAAK;GACzB,eAAe,KAAK,eAAe;GACnC,cAAc,KAAK,eAAe;GAClC,0BAA0B,KAAK;GAC/B,sBAAsB,KAAK;GAC3B,SAAS,KAAK;GACd,SAAS,KAAK,eAAe;GAC7B,WAAW,KAAK;GAChB,QAAQ,KAAK;GACd;;CAEH,sBAA8B;;EAC5B,OAAO,IAAI,GAAc;GACvB,GAAG,GAAqB,KAAK,OAAO;GACpC,GAAG,GAAe,KAAK,OAAO;GAC9B,GAAG,GAAc,KAAK,OAAO,SAAS;GACtC,IAAA,IAAI,KAAK,OAAO,WAAW,YAAA,OAAW,EAAE,GAAb;GAC5B,CAAC;;CAqOJ,YAAY,GAA0B;;EAEpC,QA7TF,oBAAoC,IAAI,IAAqB,CAAA,UAC7D,kBAAA,EAAA,aAA8C;GAG5C,AAFA,MAAM,EAAK,eAAe,YAAY,EACtC,MAAM,EAAK,cAAc,YAAY,EACrC,MAAM,EAAK,eAAe,YAAY;eAExC,kBAAA,KAAA,EAAA,UACA,iBAAA,KAAA,EAAA,UACA,SAAmC,OAAA,UACnC,iBAAA,KAAA,EAAA,UACA,8BAAA,KAAA,EAAA,UACA,sBAAA,KAAA,EAAA,UACA,kBAAA,KAAA,EAAA,UAEA,WAAkB,GAAA,UAClB,mBAAA,EAAA,aAA+C;GAC7C,IAAI,EAAK,iBAAiB,CAAC,EAAK,OAAO,WAAW,EAAK,OAAO,oBAAoB,WAAW;GAC7F,AAAI,EAAK,OAAO,UAAU,WAAW,OAAO,EAAK,qBAAqB,IAAoB;GAC1F,IAAM,IAAgB,EAAK,qBAAqB;GAChD,EAAK,gBAAgB;GACrB,IAAI;;IAEF,AADA,MAAM,EAAc,MAAM,EAAK,qBAAqB,CAAC,EACrD,EAAK,0BAAA,IAAyB,EAAK,OAAO,WAAW,qBAAA,OAAoB,EAAE,GAAtB,EAAuB;YACrE,GAAO;;IAGd,KAFA,MAAM,EAAc,UAAU,EAC9B,EAAK,gBAAgB,KAAA,GACd,EAAK,2BAA2B,SAAQ,CAAA,IAAA,EAAK,2BAA2B,KAAK,KAAA,SAAA,IAAA,EAAE,YAAA,QAAA,EAAA,KAAA,EAAW;IAGjG,OAFA,IAAA,EAAK,uBAAA,QAAA,EAAA,KAAA,EAAsB,EAC3B,EAAK,qBAAqB,KAAA,GACpB;;eAGV,kBAAA,WAAA;wBAAyC,GAAmB;;IAC1D,IAAM,IAAgB,EAAK;IACtB,OAGL;UAFA,MAAM,EAAc,UAAU,EAC9B,EAAK,gBAAgB,KAAA,GACd,EAAK,2BAA2B,SAAQ,CAAA,IAAA,EAAK,2BAA2B,KAAK,KAAA,SAAA,IAAA,EAAE,YAAA,QAAA,EAAA,KAAA,EAAW;KAOjG,AANA,EAAK,qBAAqB;OACvB,EAAe,mBAAmB,KAAA;OAClC,EAAe,YAAY,KAAA;MAC7B,CAAC,EACE,MAAO,MAAM,EAAK,gBAAgB,IACtC,IAAA,EAAK,uBAAA,QAAA,EAAA,KAAA,EAAsB,EAC3B,EAAK,qBAAqB,KAAA;;;mBAZa,GAAA;;;gBAczC,qBAAwD,EAAE,CAAA,UAC1D,kBAAA,KAAA,EAAA,UAyCA,gBAA+B,MAA2B,KAAK,OAAO,MAAM,iBAAiB,EAAW,CAAA,UAExG,UAAA,KAAA,EAAA,UAEA,YAA2B,MAAsB;;GAC/C,IAAM,IAAc,EAAO,YAAY,EAAqB,EAAkB,EAAO,UAAU,CAAC,GAAG,KAAA,GAC7F,IAAA,EAAA,EAAA,EAAA,EACD,EAAA,EAAA,EAAA,EAAA,EACH,YAAY,KAAK,YAAA,EAAA,EAAA,EAAA,EACX,MAAgB,KAAA,IAAY,KAAK,sBAAsB,GAAG,KAAK,uBAAuB,EAAY,CAAA,GAAA,IACjG,EAAO,eAAA,OAAc,EAAE,GAAhB,EACb,CAAC,EAAA,CACH;GACD,KAAK,OAAO,KAAK,EAAO;cAE1B,SAAA,EAAA,aAAoC;;GAElC,AADA,OAAA,IAAM,EAAK,kBAAA,OAAA,KAAA,IAAA,EAAe,OAAO,EACjC,MAAM,EAAK,gBAAgB;eAG7B,8BAA6C,KAAK,eAAe,eAAe,CAAA,UAChF,2BAA0C,MAAsB,KAAK,eAAe,gBAAgB,EAAU,CAAA,UAC9G,mBAAkC,KAAK,QAAA,UACvC,yBAAwC,KAAK,eAAe,iBAAiB,CAAA,UAC7E,sBAAqC,KAAK,eAAe,cAAc,CAAA,UACvE,UAAA,KAAA,EAAA,UAEA,SAAA,KAAA,EAAA,UAEA,2BAA0C,MAAqC;GAC7E,KAAK,eAAe,uBAAuB,EAAM;cAGnD,uBAAsC,MACpC,KAAK,eAAe,mBAAmB,EAAS,CAAA,UAElD,6BAA4C,MAAwC;GAC7E,EAAiB,WACtB,EAAyB;IACvB;IACA,eAAe,KAAK;IACpB,gBAAgB,KAAK;IACtB,CAAC,EACF,KAAK,2BAA2B,KAAK,GAAG,EAAiB;cAG3D,iBAAgC,EAAE,gBAAa,EAAE,EAAE,cAAmC;GACpF,IAAI,CAAC,KAAK,WAAW,EACnB;GAEF,IAAM,IAAW,EAAe,SAAS;GAgBzC,KAfkB,UAAU,iBAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EACvB,KAAK,OAAO,MAAM,mBAAmB,CAAA,EACrC,EAAA,EACA,EAAuB;IACxB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,UAAU,EAAc;IACxB,aAAa;IACb,YAAY;IACb,CAAC,CAAA,EAAA,EAAA,EAAA;KACD,EAAe,WAAW;KAC1B,EAAe,aAAa;KAC5B,EAAe,aAAa;KAC9B,CACD,CAAK,KAAK;cAGZ,sBAAqC,EAAE,gBAAa,EAAE,EAAE,UAAO,cAAmC;GAChG,IAAI,CAAC,KAAK,WAAW,EAAE;IAErB,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,EAAA,EACjC,KAAK,OAAO,MAAM,mBAAmB,CAAA,EACrC,KAAK,OAAO,MAAM,qBAAqB,CAAA,EACvC,EAAuB;IACxB,WAAW,IAAQ,WAAW;IAC9B,YAAY;IACZ,QAAQ,IAAQ,EAAgB,QAAQ,EAAgB;IACxD,aAAa;IACb,UAAU,EAAc;IACxB,aAAa;IACd,CAAC,CAAA,EACC,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,GAAwB,MAAuB;GACrF,IAAM,IAAA,EAAA,EAAA,EAAsB,EAAY;GACxC,IAAI,EAAe,aAAa,GAAY;IAC1C,IAAM,IAAY,EAAW,EAAe;IAK5C,AAJA,KAAK,UACH,KAAK,OAAO,WAAW,OAAO,KAAc,YAAY,EAAU,SAAS,IACvE,GAAc,KAAK,QAAQ,EAAU,GACrC,IACN,EAAe,KAAyB,KAAK;;GAE/C,KAAK,eAAe,cAAc,GAAgB,EAAU;cAG9D,sBAAA,WAAA;wBAA4C,GAAiC;IACvE,EAAK,OAAO,oBAAoB,MACpC,EAAK,OAAO,kBAAkB,GAC1B,EAAK,UAAU,cACf,MAAY,YAAW,MAAM,EAAK,iBAAiB,GAClD,MAAM,EAAK,eAAe,GAAK;;mBALM,GAAA;;;gBAQ5C,YAA2B,MAA0B;GACnD,KAAK,eAAe,QAAQ,EAAK;cAGnC,4BAA2C;GACzC,AAAI,KAAK,WAAW,IAAE,KAAK,eAAe,oBAAoB;IAAE,MAAM,KAAK,KAAK;IAAE,MAAM;IAAoB,CAAC;cAG/G,gBAA+B,MAAiB;GAC9C,AAAI,KAAK,WAAW,IAAE,KAAK,eAAe,oBAAoB;IAAE;IAAM,MAAM;IAAY,CAAC;cAG3F,YAAA,EAAA,aAAuC;GACjC,MAAK,UAAU,WAKnB;SAFA,MAAM,EAAK,eAAe,GAAM,EAChC,MAAM,EAAK,gBAAgB,EACpB,EAAK,kBAAkB,SAAQ;;KACpC,CAAA,IAAA,EAAK,kBAAkB,KAAK,KAAA,QAAA,GAAI;;IAKlC,AAHA,MAAM,EAAK,eAAe,UAAU,EACpC,MAAM,EAAK,cAAc,UAAU,EACnC,MAAM,EAAK,eAAe,UAAU,EACpC,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;;IAEF,EAAK,QAAQ;IACb,IAAI;KAEF,IADA,MAAM,EAAK,iBAAiB,EACxB,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;;KAIlC,MAFA,MAAM,EAAK,eAAe,GAAM,EAChC,EAAK,QAAQ,QACP;;IAER,EAAK,QAAQ;;eAGf,cAA6B,GAAc,IAAyB,EAAE,EAAE,MAAyC;;GAC/G,IAAM,KAAA,IAAA,KAAA,OAAA,KAAA,IACJ,EAAS,gBAAA,OAAgB,QAAA,KAAA,OAAA,KAAA,IAAO,EAAS,cAAc,WAAW,EAAQ,YAAY,KAAA,IAA7E,GACL,IACJ,MAAgB,KAAA,IAAY,KAAK,sBAAsB,GAAG,KAAK,eAAe,gBAAgB,EAAY;GAC5G,OAAO,KAAK,OAAO,UAAU,GAAM;IACjC,OAAA,IAAA,KAAA,OAAA,KAAA,IAAM,EAAS,SAAA,OAAQ,EAAS,WAAjB;IACf,WAAA,KAAA,OAAA,KAAA,IAAW,EAAS;IACpB,YAAY,KAAK,YAAA,EAAA,EAAA,EAAA,EACZ,EAAA,EACA,EACJ,CAAC;IACH,CAAC;cAGJ,cAA6B,IAAgC,EAAE,KAAK;GAClE,AAAI,KAAK,WAAW,IAAE,KAAK,eAAe,oBAAoB;IAAE;IAAS,MAAM;IAAS,CAAC;cAG3F,UAAA,KAAA,EAAA,UAEA,QAAuB,MAA6B;;GAGlD,OAFA,KAAK,kBAAkB,MAAM,GAC7B,IAAA,KAAK,OAAO,WAAW,YAAA,QAAA,EAAS,KAAK,EAAO,EACrC;cAGT,uBAAsC,MAA2C;;GAG/E,OAFA,KAAK,kBAAkB,qBAAqB,EACxC,KAAK,OAAO,aAAS,IAAA,KAAK,OAAO,WAAW,qBAAA,QAAA,EAAkB,KAAK,EAAgB,GAChF;MAIP,KAAK,SAAS,GAAc,EAAY,EACxC,KAAK,iBAAiB,IAAI,GACxB,GACG,IAAwB,IAC1B,EACD,KAAK,OAAO,KACb;EAED,IAAM,IAAW,EAAuB,KAAK,OAAO,mBAAmB,EACjE,WAAwB;GAC5B,IAAI,KAAK,OAAO,UAAU,WAAW,IAAO,OAAO,EAAE;GACrD,IAAM,IAAW,GACf,IAAI,EAAkB,GAAsB,KAAK,OAAO,UAAU,OAAO,CAAC,EAC1E,UACA,KAAK,OAAO,MACb;GAKD,OAAO,CACL,IAAI,GAJJ,KAAK,OAAO,kBAAkB,WAC1B,IAAI,EAAoB,EAAS,GACjC,IAAI,EAAmB,GAAU,KAAK,OAAO,UAAU,EAErB,KAAK,cACzC,KAAK,YAAA,EAAA,EAAA,EAAA,EACA,KAAK,eAAe,eAAe,CAAA,EACnC,KAAK,OAAO,MAAM,mBAAmB,CACzC,CAAC,CACH,CACF;MACC,EACE,IACJ,KAAK,OAAO,UAAU,YAAY,KAC9B,EAAE,GACF,CACE,IAAI,EAA8B;GAChC,UAAU,IAAI,GACZ,GACE,IAAI,EAAA,EAAA,EAAA,EAAA,EACC,GAAsB,KAAK,OAAO,UAAU,QAAQ,CAAA,EAAA,EAAA,EAAA,EACvD,uBAAuB,EAAiC,OAAA,CACzD,CAAC,EACF,WACA,KAAK,OAAO,MACb,CACF;GACD,sBAAsB,KAAK,OAAO;GACnC,CAAC,CACH,EACD,IACJ,KAAK,OAAO,UAAU,SAAS,KAC3B,EAAE,GACF,CACE,IAAI,GACF,IAAI,EACF,GACE,IAAI,EAAgB,GAAsB,KAAK,OAAO,UAAU,KAAK,CAAC,EACtE,QACA,KAAK,OAAO,MACb,CACF,EACD,KAAK,OACN,CACF;EAmBP,AAjBA,KAAK,iBAAiB,IAAI,GAAkB;GAC1C;GACA,SAAS,IAAI,SAAwB,KAAK,OAAO,UAAU,WAAW,MAAS,KAAK,WAAW,CAAC;GAChG;GACD,CAAC,EACF,KAAK,gBAAgB,IAAI,EAAc;GACrC;GACA,SAAS;GACV,CAAC,EACF,KAAK,iBAAiB,IAAI,EAAe;GACvC;GACA,YAAY;GACb,CAAC,EACF,KAAK,SAAS,KAAK,eAAe,UAAU,GAA2B,EACvE,KAAK,QAAQ,KAAK,cAAc,SAAS,GAA2B,EACpE,KAAK,SAAS,KAAK,eAAe,UAAU,GAA2B,EACvE,KAAK,6BAA6B,EAAE,EAChC,KAAK,OAAO,aACd,KAAU,OAAO,CAAC,OAAM,MAAS;;GAC/B,CAAA,IAAA,WAAW,YAAA,QAAA,EAAS,KAAK,8BAA8B,EAAM;IAC7D"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/schema/attributes.ts","../src/core/config.ts","../src/core/plugin.ts","../src/core/sampling.ts","../src/core/url.ts","../src/core/processor.ts","../src/core/runtime-context.ts","../src/core/id.ts","../src/core/signal.ts","../src/schema/span.ts","../src/plugins/action.ts","../src/plugins/activity.ts","../src/core/page-activity.ts","../src/core/telemetry-attributes.ts","../src/plugins/blank-screen.ts","../src/core/request-timing.ts","../src/plugins/request-body.ts","../src/plugins/resource.ts","../src/plugins/common.ts","../src/core/throttle.ts","../src/plugins/csp-violation.ts","../src/plugins/device.ts","../src/plugins/error.ts","../src/core/metric.ts","../src/plugins/long-task.ts","../src/core/route-observer.ts","../src/plugins/page-view.ts","../src/plugins/session-state.ts","../src/plugins/session.ts","../src/plugins/web-vitals.ts","../src/plugins/websocket.ts","../src/core/action-activity.ts","../src/core/sdk.ts"],"sourcesContent":["/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n/**\n * RUM 上报字段 key 集中定义。\n *\n * 插件里不要散落硬编码字符串,后续字段命名调整只改这里,避免采集端与字段文档逐步漂移。\n */\nexport const RUM_ATTRIBUTES = {\n actionErrorCount: 'action.error.count',\n actionFrustrationCount: 'action.frustration.count',\n actionFrustrationType: 'action.frustration.type',\n actionId: 'action.id',\n actionLoadingTime: 'action.loading_time',\n actionLongTaskCount: 'action.long_task.count',\n actionName: 'action.name',\n actionResourceCount: 'action.resource.count',\n actionTargetName: 'action.target.name',\n actionTargetTag: 'action.target.tag',\n actionType: 'action.type',\n deploymentEnvironmentName: 'deployment.environment.name',\n browserName: 'browser.name',\n browserVersion: 'browser.version',\n cacheHit: 'resource.cache.hit',\n deviceType: 'device.type',\n durationBucket: 'duration_bucket',\n errorType: 'error_type',\n eventLabel: 'event_label',\n exceptionFingerprint: 'exception.fingerprint',\n exceptionMessageShort: 'exception.message_short',\n exceptionStackTopFrame: 'exception.stack_top_frame',\n longTaskBlockingDuration: 'long_task.blocking_duration',\n longTaskCulprit: 'long_task.culprit',\n longTaskDuration: 'long_task.duration',\n longTaskEntryType: 'long_task.entry_type',\n longTaskId: 'long_task.id',\n longTaskIsFrozenFrame: 'long_task.is_frozen_frame',\n longTaskScriptCount: 'long_task.script.count',\n longTaskStartTime: 'long_task.start_time',\n networkEffectiveType: 'network.effective_type',\n nextHopProtocol: 'resource.protocol',\n result: 'result',\n serviceName: 'service.name',\n serviceVersion: 'service.version',\n osName: 'os.name',\n sessionHasReplay: 'session.has_replay',\n sessionId: 'session.id',\n sessionStartTime: 'session.start_time',\n spanSubtype: 'span_subtype',\n spanType: 'span_type',\n statusClass: 'status_class',\n targetDomain: 'target_domain',\n targetLabel: 'target_label',\n targetPathTemplate: 'target_path_template',\n targetValue: 'target_value',\n traceScene: 'trace_scene',\n transferSize: 'transfer_size',\n userId: 'user.id',\n vitalMetric: 'vital.metric',\n vitalRating: 'vital.rating',\n vitalValue: 'vital.value',\n viewDomComplete: 'view.dom_complete',\n viewDomContentLoaded: 'view.dom_content_loaded',\n viewDomInteractive: 'view.dom_interactive',\n viewFirstByte: 'view.first_byte',\n viewId: 'view.id',\n viewLoadEvent: 'view.load_event',\n viewLoadingTime: 'view.loading_time',\n viewLoadingType: 'view.loading_type',\n viewName: 'view.name',\n viewUrl: 'view.url',\n viewUrlPathGroup: 'view.url_path_group',\n} as const;\n\nexport type RumAttributeKey = (typeof RUM_ATTRIBUTES)[keyof typeof RUM_ATTRIBUTES];\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTActionPluginOptions } from '../plugins/action';\nimport type { BkOTBlankScreenPluginOptions } from '../plugins/blank-screen';\nimport type { BkOTDevicePluginOptions } from '../plugins/device';\nimport type { BkOTErrorPluginOptions } from '../plugins/error';\nimport type { BkOTLongTaskPluginOptions } from '../plugins/long-task';\nimport type { BkOTSessionConfig } from '../plugins/session';\nimport type { BkOTPlugin } from './plugin';\nimport type { BkOTUserContext } from './runtime-context';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTActionEventPayload {\n attributes?: Attributes;\n name: string;\n}\n\nexport interface BkOTApplicationConfig {\n environment?: string;\n name: string;\n version?: string;\n}\n\nexport interface BkOTAttributesConfig {\n custom?: () => Attributes;\n error?: () => Attributes;\n 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 BkOTBeforeSendEvent {\n attributes: Record<string, unknown>;\n name: string;\n signal: 'log' | 'span';\n}\n\nexport interface BkOTContextConfig {\n attributes?: BkOTAttributesConfig;\n user?: BkOTUserContext;\n}\n\nexport interface BkOTCustomEventPayload {\n attributes?: Attributes;\n error?: Error;\n name: string;\n}\n\nexport interface BkOTExtensionsConfig {\n instrumentations?: Instrumentation[];\n plugins?: BkOTPlugin[];\n}\n\nexport interface BkOTOptions {\n application: BkOTApplicationConfig;\n autoStart?: boolean;\n context?: BkOTContextConfig;\n debug?: boolean;\n enabled?: boolean;\n extensions?: BkOTExtensionsConfig;\n privacy?: BkOTPrivacyConfig;\n session?: BkOTSessionConfig;\n tracking?: BkOTTrackingConfig;\n transport: BkOTTransportConfig;\n}\n\nexport interface BkOTPrivacyConfig {\n /** 初始化时的采集授权状态;运行期可通过 setTrackingConsent 更新。 */\n trackingConsent?: BkOTTrackingConsent;\n /** 在所有 RUM 自定义插件 emit 前统一过滤属性。 */\n redactAttributes?: (attributes: Attributes) => Attributes;\n /** 所有写入 attribute 的 URL 都会先经过此函数。 */\n redactUrl?: (url: string) => string;\n}\n\nexport interface BkOTRequestBodyConfig {\n allowedContentTypes?: Array<RegExp | string>;\n capture?: 'all' | 'errors';\n maxSize?: number;\n redact: (payload: BkOTRequestBodyRedactPayload) => string | undefined;\n}\n\nexport interface BkOTRequestBodyRedactPayload {\n body: string;\n contentType?: string;\n kind: 'request' | 'response';\n method?: string;\n statusCode?: number;\n truncated: boolean;\n type: 'fetch' | 'xhr';\n url?: string;\n}\n\nexport interface BkOTRequestConfig {\n allowedTracingUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n body?: BkOTRequestBodyConfig;\n excludedUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n beforeSend?: (event: BkOTRequestEvent, context: BkOTRequestContext) => boolean | void;\n}\n\nexport type BkOTRequestContext =\n | { error?: unknown; request: Request | RequestInit; response?: Response; type: 'fetch' }\n | { requestBody?: Document | null | XMLHttpRequestBodyInit; type: 'xhr'; xhr: XMLHttpRequest };\n\nexport interface BkOTRequestEvent {\n attributes: Attributes;\n type: 'resource';\n resource: {\n method?: string;\n statusCode?: number;\n type: 'fetch' | 'xhr';\n url?: string;\n };\n}\n\nexport type BkOTSpanProcessorMode = 'batch' | 'simple';\n\nexport interface BkOTTrackingConfig {\n action?: BkOTActionPluginOptions | boolean;\n blankScreen?: BkOTBlankScreenPluginOptions | boolean;\n cspViolation?: boolean;\n device?: BkOTDevicePluginOptions | boolean;\n error?: BkOTErrorPluginOptions | boolean;\n longTask?: BkOTLongTaskPluginOptions | boolean;\n request?: BkOTRequestConfig | boolean;\n resource?: boolean;\n view?: BkOTViewConfig;\n websocket?: boolean;\n webVitals?: boolean;\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n}\n\nexport type BkOTTrackingConsent = 'granted' | 'not-granted';\n\nexport interface BkOTTransportConfig {\n endpoint?: string;\n headers?: Record<string, string>;\n signals?: SignalExportersInputConfig;\n token?: string;\n}\n\nexport type BkOTUrlMatcher = ((url: string) => boolean) | RegExp | string;\n\nexport interface BkOTViewConfig {\n /** 不参与 View loading time 的长轮询、流式请求或埋点 URL。 */\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n /** 自动计算初始加载与 SPA 路由的页面稳定耗时,默认开启。 */\n loadingTime?: boolean;\n tracking?: 'auto' | 'manual';\n /** 返回低基数路由分组,例如 `/trace/:id`。 */\n getPathGroup?: (url: string) => string | undefined;\n}\n\nexport interface MetricExporterInputConfig extends SignalExporterInputConfig {\n intervalMillis?: number;\n}\n\nexport interface ResolvedApplicationConfig {\n environment: string;\n name: string;\n version?: string;\n}\n\nexport interface ResolvedBkOTConfig {\n application: ResolvedApplicationConfig;\n autoStart: boolean;\n debug: boolean;\n enabled: boolean;\n extensions: BkOTExtensionsConfig;\n hooks: ResolvedConfigHooks;\n metricIntervalMillis: number;\n resourceAttributes: Attributes;\n session: ResolvedSessionConfig;\n spanBatch?: BkOTBatchConfig;\n spanProcessor: BkOTSpanProcessorMode;\n tracking: BkOTTrackingConfig;\n trackingConsent: BkOTTrackingConsent;\n transport: ResolvedTransportConfig;\n user?: BkOTUserContext;\n view: ResolvedViewConfig;\n}\n\nexport interface ResolvedConfigHooks {\n beforeSend?: (event: BkOTBeforeSendEvent) => boolean | void;\n getCustomAttributes: () => Attributes;\n getErrorAttributes: () => Attributes;\n getMetricAttributes: () => Attributes;\n getPageAttributes: () => Attributes;\n getViewPathGroup: (url: string) => string;\n redactAttributes: (attributes: Attributes) => Attributes;\n redactUrl: (url: string) => string;\n}\n\nexport interface ResolvedSessionConfig extends BkOTSessionConfig {\n sampleRate: number;\n}\n\nexport interface ResolvedTransportConfig {\n endpoint: string;\n logs: false | SignalExporterConfig;\n metrics: false | SignalExporterConfig;\n traces: false | SignalExporterConfig;\n}\n\nexport interface ResolvedViewConfig {\n excludedActivityUrls?: BkOTUrlMatcher | BkOTUrlMatcher[];\n loadingTime: boolean;\n tracking: 'auto' | 'manual';\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\nexport interface SignalExportersInputConfig {\n logs?: false | SignalExporterInputConfig;\n metrics?: false | MetricExporterInputConfig;\n traces?: false | TraceExporterInputConfig;\n}\n\nexport interface TraceExporterInputConfig extends SignalExporterInputConfig {\n batch?: BkOTBatchConfig;\n processor?: BkOTSpanProcessorMode;\n}\n\nconst DEFAULT_METRIC_INTERVAL_MILLIS = 60 * 1000; // 60s 上报一次\n\nconst trimTrailingSlash = (value: string) => value.trim().replace(/\\/+$/, '');\n\nconst SIGNAL_PATH_SEGMENT: Record<'logs' | 'metrics' | 'traces', string> = {\n traces: 'rum',\n metrics: 'metrics',\n logs: 'logs',\n};\n\n// 统一 endpoint 自动拼 /v1/{rum|metrics|logs};单信号自定义 URL 原样使用,不再拼接。\nconst resolveSignalEndpoint = (endpoint: string | undefined, signal: 'logs' | 'metrics' | 'traces') => {\n const normalized = trimTrailingSlash(endpoint ?? '');\n return `${normalized}/v1/${SIGNAL_PATH_SEGMENT[signal]}`;\n};\n\nconst resolveSignalConfig = (\n transport: BkOTTransportConfig,\n signalPath: 'logs' | 'metrics' | 'traces',\n): false | SignalExporterConfig => {\n const signalConfig = transport.signals?.[signalPath];\n if (signalConfig === false) return false;\n // signals.{signal}.endpoint 是完整 URL,不再拼接默认路径。\n const customEndpoint = signalConfig?.endpoint;\n // token 只负责提供默认鉴权头;headers 保持显式配置优先,便于特殊场景覆盖。\n const mergedHeaders =\n transport.token || transport.headers || signalConfig?.headers\n ? {\n ...(transport.token ? { Authorization: `Bearer ${transport.token}` } : {}),\n ...(transport.headers ?? {}),\n ...(signalConfig?.headers ?? {}),\n }\n : undefined;\n\n return {\n concurrencyLimit: signalConfig?.concurrencyLimit,\n endpoint: customEndpoint\n ? trimTrailingSlash(customEndpoint)\n : resolveSignalEndpoint(transport.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 identityRedactAttributes = (attributes: Attributes) => attributes;\n\n// 默认只保留 URL 的定位信息;认证信息、查询参数和 fragment 可能携带用户数据,统一移除。\nconst defaultRedactUrl = (url: string) => {\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n parsed.username = '';\n parsed.password = '';\n parsed.search = '';\n parsed.hash = '';\n return parsed.href;\n } catch {\n return url.replace(/[?#].*$/, '');\n }\n};\n\nconst getDefaultPathGroup = (url: string) => {\n try {\n return new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href).pathname || '/';\n } catch {\n return url || '/';\n }\n};\n\nconst resolveApplicationConfig = (application: BkOTOptions['application']): ResolvedApplicationConfig => ({\n environment: application.environment ?? 'production',\n name: application.name,\n version: application.version,\n});\n\nconst resolveResourceAttributes = (application: ResolvedApplicationConfig): Attributes => ({\n [RUM_ATTRIBUTES.deploymentEnvironmentName]: application.environment,\n 'rum.provider': 'blueking',\n 'rum.schema.version': '1',\n [RUM_ATTRIBUTES.serviceName]: application.name,\n ...(application.version ? { [RUM_ATTRIBUTES.serviceVersion]: application.version } : {}),\n 'telemetry.sdk.language': 'webjs',\n 'telemetry.sdk.name': '@blueking/open-telemetry',\n});\n\nconst resolveTransportConfig = (transport: BkOTOptions['transport']): ResolvedTransportConfig => {\n const traces = resolveSignalConfig(transport, 'traces');\n const metrics = resolveSignalConfig(transport, 'metrics');\n const logs = resolveSignalConfig(transport, 'logs');\n const endpoint = trimTrailingSlash(transport.endpoint ?? '');\n return {\n endpoint: endpoint || [traces, metrics, logs].find(signal => signal !== false)?.endpoint || '',\n traces,\n metrics,\n logs,\n };\n};\n\nconst validateTransportConfig = (transport: BkOTTransportConfig | undefined) => {\n if (!transport) throw new Error('[bk-ot] transport is required.');\n const enabledSignals = (['traces', 'metrics', 'logs'] as const).filter(\n signal => transport.signals?.[signal] !== false,\n );\n if (!enabledSignals.length) {\n throw new Error('[bk-ot] at least one transport signal must be enabled.');\n }\n for (const signal of enabledSignals) {\n const signalConfig = transport.signals?.[signal];\n const signalEndpoint = signalConfig ? signalConfig.endpoint : undefined;\n // 每个启用的信号都必须能从公共 endpoint 或自身配置解析出上报地址。\n if (!signalEndpoint?.trim() && !transport.endpoint?.trim()) {\n throw new Error(`[bk-ot] transport.signals.${signal}.endpoint or transport.endpoint is required.`);\n }\n }\n};\n\nconst resolveHooks = (config: BkOTOptions): ResolvedConfigHooks => ({\n beforeSend: config.tracking?.beforeSend,\n getPageAttributes:\n config.context?.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.context?.attributes?.metric ?? (() => ({})),\n getErrorAttributes: config.context?.attributes?.error ?? (() => ({})),\n getCustomAttributes: config.context?.attributes?.custom ?? (() => ({})),\n getViewPathGroup: url =>\n (typeof config.tracking?.view === 'object' ? config.tracking.view.getPathGroup?.(url) : undefined) ??\n getDefaultPathGroup(url),\n redactAttributes: config.privacy?.redactAttributes ?? identityRedactAttributes,\n redactUrl: config.privacy?.redactUrl ?? defaultRedactUrl,\n});\n\nexport const resolveConfig = (config: BkOTOptions): ResolvedBkOTConfig => {\n if (!config.application?.name?.trim()) {\n throw new Error('[bk-ot] application.name is required.');\n }\n validateTransportConfig(config.transport);\n const application = resolveApplicationConfig(config.application);\n const viewConfig = typeof config.tracking?.view === 'object' ? config.tracking.view : undefined;\n const traceConfig = config.transport.signals?.traces;\n const metricConfig = config.transport.signals?.metrics;\n\n return {\n application,\n autoStart: config.autoStart ?? true,\n debug: config.debug ?? false,\n enabled: config.enabled ?? true,\n extensions: {\n instrumentations: [...(config.extensions?.instrumentations ?? [])],\n plugins: [...(config.extensions?.plugins ?? [])],\n },\n hooks: resolveHooks(config),\n metricIntervalMillis: metricConfig\n ? (metricConfig.intervalMillis ?? DEFAULT_METRIC_INTERVAL_MILLIS)\n : DEFAULT_METRIC_INTERVAL_MILLIS,\n resourceAttributes: resolveResourceAttributes(application),\n session: {\n ...config.session,\n sampleRate: clampSampleRate(config.session?.sampleRate),\n },\n spanBatch: traceConfig ? traceConfig.batch : undefined,\n spanProcessor: traceConfig ? (traceConfig.processor ?? 'batch') : 'batch',\n tracking: config.tracking ?? {},\n trackingConsent: config.privacy?.trackingConsent ?? 'granted',\n transport: resolveTransportConfig(config.transport),\n user: config.context?.user,\n view: {\n excludedActivityUrls: viewConfig?.excludedActivityUrls,\n loadingTime: viewConfig?.loadingTime !== false,\n tracking: viewConfig?.tracking === 'manual' ? 'manual' : 'auto',\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { ResolvedBkOTConfig } from './config';\nimport type { BkOTSessionLifecycleEvent, BkOTUserContext, BkOTViewCommand, BkOTViewContext } from './runtime-context';\nimport type { Attributes, Meter, Span, SpanKind, TimeInput, Tracer } from '@opentelemetry/api';\nimport type { Logger, LogRecord } from '@opentelemetry/api-logs';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\n\nexport interface BkOTPlugin {\n enabled?: ((config: ResolvedBkOTConfig) => boolean) | boolean;\n name: string;\n flush?: () => Promise<void> | void;\n init: (context: BkOTRuntimeContext) => Promise<void> | void;\n shutdown?: () => Promise<void> | void;\n}\n\nexport interface BkOTRuntimeContext {\n config: ResolvedBkOTConfig;\n 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 flush: () => Promise<void>;\n getRuntimeAttributes: () => Attributes;\n getRuntimeAttributesAt: (timestamp: number) => Attributes;\n isSampled: () => boolean;\n isSessionActive: () => boolean;\n isViewActive: () => boolean;\n notifyActivity: (event: BkOTActivityEvent) => void;\n notifySessionLifecycle: (event: BkOTSessionLifecycleEvent) => void;\n onActivity: (listener: (event: BkOTActivityEvent) => void) => () => void;\n onSessionLifecycle: (listener: (event: BkOTSessionLifecycleEvent) => void) => () => void;\n onViewChange: (listener: (view: BkOTViewContext) => void) => () => void;\n onViewCommand: (listener: (command: BkOTViewCommand) => void) => () => void;\n registerInstrumentations: (instrumentations: Instrumentation[]) => void;\n setRuntimeAttributes: (attributes: Attributes, changedAt?: number) => void;\n /** 更新用户上下文;user.id 由接入方传入,SDK 原样透传 */\n setUser: (user: BkOTUserContext) => void;\n /** 更新当前 View 上下文,后续 span/log 会自动带上 view.* 关联字段 */\n setView: (view: BkOTViewContext, changedAt?: number) => void;\n startSpan: (name: string, attributes?: Attributes, options?: BkOTStartSpanOptions) => Span;\n}\n\n/**\n * startSpan 的扩展选项。\n *\n * 用于\"事件实际发生在过去某个时刻,但 span 现在才创建\"的场景:\n * 例如 PerformanceObserver 在条目结束后才回调,但 span 的时间窗仍需反映\n * 条目真实发生的区间,方便 trace 视图与并发请求对齐。\n *\n * startTime 接受 OT TimeInput;常用 epoch 毫秒(performance.timeOrigin + performance.now())。\n * 配合 span.end(endTime?) 可控制结束时间。\n */\nexport interface BkOTStartSpanOptions {\n /** 用于从历史中解析 Session/View/Action 归属;默认复用数字类型 startTime。 */\n contextTime?: number;\n kind?: SpanKind;\n startTime?: TimeInput;\n}\n\nexport const isPluginEnabled = (plugin: BkOTPlugin, config: ResolvedBkOTConfig) => {\n if (typeof plugin.enabled === 'function') {\n return plugin.enabled(config);\n }\n return plugin.enabled !== false;\n};\n\nexport const createPlugin = (plugin: BkOTPlugin) => plugin;\n\nexport class PluginManager {\n private readonly plugins: BkOTPlugin[];\n private startedPlugins: BkOTPlugin[] = [];\n\n public constructor(plugins: BkOTPlugin[]) {\n this.plugins = plugins;\n }\n\n public async flush() {\n // 并行 flush,避免单个慢插件拖累整体\n await Promise.all(this.startedPlugins.map(plugin => Promise.resolve().then(() => plugin.flush?.())));\n }\n\n public async shutdown() {\n const plugins = [...this.startedPlugins].reverse();\n for (const plugin of plugins) {\n await plugin.shutdown?.();\n }\n this.startedPlugins = [];\n }\n\n public async start(context: BkOTRuntimeContext) {\n // 每个插件独立 try/catch,单个插件 init 失败不应拖累其他插件:\n // - 失败的插件不会进入 startedPlugins(shutdown 时不会被反向 teardown)\n // - debug 模式打 warn,方便排查;非 debug 静默失败避免污染业务日志\n for (const plugin of this.plugins) {\n if (!isPluginEnabled(plugin, context.config)) {\n continue;\n }\n try {\n await plugin.init(context);\n this.startedPlugins.push(plugin);\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn(`[bk-ot] plugin \"${plugin.name}\" init failed:`, error);\n }\n }\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { ResolvedBkOTConfig } from './config';\n\nexport const RUM_SAMPLED_ATTRIBUTE = 'bk.rum.sampled';\n\nconst hashToUnitInterval = (input: string) => {\n let hash = 0x811c9dc5;\n for (let index = 0; index < input.length; index += 1) {\n hash = Math.imul(hash ^ input.charCodeAt(index), 0x01000193);\n }\n hash += hash << 13;\n hash ^= hash >>> 7;\n hash += hash << 3;\n hash ^= hash >>> 17;\n hash += hash << 5;\n return (hash >>> 0) / 0x100000000;\n};\n\n/**\n * 按应用和 RUM Session ID 做确定性采样。采样决定随 Session 更新,不再依赖 Tab 级 sessionStorage。\n */\nexport const isBkOTSampled = (\n { application, session }: Pick<ResolvedBkOTConfig, 'application' | 'session'>,\n sessionId: string,\n) => {\n if (session.sampleRate <= 0) {\n return false;\n }\n if (session.sampleRate >= 1) {\n return true;\n }\n return hashToUnitInterval(`${application.name}\\0${sessionId}`) < session.sampleRate;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport type { BkOTUrlMatcher, ResolvedBkOTConfig } from './config';\n\nconst getBaseUrl = () => (typeof window === 'undefined' ? 'http://localhost' : window.location.origin);\n\nexport const toAbsoluteUrl = (url: string) => {\n try {\n return new URL(url, getBaseUrl()).href;\n } catch {\n return url;\n }\n};\n\n// 去掉 trailing slash,便于 path 边界对齐比较\nconst stripTrailingSlash = (value: string) => (value.length > 1 ? value.replace(/\\/+$/, '') : value);\n\n// 比较两个 URL 是否指向同一资源(忽略 query/fragment 与末尾斜杠)\nconst isSameResource = (left: string, right: string) => {\n const stripped = (value: string) => stripTrailingSlash(value.replace(/[?#].*$/, ''));\n return stripped(left) === stripped(right);\n};\n\nexport const isBkOTEndpoint = (config: ResolvedBkOTConfig, url: string) => {\n const targetUrl = toAbsoluteUrl(url);\n\n return [config.transport.traces, config.transport.metrics, config.transport.logs]\n .filter(endpoint => endpoint !== false)\n .some(endpoint => isSameResource(targetUrl, toAbsoluteUrl(endpoint.endpoint)));\n};\n\nexport const shouldIgnoreUrl = (config: ResolvedBkOTConfig, url: string) => isBkOTEndpoint(config, url);\n\nconst matchesUrl = (matcher: BkOTUrlMatcher, url: string) => {\n if (typeof matcher === 'function') return matcher(url);\n if (typeof matcher === 'string') return url.includes(matcher);\n matcher.lastIndex = 0;\n return matcher.test(url);\n};\n\nexport const matchesUrlList = (matchers: BkOTUrlMatcher | BkOTUrlMatcher[] | undefined, url: string) =>\n (Array.isArray(matchers) ? matchers : matchers ? [matchers] : []).some(matcher => matchesUrl(matcher, url));\n\nconst getRequestConfig = (config: ResolvedBkOTConfig) =>\n typeof config.tracking.request === 'object' ? config.tracking.request : undefined;\n\nexport const shouldIgnoreRequest = (config: ResolvedBkOTConfig, url: string) =>\n shouldIgnoreUrl(config, url) || matchesUrlList(getRequestConfig(config)?.excludedUrls, toAbsoluteUrl(url));\n\n// Trace Header 仅向显式白名单注入,避免同源第三方路径被无意注入以及跨域 CORS 预检。\nexport const shouldPropagateTraceHeader = (config: ResolvedBkOTConfig, url: string) =>\n config.transport.traces !== false && matchesUrlList(getRequestConfig(config)?.allowedTracingUrls, toAbsoluteUrl(url));\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ExportResultCode } from '@opentelemetry/core';\nimport { AggregationTemporality } from '@opentelemetry/sdk-metrics';\n\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\nimport { isBkOTEndpoint } from './url';\n\nimport type { ResolvedBkOTConfig } from './config';\nimport type { Attributes, Context } from '@opentelemetry/api';\nimport type { LogRecordProcessor, SdkLogRecord } from '@opentelemetry/sdk-logs';\nimport type { InstrumentType, MetricData, PushMetricExporter, ResourceMetrics } from '@opentelemetry/sdk-metrics';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nconst SELF_TELEMETRY_URL_KEYS = ['http.url', 'url.full'];\nexport const REQUEST_DISCARD_ATTRIBUTE = 'bk.rum.request.discard';\n\nconst getSpanUrl = (span: ReadableSpan): string | undefined => {\n for (const key of SELF_TELEMETRY_URL_KEYS) {\n const value = span.attributes?.[key];\n if (typeof value === 'string') {\n return value;\n }\n }\n return undefined;\n};\n\nconst hasSessionId = (attributes: Record<string, unknown> | undefined) => {\n const sessionId = attributes?.[RUM_ATTRIBUTES.sessionId];\n return typeof sessionId === 'string' && sessionId.length > 0;\n};\n\nconst hasViewId = (attributes: Record<string, unknown> | undefined) => {\n const viewId = attributes?.[RUM_ATTRIBUTES.viewId];\n return typeof viewId === 'string' && viewId.length > 0;\n};\n\nconst hasRumContext = (attributes: Record<string, unknown> | undefined) =>\n attributes?.[RUM_SAMPLED_ATTRIBUTE] === true && hasSessionId(attributes) && hasViewId(attributes);\n\nexport class FilteringLogRecordProcessor implements LogRecordProcessor {\n public constructor(\n private readonly inner: LogRecordProcessor,\n private readonly config?: ResolvedBkOTConfig,\n ) {}\n\n public enabled(options: Parameters<NonNullable<LogRecordProcessor['enabled']>>[0]) {\n return this.inner.enabled?.(options) ?? true;\n }\n\n public forceFlush() {\n return this.inner.forceFlush();\n }\n\n public onEmit(logRecord: SdkLogRecord, context?: Context) {\n // Session 创建时 View 尚未建立,因此生命周期日志只要求 Session 上下文;其余日志必须归属到完整 View。\n const isSessionLifecycleLog = logRecord.body === 'session.start' || logRecord.body === 'session.rotate';\n const accepted =\n hasRumContext(logRecord.attributes) ||\n (isSessionLifecycleLog &&\n logRecord.attributes?.[RUM_SAMPLED_ATTRIBUTE] === true &&\n hasSessionId(logRecord.attributes));\n if (!accepted) return;\n // 采样标记仅用于 SDK 内部过滤,不能进入最终数据模型。\n delete logRecord.attributes[RUM_SAMPLED_ATTRIBUTE];\n try {\n if (\n this.config?.hooks.beforeSend?.({\n attributes: logRecord.attributes,\n name: logRecord.eventName ?? String(logRecord.body ?? ''),\n signal: 'log',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config?.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEmit(logRecord, context);\n }\n\n public shutdown() {\n return this.inner.shutdown();\n }\n}\n\n/**\n * 给所有 span 固化创建时的运行时上下文,并在 onEnd 阶段过滤自身上报请求和无 RUM 上下文 span。\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n public constructor(\n private readonly inner: SpanProcessor,\n private readonly config: ResolvedBkOTConfig,\n private readonly getRuntimeAttributes: () => Attributes,\n ) {}\n\n public forceFlush(): Promise<void> {\n return this.inner.forceFlush();\n }\n\n public onEnd(span: ReadableSpan): void {\n const url = getSpanUrl(span);\n if (\n span.attributes[REQUEST_DISCARD_ATTRIBUTE] === true ||\n (url && isBkOTEndpoint(this.config, url)) ||\n !hasRumContext(span.attributes)\n ) {\n return;\n }\n delete (span.attributes as Attributes)[RUM_SAMPLED_ATTRIBUTE];\n try {\n if (\n this.config.hooks?.beforeSend?.({\n attributes: span.attributes,\n name: span.name,\n signal: 'span',\n }) === false\n ) {\n return;\n }\n } catch (error) {\n if (this.config.debug) globalThis.console?.warn('[bk-ot] tracking.beforeSend failed:', error);\n }\n this.inner.onEnd(span);\n }\n\n public onStart(span: Span, parentContext: Context): void {\n const existingAttributes = (span as Span & { attributes?: Attributes }).attributes ?? {};\n const missingRuntimeAttributes: Attributes = {};\n // instrumentation 显式写入的属性优先,只补齐创建时缺失的运行时上下文。\n for (const [key, value] of Object.entries(this.getRuntimeAttributes())) {\n if (existingAttributes[key] === undefined && value !== undefined) missingRuntimeAttributes[key] = value;\n }\n span.setAttributes(missingRuntimeAttributes);\n this.inner.onStart(span, parentContext);\n }\n\n public shutdown(): Promise<void> {\n return this.inner.shutdown();\n }\n}\n\nconst filterMetric = (metric: MetricData): MetricData =>\n ({\n ...metric,\n dataPoints: metric.dataPoints\n .filter(point => point.attributes[RUM_SAMPLED_ATTRIBUTE] === true)\n .map(point => {\n const { [RUM_SAMPLED_ATTRIBUTE]: _sampled, ...attributes } = point.attributes;\n return { ...point, attributes };\n }),\n }) as MetricData;\n\nexport class FilteringMetricExporter implements PushMetricExporter {\n public constructor(private readonly inner: PushMetricExporter) {}\n\n public export(metrics: ResourceMetrics, resultCallback: Parameters<PushMetricExporter['export']>[1]) {\n // Metric reader 会同时产出采样与未采样数据点;在 exporter 边界按内部标记过滤,确保后端口径与 trace 一致。\n const filtered: ResourceMetrics = {\n ...metrics,\n scopeMetrics: metrics.scopeMetrics\n .map(scopeMetric => ({\n ...scopeMetric,\n metrics: scopeMetric.metrics.map(filterMetric).filter(metric => metric.dataPoints.length > 0),\n }))\n .filter(scopeMetric => scopeMetric.metrics.length > 0),\n };\n if (filtered.scopeMetrics.length === 0) {\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n this.inner.export(filtered, resultCallback);\n }\n\n public forceFlush() {\n return this.inner.forceFlush();\n }\n\n public selectAggregationTemporality(instrumentType: InstrumentType) {\n return this.inner.selectAggregationTemporality?.(instrumentType) ?? AggregationTemporality.CUMULATIVE;\n }\n\n public shutdown() {\n return this.inner.shutdown();\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nexport type BkOTSessionLifecycleEvent =\n | {\n expiredAt: number;\n id: string;\n reason: 'external' | 'inactivity' | 'maxLifetime';\n type: 'expired';\n }\n | {\n id: string;\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n startedAt: number;\n type: 'renewed';\n };\n\nexport interface BkOTStartViewOptions {\n attributes?: Attributes;\n name?: string;\n url?: string;\n}\n\nexport interface BkOTUserContext {\n /** 由接入方传入并由 SDK 原样透传;SDK 不 hash、不脱敏、不改写。 */\n id?: string;\n}\n\nexport type BkOTViewCommand =\n | { name: string; type: 'set_name' }\n | { options: BkOTStartViewOptions; type: 'start' }\n | { time: number; type: 'set_loading_time' };\n\nexport interface BkOTViewContext {\n attributes?: Attributes;\n id?: string;\n loadingType?: 'bf_cache' | 'initial_load' | 'route_change' | 'session_renewal';\n name?: string;\n startedAt?: number;\n url?: string;\n urlPathGroup?: string;\n}\n\nconst PROTECTED_VIEW_ATTRIBUTE_KEYS = new Set<string>([...Object.values(RUM_ATTRIBUTES), RUM_SAMPLED_ATTRIBUTE]);\n\n/**\n * 管理 SDK 运行期公共属性快照。\n *\n * 这里仅保存“后续事件都需要带上”的低成本上下文,Session/View/用户切换时更新快照,\n * 插件发 span/log 时读取当前快照即可,避免每个插件重复维护公共字段。\n */\nexport class RumRuntimeContext {\n private readonly actionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly attributes: Attributes = {};\n private readonly queuedViewCommands: BkOTViewCommand[] = [];\n\n // 保存关联关系的生效区间,供延迟到达的 PerformanceEntry 按事件时间回查原 Session/View/Action。\n private recordAssociation = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n changedAt: number,\n active: boolean,\n ) => {\n const previous = history.at(-1);\n if (previous && previous.endedAt === undefined) previous.endedAt = Math.max(previous.startedAt, changedAt);\n if (active) {\n const associationAttributes: Attributes = {};\n for (const key of keys) {\n const value = this.attributes[key];\n if (value !== undefined) associationAttributes[key] = value;\n }\n history.push({ attributes: associationAttributes, startedAt: changedAt });\n }\n if (history.length > 100) history.splice(0, history.length - 100);\n };\n private readonly sessionHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n private readonly sessionLifecycleListeners = new Set<(event: BkOTSessionLifecycleEvent) => void>();\n private readonly setOptionalAttribute = (key: string, value: string | undefined) => {\n if (value === undefined) {\n delete this.attributes[key];\n return;\n }\n this.attributes[key] = value;\n };\n private user: BkOTUserContext = {};\n private view: BkOTViewContext = {};\n private readonly viewAttributeKeys = new Set<string>();\n private readonly viewChangeListeners = new Set<(view: BkOTViewContext) => void>();\n private readonly viewCommandListeners = new Set<(command: BkOTViewCommand) => void>();\n\n private readonly viewHistory: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }> = [];\n\n private readonly viewScopedAttributeKeys = new Set<string>();\n\n public dispatchViewCommand = (command: BkOTViewCommand) => {\n // autoStart 下调用方可能早于 page-view 插件发命令,先排队可避免 startView/setViewName 丢失。\n if (this.viewCommandListeners.size === 0) {\n this.queuedViewCommands.push(command);\n return;\n }\n this.viewCommandListeners.forEach(listener => listener(command));\n };\n\n public getAttributes = (): Attributes => ({ ...this.attributes });\n\n public getAttributesAt = (timestamp: number): Attributes => {\n if (!Number.isFinite(timestamp)) return this.getAttributes();\n const result = this.getAttributes();\n const applyHistory = (\n history: Array<{ attributes: Attributes; endedAt?: number; startedAt: number }>,\n keys: Iterable<string>,\n ) => {\n // 先移除当前关联字段,再用目标时间所在的历史快照覆盖,避免把旧事件错误归到当前 View。\n for (const key of keys) delete result[key];\n const matched = [...history]\n .reverse()\n .find(entry => entry.startedAt <= timestamp && (entry.endedAt === undefined || timestamp < entry.endedAt));\n if (matched) Object.assign(result, matched.attributes);\n };\n applyHistory(this.sessionHistory, [\n RUM_ATTRIBUTES.sessionId,\n RUM_ATTRIBUTES.sessionHasReplay,\n RUM_SAMPLED_ATTRIBUTE,\n ]);\n applyHistory(this.viewHistory, this.viewScopedAttributeKeys);\n applyHistory(this.actionHistory, [RUM_ATTRIBUTES.actionId]);\n return result;\n };\n\n public getUser = (): BkOTUserContext => ({ ...this.user });\n\n public getView = (): BkOTViewContext => ({ ...this.view });\n\n public isSessionActive = () => typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string';\n\n public isViewActive = () => typeof this.attributes[RUM_ATTRIBUTES.viewId] === 'string';\n\n public notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.sessionLifecycleListeners.forEach(listener => listener(event));\n };\n\n public onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) => {\n this.sessionLifecycleListeners.add(listener);\n return () => this.sessionLifecycleListeners.delete(listener);\n };\n\n public onViewChange = (listener: (view: BkOTViewContext) => void) => {\n this.viewChangeListeners.add(listener);\n if (this.view.id) listener(this.getView());\n return () => this.viewChangeListeners.delete(listener);\n };\n\n public onViewCommand = (listener: (command: BkOTViewCommand) => void) => {\n this.viewCommandListeners.add(listener);\n while (this.queuedViewCommands.length) {\n listener(this.queuedViewCommands.shift()!);\n }\n return () => this.viewCommandListeners.delete(listener);\n };\n\n public setAttributes = (attributes: Attributes, changedAt = Date.now()) => {\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined) {\n delete this.attributes[key];\n continue;\n }\n this.attributes[key] = value;\n }\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n this.recordAssociation(\n this.sessionHistory,\n [RUM_ATTRIBUTES.sessionId, RUM_ATTRIBUTES.sessionHasReplay, RUM_SAMPLED_ATTRIBUTE],\n changedAt,\n typeof this.attributes[RUM_ATTRIBUTES.sessionId] === 'string',\n );\n }\n if (RUM_ATTRIBUTES.actionId in attributes) {\n const actionId = this.attributes[RUM_ATTRIBUTES.actionId];\n this.recordAssociation(\n this.actionHistory,\n [RUM_ATTRIBUTES.actionId],\n changedAt,\n Array.isArray(actionId) ? actionId.length > 0 : typeof actionId === 'string',\n );\n }\n };\n\n public setUser = (user: BkOTUserContext) => {\n this.user = {\n ...this.user,\n ...user,\n };\n\n if ('id' in user) {\n if (user.id === undefined) {\n delete this.attributes[RUM_ATTRIBUTES.userId];\n return;\n }\n this.attributes[RUM_ATTRIBUTES.userId] = user.id;\n }\n };\n\n public setView = (view: BkOTViewContext, changedAt = view.startedAt ?? Date.now()) => {\n this.viewAttributeKeys.forEach(key => delete this.attributes[key]);\n this.viewAttributeKeys.clear();\n const viewAttributes: Attributes = {};\n for (const [key, value] of Object.entries(view.attributes ?? {})) {\n // 自定义 View 属性不能覆盖 SDK 维护的关联、采样和语义字段。\n if (value === undefined || PROTECTED_VIEW_ATTRIBUTE_KEYS.has(key)) continue;\n viewAttributes[key] = value;\n this.attributes[key] = value;\n this.viewAttributeKeys.add(key);\n this.viewScopedAttributeKeys.add(key);\n }\n this.view = { ...view, attributes: viewAttributes };\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewId, view.id);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrl, view.url);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewUrlPathGroup, view.urlPathGroup);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewLoadingType, view.loadingType);\n this.setOptionalAttribute(RUM_ATTRIBUTES.viewName, view.name);\n [\n RUM_ATTRIBUTES.viewId,\n RUM_ATTRIBUTES.viewUrl,\n RUM_ATTRIBUTES.viewUrlPathGroup,\n RUM_ATTRIBUTES.viewLoadingType,\n RUM_ATTRIBUTES.viewName,\n ].forEach(key => this.viewScopedAttributeKeys.add(key));\n this.recordAssociation(\n this.viewHistory,\n this.viewScopedAttributeKeys,\n changedAt,\n typeof view.id === 'string' && view.id.length > 0,\n );\n this.viewChangeListeners.forEach(listener => listener(this.getView()));\n };\n\n public constructor(initialAttributes: Attributes = {}, user?: BkOTUserContext) {\n this.setAttributes(initialAttributes);\n if (user) {\n this.setUser(user);\n }\n }\n}\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 生成随机 ID。优先用 `crypto.randomUUID`(standards-compliant UUID v4),不可用时回退到\n * `${prefix}-${timestamp}-${rand}` 形式。\n *\n * 注意:fallback 实现使用 Math.random,**不是密码学安全**,仅适用于设备/会话/事件标识等场景,\n * 不要复用此函数生成 token、CSRF 防护等安全相关的值。\n */\nexport const createRandomId = (prefix: string) => {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n};\n","import type { BkOTRuntimeContext } from './plugin';\n\nexport type BkOTSignal = 'logs' | 'metrics' | 'traces';\n\nexport const shouldRecordSignal = (context: BkOTRuntimeContext, signal: BkOTSignal) =>\n context.isSampled() && context.config.transport[signal] !== false;\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from './attributes';\n\nimport type { Attributes } from '@opentelemetry/api';\n\n// 所有插件通过同一分类模型生成检索字段,保证错误、资源、Action 等信号可按统一维度聚合。\nexport const RUM_SPAN_RESULT = {\n abort: 'abort',\n error: 'error',\n success: 'success',\n timeout: 'timeout',\n warning: 'warning',\n} as const;\n\nexport const RUM_SPAN_TYPE = {\n action: 'action',\n custom: 'custom',\n document: 'document',\n error: 'error',\n longtask: 'longtask',\n resource: 'resource',\n route: 'route',\n vital: 'vital',\n} as const;\n\nexport interface RumResourceSpanInput {\n duration?: number;\n initiatorType?: string;\n method?: string;\n networkError?: boolean;\n responseStatus?: number;\n transferSize?: number;\n url: string;\n urlPathTemplate?: string;\n}\n\nexport interface RumSpanClassification {\n duration?: number;\n errorType?: string;\n eventLabel?: string;\n result?: RumSpanResult;\n spanSubtype: string;\n spanType: RumSpanType;\n targetLabel?: string;\n targetValue?: boolean | number | string;\n traceScene?: string;\n}\n\nexport type RumSpanResult = (typeof RUM_SPAN_RESULT)[keyof typeof RUM_SPAN_RESULT];\n\nexport type RumSpanType = (typeof RUM_SPAN_TYPE)[keyof typeof RUM_SPAN_TYPE];\n\nexport interface RumUrlTarget {\n domain?: string;\n label?: string;\n pathTemplate?: string;\n}\n\nexport const getDurationBucket = (duration: number) => {\n if (duration < 100) return '<100ms';\n if (duration < 500) return '100~500ms';\n if (duration < 2000) return '500ms~2s';\n return '>2s';\n};\n\nexport const getStatusClass = (statusCode: number) => `${Math.trunc(statusCode / 100)}xx`;\n\nexport const getHttpResult = (statusCode: number | undefined): RumSpanResult => {\n if (statusCode === 408 || statusCode === 504) return RUM_SPAN_RESULT.timeout;\n if (typeof statusCode === 'number' && statusCode >= 400) return RUM_SPAN_RESULT.error;\n return RUM_SPAN_RESULT.success;\n};\n\nexport const getHttpErrorType = (statusCode: number | undefined) => {\n if (statusCode === 408 || statusCode === 504) return 'network_timeout';\n if (typeof statusCode !== 'number' || statusCode < 400) return 'none';\n return statusCode >= 500 ? 'http_5xx' : 'http_4xx';\n};\n\nexport const getVitalResult = (rating: string | undefined): RumSpanResult =>\n rating === 'good' ? RUM_SPAN_RESULT.success : RUM_SPAN_RESULT.warning;\n\nexport const getUrlTarget = (url: string | undefined, pathTemplate?: string): RumUrlTarget => {\n if (!url) {\n return {};\n }\n try {\n const parsed = new URL(url, typeof location === 'undefined' ? 'http://localhost' : location.href);\n // 有路由模板时优先用模板生成 target label,降低动态路径带来的检索维度基数。\n const normalizedPath = pathTemplate ?? parsed.pathname;\n return {\n domain: parsed.host,\n label: `${parsed.host}${normalizedPath}`,\n pathTemplate: normalizedPath,\n };\n } catch {\n return {\n label: url,\n pathTemplate: pathTemplate ?? url,\n };\n }\n};\n\nexport const buildRumSpanAttributes = ({\n duration,\n errorType = 'none',\n eventLabel,\n result = RUM_SPAN_RESULT.success,\n spanSubtype,\n spanType,\n targetLabel,\n targetValue,\n traceScene,\n}: RumSpanClassification): Attributes => ({\n [RUM_ATTRIBUTES.spanType]: spanType,\n [RUM_ATTRIBUTES.spanSubtype]: spanSubtype,\n [RUM_ATTRIBUTES.result]: result,\n [RUM_ATTRIBUTES.errorType]: errorType,\n ...(duration !== undefined ? { [RUM_ATTRIBUTES.durationBucket]: getDurationBucket(duration) } : {}),\n ...(eventLabel !== undefined ? { [RUM_ATTRIBUTES.eventLabel]: eventLabel } : {}),\n ...(targetLabel !== undefined ? { [RUM_ATTRIBUTES.targetLabel]: targetLabel } : {}),\n ...(targetValue !== undefined ? { [RUM_ATTRIBUTES.targetValue]: targetValue } : {}),\n ...(traceScene !== undefined ? { [RUM_ATTRIBUTES.traceScene]: traceScene } : {}),\n});\n\nexport const buildRumResourceSpanAttributes = ({\n duration,\n initiatorType = 'other',\n method,\n networkError = false,\n responseStatus,\n transferSize,\n url,\n urlPathTemplate,\n}: RumResourceSpanInput): Attributes => {\n const target = getUrlTarget(url, urlPathTemplate);\n const isRequest = initiatorType === 'fetch' || initiatorType === 'xhr';\n const result = networkError\n ? RUM_SPAN_RESULT.error\n : typeof responseStatus === 'number'\n ? getHttpResult(responseStatus)\n : RUM_SPAN_RESULT.success;\n const normalizedMethod = method?.toUpperCase();\n return {\n ...buildRumSpanAttributes({\n duration,\n errorType: networkError\n ? 'network'\n : typeof responseStatus === 'number'\n ? getHttpErrorType(responseStatus)\n : 'none',\n eventLabel: isRequest ? '请求资源' : '静态资源',\n result,\n spanSubtype: initiatorType,\n spanType: RUM_SPAN_TYPE.resource,\n targetLabel: target.label ?? url,\n targetValue: isRequest ? responseStatus : transferSize,\n }),\n ...(target.domain ? { [RUM_ATTRIBUTES.targetDomain]: target.domain } : {}),\n ...(target.pathTemplate ? { [RUM_ATTRIBUTES.targetPathTemplate]: target.pathTemplate } : {}),\n 'resource.type': initiatorType,\n 'resource.url': url,\n ...(duration !== undefined ? { 'resource.duration': duration } : {}),\n ...(normalizedMethod\n ? {\n 'http.request.method': normalizedMethod,\n 'resource.method': normalizedMethod,\n }\n : {}),\n ...(typeof transferSize === 'number' ? { 'resource.size': transferSize } : {}),\n ...(typeof responseStatus === 'number'\n ? {\n 'http.response.status_code': responseStatus,\n 'resource.status_code': responseStatus,\n ...(responseStatus > 0 ? { [RUM_ATTRIBUTES.statusClass]: getStatusClass(responseStatus) } : {}),\n }\n : {}),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createRandomId } from '../core/id';\nimport { shouldRecordSignal } from '../core/signal';\nimport { matchesUrlList } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTActivityEvent } from '../core/action-activity';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 用户点击采集以“点击后产生的副作用”为 Action 生命周期:关联请求、资源、长任务、DOM 变化和错误,\n * 在活动安静后结束。完成后的 Action 暂缓至连击窗口结束再上报,以便把三次相近点击合并为一次 rage click。\n */\nconst ACTION_VALIDATION_DELAY = 100;\nconst ACTION_END_DELAY = 100;\nconst CLICK_CHAIN_DURATION = 1_000;\nconst CLICK_CHAIN_MAX_DISTANCE = 100;\nconst DEFAULT_ACTION_MAX_DURATION = 10_000;\nconst DEFAULT_ACTION_NAME_ATTRIBUTE = 'data-bk-action-name';\nconst MAX_NAME_LENGTH = 100;\nconst MAX_PARENTS_TO_CONSIDER = 10;\n\nexport interface BkOTActionPluginOptions {\n actionNameAttribute?: string;\n maxDuration?: number;\n /** 默认仅采集显式 data 属性;设为 text 才会读取可访问名称和 DOM 文本。 */\n nameCollection?: 'explicit' | 'text';\n}\n\ninterface ActionCounts {\n error: number;\n longTask: number;\n resource: number;\n}\n\ntype ActionFrustrationType = 'dead_click' | 'error_click' | 'rage_click';\n\ninterface ActiveAction {\n completed?: { endTime: number; hasLoadingTime: boolean };\n counts: ActionCounts;\n discardedByRage?: boolean;\n frustrations: Set<ActionFrustrationType>;\n hadActivity: boolean;\n id: string;\n lastActivityTime: number;\n maxDurationTimer: ReturnType<typeof setTimeout>;\n pendingRequests: Set<string>;\n quietTimer?: ReturnType<typeof setTimeout>;\n rageSources?: ActiveAction[];\n reportTimer?: ReturnType<typeof setTimeout>;\n runtimeAttributes: Attributes;\n startTime: number;\n target: Element;\n targetName: string;\n targetTag: string;\n userActivity: { input: boolean; scroll: boolean; selection: boolean };\n validationTimer: ReturnType<typeof setTimeout>;\n}\n\ninterface RecentClick {\n action: ActiveAction;\n target: Element;\n x: number;\n y: number;\n}\n\nconst normalizeText = (value: string) => value.replace(/\\s+/g, ' ').trim().slice(0, MAX_NAME_LENGTH);\n\nconst getParentElement = (element: Element) => {\n if (element.parentElement) {\n return element.parentElement;\n }\n const root = element.getRootNode?.();\n return typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.host : null;\n};\n\nconst findAttributeOnAncestors = (element: Element, attribute: string) => {\n let current: Element | null = element;\n while (current) {\n const value = current.getAttribute(attribute);\n if (value) {\n return normalizeText(value);\n }\n current = getParentElement(current);\n }\n return undefined;\n};\n\nconst getElementText = (element: Element) =>\n (element as HTMLElement).isContentEditable ? '' : normalizeText(element.textContent ?? '');\n\nconst getPriorityName = (element: Element) => {\n const formElement = element as Element & {\n labels?: HTMLLabelElement[] | NodeListOf<HTMLLabelElement>;\n value?: string;\n };\n const label = formElement.labels?.[0];\n if (label) {\n const labelText = getElementText(label);\n if (labelText) return labelText;\n }\n\n const tagName = element.tagName.toLowerCase();\n const inputType = element.getAttribute('type')?.toLowerCase();\n if (tagName === 'input' && ['button', 'reset', 'submit'].includes(inputType ?? '')) {\n const value = normalizeText(formElement.value ?? '');\n if (value) return value;\n }\n\n if (tagName === 'button' || tagName === 'label' || element.getAttribute('role') === 'button') {\n const text = getElementText(element);\n if (text) return text;\n }\n\n for (const attribute of ['aria-label', 'alt', 'name', 'title', 'placeholder']) {\n const value = normalizeText(element.getAttribute(attribute) ?? '');\n if (value) return value;\n }\n\n return undefined;\n};\n\nexport const getActionNameFromElement = (\n target: Element,\n actionNameAttribute = DEFAULT_ACTION_NAME_ATTRIBUTE,\n includeText = false,\n): string => {\n const explicitName = findAttributeOnAncestors(target, actionNameAttribute);\n if (explicitName) {\n return explicitName;\n }\n if (!includeText) return target.tagName.toLowerCase();\n\n let current: Element | null = target;\n let parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const priorityName = getPriorityName(current);\n if (priorityName) return priorityName;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n current = target;\n parentCount = 0;\n while (current && parentCount <= MAX_PARENTS_TO_CONSIDER) {\n const tagName = current.tagName.toLowerCase();\n if (tagName === 'body' || tagName === 'html' || tagName === 'head') break;\n const text = getElementText(current);\n if (text) return text;\n if (tagName === 'form') break;\n current = getParentElement(current);\n parentCount += 1;\n }\n\n return target.tagName.toLowerCase();\n};\n\nconst getElement = (event: Event) => {\n const composedTarget = event.composedPath?.()[0];\n if (composedTarget instanceof Element) return composedTarget;\n return event.target instanceof Element ? event.target : undefined;\n};\n\nconst toEpochTime = (event: Event) => {\n const eventTime = event.timeStamp;\n if (!Number.isFinite(eventTime) || eventTime <= 0) return Date.now();\n if (eventTime > 1_000_000_000_000) return eventTime;\n return typeof performance === 'undefined' ? Date.now() : performance.timeOrigin + eventTime;\n};\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\n\nexport const isDeadClickExcluded = (target: Element) => {\n let current: Element | null = target;\n if (current.tagName.toLowerCase() === 'label' && current.hasAttribute('for')) {\n current = document.getElementById(current.getAttribute('for') ?? '');\n }\n\n while (current) {\n const tagName = current.tagName.toLowerCase();\n if (\n current.hasAttribute('contenteditable') ||\n tagName === 'canvas' ||\n tagName === 'select' ||\n tagName === 'textarea'\n ) {\n return true;\n }\n if (tagName === 'a' && current.hasAttribute('href')) return true;\n if (tagName === 'input') {\n return !['button', 'checkbox', 'radio', 'range', 'reset', 'submit'].includes(\n current.getAttribute('type')?.toLowerCase() ?? 'text',\n );\n }\n current = getParentElement(current);\n }\n return false;\n};\n\nexport const createActionPlugin = (options: BkOTActionPluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeActions = new Map<string, ActiveAction>();\n const pendingActions = new Set<ActiveAction>();\n const actionNameAttribute = options.actionNameAttribute || DEFAULT_ACTION_NAME_ATTRIBUTE;\n const maxDuration = positiveNumber(options.maxDuration, DEFAULT_ACTION_MAX_DURATION);\n let recentClicks: RecentClick[] = [];\n let runtimeContext: BkOTRuntimeContext | undefined;\n\n const updateRuntimeActionIds = () => {\n runtimeContext?.setRuntimeAttributes({\n [RUM_ATTRIBUTES.actionId]: activeActions.size ? [...activeActions.keys()] : undefined,\n });\n };\n\n const isDeadAction = (action: ActiveAction) =>\n !action.hadActivity &&\n !action.userActivity.input &&\n !action.userActivity.scroll &&\n !action.userActivity.selection &&\n !isDeadClickExcluded(action.target);\n\n const reportAction = (action: ActiveAction) => {\n if (!pendingActions.delete(action) || !runtimeContext || !action.completed || action.discardedByRage) return;\n clearTimeout(action.reportTimer);\n if (action.counts.error) action.frustrations.add('error_click');\n if ((action.rageSources ?? [action]).some(isDeadAction)) action.frustrations.add('dead_click');\n const frustrationTypes = ['rage_click', 'dead_click', 'error_click'].filter(type =>\n action.frustrations.has(type as ActionFrustrationType),\n ) as ActionFrustrationType[];\n const { endTime, hasLoadingTime } = action.completed;\n const loadingTime = Math.max(0, endTime - action.startTime);\n const hasWarning = action.counts.error > 0 || frustrationTypes.length > 0;\n const span = runtimeContext.startSpan(\n 'action.click',\n {\n ...runtimeContext.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n duration: hasLoadingTime ? loadingTime : undefined,\n errorType: action.counts.error ? 'js' : frustrationTypes.length ? 'frustration' : 'none',\n eventLabel: '用户操作',\n result: hasWarning ? RUM_SPAN_RESULT.warning : RUM_SPAN_RESULT.success,\n spanSubtype: 'click',\n spanType: RUM_SPAN_TYPE.action,\n targetLabel: action.targetName,\n targetValue: hasLoadingTime ? loadingTime : undefined,\n traceScene: 'user_action',\n }),\n ...action.runtimeAttributes,\n [RUM_ATTRIBUTES.actionId]: action.id,\n [RUM_ATTRIBUTES.actionType]: 'click',\n [RUM_ATTRIBUTES.actionName]: `Click on ${action.targetName}`,\n [RUM_ATTRIBUTES.actionTargetName]: action.targetName,\n [RUM_ATTRIBUTES.actionTargetTag]: action.targetTag,\n [RUM_ATTRIBUTES.actionResourceCount]: action.counts.resource,\n [RUM_ATTRIBUTES.actionErrorCount]: action.counts.error,\n [RUM_ATTRIBUTES.actionLongTaskCount]: action.counts.longTask,\n [RUM_ATTRIBUTES.actionFrustrationCount]: frustrationTypes.length,\n ...(frustrationTypes.length ? { [RUM_ATTRIBUTES.actionFrustrationType]: frustrationTypes } : {}),\n ...(hasLoadingTime ? { [RUM_ATTRIBUTES.actionLoadingTime]: loadingTime } : {}),\n },\n { startTime: action.startTime },\n );\n span.end(hasLoadingTime ? endTime : action.startTime);\n };\n\n const finishAction = (action: ActiveAction, endTime: number, hasLoadingTime: boolean) => {\n if (!activeActions.delete(action.id) || !runtimeContext) return;\n clearTimeout(action.validationTimer);\n clearTimeout(action.quietTimer);\n clearTimeout(action.maxDurationTimer);\n updateRuntimeActionIds();\n action.completed = { endTime, hasLoadingTime };\n pendingActions.add(action);\n // 等待连击窗口关闭后再落数,否则前两次点击已上报便无法合并为 rage click。\n const reportDelay = Math.max(0, action.startTime + CLICK_CHAIN_DURATION - Date.now());\n action.reportTimer = setTimeout(() => reportAction(action), reportDelay);\n };\n\n const flushPendingActions = () => {\n [...pendingActions].forEach(reportAction);\n };\n\n const scheduleActionEnd = (action: ActiveAction) => {\n clearTimeout(action.quietTimer);\n if (!action.hadActivity || action.pendingRequests.size) return;\n const delay = Math.max(0, action.lastActivityTime + ACTION_END_DELAY - Date.now());\n action.quietTimer = setTimeout(() => finishAction(action, action.lastActivityTime, true), delay);\n };\n\n const markActivity = (action: ActiveAction, time: number) => {\n action.hadActivity = true;\n action.lastActivityTime = Math.max(action.lastActivityTime, time);\n scheduleActionEnd(action);\n };\n\n const isInActionWindow = (action: ActiveAction, time: number) =>\n time >= action.startTime && time <= action.startTime + maxDuration;\n\n const onActivity = (event: BkOTActivityEvent) => {\n for (const action of activeActions.values()) {\n if (event.type === 'request_start') {\n if (event.url && matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)) continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.pendingRequests.add(event.id);\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'request_end') {\n if (!action.pendingRequests.delete(event.id)) continue;\n action.counts.resource += 1;\n markActivity(action, event.time);\n continue;\n }\n if (event.type === 'long_task' || event.type === 'resource') {\n if (\n event.type === 'resource' &&\n event.url &&\n matchesUrlList(runtimeContext?.config.view?.excludedActivityUrls, event.url)\n ) {\n continue;\n }\n if (\n event.startTime > action.startTime + maxDuration ||\n (event.type === 'resource' ? event.startTime < action.startTime : event.endTime < action.startTime)\n ) {\n continue;\n }\n action.counts[event.type === 'long_task' ? 'longTask' : 'resource'] += 1;\n markActivity(action, event.endTime);\n continue;\n }\n if (event.type === 'dom_mutation' || event.type === 'window_open') {\n if (isInActionWindow(action, event.time)) markActivity(action, event.time);\n continue;\n }\n if (event.type !== 'error') continue;\n if (!isInActionWindow(action, event.time)) continue;\n action.counts.error += 1;\n markActivity(action, event.time);\n }\n };\n\n const finishActiveActions = (endTime = Date.now()) => {\n [...activeActions.values()].forEach(action => finishAction(action, endTime, action.hadActivity));\n };\n\n const registerClick = (action: ActiveAction, event: Event) => {\n const x = 'clientX' in event && typeof event.clientX === 'number' ? event.clientX : 0;\n const y = 'clientY' in event && typeof event.clientY === 'number' ? event.clientY : 0;\n recentClicks = recentClicks.filter(click => action.startTime - click.action.startTime <= CLICK_CHAIN_DURATION);\n const lastClick = recentClicks.at(-1);\n const hasInterruptedChain = recentClicks.some(\n click => click.action.userActivity.scroll || click.action.userActivity.selection,\n );\n // 只有同一目标附近、未被滚动或选中文本打断的连续点击才视为同一连击链。\n const isSimilar =\n lastClick?.target === action.target && Math.hypot(lastClick.x - x, lastClick.y - y) <= CLICK_CHAIN_MAX_DISTANCE;\n if (hasInterruptedChain || (lastClick && !isSimilar)) recentClicks = [];\n recentClicks.push({ action, target: action.target, x, y });\n if (\n recentClicks.length >= 3 &&\n action.startTime - recentClicks[recentClicks.length - 3].action.startTime <= CLICK_CHAIN_DURATION\n ) {\n const rageSources = recentClicks.slice(-3).map(click => click.action);\n rageSources.slice(0, -1).forEach(source => {\n source.discardedByRage = true;\n });\n action.frustrations.add('rage_click');\n action.rageSources = rageSources;\n recentClicks = [];\n }\n };\n\n return {\n name: 'action',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n runtimeContext = context;\n\n const listenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n\n const startAction = (event: Event) => {\n if (!shouldRecordSignal(context, 'traces')) return;\n if ('button' in event && typeof event.button === 'number' && event.button !== 0) return;\n const element = getElement(event);\n if (!element) return;\n const startTime = toEpochTime(event);\n const id = createRandomId('action');\n const action: ActiveAction = {\n counts: { error: 0, longTask: 0, resource: 0 },\n frustrations: new Set(),\n hadActivity: false,\n id,\n lastActivityTime: startTime,\n maxDurationTimer: setTimeout(() => finishAction(action, startTime + maxDuration, true), maxDuration),\n pendingRequests: new Set(),\n runtimeAttributes: context.getRuntimeAttributes(),\n startTime,\n target: element,\n targetName: getActionNameFromElement(element, actionNameAttribute, options.nameCollection === 'text'),\n targetTag: element.tagName.toLowerCase(),\n userActivity: { input: false, scroll: false, selection: false },\n validationTimer: setTimeout(() => {\n if (!action.hadActivity) finishAction(action, action.startTime, false);\n }, ACTION_VALIDATION_DELAY),\n };\n activeActions.set(id, action);\n registerClick(action, event);\n updateRuntimeActionIds();\n };\n\n window.addEventListener('click', startAction, listenerOptions);\n teardownCallbacks.push(() => window.removeEventListener('click', startAction, listenerOptions));\n\n const unsubscribeActivity = context.onActivity(onActivity);\n const unsubscribeSession = context.onSessionLifecycle(event => {\n if (event.type !== 'expired') return;\n finishActiveActions(event.expiredAt);\n flushPendingActions();\n });\n teardownCallbacks.push(unsubscribeActivity, unsubscribeSession);\n\n const finishOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n finishActiveActions();\n flushPendingActions();\n }\n };\n const finishOnPageHide = () => {\n finishActiveActions();\n flushPendingActions();\n };\n window.addEventListener('pagehide', finishOnPageHide);\n document.addEventListener('visibilitychange', finishOnHidden);\n teardownCallbacks.push(\n () => window.removeEventListener('pagehide', finishOnPageHide),\n () => document.removeEventListener('visibilitychange', finishOnHidden),\n );\n\n const markUserActivity = (type: keyof ActiveAction['userActivity']) => {\n const actions = new Set([\n ...activeActions.values(),\n ...pendingActions,\n ...recentClicks.map(click => click.action),\n ]);\n actions.forEach(action => {\n action.userActivity[type] = true;\n // 滚动/选择表明用户仍在正常交互,撤销尚未上报的 rage click 合并。\n if (type !== 'input' && action.frustrations.delete('rage_click')) {\n action.rageSources?.slice(0, -1).forEach(source => {\n source.discardedByRage = false;\n });\n action.rageSources = undefined;\n }\n });\n };\n const onInput = () => markUserActivity('input');\n const onScroll = () => markUserActivity('scroll');\n const onSelectionChange = () => markUserActivity('selection');\n window.addEventListener('input', onInput, listenerOptions);\n window.addEventListener('scroll', onScroll, listenerOptions);\n document.addEventListener('selectionchange', onSelectionChange, listenerOptions);\n teardownCallbacks.push(\n () => window.removeEventListener('input', onInput, listenerOptions),\n () => window.removeEventListener('scroll', onScroll, listenerOptions),\n () => document.removeEventListener('selectionchange', onSelectionChange, listenerOptions),\n );\n },\n shutdown() {\n finishActiveActions();\n flushPendingActions();\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n recentClicks = [];\n runtimeContext = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nexport const EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE = 'data-bk-excluded-activity-mutations';\n\nconst isExcludedMutation = (mutation: MutationRecord) => {\n const target = mutation.type === 'characterData' ? mutation.target.parentElement : mutation.target;\n return target instanceof Element && Boolean(target.closest(`[${EXCLUDED_ACTIVITY_MUTATIONS_ATTRIBUTE}]`));\n};\n\n/**\n * 页面级 DOM 活动只观察一次,再通过运行时活动总线分发给 View、Action 和白屏检测。\n */\nexport const createActivityPlugin = (): BkOTPlugin => {\n let mutationObserver: MutationObserver | undefined;\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'activity',\n init(context) {\n if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {\n mutationObserver = new MutationObserver(mutations => {\n if (mutations.length && !mutations.every(isExcludedMutation)) {\n context.notifyActivity({ time: Date.now(), type: 'dom_mutation' });\n }\n });\n mutationObserver.observe(document, { attributes: true, characterData: true, childList: true, subtree: true });\n }\n\n if (typeof window === 'undefined') return;\n\n if (typeof window.open === 'function') {\n const originalOpen = window.open;\n const patchedOpen = ((...args: Parameters<typeof window.open>) => {\n context.notifyActivity({ time: Date.now(), type: 'window_open' });\n return Reflect.apply(originalOpen, window, args);\n }) as typeof window.open;\n window.open = patchedOpen;\n teardownCallbacks.push(() => {\n if (window.open === patchedOpen) window.open = originalOpen;\n });\n }\n\n },\n shutdown() {\n mutationObserver?.disconnect();\n mutationObserver = undefined;\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { matchesUrlList } from './url';\n\nimport type { BkOTActivityEvent } from './action-activity';\nimport type { BkOTRuntimeContext } from './plugin';\n\nexport const PAGE_ACTIVITY_VALIDATION_DELAY = 100;\nexport const PAGE_ACTIVITY_END_DELAY = 100;\n\nexport type PageActivityEnd = { endTime: number; hadActivity: true } | { hadActivity: false };\n\nconst isActivityUrlExcluded = (context: BkOTRuntimeContext, url: string | undefined) =>\n Boolean(url && matchesUrlList(context.config.view.excludedActivityUrls, url));\n\n/**\n * 等待 View/Action 启动后的请求、静态资源和 DOM 变更全部安静 100ms。\n * 请求用 id 精确配对,避免并发请求提前结束等待。\n */\nexport const waitForPageActivityEnd = (\n context: BkOTRuntimeContext,\n startTime: number,\n callback: (result: PageActivityEnd) => void,\n) => {\n const pendingRequests = new Set<string>();\n let completed = false;\n let hadActivity = false;\n let lastActivityTime = startTime;\n let quietTimer: ReturnType<typeof setTimeout> | undefined;\n\n const complete = (result: PageActivityEnd) => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n callback(result);\n };\n\n const scheduleEnd = () => {\n clearTimeout(quietTimer);\n if (!hadActivity || pendingRequests.size) return;\n quietTimer = setTimeout(\n () => complete({ endTime: lastActivityTime, hadActivity: true }),\n Math.max(0, lastActivityTime + PAGE_ACTIVITY_END_DELAY - Date.now()),\n );\n };\n\n const markActivity = (time: number) => {\n hadActivity = true;\n lastActivityTime = Math.max(lastActivityTime, time);\n scheduleEnd();\n };\n\n const onActivity = (event: BkOTActivityEvent) => {\n if (event.type === 'error' || event.type === 'long_task') return;\n if (event.type === 'request_start') {\n if (event.time < startTime || isActivityUrlExcluded(context, event.url)) return;\n pendingRequests.add(event.id);\n markActivity(event.time);\n return;\n }\n if (event.type === 'request_end') {\n if (!pendingRequests.delete(event.id)) return;\n markActivity(event.time);\n return;\n }\n if (event.type === 'resource') {\n if (event.endTime < startTime || isActivityUrlExcluded(context, event.url)) return;\n markActivity(event.endTime);\n return;\n }\n if (event.time >= startTime) markActivity(event.time);\n };\n\n const unsubscribe = context.onActivity(onActivity);\n const validationTimer = setTimeout(() => {\n if (!hadActivity) complete({ hadActivity: false });\n }, PAGE_ACTIVITY_VALIDATION_DELAY);\n\n return () => {\n if (completed) return;\n completed = true;\n clearTimeout(validationTimer);\n clearTimeout(quietTimer);\n unsubscribe();\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { Attributes } from '@opentelemetry/api';\n\nconst DYNAMIC_PATH_SEGMENT = /^(?:\\d+|[0-9a-f]{8}-[0-9a-f-]{27,}|[0-9a-f]{16,})$/i;\n\n// Metric 只能保留低基数维度,常见数字 ID、UUID 和长十六进制段统一折叠为 :id。\nexport const normalizeMetricPathGroup = (pathGroup: string) => {\n const path = pathGroup.split(/[?#]/, 1)[0];\n return path\n .split('/')\n .map(segment => (DYNAMIC_PATH_SEGMENT.test(segment) ? ':id' : segment))\n .join('/');\n};\n\nconst getViewPathGroup = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? normalizeMetricPathGroup(value) : undefined;\n\nconst buildViewPathGroupAttribute = (viewPathGroup: unknown): Attributes => {\n const normalized = getViewPathGroup(viewPathGroup);\n return normalized ? { [RUM_ATTRIBUTES.viewUrlPathGroup]: normalized } : {};\n};\n\nexport const getMetricViewAttributes = (runtimeAttributes: Attributes): Attributes => ({\n ...buildViewPathGroupAttribute(runtimeAttributes[RUM_ATTRIBUTES.viewUrlPathGroup]),\n [RUM_SAMPLED_ATTRIBUTE]: runtimeAttributes[RUM_SAMPLED_ATTRIBUTE] === true,\n});\n\nexport const getInstrumentationSpanRuntimeAttributes = (\n runtimeAttributes: Attributes,\n fallbackViewPathGroup?: string,\n): Attributes => {\n // Span 可保留完整运行时上下文,但 path group 仍规范化,便于后端与 metric 使用同一分组口径。\n const { [RUM_ATTRIBUTES.viewUrlPathGroup]: rawViewPathGroup, ...restAttributes } = runtimeAttributes;\n return {\n ...restAttributes,\n ...buildViewPathGroupAttribute(getViewPathGroup(rawViewPathGroup) ?? fallbackViewPathGroup),\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { shouldRecordSignal } from '../core/signal';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * 通过视口九宫格采样估算空白比例,并在页面稳定后连续确认两次。每个 View 最多上报一次,\n * 避免首屏渲染、路由切换和 loading 状态造成瞬时误报。\n */\nexport interface BkOTBlankScreenPluginOptions {\n /** 页面活动稳定后首次检查的延迟,默认 1000ms。 */\n checkDelay?: number;\n /** 两次空白结果之间的确认间隔,默认 200ms。 */\n confirmationDelay?: number;\n /** loading、骨架屏等不参与空白比例计算的元素。 */\n ignoreSelectors?: string[];\n /** 应用挂载根节点,默认 body。根节点不存在本身会被判为候选白屏。 */\n rootSelector?: string;\n /** 空白采样点比例阈值,默认 0.8。 */\n threshold?: number;\n}\n\ninterface DetectionResult {\n centerElement: string;\n emptyCount: number;\n ignoredCount: number;\n reason: 'empty_viewport' | 'missing_root';\n rootFound: boolean;\n score: number;\n validCount: number;\n}\n\nconst DEFAULT_CHECK_DELAY = 1000;\nconst DEFAULT_CONFIRMATION_DELAY = 200;\nconst DEFAULT_ROOT_SELECTOR = 'body';\nconst DEFAULT_THRESHOLD = 0.8;\nconst DEFAULT_IGNORED_SELECTORS = [\n '[data-loading=\"true\"]',\n '[aria-busy=\"true\"]',\n '.loading',\n '.is-loading',\n '.spinner',\n '.skeleton',\n '.bk-loading',\n];\n\nconst SAMPLE_POINTS: ReadonlyArray<readonly [number, number]> = [\n [0.2, 0.2],\n [0.5, 0.2],\n [0.8, 0.2],\n [0.2, 0.5],\n [0.5, 0.5],\n [0.8, 0.5],\n [0.2, 0.8],\n [0.5, 0.8],\n [0.8, 0.8],\n];\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst boundedRatio = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : fallback;\n\nconst getElementSelector = (element: Element | undefined) => {\n if (!element) return '';\n const tag = element.tagName.toLowerCase();\n return element.id ? `${tag}#${element.id}` : tag;\n};\n\nconst matchesAny = (element: Element, selectors: string[]) =>\n selectors.some(selector => {\n try {\n return element.matches(selector) || Boolean(element.closest(selector));\n } catch {\n return false;\n }\n });\n\nconst isVisuallyPresent = (element: Element) => {\n if (typeof getComputedStyle !== 'function') return true;\n const style = getComputedStyle(element);\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity || 1) > 0;\n};\n\nconst getElementsAtPoint = (x: number, y: number): Element[] => {\n if (typeof document.elementsFromPoint === 'function') return document.elementsFromPoint(x, y);\n const element = document.elementFromPoint(x, y);\n return element ? [element] : [];\n};\n\nexport const detectBlankScreen = (rootSelector: string, ignoredSelectors: string[]): DetectionResult => {\n const root = document.querySelector(rootSelector);\n if (!root) {\n return {\n centerElement: getElementSelector(getElementsAtPoint(window.innerWidth / 2, window.innerHeight / 2)[0]),\n emptyCount: SAMPLE_POINTS.length,\n ignoredCount: 0,\n reason: 'missing_root',\n rootFound: false,\n score: 1,\n validCount: SAMPLE_POINTS.length,\n };\n }\n\n let emptyCount = 0;\n let ignoredCount = 0;\n let centerElement = '';\n // 采样视口九宫格而非遍历 DOM,检测成本固定;被 loading/骨架屏覆盖的点不进入分母。\n for (const [xRatio, yRatio] of SAMPLE_POINTS) {\n const layers = getElementsAtPoint(window.innerWidth * xRatio, window.innerHeight * yRatio);\n if (xRatio === 0.5 && yRatio === 0.5) centerElement = getElementSelector(layers[0]);\n if (layers.some(element => matchesAny(element, ignoredSelectors))) {\n ignoredCount += 1;\n continue;\n }\n const hasContent = layers.some(\n element =>\n element !== root &&\n element !== document.body &&\n element !== document.documentElement &&\n root.contains(element) &&\n isVisuallyPresent(element),\n );\n if (!hasContent) emptyCount += 1;\n }\n\n const validCount = SAMPLE_POINTS.length - ignoredCount;\n return {\n centerElement,\n emptyCount,\n ignoredCount,\n reason: 'empty_viewport',\n rootFound: true,\n score: validCount ? emptyCount / validCount : 0,\n validCount,\n };\n};\n\nexport const createBlankScreenPlugin = (options: BkOTBlankScreenPluginOptions = {}): BkOTPlugin => {\n const checkDelay = nonNegativeNumber(options.checkDelay, DEFAULT_CHECK_DELAY);\n const confirmationDelay = nonNegativeNumber(options.confirmationDelay, DEFAULT_CONFIRMATION_DELAY);\n const rootSelector = options.rootSelector || DEFAULT_ROOT_SELECTOR;\n const threshold = boundedRatio(options.threshold, DEFAULT_THRESHOLD);\n const ignoredSelectors = [...DEFAULT_IGNORED_SELECTORS, ...(options.ignoreSelectors ?? [])];\n let activeViewId: string | undefined;\n let reportedViewId: string | undefined;\n let stopActivityTracking: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeView = () => {};\n let visibilityListener: (() => void) | undefined;\n\n return {\n name: 'blank-screen',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const counter = context.meter.createCounter('browser.blank_screen.count', {\n description: 'Number of confirmed blank screens',\n });\n\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n\n const report = (viewId: string, result: DetectionResult) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n const recordMetrics = shouldRecordSignal(context, 'metrics');\n const recordTrace = shouldRecordSignal(context, 'traces');\n if (!recordMetrics && !recordTrace) return;\n reportedViewId = viewId;\n if (recordMetrics) {\n counter.add(\n 1,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_selector': rootSelector,\n }),\n );\n }\n if (recordTrace) context\n .startSpan('browser.blank_screen', {\n ...buildRumSpanAttributes({\n errorType: 'blank_screen',\n eventLabel: '白屏',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: result.reason,\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: rootSelector,\n targetValue: result.score,\n }),\n 'blank_screen.center_element': result.centerElement,\n 'blank_screen.empty_ratio': result.score,\n 'blank_screen.empty_sample_count': result.emptyCount,\n 'blank_screen.ignored_sample_count': result.ignoredCount,\n 'blank_screen.reason': result.reason,\n 'blank_screen.root_found': result.rootFound,\n 'blank_screen.root_selector': rootSelector,\n 'blank_screen.sample_count': SAMPLE_POINTS.length,\n 'blank_screen.valid_sample_count': result.validCount,\n })\n .end();\n };\n\n const detectAndConfirm = (viewId: string) => {\n if (activeViewId !== viewId || reportedViewId === viewId) return;\n if (document.visibilityState === 'hidden') return;\n let first: DetectionResult;\n try {\n first = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] detection failed:', error);\n return;\n }\n if (!first.validCount || first.score < threshold) return;\n timer = setTimeout(() => {\n if (activeViewId !== viewId || document.visibilityState === 'hidden') return;\n let confirmed: DetectionResult;\n try {\n confirmed = detectBlankScreen(rootSelector, ignoredSelectors);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot][blank-screen] confirmation failed:', error);\n return;\n }\n if (confirmed.validCount && confirmed.score >= threshold) report(viewId, confirmed);\n }, confirmationDelay);\n };\n\n const schedule = (viewId: string) => {\n clearTimer();\n if (\n document.visibilityState === 'hidden' ||\n (!shouldRecordSignal(context, 'metrics') && !shouldRecordSignal(context, 'traces'))\n )\n return;\n timer = setTimeout(() => detectAndConfirm(viewId), checkDelay);\n };\n\n unsubscribeView = context.onViewChange(view => {\n if (view.id === activeViewId) return;\n clearTimer();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n activeViewId = view.id;\n reportedViewId = undefined;\n if (view.id) {\n const viewId = view.id;\n // 页面活动结束后再延迟检测,避免把正常渲染过程中的短暂空白误报为白屏。\n stopActivityTracking = waitForPageActivityEnd(context, Date.now(), () => {\n stopActivityTracking = undefined;\n schedule(viewId);\n });\n }\n });\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n clearTimer();\n } else if (activeViewId && reportedViewId !== activeViewId) {\n schedule(activeViewId);\n }\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n },\n shutdown() {\n clearTimeout(timer);\n timer = undefined;\n unsubscribeView();\n stopActivityTracking?.();\n stopActivityTracking = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n activeViewId = undefined;\n reportedViewId = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\ninterface PendingRequestTiming {\n entry?: PerformanceResourceTiming;\n startTime: number;\n timer?: ReturnType<typeof setTimeout>;\n url: string;\n callback?: (entry?: PerformanceResourceTiming) => void;\n}\n\nconst MAX_PENDING_REQUESTS = 100;\nconst RESOURCE_TIMING_WAIT_MS = 200;\nconst pendingRequests: PendingRequestTiming[] = [];\n\nconst normalizeUrl = (url: string) => url.replace(/#.*$/, '');\n\nconst removePending = (pending: PendingRequestTiming) => {\n const index = pendingRequests.indexOf(pending);\n if (index >= 0) pendingRequests.splice(index, 1);\n if (pending.timer !== undefined) clearTimeout(pending.timer);\n};\n\nconst finishPending = (pending: PendingRequestTiming, entry?: PerformanceResourceTiming) => {\n const callback = pending.callback;\n removePending(pending);\n pending.callback = undefined;\n callback?.(entry);\n};\n\nexport const notifyRequestTiming = (entry: PerformanceResourceTiming) => {\n const entryStartTime = performance.timeOrigin + entry.startTime;\n // 同 URL 并发时选择起始时间最接近的请求;50ms 容差覆盖 JS 调用与浏览器创建 timing entry 的偏差。\n const candidate = pendingRequests\n .filter(\n pending =>\n !pending.entry &&\n normalizeUrl(pending.url) === normalizeUrl(entry.name) &&\n entryStartTime >= pending.startTime - 50,\n )\n .sort((left, right) => Math.abs(left.startTime - entryStartTime) - Math.abs(right.startTime - entryStartTime))[0];\n if (!candidate) return;\n candidate.entry = entry;\n if (candidate.callback) finishPending(candidate, entry);\n};\n\nexport const trackRequestTiming = (url: string, startTime = Date.now()) => {\n const pending: PendingRequestTiming = { startTime, url };\n pendingRequests.push(pending);\n if (pendingRequests.length > MAX_PENDING_REQUESTS) finishPending(pendingRequests[0]);\n return {\n cancel: () => finishPending(pending),\n complete: (callback: (entry?: PerformanceResourceTiming) => void) => {\n pending.callback = callback;\n if (pending.entry) {\n finishPending(pending, pending.entry);\n return;\n }\n // loadend/fetch resolve 通常早于 ResourceTiming 回调,短暂等待以补齐 DNS、连接和下载阶段。\n pending.timer = setTimeout(() => finishPending(pending), RESOURCE_TIMING_WAIT_MS);\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 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, trace } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport type {\n BkOTRequestBodyConfig,\n BkOTRequestBodyRedactPayload,\n BkOTRequestContext,\n BkOTRequestEvent,\n} from '../core/config';\nimport type { BkOTRuntimeContext } from '../core/plugin';\nimport type { Span } from '@opentelemetry/api';\n\n/**\n * 请求体采集是显式启用的旁路能力:只读取可 clone 的文本内容,强制经过接入方 redact,\n * 并以独立 log 关联请求 span。任何读取或脱敏失败都静默放弃,不能影响业务请求。\n */\ninterface BodySnapshot {\n body: string;\n contentType?: string;\n truncated: boolean;\n}\n\ninterface CaptureRequestBodyLogOptions {\n bodyConfig: BkOTRequestBodyConfig;\n domainContext: BkOTRequestContext;\n event: BkOTRequestEvent;\n fetchRequest?: Request | RequestInit;\n occurredAt?: number;\n runtimeContext: Pick<BkOTRuntimeContext, 'emitLog'>;\n span: Span;\n}\n\nconst DEFAULT_MAX_SIZE = 10 * 1024;\nconst DEFAULT_ALLOWED_CONTENT_TYPES: Array<RegExp | string> = [\n /^text\\//i,\n /json/i,\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nconst resolveMaxSize = (value: number | undefined) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : DEFAULT_MAX_SIZE;\n\nconst isAllowedContentType = (\n contentType: string | undefined,\n matchers: Array<RegExp | string>,\n isRequest: boolean,\n) => {\n if (!contentType) return isRequest;\n return matchers.some(matcher => {\n if (typeof matcher === 'string') return contentType.toLowerCase().includes(matcher.toLowerCase());\n matcher.lastIndex = 0;\n return matcher.test(contentType);\n });\n};\n\nconst getHeadersContentType = (headers: HeadersInit | undefined) => {\n try {\n return headers ? (new Headers(headers).get('content-type') ?? undefined) : undefined;\n } catch {\n return undefined;\n }\n};\n\nconst readStream = async (stream: ReadableStream<Uint8Array>, readLimit: number) => {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let body = '';\n try {\n while (body.length <= readLimit) {\n const { done, value } = await reader.read();\n if (done) {\n body += decoder.decode();\n break;\n }\n body += decoder.decode(value, { stream: true });\n }\n if (body.length > readLimit) {\n await reader.cancel().catch(() => undefined);\n }\n } finally {\n reader.releaseLock();\n }\n return body;\n};\n\nconst bodyValueToText = async (body: BodyInit | Document | null | undefined, readLimit: number) => {\n if (typeof body === 'string') return body;\n if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) return body.toString();\n if (typeof Blob !== 'undefined' && body instanceof Blob) return readStream(body.stream(), readLimit);\n return undefined;\n};\n\nconst readFetchRequest = async (\n request: Request | RequestInit,\n readLimit: number,\n): Promise<{ contentType?: string; text?: string }> => {\n if (typeof Request !== 'undefined' && request instanceof Request) {\n const contentType = request.headers.get('content-type') ?? undefined;\n const clone = request.clone();\n const text = clone.body ? await readStream(clone.body, readLimit) : '';\n return { contentType, text };\n }\n return {\n contentType: getHeadersContentType(request.headers),\n text: await bodyValueToText(request.body, readLimit),\n };\n};\n\nexport const prepareFetchRequestBodySource = (request: Request | RequestInit) => {\n if (typeof Request === 'undefined' || !(request instanceof Request)) return request;\n try {\n // Request body 是一次性流,必须在原 fetch 消费前 clone,采集过程不能改变业务请求。\n return request.clone();\n } catch {\n return undefined;\n }\n};\n\nconst readFetchResponse = async (\n response: Response | undefined,\n readLimit: number,\n): Promise<{ contentType?: string; text?: string }> => {\n if (!response) return {};\n const contentType = response.headers.get('content-type') ?? undefined;\n const clone = response.clone();\n const text = clone.body ? await readStream(clone.body, readLimit) : '';\n return { contentType, text };\n};\n\nconst readXhrResponse = (xhr: XMLHttpRequest) => {\n const contentType = xhr.getResponseHeader('content-type') ?? undefined;\n if (xhr.responseType === '' || xhr.responseType === 'text') {\n return { contentType, text: xhr.responseText };\n }\n if (xhr.responseType === 'json') {\n return { contentType, text: JSON.stringify(xhr.response) };\n }\n return { contentType };\n};\n\nconst createSnapshot = (\n bodyConfig: BkOTRequestBodyConfig,\n event: BkOTRequestEvent,\n kind: 'request' | 'response',\n contentType: string | undefined,\n text: string | undefined,\n): BodySnapshot | undefined => {\n if (text === undefined) return undefined;\n const maxSize = resolveMaxSize(bodyConfig.maxSize);\n const truncated = text.length > maxSize;\n const payload: BkOTRequestBodyRedactPayload = {\n body: text.slice(0, maxSize * 2),\n contentType,\n kind,\n method: event.resource.method,\n statusCode: event.resource.statusCode,\n truncated,\n type: event.resource.type,\n url: event.resource.url,\n };\n const redacted = bodyConfig.redact(payload);\n if (redacted === undefined) return undefined;\n return {\n body: redacted.slice(0, maxSize),\n contentType,\n truncated: truncated || redacted.length > maxSize,\n };\n};\n\nexport const captureRequestBodyLog = async ({\n bodyConfig,\n domainContext,\n event,\n fetchRequest,\n occurredAt,\n runtimeContext,\n span,\n}: CaptureRequestBodyLogOptions) => {\n const statusCode = event.resource.statusCode;\n const isError = statusCode === undefined || statusCode === 0 || statusCode >= 400;\n if ((bodyConfig.capture ?? 'errors') === 'errors' && !isError) return;\n\n const maxSize = resolveMaxSize(bodyConfig.maxSize);\n // 给 redact 留出处理结构化内容的余量,但读取和最终上报都有硬上限,避免复制超大 payload。\n const readLimit = maxSize * 2;\n const allowedContentTypes = bodyConfig.allowedContentTypes ?? DEFAULT_ALLOWED_CONTENT_TYPES;\n let requestInput: { contentType?: string; text?: string } = {};\n let responseInput: { contentType?: string; text?: string } = {};\n\n try {\n if (domainContext.type === 'fetch') {\n [requestInput, responseInput] = await Promise.all([\n readFetchRequest(fetchRequest ?? domainContext.request, readLimit),\n readFetchResponse(domainContext.response, readLimit),\n ]);\n } else {\n requestInput = { text: await bodyValueToText(domainContext.requestBody, readLimit) };\n responseInput = readXhrResponse(domainContext.xhr);\n }\n } catch {\n // 流已锁定、clone 或响应读取失败时放弃采集,不能影响原请求。\n return;\n }\n\n let request: BodySnapshot | undefined;\n let response: BodySnapshot | undefined;\n try {\n request = isAllowedContentType(requestInput.contentType, allowedContentTypes, true)\n ? createSnapshot(bodyConfig, event, 'request', requestInput.contentType, requestInput.text)\n : undefined;\n response = isAllowedContentType(responseInput.contentType, allowedContentTypes, false)\n ? createSnapshot(bodyConfig, event, 'response', responseInput.contentType, responseInput.text)\n : undefined;\n } catch {\n // redact 是接入方代码,其异常同样按旁路失败处理。\n return;\n }\n if (!request && !response) return;\n\n runtimeContext.emitLog({\n attributes: {\n ...event.attributes,\n ...(request\n ? {\n 'request.body': request.body,\n 'request.body.content_type': request.contentType,\n 'request.body.truncated': request.truncated,\n }\n : {}),\n ...(response\n ? {\n 'response.body': response.body,\n 'response.body.content_type': response.contentType,\n 'response.body.truncated': response.truncated,\n }\n : {}),\n },\n body: 'browser.request.body',\n context: trace.setSpan(otelContext.active(), span),\n eventName: 'browser.request.body',\n severityNumber: isError ? SeverityNumber.ERROR : SeverityNumber.INFO,\n severityText: isError ? 'ERROR' : 'INFO',\n timestamp: occurredAt,\n });\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { shouldIgnoreUrl } from '../core/url';\nimport { notifyRequestTiming } from '../core/request-timing';\nimport { shouldRecordSignal } from '../core/signal';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\n/**\n * Resource Timing 的统一消费端:静态资源按配置生成 span;Fetch/XHR timing 转交 request 插件,\n * 与其已创建的请求 span 合并,避免同一次网络请求重复计数。\n */\ntype ResourceTimingWithStatus = PerformanceResourceTiming & {\n deliveryType?: string;\n renderBlockingStatus?: string;\n responseStatus?: number;\n};\n\nconst getCacheHit = (entry: ResourceTimingWithStatus) =>\n entry.deliveryType === 'cache' || (entry.transferSize === 0 && entry.decodedBodySize > 0) ? true : undefined;\n\nconst areInOrder = (...values: number[]) => values.every((value, index) => index === 0 || values[index - 1] <= value);\n\nconst setTiming = (attributes: Record<string, number>, name: string, origin: number, start: number, end: number) => {\n if (!areInOrder(origin, start, end)) return;\n attributes[`resource.${name}.start`] = start - origin;\n attributes[`resource.${name}.duration`] = end - start;\n};\n\nexport const getResourceTimingAttributes = (entry: ResourceTimingWithStatus) => {\n const attributes: Record<string, number> = {};\n const commonTimings = [\n entry.startTime,\n entry.fetchStart,\n entry.domainLookupStart,\n entry.domainLookupEnd,\n entry.connectStart,\n entry.connectEnd,\n entry.requestStart,\n entry.responseStart,\n entry.responseEnd,\n ];\n // 浏览器因 TAO 或实现差异可能返回缺失/乱序时间点;整组校验可避免产生负 duration。\n if (!commonTimings.every(Number.isFinite) || !areInOrder(...commonTimings)) return attributes;\n\n setTiming(attributes, 'first_byte', entry.startTime, entry.requestStart, entry.responseStart);\n setTiming(attributes, 'download', entry.startTime, entry.responseStart, entry.responseEnd);\n if (entry.workerStart > 0 && entry.workerStart < entry.fetchStart) {\n setTiming(attributes, 'worker', entry.startTime, entry.workerStart, entry.fetchStart);\n }\n if (entry.fetchStart < entry.domainLookupEnd) {\n setTiming(attributes, 'dns', entry.startTime, entry.domainLookupStart, entry.domainLookupEnd);\n }\n if (entry.fetchStart < entry.connectEnd) {\n setTiming(attributes, 'connect', entry.startTime, entry.connectStart, entry.connectEnd);\n if (entry.connectStart <= entry.secureConnectionStart && entry.secureConnectionStart <= entry.connectEnd) {\n setTiming(attributes, 'ssl', entry.startTime, entry.secureConnectionStart, entry.connectEnd);\n }\n }\n if (\n entry.redirectEnd > entry.startTime &&\n areInOrder(entry.startTime, entry.redirectStart, entry.redirectEnd, entry.fetchStart)\n ) {\n setTiming(attributes, 'redirect', entry.startTime, entry.redirectStart, entry.redirectEnd);\n }\n return attributes;\n};\n\nconst getResourceDuration = (entry: ResourceTimingWithStatus) =>\n entry.duration === 0 && entry.responseEnd > entry.startTime ? entry.responseEnd - entry.startTime : entry.duration;\n\nexport const getResourcePerformanceAttributes = (entry: ResourceTimingWithStatus) => {\n const deliveryType = typeof entry.deliveryType === 'string' ? entry.deliveryType || 'other' : undefined;\n return {\n [RUM_ATTRIBUTES.transferSize]: entry.transferSize,\n [RUM_ATTRIBUTES.cacheHit]: getCacheHit(entry),\n [RUM_ATTRIBUTES.nextHopProtocol]: entry.nextHopProtocol,\n ...getResourceTimingAttributes(entry),\n ...(deliveryType ? { 'resource.delivery_type': deliveryType } : {}),\n ...(entry.renderBlockingStatus ? { 'resource.render_blocking_status': entry.renderBlockingStatus } : {}),\n 'resource.encoded_body_size': entry.encodedBodySize,\n 'resource.decoded_body_size': entry.decodedBodySize,\n };\n};\n\nexport const createResourcePlugin = (emitResourceSpans = true): BkOTPlugin => {\n let observer: PerformanceObserver | undefined;\n\n return {\n name: 'resource',\n init(context) {\n if (typeof PerformanceObserver === 'undefined') {\n return;\n }\n if (!PerformanceObserver.supportedEntryTypes?.includes('resource')) {\n return;\n }\n\n const emitEntry = (entry: ResourceTimingWithStatus) => {\n if (!context.isSampled()) return;\n if (!entry.name || shouldIgnoreUrl(context.config, entry.name)) {\n return;\n }\n if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {\n // Fetch/XHR 已由 request 插件建 span;这里只把 ResourceTiming 回传补齐网络阶段,避免重复上报。\n notifyRequestTiming(entry);\n return;\n }\n if (entry.duration < 0) return;\n const redactedUrl = context.config.hooks.redactUrl(entry.name);\n const duration = getResourceDuration(entry);\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + duration;\n if (emitResourceSpans && shouldRecordSignal(context, 'traces')) {\n const span = context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n duration,\n initiatorType: entry.initiatorType || 'other',\n responseStatus: entry.responseStatus,\n transferSize: entry.transferSize,\n url: redactedUrl,\n urlPathTemplate: context.config.hooks.getViewPathGroup(entry.name),\n }),\n 'url.full': redactedUrl,\n initiator_type: entry.initiatorType || 'other',\n ...getResourcePerformanceAttributes(entry),\n },\n { startTime },\n );\n span.end(endTime);\n }\n context.notifyActivity({ endTime, startTime, type: 'resource', url: entry.name });\n };\n\n try {\n observer = new PerformanceObserver(list => {\n for (const entry of list.getEntries() as ResourceTimingWithStatus[]) {\n emitEntry(entry);\n }\n });\n observer.observe({ type: 'resource', buffered: true });\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\n/**\n * Fetch/XHR 请求统一采集入口。\n *\n * 同一层 patch 负责请求 span、Trace Header、页面活动、body 与 beforeSend,避免多套 patch\n * 重复计数,也不依赖 OpenTelemetry instrumentation 的私有方法。\n */\n\nimport { context as otelContext, propagation, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { REQUEST_DISCARD_ATTRIBUTE } from '../core/processor';\nimport { trackRequestTiming } from '../core/request-timing';\nimport {\n matchesUrlList,\n shouldIgnoreRequest,\n shouldIgnoreUrl,\n shouldPropagateTraceHeader,\n toAbsoluteUrl,\n} from '../core/url';\nimport { buildRumResourceSpanAttributes } from '../schema/span';\nimport { captureRequestBodyLog, prepareFetchRequestBodySource } from './request-body';\nimport { getResourcePerformanceAttributes } from './resource';\n\nimport type { BkOTRequestConfig, BkOTRequestContext } from '../core/config';\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface RequestDetails {\n method?: string;\n startedAt?: number;\n url?: string;\n}\n\nconst getRequestOptions = (context: BkOTRuntimeContext) =>\n typeof context.config.tracking.request === 'object' ? context.config.tracking.request : undefined;\n\nconst getDomainStatus = (domainContext: BkOTRequestContext) => {\n if (domainContext.type === 'xhr') return domainContext.xhr.status;\n if (domainContext.response) return domainContext.response.status;\n if (typeof domainContext.error === 'object' && domainContext.error && 'status' in domainContext.error) {\n const status = (domainContext.error as { status?: unknown }).status;\n return typeof status === 'number' ? status : undefined;\n }\n return undefined;\n};\n\nconst getFetchUrl = (input: RequestInfo | URL) => {\n if (typeof input === 'string' || input instanceof URL) return toAbsoluteUrl(String(input));\n return toAbsoluteUrl(input.url);\n};\n\nconst getFetchMethod = (input: RequestInfo | URL, init?: RequestInit) =>\n (init?.method ?? (typeof Request !== 'undefined' && input instanceof Request ? input.method : 'GET')).toUpperCase();\n\nconst getFetchRequestContext = (input: RequestInfo | URL, init?: RequestInit): Request | RequestInit =>\n typeof Request !== 'undefined' && input instanceof Request ? input : (init ?? {});\n\nconst injectFetchTraceHeaders = (input: RequestInfo | URL, init: RequestInit | undefined, span: Span): RequestInit => {\n const requestHeaders = typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined;\n const headers = new Headers(init?.headers ?? requestHeaders);\n propagation.inject(trace.setSpan(otelContext.active(), span), headers, {\n set: (carrier, key, value) => carrier.set(key, value),\n });\n return { ...init, headers };\n};\n\nconst injectXhrTraceHeaders = (xhr: XMLHttpRequest, span: Span) => {\n const headers: Record<string, string> = {};\n propagation.inject(trace.setSpan(otelContext.active(), span), headers);\n for (const [key, value] of Object.entries(headers)) xhr.setRequestHeader(key, value);\n};\n\nconst isActivityExcluded = (context: BkOTRuntimeContext, url: string) =>\n shouldIgnoreUrl(context.config, url) || matchesUrlList(context.config.view.excludedActivityUrls, url);\n\nconst startRequestActivity = (context: BkOTRuntimeContext, url: string) => {\n if (isActivityExcluded(context, url)) return () => {};\n const id = createRandomId('request');\n context.notifyActivity({ id, time: Date.now(), type: 'request_start', url });\n let ended = false;\n return () => {\n if (ended) return;\n ended = true;\n context.notifyActivity({ id, time: Date.now(), type: 'request_end' });\n };\n};\n\nconst trackRequestSpanTiming = (context: BkOTRuntimeContext, url: string) =>\n context.config.transport.traces === false || !context.isSampled()\n ? {\n cancel() {},\n complete(callback: (entry?: PerformanceResourceTiming) => void) {\n callback();\n },\n }\n : trackRequestTiming(url);\n\nconst startRequestSpan = (context: BkOTRuntimeContext, subtype: 'fetch' | 'xhr', details: RequestDetails) =>\n context.startSpan(\n 'browser.resource',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method: details.method,\n url: details.url ?? '',\n urlPathTemplate: details.url ? context.config.hooks.getViewPathGroup(details.url) : undefined,\n }),\n ...(details.url ? { 'resource.url': context.config.hooks.redactUrl(details.url) } : {}),\n },\n { kind: SpanKind.CLIENT },\n );\n\nexport { REQUEST_DISCARD_ATTRIBUTE };\n\nexport const decorateRequestSpan = (\n context: BkOTRuntimeContext,\n span: Span,\n subtype: 'fetch' | 'xhr',\n domainContext: BkOTRequestContext,\n details: RequestDetails = {},\n fetchRequest?: Request | RequestInit,\n) => {\n const sourceUrl = details.url;\n const redactedUrl = sourceUrl ? context.config.hooks.redactUrl(sourceUrl) : undefined;\n const statusCode = getDomainStatus(domainContext);\n const method = details.method;\n const attributes: Attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumResourceSpanAttributes({\n initiatorType: subtype,\n method,\n networkError: domainContext.type === 'fetch' ? !domainContext.response : domainContext.xhr.status === 0,\n responseStatus: statusCode,\n url: redactedUrl ?? '',\n urlPathTemplate: sourceUrl ? context.config.hooks.getViewPathGroup(sourceUrl) : undefined,\n }),\n ...(redactedUrl ? { 'resource.url': redactedUrl, 'url.full': redactedUrl } : {}),\n };\n\n const event = {\n attributes,\n resource: {\n method,\n statusCode,\n type: subtype,\n url: redactedUrl,\n },\n type: 'resource' as const,\n };\n let shouldSend: boolean | void = true;\n try {\n shouldSend = getRequestOptions(context)?.beforeSend?.(event, domainContext);\n } catch (error) {\n if (context.config.debug) globalThis.console?.warn('[bk-ot] request.beforeSend failed:', error);\n }\n span.setAttributes(context.applyRedact(event.attributes));\n if (shouldSend === false) {\n // SpanProcessor 在 onEnd 统一识别内部标记,避免这里绕过正常的 span 结束流程。\n span.setAttribute(REQUEST_DISCARD_ATTRIBUTE, true);\n return;\n }\n if ((statusCode ?? 0) >= 400 || statusCode === 0 || (domainContext.type === 'fetch' && domainContext.error)) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n const bodyConfig = context.config.transport.logs === false ? undefined : getRequestOptions(context)?.body;\n if (bodyConfig) {\n void captureRequestBodyLog({\n bodyConfig,\n domainContext,\n event,\n fetchRequest,\n occurredAt: details.startedAt,\n runtimeContext: context,\n span,\n });\n }\n};\n\nexport const createCommonInstrumentationsPlugin = (option: BkOTRequestConfig | boolean | undefined): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n const activeRequestEnds = new Set<() => void>();\n\n return {\n name: 'request',\n enabled: option !== false,\n init(context) {\n if (typeof window === 'undefined') return;\n\n if (typeof window.fetch === 'function') {\n const originalFetch = window.fetch;\n const patchedFetch: typeof window.fetch = async function (this: Window, input, init) {\n const url = getFetchUrl(input);\n if (!context.isSampled() || shouldIgnoreRequest(context.config, url)) return originalFetch.call(this, input, init);\n const method = getFetchMethod(input, init);\n const details = { method, startedAt: Date.now(), url };\n const span = startRequestSpan(context, 'fetch', details);\n const timing = trackRequestSpanTiming(context, url);\n const endActivity = startRequestActivity(context, url);\n const requestContext = getFetchRequestContext(input, init);\n const fetchRequest =\n context.config.transport.logs !== false && getRequestOptions(context)?.body\n ? prepareFetchRequestBodySource(requestContext)\n : undefined;\n const requestInit = shouldPropagateTraceHeader(context.config, url)\n ? injectFetchTraceHeaders(input, init, span)\n : init;\n let ended = false;\n const endSpan = (entry?: PerformanceResourceTiming) => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end();\n };\n const forceEnd = () => {\n // shutdown 不再等待异步 ResourceTiming,立即结束仍在飞行中的 span。\n timing.cancel();\n endSpan();\n };\n activeRequestEnds.add(forceEnd);\n try {\n const response = await originalFetch.call(this, input, requestInit);\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { request: requestContext, response, type: 'fetch' },\n details,\n fetchRequest,\n );\n timing.complete(endSpan);\n return response;\n } catch (error) {\n decorateRequestSpan(\n context,\n span,\n 'fetch',\n { error, request: requestContext, type: 'fetch' },\n details,\n fetchRequest,\n );\n if (error instanceof Error) span.recordException(error);\n timing.complete(endSpan);\n throw error;\n } finally {\n endActivity();\n }\n };\n window.fetch = patchedFetch;\n teardownCallbacks.push(() => {\n if (window.fetch === patchedFetch) window.fetch = originalFetch;\n });\n }\n\n if (typeof XMLHttpRequest !== 'undefined') {\n const requests = new WeakMap<XMLHttpRequest, RequestDetails>();\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const patchedOpen: typeof XMLHttpRequest.prototype.open = function (\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n async = true,\n username?: null | string,\n password?: null | string,\n ) {\n requests.set(this, { method: method.toUpperCase(), url: toAbsoluteUrl(String(url)) });\n return originalOpen.call(this, method, String(url), async, username, password);\n };\n const patchedSend: typeof XMLHttpRequest.prototype.send = function (this: XMLHttpRequest, body) {\n const details = requests.get(this) ?? {};\n if (!context.isSampled() || !details.url || shouldIgnoreRequest(context.config, details.url))\n return originalSend.call(this, body);\n details.startedAt = Date.now();\n const span = startRequestSpan(context, 'xhr', details);\n const timing = trackRequestSpanTiming(context, details.url);\n const endActivity = startRequestActivity(context, details.url);\n if (shouldPropagateTraceHeader(context.config, details.url)) injectXhrTraceHeaders(this, span);\n let ended = false;\n const forceEnd = () => {\n // XHR 可能永不触发 loadend;SDK 停止时必须清理活动计数并结束 span。\n timing.cancel();\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n endActivity();\n span.end();\n };\n activeRequestEnds.add(forceEnd);\n const end = () => {\n if (ended) return;\n decorateRequestSpan(context, span, 'xhr', { requestBody: body, type: 'xhr', xhr: this }, details);\n endActivity();\n timing.complete(entry => {\n if (ended) return;\n ended = true;\n activeRequestEnds.delete(forceEnd);\n if (entry) span.setAttributes(context.applyRedact(getResourcePerformanceAttributes(entry)));\n span.end();\n });\n };\n this.addEventListener('loadend', end, { once: true });\n try {\n return originalSend.call(this, body);\n } catch (error) {\n if (error instanceof Error) span.recordException(error);\n end();\n throw error;\n }\n };\n XMLHttpRequest.prototype.open = patchedOpen;\n XMLHttpRequest.prototype.send = patchedSend;\n teardownCallbacks.push(() => {\n if (XMLHttpRequest.prototype.open === patchedOpen) XMLHttpRequest.prototype.open = originalOpen;\n if (XMLHttpRequest.prototype.send === patchedSend) XMLHttpRequest.prototype.send = originalSend;\n });\n }\n },\n shutdown() {\n for (const end of [...activeRequestEnds]) end();\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 简易 djb2 字符串 hash。结果稳定、短(base36),足以区分常见 fingerprint 字符串组合。\n * 不是密码学安全 hash,仅用于事件聚合分组场景。\n */\nexport const 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\nexport interface ThrottleResult {\n /** 是否允许本次上报 */\n allowed: boolean;\n /** 当前窗口内同 fingerprint 的触发序号(含本次) */\n count: number;\n}\n\ninterface ThrottleEntry {\n count: number;\n windowStart: number;\n}\n\n/**\n * 时间窗节流:同一 key 在 windowMs 窗口内最多允许 maxPerWindow 次通过;\n * 超出则被拦截,但 `count` 仍累加,供调用方维护内部统计。\n *\n * 长会话场景下避免 records 无限累积——按窗口长度节奏轻量清理一次过期 entry。\n */\nexport const createThrottle = (windowMs: number, maxPerWindow: number) => {\n const records = new Map<string, ThrottleEntry>();\n let lastSweep = 0;\n const sweep = (now: number) => {\n if (now - lastSweep < windowMs) return;\n lastSweep = now;\n for (const [key, entry] of records) {\n if (now - entry.windowStart > windowMs) records.delete(key);\n }\n };\n return {\n allow(key: string): ThrottleResult {\n const now = Date.now();\n sweep(now);\n const entry = records.get(key);\n if (!entry || now - entry.windowStart > windowMs) {\n records.set(key, { count: 1, windowStart: now });\n return { allowed: true, count: 1 };\n }\n entry.count += 1;\n return { allowed: entry.count <= maxPerWindow, count: entry.count };\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 { createThrottle, hashString } from '../core/throttle';\nimport { shouldRecordSignal } from '../core/signal';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\n\nconst CSP_THROTTLE_WINDOW_MS = 60_000;\nconst CSP_THROTTLE_MAX_PER_WINDOW = 5;\n\n// blockedURI 除了 URL 还可能是 'inline' / 'eval' / 'wasm-eval' / 'self' 等关键字(CSP spec),\n// 这些不是 URL,不应该走 redactUrl\nconst URL_LIKE_BLOCKED_URI = /^https?:|^\\/\\//i;\n\nconst redactBlockedUri = (redactUrl: (url: string) => string, blockedURI: string | undefined) => {\n if (!blockedURI) return undefined;\n return URL_LIKE_BLOCKED_URI.test(blockedURI) ? redactUrl(blockedURI) : blockedURI;\n};\n\n/**\n * CSP 违规事件采集插件,通过监听 `securitypolicyviolation` 事件捕获被 CSP 策略拦截的脚本/资源,\n * 以 error span 上报,进入统一错误分析口径。\n *\n * 节流策略:\n * - 同 fingerprint(`violatedDirective + blockedURI + sourceFile + lineNumber`)\n * 在 60s 窗口内最多上报 5 条,避免风暴打爆 collector(如某段被拦截的 inline 脚本被反复渲染)。\n * - `browser.csp_violation.count` counter 在节流前累计所有违规,span 只保留窗口触发序号。\n *\n * 体积优化:\n * - `csp.original_policy`(完整 CSP 策略,可能数 KB)仅在窗口首条上报,后续同 fingerprint 不再重复携带。\n *\n * URL 处理:\n * - `blockedURI` / `sourceFile` 中的 URL 类值会经 `redactUrl` 脱敏。\n * - `blockedURI` 是 'inline' / 'eval' 等 CSP 关键字时直接保留,不调用 redactUrl。\n */\nexport const createCspViolationPlugin = (): BkOTPlugin => {\n let teardown: (() => void) | undefined;\n\n return {\n name: 'csp-violation',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n const throttle = createThrottle(CSP_THROTTLE_WINDOW_MS, CSP_THROTTLE_MAX_PER_WINDOW);\n const counter = context.meter.createCounter('browser.csp_violation.count', {\n description: 'Total number of CSP violations before span throttling',\n });\n\n const handler = (event: SecurityPolicyViolationEvent) => {\n const recordMetrics = shouldRecordSignal(context, 'metrics');\n const recordTrace = shouldRecordSignal(context, 'traces');\n if (!recordMetrics && !recordTrace) return;\n if (recordMetrics) {\n counter.add(\n 1,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'csp.disposition': event.disposition,\n 'csp.effective_directive': event.effectiveDirective,\n }),\n );\n }\n if (!recordTrace) return;\n const fingerprint = hashString(\n `${event.violatedDirective}|${event.blockedURI}|${event.sourceFile}|${event.lineNumber}`,\n );\n const result = throttle.allow(fingerprint);\n if (!result.allowed) return;\n\n const blockedUri = redactBlockedUri(context.config.hooks.redactUrl, event.blockedURI);\n const sourceFile = event.sourceFile ? context.config.hooks.redactUrl(event.sourceFile) : undefined;\n // 窗口首条带完整 originalPolicy 用于排查策略本身;后续同 fingerprint 省略,避免数 KB 字段反复传输\n const includeOriginalPolicy = result.count === 1;\n\n context\n .startSpan('csp.violation', {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n errorType: 'csp',\n eventLabel: 'CSP 错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: 'csp',\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: blockedUri ?? event.effectiveDirective,\n targetValue: event.statusCode,\n }),\n 'csp.blocked_uri': blockedUri,\n 'csp.violated_directive': event.violatedDirective,\n 'csp.effective_directive': event.effectiveDirective,\n 'csp.disposition': event.disposition,\n 'csp.source_file': sourceFile,\n 'csp.line_number': event.lineNumber,\n 'csp.column_number': event.columnNumber,\n 'csp.status_code': event.statusCode,\n 'csp.fingerprint': fingerprint,\n 'csp.window_occurrence_index': result.count,\n [RUM_ATTRIBUTES.exceptionFingerprint]: fingerprint,\n 'error.source': 'csp',\n ...(includeOriginalPolicy ? { 'csp.original_policy': event.originalPolicy } : {}),\n })\n .end();\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 { createRandomId } from '../core/id';\nimport { hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\nexport interface BkOTDevicePluginOptions {\n /** 是否跨页面持久化匿名设备 ID,默认 false。 */\n persistentId?: boolean;\n storageKey?: string;\n}\n\nconst DEFAULT_DEVICE_STORAGE_KEY = 'BK_RUM_DEVICE_ID';\n// 用户拖窗口时 resize 高频触发,debounce 静默期内只在最后一次更新,避免无谓的 setRuntimeAttributes 风暴\nconst VIEWPORT_DEBOUNCE_MS = 250;\n\ninterface NetworkInformationLike {\n downlink?: number;\n effectiveType?: string;\n rtt?: number;\n saveData?: boolean;\n type?: string;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ninterface UserAgentDataLike {\n mobile?: boolean;\n platform?: string;\n}\n\nconst getConnection = (): NetworkInformationLike | undefined =>\n (\n navigator as Navigator & {\n connection?: NetworkInformationLike;\n }\n ).connection;\n\nconst getUserAgentData = (): undefined | UserAgentDataLike =>\n (\n navigator as Navigator & {\n userAgentData?: UserAgentDataLike;\n }\n ).userAgentData;\n\nconst getBrowserInfo = (ua: string) => {\n const matchers: Array<[string, RegExp]> = [\n ['Edge', /Edg\\/([\\d.]+)/],\n ['Chrome', /Chrome\\/([\\d.]+)/],\n ['Firefox', /Firefox\\/([\\d.]+)/],\n ['Safari', /Version\\/([\\d.]+).*Safari/],\n ];\n for (const [name, pattern] of matchers) {\n const matched = ua.match(pattern);\n if (matched?.[1]) {\n return { name, version: matched[1].split('.')[0] };\n }\n }\n return { name: 'Other', version: undefined };\n};\n\nconst getOsName = (ua: string, platform?: string) => {\n const value = `${platform ?? ''} ${ua}`;\n if (/iphone|ipad|ipod/i.test(value)) return 'iOS';\n if (/android/i.test(value)) return 'Android';\n if (/mac/i.test(value)) return 'macOS';\n if (/win/i.test(value)) return 'Windows';\n if (/linux/i.test(value)) return 'Linux';\n return 'Other';\n};\n\nconst readDeviceId = (storageKey: string) => {\n let existed: null | string = null;\n try {\n existed = window.localStorage.getItem(storageKey);\n } catch {\n /* localStorage 不可用(隐私模式 / sandbox iframe),下面会创建一个 in-memory id */\n }\n if (existed) {\n return existed;\n }\n const deviceId = createRandomId('device');\n try {\n window.localStorage.setItem(storageKey, deviceId);\n } catch {\n /* 写不进去也行,至少本次 SDK 生命周期内 deviceId 仍然稳定 */\n }\n return deviceId;\n};\n\n// 视口尺寸:随窗口大小 / 屏幕旋转变化,需要在 resize / orientationchange 时刷新\nconst getViewportAttributes = (): Attributes => {\n if (typeof window === 'undefined') {\n return {};\n }\n return {\n 'browser.viewport.width': window.innerWidth,\n 'browser.viewport.height': window.innerHeight,\n 'browser.screen.width': window.screen?.width,\n 'browser.screen.height': window.screen?.height,\n };\n};\n\n// 网络信息:随 connection.change 变化\nconst getNetworkAttributes = (): Attributes => {\n const connection = getConnection();\n if (!connection) {\n return {};\n }\n return {\n 'network.effective_type': connection.effectiveType,\n 'network.connection_type': connection.type,\n 'network.downlink': connection.downlink,\n 'network.rtt': connection.rtt,\n 'network.save_data': connection.saveData,\n };\n};\n\n// 设备硬件能力:进入页面时一次性读取,运行期不会变化\nconst getHardwareAttributes = (): Attributes => {\n if (typeof navigator === 'undefined') {\n return {};\n }\n const uaData = getUserAgentData();\n const ua = navigator.userAgent;\n const browser = getBrowserInfo(ua);\n const osName = getOsName(ua, uaData?.platform);\n const deviceType = uaData?.mobile || /mobi|android|iphone|ipad|ipod/i.test(ua) ? 'mobile' : 'desktop';\n return {\n 'device.memory': (navigator as Navigator & { deviceMemory?: number }).deviceMemory,\n 'device.cpu_cores': navigator.hardwareConcurrency,\n 'device.platform': uaData?.platform,\n 'device.mobile': uaData?.mobile,\n [RUM_ATTRIBUTES.browserName]: browser.name,\n [RUM_ATTRIBUTES.browserVersion]: browser.version,\n [RUM_ATTRIBUTES.deviceType]: deviceType,\n [RUM_ATTRIBUTES.osName]: osName,\n };\n};\n\n// trailing debounce:windowMs 静默期内的多次触发只执行最后一次。\n// 适合 resize 这类\"用户停下来后才需要拿到最终值\"的场景。\nconst createDebounce = (fn: () => void, windowMs: number) => {\n let timer: number | undefined;\n return () => {\n if (timer !== undefined) {\n window.clearTimeout(timer);\n }\n timer = window.setTimeout(() => {\n timer = undefined;\n fn();\n }, windowMs);\n };\n};\n\n/**\n * 设备元数据采集插件,把\"设备级\"信息写入 SDK 的 runtimeAttributes,\n * 让所有 log/span 都自动带上这些维度。\n *\n * 上报字段:\n * - `device.id`:设备级永久标识,存于 localStorage(默认 key 复用旧 session 插件以保留现网用户)。\n * - `device.memory` / `device.cpu_cores` / `device.platform` / `device.mobile`:硬件能力快照(运行期不变)。\n * - `browser.viewport.*` / `browser.screen.*`:视口与屏幕尺寸。\n * - `network.effective_type` / `network.connection_type` / `network.downlink` / `network.rtt` / `network.save_data`:\n * 网络信息。\n *\n * 实时更新:\n * - viewport 监听 `resize` / `orientationchange`,并经 250ms debounce 合并,避免拖窗口时高频触发;\n * 静默期结束后用最终值刷新 runtimeAttributes。\n * - network 监听 `connection.change`(本身低频,不需要 throttle)。\n * - 二者只更新 in-memory 的 runtimeAttributes,不会主动产生新的 span/log,cardinality 安全;\n * 但更新后续生成的 log/span 会带最新值。\n *\n * 与 session 插件的关系:device 跨会话持久(终身),session 有过期与续期(连续访问语义)。\n */\nexport const createDevicePlugin = (options: BkOTDevicePluginOptions = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n return {\n name: 'device',\n init(context) {\n const storageKey =\n options.storageKey ?? `${DEFAULT_DEVICE_STORAGE_KEY}_${hashString(context.config.application.name)}`;\n const deviceId =\n typeof window === 'undefined' || options.persistentId !== true\n ? createRandomId('device')\n : readDeviceId(storageKey);\n\n context.setRuntimeAttributes({\n 'device.id': deviceId,\n ...getHardwareAttributes(),\n ...getViewportAttributes(),\n ...getNetworkAttributes(),\n });\n\n if (typeof window === 'undefined') {\n return;\n }\n\n const refreshViewport = createDebounce(() => {\n context.setRuntimeAttributes(getViewportAttributes());\n }, VIEWPORT_DEBOUNCE_MS);\n window.addEventListener('resize', refreshViewport);\n window.addEventListener('orientationchange', refreshViewport);\n teardownCallbacks.push(\n () => window.removeEventListener('resize', refreshViewport),\n () => window.removeEventListener('orientationchange', refreshViewport),\n );\n\n const connection = getConnection();\n // NetworkInformation 实现了 EventTarget;旧浏览器或某些 polyfill 可能没有 addEventListener,做能力检测\n if (connection && typeof connection.addEventListener === 'function') {\n const refreshNetwork = () => {\n context.setRuntimeAttributes(getNetworkAttributes());\n };\n connection.addEventListener('change', refreshNetwork);\n teardownCallbacks.push(() => {\n connection.removeEventListener?.('change', refreshNetwork);\n });\n }\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * 浏览器错误采集插件,负责捕获 JS 运行时错误、未处理的 Promise rejection、资源加载失败三类错误。\n *\n * 上报策略:\n * - 错误分析主数据统一使用 `span_type=error` 的 error span。\n * - 不再为同一个错误额外 emit error log,避免后端错误数按 span/log 混算时重复。\n *\n * 节流策略:\n * - 以 source + message + stack 前 256 字符做 djb2 hash 作为 fingerprint。\n * - 同 fingerprint 在 windowMs(默认 60s)内最多上报 maxPerWindow(默认 5)条;超过的会被丢弃。\n * - `browser.error.count` counter 在节流前累计所有错误,span 只保留窗口触发序号。\n *\n * 关键 attribute:\n * - `error.source`:错误来源,取值 `window.error` / `unhandledrejection` / `resource`。\n * - `exception.fingerprint`:节流分组用的指纹 hash,可用于聚合同类错误。\n * - `error.window_occurrence_index`:该 span 在当前节流窗口内的触发序号。\n * - `error.cross_origin`:仅在跨域脚本错误(\"Script error.\")时为 true,便于过滤这类无 stack 噪声。\n * - 资源错误额外带 `url.full`(经 redactUrl 脱敏)与 `html.tag`。\n * - JS 错误额外带 `code.filepath` / `code.lineno` / `code.column`。\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\n\nimport { createThrottle, hashString } from '../core/throttle';\nimport { shouldRecordSignal } from '../core/signal';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\n\nexport interface BkOTErrorPluginOptions {\n // 同 hash 错误窗口内最多上报多少条,默认 5\n maxPerWindow?: number;\n // 节流窗口长度(ms),默认 60_000\n windowMs?: number;\n}\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_PER_WINDOW = 5;\n\nconst positiveNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\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\nconst getMessageShort = (message: string) => message.slice(0, 80);\n\nconst getStackTopFrame = (stack: string) =>\n stack\n .split('\\n')\n .find(line => line.trim().startsWith('at '))\n ?.trim() ?? '';\n\nconst getErrorSubtype = (source: string) => {\n if (source === 'unhandledrejection') return 'promise';\n if (source === 'resource') return 'resource_load';\n return 'js';\n};\n\ninterface EmitErrorOptions {\n context: BkOTRuntimeContext;\n counter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\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, counter, error, exceptionType, extra = {}, source, spanName, throttle }: EmitErrorOptions) => {\n const recordMetrics = shouldRecordSignal(context, 'metrics');\n const recordTrace = shouldRecordSignal(context, 'traces');\n if (!recordMetrics && !recordTrace) return;\n const message = getErrorMessage(error);\n const stack = getErrorStack(error);\n const throttleKey = hashString(`${source}|${message}|${stack.slice(0, 256)}`);\n\n if (recordMetrics) {\n counter.add(\n 1,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'error.source': source,\n 'error.subtype': getErrorSubtype(source),\n }),\n );\n }\n if (!recordTrace) return;\n\n context.notifyActivity({ time: Date.now(), type: 'error' });\n\n const throttleResult = throttle.allow(throttleKey);\n if (!throttleResult.allowed) return;\n\n const exceptionLike = error instanceof Error ? error : { message, name: exceptionType ?? source };\n const attributes = {\n ...context.config.hooks.getPageAttributes(),\n ...context.config.hooks.getErrorAttributes(),\n ...buildRumSpanAttributes({\n errorType: getErrorSubtype(source),\n eventLabel: '错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: getErrorSubtype(source),\n spanType: RUM_SPAN_TYPE.error,\n targetLabel: error instanceof Error ? error.name : (exceptionType ?? source),\n }),\n [RUM_ATTRIBUTES.exceptionMessageShort]: getMessageShort(message),\n [RUM_ATTRIBUTES.exceptionStackTopFrame]: getStackTopFrame(stack),\n [RUM_ATTRIBUTES.exceptionFingerprint]: throttleKey,\n 'error.handled': false,\n 'error.source': source,\n 'error.window_occurrence_index': throttleResult.count,\n ...extra,\n };\n // 错误分析统一沉淀为 error span;若当前存在 active context,OTel 会自动建立父子关系。\n const errorSpan = context.startSpan(spanName, attributes);\n errorSpan.recordException(exceptionLike);\n errorSpan.setStatus({ code: SpanStatusCode.ERROR, message });\n errorSpan.end();\n};\n\nexport const createErrorPlugin = (options: BkOTErrorPluginOptions = {}): BkOTPlugin => {\n const listeners: Array<() => void> = [];\n const windowMs = positiveNumber(options.windowMs, DEFAULT_WINDOW_MS);\n const maxPerWindow = positiveNumber(options.maxPerWindow, DEFAULT_MAX_PER_WINDOW);\n\n return {\n name: 'error',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n const throttle = createThrottle(windowMs, maxPerWindow);\n const counter = context.meter.createCounter('browser.error.count', {\n description: 'Total number of browser errors before span throttling',\n });\n\n const onError = (event: ErrorEvent) => {\n // 跨域脚本错误浏览器会屏蔽 stack/filename,message 通常是 \"Script error.\"\n const isCrossOrigin = !event.error && !event.filename && event.message === 'Script error.';\n emitError({\n context,\n counter,\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 ...(isCrossOrigin ? { 'error.cross_origin': true } : {}),\n },\n });\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n emitError({\n context,\n counter,\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 // JS 运行时错误是 ErrorEvent,已由 bubble 阶段的 onError 处理;这里只关心资源加载失败\n if (event instanceof ErrorEvent) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const elementWithUrl = target as Element & { href?: string; src?: string };\n const resourceUrl = elementWithUrl.src || elementWithUrl.href || (target as HTMLMediaElement).currentSrc || '';\n emitError({\n context,\n counter,\n throttle,\n spanName: 'browser.resource_error',\n source: 'resource',\n error: new Error(`Resource load failed: ${target.tagName.toLowerCase()} ${resourceUrl}`),\n exceptionType: 'ResourceError',\n extra: {\n 'url.full': context.config.hooks.redactUrl(resourceUrl),\n 'html.tag': target.tagName,\n },\n });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('error', onResourceError, true);\n listeners.push(\n () => window.removeEventListener('error', onError),\n () => window.removeEventListener('unhandledrejection', onUnhandledRejection),\n () => window.removeEventListener('error', onResourceError, true),\n );\n },\n shutdown() {\n while (listeners.length) {\n listeners.pop()?.();\n }\n },\n };\n};\n","export const CLS_HISTOGRAM_BOUNDARIES = [0, 0.01, 0.025, 0.05, 0.075, 0.1, 0.15, 0.25, 0.5, 1];\n\nexport const DURATION_HISTOGRAM_BOUNDARIES = [0, 50, 100, 200, 300, 500, 800, 1_000, 1_500, 2_000, 2_500, 4_000, 5_000, 10_000];\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nimport { createRandomId } from '../core/id';\nimport { DURATION_HISTOGRAM_BOUNDARIES } from '../core/metric';\nimport { shouldRecordSignal } from '../core/signal';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * 主线程阻塞优先使用 Long Animation Frame API 获取脚本归因,不支持时回退到 Long Tasks API;\n * span 保留单次诊断详情,metric 只保留低基数维度用于聚合分布。\n */\nexport interface BkOTLongTaskPluginOptions {\n /** 采集阈值,默认 50ms。Performance API 本身通常只产生大于 50ms 的条目。 */\n threshold?: number;\n /** 标记 warning 的阈值,默认 100ms。 */\n warningThreshold?: number;\n}\n\ninterface LegacyLongTaskEntry extends PerformanceEntry {\n attribution?: Array<{\n containerId?: string;\n containerName?: string;\n containerSrc?: string;\n containerType?: string;\n name?: string;\n }>;\n}\n\ninterface LongAnimationFrameEntry extends PerformanceEntry {\n blockingDuration: number;\n firstUIEventTimestamp: number;\n renderStart: number;\n scripts: ScriptTiming[];\n styleAndLayoutStart: number;\n}\n\ninterface ScriptTiming {\n duration: number;\n executionStart: number;\n forcedStyleAndLayoutDuration: number;\n invoker: string;\n invokerType: string;\n pauseDuration: number;\n sourceCharPosition: number;\n sourceFunctionName: string;\n sourceURL: string;\n startTime: number;\n windowAttribution: string;\n}\n\nconst DEFAULT_THRESHOLD = 50;\nconst DEFAULT_WARNING_THRESHOLD = 100;\nconst FROZEN_FRAME_THRESHOLD = 700;\n\nconst nonNegativeNumber = (value: number | undefined, fallback: number) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n\nconst finiteNumber = (value: number | undefined) => (Number.isFinite(value) ? value : undefined);\n\nconst getLegacyAttribution = (entry: LegacyLongTaskEntry, redactUrl: (url: string) => string) => {\n const attribution = entry.attribution?.[0];\n return attribution?.containerSrc\n ? redactUrl(attribution.containerSrc)\n : attribution?.containerName || attribution?.name || entry.name || 'unknown';\n};\n\nconst getScriptAttributes = (script: ScriptTiming, redactUrl: (url: string) => string): Attributes => ({\n 'long_task.script.duration': script.duration,\n 'long_task.script.execution_start': script.executionStart,\n 'long_task.script.forced_style_and_layout_duration': script.forcedStyleAndLayoutDuration,\n 'long_task.script.invoker': script.invoker,\n 'long_task.script.invoker_type': script.invokerType,\n 'long_task.script.pause_duration': script.pauseDuration,\n 'long_task.script.source_char_position': script.sourceCharPosition,\n 'long_task.script.source_function_name': script.sourceFunctionName,\n 'long_task.script.source_url': script.sourceURL ? redactUrl(script.sourceURL) : '',\n 'long_task.script.start_time': script.startTime,\n 'long_task.script.window_attribution': script.windowAttribution,\n});\n\nexport const createLongTaskPlugin = (options: BkOTLongTaskPluginOptions = {}): BkOTPlugin => {\n const threshold = nonNegativeNumber(options.threshold, DEFAULT_THRESHOLD);\n const warningThreshold = Math.max(threshold, nonNegativeNumber(options.warningThreshold, DEFAULT_WARNING_THRESHOLD));\n let observer: PerformanceObserver | undefined;\n let initialBatchTimer: ReturnType<typeof setTimeout> | undefined;\n let initialEntries: PerformanceEntry[] = [];\n\n return {\n name: 'long-task',\n init(context) {\n if (typeof PerformanceObserver === 'undefined' || typeof performance === 'undefined') return;\n const supportedEntryTypes = PerformanceObserver.supportedEntryTypes ?? [];\n // LoAF 提供脚本归因与 blockingDuration,支持时优先使用;旧浏览器回退到 longtask。\n const entryType = supportedEntryTypes.includes('long-animation-frame')\n ? 'long-animation-frame'\n : supportedEntryTypes.includes('longtask')\n ? 'longtask'\n : undefined;\n if (!entryType) return;\n\n const counter = context.meter.createCounter('browser.long_task.count', {\n description: 'Number of long tasks and long animation frames',\n });\n const durationHistogram = context.meter.createHistogram('browser.long_task.duration', {\n description: 'Duration of long tasks and long animation frames',\n unit: 'ms',\n advice: { explicitBucketBoundaries: DURATION_HISTOGRAM_BOUNDARIES },\n });\n const blockingHistogram = context.meter.createHistogram('browser.long_task.blocking_duration', {\n description: 'Blocking duration of long animation frames',\n unit: 'ms',\n advice: { explicitBucketBoundaries: DURATION_HISTOGRAM_BOUNDARIES },\n });\n\n const emitEntry = (entry: LegacyLongTaskEntry | LongAnimationFrameEntry) => {\n const recordMetrics = shouldRecordSignal(context, 'metrics');\n const recordTrace = shouldRecordSignal(context, 'traces');\n if (!recordMetrics && !recordTrace) return;\n if (!Number.isFinite(entry.duration) || entry.duration < threshold) return;\n const isLoaf = entry.entryType === 'long-animation-frame';\n const loaf = isLoaf ? (entry as LongAnimationFrameEntry) : undefined;\n const scripts = loaf?.scripts ?? [];\n // 选择耗时最长的脚本作为 culprit,完整脚本列表仍作为 span events 保留。\n const culprit = scripts.reduce<ScriptTiming | undefined>(\n (selected, script) => (!selected || script.duration > selected.duration ? script : selected),\n undefined,\n );\n const id = createRandomId('long-task');\n const startTime = performance.timeOrigin + entry.startTime;\n const endTime = startTime + entry.duration;\n const result = entry.duration >= warningThreshold ? RUM_SPAN_RESULT.warning : RUM_SPAN_RESULT.success;\n const entryTypeAttribute = isLoaf ? 'long-animation-frame' : 'long-task';\n const culpritUrl = culprit?.sourceURL ? context.config.hooks.redactUrl(culprit.sourceURL) : undefined;\n const legacyAttribution = isLoaf\n ? undefined\n : getLegacyAttribution(entry as LegacyLongTaskEntry, context.config.hooks.redactUrl);\n const target = culpritUrl || culprit?.sourceFunctionName || legacyAttribution || 'unknown';\n if (recordTrace) {\n const span = context.startSpan(\n 'browser.long_task',\n {\n ...context.config.hooks.getPageAttributes(),\n ...buildRumSpanAttributes({\n duration: entry.duration,\n errorType: result === RUM_SPAN_RESULT.warning ? 'main_thread_blocked' : 'none',\n eventLabel: isLoaf ? '长动画帧' : '长任务',\n result,\n spanSubtype: entryTypeAttribute,\n spanType: RUM_SPAN_TYPE.longtask,\n targetLabel: target,\n targetValue: loaf?.blockingDuration ?? entry.duration,\n }),\n [RUM_ATTRIBUTES.longTaskBlockingDuration]: loaf?.blockingDuration ?? entry.duration,\n [RUM_ATTRIBUTES.longTaskDuration]: entry.duration,\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n [RUM_ATTRIBUTES.longTaskId]: id,\n [RUM_ATTRIBUTES.longTaskIsFrozenFrame]: entry.duration >= FROZEN_FRAME_THRESHOLD,\n [RUM_ATTRIBUTES.longTaskScriptCount]: scripts.length,\n ...(legacyAttribution ? { [RUM_ATTRIBUTES.longTaskCulprit]: legacyAttribution } : {}),\n ...(culprit\n ? {\n [RUM_ATTRIBUTES.longTaskCulprit]: target,\n 'long_task.culprit.duration': culprit.duration,\n 'long_task.culprit.forced_style_and_layout_duration': culprit.forcedStyleAndLayoutDuration,\n 'long_task.culprit.function_name': culprit.sourceFunctionName,\n 'long_task.culprit.invoker_type': culprit.invokerType,\n }\n : {}),\n ...(finiteNumber(loaf?.firstUIEventTimestamp) !== undefined\n ? { 'long_task.first_ui_event_timestamp': loaf!.firstUIEventTimestamp }\n : {}),\n ...(finiteNumber(loaf?.renderStart) !== undefined ? { 'long_task.render_start': loaf!.renderStart } : {}),\n ...(finiteNumber(loaf?.styleAndLayoutStart) !== undefined\n ? { 'long_task.style_and_layout_start': loaf!.styleAndLayoutStart }\n : {}),\n ...(loaf ? { [RUM_ATTRIBUTES.longTaskStartTime]: loaf.startTime } : {}),\n },\n { startTime },\n );\n scripts.forEach(script => {\n span.addEvent(\n 'long_task.script',\n context.applyRedact(getScriptAttributes(script, context.config.hooks.redactUrl)),\n performance.timeOrigin + script.startTime,\n );\n });\n span.end(endTime);\n context.notifyActivity({ endTime, startTime, type: 'long_task' });\n }\n\n if (recordMetrics) {\n const dimensions = context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributesAt(startTime)),\n [RUM_ATTRIBUTES.longTaskEntryType]: entryTypeAttribute,\n });\n counter.add(1, dimensions);\n durationHistogram.record(entry.duration, dimensions);\n if (loaf) blockingHistogram.record(loaf.blockingDuration, dimensions);\n }\n };\n\n const emitEntries = (entries: PerformanceEntry[]) =>\n entries.forEach(entry => emitEntry(entry as LegacyLongTaskEntry));\n try {\n let initializing = true;\n observer = new PerformanceObserver(list => {\n const entries = list.getEntries();\n if (initializing) {\n // buffered 条目可能在 observe() 内同步回调,先合并到下一宏任务,等 Session/View 上下文就绪后再归属。\n initialEntries.push(...entries);\n if (!initialBatchTimer) {\n initialBatchTimer = setTimeout(() => {\n const entriesToEmit = initialEntries;\n initialEntries = [];\n initialBatchTimer = undefined;\n emitEntries(entriesToEmit);\n });\n }\n } else {\n emitEntries(entries);\n }\n });\n observer.observe({ type: entryType, buffered: true });\n initializing = false;\n } catch {\n observer = undefined;\n }\n },\n shutdown() {\n observer?.disconnect();\n observer = undefined;\n clearTimeout(initialBatchTimer);\n initialBatchTimer = undefined;\n initialEntries = [];\n },\n };\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport interface RouteChangeEvent {\n fromUrl: string;\n source: RouteChangeSource;\n toUrl: string;\n}\n\nexport type RouteChangeSource = 'hashchange' | 'popstate' | 'pushState' | 'replaceState';\n\ntype RouteChangeHandler = (event: RouteChangeEvent) => void;\n\nconst subscribers = new Set<RouteChangeHandler>();\n\n// History 只 patch 一次,多个插件/实例共用订阅集合;最后一个订阅者退出时恢复原方法。\nlet lastUrl = '';\nlet originalPushState: typeof history.pushState | undefined;\nlet originalReplaceState: typeof history.replaceState | undefined;\nlet patched = false;\n\nconst getCurrentUrl = () => (typeof location === 'undefined' ? '' : location.href);\n\nconst emitRouteChange = (source: RouteChangeSource, fromUrl: string) => {\n const toUrl = getCurrentUrl();\n lastUrl = toUrl;\n const event: RouteChangeEvent = { fromUrl, source, toUrl };\n\n for (const subscriber of Array.from(subscribers)) {\n subscriber(event);\n }\n};\n\nconst onPopState = () => {\n emitRouteChange('popstate', lastUrl || getCurrentUrl());\n};\n\nconst onHashChange = () => {\n emitRouteChange('hashchange', lastUrl || getCurrentUrl());\n};\n\nconst patchHistory = () => {\n if (patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n patched = true;\n lastUrl = getCurrentUrl();\n originalPushState = history.pushState;\n originalReplaceState = history.replaceState;\n\n history.pushState = function patchedPushState(this: History, data: any, title: string, url?: null | string | URL) {\n const fromUrl = lastUrl || getCurrentUrl();\n // 先执行原方法,让订阅者读取到的 location 与浏览器最终状态一致。\n const result = originalPushState?.apply(this, [data, title, url]);\n emitRouteChange('pushState', fromUrl);\n return result;\n };\n history.replaceState = function patchedReplaceState(\n this: History,\n data: any,\n title: string,\n url?: null | string | URL,\n ) {\n const fromUrl = lastUrl || getCurrentUrl();\n const result = originalReplaceState?.apply(this, [data, title, url]);\n emitRouteChange('replaceState', fromUrl);\n return result;\n };\n window.addEventListener('popstate', onPopState);\n window.addEventListener('hashchange', onHashChange);\n};\n\nconst restoreHistory = () => {\n if (!patched || typeof window === 'undefined' || typeof history === 'undefined') {\n return;\n }\n\n if (originalPushState) {\n history.pushState = originalPushState;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n }\n window.removeEventListener('popstate', onPopState);\n window.removeEventListener('hashchange', onHashChange);\n originalPushState = undefined;\n originalReplaceState = undefined;\n patched = false;\n lastUrl = '';\n};\n\nexport const subscribeRouteChange = (handler: RouteChangeHandler) => {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n patchHistory();\n subscribers.add(handler);\n\n return () => {\n subscribers.delete(handler);\n if (subscribers.size === 0) {\n restoreHistory();\n }\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Page View 插件\n *\n * 自动模式监听初始加载、SPA 路由和 BFCache;手动模式仅由 startView() 划分 View。\n *\n * 上报形态:一个覆盖完整生命周期的 browser.view span,加 create/update/final View Snapshot log。\n *\n * 自动边界与 Datadog 对齐:pathname 或非锚点 hash path 改变才创建 View,query-only 变化不创建。\n *\n * 关键属性:\n * - view.url:当前页面 URL(经 redactUrl 脱敏)\n * - url.previous:跳转前的 URL(来自 route-observer 的 fromUrl,初始 load 为空字符串)\n * - document.referrer:仅 'load' 事件携带\n * - event.source:触发源(load / pushState / replaceState / popstate / hashchange)\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { createRandomId } from '../core/id';\nimport { DURATION_HISTOGRAM_BOUNDARIES } from '../core/metric';\nimport { shouldRecordSignal } from '../core/signal';\nimport { waitForPageActivityEnd } from '../core/page-activity';\nimport { subscribeRouteChange } from '../core/route-observer';\nimport { RUM_SAMPLED_ATTRIBUTE } from '../core/sampling';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type { BkOTStartViewOptions, BkOTViewContext } from '../core/runtime-context';\nimport type { Attributes, Span } from '@opentelemetry/api';\n\ninterface ActiveView {\n attributes: Attributes;\n documentVersion: number;\n id: string;\n loadingTime?: number;\n loadingTimeSource?: 'auto' | 'manual';\n loadingType: ViewLoadingType;\n name: string;\n rawUrl: string;\n snapshotAttributes: Attributes;\n span: Span;\n startedAt: number;\n url: string;\n urlPathGroup: string;\n stopLoadingTime?: () => void;\n}\n\ntype ViewLoadingType = NonNullable<BkOTViewContext['loadingType']>;\n\nconst PROTECTED_ATTRIBUTE_KEYS = new Set<string>([...Object.values(RUM_ATTRIBUTES), RUM_SAMPLED_ATTRIBUTE]);\n\nconst getCurrentUrl = () => {\n if (typeof location === 'undefined') {\n return '';\n }\n return location.href;\n};\n\nconst getNavigationTimingAttributes = (): Attributes => {\n if (typeof performance === 'undefined') return {};\n const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation) return {};\n const attributes: Attributes = {};\n const setTiming = (key: string, value: number) => {\n if (Number.isFinite(value) && value > 0) attributes[key] = value;\n };\n setTiming(RUM_ATTRIBUTES.viewFirstByte, navigation.responseStart);\n setTiming(RUM_ATTRIBUTES.viewDomInteractive, navigation.domInteractive);\n setTiming(RUM_ATTRIBUTES.viewDomContentLoaded, navigation.domContentLoadedEventEnd);\n setTiming(RUM_ATTRIBUTES.viewDomComplete, navigation.domComplete);\n setTiming(RUM_ATTRIBUTES.viewLoadEvent, navigation.loadEventEnd);\n return attributes;\n};\n\nconst getHashPath = (hash: string) => {\n const queryIndex = hash.indexOf('?');\n return queryIndex < 0 ? hash : hash.slice(0, queryIndex);\n};\n\nconst isHashAnchor = (hash: string) => {\n const id = hash.slice(1);\n if (!id || typeof document === 'undefined' || typeof document.getElementById !== 'function') return false;\n try {\n return Boolean(document.getElementById(decodeURIComponent(id)));\n } catch {\n return Boolean(document.getElementById(id));\n }\n};\n\nexport const isViewLocationChange = (fromUrl: string, toUrl: string) => {\n try {\n const base = typeof location === 'undefined' ? 'http://localhost' : location.href;\n const from = new URL(fromUrl, base);\n const to = new URL(toUrl, base);\n return from.pathname !== to.pathname || (!isHashAnchor(to.hash) && getHashPath(from.hash) !== getHashPath(to.hash));\n } catch {\n return fromUrl !== toUrl;\n }\n};\n\nexport const createPageViewPlugin = (): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'page-view',\n init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n let activeView: ActiveView | undefined;\n let renewalView: Omit<ActiveView, 'span' | 'stopLoadingTime'> | undefined;\n const loadingTimeHistogram = context.config.view.loadingTime\n ? context.meter.createHistogram('browser.view.loading_time', {\n description: 'Time from view start until network and DOM activity become idle',\n unit: 'ms',\n advice: { explicitBucketBoundaries: DURATION_HISTOGRAM_BOUNDARIES },\n })\n : undefined;\n\n const clearViewContext = (changedAt = Date.now()) => context.setView({}, changedAt);\n\n const emitViewSnapshot = (\n view: ActiveView,\n state: 'create' | 'final' | 'update',\n timestamp = Date.now(),\n reason?: string,\n ) => {\n if (!shouldRecordSignal(context, 'logs')) return;\n view.documentVersion += 1;\n context.emitLog({\n attributes: {\n ...context.config.hooks.getPageAttributes(),\n ...view.attributes,\n ...view.snapshotAttributes,\n 'event.type': 'view',\n 'view.document_version': view.documentVersion,\n 'view.duration': Math.max(0, timestamp - view.startedAt),\n 'view.is_active': state !== 'final',\n 'view.snapshot.type': state,\n ...(reason ? { 'view.end_reason': reason } : {}),\n ...(view.loadingTime === undefined\n ? {}\n : {\n [RUM_ATTRIBUTES.viewLoadingTime]: view.loadingTime,\n 'view.loading_time.source': view.loadingTimeSource,\n }),\n },\n body: 'browser.view',\n eventName: 'browser.view',\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n timestamp,\n });\n };\n\n const endActiveView = (reason: string, clearContext = true, endedAt = Date.now()) => {\n if (!activeView) {\n if (clearContext) clearViewContext(endedAt);\n return undefined;\n }\n const snapshot = { ...activeView };\n activeView.stopLoadingTime?.();\n if (activeView.loadingTime !== undefined && shouldRecordSignal(context, 'metrics')) {\n loadingTimeHistogram?.record(\n activeView.loadingTime,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n 'view.loading_type': activeView.loadingType,\n }),\n );\n }\n emitViewSnapshot(activeView, 'final', endedAt, reason);\n activeView.span.setAttribute('view.end_reason', reason);\n activeView.span.end(endedAt);\n activeView = undefined;\n if (clearContext) clearViewContext(endedAt);\n const { span: _span, stopLoadingTime: _stopLoadingTime, ...rest } = snapshot;\n return rest;\n };\n\n const setLoadingTime = (view: ActiveView, duration: number, source: 'auto' | 'manual') => {\n if (activeView !== view || !Number.isFinite(duration) || duration < 0) return;\n const navigationTimingAttributes =\n view.loadingType === 'initial_load' ? getNavigationTimingAttributes() : {};\n const updatedAttributes = {\n ...navigationTimingAttributes,\n [RUM_ATTRIBUTES.viewLoadingTime]: duration,\n 'view.loading_time.source': source,\n };\n view.span.setAttributes(updatedAttributes);\n Object.assign(view.snapshotAttributes, updatedAttributes);\n view.loadingTime = duration;\n view.loadingTimeSource = source;\n emitViewSnapshot(view, 'update');\n };\n\n const startLoadingTimeTracking = (view: ActiveView) => {\n if (!context.config.view.loadingTime || document.visibilityState === 'hidden') return;\n let activityResolved = false;\n let activityCandidate: number | undefined;\n let loadResolved = view.loadingType !== 'initial_load';\n let loadCandidate: number | undefined;\n let loadListener: (() => void) | undefined;\n let hidden = false;\n let visibilityListener: (() => void) | undefined;\n\n const tryComplete = () => {\n if (hidden || !activityResolved || !loadResolved || activeView !== view) return;\n const candidates = [activityCandidate, loadCandidate].filter(\n (candidate): candidate is number => candidate !== undefined,\n );\n if (candidates.length) setLoadingTime(view, Math.max(...candidates), 'auto');\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n\n const readLoadEvent = () => {\n const navigation = performance.getEntriesByType?.('navigation')[0] as PerformanceNavigationTiming | undefined;\n if (!navigation?.loadEventEnd) return false;\n loadCandidate = Math.max(0, performance.timeOrigin + navigation.loadEventEnd - view.startedAt);\n loadResolved = true;\n return true;\n };\n\n if (!loadResolved && !readLoadEvent()) {\n const onLoad = () => {\n setTimeout(() => {\n if (activeView !== view) return;\n readLoadEvent();\n loadResolved = true;\n tryComplete();\n });\n };\n window.addEventListener('load', onLoad, { once: true });\n loadListener = () => window.removeEventListener('load', onLoad);\n }\n\n const stopActivity = waitForPageActivityEnd(context, view.startedAt, result => {\n activityResolved = true;\n if (result.hadActivity) activityCandidate = Math.max(0, result.endTime - view.startedAt);\n tryComplete();\n });\n const onVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return;\n hidden = true;\n view.stopLoadingTime?.();\n view.stopLoadingTime = undefined;\n };\n document.addEventListener('visibilitychange', onVisibilityChange);\n visibilityListener = () => document.removeEventListener('visibilitychange', onVisibilityChange);\n view.stopLoadingTime = () => {\n stopActivity();\n loadListener?.();\n loadListener = undefined;\n visibilityListener?.();\n visibilityListener = undefined;\n };\n tryComplete();\n };\n\n const sanitizeViewAttributes = (attributes: Attributes | undefined) => {\n const redacted = context.applyRedact(attributes ?? {});\n return Object.fromEntries(\n Object.entries(redacted).filter(([key, value]) => value !== undefined && !PROTECTED_ATTRIBUTE_KEYS.has(key)),\n ) as Attributes;\n };\n\n const startView = (\n options: BkOTStartViewOptions,\n source: string,\n loadingType: ViewLoadingType,\n previousUrl = activeView?.rawUrl ?? '',\n ) => {\n if (!context.isSessionActive()) return;\n const currentUrl = options.url ?? getCurrentUrl();\n const pathGroup = context.config.hooks.getViewPathGroup(currentUrl);\n const name = options.name ?? pathGroup;\n const customAttributes = sanitizeViewAttributes(options.attributes);\n endActiveView(source, false);\n\n const viewId = createRandomId('view');\n const redactedCurrentUrl = context.config.hooks.redactUrl(currentUrl);\n const redactedPreviousUrl = context.config.hooks.redactUrl(previousUrl);\n const isInitialLoad = loadingType === 'initial_load';\n const startedAt = isInitialLoad && typeof performance !== 'undefined' ? performance.timeOrigin : Date.now();\n const navigationTimingAttributes = isInitialLoad ? getNavigationTimingAttributes() : {};\n\n context.setView(\n {\n attributes: customAttributes,\n id: viewId,\n loadingType,\n name,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n },\n startedAt,\n );\n\n const viewAttributes = {\n ...customAttributes,\n ...context.config.hooks.getPageAttributes(),\n ...navigationTimingAttributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: isInitialLoad ? '文档加载' : '路由切换',\n spanSubtype: isInitialLoad ? 'navigate' : source,\n spanType: isInitialLoad ? RUM_SPAN_TYPE.document : RUM_SPAN_TYPE.route,\n targetLabel: pathGroup,\n traceScene: isInitialLoad ? 'page_load' : 'route_change',\n }),\n [RUM_ATTRIBUTES.viewId]: viewId,\n [RUM_ATTRIBUTES.viewName]: name,\n [RUM_ATTRIBUTES.viewUrl]: redactedCurrentUrl,\n [RUM_ATTRIBUTES.viewUrlPathGroup]: pathGroup,\n [RUM_ATTRIBUTES.viewLoadingType]: loadingType,\n 'url.previous': redactedPreviousUrl,\n 'event.source': source,\n ...(isInitialLoad ? { 'document.referrer': context.config.hooks.redactUrl(document.referrer || '') } : {}),\n };\n const activeViewSpan = context.startSpan('browser.view', viewAttributes, { startTime: startedAt });\n activeView = {\n attributes: customAttributes,\n documentVersion: 0,\n id: viewId,\n loadingType,\n name,\n rawUrl: currentUrl,\n snapshotAttributes: viewAttributes,\n span: activeViewSpan,\n startedAt,\n url: redactedCurrentUrl,\n urlPathGroup: pathGroup,\n };\n emitViewSnapshot(activeView, 'create', startedAt);\n startLoadingTimeTracking(activeView);\n };\n\n const unsubscribeViewCommands = context.onViewCommand(command => {\n if (command.type === 'start') {\n startView(command.options, 'manual', activeView ? 'route_change' : 'initial_load');\n return;\n }\n if (!activeView) return;\n if (command.type === 'set_loading_time') {\n activeView.stopLoadingTime?.();\n activeView.stopLoadingTime = undefined;\n setLoadingTime(activeView, Math.max(0, command.time - activeView.startedAt), 'manual');\n return;\n }\n if (activeView.name === command.name) return;\n activeView.name = command.name;\n activeView.span.setAttribute(RUM_ATTRIBUTES.viewName, command.name);\n activeView.snapshotAttributes[RUM_ATTRIBUTES.viewName] = command.name;\n context.setView({\n attributes: activeView.attributes,\n id: activeView.id,\n loadingType: activeView.loadingType,\n name: activeView.name,\n startedAt: activeView.startedAt,\n url: activeView.url,\n urlPathGroup: activeView.urlPathGroup,\n });\n emitViewSnapshot(activeView, 'update');\n });\n\n let unsubscribeRoute = () => {};\n if (context.config.view.tracking === 'auto') {\n if (!activeView) startView({}, 'load', 'initial_load');\n unsubscribeRoute = subscribeRouteChange(event => {\n if (isViewLocationChange(event.fromUrl, event.toUrl)) {\n startView({}, event.source, 'route_change', event.fromUrl);\n }\n });\n }\n\n const onPageHide = (event: PageTransitionEvent) => {\n if (!event.persisted) endActiveView('pagehide');\n };\n const onPageShow = (event: PageTransitionEvent) => {\n if (event.persisted && context.config.view.tracking === 'auto') {\n startView({}, 'bfCache', 'bf_cache', activeView?.rawUrl);\n }\n };\n const unsubscribeSessionLifecycle = context.onSessionLifecycle(event => {\n if (event.type === 'expired') {\n renewalView = endActiveView('session_expired', true, event.expiredAt);\n return;\n }\n if (renewalView) {\n startView(\n { attributes: renewalView.attributes, name: renewalView.name, url: renewalView.rawUrl },\n 'sessionRenewal',\n 'session_renewal',\n renewalView.rawUrl,\n );\n renewalView = undefined;\n }\n });\n window.addEventListener('pagehide', onPageHide);\n window.addEventListener('pageshow', onPageShow);\n\n teardownCallbacks.push(\n unsubscribeRoute,\n unsubscribeSessionLifecycle,\n unsubscribeViewCommands,\n () => window.removeEventListener('pagehide', onPageHide),\n () => window.removeEventListener('pageshow', onPageShow),\n () => endActiveView('shutdown'),\n );\n },\n shutdown() {\n while (teardownCallbacks.length) {\n teardownCallbacks.pop()?.();\n }\n },\n };\n};\n","export interface ActiveSessionRecord {\n expiresAt: number;\n id: string;\n startedAt: number;\n status: 'active';\n}\n\nexport interface ExpiredSessionRecord {\n expiredAt: number;\n previousId: string;\n reason: SessionExpireReason;\n status: 'expired';\n}\n\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nexport type SessionExpireReason = 'inactivity' | 'maxLifetime';\n\nexport type SessionRecord = ActiveSessionRecord | ExpiredSessionRecord;\n\nconst FUTURE_CLOCK_TOLERANCE_MS = 5_000;\n\nconst isTimestamp = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0;\n\nexport const createActiveSessionRecord = (\n id: string,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n expiresAt: Math.min(now + idleTimeoutMs, now + maxDurationMs),\n id,\n startedAt: now,\n status: 'active',\n});\n\nexport const getSessionExpireReason = (\n record: ActiveSessionRecord,\n now: number,\n maxDurationMs: number,\n): SessionExpireReason | undefined => {\n if (now >= record.startedAt + maxDurationMs) return 'maxLifetime';\n if (now >= record.expiresAt) return 'inactivity';\n return undefined;\n};\n\nexport const expandActiveSession = (\n record: ActiveSessionRecord,\n now: number,\n idleTimeoutMs: number,\n maxDurationMs: number,\n): ActiveSessionRecord => ({\n ...record,\n expiresAt: Math.min(now + idleTimeoutMs, record.startedAt + maxDurationMs),\n});\n\nexport const parseSessionRecord = (raw: null | string, now: number, maxDurationMs: number): null | SessionRecord => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<SessionRecord>;\n if (\n parsed.status === 'active' &&\n typeof parsed.id === 'string' &&\n parsed.id.length > 0 &&\n isTimestamp(parsed.startedAt) &&\n // 容忍轻微时钟漂移,但拒绝远期时间与超出最大生命周期的记录,避免脏 storage 永久续期。\n parsed.startedAt <= now + FUTURE_CLOCK_TOLERANCE_MS &&\n isTimestamp(parsed.expiresAt) &&\n parsed.expiresAt >= parsed.startedAt &&\n parsed.expiresAt <= parsed.startedAt + maxDurationMs\n ) {\n return parsed as ActiveSessionRecord;\n }\n if (\n parsed.status === 'expired' &&\n typeof parsed.previousId === 'string' &&\n parsed.previousId.length > 0 &&\n (parsed.reason === 'inactivity' || parsed.reason === 'maxLifetime') &&\n isTimestamp(parsed.expiredAt) &&\n parsed.expiredAt <= now + FUTURE_CLOCK_TOLERANCE_MS\n ) {\n return parsed as ExpiredSessionRecord;\n }\n } catch {\n /* JSON 损坏视为无记录 */\n }\n return null;\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Session 插件\n *\n * 生命周期:\n * - 首次访问创建 session;用户交互会续期有效 session,或重建已过期 session\n * - 固定 15 分钟无活动 / 4 小时最长生命周期,任一到达时立即清理运行时 session.id\n * - 页面重新可见时只续期有效 session,不会复活已过期 session\n * - 跨 tab 共享持久化状态;本 tab 过期后不会被其他 tab 的新 session 被动复活\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { createRandomId } from '../core/id';\nimport { hashString } from '../core/throttle';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport {\n createActiveSessionRecord,\n expandActiveSession,\n getSessionExpireReason,\n parseSessionRecord,\n} from './session-state';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { ActiveSessionRecord, ExpiredSessionRecord, SessionExpireReason, SessionRecord } from './session-state';\n\nexport interface BkOTSessionConfig {\n /** 以 0~1 表示的 Session 采样率,默认 1。 */\n sampleRate?: number;\n // 自定义 storage key;不传则按 endpoint host 自动生成隔离后缀\n storageKey?: string;\n}\n\nconst SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;\nconst SESSION_MAX_DURATION_MS = 4 * 60 * 60 * 1000;\nconst ACTIVITY_THROTTLE_MS = 1_000;\nconst VISIBILITY_CHECK_MS = 60_000;\nconst ACTIVITY_EVENTS: Array<keyof WindowEventMap> = ['click', 'keydown', 'scroll', 'touchstart'];\nconst memoryRecords = new Map<string, string>();\n\ninterface SessionRenewResult {\n previousId?: string;\n reason?: 'init' | SessionExpireReason;\n record: ActiveSessionRecord;\n}\n\ninterface SessionStorage {\n isLocalStorage: () => boolean;\n read: () => null | string;\n write: (record: SessionRecord) => void;\n}\n\nconst createSessionStorage = (storageKey: string): SessionStorage => {\n let localStorageAvailable = typeof window !== 'undefined';\n\n const read = () => {\n if (localStorageAvailable) {\n try {\n return window.localStorage.getItem(storageKey);\n } catch {\n localStorageAvailable = false;\n }\n }\n return memoryRecords.get(storageKey) ?? null;\n };\n\n const write = (record: SessionRecord) => {\n const serialized = JSON.stringify(record);\n memoryRecords.set(storageKey, serialized);\n if (!localStorageAvailable) return;\n try {\n window.localStorage.setItem(storageKey, serialized);\n } catch {\n localStorageAvailable = false;\n }\n };\n\n read();\n return { isLocalStorage: () => localStorageAvailable, read, write };\n};\n\nconst buildDefaultStorageKey = (endpoint: string, applicationName: string) => {\n let suffix = '';\n try {\n suffix = new URL(endpoint).host;\n } catch {\n suffix = endpoint;\n }\n return `bk_ot_session__${hashString(`${applicationName}\\0${suffix || 'default'}`)}`;\n};\n\nconst emitSessionEvent = (context: BkOTRuntimeContext, result: SessionRenewResult) => {\n if (!result.reason) return;\n context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: result.reason === 'init' ? 'session.start' : 'session.rotate',\n attributes: {\n ...context.config.hooks.getPageAttributes(),\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: result.record.id,\n [RUM_ATTRIBUTES.sessionStartTime]: result.record.startedAt,\n 'session.rotate.reason': result.reason,\n ...(result.previousId ? { 'session.previous_id': result.previousId } : {}),\n },\n });\n};\n\nexport const createSessionPlugin = (options: BkOTSessionConfig = {}): BkOTPlugin => {\n const teardownCallbacks: Array<() => void> = [];\n\n return {\n name: 'session',\n init(context) {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n context.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: createRandomId('session'),\n });\n return;\n }\n\n const storageKey =\n options.storageKey ??\n buildDefaultStorageKey(context.config.transport.endpoint, context.config.application.name);\n const storage = createSessionStorage(storageKey);\n const documentStartedAt =\n typeof performance !== 'undefined' && Number.isFinite(performance.timeOrigin)\n ? performance.timeOrigin\n : Date.now();\n let currentSessionId: string | undefined;\n let expirationTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const readRecord = (now = Date.now()) => parseSessionRecord(storage.read(), now, SESSION_MAX_DURATION_MS);\n\n const clearExpirationTimeout = () => {\n if (expirationTimeout !== undefined) {\n clearTimeout(expirationTimeout);\n expirationTimeout = undefined;\n }\n };\n\n const deactivate = (reason: 'external' | SessionExpireReason, expiredAt = Date.now()) => {\n const expiredSessionId = currentSessionId;\n if (!expiredSessionId) return;\n context.notifySessionLifecycle({\n expiredAt,\n id: expiredSessionId,\n reason,\n type: 'expired',\n });\n clearExpirationTimeout();\n currentSessionId = undefined;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n },\n expiredAt,\n );\n void context.flush();\n };\n\n const expireCurrentSession = () => {\n const now = Date.now();\n const record = readRecord(now);\n if (!record) {\n deactivate('external', now);\n return;\n }\n if (record.status === 'expired') {\n deactivate(record.reason, record.expiredAt);\n return;\n }\n if (record.id !== currentSessionId) {\n deactivate('external', now);\n return;\n }\n const reason = getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS);\n if (!reason) {\n scheduleExpiration(record);\n return;\n }\n const expired: ExpiredSessionRecord = {\n expiredAt: now,\n previousId: record.id,\n reason,\n status: 'expired',\n };\n storage.write(expired);\n deactivate(reason, now);\n };\n\n const scheduleExpiration = (record: ActiveSessionRecord) => {\n clearExpirationTimeout();\n const expiresAt = Math.min(record.expiresAt, record.startedAt + SESSION_MAX_DURATION_MS);\n expirationTimeout = setTimeout(expireCurrentSession, Math.max(0, expiresAt - Date.now()));\n };\n\n const activate = (\n record: ActiveSessionRecord,\n lifecycle?: {\n previousId?: string;\n reason: 'inactivity' | 'init' | 'maxLifetime' | 'shared';\n },\n ) => {\n const changed = record.id !== currentSessionId;\n currentSessionId = record.id;\n context.setRuntimeAttributes(\n {\n [RUM_ATTRIBUTES.sessionHasReplay]: false,\n [RUM_ATTRIBUTES.sessionId]: record.id,\n },\n Math.min(record.startedAt, documentStartedAt),\n );\n scheduleExpiration(record);\n if (changed && lifecycle) {\n context.notifySessionLifecycle({\n id: record.id,\n previousId: lifecycle.previousId,\n reason: lifecycle.reason,\n startedAt: record.startedAt,\n type: 'renewed',\n });\n }\n };\n\n const expandOrRenew = (isInitialization = false) => {\n const now = Date.now();\n const record = readRecord(now);\n if (record?.status === 'active' && !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)) {\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded, currentSessionId ? undefined : { reason: 'shared' });\n return;\n }\n\n const previousId = record?.status === 'expired' ? record.previousId : record?.id;\n const reason =\n record?.status === 'expired'\n ? record.reason\n : record\n ? getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n : isInitialization\n ? 'init'\n : undefined;\n const created = createActiveSessionRecord(\n createRandomId('session'),\n now,\n SESSION_IDLE_TIMEOUT_MS,\n SESSION_MAX_DURATION_MS,\n );\n storage.write(created);\n activate(created, {\n previousId,\n reason: reason ?? 'init',\n });\n emitSessionEvent(context, {\n previousId,\n reason: reason ?? 'init',\n record: created,\n });\n };\n\n const expandOnly = () => {\n if (!currentSessionId) return;\n const now = Date.now();\n const record = readRecord(now);\n if (\n record?.status !== 'active' ||\n record.id !== currentSessionId ||\n getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n expireCurrentSession();\n return;\n }\n const expanded = expandActiveSession(record, now, SESSION_IDLE_TIMEOUT_MS, SESSION_MAX_DURATION_MS);\n storage.write(expanded);\n activate(expanded);\n };\n\n expandOrRenew(true);\n\n let lastActivityAt = 0;\n const onActivity = () => {\n const now = Date.now();\n if (now - lastActivityAt < ACTIVITY_THROTTLE_MS) return;\n lastActivityAt = now;\n expandOrRenew();\n };\n const activityListenerOptions: AddEventListenerOptions = { capture: true, passive: true };\n ACTIVITY_EVENTS.forEach(eventName => window.addEventListener(eventName, onActivity, activityListenerOptions));\n teardownCallbacks.push(() => {\n ACTIVITY_EVENTS.forEach(eventName =>\n window.removeEventListener(eventName, onActivity, activityListenerOptions),\n );\n });\n\n const expandWhenVisible = () => {\n if (document.visibilityState === 'visible') expandOnly();\n };\n document.addEventListener('visibilitychange', expandWhenVisible);\n const visibilityInterval = window.setInterval(expandWhenVisible, VISIBILITY_CHECK_MS);\n teardownCallbacks.push(() => {\n document.removeEventListener('visibilitychange', expandWhenVisible);\n window.clearInterval(visibilityInterval);\n });\n\n const onStorage = (event: StorageEvent) => {\n if (!storage.isLocalStorage() || event.storageArea !== window.localStorage || event.key !== storageKey) return;\n const now = Date.now();\n const record = parseSessionRecord(event.newValue, now, SESSION_MAX_DURATION_MS);\n if (\n record?.status === 'active' &&\n record.id === currentSessionId &&\n !getSessionExpireReason(record, now, SESSION_MAX_DURATION_MS)\n ) {\n scheduleExpiration(record);\n return;\n }\n // 当前 tab 一旦失效,不被其他 tab 新建的 session 被动复活;本 tab 发生交互后再接管共享 session。\n deactivate(\n record?.status === 'expired' ? record.reason : 'external',\n record?.status === 'expired' ? record.expiredAt : now,\n );\n };\n window.addEventListener('storage', onStorage);\n teardownCallbacks.push(() => window.removeEventListener('storage', onStorage));\n teardownCallbacks.push(clearExpirationTimeout);\n },\n shutdown() {\n while (teardownCallbacks.length) teardownCallbacks.pop()?.();\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * Web Vitals 插件\n *\n * 基于 google/web-vitals 库(attribution 版本)采集 Core Web Vitals:\n * CLS / FCP / INP / LCP / TTFB\n *\n * 维度分层(关键设计):\n * - metric (histogram):仅带低基数维度(`view.url_path_group` / navigation.type / vital.name / vital.rating),\n * 适合做 P50 / P75 / P99 聚合统计,避免维度爆炸\n * - span (browser.web_vital):带 vital.id(唯一)、vital.value 与 attribution 详情\n * (LCP 元素 url / CLS shift target / INP interaction target 等高基数字段),\n * 方便按个例排查\"哪个元素拖累了 LCP\"\n *\n * Histogram 设计:\n * - browser.web_vital.cls:单位 '1'(无量纲),CLS 是累积分数,不能与 ms 混用\n * - browser.web_vital.duration:单位 'ms',FCP/INP/LCP/TTFB 共享,靠 vital.metric 维度区分\n *\n * Navigation Type:\n * 优先使用 metric.navigationType(web-vitals 内部计算,含 'back-forward-cache' 等准确值),\n * fallback 到 PerformanceNavigationTiming.type(注意 web-vitals 用连字符 'back-forward',\n * PerformanceNavigationTiming 用下划线 'back_forward',统一成连字符)。\n *\n * View 边界:FCP/LCP/TTFB 使用 web-vitals 的条目时间回查历史 View;CLS/INP 按 SDK View 重置,\n * CLS 使用 session window,INP 使用当前 View 的 p98 交互候选。\n *\n * 生命周期保护:\n * - disposed 标志位:shutdown 后所有回调入口 early return,避免向已销毁的 provider 上报\n * - dynamic import 完成后再次检查 disposed:处理\"start() 完成前 shutdown()\"的 race\n * - dynamic import 自身 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n * 即使加载失败也不影响整体启动\n */\n\nimport { type Attributes } from '@opentelemetry/api';\n\nimport { createRandomId } from '../core/id';\nimport { CLS_HISTOGRAM_BOUNDARIES, DURATION_HISTOGRAM_BOUNDARIES } from '../core/metric';\nimport { shouldRecordSignal } from '../core/signal';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, getVitalResult, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin } from '../core/plugin';\nimport type * as WebVitalsAttribution from 'web-vitals/attribution';\nimport type { MetricWithAttribution } from 'web-vitals/attribution';\n\ninterface EventTimingEntry extends PerformanceEntry {\n interactionId?: number;\n processingEnd?: number;\n processingStart?: number;\n target?: EventTarget | null;\n}\n\ninterface LayoutShiftEntry extends PerformanceEntry {\n hadRecentInput?: boolean;\n sources?: Array<{ node?: Node | null }>;\n value?: number;\n}\n\ntype NavigationTypeName =\n | 'back-forward'\n | 'back-forward-cache'\n | 'navigate'\n | 'prerender'\n | 'reload'\n | 'restore'\n | 'unknown';\n\ninterface ViewVitalState {\n cls: number;\n clsWindowEnd?: number;\n clsWindowLargestShiftTarget?: string;\n clsWindowLargestShiftValue: number;\n clsWindowStart?: number;\n clsWindowValue: number;\n interactionCountAtStart?: number;\n interactionIdMax: number;\n interactionIdMin: number;\n inpCandidates: InpCandidate[];\n id: string;\n largestShiftTarget?: string;\n largestShiftValue: number;\n runtimeAttributes: Attributes;\n startedAt: number;\n}\n\ninterface InpCandidate {\n duration: number;\n inputDelay: number;\n interactionId: number;\n interactionTarget?: string;\n interactionType: string;\n presentationDelay: number;\n processingDuration: number;\n}\n\ntype VitalLike = Pick<\n MetricWithAttribution,\n 'attribution' | 'entries' | 'id' | 'name' | 'navigationType' | 'rating' | 'value'\n>;\n\nconst FALLBACK_NAVIGATION_TYPE: NavigationTypeName = 'unknown';\nconst MAX_INP_CANDIDATES = 10;\nconst MAX_INP_VALUE = 60_000;\nconst MAX_CLS_WINDOW_DURATION = 5_000;\nconst MAX_CLS_UPDATE_GAP = 1_000;\n\nexport const isSameViewId = (currentViewId: string | undefined, nextViewId: string | undefined) =>\n Boolean(nextViewId) && currentViewId === nextViewId;\n\n// PerformanceNavigationTiming.type 用下划线('back_forward'),web-vitals 用连字符('back-forward'),\n// 统一成 web-vitals 的连字符风格作为 fallback,下游聚合时维度值一致\nconst normalizeNavigationType = (raw: string | undefined): NavigationTypeName => {\n if (!raw) return FALLBACK_NAVIGATION_TYPE;\n return raw.replace(/_/g, '-') as NavigationTypeName;\n};\n\nconst computeFallbackNavigationType = (): NavigationTypeName => {\n if (typeof performance === 'undefined') return FALLBACK_NAVIGATION_TYPE;\n const entry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n return normalizeNavigationType(entry?.type);\n};\n\nconst getRating = (name: 'CLS' | 'INP', value: number): VitalLike['rating'] => {\n const [good, poor] = name === 'CLS' ? [0.1, 0.25] : [200, 500];\n return value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n};\n\nconst getTargetName = (target: EventTarget | Node | null | undefined) => {\n if (typeof Element === 'undefined' || !(target instanceof Element)) return undefined;\n return (\n target.getAttribute('data-bk-action-name') ??\n target.getAttribute('aria-label') ??\n target.getAttribute('name') ??\n target.tagName.toLowerCase()\n );\n};\n\nconst getInteractionCount = () => {\n if (typeof performance === 'undefined') return undefined;\n const value = (performance as Performance & { interactionCount?: number }).interactionCount;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n};\n\nexport const updateViewCls = (view: ViewVitalState, entry: LayoutShiftEntry) => {\n const value = entry.value ?? 0;\n if (entry.hadRecentInput || !Number.isFinite(value)) return;\n const shouldCreateWindow =\n view.clsWindowStart === undefined ||\n entry.startTime - (view.clsWindowEnd ?? entry.startTime) >= MAX_CLS_UPDATE_GAP ||\n entry.startTime - view.clsWindowStart >= MAX_CLS_WINDOW_DURATION;\n if (shouldCreateWindow) {\n view.clsWindowStart = entry.startTime;\n view.clsWindowValue = value;\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n } else {\n view.clsWindowValue += value;\n if (value > view.clsWindowLargestShiftValue) {\n view.clsWindowLargestShiftValue = value;\n view.clsWindowLargestShiftTarget = getTargetName(entry.sources?.[0]?.node);\n }\n }\n view.clsWindowEnd = entry.startTime;\n if (view.clsWindowValue > view.cls) {\n view.cls = Math.round(view.clsWindowValue * 10_000) / 10_000;\n view.largestShiftValue = view.clsWindowLargestShiftValue;\n view.largestShiftTarget = view.clsWindowLargestShiftTarget;\n }\n};\n\nexport const updateViewInp = (view: ViewVitalState, entry: EventTimingEntry) => {\n if (!entry.interactionId || !Number.isFinite(entry.duration)) return;\n view.interactionIdMin = Math.min(view.interactionIdMin, entry.interactionId);\n view.interactionIdMax = Math.max(view.interactionIdMax, entry.interactionId);\n const processingStart = entry.processingStart ?? entry.startTime;\n const processingEnd = entry.processingEnd ?? processingStart;\n const candidate: InpCandidate = {\n duration: Math.min(entry.duration, MAX_INP_VALUE),\n inputDelay: Math.max(0, processingStart - entry.startTime),\n interactionId: entry.interactionId,\n interactionTarget: getTargetName(entry.target),\n interactionType: entry.name,\n presentationDelay: Math.max(0, entry.startTime + entry.duration - processingEnd),\n processingDuration: Math.max(0, processingEnd - processingStart),\n };\n const index = view.inpCandidates.findIndex(item => item.interactionId === entry.interactionId);\n if (index >= 0) {\n if (candidate.duration > view.inpCandidates[index].duration) view.inpCandidates[index] = candidate;\n } else {\n view.inpCandidates.push(candidate);\n }\n view.inpCandidates.sort((left, right) => right.duration - left.duration).splice(MAX_INP_CANDIDATES);\n};\n\nexport const getViewInp = (view: ViewVitalState): InpCandidate | undefined => {\n const nativeCount = getInteractionCount();\n const interactionCount =\n nativeCount !== undefined && view.interactionCountAtStart !== undefined\n ? Math.max(0, nativeCount - view.interactionCountAtStart)\n : view.interactionIdMin === Infinity\n ? 0\n : (view.interactionIdMax - view.interactionIdMin) / 7 + 1;\n if (!view.inpCandidates.length) return undefined;\n return view.inpCandidates[Math.min(view.inpCandidates.length - 1, Math.floor(interactionCount / 50))];\n};\n\n// 仅挑选低基数、可枚举的字段进 metric attributes,避免指标维度爆炸\nconst buildMetricDimensions = (metric: VitalLike, fallbackNavType: NavigationTypeName): Attributes => ({\n // 优先使用 metric.navigationType(含 bfcache 等准确值),fallback 到首次 navigation entry\n 'rum.navigation.type': metric.navigationType ?? fallbackNavType,\n [RUM_ATTRIBUTES.vitalMetric]: metric.name.toLowerCase(),\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n});\n\n// 把 attribution 中价值最高的字段抽出来(仅放进 span,不放进 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('vital.lcp.url', getAttrValue('url'), redactUrl);\n setIfDefined('vital.lcp.target', getAttrValue('target'));\n setIfDefined('vital.lcp.element_render_delay', getAttrValue('elementRenderDelay'));\n setIfDefined('vital.lcp.resource_load_duration', getAttrValue('resourceLoadDuration'));\n setIfDefined('vital.lcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n break;\n case 'CLS':\n setIfDefined('vital.cls.largest_shift_target', getAttrValue('largestShiftTarget'));\n setIfDefined('vital.cls.largest_shift_value', getAttrValue('largestShiftValue'));\n setIfDefined('vital.cls.load_state', getAttrValue('loadState'));\n break;\n case 'INP':\n setIfDefined('vital.inp.interaction_target', getAttrValue('interactionTarget'));\n setIfDefined('vital.inp.interaction_type', getAttrValue('interactionType'));\n setIfDefined('vital.inp.input_delay', getAttrValue('inputDelay'));\n setIfDefined('vital.inp.processing_duration', getAttrValue('processingDuration'));\n setIfDefined('vital.inp.presentation_delay', getAttrValue('presentationDelay'));\n break;\n case 'FCP':\n setIfDefined('vital.fcp.time_to_first_byte', getAttrValue('timeToFirstByte'));\n setIfDefined('vital.fcp.load_state', getAttrValue('loadState'));\n break;\n case 'TTFB':\n setIfDefined('vital.ttfb.waiting_duration', getAttrValue('waitingDuration'));\n setIfDefined('vital.ttfb.dns_duration', getAttrValue('dnsDuration'));\n setIfDefined('vital.ttfb.connection_duration', getAttrValue('connectionDuration'));\n setIfDefined('vital.ttfb.request_duration', getAttrValue('requestDuration'));\n break;\n default:\n break;\n }\n return result;\n};\n\nexport const createWebVitalsPlugin = (): BkOTPlugin => {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n return {\n name: 'web-vitals',\n async init(context) {\n if (typeof window === 'undefined') {\n return;\n }\n\n // dynamic import 自包 try/catch:与 PluginManager 的 per-plugin 隔离形成纵深防御,\n // 离线 / chunk 加载失败时不影响其他插件,且 debug 模式留下排查痕迹\n let webVitalsModule: typeof WebVitalsAttribution;\n try {\n webVitalsModule = await import('web-vitals/attribution');\n } catch (error) {\n if (context.config.debug) {\n globalThis.console?.warn('[bk-ot] web-vitals load failed:', error);\n }\n return;\n }\n // import 期间用户可能已经 shutdown:此后再注册回调会向已销毁 provider 上报\n if (disposed) return;\n\n const { onFCP, onLCP, onTTFB } = webVitalsModule;\n const fallbackNavType = computeFallbackNavigationType();\n const clsHistogram = context.meter.createHistogram('browser.web_vital.cls', {\n unit: '1',\n description: 'Cumulative Layout Shift',\n advice: { explicitBucketBoundaries: CLS_HISTOGRAM_BOUNDARIES },\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 advice: { explicitBucketBoundaries: DURATION_HISTOGRAM_BOUNDARIES },\n });\n\n const recordSpan = (metric: VitalLike, runtimeAttributes: Attributes, contextTime?: number) => {\n if (!shouldRecordSignal(context, 'traces')) return;\n const metricName = metric.name.toLowerCase();\n const span = context.startSpan(\n 'browser.web_vital',\n {\n ...context.config.hooks.getPageAttributes(),\n ...runtimeAttributes,\n ...buildMetricDimensions(metric, fallbackNavType),\n ...buildRumSpanAttributes({\n duration: metric.name === 'CLS' ? undefined : metric.value,\n errorType: metric.rating === 'good' ? 'none' : 'slow',\n eventLabel: 'Web 指标',\n result: getVitalResult(metric.rating),\n spanSubtype: metricName,\n spanType: RUM_SPAN_TYPE.vital,\n targetLabel: metricName,\n targetValue: metric.value,\n }),\n [RUM_ATTRIBUTES.vitalMetric]: metricName,\n [RUM_ATTRIBUTES.vitalValue]: metric.value,\n [RUM_ATTRIBUTES.vitalRating]: metric.rating,\n // 单条度量唯一 ID 仅放进 span,避免污染 metric 维度\n 'vital.id': metric.id,\n ...pickAttribution(metric, context.config.hooks.redactUrl),\n },\n contextTime === undefined ? undefined : { contextTime },\n );\n span.end();\n };\n\n const recordDurationMetric = (metric: VitalLike) => {\n // 防御非法值:极端浏览器实现 bug 可能给出 NaN/Infinity,OT 后端会拒收\n if (\n disposed ||\n !Number.isFinite(metric.value) ||\n (!shouldRecordSignal(context, 'metrics') && !shouldRecordSignal(context, 'traces'))\n )\n return;\n const firstEntry = metric.entries[0];\n const contextTime =\n firstEntry && typeof performance !== 'undefined' ? performance.timeOrigin + firstEntry.startTime : undefined;\n const runtimeAttributes =\n contextTime === undefined ? context.getRuntimeAttributes() : context.getRuntimeAttributesAt(contextTime);\n if (shouldRecordSignal(context, 'metrics')) {\n durationHistogram.record(\n metric.value,\n context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(runtimeAttributes),\n ...buildMetricDimensions(metric, fallbackNavType),\n }),\n );\n }\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n let currentView: undefined | ViewVitalState;\n let eventObserver: PerformanceObserver | undefined;\n let layoutShiftObserver: PerformanceObserver | undefined;\n\n const recordViewMetric = (metric: VitalLike, runtimeAttributes: Attributes, contextTime: number) => {\n if (shouldRecordSignal(context, 'metrics')) {\n const metricAttributes = context.applyRedact({\n ...context.config.hooks.getMetricAttributes(),\n ...getMetricViewAttributes(runtimeAttributes),\n ...buildMetricDimensions(metric, fallbackNavType),\n });\n if (metric.name === 'CLS') clsHistogram.record(metric.value, metricAttributes);\n else durationHistogram.record(metric.value, metricAttributes);\n }\n recordSpan(metric, runtimeAttributes, contextTime);\n };\n\n const finalizeView = () => {\n const view = currentView;\n if (\n !view ||\n (!shouldRecordSignal(context, 'metrics') && !shouldRecordSignal(context, 'traces'))\n )\n return;\n recordViewMetric(\n {\n attribution: {\n largestShiftTarget: view.largestShiftTarget,\n largestShiftValue: view.largestShiftValue,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'CLS',\n navigationType: fallbackNavType,\n rating: getRating('CLS', view.cls),\n value: view.cls,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n const inp = getViewInp(view);\n if (inp) {\n recordViewMetric(\n {\n attribution: {\n inputDelay: inp.inputDelay,\n interactionTarget: inp.interactionTarget,\n interactionType: inp.interactionType,\n presentationDelay: inp.presentationDelay,\n processingDuration: inp.processingDuration,\n },\n entries: [],\n id: createRandomId('vital'),\n name: 'INP',\n navigationType: fallbackNavType,\n rating: getRating('INP', inp.duration),\n value: inp.duration,\n } as VitalLike,\n view.runtimeAttributes,\n view.startedAt,\n );\n }\n };\n\n const unsubscribeView = context.onViewChange(view => {\n const activeVitalView = currentView;\n if (activeVitalView && isSameViewId(activeVitalView.id, view.id)) {\n activeVitalView.runtimeAttributes = {\n ...activeVitalView.runtimeAttributes,\n [RUM_ATTRIBUTES.viewId]: view.id,\n [RUM_ATTRIBUTES.viewName]: view.name,\n [RUM_ATTRIBUTES.viewUrl]: view.url,\n [RUM_ATTRIBUTES.viewUrlPathGroup]: view.urlPathGroup,\n };\n return;\n }\n finalizeView();\n currentView = view.id\n ? {\n cls: 0,\n clsWindowLargestShiftValue: 0,\n clsWindowValue: 0,\n interactionCountAtStart: getInteractionCount(),\n interactionIdMax: 0,\n interactionIdMin: Infinity,\n inpCandidates: [],\n id: view.id,\n largestShiftValue: 0,\n runtimeAttributes: context.getRuntimeAttributes(),\n startedAt: view.startedAt ?? Date.now(),\n }\n : undefined;\n });\n\n if (typeof PerformanceObserver !== 'undefined') {\n if (PerformanceObserver.supportedEntryTypes?.includes('layout-shift')) {\n layoutShiftObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as LayoutShiftEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt) continue;\n updateViewCls(currentView, entry);\n }\n });\n layoutShiftObserver.observe({ buffered: true, type: 'layout-shift' });\n }\n if (PerformanceObserver.supportedEntryTypes?.includes('event')) {\n eventObserver = new PerformanceObserver(list => {\n if (!currentView) return;\n for (const entry of list.getEntries() as EventTimingEntry[]) {\n const startedAt = performance.timeOrigin + entry.startTime;\n if (startedAt < currentView.startedAt || !entry.interactionId || !Number.isFinite(entry.duration))\n continue;\n updateViewInp(currentView, entry);\n }\n });\n eventObserver.observe({ buffered: true, durationThreshold: 40, type: 'event' });\n }\n }\n onFCP(recordDurationMetric);\n onLCP(recordDurationMetric);\n onTTFB(recordDurationMetric);\n\n cleanup = () => {\n finalizeView();\n currentView = undefined;\n unsubscribeView();\n layoutShiftObserver?.disconnect();\n eventObserver?.disconnect();\n };\n },\n shutdown() {\n // web-vitals 的 onXXX 不返回 unsubscribe,无法真正取消订阅;\n // 用 disposed 标志位让回调入口 early return 即可\n disposed = true;\n cleanup?.();\n cleanup = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/**\n * WebSocket 插件\n *\n * 通过 Proxy 包装 window.WebSocket,对每个 ws 实例采集:\n * - span (`websocket.connect`):仅覆盖建连阶段,避免长连接 span 永远不结束\n * span attributes: url.full / url.scheme / server.address / network.protocol.name\n * - histogram (`browser.websocket.connect.duration`):成功建连耗时(ms),便于聚合 P99\n * - counter (`browser.websocket.message.count` / `.bytes`):双向消息数与字节数,\n * 按 websocket.direction (in/out) 维度区分\n * - counter (`browser.websocket.error.count`):错误次数,按 websocket.error.phase\n * (connect / runtime) 区分建连失败与运行时中断\n * - log (`websocket.error` / `websocket.close`):错误与关闭事件详情;error 按 url\n * 做 throttle 防 reconnect 风暴\n *\n * Patch 策略(关键设计):\n * 使用 Proxy(NativeWebSocket, { construct }),相比传统 monkey-patch 优势:\n * - 自动透传静态常量(WebSocket.OPEN / CONNECTING 等),无需手动复制;\n * Object.assign 复制会失败,因为这些常量是 non-enumerable\n * - 完美支持 `class MyWS extends WebSocket` 子类化场景:\n * Reflect.construct 用 newTarget=MyWS 创建对象,原型链与 instanceof 都正确\n * - 透明的 Symbol.hasInstance 行为,业务侧 instanceof 检查正常\n *\n * 已知行为与限制:\n * - send 方法在每个实例上覆盖(不在 prototype),原因是 native ws 没有 send 回调,\n * 只能包装实例方法计数 outbound;副作用是若业务 `delete socket.send` 会失去统计\n * - shutdown 后已存在的 socket 实例仍带着我们的事件监听与 send 包装;\n * 用 disposed 标志位让回调入口 early return,新建的 ws 走原生路径\n * - 大消息(> 64KB string)字节计算降级为 string.length 估算(CJK 等多字节字符会偏小),\n * 避免 TextEncoder().encode() 的 O(n) 开销在 IM / 流式场景拖累主线程\n * - error throttle:默认 60s 窗口最多 5 条同 url 错误日志,counter 仍精确累计不受影响\n *\n * 多 patch 兼容:\n * shutdown 时检查 window.WebSocket 是否仍是我们的 Proxy,若被其他库覆盖则不强行恢复,\n * 避免抹掉后来者的 patch\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\n\nimport { DURATION_HISTOGRAM_BOUNDARIES } from '../core/metric';\nimport { shouldRecordSignal } from '../core/signal';\nimport { getMetricViewAttributes } from '../core/telemetry-attributes';\nimport { createThrottle } from '../core/throttle';\nimport { shouldIgnoreUrl } from '../core/url';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\n\nimport type { BkOTPlugin, BkOTRuntimeContext } from '../core/plugin';\nimport type { Span } from '@opentelemetry/api';\n\n// 64KB 以下精确计算 UTF-8 字节,超过则用 string.length 粗估,避免 IM 大文本拖慢主线程\nconst BYTES_PRECISE_THRESHOLD = 64 * 1024;\n// 错误日志节流:60s / 5 条同 url,防 reconnect 风暴\nconst ERROR_THROTTLE_WINDOW_MS = 60_000;\nconst ERROR_THROTTLE_MAX_PER_WINDOW = 5;\n\nconst getMessageByteLength = (data: unknown): number => {\n if (data == null) return 0;\n if (typeof data === 'string') {\n if (data.length > BYTES_PRECISE_THRESHOLD || typeof TextEncoder === 'undefined') {\n // 粗估:纯 ASCII 准确,CJK / emoji 会偏小;权衡性能与准确性\n return data.length;\n }\n return 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\ninterface WebSocketAttributeBundle {\n metricAttributes: Record<string, string>;\n spanAttributes: Record<string, string>;\n}\n\nconst buildWebSocketAttributes = (url: string, redactUrl: (url: string) => string): WebSocketAttributeBundle => {\n const spanAttributes: Record<string, string> = {\n 'network.protocol.name': 'websocket',\n 'url.full': redactUrl(url),\n };\n const metricAttributes: Record<string, string> = {\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 // url.scheme 区分 ws / wss,下游能按加密/非加密分组\n spanAttributes['url.scheme'] = parsed.protocol.replace(':', '');\n // server.address 进 metric 是中等基数(按域名维度通常 < 10),对 metric 系列影响可控\n metricAttributes['server.address'] = parsed.host;\n metricAttributes['url.scheme'] = spanAttributes['url.scheme'];\n } catch {\n /* malformed URL:保留 redacted 原 url,跳过 host / scheme 解析 */\n }\n\n return { metricAttributes, spanAttributes };\n};\n\ninterface InstrumentDeps {\n bytesCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n connectDurationHistogram: ReturnType<BkOTRuntimeContext['meter']['createHistogram']>;\n context: BkOTRuntimeContext;\n errorCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n errorThrottle: ReturnType<typeof createThrottle>;\n messageCounter: ReturnType<BkOTRuntimeContext['meter']['createCounter']>;\n url: string;\n isDisposed: () => boolean;\n}\n\nconst instrumentSocket = (socket: WebSocket, deps: InstrumentDeps) => {\n const {\n bytesCounter,\n connectDurationHistogram,\n context,\n errorCounter,\n errorThrottle,\n isDisposed,\n messageCounter,\n url,\n } = deps;\n const { metricAttributes, spanAttributes } = buildWebSocketAttributes(url, context.config.hooks.redactUrl);\n // 补上业务自定义 metric 维度(context.attributes.metric hook),与 web-vitals / longTask 保持一致。\n // 每个 socket 只计算一次,避免消息热路径反复调用用户 hook。\n const metricCustomAttributes = context.config.hooks.getMetricAttributes();\n const getMetricBaseAttributes = () =>\n context.applyRedact({\n ...metricCustomAttributes,\n ...getMetricViewAttributes(context.getRuntimeAttributes()),\n ...metricAttributes,\n });\n const targetLabel = spanAttributes['server.address'] ?? spanAttributes['url.full'];\n\n // connect span 仅覆盖建连阶段,长连接生命周期不绑定 span,避免\"幽灵 span\"\n const connectSpan: Span = context.startSpan('websocket.connect', {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: 'WebSocket',\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n }),\n ...(spanAttributes['server.address'] ? { [RUM_ATTRIBUTES.targetDomain]: spanAttributes['server.address'] } : {}),\n });\n const startTime = performance.now();\n let connectEnded = false;\n\n const endConnectSpan = (status: 'error' | 'opened') => {\n if (connectEnded) return;\n connectEnded = true;\n const duration = performance.now() - startTime;\n if (status === 'error') {\n connectSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'websocket connect failed' });\n }\n connectSpan.setAttributes(\n buildRumSpanAttributes({\n duration,\n errorType: status === 'error' ? 'network' : 'none',\n eventLabel: 'WebSocket',\n result: status === 'error' ? RUM_SPAN_RESULT.error : RUM_SPAN_RESULT.success,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n targetValue: duration,\n }),\n );\n connectSpan.end();\n // 仅成功建连记入 histogram;错误的 duration 多为 timeout 固定值,统计意义有限\n if (status === 'opened' && !isDisposed() && shouldRecordSignal(context, 'metrics')) {\n connectDurationHistogram.record(duration, getMetricBaseAttributes());\n }\n };\n\n socket.addEventListener('open', () => {\n if (isDisposed()) return;\n endConnectSpan('opened');\n });\n\n socket.addEventListener('message', event => {\n if (isDisposed()) return;\n if (!shouldRecordSignal(context, 'metrics')) return;\n const inAttrs = { ...getMetricBaseAttributes(), 'websocket.direction': 'in' };\n messageCounter.add(1, inAttrs);\n bytesCounter.add(getMessageByteLength(event.data), inAttrs);\n });\n\n socket.addEventListener('error', () => {\n if (isDisposed()) return;\n // 用 connectEnded 区分阶段:未 open 即出错为 connect 阶段,已 open 后为 runtime\n const phase = connectEnded ? 'runtime' : 'connect';\n if (shouldRecordSignal(context, 'metrics')) {\n errorCounter.add(1, { ...getMetricBaseAttributes(), 'websocket.error.phase': phase });\n }\n endConnectSpan('error');\n if (!shouldRecordSignal(context, 'logs')) return;\n // 同 url 错误节流:reconnect 失败循环不至于产生日志风暴;counter 不受影响(已在上方累计)\n const throttleResult = errorThrottle.allow(`ws:error:${url}`);\n if (!throttleResult.allowed) return;\n context.emitLog({\n severityNumber: SeverityNumber.ERROR,\n severityText: 'ERROR',\n body: 'websocket.error',\n attributes: {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: 'network',\n eventLabel: 'WebSocket 错误',\n result: RUM_SPAN_RESULT.error,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n }),\n 'websocket.error.phase': phase,\n 'websocket.error.window_occurrence_index': throttleResult.count,\n },\n });\n });\n\n socket.addEventListener('close', event => {\n if (isDisposed()) return;\n // 从未 open 就 close → 建连失败(部分浏览器只触发 close 不触发 error 的边界)\n if (!connectEnded) {\n endConnectSpan('error');\n }\n if (shouldRecordSignal(context, 'logs')) context.emitLog({\n severityNumber: SeverityNumber.INFO,\n severityText: 'INFO',\n body: 'websocket.close',\n attributes: {\n ...spanAttributes,\n ...buildRumSpanAttributes({\n errorType: event.wasClean ? 'none' : 'network',\n eventLabel: 'WebSocket 关闭',\n result: event.wasClean ? RUM_SPAN_RESULT.success : RUM_SPAN_RESULT.warning,\n spanSubtype: 'websocket',\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel,\n targetValue: event.code,\n }),\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 统计 outbound:原生 WebSocket 没有 send 回调或事件,只能逐实例覆盖方法\n const originalSend = socket.send.bind(socket);\n socket.send = (data: ArrayBufferLike | ArrayBufferView | Blob | string) => {\n if (!isDisposed() && shouldRecordSignal(context, 'metrics')) {\n const outAttrs = { ...getMetricBaseAttributes(), 'websocket.direction': 'out' };\n messageCounter.add(1, outAttrs);\n bytesCounter.add(getMessageByteLength(data), outAttrs);\n }\n return originalSend(data);\n };\n};\n\nexport const createWebSocketPlugin = (): BkOTPlugin => {\n let originalWebSocket: typeof WebSocket | undefined;\n let patchedProxy: typeof WebSocket | undefined;\n let disposed = false;\n\n return {\n name: 'websocket',\n init(context) {\n if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {\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 const connectDurationHistogram = context.meter.createHistogram('browser.websocket.connect.duration', {\n unit: 'ms',\n description: 'WebSocket connection establishment duration (only successful connects)',\n advice: { explicitBucketBoundaries: DURATION_HISTOGRAM_BOUNDARIES },\n });\n const errorThrottle = createThrottle(ERROR_THROTTLE_WINDOW_MS, ERROR_THROTTLE_MAX_PER_WINDOW);\n const isDisposed = () => disposed;\n\n // Proxy 而非 function 替换:自动透传静态常量、原型与 Symbol.hasInstance;\n // 同时 Reflect.construct 用 newTarget 让 `class extends WebSocket` 子类化正确工作\n patchedProxy = new Proxy(nativeWebSocket, {\n construct(target, args, newTarget) {\n const socket = Reflect.construct(target, args, newTarget) as WebSocket;\n if (disposed || !context.isSampled()) return socket;\n const url = args[0] != null ? String(args[0]) : '';\n // 用户主动 ignore 或上报 endpoint,避免回环监控\n if (!url || shouldIgnoreUrl(context.config, url)) {\n return socket;\n }\n instrumentSocket(socket, {\n bytesCounter,\n connectDurationHistogram,\n context,\n errorCounter,\n errorThrottle,\n isDisposed,\n messageCounter,\n url,\n });\n return socket;\n },\n });\n\n window.WebSocket = patchedProxy;\n },\n shutdown() {\n disposed = true;\n // 多 patch 兼容:仅当当前 window.WebSocket 还是我们的 Proxy 时才恢复,\n // 避免抹掉其他库后来者的 patch\n if (typeof window !== 'undefined' && originalWebSocket && window.WebSocket === patchedProxy) {\n window.WebSocket = originalWebSocket;\n }\n patchedProxy = undefined;\n originalWebSocket = undefined;\n },\n };\n};\n","/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n */\n\nexport type BkOTActivityEvent =\n | { endTime: number; startTime: number; type: 'long_task'; url?: never }\n | { endTime: number; startTime: number; type: 'resource'; url?: string }\n | { id: string; time: number; type: 'request_end' }\n | { id: string; time: number; type: 'request_start'; url?: string }\n | { time: number; type: 'dom_mutation' | 'error' | 'window_open' };\n\nexport class RumActivityObserver {\n private readonly listeners = new Set<(event: BkOTActivityEvent) => void>();\n\n public notify = (event: BkOTActivityEvent) => {\n this.listeners.forEach(listener => listener(event));\n };\n\n public subscribe = (listener: (event: BkOTActivityEvent) => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Tencent is pleased to support the open source community by making\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.\n *\n * Copyright (C) 2017-2025 Tencent. All rights reserved.\n *\n * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.\n *\n * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):\n *\n * ---------------------------------------------------\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and\n * to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of\n * the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { type Attributes, type Span, context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';\nimport { ZoneContextManager } from '@opentelemetry/context-zone';\nimport { hrTimeToMilliseconds, timeInputToHrTime, W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { AggregationTemporalityPreference, 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, SamplingDecision, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\n\nimport { createActionPlugin } from '../plugins/action';\nimport { createActivityPlugin } from '../plugins/activity';\nimport { createBlankScreenPlugin } from '../plugins/blank-screen';\nimport { createCommonInstrumentationsPlugin } from '../plugins/common';\nimport { createCspViolationPlugin } from '../plugins/csp-violation';\nimport { createDevicePlugin } from '../plugins/device';\nimport { createErrorPlugin } from '../plugins/error';\nimport { createLongTaskPlugin } from '../plugins/long-task';\nimport { createPageViewPlugin } from '../plugins/page-view';\nimport { createResourcePlugin } from '../plugins/resource';\nimport { createSessionPlugin } from '../plugins/session';\nimport { createWebVitalsPlugin } from '../plugins/web-vitals';\nimport { createWebSocketPlugin } from '../plugins/websocket';\nimport { RUM_ATTRIBUTES } from '../schema/attributes';\nimport { buildRumSpanAttributes, RUM_SPAN_RESULT, RUM_SPAN_TYPE } from '../schema/span';\nimport { RumActivityObserver } from './action-activity';\nimport {\n type BkOTActionEventPayload,\n type BkOTCustomEventPayload,\n type BkOTOptions,\n type BkOTTrackingConfig,\n type BkOTTrackingConsent,\n type ResolvedBkOTConfig,\n type SignalExporterConfig,\n resolveConfig,\n} from './config';\nimport { createRandomId } from './id';\nimport { type BkOTPlugin, type BkOTRuntimeContext, type BkOTStartSpanOptions, PluginManager } from './plugin';\nimport { FilteringLogRecordProcessor, FilteringMetricExporter, FilteringSpanProcessor } from './processor';\nimport {\n type BkOTSessionLifecycleEvent,\n type BkOTStartViewOptions,\n type BkOTUserContext,\n RumRuntimeContext,\n} from './runtime-context';\nimport { isBkOTSampled, RUM_SAMPLED_ATTRIBUTE } from './sampling';\n\nimport type { LogRecord } from '@opentelemetry/api-logs';\nimport type { Sampler } from '@opentelemetry/sdk-trace-base';\n\nexport interface BkOTInstance extends Omit<\n BkOTRuntimeContext,\n 'notifyActivity' | 'onActivity' | 'onViewChange' | 'onViewCommand' | 'registerInstrumentations' | 'setView'\n> {\n flush: () => Promise<void>;\n reportAction: (payload: BkOTActionEventPayload) => void;\n reportCustomEvent: (payload: BkOTCustomEventPayload) => void;\n setTrackingConsent: (consent: BkOTTrackingConsent) => Promise<void>;\n setUser: (user: BkOTUserContext) => void;\n setViewLoadingTime: () => void;\n setViewName: (name: string) => void;\n shutdown: () => Promise<void>;\n start: () => Promise<void>;\n startView: (options?: BkOTStartViewOptions) => void;\n use: (plugin: BkOTPlugin) => BkOTInstance;\n useInstrumentation: (instrumentation: Instrumentation) => BkOTInstance;\n}\n\nconst createExporterOptions = (config: SignalExporterConfig) => ({\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\nclass RumSessionSampler implements Sampler {\n public constructor(private readonly isSampled: () => boolean) {}\n\n public shouldSample() {\n return {\n decision: this.isSampled() ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,\n };\n }\n\n public toString() {\n return 'RumSessionSampler';\n }\n}\n\n// OTel context manager 和 propagator 是页面级全局对象,多 SDK 实例通过引用计数共享,最后一个实例退出时释放。\nlet globalRegistration:\n | undefined\n | {\n contextManager?: ZoneContextManager;\n ownsContext: boolean;\n ownsPropagator: boolean;\n users: number;\n };\n\nconst acquireOtelGlobals = () => {\n if (globalRegistration) {\n globalRegistration.users += 1;\n } else {\n const contextManager = new ZoneContextManager().enable();\n const ownsContext = context.setGlobalContextManager(contextManager);\n if (!ownsContext) contextManager.disable();\n globalRegistration = {\n contextManager: ownsContext ? contextManager : undefined,\n ownsContext,\n ownsPropagator: propagation.setGlobalPropagator(new W3CTraceContextPropagator()),\n users: 1,\n };\n }\n let released = false;\n return () => {\n if (released || !globalRegistration) return;\n released = true;\n globalRegistration.users -= 1;\n if (globalRegistration.users > 0) return;\n if (globalRegistration.ownsContext) context.disable();\n if (globalRegistration.ownsPropagator) propagation.disable();\n globalRegistration = undefined;\n };\n};\n\n/**\n * 用 Proxy 包装一层 debug 旁路,避免直接 mutate 第三方 exporter 实例(其内部可能含 #private fields)。\n */\nconst wrapWithDebug = <TExporter extends { export: (...args: any[]) => any }>(\n exporter: TExporter,\n 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 span、Trace Header 与活动计数。\nconst getCorePlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createCommonInstrumentationsPlugin(config.tracking.request),\n];\n\nconst getRumContextPlugins = (config: ResolvedBkOTConfig): BkOTPlugin[] => [\n createSessionPlugin(config.session),\n ...createBuiltInPlugin(config.tracking.device, true, createDevicePlugin),\n createActivityPlugin(),\n createPageViewPlugin(),\n createResourcePlugin(isBuiltInPluginEnabled(config.tracking.resource, true)),\n];\n\nconst isBuiltInPluginEnabled = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n) => (option === undefined ? defaultEnabled : option !== false);\n\nconst getBuiltInPluginOptions = <TOptions extends object>(option: boolean | TOptions | undefined) =>\n typeof option === 'object' && option !== null ? option : undefined;\n\nconst createBuiltInPlugin = <TOptions extends object>(\n option: boolean | TOptions | undefined,\n defaultEnabled: boolean,\n factory: (options?: TOptions) => BkOTPlugin,\n) => (isBuiltInPluginEnabled(option, defaultEnabled) ? [factory(getBuiltInPluginOptions(option))] : []);\n\n// 上下文插件先启动,确保后续 span/log 能带上关联字段;Session sampler 与 processor 负责统一采样。\nconst getRumPlugins = (tracking: BkOTTrackingConfig): BkOTPlugin[] => [\n ...createBuiltInPlugin(tracking.error, true, createErrorPlugin),\n ...createBuiltInPlugin(tracking.webVitals, true, createWebVitalsPlugin),\n ...createBuiltInPlugin(tracking.blankScreen, true, createBlankScreenPlugin),\n ...createBuiltInPlugin(tracking.action, true, createActionPlugin),\n ...createBuiltInPlugin(tracking.websocket, false, createWebSocketPlugin),\n ...createBuiltInPlugin(tracking.longTask, false, createLongTaskPlugin),\n ...createBuiltInPlugin(tracking.cspViolation, false, createCspViolationPlugin),\n];\n\ntype SdkLifecyclePhase = 'idle' | 'started' | 'starting' | 'stopped';\n\n/**\n * 浏览器 RUM SDK 的生命周期入口。Provider 在构造时创建,插件和全局 patch 则随 start、授权变更和\n * shutdown 安装或卸载;Session 决定统一作用于 trace、metric、log 三类信号。\n */\nexport class BkOpenTelemetry implements BkOTInstance {\n private readonly activityObserver = new RumActivityObserver();\n private readonly flushProviders = async () => {\n await this.tracerProvider.forceFlush();\n await this.meterProvider.forceFlush();\n await this.loggerProvider.forceFlush();\n };\n private readonly loggerProvider: LoggerProvider;\n private readonly meterProvider: MeterProvider;\n private phase: SdkLifecyclePhase = 'idle';\n private pluginManager?: PluginManager;\n private readonly registeredInstrumentations: Instrumentation[];\n private releaseOtelGlobals?: () => void;\n private readonly runtimeContext: RumRuntimeContext;\n\n private sampled = false;\n private readonly startCollection = async () => {\n // Provider 始终存在以保持公开 API 稳定;采集插件仅在启用且获得授权后安装。\n if (this.pluginManager || !this.config.enabled || this.config.trackingConsent !== 'granted') return;\n if (this.config.transport.traces !== false) this.releaseOtelGlobals = acquireOtelGlobals();\n const pluginManager = this.createPluginManager();\n this.pluginManager = pluginManager;\n try {\n await pluginManager.start(this.createPluginContext());\n this.registerInstrumentations(this.config.extensions.instrumentations ?? []);\n } catch (error) {\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n throw error;\n }\n };\n private readonly stopCollection = async (flush: boolean) => {\n const pluginManager = this.pluginManager;\n if (!pluginManager) return;\n await pluginManager.shutdown();\n this.pluginManager = undefined;\n while (this.registeredInstrumentations.length) this.registeredInstrumentations.pop()?.disable?.();\n // 撤销授权后立即清空 Session,防止后续手工 API 继续复用旧采样决定。\n this.setRuntimeAttributes({\n [RUM_ATTRIBUTES.sessionHasReplay]: undefined,\n [RUM_ATTRIBUTES.sessionId]: undefined,\n });\n if (flush) await this.flushProviders();\n this.releaseOtelGlobals?.();\n this.releaseOtelGlobals = undefined;\n };\n private readonly teardownCallbacks: Array<() => void> = [];\n private readonly tracerProvider: WebTracerProvider;\n private assertBeforeStart(method: string) {\n if (this.phase !== 'idle') {\n throw new Error(`[bk-ot] ${method}() must be called before start(); set autoStart: false before registration.`);\n }\n }\n private createPluginContext(): BkOTRuntimeContext {\n return {\n applyRedact: this.applyRedact,\n config: this.config,\n emitLog: this.emitLog,\n flush: this.flush,\n getRuntimeAttributes: this.getRuntimeAttributes,\n getRuntimeAttributesAt: this.getRuntimeAttributesAt,\n isSampled: this.isSampled,\n isSessionActive: this.isSessionActive,\n isViewActive: this.isViewActive,\n logger: this.logger,\n meter: this.meter,\n notifySessionLifecycle: this.notifySessionLifecycle,\n notifyActivity: this.activityObserver.notify,\n onActivity: this.activityObserver.subscribe,\n onSessionLifecycle: this.onSessionLifecycle,\n onViewCommand: this.runtimeContext.onViewCommand,\n onViewChange: this.runtimeContext.onViewChange,\n registerInstrumentations: this.registerInstrumentations,\n setRuntimeAttributes: this.setRuntimeAttributes,\n setUser: this.setUser,\n setView: this.runtimeContext.setView,\n startSpan: this.startSpan,\n tracer: this.tracer,\n };\n }\n private createPluginManager() {\n return new PluginManager([\n ...getRumContextPlugins(this.config),\n ...getCorePlugins(this.config),\n ...getRumPlugins(this.config.tracking),\n ...(this.config.extensions.plugins ?? []),\n ]);\n }\n public readonly applyRedact = (attributes: Attributes) => this.config.hooks.redactAttributes(attributes);\n\n public readonly config: ResolvedBkOTConfig;\n\n public readonly emitLog = (record: LogRecord) => {\n if (!this.isSampled() || this.config.transport.logs === false) return;\n // 带显式 timestamp 的日志可能延迟产生,应按发生时间而非 emit 时间关联 Session/View/Action。\n const contextTime = record.timestamp ? hrTimeToMilliseconds(timeInputToHrTime(record.timestamp)) : undefined;\n const merged: LogRecord = {\n ...record,\n attributes: this.applyRedact({\n ...(contextTime === undefined ? this.getRuntimeAttributes() : this.getRuntimeAttributesAt(contextTime)),\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.flushProviders();\n };\n\n public readonly getRuntimeAttributes = () => this.runtimeContext.getAttributes();\n public readonly getRuntimeAttributesAt = (timestamp: number) => this.runtimeContext.getAttributesAt(timestamp);\n public readonly isSampled = () => this.sampled;\n public readonly isSessionActive = () => this.runtimeContext.isSessionActive();\n public readonly isViewActive = () => this.runtimeContext.isViewActive();\n public readonly logger: BkOTRuntimeContext['logger'];\n\n public readonly meter: BkOTRuntimeContext['meter'];\n\n public readonly notifySessionLifecycle = (event: BkOTSessionLifecycleEvent) => {\n this.runtimeContext.notifySessionLifecycle(event);\n };\n\n public readonly onSessionLifecycle = (listener: (event: BkOTSessionLifecycleEvent) => void) =>\n this.runtimeContext.onSessionLifecycle(listener);\n\n public readonly registerInstrumentations = (instrumentations: Instrumentation[]) => {\n if (!instrumentations.length) return;\n registerInstrumentations({\n instrumentations,\n meterProvider: this.meterProvider,\n tracerProvider: this.tracerProvider,\n });\n this.registeredInstrumentations.push(...instrumentations);\n };\n\n public readonly reportAction = ({ attributes = {}, name }: BkOTActionEventPayload) => {\n if (!this.isSampled() || this.config.transport.traces === false) {\n return;\n }\n const actionId = createRandomId('action');\n const span = this.startSpan('action.custom', {\n ...this.config.hooks.getPageAttributes(),\n ...attributes,\n ...buildRumSpanAttributes({\n errorType: 'none',\n eventLabel: '用户操作',\n spanSubtype: 'custom',\n spanType: RUM_SPAN_TYPE.action,\n targetLabel: name,\n traceScene: 'user_action',\n }),\n [RUM_ATTRIBUTES.actionId]: actionId,\n [RUM_ATTRIBUTES.actionName]: name,\n [RUM_ATTRIBUTES.actionType]: 'custom',\n });\n span.end();\n };\n\n public readonly reportCustomEvent = ({ attributes = {}, error, name }: BkOTCustomEventPayload) => {\n if (!this.isSampled() || this.config.transport.traces === false) {\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.hooks.getPageAttributes(),\n ...this.config.hooks.getCustomAttributes(),\n ...buildRumSpanAttributes({\n errorType: error ? 'custom' : 'none',\n eventLabel: '自定义事件',\n result: error ? RUM_SPAN_RESULT.error : RUM_SPAN_RESULT.success,\n spanSubtype: name,\n spanType: RUM_SPAN_TYPE.custom,\n targetLabel: name,\n }),\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, changedAt?: number) => {\n const nextAttributes = { ...attributes };\n if (RUM_ATTRIBUTES.sessionId in attributes) {\n // 采样以 Session 为边界重新计算,并作为内部属性传递给三类信号的统一过滤层。\n const sessionId = attributes[RUM_ATTRIBUTES.sessionId];\n this.sampled =\n this.config.enabled && typeof sessionId === 'string' && sessionId.length > 0\n ? isBkOTSampled(this.config, sessionId)\n : false;\n nextAttributes[RUM_SAMPLED_ATTRIBUTE] = this.sampled;\n }\n this.runtimeContext.setAttributes(nextAttributes, changedAt);\n };\n\n public readonly setTrackingConsent = async (consent: BkOTTrackingConsent) => {\n if (this.config.trackingConsent === consent) return;\n this.config.trackingConsent = consent;\n if (this.phase !== 'started') return;\n if (consent === 'granted') await this.startCollection();\n else await this.stopCollection(true);\n };\n\n public readonly setUser = (user: BkOTUserContext) => {\n this.runtimeContext.setUser(user);\n };\n\n public readonly setViewLoadingTime = () => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ time: Date.now(), type: 'set_loading_time' });\n };\n\n public readonly setViewName = (name: string) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ name, type: 'set_name' });\n };\n\n public readonly shutdown = async () => {\n if (this.phase === 'stopped') {\n return;\n }\n await this.stopCollection(false);\n await this.flushProviders();\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.tracerProvider.shutdown();\n await this.meterProvider.shutdown();\n await this.loggerProvider.shutdown();\n this.phase = 'stopped';\n };\n\n public readonly start = async () => {\n if (this.phase === 'started' || this.phase === 'starting') {\n return;\n }\n if (this.phase === 'stopped') {\n // shutdown 已经销毁了 provider/processor,此处禁止再次启动\n throw new Error('[bk-ot] instance has been shut down and cannot be restarted; create a new instance instead.');\n }\n if (!this.config.enabled) {\n this.phase = 'started';\n return;\n }\n this.phase = 'starting';\n try {\n await this.startCollection();\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // 页面进入后台或被丢弃前尽力导出批量数据;两个事件并存以覆盖不同浏览器生命周期。\n const flushOnPageHide = () => {\n void this.flush();\n };\n const flushOnHidden = () => {\n if (document.visibilityState === 'hidden') {\n void this.flush();\n }\n };\n window.addEventListener('pagehide', flushOnPageHide);\n document.addEventListener('visibilitychange', flushOnHidden);\n this.teardownCallbacks.push(\n () => window.removeEventListener('pagehide', flushOnPageHide),\n () => document.removeEventListener('visibilitychange', flushOnHidden),\n );\n }\n } catch (error) {\n while (this.teardownCallbacks.length) {\n this.teardownCallbacks.pop()?.();\n }\n await this.stopCollection(false);\n this.phase = 'idle';\n throw error;\n }\n this.phase = 'started';\n };\n\n public readonly startSpan = (name: string, attributes: Attributes = {}, options?: BkOTStartSpanOptions): Span => {\n // 返回 provider 创建的 non-recording span,而不是让每个调用方处理 undefined。\n if (!this.isSampled() || this.config.transport.traces === false) return this.tracer.startSpan(name);\n const contextTime =\n options?.contextTime ?? (typeof options?.startTime === 'number' ? options.startTime : undefined);\n const runtimeAttributes =\n contextTime === undefined ? this.getRuntimeAttributes() : this.runtimeContext.getAttributesAt(contextTime);\n return this.tracer.startSpan(name, {\n kind: options?.kind ?? SpanKind.INTERNAL,\n startTime: options?.startTime,\n attributes: this.applyRedact({\n ...runtimeAttributes,\n ...attributes,\n }),\n });\n };\n\n public readonly startView = (options: BkOTStartViewOptions = {}) => {\n if (this.isSampled()) this.runtimeContext.dispatchViewCommand({ options, type: 'start' });\n };\n\n public readonly tracer: BkOTRuntimeContext['tracer'];\n\n public readonly use = (plugin: BkOTPlugin): this => {\n this.assertBeforeStart('use');\n this.config.extensions.plugins?.push(plugin);\n return this;\n };\n\n public readonly useInstrumentation = (instrumentation: Instrumentation): this => {\n this.assertBeforeStart('useInstrumentation');\n if (this.config.enabled) this.config.extensions.instrumentations?.push(instrumentation);\n return this;\n };\n\n constructor(inputConfig: BkOTOptions) {\n this.config = resolveConfig(inputConfig);\n this.runtimeContext = new RumRuntimeContext(\n {\n [RUM_SAMPLED_ATTRIBUTE]: false,\n },\n this.config.user,\n );\n\n const resource = resourceFromAttributes(this.config.resourceAttributes);\n const spanProcessors = (() => {\n if (this.config.transport.traces === false) return [];\n const exporter = wrapWithDebug(\n new OTLPTraceExporter(createExporterOptions(this.config.transport.traces)),\n 'traces',\n this.config.debug,\n );\n const processor =\n this.config.spanProcessor === 'simple'\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, this.config.spanBatch);\n return [\n new FilteringSpanProcessor(processor, this.config, () =>\n this.applyRedact({\n ...this.runtimeContext.getAttributes(),\n ...this.config.hooks.getPageAttributes(),\n }),\n ),\n ];\n })();\n const metricReaders =\n this.config.transport.metrics === false\n ? []\n : [\n new PeriodicExportingMetricReader({\n exporter: new FilteringMetricExporter(\n wrapWithDebug(\n new OTLPMetricExporter({\n ...createExporterOptions(this.config.transport.metrics),\n temporalityPreference: AggregationTemporalityPreference.DELTA,\n }),\n 'metrics',\n this.config.debug,\n ),\n ),\n exportIntervalMillis: this.config.metricIntervalMillis,\n }),\n ];\n const logProcessors =\n this.config.transport.logs === false\n ? []\n : [\n new FilteringLogRecordProcessor(\n new BatchLogRecordProcessor(\n wrapWithDebug(\n new OTLPLogExporter(createExporterOptions(this.config.transport.logs)),\n 'logs',\n this.config.debug,\n ),\n ),\n this.config,\n ),\n ];\n\n this.tracerProvider = new WebTracerProvider({\n resource,\n sampler: new RumSessionSampler(() => this.config.transport.traces !== false && this.isSampled()),\n spanProcessors,\n });\n this.meterProvider = new MeterProvider({\n resource,\n readers: metricReaders,\n });\n this.loggerProvider = new LoggerProvider({\n resource,\n processors: logProcessors,\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.registeredInstrumentations = [];\n if (this.config.autoStart) {\n void this.start().catch(error => {\n globalThis.console?.warn('[bk-ot] auto start failed:', error);\n });\n }\n }\n}\n"],"mappings":"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"}
|