@wdio/nightwatch-devtools 1.8.0 → 1.8.2
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/{bidi-WAYPMJPQ.js → bidi-WNZOXX5X.js} +2 -2
- package/dist/{chunk-C7GXVKG6.js → chunk-DM4FCAIJ.js} +3 -2
- package/dist/chunk-DM4FCAIJ.js.map +1 -0
- package/dist/index.js +24 -15
- package/dist/index.js.map +1 -1
- package/package.json +8 -9
- package/dist/chunk-C7GXVKG6.js.map +0 -1
- /package/dist/{bidi-WAYPMJPQ.js.map → bidi-WNZOXX5X.js.map} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../shared/src/files.ts","../../shared/src/timing.ts","../src/trace-context.ts","../src/test-artifacts.ts","../src/helpers/assertCapture.ts","../src/run-lifecycle.ts","../src/plugin-global-key.ts","../src/constants.ts","../src/helpers/utils.ts","../src/session-init.ts","../src/helpers/webdriverHttp.ts","../src/session.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/runningTest.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/callbackError.ts","../src/helpers/assertTarget.ts","../src/helpers/serializeCommandResult.ts","../src/helpers/nativeAssertions.ts","../src/helpers/nativeAssertRecorder.ts","../src/screencast-session.ts","../src/screencast.ts","../src/plugin-screencast.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/trace-slices.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.ts"],"sourcesContent":["/**\n * Nightwatch DevTools Plugin\n *\n * Integrates Nightwatch with WebdriverIO DevTools following the WDIO service pattern.\n * Captures commands, network requests, and console logs during test execution in real-time.\n */\n\nimport { fileURLToPath } from 'node:url'\nimport {\n errorMessage,\n finalizeTraceExport,\n flushRangeLogged,\n resolveAdapterOutputDir,\n TestAttemptTracker,\n tracePolicyModeWarning,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport { buildTraceContext } from './trace-context.js'\nimport { emitTestArtifacts } from './test-artifacts.js'\nimport { wireAssertCapture } from './helpers/assertCapture.js'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport {\n REUSE_ENV,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\nimport logger from '@wdio/logger'\nimport {\n handleReuseMode,\n openDevtoolsBrowser,\n finalizeAllSuites,\n logRunSummary,\n waitForDevtoolsBrowserClose,\n runPluginBefore,\n type PluginBeforeCtx\n} from './run-lifecycle.js'\nimport type { PluginInternals } from './plugin-internals.js'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport {\n PluginScreencast,\n resolveScreencastOptions\n} from './plugin-screencast.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n DevToolsOptions,\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchEventHub,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n captureCucumberScenarioBeforeQuit as cucumberLifecycleBeforeQuit,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\n type CucumberResult\n} from './cucumber-lifecycle.js'\nimport {\n resolveSuiteMetadata,\n pickCurrentTestName,\n startNextTest,\n closePreviousRunningTest,\n wrapBrowserOnce,\n closeOutTestcases\n} from './test-lifecycle.js'\nimport { ensureSessionInitialized } from './session-init.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { flushTestSlice, recordSpecSliceBoundary } from './trace-slices.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions,\n nightwatchRunnerId\n} from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools')\n\nclass NightwatchDevToolsPlugin {\n private options: Required<DevToolsOptions>\n private sessionCapturer!: SessionCapturer\n private testReporter!: TestReporter\n private testManager!: TestManager\n private suiteManager!: SuiteManager\n private browserProxy!: BrowserProxy\n private isScriptInjected = false\n #currentTest: unknown = null\n #currentScenarioSuite: SuiteStats | null = null\n #currentStep: unknown = null\n #lastSessionId: string | null = null\n #devtoolsBrowser?: WebdriverIO.Browser\n #userDataDir?: string\n #isCucumberRunner = false\n #passCount = 0\n #failCount = 0\n #skipCount = 0\n #configPath: string | undefined\n #srcFolders: string[] = []\n\n /** Index ranges into the session capturer's flat arrays, one per spec file. */\n #specRanges: SpecRange[] = []\n\n /** Set of spec files already flushed to disk. */\n #flushedSpecs = new Set<string>()\n\n /** Every trace/video artifact seen this run (retained or not), for the\n * end-of-run artifacts manifest. Populated via the context's onArtifact. */\n #artifacts: TraceArtifact[] = []\n\n /** In-flight fire-and-forget eager/boundary slice flushes, awaited at finalize\n * so the last slice's write + its manifest entry land before teardown. */\n #traceFlushes: Promise<unknown>[] = []\n\n #getRerunLabel() {\n return process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n }\n\n #screencast: PluginScreencast\n #bidiEnabled = false\n #armedSessionId: string | undefined\n\n /** Wall-clock ms at the current test/scenario start — the lower bound of that\n * test's action-snapshot and video-frame window (per-test artifact slicing). */\n #currentTestStartWallTime = 0\n\n // Nightwatch `--retries` and cross-worker reruns may reset this in-process\n // tracker; only retries that re-enter this process's start hook are counted.\n #attemptTracker = new TestAttemptTracker()\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n // Resolve the defaults ONCE and gate on the resolved values: reading the raw\n // `options.filmstrip === true` left the default-on filmstrip inert unless a\n // config set it explicitly, so the recorder never started while\n // `this.options.filmstrip` claimed it was on.\n const filmstrip = options.filmstrip ?? true\n const video = options.video ?? 'off'\n const screencast = resolveScreencastOptions({\n mode,\n filmstrip,\n video,\n screencast: options.screencast\n })\n if (screencast.warning) {\n log.warn(screencast.warning)\n }\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast: screencast.options,\n bidi: options.bidi ?? false,\n captureAssertions: options.captureAssertions ?? true,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session',\n tracePolicy: options.tracePolicy ?? 'on',\n filmstrip,\n screenshot: options.screenshot ?? 'off',\n video,\n // No live Allure signal in Nightwatch (produce-only), so no auto-detect:\n // off by default, opt-in via the option.\n emitArtifactsManifest: options.emitArtifactsManifest ?? false\n }\n const policyWarning = tracePolicyModeWarning(options.tracePolicy, mode)\n if (policyWarning) {\n log.warn(policyWarning)\n }\n this.#screencast = new PluginScreencast(\n screencast.options,\n filmstrip || video !== 'off'\n )\n this.#bidiEnabled = options.bidi === true\n }\n\n // Single internals \"bag\" — structurally satisfies all 4 lifecycle ctx\n // interfaces. Lifecycle modules cast it to their narrow type at call time.\n #internals: PluginInternals | undefined\n // Declarative accessor map — splitting this purely to satisfy the\n // line-count rule hurts readability; the body is mechanical wiring.\n // eslint-disable-next-line max-lines-per-function\n #getInternals(): PluginInternals {\n if (this.#internals) {\n return this.#internals\n }\n const self = this\n this.#internals = {\n get options() {\n return self.options\n },\n get hostname() {\n return self.options.hostname\n },\n get port() {\n return self.options.port\n },\n get mode() {\n return self.options.mode\n },\n get screencastOptions() {\n return self.#screencast.options\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get captureAssertions() {\n return self.options.captureAssertions\n },\n get sessionCapturer() {\n return self.sessionCapturer\n },\n set sessionCapturer(v) {\n self.sessionCapturer = v\n },\n get testReporter() {\n return self.testReporter\n },\n set testReporter(v) {\n self.testReporter = v\n },\n get testManager() {\n return self.testManager\n },\n set testManager(v) {\n self.testManager = v\n },\n get suiteManager() {\n return self.suiteManager\n },\n set suiteManager(v) {\n self.suiteManager = v\n },\n get browserProxy() {\n return self.browserProxy\n },\n set browserProxy(v) {\n self.browserProxy = v\n },\n get isScriptInjected() {\n return self.isScriptInjected\n },\n set isScriptInjected(v) {\n self.isScriptInjected = v\n },\n get devtoolsBrowser() {\n return self.#devtoolsBrowser\n },\n set devtoolsBrowser(v) {\n self.#devtoolsBrowser = v\n },\n get userDataDir() {\n return self.#userDataDir\n },\n set userDataDir(v) {\n self.#userDataDir = v\n },\n get passCount() {\n return self.#passCount\n },\n set passCount(v) {\n self.#passCount = v\n },\n get failCount() {\n return self.#failCount\n },\n set failCount(v) {\n self.#failCount = v\n },\n get skipCount() {\n return self.#skipCount\n },\n set skipCount(v) {\n self.#skipCount = v\n },\n get lastSessionId() {\n return self.#lastSessionId\n },\n set lastSessionId(v) {\n self.#lastSessionId = v\n },\n get armedSessionId() {\n return self.#armedSessionId\n },\n set armedSessionId(v) {\n self.#armedSessionId = v\n },\n get srcFolders() {\n return self.#srcFolders\n },\n set srcFolders(v) {\n self.#srcFolders = v\n },\n get screencastRecorder() {\n return self.#screencast.recorder\n },\n set screencastRecorder(v) {\n self.#screencast.recorder = v\n },\n get screencastSessionId() {\n return self.#screencast.sessionId\n },\n set screencastSessionId(v) {\n self.#screencast.sessionId = v\n },\n get screencastRotation() {\n return self.#screencast.rotation\n },\n set screencastRotation(v) {\n self.#screencast.rotation = v\n },\n get configPath() {\n return self.#configPath\n },\n get runner() {\n return nightwatchRunnerId(self.#isCucumberRunner)\n },\n getCurrentTest: () => self.#currentTest,\n getCurrentScenarioSuite: () => self.#currentScenarioSuite,\n getCurrentStep: () => self.#currentStep,\n setCurrentTest: (t) => {\n self.#currentTest = t\n },\n setCurrentScenarioSuite: (s) => {\n self.#currentScenarioSuite = s\n },\n setCurrentStep: (s) => {\n self.#currentStep = s\n },\n clearExecutionData: () => {\n self.testReporter.clearExecutionData()\n self.suiteManager.clearExecutionData()\n self.#attemptTracker.reset()\n },\n recordAttempt: (uid, specFile) =>\n self.#attemptTracker.recordStart(uid, specFile),\n recordOutcome: (uid, state) =>\n self.#attemptTracker.recordOutcome(uid, state),\n attemptFor: (uid) => self.#attemptTracker.attemptFor(uid),\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n finalizeCurrentScreencast: () => self.#finalizeCurrentScreencast(),\n wrapBrowserOnce: (b) => self.#wrapBrowserOnce(b),\n incrementCount: (s) => self.#incrementCount(s),\n testIcon: (s) => self.#testIcon(s),\n setCucumberRunner: (v) => {\n self.#isCucumberRunner = v\n },\n getRerunLabel: () => self.#getRerunLabel(),\n get traceMode() {\n return self.options.mode === 'trace'\n },\n get traceGranularity() {\n return self.options.traceGranularity\n },\n get specRanges() {\n return self.#specRanges\n },\n get flushedSpecs() {\n return self.#flushedSpecs\n },\n flushTraceRange: (range) => self.#flushSpecTrace(range),\n emitTestArtifacts: (uid, failed) => self.#emitTestArtifacts(uid, failed)\n }\n return this.#internals\n }\n\n /** Boundary cast: currentTest is Nightwatch's loose bag; only uid is read. */\n #currentTestUid(): string | undefined {\n return (this.#currentTest as { uid?: string } | null)?.uid\n }\n\n #handleReuseMode(): void {\n handleReuseMode(this.#getInternals())\n }\n\n async #openDevtoolsBrowser(url: string): Promise<void> {\n await openDevtoolsBrowser(this.#getInternals(), url)\n }\n\n async before() {\n const internals = this.#getInternals() as unknown as PluginBeforeCtx\n internals.setConfigPath = (v) => {\n this.#configPath = v\n }\n internals.openDevtoolsBrowserAt = (url) => this.#openDevtoolsBrowser(url)\n internals.handleReuse = () => this.#handleReuseMode()\n internals.plugin = this\n await runPluginBefore(internals)\n if (this.options.captureAssertions) {\n wireAssertCapture(\n () => this.sessionCapturer,\n // Rows are tagged per-`it` (BrowserProxy.rowTestUid); `#currentTestUid`\n // — the lifecycle test, which the BDD interface pins to the module's\n // first `it` — is only the fallback, and stays the artifact/slice key.\n () => this.browserProxy?.rowTestUid() ?? this.#currentTestUid()\n )\n }\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser)\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await this.#screencast.finalize(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n this.#currentTestStartWallTime = Date.now()\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberBeforeQuit(browser: NightwatchBrowser, result: CucumberResult) {\n await cucumberLifecycleBeforeQuit(this.#getInternals(), browser, result)\n }\n\n async cucumberAfter(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfter(this.#getInternals(), browser, result, pickle)\n }\n\n async cucumberBeforeStep(\n browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleBeforeStep(\n this.#getInternals(),\n browser,\n pickleStep,\n pickle\n )\n }\n\n async cucumberAfterStep(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfterStep(\n this.#getInternals(),\n browser,\n result,\n pickleStep,\n pickle\n )\n }\n\n #resolveSuiteMetadata(currentTest: NightwatchCurrentTest) {\n return resolveSuiteMetadata(this.#getInternals(), currentTest)\n }\n\n #pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n ): string | undefined {\n return pickCurrentTestName(currentTest, testNames, processedTests)\n }\n\n async #startNextTest(\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>,\n specFile: string | null\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests,\n specFile\n )\n }\n\n async #closePreviousRunningTest(\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n ): Promise<void> {\n await closePreviousRunningTest(\n this.#getInternals(),\n currentSuite,\n testFile,\n currentTest\n )\n }\n\n #wrapBrowserOnce(browser: NightwatchBrowser): void {\n wrapBrowserOnce(this.#getInternals(), browser)\n }\n\n async beforeEach(browser: NightwatchBrowser) {\n if (this.#isCucumberRunner) {\n return\n }\n this.#currentTestStartWallTime = Date.now()\n await this.#ensureSessionInitialized(browser)\n\n const currentTest = browser.currentTest as NightwatchCurrentTest | undefined\n if (!currentTest) {\n return\n }\n\n const { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines } =\n this.#resolveSuiteMetadata(currentTest)\n const currentSuite = this.suiteManager.getOrCreateSuite(\n testFile,\n suiteTitle,\n fullPath,\n testNames,\n suiteLine,\n testLines\n )\n if (fullPath && fullPath.includes('/')) {\n this.sessionCapturer.captureSource(fullPath).catch(() => {})\n }\n\n if (fullPath) {\n recordSpecSliceBoundary(this.#getInternals(), fullPath)\n }\n\n await this.#closePreviousRunningTest(currentSuite, testFile, currentTest)\n\n const processedTests = this.testManager.getProcessedTests(testFile)\n const currentTestName = this.#pickCurrentTestName(\n currentTest,\n testNames,\n processedTests\n )\n if (currentTestName) {\n await this.#startNextTest(\n currentSuite,\n currentTestName,\n processedTests,\n fullPath\n )\n }\n this.#wrapBrowserOnce(browser)\n }\n\n async afterEach(browser: NightwatchBrowser) {\n // Cucumber runner manages its own lifecycle via cucumberHooks.cjs\n if (this.#isCucumberRunner) {\n return\n }\n\n if (browser && this.sessionCapturer) {\n try {\n await this.#closeOutTestcases(browser)\n if (this.options.captureAssertions) {\n await captureNativeAssertions(\n this.sessionCapturer,\n browser,\n browser.currentTest as NightwatchCurrentTest | undefined,\n this.#currentTestUid(),\n this.browserProxy.drainNativeAssertCalls()\n )\n }\n // Anchored: a test ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await this.sessionCapturer.captureTrace(browser, true)\n // Flush this test's slice before the next test overwrites its outcome.\n flushTestSlice(this.#getInternals())\n const results = (browser.currentTest as NightwatchCurrentTest)?.results\n const failed =\n !!results && ((results.errors ?? 0) > 0 || (results.failed ?? 0) > 0)\n await this.#emitTestArtifacts(this.#currentTestUid(), failed)\n } catch (err) {\n log.error(`Failed to capture trace: ${errorMessage(err)}`)\n }\n }\n }\n\n async #closeOutTestcases(browser: NightwatchBrowser): Promise<void> {\n await closeOutTestcases(this.#getInternals(), browser)\n }\n\n /** Directory for produced artifacts — next to the test file, else the config,\n * else cwd (mirrors #traceContext's resolution). */\n get #outputDir(): string {\n return resolveAdapterOutputDir({\n testFilePath: this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: this.#configPath\n })\n }\n\n /** Produce (never attach — no live Allure API) this test's per-test screenshot\n * and video slice. No-op outside trace mode + `test` granularity (core-gated).\n * Shared by the per-test `afterEach` and the cucumber per-scenario finalize. */\n async #emitTestArtifacts(\n uid: string | undefined,\n failed: boolean\n ): Promise<void> {\n const attempt = uid ? this.#attemptTracker.attemptFor(uid) : undefined\n await emitTestArtifacts({\n mode: this.options.mode,\n granularity: this.options.traceGranularity,\n screenshotPolicy: this.options.screenshot,\n videoPolicy: this.options.video,\n failed,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n frames: this.#screencast.frames,\n startWallTime: this.#currentTestStartWallTime,\n outcomes: uid ? this.#attemptTracker.forTest(uid, attempt) : [],\n uid,\n attempt,\n sessionId: this.sessionCapturer?.metadata?.sessionId,\n outputDir: this.#outputDir,\n captureFormat: this.#screencast.options.captureFormat,\n onArtifact: (a) => this.#artifacts.push(a),\n onLog: (level, msg) => log[level](msg)\n })\n }\n\n async after(browser?: NightwatchBrowser) {\n // Drain first: a rotation still in flight would otherwise start its new\n // recorder after this finalize and strand the poll interval.\n await this.#screencast.rotation\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceIfNeeded()\n await this.sessionCapturer?.closeWebSocket()\n await stopBackend()\n return\n }\n if (!this.#devtoolsBrowser) {\n // Reuse mode: force one final suites broadcast so the UI reflects the\n // actual outcome before the process exits.\n this.testReporter?.updateSuites()\n log.info('♻ Rerun complete — flushing WebSocket...')\n await this.sessionCapturer?.closeWebSocket()\n return\n }\n log.info('💡 Please close the DevTools browser window to finish...')\n await this.#waitForDevtoolsBrowserClose()\n } catch (err) {\n log.error(`Failed to stop backend: ${errorMessage(err)}`)\n }\n }\n\n async #finalizeAllSuites(browser?: NightwatchBrowser): Promise<void> {\n await finalizeAllSuites(this.#getInternals(), browser)\n }\n\n #logRunSummary(): void {\n logRunSummary(this.#getInternals())\n }\n\n /** Thin wrapper so boundary flushes and the final flush share one path.\n * flushRangeLogged logs+swallows a failed flush (shared spec/test string) so\n * the fire-and-forget boundary callers don't each re-implement the guard. */\n #flushSpecTrace(range: SpecRange): Promise<TraceArtifact | undefined> {\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return Promise.resolve(undefined)\n }\n // Track the promise (before it's added to awaitPending) so finalize awaits\n // this fire-and-forget flush — its context snapshot already excludes it.\n const flush = flushRangeLogged(this.#traceContext(sessionId), range)\n this.#traceFlushes.push(flush)\n return flush\n }\n\n /** Assemble the framework-agnostic trace-export context from plugin state.\n * Output dir ignores the spec range — nightwatch writes next to config. */\n #traceContext(sessionId: string): TraceExportContext {\n return buildTraceContext(\n {\n mode: this.options.mode,\n policy: this.options.tracePolicy,\n granularity: this.options.traceGranularity,\n format: this.options.traceFormat,\n capturer: this.sessionCapturer,\n suites: this.suiteManager.getAllSuites().values(),\n outcomes: this.#attemptTracker,\n ranges: this.#specRanges,\n flushed: this.#flushedSpecs,\n artifacts: this.#artifacts,\n traceFlushes: this.#traceFlushes,\n emitArtifactsManifest: this.options.emitArtifactsManifest,\n screencastFrames: this.options.filmstrip\n ? this.#screencast.frames\n : undefined,\n configPath: this.#configPath,\n testFilePath:\n this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n log: (level, msg) => log[level](msg)\n },\n sessionId\n )\n }\n\n async #writeTraceIfNeeded(): Promise<void> {\n const sessionId = this.sessionCapturer?.metadata?.sessionId\n if (this.options.mode !== 'trace' || !this.sessionCapturer || !sessionId) {\n return\n }\n await finalizeTraceExport(this.#traceContext(sessionId))\n }\n\n async #waitForDevtoolsBrowserClose(): Promise<void> {\n await waitForDevtoolsBrowserClose(this.#getInternals())\n }\n\n #buildMetadataOptions() {\n return buildPluginMetadataOptions({\n isCucumberRunner: this.#isCucumberRunner,\n configPath: this.#configPath\n })\n }\n\n #incrementCount(state: TestStats['state']): void {\n const counters = {\n passCount: this.#passCount,\n failCount: this.#failCount,\n skipCount: this.#skipCount\n }\n incrementCounters(counters, state)\n this.#passCount = counters.passCount\n this.#failCount = counters.failCount\n this.#skipCount = counters.skipCount\n }\n\n #testIcon(state: TestStats['state']): string {\n return getTestIcon(state)\n }\n\n registerEventHandlers(eventHub: NightwatchEventHub): void {\n registerEventHandlersImpl(eventHub, {\n getSessionCapturer: () => this.sessionCapturer,\n buildMetadataOptions: () => this.#buildMetadataOptions(),\n setCucumberRunner: (v: boolean) => {\n this.#isCucumberRunner = v\n }\n })\n }\n}\n\nexport const cucumberHooksPath = fileURLToPath(\n new URL('./helpers/cucumberHooks.cjs', import.meta.url)\n)\n\nexport default function createNightwatchDevTools(options?: DevToolsOptions) {\n const plugin = new NightwatchDevToolsPlugin(options)\n\n return {\n // Set long timeout to allow user to review DevTools UI\n // The after() hook waits for the browser window to be closed\n asyncHookTimeout: 3600000,\n\n before: async function (this: unknown) {\n await plugin.before()\n },\n beforeEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.beforeEach(browser)\n },\n afterEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.afterEach(browser)\n },\n after: async function (this: unknown) {\n await plugin.after()\n },\n\n registerEventHandlers: function (eventHub: NightwatchEventHub) {\n plugin.registerEventHandlers(eventHub)\n }\n }\n}\n\nexport { NightwatchDevToolsPlugin }\n","// File-pattern regexes shared across packages — keep one canonical form\n// per concept so changes to \"what counts as a test file\" or \"what counts\n// as a Cucumber feature file\" propagate everywhere. See CLAUDE.md §2.1.\n\n/** Matches `*.test.ts`, `*.spec.ts`, `*.test.cjs`, etc. — the test-runner\n * convention used by every adapter to recognize spec files. */\nexport const SPEC_FILE_RE = /\\.(?:test|spec)\\.[cm]?[jt]sx?$/i\n\n/** Matches `*.feature` — Cucumber feature files. */\nexport const FEATURE_FILE_RE = /\\.feature$/i\n","/**\n * Cross-adapter timing + identifier defaults.\n *\n * The TIMING values are wall-clock milliseconds the adapter's plugin loop\n * waits for (UI render, test/suite boundaries, browser close, WS connection\n * setup). The DEFAULTS provide the canonical TestStats / SuiteStats field\n * defaults so every adapter produces the same shape on the wire.\n *\n * Adapters spread these into their local TIMING / DEFAULTS objects so they\n * can override per-framework values (Nightwatch's UI_CONNECTION_WAIT is\n * higher because its boot is slower; each adapter has its own SESSION_TITLE\n * / TEST_NAME placeholder).\n */\n\nexport const TIMING_BASE = {\n /** ms to let the dashboard render between rapid state updates. */\n UI_RENDER_DELAY: 150,\n /** ms to wait after a test starts before flushing initial state. */\n TEST_START_DELAY: 100,\n /** ms gap between consecutive suite-finalize broadcasts. */\n SUITE_COMPLETE_DELAY: 200,\n /** ms allowed for the browser-close handshake before forcing teardown. */\n BROWSER_CLOSE_WAIT: 2000,\n /** ms before first worker WS connection attempt — lets backend bind. */\n INITIAL_CONNECTION_WAIT: 500,\n /** ms between browser-window-alive polls. */\n BROWSER_POLL_INTERVAL: 1000\n} as const\n\nexport const DEFAULTS_BASE = {\n /** Synthetic capability ID — present on every CommandLog / TestStats. */\n CID: '0-0',\n /** TestStats.retries default — adapters that surface retry counts override. */\n RETRIES: 0,\n /** _duration placeholder for not-yet-finalized stats. */\n DURATION: 0\n} as const\n","/**\n * Assembles the framework-agnostic trace-export context from plugin state.\n *\n * Extracted from `NightwatchDevToolsPlugin.#traceContext` so the assembly is\n * unit-testable and to keep `index.ts` under the file-size cap. Both the\n * per-spec boundary flush and the final trace write share this one builder.\n */\n\nimport {\n collectSuiteTestMetadata,\n resolveAdapterOutputDir,\n type RetryOutcomeView,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n SuiteStats,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from './types.js'\n\nexport interface TraceContextInput {\n mode: DevToolsMode\n policy: TraceRetentionPolicy\n granularity: TraceGranularity\n format: TraceFormat\n capturer: SessionCapturer\n suites: Iterable<SuiteStats>\n outcomes?: RetryOutcomeView\n ranges: SpecRange[]\n flushed: Set<string>\n artifacts: TraceArtifact[]\n traceFlushes: Promise<unknown>[]\n screencastFrames?: readonly ScreencastFrame[]\n configPath: string | undefined\n testFilePath: string | undefined\n emitArtifactsManifest?: boolean\n log: (level: 'info' | 'warn', msg: string) => void\n}\n\nexport function buildTraceContext(\n input: TraceContextInput,\n sessionId: string\n): TraceExportContext {\n return {\n mode: input.mode,\n policy: input.policy,\n granularity: input.granularity,\n format: input.format,\n capturer: input.capturer,\n actionSnapshots: input.capturer.actionSnapshots,\n sessionId,\n testMetadata: collectSuiteTestMetadata(input.suites),\n outcomes: input.outcomes,\n ranges: input.ranges,\n flushed: input.flushed,\n screencastFrames: input.screencastFrames,\n resolveOutputDir: () =>\n resolveAdapterOutputDir({\n testFilePath: input.testFilePath,\n configPath: input.configPath\n }),\n awaitPending: [...input.capturer.snapshotCaptures, ...input.traceFlushes],\n pendingCaptures: input.capturer.snapshotCaptures,\n // Nightwatch feeds real per-test attempt numbers via TestAttemptTracker\n // (B4), so retry-aware policies use per-test attempts, not the fallback.\n attemptInfoAvailable: true,\n // Off by default; opt-in only — Nightwatch has no live Allure signal to\n // auto-detect against (produce-only Allure), unlike WDIO/Selenium.\n emitManifest: input.emitArtifactsManifest ?? false,\n collectedArtifacts: input.artifacts,\n onArtifact: (a) => input.artifacts.push(a),\n log: input.log\n }\n}\n","/**\n * Produce-only per-test artifacts (screenshot + video) for the Nightwatch\n * plugin.\n *\n * Nightwatch has no live Allure attach API — its official `nightwatch-allure`\n * reporter is post-hoc, and `allure-js-commons`' `attachment()` no-ops in a\n * Nightwatch run (nothing wires the global test runtime). So we pass NO sink\n * (`attach: undefined`), which the core treats as produce-only: it writes the\n * file and records the manifest artifact via `onArtifact`, but skips the attach.\n *\n * Both the per-test (Mocha/Jasmine `afterEach`) and cucumber (per-scenario\n * finalize) paths funnel through here so the two produce calls live once. Core\n * gates both to trace mode + `test` granularity, so this no-ops otherwise.\n */\n\nimport {\n captureAndAttachScreenshot,\n captureAndAttachVideo,\n lastRenderedScreenshot,\n type TestOutcome,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\n\nexport interface EmitTestArtifactsInput {\n mode: DevToolsMode\n granularity: TraceGranularity\n screenshotPolicy: TraceScreenshotPolicy\n videoPolicy: TraceVideoPolicy\n failed: boolean\n actionSnapshots: readonly ActionSnapshot[]\n frames: readonly ScreencastFrame[]\n startWallTime: number\n /** This test's attempt slots (already scoped via `forTest`) for retention. */\n outcomes: TestOutcome[]\n uid: string | undefined\n attempt: number | undefined\n sessionId: string | undefined\n outputDir: string\n captureFormat?: 'jpeg' | 'png'\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Produce this test's screenshot then its video slice, both with `attach:\n * undefined` (produce-only). Each core call self-gates on mode/granularity/\n * policy, so a non-trace run or a non-`test` granularity is a no-op.\n */\nexport async function emitTestArtifacts(\n input: EmitTestArtifactsInput\n): Promise<void> {\n await captureAndAttachScreenshot({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.screenshotPolicy,\n failed: input.failed,\n screenshotBase64: lastRenderedScreenshot(\n input.actionSnapshots,\n input.startWallTime\n ),\n sessionId: input.sessionId,\n outputDir: input.outputDir,\n testUid: input.uid,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n await captureAndAttachVideo({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.videoPolicy,\n frames: input.frames,\n startWallTime: input.startWallTime,\n outcomes: input.outcomes,\n attempt: input.attempt,\n outputDir: input.outputDir,\n testUid: input.uid,\n sessionId: input.sessionId,\n captureFormat: input.captureFormat,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n}\n","// node:assert capture wiring: routes patched assertions into the session\n// capturer through the same captureCommand path driver commands use, so\n// retry bookkeeping and trace-mode action snapshots behave identically.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n patchNodeAssert,\n resolveAssertTargetFromArgs,\n type CapturedAssert\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:assertCapture')\n\n/**\n * Patch node:assert once per process. Getters are read at capture time — the\n * capturer is created lazily in session init and the test UID changes per test.\n */\nexport function wireAssertCapture(\n getCapturer: () => SessionCapturer | undefined,\n getTestUid: () => string | undefined\n): void {\n patchNodeAssert(\n (cmd) => captureAssert(getCapturer(), getTestUid(), cmd),\n (level, message) => log[level](message),\n // No handle resolver: Nightwatch commands take selector strings, so an\n // element object never reaches an assert — only the value's provenance,\n // recorded by the command path (BrowserProxy), can name the target.\n (args) => resolveAssertTargetFromArgs(args)\n )\n}\n\nfunction captureAssert(\n capturer: SessionCapturer | undefined,\n testUid: string | undefined,\n cmd: CapturedAssert\n): void {\n if (!capturer) {\n return\n }\n capturer\n .captureCommand(cmd.command, cmd.args, cmd.result, cmd.error, {\n testUid,\n callSource: cmd.callSource,\n timestamp: cmd.timestamp,\n // The assert's args hold the compared VALUES, so provenance — recorded by\n // the command path — is the row's only route to a locator.\n selector: cmd.selector\n })\n .catch((err) =>\n log.warn(`Failed to capture ${cmd.command}: ${errorMessage(err)}`)\n )\n // captureCommand pushes synchronously; mirror captureCommandError's send.\n const last = capturer.commandsLog[capturer.commandsLog.length - 1]\n if (last) {\n capturer.sendCommand(last)\n }\n}\n","/**\n * Run-level lifecycle helpers for the Nightwatch plugin — reuse-mode setup,\n * DevTools-browser spawning, end-of-run summary, and the post-run \"wait for\n * browser close\" loop.\n *\n * Extracted from `index.ts` to keep that file under the file-size cap.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport logger from '@wdio/logger'\nimport { remote } from 'webdriverio'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { start, stop } from '@wdio/devtools-backend'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n NightwatchCurrentTest\n} from './types.js'\nimport { TIMING, PLUGIN_GLOBAL_KEY } from './constants.js'\nimport { findFreePort, resolveNightwatchConfig } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:run-lifecycle')\n\nexport interface RunLifecycleCtx {\n options: { hostname: string; port: number; mode?: DevToolsMode }\n readonly testReporter: TestReporter | undefined\n readonly suiteManager: SuiteManager | undefined\n readonly testManager: TestManager\n readonly sessionCapturer: SessionCapturer | undefined\n devtoolsBrowser: WebdriverIO.Browser | undefined\n userDataDir: string | undefined\n passCount: number\n failCount: number\n skipCount: number\n clearExecutionData(): void\n}\n\nexport function handleReuseMode(ctx: RunLifecycleCtx): void {\n ctx.options.hostname = process.env[REUSE_ENV.HOST]!\n ctx.options.port = Number(process.env[REUSE_ENV.PORT])\n log.info(\n `♻ Reusing DevTools backend at ${ctx.options.hostname}:${ctx.options.port}`\n )\n // Clear execution data from the previous run when rerunning so test-name\n // caches and suites are fresh for the new run.\n if (ctx.testReporter) {\n ctx.clearExecutionData()\n ctx.passCount = 0\n ctx.failCount = 0\n ctx.skipCount = 0\n log.info('Cleared execution data for rerun')\n }\n}\n\nexport interface PluginBeforeCtx extends RunLifecycleCtx {\n setConfigPath(v: string | undefined): void\n openDevtoolsBrowserAt(url: string): Promise<void>\n handleReuse(): void\n // The plugin instance to assign to the global slot for cucumber hooks.\n plugin: unknown\n}\n\nexport async function runPluginBefore(ctx: PluginBeforeCtx): Promise<void> {\n // When relaunched by the DevTools UI rerun button the backend is already\n // running — skip startup and just connect the WebSocket worker.\n const isReuse =\n process.env[REUSE_ENV.REUSE] === '1' &&\n !!process.env[REUSE_ENV.HOST] &&\n !!process.env[REUSE_ENV.PORT]\n if (isReuse) {\n ctx.handleReuse()\n }\n const configPath = resolveNightwatchConfig()\n ctx.setConfigPath(configPath)\n if (configPath) {\n log.info(`✓ Config: ${configPath}`)\n } else {\n log.warn(\n 'Could not find nightwatch config — test rerun will be unavailable'\n )\n }\n if (isReuse) {\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n return\n }\n try {\n // Trace mode: skip backend port-bind and UI entirely — matches the WDIO\n // launcher gate. SessionCapturer construction in session-init also gates\n // its WS init off in trace mode.\n if (ctx.options.mode === 'trace') {\n log.info('Trace mode — skipping backend port-bind and UI window')\n } else {\n ctx.options.port = await findFreePort(\n ctx.options.port,\n ctx.options.hostname\n )\n log.info('🚀 Starting DevTools backend...')\n const { port } = await start(ctx.options)\n ctx.options.port = port\n const url = `http://${ctx.options.hostname}:${ctx.options.port}`\n log.info(`✓ Backend started on port ${ctx.options.port}`)\n log.info(` DevTools UI: ${url}`)\n await ctx.openDevtoolsBrowserAt(url)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.UI_CONNECTION_WAIT)\n )\n }\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n } catch (err) {\n log.error(`Failed to start backend: ${errorMessage(err)}`)\n throw err\n }\n}\n\nexport async function openDevtoolsBrowser(\n ctx: RunLifecycleCtx,\n url: string\n): Promise<void> {\n try {\n // Unique user data directory per instance to prevent conflicts.\n ctx.userDataDir = path.join(\n os.tmpdir(),\n `nightwatch-devtools-${ctx.options.port}-${Date.now()}`\n )\n if (!fs.existsSync(ctx.userDataDir)) {\n fs.mkdirSync(ctx.userDataDir, { recursive: true })\n }\n ctx.devtoolsBrowser = await remote({\n logLevel: 'info',\n automationProtocol: 'devtools',\n capabilities: {\n browserName: 'chrome',\n 'goog:chromeOptions': {\n args: [\n '--window-size=1600,1200',\n `--user-data-dir=${ctx.userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ]\n },\n // Dashboard uses the Puppeteer-based 'devtools' protocol; drop the\n // \"controlled by automated test software\" infobar by ignoring\n // Puppeteer's --enable-automation default. (Runtime-valid capability\n // not present in the type.)\n 'wdio:devtoolsOptions': {\n ignoreDefaultArgs: ['--enable-automation']\n }\n } as WebdriverIO.Capabilities\n })\n await ctx.devtoolsBrowser.url(url)\n } catch (err) {\n log.error(`Failed to open DevTools UI: ${errorMessage(err)}`)\n log.info(`Please manually open: ${url}`)\n }\n}\n\nexport async function finalizeAllSuites(\n ctx: RunLifecycleCtx,\n browser?: NightwatchBrowser\n): Promise<void> {\n const currentTest = browser?.currentTest as NightwatchCurrentTest | undefined\n const testcases = currentTest?.results?.testcases ?? {}\n for (const [, suite] of (\n ctx.suiteManager?.getAllSuites() ?? new Map()\n ).entries()) {\n ctx.testManager.finalizeSuiteTests(suite, testcases)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n ctx.suiteManager?.finalizeSuite(suite)\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n}\n\nexport function logRunSummary(ctx: RunLifecycleCtx): void {\n const summary = [\n ctx.passCount > 0 ? `${ctx.passCount} passed` : null,\n ctx.failCount > 0 ? `${ctx.failCount} failed` : null,\n ctx.skipCount > 0 ? `${ctx.skipCount} skipped` : null\n ]\n .filter(Boolean)\n .join(' ')\n log.info(`${ctx.failCount > 0 ? '❌' : '✅'} Tests complete! ${summary}`)\n log.info(` DevTools UI: http://${ctx.options.hostname}:${ctx.options.port}`)\n}\n\nexport async function waitForDevtoolsBrowserClose(\n ctx: RunLifecycleCtx\n): Promise<void> {\n if (!ctx.devtoolsBrowser) {\n return\n }\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'warn'\n )\n let exitBySignal = false\n const signalHandler = () => {\n exitBySignal = true\n log.info('\\n✓ Exiting... Browser window will remain open')\n process.exit(0)\n }\n process.once('SIGINT', signalHandler)\n process.once('SIGTERM', signalHandler)\n while (true) {\n try {\n await ctx.devtoolsBrowser.getTitle()\n await new Promise((res) => setTimeout(res, TIMING.BROWSER_POLL_INTERVAL))\n } catch {\n if (!exitBySignal) {\n log.info('Browser window closed, stopping DevTools app')\n break\n }\n }\n }\n if (exitBySignal) {\n return\n }\n process.removeListener('SIGINT', signalHandler)\n process.removeListener('SIGTERM', signalHandler)\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'info'\n )\n try {\n await ctx.devtoolsBrowser.deleteSession()\n } catch {\n /* session already closed */\n }\n await stop()\n process.exit(0)\n}\n","/**\n * Global key the plugin uses to hand itself to the Cucumber hooks\n * (`helpers/cucumberHooks.cts`). Single source of truth, re-exported by\n * `constants.ts`.\n *\n * It lives in its own leaf module with NO imports on purpose: the Cucumber\n * hooks are bundled standalone into a CJS file, and importing this from the\n * full `constants.ts` would drag in its ESM dependency graph (→ core), which\n * uses `import.meta.url` / `createRequire` and throws when bundled to CJS.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\n","/**\n * Internal Nightwatch commands to exclude from capture\n */\nexport const INTERNAL_COMMANDS_TO_IGNORE = [\n 'isAppiumClient',\n 'isSafari',\n 'isChrome',\n 'isFirefox',\n 'isEdge',\n 'isMobile',\n 'isAndroid',\n 'isIOS',\n 'session',\n 'sessions',\n 'timeouts',\n 'timeoutsAsyncScript',\n 'timeoutsImplicitWait',\n 'getLog',\n 'getLogTypes',\n 'screenshot',\n 'availableContexts',\n 'currentContext',\n 'setChromeOptions',\n 'setDeviceName',\n 'perform',\n 'execute',\n 'executeAsync',\n 'executeScript',\n // Internal Nightwatch transport commands (used for log capture, not user actions)\n 'sessionLog',\n 'sessionLogTypes',\n 'isLogAvailable',\n 'end'\n] as const\n\n// Console capture constants are defined in @wdio/devtools-core; re-exported\n// here so existing imports from ./constants.js continue to work.\nexport {\n ANSI_REGEX,\n CONSOLE_METHODS,\n LOG_LEVEL_PATTERNS,\n LOG_SOURCES\n} from '@wdio/devtools-core'\n\nimport { DEFAULTS_BASE, TIMING_BASE } from '@wdio/devtools-shared'\n\nexport const DEFAULTS = {\n ...DEFAULTS_BASE,\n TEST_NAME: 'unknown',\n FILE_NAME: 'unknown'\n} as const\n\nexport const TIMING = {\n ...TIMING_BASE,\n /** Nightwatch boots slower than selenium — give the dashboard 10s. */\n UI_CONNECTION_WAIT: 10000\n} as const\n\nexport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Generic pattern matching Nightwatch commands whose result is a boolean.\n */\nexport const BOOLEAN_COMMAND_PATTERN =\n /^waitFor|^is[A-Z]|^has[A-Z]|(Visible|Present|Enabled|Selected|NotVisible|NotPresent)$/\n\nexport const NAVIGATION_COMMANDS = ['url', 'navigate', 'navigateTo'] as const\n\nexport { SPINNER_RE } from '@wdio/devtools-core'\n\n/** Matches file names that follow the *.test.ts / *.spec.js naming\n * convention. Re-exported from @wdio/devtools-shared (single source of\n * truth — service uses the same pattern under the name SPEC_FILE_RE). */\nexport { SPEC_FILE_RE as TEST_FILE_PATTERN } from '@wdio/devtools-shared'\n\n/** Nightwatch config file names to search for, in priority order. */\nexport const CONFIG_FILENAMES = [\n 'nightwatch.conf.cjs',\n 'nightwatch.conf.js',\n 'nightwatch.conf.ts',\n 'nightwatch.conf.mjs',\n 'nightwatch.json'\n] as const\n\n/**\n * Global key used to share the plugin instance with Cucumber hooks.\n * Defined in its own leaf module so the Cucumber hooks can bundle it standalone\n * (see plugin-global-key.ts); re-exported here for the rest of the plugin.\n */\nexport { PLUGIN_GLOBAL_KEY } from './plugin-global-key.js'\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { parse as parseStackTrace } from 'stacktrace-parser'\nimport {\n findTestDefinitions,\n generateStableUid as generateStableUidByFileName,\n isUserCodeFrame,\n normalizeFilePath\n} from '@wdio/devtools-core'\nimport { TEST_FILE_PATTERN, CONFIG_FILENAMES } from '../constants.js'\nimport type {\n NightwatchTestCase,\n TestFileMetadata,\n StepLocation,\n TestRunnerId,\n TestStats\n} from '../types.js'\n\n// These three are pure re-exports — adapters use the core implementations\n// directly, no wrapper logic. Single-line re-exports keep the indirection\n// visible without introducing dummy variables.\nexport {\n deterministicUid,\n getCallSourceFromStack,\n resetSignatureCounters\n} from '@wdio/devtools-core'\n\nexport function determineTestState(\n testcase: NightwatchTestCase\n): 'passed' | 'failed' | 'skipped' {\n if (testcase.passed === 0 && testcase.failed === 0) {\n return 'skipped'\n }\n return testcase.passed > 0 && testcase.failed === 0 ? 'passed' : 'failed'\n}\n\nexport function getTestIcon(state: TestStats['state']): string {\n return state === 'passed' ? '✅' : state === 'skipped' ? '⏭' : '❌'\n}\n\nexport function incrementCounters(\n counters: { passCount: number; failCount: number; skipCount: number },\n state: TestStats['state']\n): void {\n if (state === 'passed') {\n counters.passCount++\n } else if (state === 'skipped') {\n counters.skipCount++\n } else {\n counters.failCount++\n }\n}\n\n/** This adapter's runner identity. Both Nightwatch runners take the same\n * locator dialect, but the id also reaches the dashboard's run capabilities,\n * where the cucumber variant differs. */\nexport function nightwatchRunnerId(isCucumberRunner: boolean): TestRunnerId {\n return isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch'\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: nightwatchRunnerId(input.isCucumberRunner),\n configFile: input.configPath,\n baseDir: process.cwd(),\n runCapabilities: {\n canRunSuites: true,\n canRunTests: !input.isCucumberRunner,\n canRunAll: false\n }\n }\n}\n\n/**\n * Generate stable UID for test/suite.\n * Accepts either (item: SuiteStats | TestStats) or (file: string, name: string).\n * Hashing is delegated to @wdio/devtools-core; this wrapper preserves the\n * dual-signature convenience used by the Nightwatch suite/test managers.\n */\ntype StableUidSource = { file?: string; fullTitle?: string; title?: string }\nexport function generateStableUid(\n itemOrFile: string | StableUidSource,\n name?: string\n): string {\n let file: string, testName: string\n if (\n typeof itemOrFile === 'object' &&\n itemOrFile !== null &&\n name === undefined\n ) {\n file = itemOrFile.file || ''\n testName = String(itemOrFile.fullTitle || itemOrFile.title)\n } else {\n file = (itemOrFile as string) || ''\n testName = String(name || '')\n }\n return generateStableUidByFileName(file, testName)\n}\n\n/**\n * Find test file from stack trace.\n * Parses call stack to find the first frame that looks like a test file.\n */\nexport function findTestFileFromStack(): string | undefined {\n const stack = new Error().stack\n if (!stack) {\n return undefined\n }\n\n // Prefer a frame whose filename matches *.test.ts / *.spec.ts (strong\n // signal). Fall back to the first plain user-code frame so arbitrary\n // project layouts (e.g. Cucumber step files under `src/steps/`) still\n // surface their owning file instead of going undetected.\n const frames = parseStackTrace(stack)\n const preferred = frames.find(\n (f) => isUserCodeFrame(f) && TEST_FILE_PATTERN.test(f.file)\n )\n const frame = preferred ?? frames.find(isUserCodeFrame)\n if (!frame?.file) {\n return undefined\n }\n\n const filePath = normalizeFilePath(frame.file)\n return fs.existsSync(filePath) ? filePath : undefined\n}\n\n/**\n * Extract suite and test names — and their line numbers — from a test file.\n * Returns a TestFileMetadata object used to build `callSource` strings for\n * the TestLens eye-icon navigation.\n */\nexport function extractTestMetadata(filePath: string): TestFileMetadata {\n const defs = findTestDefinitions(filePath, {\n includeNightwatchObjectStyle: true\n })\n const firstSuite = defs.find((d) => d.kind === 'suite')\n const tests = defs.filter((d) => d.kind === 'test')\n return {\n suiteTitle: firstSuite?.title ?? null,\n suiteLine: firstSuite?.line ?? null,\n testNames: tests.map((t) => t.title),\n testLines: tests.map((t) => t.line)\n }\n}\n\n/**\n * Find test file by searching the workspace for a matching filename.\n * Used when the stack trace doesn't have the file yet (e.g. in beforeEach).\n */\nexport function findTestFileByName(\n filename: string,\n workspaceRoot?: string\n): string | undefined {\n if (!filename || !workspaceRoot) {\n return undefined\n }\n\n const baseFilename = filename.replace(/\\.[cm]?[jt]sx?$/, '')\n\n function searchDir(dir: string, depth = 0): string | undefined {\n if (depth > 5) {\n return undefined\n }\n try {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n const found = searchDir(fullPath, depth + 1)\n if (found) {\n return found\n }\n } else if (\n TEST_FILE_PATTERN.test(entry.name) &&\n entry.name.replace(TEST_FILE_PATTERN, '') === baseFilename\n ) {\n return fullPath\n }\n }\n } catch {\n // Permission denied or other error — skip\n }\n return undefined\n }\n\n return searchDir(workspaceRoot)\n}\n\n// ---------------------------------------------------------------------------\n// Console / log helpers (used by SessionCapturer)\n// ---------------------------------------------------------------------------\n\n// Console helpers come from @wdio/devtools-core. `stripAnsiCodes` is the\n// local name kept for backwards compatibility with existing import sites.\nexport {\n stripAnsi as stripAnsiCodes,\n detectLogLevel,\n createConsoleLogEntry\n} from '@wdio/devtools-core'\n\nexport { chromeLogLevelToLogLevel } from '@wdio/devtools-core'\n\n/** Derive a human-readable request type from URL and MIME type. */\nexport { getRequestType } from '@wdio/devtools-core'\n\n// ---------------------------------------------------------------------------\n// Cucumber helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a feature file for a given scenario in a single pass, returning:\n * - `featureLine` — 1-based line of the `Feature:` declaration\n * - `scenarioLine` — 1-based line of the matching `Scenario:` block\n * - `stepLines` — 1-based line numbers for each step (for TestLens navigation)\n * - `stepKeywords` — BDD keyword (Given/When/Then/And/But) for each step (for labels)\n */\nfunction collectStepsAfterScenario(\n lines: string[],\n scenarioIndex: number,\n stepCount: number\n): { stepLines: number[]; stepKeywords: string[] } {\n const stepRe = /^\\s*(Given|When|Then|And|But)\\s+/i\n const stepLines: number[] = []\n const stepKeywords: string[] = []\n for (\n let j = scenarioIndex + 1;\n j < lines.length && stepLines.length < stepCount;\n j++\n ) {\n if (/^\\s*(?:Scenario:|Feature:)/i.test(lines[j])) {\n break\n }\n const m = stepRe.exec(lines[j])\n if (m) {\n stepLines.push(j + 1)\n stepKeywords.push(m[1])\n }\n }\n return { stepLines, stepKeywords }\n}\n\nexport function parseCucumberScenario(\n featureContent: string,\n scenarioName: string,\n stepTexts: string[]\n): {\n featureLine: number\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const stepCount = stepTexts.length\n if (!featureContent) {\n return {\n featureLine: 1,\n scenarioLine: 1,\n stepLines: [],\n stepKeywords: Array<string>(stepCount).fill('')\n }\n }\n const lines = featureContent.split('\\n')\n let featureLine = 1\n let scenarioLine = 1\n let stepLines: number[] = []\n let stepKeywords: string[] = []\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n if (featureLine === 1 && /^\\s*Feature:/i.test(line)) {\n featureLine = lineNum\n continue\n }\n if (/^\\s*Scenario:/i.test(line) && line.includes(scenarioName)) {\n scenarioLine = lineNum\n const collected = collectStepsAfterScenario(lines, i, stepCount)\n stepLines = collected.stepLines\n stepKeywords = collected.stepKeywords\n break\n }\n }\n while (stepKeywords.length < stepCount) {\n stepKeywords.push('')\n }\n return { featureLine, scenarioLine, stepLines, stepKeywords }\n}\n\n/** Maps Cucumber parameter type placeholder names to their regex equivalents. */\nconst CUCUMBER_TYPE_PATTERNS: Record<string, string> = {\n int: '-?\\\\d+',\n float: '-?\\\\d+(?:\\\\.\\\\d+)?',\n string: '(?:\"[^\"]*\"|\\'[^\\']*\\')',\n word: '\\\\S+'\n}\n\n/**\n * Convert a Cucumber expression pattern to a regular expression and test it\n * against the given step text. Handles {int}, {float}, {string}, {word}, and\n * arbitrary {type} placeholders.\n */\nfunction matchesCucumberExpression(pattern: string, text: string): boolean {\n if (pattern === text) {\n return true\n }\n const regexSource = pattern\n .split(/(\\{[^}]*\\})/)\n .map((part, idx) => {\n if (idx % 2 === 1) {\n const type = part.slice(1, -1)\n return CUCUMBER_TYPE_PATTERNS[type] ?? '[\\\\s\\\\S]+'\n }\n return part.replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&')\n })\n .join('')\n try {\n return new RegExp(`^${regexSource}$`).test(text)\n } catch {\n return false\n }\n}\n\n/**\n * Find the file and 1-based line number of the step definition that matches\n * the given step text. Searches the provided step definition files in order.\n * Handles both string/Cucumber-expression patterns and regex literals.\n */\nexport function findStepDefinitionLine(\n stepDefFiles: Array<{ filePath: string; content: string }>,\n stepText: string\n): StepLocation | null {\n const stepDefRe = /(?:Given|When|Then|And|But)\\s*\\(/i\n\n for (const { filePath, content } of stepDefFiles) {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!stepDefRe.test(line)) {\n continue\n }\n\n // String / Cucumber-expression: Given('pattern', ...) or Given(\"pattern\", ...)\n const literalMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*(['\"`])(.*?)\\1/i\n )\n if (literalMatch) {\n if (matchesCucumberExpression(literalMatch[2], stepText)) {\n return { filePath, line: i + 1 }\n }\n continue\n }\n\n // Regex literal: Given(/pattern/flags, ...)\n const regexMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*\\/(.+?)\\/([gimy]*)/i\n )\n if (regexMatch) {\n try {\n if (new RegExp(regexMatch[1], regexMatch[2]).test(stepText)) {\n return { filePath, line: i + 1 }\n }\n } catch {\n // invalid regex — skip\n }\n }\n }\n }\n return null\n}\n\nexport { isPortInUse, findFreePort } from '@wdio/devtools-core'\n\nexport function resolveNightwatchConfig(): string | undefined {\n // Prefer the config explicitly passed via -c / --config to avoid picking up\n // an unrelated config file that happens to sit higher in the directory tree.\n const configFlagIdx = process.argv.findIndex(\n (arg) => arg === '--config' || arg === '-c'\n )\n if (configFlagIdx !== -1 && process.argv[configFlagIdx + 1]) {\n const argvConfig = process.argv[configFlagIdx + 1]\n const resolved = path.isAbsolute(argvConfig)\n ? argvConfig\n : path.resolve(process.cwd(), argvConfig)\n if (fs.existsSync(resolved)) {\n return resolved\n }\n }\n\n // Fallback: walk up the directory tree\n let dir = process.cwd()\n const root = path.parse(dir).root\n while (dir && dir !== root) {\n for (const file of CONFIG_FILENAMES) {\n const candidate = path.join(dir, file)\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n const parent = path.dirname(dir)\n if (parent === dir) {\n break\n }\n dir = parent\n }\n return undefined\n}\n","/**\n * Session-initialization lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size\n * cap. The plugin passes itself as a `SessionInitCtx` — a narrow interface\n * exposing only the fields and methods these helpers need.\n *\n * Includes:\n * - Per-run bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Re-targeting that one capturer when the session under it is replaced\n * - Re-arming the per-session capture channels when a session is replaced\n * - Screencast finalize-and-clear\n *\n * The recorder's own session binding lives in `screencast-session.ts`.\n */\n\nimport logger from '@wdio/logger'\nimport { resolveViewport } from '@wdio/devtools-core'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport {\n errorMessage,\n finalizeScreencast,\n registerCollectorPreload,\n resolveAdapterOutputDir\n} from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport { TIMING } from './constants.js'\nimport { SessionCapturer } from './session.js'\nimport { TestReporter } from './reporter.js'\nimport { TestManager } from './helpers/testManager.js'\nimport { SuiteManager } from './helpers/suiteManager.js'\nimport { BrowserProxy } from './helpers/browserProxy.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport {\n rotateScreencastForSession,\n startScreencast\n} from './screencast-session.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats,\n TestRunnerId,\n TestStats\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:session-init')\n\nexport interface SessionInitCtx {\n readonly hostname: string\n readonly port: number\n readonly screencastOptions: ScreencastOptions\n readonly bidiEnabled: boolean\n readonly mode: DevToolsMode\n readonly captureAssertions: boolean\n readonly runner: TestRunnerId\n\n sessionCapturer: SessionCapturer\n testReporter: TestReporter\n testManager: TestManager\n suiteManager: SuiteManager\n browserProxy: BrowserProxy\n isScriptInjected: boolean\n\n lastSessionId: string | null\n /** Session the BiDi attach + collector preload were last armed for — the\n * once-per-session guard for both, and the rotation detector. */\n armedSessionId: string | undefined\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n /** In-flight recorder rotation — see screencast-session.ts. */\n screencastRotation: Promise<void> | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n attemptFor(uid: string): number | undefined\n recordOutcome(uid: string, state: TestStats['state']): void\n /** Drains the live recorder's frames into the run's filmstrip buffer, then\n * stops and clears it. */\n finalizeCurrentScreencast(): Promise<void>\n}\n\nfunction initReporterChain(ctx: SessionInitCtx): void {\n // First-time setup: create reporter chain once for the entire run.\n // These must NOT be recreated on session change — doing so generates a\n // new feature suite with a fresh start timestamp, which DataManager sees\n // as a new run and wipes all accumulated commands.\n ctx.testReporter = new TestReporter(\n (suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n },\n (uid) => ctx.attemptFor(uid)\n )\n ctx.testManager = new TestManager(ctx.testReporter, (uid, state) =>\n ctx.recordOutcome(uid, state)\n )\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite(),\n ctx.captureAssertions,\n // Nightwatch raises no session event and never wraps `end`, so a command\n // running under a new sessionId is the only trace a mid-run\n // `browser.end()` leaves behind. Both re-bindings hang off it; each latches\n // and gates itself, so neither can suppress the other.\n (browser) => {\n rotateScreencastForSession(ctx, browser)\n rearmCaptureForSession(ctx, browser)\n }\n )\n}\n\n/** The run's geometry, over the raw WebDriver transport: `browser.*` commands\n * are QUEUED, so a read issued here would sit behind the command in flight. */\nfunction readViewport(browser: NightwatchBrowser) {\n return resolveViewport(browser.capabilities || {}, {\n runScript: (body) => webdriverExecute(browser, body),\n getWindowSize: () => webdriverGet(browser, 'window/rect'),\n onWarn: (message) => log.warn(message)\n })\n}\n\nasync function broadcastSessionMetadata(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const capabilities = browser.capabilities || {}\n const desiredCapabilities = browser.desiredCapabilities || {}\n const sessionId = browser.sessionId\n const opts = browser.options || {}\n\n if (ctx.srcFolders.length === 0) {\n const sf = (opts as { src_folders?: string | string[] }).src_folders\n ctx.srcFolders = Array.isArray(sf) ? sf : sf ? [sf] : []\n }\n\n const viewport = await readViewport(browser)\n const metadata = {\n type: TraceType.Testrunner,\n capabilities,\n ...(viewport ? { viewport } : {}),\n desiredCapabilities,\n sessionId,\n testEnv: opts.testEnv,\n host: opts.webdriver?.host,\n options: ctx.buildMetadataOptions(),\n runner: ctx.runner,\n url: ''\n }\n // Single-valued, so a run-spanning trace carries the LAST session's identity.\n ctx.sessionCapturer.metadata = metadata\n ctx.sessionCapturer.sendUpstream('metadata', metadata)\n\n const browserName =\n capabilities.browserName || desiredCapabilities.browserName || 'unknown'\n const browserVersion =\n capabilities.browserVersion ||\n (capabilities as { version?: string }).version ||\n ''\n log.info(\n `✓ Browser: ${browserName}${browserVersion ? ' ' + browserVersion : ''} (session: ${sessionId})`\n )\n\n const loggingPrefs = ((capabilities as Record<string, unknown>)[\n 'goog:loggingPrefs'\n ] ||\n (desiredCapabilities as Record<string, unknown>)['goog:loggingPrefs'] ||\n {}) as { performance?: string }\n if (!loggingPrefs.performance && !ctx.bidiEnabled) {\n log.warn(\n \"⚠ Network tab will be empty — add 'goog:loggingPrefs': { performance: 'ALL' } to your capabilities (or enable bidi:true)\"\n )\n }\n}\n\n// BiDi: opt-in. Requires `webSocketUrl: true` capability + a BiDi-capable\n// chromedriver. Attempted once per session (the caller's `armedSessionId` stamp\n// is that guard); on failure or unavailability the perf-log fallback path\n// continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled) {\n return\n }\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n log.warn('bidi:true set but browser.driver unavailable — skipping')\n return\n }\n const { attachBidiHandlers, buildBidiSinks } = await import('./bidi.js')\n const ok = await attachBidiHandlers(\n driver,\n buildBidiSinks(ctx.sessionCapturer)\n )\n if (ok) {\n ctx.sessionCapturer.bidiActive = true\n log.info('✓ BiDi attached — perf-log network capture disabled')\n }\n}\n\n/**\n * Register the collector to run at document-start, so every document — including\n * the ones a navigation creates without us noticing — instruments and anchors\n * itself.\n *\n * Deliberately NOT gated on the `bidi` option: that option exists to avoid\n * double-reporting console/network against the perf-log path, whereas this needs\n * nothing but a session created with `webSocketUrl: true`. Gating DOM capture on\n * an unrelated opt-in that defaults to false would leave the race in place for\n * almost every user. Self-degrades to the per-document `<script>` injection when\n * BiDi isn't there.\n */\nasync function tryRegisterPreload(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n return\n }\n ctx.sessionCapturer.preloadRegistered = await registerCollectorPreload(\n driver,\n (level, message) => log[level](message)\n )\n}\n\n/**\n * Arm the two capture channels that belong to ONE WebDriver session: the BiDi\n * inspectors (bound to that session's driver object) and the document-start\n * collector preload (registered on that session's BiDi channel). Neither\n * survives the session, so a replaced session needs both again.\n *\n * The stamp is written before any await: on rotation this runs from the command\n * hook, where the next command arrives long before a BiDi round trip completes,\n * and the stamp is what keeps that flood from re-arming.\n *\n * The preload goes FIRST. Behind the attach it lost the race it exists to win —\n * measured on a re-arm, the network subscribe took 6.2 s while the command that\n * triggered the re-arm navigated, so the registration landed two seconds after\n * the document it was meant to instrument was born. Sequential rather than\n * parallel because both steps open the session's BiDi channel through the same\n * unsynchronized `getBidi()` cache, and racing them opens two websockets of\n * which `quit()` closes one.\n */\nasync function armCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n ctx.armedSessionId = browser.sessionId ?? undefined\n await tryRegisterPreload(ctx, browser)\n await tryAttachBidi(ctx, browser)\n}\n\n/**\n * Whether the browser is executing under a session the capture channels were\n * never armed for. Requires an already-armed session: before bringup there is no\n * capturer to arm onto, and `ensureSessionInitialized` owns that first arm.\n */\nexport function needsCaptureRearm(\n ctx: Pick<SessionInitCtx, 'armedSessionId' | 'sessionCapturer'>,\n sessionId: string | undefined\n): boolean {\n return (\n Boolean(ctx.sessionCapturer) &&\n sessionId !== undefined &&\n ctx.armedSessionId !== undefined &&\n ctx.armedSessionId !== sessionId\n )\n}\n\n/**\n * Re-arm BiDi and the collector preload after the session was replaced. Without\n * it both are established exactly once, at bringup, and sessions 2..N silently\n * fall back to `<script>` injection — the whole navigation race class the\n * preload exists to remove comes back for the rest of the run.\n *\n * The `needsCaptureRearm` guard is what makes it idempotent: the command hook\n * and `ensureSessionInitialized` both detect the same replacement, and\n * `armCaptureForSession` stamps `armedSessionId` before its first await, so\n * whichever gets there first latches the other out.\n */\nasync function armReplacedSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const sessionId = browser.sessionId ?? undefined\n if (!needsCaptureRearm(ctx, sessionId)) {\n return\n }\n // Both claims belong to the session that just died. Until the new\n // registration lands this session's documents are back on the `<script>`\n // path — exactly what `anchorAfterNavigation` polls for — and a stale\n // `bidiActive` would keep the perf-log network path gated off behind\n // inspectors that no longer receive anything.\n ctx.sessionCapturer.preloadRegistered = false\n ctx.sessionCapturer.bidiActive = false\n log.info(`Session replaced — re-arming capture for ${sessionId}`)\n await armCaptureForSession(ctx, browser)\n}\n\n/** Fire-and-forget {@link armReplacedSession}, for the command hook — a command\n * must not wait on a BiDi handshake. */\nexport function rearmCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n void armReplacedSession(ctx, browser).catch((err) =>\n log.warn(`Capture re-arm failed: ${errorMessage(err)}`)\n )\n}\n\n/** Re-run the per-session bringup against a replaced session, keeping the run's\n * one capturer. Both halves are the guarded helpers the command hook uses, so a\n * replacement both paths see is armed and rotated once. */\nasync function rebindSessionToBrowser(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n log.info('Browser session changed — re-targeting the run capturer')\n // Also gates `wrapUrlMethod`, which is per browser OBJECT — cucumber hands\n // over a new one per scenario.\n ctx.isScriptInjected = false\n await broadcastSessionMetadata(ctx, browser)\n await armReplacedSession(ctx, browser)\n rotateScreencastForSession(ctx, browser)\n await ctx.screencastRotation\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n // Unconditional: cucumber hands over a new browser object per scenario, and\n // a stale reference answers neither probe the capturer runs against it.\n ctx.sessionCapturer.setBrowser(browser)\n if (isSessionChange) {\n await rebindSessionToBrowser(ctx, browser)\n }\n return\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.INITIAL_CONNECTION_WAIT)\n )\n // Trace mode: empty opts skip SessionCapturerBase's WS init — no backend\n // to forward events to anyway.\n ctx.sessionCapturer = new SessionCapturer(\n ctx.mode === 'trace' ? {} : { port: ctx.port, hostname: ctx.hostname },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n ctx.sessionCapturer.runner = ctx.runner\n if (ctx.mode !== 'trace') {\n const connected = await ctx.sessionCapturer.waitForConnection(3000)\n if (!connected) {\n log.error('❌ Worker WebSocket failed to connect!')\n }\n }\n initReporterChain(ctx)\n await broadcastSessionMetadata(ctx, browser)\n await armCaptureForSession(ctx, browser)\n await startScreencast(ctx, browser, browser.sessionId)\n}\n\nexport async function finalizeCurrentScreencast(\n ctx: SessionInitCtx\n): Promise<void> {\n if (!ctx.screencastRecorder || !ctx.screencastSessionId) {\n return\n }\n if (ctx.mode === 'trace') {\n // Trace mode: the per-test video is written by the produce path and the\n // filmstrip frames are embedded in the trace itself, so stop the recorder\n // without encoding a session .webm nothing references (orphan file).\n await ctx.screencastRecorder.stop()\n } else {\n await finalizeScreencast({\n recorder: ctx.screencastRecorder,\n sessionId: ctx.screencastSessionId,\n filenamePrefix: 'nightwatch-video',\n outputDir: resolveAdapterOutputDir({\n testFilePath: ctx.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: ctx.configPath\n }),\n captureFormat: ctx.screencastOptions.captureFormat,\n sendUpstream: (scope, data) =>\n ctx.sessionCapturer?.sendUpstream(scope, data),\n onLog: (level, message) => log[level](message)\n })\n }\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","// Direct WebDriver HTTP transport for the capture probes.\n//\n// Every probe the trace capture makes — url, title, screenshot, in-page script\n// — has to bypass Nightwatch's command queue. `browser.*` commands are QUEUED,\n// so a probe the plugin issues from inside its own command hook enqueues behind\n// the command that is still running and cannot resolve until the queue drains.\n// Unguarded, one such probe stranded the whole per-action snapshot capture; the\n// in-page script probe merely timed out, leaving empty a11y trees.\n\nimport logger from '@wdio/logger'\nimport {\n webdriverGet as coreGet,\n webdriverPost as corePost,\n type WebDriverAddress\n} from '@wdio/devtools-core'\nimport type { NightwatchBrowser } from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:webdriverHttp')\n\ntype LooseRec = Record<string, unknown>\n\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\n\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\n\nconst firstDefined = (obj: unknown, ...keys: string[]): unknown => {\n if (!obj || typeof obj !== 'object') {\n return undefined\n }\n const rec = obj as LooseRec\n for (const k of keys) {\n const v = rec[k]\n if (v !== undefined && v !== null) {\n return v\n }\n }\n return undefined\n}\n\n/**\n * Walks Nightwatch's internal config (transport / queue.transport /\n * nightwatchInstance — none of which are on the public NightwatchBrowser type)\n * to find the underlying WebDriver host+port.\n */\nexport function resolveWebDriverAddress(browser: NightwatchBrowser): {\n driverHost: string\n driverPort: number\n} {\n const transportSettings =\n getPath(browser, 'transport', 'settings', 'webdriver') ||\n getPath(browser, 'queue', 'transport', 'settings', 'webdriver') ||\n getPath(\n browser,\n 'nightwatchInstance',\n 'transport',\n 'settings',\n 'webdriver'\n ) ||\n {}\n const opts = getProp(browser, 'options') ?? {}\n const nightwatchSettings =\n getPath(browser, 'nightwatchInstance', 'settings') ||\n getPath(browser, 'globals', 'nightwatchInstance', 'settings') ||\n {}\n const driverHost = String(\n firstDefined(transportSettings, 'host', 'server_address') ||\n firstDefined(getProp(opts, 'webdriver'), 'host') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'host') ||\n 'localhost'\n )\n const driverPort = Number(\n firstDefined(transportSettings, 'port') ||\n firstDefined(getProp(opts, 'webdriver'), 'port') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'port') ||\n 9515\n )\n return { driverHost, driverPort }\n}\n\n/** The core transport's address, plus this adapter's logger. Nightwatch always\n * speaks plain http to a local driver — it has no cloud-grid path of its own. */\nfunction address(browser: NightwatchBrowser): WebDriverAddress {\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return {\n hostname: driverHost,\n port: driverPort,\n onWarn: (message: string) => log.warn(message)\n }\n}\n\nfunction sessionId(browser: NightwatchBrowser): string | undefined {\n return (browser as unknown as { sessionId?: string }).sessionId\n}\n\n/** GET `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverGet<T>(\n browser: NightwatchBrowser,\n path: string\n): Promise<T | null> {\n const id = sessionId(browser)\n return id ? coreGet<T>(address(browser), id, path) : Promise.resolve(null)\n}\n\n/** POST `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverPost<T>(\n browser: NightwatchBrowser,\n path: string,\n body: unknown\n): Promise<T | null> {\n const id = sessionId(browser)\n return id\n ? corePost<T>(address(browser), id, path, body)\n : Promise.resolve(null)\n}\n\n/** Run a script in the page, outside the command queue. `body` is a function\n * body, matching what `browser.execute` accepts. */\nexport function webdriverExecute<T>(\n browser: NightwatchBrowser,\n body: string\n): Promise<T | null> {\n return webdriverPost<T>(browser, 'execute/sync', { script: body, args: [] })\n}\n","import logger from '@wdio/logger'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n COLLECTOR_READY_EXPRESSION,\n SessionCapturerBase,\n applyPerformanceData,\n collectorDrainExpression,\n drainCollectorWithRecovery,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n mapCommandToAction,\n pollUntilReady,\n serializeError,\n upsertRichestSnapshot,\n type CapturedPerformancePayload,\n type SessionCapturerOptions\n} from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { nightwatchRunnerId } from './helpers/utils.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type {\n ActionSnapshot,\n CommandCaptureMeta,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser,\n TestRunnerId\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:SessionCapturer')\n\n/**\n * WebDriver responses are sometimes wrapped as `{ value: T }` (the W3C\n * protocol shape) and sometimes flat. This helper unwraps the value field\n * if present, otherwise returns the input as-is.\n */\nfunction unwrapDriverValue<T = unknown>(result: unknown): T {\n if (result && typeof result === 'object' && 'value' in result) {\n return (result as { value: T }).value\n }\n return result as T\n}\n\nexport class SessionCapturer extends SessionCapturerBase {\n #browser: NightwatchBrowser | undefined\n\n // True once BiDi inspectors are attached — the per-command perf-log network\n // capture path skips when set, so we don't double-emit network requests.\n bidiActive = false\n\n /** True once the collector is registered to run at document-start. Every\n * document then instruments and anchors itself, so the paths that exist to\n * notice a navigation after the fact — re-injection and the settle poll — have\n * nothing left to do and stand down. */\n preloadRegistered = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n\n /** Which Nightwatch runner is driving — set by the plugin, and passed to the\n * per-action capture so the injected scripts emit this runner's dialect. */\n runner: TestRunnerId = nightwatchRunnerId(false)\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: SessionCapturerOptions = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n /** Re-target at the browser the run now drives — the capturer's only\n * session-bound state, so a replaced session is a re-target, not a rebuild\n * (mirrors selenium's `setDriver`). */\n setBrowser(browser: NightwatchBrowser): void {\n this.#browser = browser\n }\n\n protected override onWsOpen(): void {\n log.info('✓ Worker WebSocket connected to backend')\n }\n\n protected override onWsError(err: unknown): void {\n log.error(`Couldn't connect to devtools backend: ${errorMessage(err)}`)\n }\n\n protected override onWsClose(): void {\n log.info('Worker WebSocket disconnected')\n }\n\n async captureCommand(\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n meta: CommandCaptureMeta = {}\n ): Promise<boolean> {\n // Serialize error properly (Error objects don't JSON.stringify well)\n const serializedError = serializeError(error)\n\n const commandId = this.commandCounter++\n const commandLogEntry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n\n this.commandsLog.push(commandLogEntry)\n\n const isNavigationCommand = NAVIGATION_COMMANDS.some((cmd) =>\n command.toLowerCase().includes(cmd.toLowerCase())\n )\n if (isNavigationCommand && this.#browser && !error) {\n this.#capturePerformanceData(commandLogEntry, args).catch((err) => {\n log.warn(`Failed to capture performance data: ${errorMessage(err)}`)\n })\n }\n\n if (!error) {\n this.#queueActionSnapshot(command, commandLogEntry.timestamp)\n }\n\n return true\n }\n\n /** Fire-and-forget post-action snapshot, drained at finalize. Stamped with the\n * logged command's timestamp, not the capture's own: the capture resolves\n * after the command completes, and export claims snapshots by exact equality\n * with the command timestamp (FrameSnapshotIndex.claimAfter / elementsAt). */\n #queueActionSnapshot(command: string, timestamp: number): void {\n if (\n this.traceMode !== 'trace' ||\n !this.#browser ||\n !mapCommandToAction(command)\n ) {\n return\n }\n this.snapshotCaptures.push(\n captureActionSnapshot(\n this.#browser,\n command,\n timestamp,\n this.runner,\n this.isNativeAppSession\n ).then((snap) => {\n if (snap) {\n upsertRichestSnapshot(this.actionSnapshots, snap)\n }\n })\n )\n }\n\n async #capturePerformanceData(\n commandLogEntry: CommandLog & { _id?: number },\n args: unknown[]\n ) {\n // Page script, and the 500 ms settle below would be spent to reach a\n // document that does not exist.\n if (this.isNativeAppSession) {\n return\n }\n await new Promise((resolve) => setTimeout(resolve, 500))\n const raw = await this.#browser!.execute(CAPTURE_PERFORMANCE_SCRIPT)\n const payload = unwrapDriverValue<CapturedPerformancePayload | undefined>(\n raw\n )\n applyPerformanceData(\n commandLogEntry,\n payload,\n typeof args[0] === 'string' ? args[0] : undefined\n )\n }\n\n /**\n * Ingest a pre-built assertion entry (native `browser.assert`/`verify`\n * synthesized from Nightwatch's results, or a node:assert capture) into the\n * same command stream driver commands use. Unlike {@link captureCommand} this\n * preserves the entry's `title` (the human assertion message) and never\n * dedups — each call is a distinct action row. Assigns a fresh `_id` and a\n * matching stable public `id` (so a later `sendReplaceCommand` can update\n * this exact row in place — native asserts stream a pending row at call time,\n * then finalize their pass/fail), pushes, and broadcasts.\n */\n captureAssertCommand(entry: CommandLog): void {\n const withId = entry as CommandLog & { _id?: number }\n withId._id = this.commandCounter++\n withId.id = withId._id\n this.commandsLog.push(withId)\n // Deliberately no snapshot capture. These rows are emitted in a batch at\n // test-end but positioned back on their real execution window, so capturing\n // here would probe the page as it is NOW and stamp it at a moment seconds\n // earlier — an assert that ran on /secure rendered the /login page it later\n // logged out to. An assertion reads the page rather than changing it, so the\n // exporter lets it inherit the preceding action's capture instead\n // (FrameSnapshotIndex.claimAfter). The row's own `screenshot` is already\n // attached by the caller from the last resolved capture.\n this.sendCommand(withId)\n }\n\n /**\n * Replace an already-captured command entry (used for retried commands so\n * only the final execution result is shown in the UI).\n * Removes the old entry from commandsLog, revokes its sent-status so the\n * replacement can be sent, and returns the new entry together with the\n * old entry's timestamp (so the UI can locate and replace it in-place).\n */\n replaceCommand(\n oldId: number,\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n meta: CommandCaptureMeta = {}\n ): { entry: CommandLog & { _id?: number }; oldTimestamp: number } {\n // Remove the superseded entry and capture its timestamp for the UI\n const idx = this.commandsLog.findIndex(\n (c) => (c as CommandLog & { _id?: number })._id === oldId\n )\n const oldTimestamp: number =\n idx !== -1 ? (this.commandsLog[idx]?.timestamp ?? 0) : 0\n if (idx !== -1) {\n this.commandsLog.splice(idx, 1)\n }\n // Allow the slot to be re-used by a new entry\n this.sentCommandIds.delete(oldId)\n\n const serializedError = serializeError(error)\n const commandId = this.commandCounter++\n const entry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /** Birth time of the document this capturer last observed, so a replacement\n * is still visible to a poll that starts after the navigation committed. */\n private lastDocumentOrigin: number | undefined\n\n /** The current document's own birth time, which is what a DOM anchor is\n * stamped with. Undefined when the session cannot answer. */\n private async documentOrigin(\n browser: NightwatchBrowser\n ): Promise<number | undefined> {\n return webdriverExecute<number>(browser, 'return performance.timeOrigin;')\n .then((origin) => origin ?? undefined)\n .catch(() => undefined)\n }\n\n /**\n * Wait for a navigation to actually replace the document, then drain with a\n * forced anchor so the destination's DOM enters the stream.\n *\n * A Nightwatch click resolves BEFORE its navigation commits, so the drain that\n * runs at command completion still finds the outgoing page's collector: it\n * recovers nothing, and the destination is never anchored — every action on the\n * new page then replays the page it came from.\n *\n * The replacement signal is the document's own `performance.timeOrigin`\n * changing, NOT the collector going missing. Under the preload every document\n * instruments itself at document-start, so the collector is never absent and\n * that older signal stood permanently false — the destination was then only\n * ever anchored by a drain that happened to be slow enough to land after the\n * navigation, and tightening that latency lost the destination page entirely.\n * A same-URL re-navigation is a new document and a new origin, so this covers\n * it too; a command that navigated nowhere polls out and costs one drain of an\n * empty buffer.\n *\n * The baseline is the last origin THIS capturer saw, never one read on entry.\n * Reading it here would compare the document against itself: a command that\n * completes AFTER its navigation committed (`browser.url` waits for load, so\n * that is the normal case) would baseline the destination and then report no\n * change, dropping the very anchor this exists to take. With no baseline yet\n * a navigation cannot be ruled out either, so the first DOM-mutating command\n * of a run anchors unconditionally rather than guessing — `captureCurrentDom`\n * is idempotent per document, so an already-anchored page costs one drain.\n */\n async anchorAfterNavigation(browser: NightwatchBrowser): Promise<void> {\n // Polls the page for its own document identity, so there is nothing to\n // poll and nothing to anchor without one.\n if (this.isNativeAppSession) {\n return\n }\n const before = this.lastDocumentOrigin\n const replaced = await pollUntilReady(\n async () => {\n const now = await this.documentOrigin(browser)\n if (now === undefined) {\n return false\n }\n this.lastDocumentOrigin = now\n return before === undefined || now !== before\n },\n { attempts: 5, intervalMs: 150 }\n )\n if (!replaced) {\n return\n }\n // captureTrace's own recovery injects into the fresh document and re-drains,\n // and it gates that on the url being worth instrumenting — so a torn-down\n // session falls out here rather than logging an injection failure.\n //\n // The attribution is the newest command at THIS moment, not the one whose\n // completion started the poll: several polls overlap, so a field edit's poll\n // routinely observes the navigation the following click caused and would\n // credit itself — which pulled the anchor back before the edit's own row and\n // moved the wrong-DOM row one earlier instead of fixing it. Read at detection\n // time it is right for whichever poll gets there first.\n //\n // Unlike the inference in `anchorOwnerTimestamp`, this does not need the\n // \"nothing completed after the anchor\" guard: we just watched the document be\n // replaced, so a navigation demonstrably happened and the newest command is\n // the one that caused it. The guard exists for the case where that is a guess.\n await this.captureTrace(\n browser,\n true,\n this.commandsLog[this.commandsLog.length - 1]?.timestamp\n )\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly, bypassing\n * Nightwatch's command queue so the request can't be appended after\n * `end()` / `quit()`.\n */\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n return webdriverGet<string>(browser, 'screenshot')\n }\n\n protected override onSourceReadError(filePath: string, err: unknown): void {\n log.warn(`Failed to read source file ${filePath}: ${errorMessage(err)}`)\n }\n\n protected override onUpstreamDrop(\n event: string,\n reason: 'closed' | 'send-error',\n err?: unknown\n ): void {\n if (reason === 'send-error') {\n log.warn(`[upstream] Failed to send \"${event}\": ${errorMessage(err)}`)\n return\n }\n if (this.hasEverConnected()) {\n log.warn(`[upstream] WebSocket not open — dropping \"${event}\" event`)\n }\n }\n\n /**\n * Inject the WDIO devtools script into the browser page\n */\n async injectScript(browser: NightwatchBrowser) {\n if (this.isNativeAppSession) {\n return\n }\n try {\n // Injecting over a live collector replaces `window.wdioTraceCollector`\n // with a fresh instance and DISCARDS whatever it had buffered — including\n // the destination's full-DOM anchor. Several paths can reach the same\n // document (navigation proxy, drain recovery), so guard here once.\n const alreadyPresent = await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )\n if (alreadyPresent === true) {\n return\n }\n const scriptContent = await loadInjectableScript()\n // Inlined as a literal rather than passed via `arguments[0]`: the raw\n // /execute/sync transport takes its args separately and this keeps the\n // injection a single self-contained body.\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = ${JSON.stringify(scriptContent)};\n document.head.appendChild(script);\n return true;\n `\n await webdriverExecute(browser, injectionScript)\n\n const hasCollector = await pollUntilReady(\n async () =>\n (await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )) === true\n )\n\n if (hasCollector) {\n log.info('✓ Script injected and collector ready')\n } else {\n log.warn('Script injection may have failed — collector not found')\n }\n } catch (err) {\n log.error(`Failed to inject script: ${errorMessage(err)}`)\n throw err\n }\n }\n\n /**\n * Capture Chrome DevTools browser console logs via WebDriver log API.\n * Requires loggingPrefs: { browser: 'ALL' } in Chrome capabilities.\n */\n async captureBrowserLogs(browser: NightwatchBrowser) {\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('browser')\n const logs =\n unwrapDriverValue<\n Array<{ level: string; message: string; timestamp: number }>\n >(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const entries = mapChromeBrowserLogs(logs)\n this.consoleLogs.push(...entries)\n this.sendUpstream('consoleLogs', entries)\n } catch {\n // Browser log capture not available (loggingPrefs not set or not supported)\n }\n }\n\n /**\n * Parse Chrome performance logs to extract network request entries.\n * Requires loggingPrefs: { performance: 'ALL' } in Chrome capabilities.\n */\n async captureNetworkFromPerformanceLogs(browser: NightwatchBrowser) {\n // BiDi network inspector is the source of truth when attached.\n if (this.bidiActive) {\n return\n }\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('performance')\n const logs = unwrapDriverValue<PerfLogEntry[]>(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const networkEntries = parseNetworkFromPerfLogs(logs)\n if (networkEntries.length === 0) {\n return\n }\n\n const deduped = dedupeNetworkRequests(\n networkEntries,\n this.networkRequests as NetworkEntry[]\n )\n if (deduped.length > 0) {\n // A perf-log entry that never saw its response event carries no type;\n // default it to the vocabulary's residual at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'other'\n }))\n this.networkRequests.push(...normalized)\n this.sendUpstream('networkRequests', normalized)\n }\n } catch (err) {\n const msg = errorMessage(err) ?? ''\n // Silently skip when performance logging was not enabled in capabilities\n if (!msg.includes('log type') && !msg.includes('performance')) {\n log.warn(`Performance log capture failed: ${msg}`)\n }\n }\n }\n\n /** `forceAnchor` forces a full-DOM anchor of the current document before\n * draining, so a freshly injected collector's async anchor isn't lost.\n * `anchorTimestamp` is the command this drain's anchors were CAUSED by — pass\n * it only when the caller observed the document being replaced right after\n * that command, since it overrides the inference at ingest. */\n async captureTrace(\n browser: NightwatchBrowser,\n forceAnchor = false,\n anchorTimestamp?: number\n ) {\n // A native app has no document to drain. Inside the method, because four\n // call sites reach it — the command hook, the navigation proxy, the\n // cucumber pre-quit hook and finalize.\n if (this.isNativeAppSession) {\n return\n }\n // Performance logs only accumulate, so they lose nothing by waiting, while\n // the drain is racing the page it reads from (`drainOutgoingPage`).\n await this.drainCollector(browser, forceAnchor, anchorTimestamp)\n await this.captureNetworkFromPerformanceLogs(browser)\n }\n\n private async drainCollector(\n browser: NightwatchBrowser,\n forceAnchor: boolean,\n anchorTimestamp?: number\n ) {\n // Try the injected wdioTraceCollector script for XHR/fetch and mutations.\n // Atomic check+read — the inline `typeof === 'undefined' → null` guard is\n // the only safe form; a separate existence check would race page navigation\n // (the collector can disappear between the two round-trips).\n try {\n const traceData = await drainCollectorWithRecovery({\n drain: () =>\n webdriverExecute<Record<string, unknown> | null>(\n browser,\n collectorDrainExpression(forceAnchor)\n ),\n injectIntoCurrentDocument: () => this.injectScript(browser),\n currentUrl: () =>\n webdriverExecute<string>(browser, 'return location.href;').then(\n (u) => u ?? undefined\n ),\n log: (level, message) => log[level](message)\n })\n\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData, { anchorTimestamp })\n const mutationCount = Array.isArray(\n (traceData as { mutations?: unknown }).mutations\n )\n ? (traceData as { mutations: unknown[] }).mutations.length\n : 0\n if (mutationCount > 0) {\n log.info(`[trace] Captured ${mutationCount} DOM mutation(s)`)\n }\n } catch (err) {\n log.error(\n `Failed to capture trace from injected script: ${errorMessage(err)}`\n )\n }\n }\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n// Every probe goes over the raw WebDriver HTTP transport rather than `browser.*`:\n// those are QUEUED commands, so a probe issued from inside the plugin's own\n// command hook enqueues behind the command still running and can't resolve until\n// the queue drains. `browser.getCurrentUrl()`/`getTitle()` that way left every\n// snapshot with `url: undefined` (so `frameUrl` fell back to `about:blank`) and\n// hung the capture outright; the queued `execute` merely timed out, leaving an\n// empty a11y tree and no element rects.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type { NightwatchBrowser, TestRunnerId } from './types.js'\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n timestamp?: number,\n runner?: TestRunnerId,\n native = false\n): Promise<ActionSnapshot | null> {\n // The screenshot is the only one of these a native app can serve. The rest\n // are page reads — an injected script plus url and title — and they fire on\n // EVERY action, so they are the densest source of round trips that can only\n // fail on a session with no document.\n return coreCapture({\n command,\n timestamp,\n runner,\n takeScreenshot: () => webdriverGet<string>(browser, 'screenshot'),\n ...(native\n ? {}\n : {\n runScript: (src: string) =>\n webdriverExecute(browser, `return (${src})`),\n getUrl: () => webdriverGet<string>(browser, 'url'),\n getTitle: () => webdriverGet<string>(browser, 'title')\n })\n })\n}\n","import type { RequestType } from '@wdio/devtools-shared'\n\nimport { getRequestType } from './utils.js'\n\n/**\n * Pure parsers for Chrome's `performance` log (the format `browser.getLog('performance')`\n * returns). Separated from the SessionCapturer so they're testable and the\n * capture method stays focused on state + I/O.\n */\n\nexport interface PerfLogEntry {\n level: string\n message: string\n timestamp: number\n}\n\nexport interface NetworkEntry {\n id: string\n url: string\n method: string\n requestHeaders: Record<string, string>\n timestamp: number\n startTime: number\n status?: number\n statusText?: string\n responseHeaders?: Record<string, string>\n mimeType?: string\n /** Set only by `getRequestType`, so it carries the shared vocabulary; absent\n * until the response event arrives. */\n type?: RequestType\n size?: number\n endTime?: number\n time?: number\n error?: string\n}\n\n/**\n * Parse CDP `Network.*` events out of Chrome performance log entries into a\n * flat array of network entries. Builds up a per-requestId pending map as it\n * sees `requestWillBeSent` → `responseReceived` → `loadingFinished` events,\n * and emits the completed entry on the terminal event.\n */\ninterface CdpResponse {\n status?: number\n statusText?: string\n headers?: Record<string, unknown>\n mimeType?: string\n}\n\ninterface CdpRequest {\n url: string\n method: string\n headers?: Record<string, string>\n}\n\ninterface CdpEventParams {\n requestId: string\n request?: CdpRequest\n response?: CdpResponse\n timestamp?: number\n encodedDataLength?: number\n errorText?: string\n}\n\nfunction applyResponseReceived(p: NetworkEntry, response: CdpResponse): void {\n const responseHeaders: Record<string, string> = {}\n for (const [k, v] of Object.entries(response.headers || {})) {\n responseHeaders[k.toLowerCase()] = String(v)\n }\n p.status = response.status\n p.statusText = response.statusText\n p.responseHeaders = responseHeaders\n p.mimeType = response.mimeType\n p.type = getRequestType(p.url, response.mimeType)\n}\n\n/** CDP event time in ms. Uses the event's own monotonic `params.timestamp`\n * (what Chrome DevTools times requests with) rather than the perf-log wrapper\n * timestamp, which WebDriver stamps per batch — so request/response diffs in\n * the same batch would otherwise be 0. */\nfunction eventTimeMs(params: CdpEventParams, entry: PerfLogEntry): number {\n return typeof params.timestamp === 'number'\n ? Math.round(params.timestamp * 1000)\n : entry.timestamp\n}\n\nfunction startPendingRequest(\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>\n): void {\n const { requestId, request: req, timestamp } = params\n if (!req || typeof timestamp !== 'number') {\n return\n }\n const startTime = eventTimeMs(params, entry)\n pending.set(requestId, {\n id: `${entry.timestamp}-${requestId}`,\n url: req.url,\n method: req.method,\n requestHeaders: req.headers ?? {},\n timestamp: startTime,\n startTime\n })\n}\n\nfunction handlePerfLogEvent(\n method: string,\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>,\n completed: NetworkEntry[]\n): void {\n if (method === 'Network.requestWillBeSent') {\n startPendingRequest(params, entry, pending)\n return\n }\n if (method === 'Network.responseReceived') {\n const p = pending.get(params.requestId)\n if (p && params.response) {\n applyResponseReceived(p, params.response)\n }\n return\n }\n if (method === 'Network.loadingFinished') {\n const p = pending.get(params.requestId)\n if (p && p.status !== undefined) {\n p.size = params.encodedDataLength\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n return\n }\n if (method === 'Network.loadingFailed') {\n const p = pending.get(params.requestId)\n if (p) {\n p.error = params.errorText\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n }\n}\n\nexport function parseNetworkFromPerfLogs(logs: PerfLogEntry[]): NetworkEntry[] {\n const pending = new Map<string, NetworkEntry>()\n const completed: NetworkEntry[] = []\n for (const entry of logs) {\n let parsed: unknown\n try {\n parsed = JSON.parse(entry.message)\n } catch {\n continue\n }\n type ParsedShape = {\n message?: { method?: string; params?: CdpEventParams }\n }\n const message = (parsed as ParsedShape | undefined)?.message\n const method = message?.method\n const params = message?.params\n if (!method || !params) {\n continue\n }\n handlePerfLogEvent(method, params, entry, pending, completed)\n }\n return completed\n}\n\n/**\n * Dedupe incoming network entries against ones the session already holds.\n * Successful requests dedupe by (method, url, timestamp). Failed requests\n * collapse by (method, origin, pathname) — parallel autocomplete/prefetch\n * requests to the same path (e.g. `/search?q=W`, `/search?q=We`) otherwise\n * spam the network panel.\n */\nexport function dedupeNetworkRequests(\n incoming: NetworkEntry[],\n existing: NetworkEntry[]\n): NetworkEntry[] {\n const failedKey = (entry: NetworkEntry): string => {\n try {\n const u = new URL(entry.url)\n return `err:${entry.method}:${u.origin}${u.pathname}`\n } catch {\n return `err:${entry.method}:${entry.url}`\n }\n }\n\n const alreadySeen = new Set(\n existing.map((r) =>\n r.error !== undefined\n ? failedKey(r)\n : `ok:${r.method}:${r.url}:${r.timestamp}`\n )\n )\n\n const deduped: NetworkEntry[] = []\n const seenFailedInBatch = new Map<string, number>()\n\n for (const entry of incoming) {\n if (entry.error !== undefined) {\n const key = failedKey(entry)\n if (alreadySeen.has(key)) {\n continue\n }\n const existingIdx = seenFailedInBatch.get(key)\n if (existingIdx !== undefined) {\n deduped[existingIdx] = entry // replace with latest failure\n } else {\n seenFailedInBatch.set(key, deduped.length)\n deduped.push(entry)\n }\n } else {\n const key = `ok:${entry.method}:${entry.url}:${entry.timestamp}`\n if (!alreadySeen.has(key)) {\n deduped.push(entry)\n }\n }\n }\n\n return deduped\n}\n","import logger from '@wdio/logger'\nimport { TestReporterBase, type ReporterUpstream } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { DEFAULTS } from './constants.js'\nimport { extractTestMetadata, generateStableUid } from './helpers/utils.js'\nimport type { SuiteStats, TestStats } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:Reporter')\n\nexport class TestReporter extends TestReporterBase {\n #currentSpecFile?: string\n #testNamesCache = new Map<string, string[]>()\n #currentSuite?: SuiteStats\n #attemptFor: (uid: string) => number | undefined\n\n constructor(\n report: ReporterUpstream,\n attemptFor: (uid: string) => number | undefined\n ) {\n super(report)\n this.#attemptFor = attemptFor\n }\n\n onSuiteStart(suiteStats: SuiteStats) {\n this.#currentSpecFile = suiteStats.file\n this.#currentSuite = suiteStats\n const rerunLabel =\n process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n\n if (!suiteStats.uid) {\n suiteStats.uid = generateStableUid(suiteStats)\n }\n\n if (\n this.#currentSpecFile &&\n !this.#testNamesCache.has(this.#currentSpecFile)\n ) {\n const metadata = extractTestMetadata(this.#currentSpecFile)\n const testNames = rerunLabel\n ? metadata.testNames.filter((name) => name === rerunLabel)\n : metadata.testNames\n if (testNames.length > 0) {\n this.#testNamesCache.set(this.#currentSpecFile, testNames)\n log.info(\n `📝 Extracted ${testNames.length} test names from ${this.#currentSpecFile}`\n )\n }\n }\n\n this.allSuites.push(suiteStats)\n this.sendUpstream()\n }\n\n override clearExecutionData() {\n super.clearExecutionData()\n this.#testNamesCache.clear()\n this.#currentSuite = undefined\n this.#currentSpecFile = undefined\n }\n\n getCurrentSuite(): SuiteStats | undefined {\n return this.#currentSuite\n }\n\n /** Find by title within parent suite — Nightwatch retries reuse the title slot. */\n onTestStart(testStats: TestStats) {\n if (!testStats.uid || testStats.uid.includes('temp-')) {\n testStats.uid = generateStableUid(testStats)\n }\n\n for (const suite of this.allSuites) {\n const testIndex = suite.tests.findIndex((t) => {\n if (typeof t === 'string') {\n return false\n }\n return t.title === testStats.title && t.parent === suite.uid\n })\n if (testIndex !== -1) {\n suite.tests[testIndex] = testStats\n this.sendUpstream()\n return\n }\n }\n\n if (this.#currentSuite) {\n this.#currentSuite.tests.push(testStats)\n }\n this.sendUpstream()\n }\n\n /** Synthesize `skipped` entries for tests that never executed. */\n override onSuiteEnd(suiteStats: SuiteStats) {\n const cachedNames = this.#testNamesCache.get(suiteStats.file) || []\n const processedTestNames = new Set(\n suiteStats.tests\n .map((t) => (typeof t === 'string' ? t : t.title))\n .filter((title): title is string => Boolean(title))\n )\n\n cachedNames.forEach((testName) => {\n if (!processedTestNames.has(testName)) {\n const uid = generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats)\n const skippedTest: TestStats = {\n uid,\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle: `${suiteStats.title} ${testName}`,\n parent: suiteStats.uid,\n state: 'skipped',\n start: new Date(),\n end: new Date(),\n type: 'test',\n file: suiteStats.file,\n retries: this.#attemptFor(uid) ?? DEFAULTS.RETRIES,\n _duration: DEFAULTS.DURATION,\n hooks: []\n }\n suiteStats.tests.push(skippedTest)\n log.info(`Created skipped test \"${testName}\" (never executed)`)\n }\n })\n\n this.sendUpstream()\n }\n\n /** Replace a suite when its UID changes mid-run (after spec rescan). */\n updateSuite(suiteStats: SuiteStats) {\n const index = this.allSuites.findIndex((s) => s.file === suiteStats.file)\n if (index !== -1) {\n this.allSuites.splice(index, 1)\n }\n this.allSuites.push(suiteStats)\n this.#currentSuite = suiteStats\n this.sendUpstream()\n }\n}\n","/**\n * Which `it` the runner is executing RIGHT NOW.\n *\n * The plugin's `beforeEach` fires once per MODULE on the BDD `describe/it`\n * interface, so the lifecycle-level current test is the module's first `it` for\n * every row in the file. Nightwatch's own `setReporterCurrentTest()` — \"called\n * for every test case (not for hooks)\" — runs per `it` and installs\n * `browser.currentTest` as a live getter over `reporter.currentTest`, so the\n * running testcase's NAME is readable as state on every command. That name is\n * the per-`it` signal no hook or event exposes; mapping it back onto the\n * `TestStats` the suite already registered gives each `it` its own uid, and with\n * it its own group in the trace's action tree.\n */\n\nimport type { NightwatchCurrentTest, SuiteStats, TestStats } from '../types.js'\n\n/**\n * The registered test title a runtime testcase name refers to. Nightwatch\n * reports either the bare `it` title or one prefixed with its parents, so a\n * whole-title suffix match is accepted; anything else is no match — this never\n * guesses at \"the next unprocessed test\" the way the lifecycle picker does.\n */\nexport function matchTestName(\n runtimeName: string | undefined,\n testNames: readonly string[]\n): string | undefined {\n const name = typeof runtimeName === 'string' ? runtimeName.trim() : undefined\n if (!name) {\n return undefined\n }\n return testNames.find((title) => name === title || name.endsWith(` ${title}`))\n}\n\n/**\n * The suite's registered `TestStats` for the testcase Nightwatch reports as\n * running. Undefined when nothing matches: a command issued from a hook (where\n * Nightwatch leaves the previous testcase in place), a spec whose source\n * couldn't be parsed into test names, or the cucumber runner — whose client\n * carries no `currentTest` at all, which is what keeps its per-scenario\n * structure on the lifecycle path untouched.\n */\nexport function findRunningTest(\n suite: SuiteStats | undefined,\n currentTest: NightwatchCurrentTest | undefined\n): TestStats | undefined {\n const tests = (suite?.tests ?? []).filter(\n (t): t is TestStats => typeof t !== 'string'\n )\n const title = matchTestName(\n currentTest?.name,\n tests.map((t) => t.title)\n )\n return title ? tests.find((t) => t.title === title) : undefined\n}\n","import { stampTestEnd } from '@wdio/devtools-core'\nimport { TEST_STATE, DEFAULTS } from '../constants.js'\nimport {\n type TestStats,\n type SuiteStats,\n type NightwatchCurrentTest,\n type NightwatchTestCase\n} from '../types.js'\nimport { determineTestState } from './utils.js'\nimport { findRunningTest } from './runningTest.js'\nimport type { TestReporter } from '../reporter.js'\n\nexport class TestManager {\n private processedTests = new Map<string, Set<string>>()\n private lastKnownTestName: string | null = null\n\n constructor(\n private testReporter: TestReporter,\n /** Stamps a test's resolved terminal state onto the retry ledger. */\n private recordOutcome?: (uid: string, state: TestStats['state']) => void\n ) {}\n\n /**\n * Update test state and report to UI\n */\n updateTestState(\n test: TestStats,\n state: TestStats['state'],\n endTime?: Date,\n duration?: number\n ): void {\n test.state = state\n if (endTime) {\n test.end = endTime\n }\n if (duration !== undefined) {\n test._duration = duration\n }\n\n if (state === TEST_STATE.PASSED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.FAILED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.RUNNING) {\n this.testReporter.onTestStart(test)\n }\n\n if (state !== TEST_STATE.RUNNING) {\n this.testReporter.onTestEnd(test)\n this.recordOutcome?.(test.uid, state)\n }\n }\n\n /**\n * Find test in suite by title\n */\n findTestInSuite(suite: SuiteStats, testTitle: string): TestStats | undefined {\n return suite.tests.find(\n (t): t is TestStats => typeof t !== 'string' && t.title === testTitle\n )\n }\n\n /**\n * Mark test as processed to avoid duplicate reporting\n */\n markTestAsProcessed(testFile: string, testTitle: string): void {\n if (!this.processedTests.has(testFile)) {\n this.processedTests.set(testFile, new Set())\n }\n this.processedTests.get(testFile)!.add(testTitle)\n }\n\n /**\n * Check if test has been processed\n */\n isTestProcessed(testFile: string, testTitle: string): boolean {\n return this.processedTests.get(testFile)?.has(testTitle) ?? false\n }\n\n /**\n * Get processed tests for a file\n */\n getProcessedTests(testFile: string): Set<string> {\n return this.processedTests.get(testFile) || new Set()\n }\n\n /**\n * Detect test boundary and finalize previous test if needed\n * Returns the current test name\n */\n detectTestBoundary(currentNightwatchTest: NightwatchCurrentTest): string {\n const currentTestName = currentNightwatchTest?.name || DEFAULTS.TEST_NAME\n\n // If test name changed, finalize previous test\n if (\n this.lastKnownTestName &&\n currentTestName !== this.lastKnownTestName &&\n currentTestName !== DEFAULTS.TEST_NAME\n ) {\n const testFile =\n currentNightwatchTest.module?.split('/').pop() || DEFAULTS.FILE_NAME\n const currentSuite = this.testReporter.getCurrentSuite()\n\n if (currentSuite) {\n const prevTest = this.findTestInSuite(\n currentSuite,\n this.lastKnownTestName\n )\n if (prevTest && prevTest.state === TEST_STATE.RUNNING) {\n const prevTestCase =\n currentNightwatchTest.results?.testcases?.[this.lastKnownTestName]\n\n if (prevTestCase) {\n const testState = determineTestState(prevTestCase)\n this.updateTestState(\n prevTest,\n testState,\n new Date(),\n parseFloat(prevTestCase.time || '0') * 1000\n )\n this.markTestAsProcessed(testFile, this.lastKnownTestName)\n }\n }\n }\n }\n\n // Update last known test name\n if (currentTestName !== DEFAULTS.TEST_NAME) {\n this.lastKnownTestName = currentTestName\n }\n\n return currentTestName\n }\n\n /**\n * The registered test for the testcase Nightwatch reports as running, read\n * from live `browser.currentTest` state rather than from a hook — the only\n * per-`it` signal the BDD interface gives a plugin (see ./runningTest.ts).\n * Synchronous by construction: the command path resolves a row's test on the\n * invocation stack, where an await would reorder capture.\n */\n runningTest(\n currentTest: NightwatchCurrentTest | undefined\n ): TestStats | undefined {\n return findRunningTest(this.testReporter.getCurrentSuite(), currentTest)\n }\n\n /**\n * Start a pending test if this is its first command\n */\n startTestIfPending(currentTestName: string): void {\n if (\n currentTestName === DEFAULTS.TEST_NAME ||\n this.lastKnownTestName !== currentTestName\n ) {\n return\n }\n\n const currentSuite = this.testReporter.getCurrentSuite()\n if (!currentSuite) {\n return\n }\n\n const test = this.findTestInSuite(currentSuite, currentTestName)\n if (test && test.state === TEST_STATE.PENDING) {\n test.start = new Date()\n test.end = null\n this.updateTestState(test, TEST_STATE.RUNNING as TestStats['state'])\n }\n }\n\n /**\n * Finalize all incomplete tests in a suite\n */\n finalizeSuiteTests(\n suite: SuiteStats,\n testcases: Record<string, NightwatchTestCase>\n ): void {\n suite.tests.forEach((entry) => {\n if (typeof entry === 'string') {\n return\n }\n const test = entry\n if (test.state === TEST_STATE.RUNNING && test.start) {\n // Test was started but never finished - assume passed\n test.state = TEST_STATE.PASSED\n stampTestEnd(test)\n this.updateTestState(test, TEST_STATE.PASSED as TestStats['state'])\n } else if (test.state === TEST_STATE.PENDING) {\n const testcase = testcases[test.title]\n if (testcase) {\n const testState = determineTestState(testcase)\n test.start = test.start || new Date()\n this.updateTestState(\n test,\n testState,\n new Date(),\n parseFloat(testcase.time || '0') * 1000\n )\n } else {\n // Test never ran - mark as skipped\n this.updateTestState(\n test,\n TEST_STATE.SKIPPED as TestStats['state'],\n new Date(),\n 0\n )\n }\n }\n })\n }\n\n /**\n * Reset internal state for new test files\n */\n reset(): void {\n this.lastKnownTestName = null\n }\n}\n","/**\n * Suite Manager — Nightwatch flavor.\n * Maintains one suite per test file (Nightwatch runs each file independently).\n * Shares the suite factory + state-computation logic with selenium via\n * @wdio/devtools-core's suite-helpers; the storage strategy (Map by file)\n * is Nightwatch-specific and stays here.\n */\n\nimport {\n computeSuiteFinalStateStrict,\n computeSuiteRunningState,\n createSuiteStats,\n createTestStats,\n stampSuiteEnd\n} from '@wdio/devtools-core'\nimport { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\nimport type { TestReporter } from '../reporter.js'\nimport { generateStableUid } from './utils.js'\n\nexport class SuiteManager {\n private currentSuiteByFile = new Map<string, SuiteStats>()\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Clear execution data when a rerun starts.\n * Resets all cached suites so they're recreated fresh during the new run.\n */\n clearExecutionData() {\n this.currentSuiteByFile.clear()\n }\n\n /**\n * Get or create suite for a test file\n */\n getOrCreateSuite(\n testFile: string,\n suiteTitle: string,\n fullPath: string | null,\n testNames: string[],\n suiteLine?: number | null,\n testLines?: number[]\n ): SuiteStats {\n if (!this.currentSuiteByFile.has(testFile)) {\n const file = fullPath || testFile\n const suiteStats = createSuiteStats({\n uid: generateStableUid(file, suiteTitle),\n cid: DEFAULTS.CID,\n title: suiteTitle,\n file,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n suiteLine && fullPath ? `${fullPath}:${suiteLine}` : undefined\n })\n\n // Create test entries with pending state\n for (let idx = 0; idx < testNames.length; idx++) {\n const testName = testNames[idx]\n const testLine = testLines?.[idx]\n const fullTitle = `${suiteTitle} ${testName}`\n const testEntry = createTestStats({\n uid: generateStableUid(file, fullTitle),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle,\n file,\n parent: suiteStats.uid,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n testLine && fullPath ? `${fullPath}:${testLine}` : undefined\n })\n suiteStats.tests.push(testEntry)\n }\n\n this.currentSuiteByFile.set(testFile, suiteStats)\n this.testReporter.onSuiteStart(suiteStats)\n }\n\n return this.currentSuiteByFile.get(testFile)!\n }\n\n /**\n * Get suite for a test file\n */\n getSuite(testFile: string): SuiteStats | undefined {\n return this.currentSuiteByFile.get(testFile)\n }\n\n /**\n * Mark suite as running.\n * Clears `end` so that the `finalizeSuite` guard (`if (suite.end) return`)\n * does not skip re-finalization during a rerun.\n */\n markSuiteAsRunning(suite: SuiteStats): void {\n suite.state = TEST_STATE.RUNNING\n suite.end = null\n this.testReporter.updateSuites()\n }\n\n /**\n * Update suite state from current children without marking it as ended.\n * Used during Cucumber runs to keep the feature-level suite state fresh.\n */\n finalizeSuiteState(suite: SuiteStats): void {\n suite.state = computeSuiteRunningState(suite)\n this.testReporter.updateSuites()\n }\n\n /**\n * Finalize suite with test results\n */\n finalizeSuite(suite: SuiteStats): void {\n if (suite.end) {\n return\n }\n stampSuiteEnd(suite)\n suite.state = computeSuiteFinalStateStrict(suite)\n this.testReporter.onSuiteEnd(suite)\n }\n\n /**\n * Get all suites\n */\n getAllSuites(): Map<string, SuiteStats> {\n return this.currentSuiteByFile\n }\n}\n","/**\n * Browser Proxy\n * Handles browser command interception and tracking\n */\n\nimport logger from '@wdio/logger'\nimport {\n INTERNAL_COMMANDS_TO_IGNORE,\n NAVIGATION_COMMANDS\n} from '../constants.js'\nimport { callbackError } from './callbackError.js'\nimport { commandRowSelector, commandTargetSelector } from './assertTarget.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { NativeAssertRecorder } from './nativeAssertRecorder.js'\nimport {\n RetryTracker,\n beginInputDispatch,\n rememberReadValue,\n toError\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandCaptureMeta,\n CommandInvocation,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchChainable,\n NightwatchCurrentTest,\n CommandStackFrame\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:browserProxy')\n\nexport class BrowserProxy {\n /** Tracks which browser *instances* have already been proxied to avoid double-wrapping. */\n private proxiedBrowsers = new WeakSet<object>()\n private commandStack: CommandStackFrame[] = []\n private lastCommandSig: string | null = null\n private currentTestFullPath: string | null = null\n /**\n * Tracks the last captured command so that consecutive retries of the same\n * command (e.g. getText inside a waitFor loop) overwrite the previous entry\n * rather than appending, showing only the final execution result.\n */\n private retryTracker = new RetryTracker()\n\n /**\n * Monotonic issue-order counter. Stamped when the test ISSUES a row — a\n * driver command at invocation, an assert at enqueue — which is source order.\n * Both are recorded into `commandsLog` much later and out of that order (a\n * driver command at completion, an assert in the test-end batch), so this is\n * what lets the exporter recover the order the test wrote. Shared with the\n * assert recorder so both row kinds draw from one sequence.\n */\n private issueCounter = 0\n\n private assertRecorder: NativeAssertRecorder\n\n /**\n * The browser every captured row is issued on. Nightwatch mutates ONE browser\n * object in place — even across a mid-run session replacement — so this stays\n * valid for the whole run, and it is how `browser.currentTest` is read from\n * the assert path, which runs outside any command callback.\n */\n private browser: NightwatchBrowser | null = null\n\n constructor(\n private readonly sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null,\n private captureAssertions = true,\n private observeSession?: (browser: NightwatchBrowser) => void\n ) {\n this.assertRecorder = new NativeAssertRecorder(\n sessionCapturer,\n () => this.rowTest(),\n () => this.issueCounter++\n )\n }\n\n /**\n * The test a captured row belongs to: the `it` Nightwatch reports as running\n * right now, falling back to the lifecycle test. On the BDD `describe/it`\n * interface the lifecycle test is the module's FIRST `it` for every row in the\n * file (its `beforeEach` fires once per module), so without the live read\n * every command in the module carried one uid and the trace's action tree\n * collapsed to a single test group. The fallback is what the runners with no\n * per-testcase state keep using — cucumber has no `currentTest` at all.\n */\n private rowTest(): { uid?: string } | null {\n return (\n this.testManager.runningTest(this.browser?.currentTest) ??\n this.getCurrentTest()\n )\n }\n\n /** The uid rows are stamped with — see {@link rowTest}. Public so the\n * node:assert capture wiring, which has no command of its own, tags its rows\n * onto the same test the surrounding commands landed on. */\n rowTestUid(): string | undefined {\n return this.rowTest()?.uid\n }\n\n /** Per-command-scope dedup state only. Cucumber calls this per STEP, so it\n * must not touch the native-assert buffer — that buffer spans the whole\n * scenario and is drained once, at the scenario's pre-quit hook. */\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n /** Everything scoped to one test unit: dedup state plus any native-assert\n * calls a previous unit left behind because its drain never ran. */\n resetTestTracking(): void {\n this.resetCommandTracking()\n this.assertRecorder.clear()\n }\n\n /** Hand off this test's recorded native-assertion calls and clear the\n * buffer so the next test starts fresh. */\n drainNativeAssertCalls(): NativeAssertCall[] {\n return this.assertRecorder.drain()\n }\n\n getCurrentTestFullPath(): string | null {\n return this.currentTestFullPath\n }\n\n setCurrentTestFullPath(path: string | null): void {\n this.currentTestFullPath = path\n }\n\n /**\n * Wrap browser navigation methods (url / navigate / navigateTo) to inject\n * the DevTools script after every navigation.\n *\n * Uses `browser` from the closure (not `this` inside perform) so it works\n * for both standard Nightwatch (chainable API) and Cucumber async/await mode\n * where `this` inside a perform callback is not the browser.\n */\n wrapUrlMethod(browser: NightwatchBrowser): void {\n const sessionCapturer = this.sessionCapturer\n\n // Cast once for dynamic method access — Nightwatch's typed surface\n // doesn't enumerate every command, but they all live on the same object.\n // Return type is `unknown` because wrapNav has to handle both\n // Nightwatch's chainable API (returns a chainable with `.perform`) and\n // Cucumber async/await (returns a Promise) — we narrow at each branch.\n const b = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n\n const wrapNav = (methodName: string) => {\n if (typeof b[methodName] !== 'function') {\n return\n }\n const original = b[methodName].bind(browser)\n\n b[methodName] = function (...args: unknown[]) {\n // `browser.url()` is BOTH a navigation and a getter: with no url it just\n // reads the current one, which is how `assert.urlContains` reads it from\n // inside Nightwatch's own queue. Treating that read as a navigation\n // re-injected the collector and forced an anchored drain mid-test — real\n // round trips against the page for a command that changed nothing — and\n // logged the capture callback's whole function body as the \"url\".\n if (typeof args[0] !== 'string') {\n return original(...args)\n }\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return (\n sessionCapturer\n .injectScript(browser)\n // Anchored: the just-injected collector anchors the document\n // asynchronously, so an unanchored drain beats it and this page's\n // DOM never reaches the trace.\n .then(() => sessionCapturer.captureTrace(browser, true))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n )\n }\n\n const chainable = result as NightwatchChainable | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API). Hung off the command's OWN\n // thenable, not a queued `perform()`: a queued callback is discarded\n // when a failing test aborts the rest of the queue, so the destination\n // of a navigation in a failing test was never instrumented at all\n // (measured: 2 of 3 navigations skipped, and with them the first\n // document's entire DOM). The chainable is itself thenable, and\n // resolving it means the navigation finished — the same signal\n // `perform` gave, minus the dependency on the queue surviving.\n //\n // `result` is returned unchanged so `browser.url(…).waitFor…()`\n // chaining still works; the injection runs as a side effect.\n void Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n return result\n }\n // Cucumber async/await: result is a Promise (or thenable).\n // Return the AUGMENTED promise so that `await browser.url(...)` in\n // the step definition waits for injectAndCapture to finish before\n // Cucumber moves to the next step. Without this the injection races\n // with the next step's commands (e.g. setValue), causing stale-element\n // errors because the script-tag insertion mutates the DOM mid-form.\n return Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n }\n }\n\n wrapNav('url')\n wrapNav('navigate')\n wrapNav('navigateTo')\n\n log.info('✓ Script injection wrapped')\n }\n\n wrapBrowserCommands(browser: NightwatchBrowser): void {\n // Held before the already-wrapped guard: the reference is what `rowTest`\n // reads `currentTest` off, and a re-entry is exactly a run that never\n // re-wraps.\n this.browser = browser\n if (this.proxiedBrowsers.has(browser as object)) {\n return\n }\n\n // Single widening: Nightwatch's `browser` is a dynamic command bag —\n // every wrapped lookup below is property-name → function. Casting once\n // keeps the wrap loop readable.\n const browserAny = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const allMethods = new Set([\n ...Object.keys(browser),\n ...Object.getOwnPropertyNames(Object.getPrototypeOf(browser))\n ])\n const wrappedMethods: string[] = []\n\n allMethods.forEach((methodName) => {\n if (\n methodName === 'constructor' ||\n typeof browserAny[methodName] !== 'function'\n ) {\n return\n }\n\n if (\n (INTERNAL_COMMANDS_TO_IGNORE as readonly string[]).includes(\n methodName\n ) ||\n methodName.startsWith('__')\n ) {\n return\n }\n\n const originalMethod = browserAny[methodName].bind(browser)\n\n browserAny[methodName] = (...args: unknown[]) => {\n return this.handleCommandExecution(\n browser,\n browserAny,\n methodName,\n originalMethod,\n args\n )\n }\n\n wrappedMethods.push(methodName)\n })\n\n // captureAssertions:false → leave assert/verify original so no native\n // assertion rows are recorded (the finalize paths are gated to match).\n if (this.captureAssertions) {\n this.assertRecorder.wrapNamespaces(browser)\n }\n this.proxiedBrowsers.add(browser as object)\n log.info(`✓ Wrapped ${wrappedMethods.length} browser methods`)\n }\n\n private handleRetryReplacement(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n meta: CommandCaptureMeta,\n commandError: Error | undefined\n ): void {\n // Same command fired again (internal retry) — replace the previous\n // entry so only the final result appears in the UI.\n const { entry, oldTimestamp } = this.sessionCapturer.replaceCommand(\n this.retryTracker.lastId!,\n methodName,\n logArgs,\n serializedResult,\n commandError,\n meta\n )\n this.retryTracker.setLastId(entry._id ?? null)\n this.sessionCapturer.sendReplaceCommand(oldTimestamp, entry)\n this.attachScreenshot(browser, entry, methodName, ' (retry)')\n }\n\n private captureFreshCommand(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n meta: CommandCaptureMeta,\n cmdSig: string,\n commandError: Error | undefined\n ): void {\n // captureCommand() pushes the entry to commandsLog synchronously before\n // any async work (navigation perf capture), so we can grab the ID\n // immediately after the call — before any microtask fires. This avoids\n // the race where a Nightwatch retry callback executes before .then() sets\n // lastId, causing missed dedup. Stage the sig now, set the id after the\n // synchronous push lands.\n this.retryTracker.setLastSig(cmdSig)\n this.retryTracker.setLastId(null)\n // Issued first, and only this one — see `drainOutgoingPage`.\n this.drainOutgoingPage(browser, methodName)\n this.sessionCapturer\n .captureCommand(methodName, logArgs, serializedResult, commandError, meta)\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.retryTracker.setLastId((lastCommand as { _id?: number })._id ?? null)\n this.sessionCapturer.sendCommand(lastCommand)\n log.info(`[command] ${methodName}`)\n this.attachScreenshot(browser, lastCommand, methodName)\n }\n this.anchorDestination(browser, methodName)\n }\n\n private isDomMutating(methodName: string): boolean {\n return (\n (NAVIGATION_COMMANDS as readonly string[]).includes(methodName) ||\n [\n 'click',\n 'doubleClick',\n 'rightClick',\n 'setValue',\n 'clearValue',\n 'sendKeys',\n 'submitForm',\n 'back',\n 'forward',\n 'refresh'\n ].includes(methodName)\n )\n }\n\n /**\n * Drains the OUTGOING page's field edits, which die with the document the\n * moment a submit navigates. It goes over the raw HTTP transport, so it\n * touches no Nightwatch queue, and a driver serialises requests per session —\n * so it is served before the next queued command only for as long as it stays\n * the FIRST request this class issues when a command completes. That call\n * position, and the drain-before-perf-logs order inside `captureTrace`, are\n * both load-bearing: behind the snapshot probes and the screenshot it needed\n * about 130 ms of head start and got 18 ms, and the last field a test filled\n * before submitting replayed as an empty box.\n *\n * It deliberately carries only this one request. `anchorAfterNavigation` is\n * issued later, from `anchorDestination`, so the row's own screenshot and DOM\n * snapshot are not pushed behind two round trips.\n */\n private drainOutgoingPage(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n this.sessionCapturer.captureTrace(browser, true).catch(() => {})\n }\n\n /**\n * Anchors the DESTINATION, and it has to wait: a Nightwatch click resolves\n * before its navigation commits, so the outgoing drain still finds the old\n * page's collector, recovers nothing, and the new document is never anchored —\n * every action on it then replayed the page it came from (measured: 5 of 20\n * rows). `anchorAfterNavigation` keys off the document actually being replaced\n * rather than off which command ran, so it covers any route to a new document —\n * a submit click, a JS redirect, a same-command re-navigation — without a list\n * of commands to keep in sync.\n */\n private anchorDestination(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n // Tracked so finalize awaits it — an untracked anchor drain can still be in\n // flight when the trace is written, which loses the document entirely.\n this.sessionCapturer.snapshotCaptures.push(\n this.sessionCapturer.anchorAfterNavigation(browser).catch(() => {})\n )\n }\n\n private popCommandStackIfMatches(methodName: string, cmdSig: string): void {\n const stackFrame = this.commandStack[this.commandStack.length - 1]\n if (stackFrame?.command === methodName && stackFrame.signature === cmdSig) {\n this.commandStack.pop()\n }\n }\n\n /** Serialize the result and record which element produced it, so a later\n * node:assert on that value can name its target (core's value→locator\n * registry). Fed for every command, so a page-level read records \"no\n * element\" — see `rememberReadValue` for why that sentinel matters. Folded\n * into one call because `makeCaptureCallback` is already over the line cap. */\n private serializeAndAttributeResult(\n methodName: string,\n logArgs: unknown[],\n callbackResult: unknown\n ): unknown {\n const result = serializeCommandResult(callbackResult, methodName)\n rememberReadValue(result, commandTargetSelector(methodName, logArgs))\n return result\n }\n\n // Result-capturing callback factory — called by Nightwatch's async queue\n // when the command completes. This is where we get the *actual* result.\n private makeCaptureCallback(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n cmdSig: string,\n callSource: string | undefined,\n hasUserSource: boolean,\n invokedAt: number,\n sequence: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n // Stamped HERE, when the command actually finished in the browser — not\n // when the test enqueued it. Nightwatch runs commands off a queue, so\n // invocation leads real execution by hundreds of ms, and the page-side\n // mutation stream is on real time: a row stamped at invocation replayed\n // the page from BEFORE its own effect (the `#username` fill rendered an\n // empty field, the `#password` fill rendered only the username). The\n // invocation time becomes the row's `startTime`, so the row also spans its\n // real duration instead of a synthetic 1ms.\n const commandTimestamp = Date.now()\n this.popCommandStackIfMatches(methodName, cmdSig)\n const commandError = callbackError(callbackResult)\n const serializedResult = commandError\n ? undefined\n : this.serializeAndAttributeResult(methodName, logArgs, callbackResult)\n const effectiveUid = this.rowTestUid() ?? testUid\n // Only surface commands that originate from a user-code frame. Commands\n // Nightwatch issues from inside its own queue (e.g. the getTitle a\n // `browser.assert.titleContains` runs) execute in a detached tick with no\n // user frame on the stack, so they'd otherwise leak as top-level actions\n // with an \"unknown\" source. Mirrors the service's user-spec-source guard.\n if (effectiveUid && hasUserSource) {\n const meta: CommandCaptureMeta = {\n testUid: effectiveUid,\n callSource,\n timestamp: commandTimestamp,\n startTime: invokedAt,\n sequence,\n selector: commandRowSelector(methodName, logArgs)\n }\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n commandError\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n cmdSig,\n commandError\n )\n }\n }\n if (userCallback) {\n return userCallback(callbackResult)\n }\n }\n }\n\n private pushCommandStackIfNew(\n methodName: string,\n cmdSig: string,\n callSource: string | undefined\n ): void {\n if (this.lastCommandSig === cmdSig) {\n return\n }\n this.commandStack.push({\n command: methodName,\n callSource,\n signature: cmdSig\n })\n this.lastCommandSig = cmdSig\n }\n\n private describeInvocation(\n methodName: string,\n args: unknown[]\n ): CommandInvocation {\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n return {\n userCallback: hasUserCallback\n ? (lastArg as (result: unknown) => unknown)\n : null,\n logArgs,\n cmdSig,\n callSource: callInfo.callSource,\n hasUserSource: callInfo.filePath !== undefined\n }\n }\n\n private handleCommandExecution(\n browser: NightwatchBrowser,\n browserAny: Record<string, unknown>,\n methodName: string,\n originalMethod: Function,\n args: unknown[]\n ): unknown {\n // The command's session id is the only evidence a mid-run `browser.end()`\n // leaves behind — Nightwatch raises no event and `end` is never wrapped.\n this.observeSession?.(browser)\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const { userCallback, logArgs, cmdSig, callSource, hasUserSource } =\n this.describeInvocation(methodName, args)\n // Invocation time only — the row's real completion stamp is taken inside the\n // capture callback (see makeCaptureCallback).\n const invokedAt = Date.now()\n const sequence = this.issueCounter++\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n hasUserSource,\n invokedAt,\n sequence,\n this.rowTestUid(),\n userCallback\n )\n // Suppress screencast polling while this command may be dispatching input: a\n // screenshot landing mid-click makes the click activate nothing. Enqueue is\n // the only hook the adapter has — Nightwatch executes off a queue it exposes\n // no start event for — so an awaited call (enqueue ≈ execution) is covered,\n // while one queued behind a long wait can see its window expire before it\n // runs. The bound is what keeps that from stalling the recorder instead.\n const closeInputWindow = beginInputDispatch(methodName)\n const gatedCallback = (callbackResult: unknown) => {\n closeInputWindow()\n return captureCallback(callbackResult)\n }\n const modifiedArgs = [...logArgs, gatedCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n closeInputWindow()\n this.popCommandStackIfMatches(methodName, cmdSig)\n this.captureCommandError(methodName, logArgs, error, callSource)\n throw error\n }\n }\n\n private captureCommandError(\n methodName: string,\n args: unknown[],\n error: unknown,\n callSource: string | undefined\n ): void {\n const currentTest = this.rowTest()\n if (!currentTest) {\n return\n }\n\n const normalizedError = toError(error)\n log.error(`[command error] ${methodName}: ${normalizedError.message}`)\n\n this.sessionCapturer\n .captureCommand(methodName, args, undefined, normalizedError, {\n testUid: currentTest.uid,\n callSource,\n selector: commandRowSelector(methodName, args)\n })\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.sessionCapturer.sendCommand(lastCommand)\n }\n }\n\n isProxied(browser: NightwatchBrowser): boolean {\n return this.proxiedBrowsers.has(browser as object)\n }\n\n /**\n * Fire-and-forget: pull a screenshot via the WebDriver HTTP endpoint and\n * attach it to an already-captured command entry. The `suffix` is appended\n * to the log line so retried-command screenshots show `(retry)`. Errors\n * are silently swallowed — screenshots are best-effort and shouldn't fail\n * the run.\n */\n private attachScreenshot(\n browser: NightwatchBrowser,\n entry: { timestamp?: number; screenshot?: string | null },\n methodName: string,\n suffix = ''\n ): void {\n const ts = entry.timestamp ?? 0\n this.sessionCapturer\n .takeScreenshotViaHttp(browser)\n .then((screenshot) => {\n if (screenshot) {\n entry.screenshot = screenshot\n this.sessionCapturer.sendReplaceCommand(ts, entry as CommandLog)\n log.info(`[screenshot] Attached to ${methodName}${suffix}`)\n }\n })\n .catch(() => {})\n }\n}\n","/**\n * The failure a Nightwatch command reports by invoking its callback with an\n * error-shaped result instead of throwing.\n *\n * Only a SYNCHRONOUS failure reaches the try/catch around the wrapped method; an\n * async one (a command that times out waiting for an element) arrives here as a\n * result object. Left in `result`, the row kept `error: undefined` and rendered\n * as a success — the failure was readable only as raw text in the result pane,\n * with no red row and nothing in the Errors tab.\n *\n * `passed` present means this is an assertion result, which carries its own\n * pass/fail handling and must not be reinterpreted here.\n */\nexport function callbackError(result: unknown, depth = 0): Error | undefined {\n if (result instanceof Error) {\n return result\n }\n if (!result || typeof result !== 'object') {\n return undefined\n }\n const r = result as Record<string, unknown>\n // W3C driver responses nest their payload under `value` and Nightwatch passes\n // that wrapper through untouched, so the failure sits one level down — reading\n // only the top level found no `error` and the row stayed green. Depth-bounded:\n // this runs inside the command hook, so a self-referencing result must not be\n // able to overflow the stack and take the user's test down with it.\n if ('value' in r) {\n return depth < 2 ? callbackError(r.value, depth + 1) : undefined\n }\n if ('passed' in r || !r.error) {\n return undefined\n }\n const err = new Error(String(r.message ?? r.error))\n if (typeof r.stack === 'string') {\n err.stack = r.stack\n }\n return err\n}\n","// Which element a Nightwatch row is about — for a native `browser.assert.*` /\n// `browser.verify.*` row, and for a driver command whose result an assertion may\n// later be made on.\n//\n// Nightwatch names the target in the CALL: every element assertion in\n// `lib/api/assertions/*.js` takes the element definition as its FIRST argument\n// (`textContains(definition, expected)`), while the page-level ones take the\n// expected value there instead (`urlContains(expected)`). Element commands\n// follow the same shape (`getText(definition, callback)`), so both answers come\n// off arg 0 — under a per-kind allowlist, because the same namespaces also carry\n// calls whose arg 0 is a plain value or a url.\n//\n// node:assert is the case this can't answer: it takes VALUES and names no\n// element, so `commandTargetSelector` feeds core's value→locator registry from\n// the command path and the assert row reads its target back from there.\n//\n// Neither allowlist is derivable from shared's `ACTION_MAP`: that table says how\n// a command RENDERS (its `Element` entries include WDIO commands called on a\n// handle, with no selector argument at all), not whether arg 0 is a definition.\n\n/**\n * Assertions whose first argument is an element definition, transcribed from\n * Nightwatch's own assertion signatures. An allowlist, not a heuristic: the same\n * namespace also carries the page-level assertions (`titleContains`,\n * `urlContains`) and node:assert's value-based mirrors (`ok`, `equal`, …), whose\n * first argument is a plain value — attaching it as a locator is how a\n * page-title assertion ends up boxing an unrelated element.\n */\nconst ELEMENT_TARGET_ASSERTIONS: ReadonlySet<string> = new Set([\n 'attributeContains',\n 'attributeEquals',\n 'attributeMatches',\n 'containsText',\n 'cssClassNotPresent',\n 'cssClassPresent',\n 'cssProperty',\n 'domPropertyContains',\n 'domPropertyEquals',\n 'domPropertyMatches',\n 'elementNotPresent',\n 'elementPresent',\n 'elementsCount',\n 'enabled',\n 'hasAttribute',\n 'hasClass',\n 'hasDescendants',\n 'hidden',\n 'selected',\n 'textContains',\n 'textEquals',\n 'textMatches',\n 'value',\n 'valueContains',\n 'valueEquals',\n 'visible'\n])\n\n/**\n * Commands whose RESULT is a read of the element named by their first argument,\n * transcribed from Nightwatch's element-command signatures. An allowlist for the\n * same reason as above: `browser.url('https://…')` takes a string first too, and\n * reading it as a locator would attribute the page's own reads to an element.\n * Everything absent — every driver-level read included — records its value as\n * belonging to no element (core's `rememberReadValue` sentinel).\n */\nconst ELEMENT_READ_COMMANDS: ReadonlySet<string> = new Set([\n 'getAccessibleName',\n 'getAriaRole',\n 'getAttribute',\n 'getCssProperty',\n 'getElementProperty',\n 'getElementSize',\n 'getLocation',\n 'getLocationInView',\n 'getRect',\n 'getTagName',\n 'getText',\n 'getValue',\n 'isEnabled',\n 'isPresent',\n 'isSelected',\n 'isVisible',\n 'waitForElementNotPresent',\n 'waitForElementNotVisible',\n 'waitForElementPresent',\n 'waitForElementVisible'\n])\n\n/**\n * Element commands that ACT on the element named by their first argument.\n * Deliberately a second set rather than an extension of the reads above: that\n * one also decides which results enter the value→locator registry, and a click's\n * result is not a value any assertion can be made about. Same allowlist\n * discipline — `browser.keys('abc')` takes a string first too.\n */\nconst ELEMENT_ACTION_COMMANDS: ReadonlySet<string> = new Set([\n 'clearValue',\n 'click',\n 'doubleClick',\n 'moveToElement',\n 'rightClick',\n 'setValue',\n 'submitForm',\n 'updateValue'\n])\n\n/** `assert.not.visible('#x')` is recorded under the dotted path `not.visible`;\n * negating an assertion doesn't change which argument is the element. */\nfunction baseMethod(method: string): string {\n return method.startsWith('not.') ? method.slice('not.'.length) : method\n}\n\n/**\n * The selector an element definition carries: a raw string, or the `selector`\n * property of a `{selector, locateStrategy}` bag / a Nightwatch `Element`\n * (duck-typed, so no dependency on Nightwatch's class identity). A definition\n * built from an already-resolved handle carries no selector at all — such a row\n * gets none rather than a wrong one.\n */\nfunction definitionSelector(definition: unknown): string | undefined {\n if (typeof definition === 'string') {\n return definition || undefined\n }\n if (definition && typeof definition === 'object') {\n const selector = (definition as { selector?: unknown }).selector\n if (typeof selector === 'string' && selector) {\n return selector\n }\n }\n return undefined\n}\n\n/** Locator of the element a native assertion was about, or undefined when the\n * assertion targets no element (page title, url, a value comparison). Lands on\n * `CommandLog.selector` so the player's overlay boxes the target the way it\n * does for WDIO's folded matcher rows. */\nexport function assertTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_TARGET_ASSERTIONS.has(baseMethod(method))) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator a driver-command row is about — the element it read or acted on.\n * Lands on `CommandLog.selector` so the row says what it targeted instead of\n * leaving the exporter to fall back to `args[0]`. Wider than\n * {@link commandTargetSelector}, which answers the narrower provenance question\n * (whose read produced this value) for the value→locator registry. */\nexport function commandRowSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (\n !ELEMENT_READ_COMMANDS.has(method) &&\n !ELEMENT_ACTION_COMMANDS.has(method)\n ) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator of the element a driver command read from, or undefined when the\n * command reads the page rather than an element. Feeds core's value→locator\n * registry: `undefined` records the result as belonging to no element, which is\n * the sentinel a later node:assert on a page-level value relies on. */\nexport function commandTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_READ_COMMANDS.has(method)) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n","import { BOOLEAN_COMMAND_PATTERN } from '../constants.js'\n\n/**\n * Convert the raw value Nightwatch's async queue hands back to a\n * UI-friendly JSON-safe representation. Three special cases:\n *\n * - Nightwatch assertion objects `{ passed, actual, expected, message }`\n * collapse to `true` on pass, or the structured failure record on fail.\n * - Driver-result wrappers `{ value: <raw> }` unwrap to the inner value.\n * `null` on a boolean-semantic command (e.g. `waitForExist`) means\n * \"timed out / not found\" — coerce to `false` so the UI doesn't render\n * `null`.\n * - Plain objects are deep-cloned via JSON.parse/stringify so the UI can\n * safely serialize them; functions and circular references fall back to\n * `String(value)`.\n */\nexport function serializeCommandResult(\n callbackResult: unknown,\n methodName: string\n): unknown {\n if (callbackResult === null || callbackResult === undefined) {\n return undefined\n }\n\n const isBooleanCommand = BOOLEAN_COMMAND_PATTERN.test(methodName)\n\n // After the typeof + null guard above, the value is a non-null object —\n // safe to widen via `Record<string, unknown>` and probe for the discriminator\n // properties without per-access `as any`.\n if (typeof callbackResult === 'object') {\n const r = callbackResult as Record<string, unknown>\n if ('passed' in r) {\n return r.passed\n ? true\n : {\n passed: false,\n actual: r.actual,\n expected: r.expected,\n message: r.message\n }\n }\n if ('value' in r) {\n const raw = r.value\n return raw === null && isBooleanCommand ? false : raw\n }\n }\n\n if (typeof callbackResult !== 'function') {\n try {\n return JSON.parse(JSON.stringify(callbackResult))\n } catch {\n return String(callbackResult)\n }\n }\n\n return undefined\n}\n","// Native-assertion capture: turns explicit `browser.assert.*` /\n// `browser.verify.*` calls into concise trace/UI action rows with real args,\n// a clickable source location, and pass/fail colour.\n//\n// Nightwatch exposes no per-assertion hook. Each explicit call is intercepted\n// at CALL TIME (NativeAssertRecorder) and BUFFERED — concise title, real args,\n// callSource, the per-test testUid, and the preceding screenshot — but NOT\n// streamed, because streaming a neutral row mid-run would render every assert\n// green before its outcome is known. At test-end `captureNativeAssertions` reads\n// the pass/fail truth + verbose failure message and emits each row once,\n// positioned on its real execution window (the exporter re-sorts by the buffered\n// call timestamp). Correlating the recorded calls (not the raw results) also\n// excludes Nightwatch's implicit command-generated assertions (e.g.\n// waitForElementVisible's \"element was visible\" entry).\n//\n// Two outcome sources, in this order: the per-test results bag (`correlate`),\n// then the settlement of the call's own promise (`observedAssertOutcome`). The\n// cucumber runner has no results bag at all, so the settlement is its only\n// source; the describe/it interface has a bag but no per-assertion window.\n//\n// The plugin `afterEach` fires once per describe-suite (not per `it`), so the\n// finalize reads assertions from `results.testcases` (all tests) — see\n// gatherResultAssertions.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n matcherAssertionToCommandLog,\n safeSerializeAssertArg,\n stripAnsi\n} from '@wdio/devtools-core'\nimport { assertTargetSelector } from './assertTarget.js'\nimport type { SessionCapturer } from '../session.js'\nimport type {\n AssertSettlement,\n CollapsedAssertResult,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchCurrentTest,\n ObservedAssertOutcome\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:nativeAssertions')\n\n/**\n * One entry Nightwatch pushes to `results.assertions` (from\n * `NightwatchAssertion.getAssertResult`, lib/assertion/assertion.js). Carries\n * only a human message — no method/args. `failure === false` is the reliable\n * pass signal; any truthy `failure` (message string, or `true`) means failed.\n */\ninterface NwAssertionEntry {\n message?: string\n fullMsg?: string\n failure?: string | boolean\n}\n\n/** One entry Nightwatch pushes to `results.commands` (lib/reporter/index.js\n * logCommandResult). For an assert/verify `name` is the namespaced method and\n * `startTime`/`endTime` are the real queue-execution window in ms\n * (treenode.js). Read only to position the finalized row on the timeline. */\ninterface NwCommandEntry {\n name?: string\n startTime?: number\n endTime?: number\n}\n\nconst ASSERT_CMD_RE = /^(assert|verify)\\.\\w+$/\n\n/** Real per-assertion execution windows, in call order, aligned to `count`\n * recorded calls. Nightwatch enqueues assertions synchronously (all at once)\n * but runs them later one at a time, so the enqueue timestamp clusters the\n * rows; this recovers each row's true execution time so the trace timeline\n * spreads them out. Returns `null` per slot when the executed-command count\n * doesn't line up (e.g. retries) — the enqueue timestamp is kept then. */\nfunction assertCommandTimings(\n commands: NwCommandEntry[],\n count: number\n): Array<{ startTime: number; endTime: number } | null> {\n const executed = commands.filter(\n (c) => typeof c?.name === 'string' && ASSERT_CMD_RE.test(c.name)\n )\n if (executed.length !== count) {\n return new Array(count).fill(null)\n }\n return executed.map((c) =>\n typeof c.startTime === 'number' && typeof c.endTime === 'number'\n ? { startTime: c.startTime, endTime: c.endTime }\n : null\n )\n}\n\n/** Nightwatch embeds the assertion arguments in the message text\n * (AssertionInstance.initialize → Logger.formatMessage), so a passing entry\n * for `titleContains('Example')` reads \"Testing if the page title contains\n * 'Example'\". Match a call to its result entry when every string/number arg\n * appears in the message. */\nfunction messageMatchesArgs(entry: NwAssertionEntry, args: unknown[]): boolean {\n const text = String(entry.fullMsg ?? entry.message ?? '')\n const literals = args.filter(\n (a): a is string | number => typeof a === 'string' || typeof a === 'number'\n )\n return literals.length > 0 && literals.every((a) => text.includes(String(a)))\n}\n\ninterface Outcome {\n passed: boolean\n message: string\n}\n\n/** Pass/fail read off the assertion's own promise — the only outcome evidence\n * cucumber has, since its client is built with no reporter so `results` is\n * never populated. A fulfilment of `undefined` stays neutral: that is an\n * assertion enqueued but never executed, not a pass. Semantics and measurements\n * in CLAUDE.md § Known debt. */\nexport function observedAssertOutcome({\n rejected,\n value,\n settledAt\n}: AssertSettlement): ObservedAssertOutcome | undefined {\n if (rejected || value instanceof Error) {\n return {\n passed: false,\n message: stripAnsi(errorMessage(value)).trim(),\n settledAt\n }\n }\n if (value === undefined) {\n return undefined\n }\n return { passed: true, message: '', settledAt }\n}\n\n/** Execution window recovered from the observed settlement: enqueue → settle.\n * The cucumber runner logs no commands, so `assertCommandTimings` yields\n * nothing there and the row would otherwise span a synthetic 1 ms. */\nfunction observedTiming(\n call: NativeAssertCall\n): { startTime: number; endTime: number } | null {\n return call.observed\n ? { startTime: call.timestamp, endTime: call.observed.settledAt }\n : null\n}\n\n/**\n * All assertion entries for the suite, in declaration order. Nightwatch's\n * plugin `afterEach` fires once per describe-suite (not per `it`), so the flat\n * `results.assertions` reflects only the last testcase; the full per-test\n * breakdown lives in `results.testcases[title].assertions`. Flattening every\n * testcase's entries lets each buffered call correlate to its own test's\n * outcome (without this, only the last test's asserts get a pass/fail and the\n * rest render neutral). Falls back to the flat list for single-test modules or\n * older Nightwatch that doesn't populate `testcases`.\n */\nfunction gatherResultAssertions(\n results:\n | {\n assertions?: NwAssertionEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n): NwAssertionEntry[] {\n const testcases = results?.testcases\n if (testcases && typeof testcases === 'object') {\n const all: NwAssertionEntry[] = []\n for (const tc of Object.values(testcases)) {\n if (Array.isArray(tc?.assertions)) {\n all.push(...(tc.assertions as NwAssertionEntry[]))\n }\n }\n if (all.length > 0) {\n return all\n }\n }\n return Array.isArray(results?.assertions) ? results.assertions : []\n}\n\n/**\n * Pair each recorded call with its `results.assertions` entry for pass/fail +\n * verbose message. Both lists are in call/execution order and each explicit\n * call produces exactly one assertion entry, so: match by args-in-message\n * first (most specific, skips interleaved implicit entries), then fall back to\n * the next unconsumed entry positionally. A call with no matching entry left\n * (never happens for a real assertion) is dropped (`null`).\n */\nfunction correlate(\n calls: NativeAssertCall[],\n assertions: NwAssertionEntry[]\n): Array<Outcome | null> {\n const consumed = new Array(assertions.length).fill(false)\n const toOutcome = (idx: number): Outcome => {\n consumed[idx] = true\n const entry = assertions[idx]\n return {\n passed: !entry.failure,\n message: stripAnsi(String(entry.fullMsg ?? entry.message ?? '')).trim()\n }\n }\n const matched = calls.map((call) => {\n const idx = assertions.findIndex(\n (entry, i) => !consumed[i] && messageMatchesArgs(entry, call.args)\n )\n return idx === -1 ? null : toOutcome(idx)\n })\n return matched.map((outcome) => {\n if (outcome) {\n return outcome\n }\n const idx = consumed.findIndex((used) => !used)\n return idx === -1 ? null : toOutcome(idx)\n })\n}\n\n/** Last already-resolved screenshot in the command log — the DOM the assertion\n * evaluated against (title/most asserts don't mutate it). Synchronous, so it's\n * usable from the call-time wrapper. */\nexport function latestResolvedScreenshot(\n capturer: SessionCapturer\n): string | null {\n for (let i = capturer.commandsLog.length - 1; i >= 0; i--) {\n const shot = capturer.commandsLog[i]?.screenshot\n if (shot) {\n return shot\n }\n }\n return null\n}\n\n/** Reuse the nearest preceding command's screenshot; if the fire-and-forget\n * capture hasn't resolved yet (race), fall back to a fresh end-of-test one. */\nasync function resolveAssertionScreenshot(\n capturer: SessionCapturer,\n browser: NightwatchBrowser\n): Promise<string | null> {\n return (\n latestResolvedScreenshot(capturer) ??\n (await capturer.takeScreenshotViaHttp(browser))\n )\n}\n\n/** `assert.titleContains('SOFT_FAIL_ME')` — the concise row label. Strings are\n * quoted, objects elided; never the verbose failure message. */\nfunction conciseTitle(call: NativeAssertCall): string {\n const preview = call.args\n .map((a) =>\n typeof a === 'string'\n ? `'${a}'`\n : a !== null && typeof a === 'object'\n ? '…'\n : String(a)\n )\n .join(', ')\n return `${call.prefix}.${call.method}(${preview})`\n}\n\n/**\n * Build the neutral \"pending\" row emitted the moment an assert/verify is\n * called — everything known at call time (concise title, real args, callSource,\n * screenshot) but NO result/error yet, so it renders neutral (not red/green)\n * and streams in like a normal in-flight command. `startTime`/`timestamp` are\n * the call time; the row is finalized in place later by\n * {@link captureNativeAssertions}.\n */\nexport function pendingAssertionCommand(\n call: NativeAssertCall,\n testUid: string | undefined,\n screenshot: string | null\n): CommandLog {\n const entry: CommandLog = {\n command: `${call.prefix}.${call.method}`,\n args: call.args.map(safeSerializeAssertArg),\n title: conciseTitle(call),\n timestamp: call.timestamp,\n startTime: call.timestamp\n }\n if (call.callSource) {\n entry.callSource = call.callSource\n }\n if (testUid) {\n entry.testUid = testUid\n }\n if (screenshot) {\n entry.screenshot = screenshot\n }\n // Only element assertions name a target; a title/url/value one gets none, so\n // the overlay never boxes an element the assertion wasn't about.\n const selector = assertTargetSelector(call.method, call.args)\n if (selector) {\n entry.selector = selector\n }\n return entry\n}\n\n/** Nightwatch failure messages end with `… but got: \"<actual>\"`. Pull out the\n * real observed value: Nightwatch passes only the EXPECTED as an arg, so the\n * actual lives in the message (and only on failure). Undefined when absent.\n * Uses indexOf + slice (no backtracking-prone regex) on the message tail. */\nfunction parseActualFromMessage(message: string): string | undefined {\n const marker = 'but got:'\n const idx = message.lastIndexOf(marker)\n if (idx === -1) {\n return undefined\n }\n let rest = message.slice(idx + marker.length).trim()\n rest = rest.replace(/ \\(\\d+ms\\)$/, '').trim() // drop trailing \"(123ms)\"\n rest = rest.replace(/^[\"']/, '').replace(/[\"']$/, '').trim() // strip quotes\n return rest || undefined\n}\n\n/** Build the collapsed `{passed, expected, actual?, message}` result core's\n * `buildAssertParams` prefers over the positional `[actual, expected]` arg\n * convention — which is wrong for Nightwatch, whose asserts pass only the\n * expected value (`titleContains('x')`), never the actual. */\nfunction collapsedAssertResult(\n call: NativeAssertCall,\n outcome: Outcome\n): CollapsedAssertResult {\n const result: CollapsedAssertResult = {\n passed: outcome.passed,\n expected: call.args.length <= 1 ? call.args[0] : call.args\n }\n // Omitted rather than empty for a pass observed off the settlement: the\n // promise carries no prose, and an empty message would render as a blank row.\n if (outcome.message) {\n result.message = outcome.message\n }\n const actual = outcome.passed\n ? undefined\n : parseActualFromMessage(outcome.message)\n if (actual !== undefined) {\n result.actual = actual\n }\n return result\n}\n\n/** Emit one assertion row at test-end: apply pass/fail + verbose error, a\n * screenshot, and its real execution window, then send it. Rows are NOT\n * streamed at call time, so this is the single emit (no neutral pending row). */\nfunction finalizeAssertionRow(\n capturer: SessionCapturer,\n call: NativeAssertCall,\n outcome: Outcome,\n timing: { startTime: number; endTime: number } | null,\n screenshot: string | null,\n testUid: string | undefined\n): void {\n const entry = call.entry!\n const finalized = matcherAssertionToCommandLog(\n {\n prefix: call.prefix,\n method: call.method,\n args: call.args,\n passed: outcome.passed,\n message: outcome.message || `${call.prefix}.${call.method} failed`,\n callSource: call.callSource,\n title: entry.title\n },\n testUid\n )\n // Collapsed object result (not the plain 'passed' string) so the trace's\n // action params show a true actual-vs-expected diff instead of mislabelling\n // Nightwatch's single expected-arg as the actual.\n entry.result = collapsedAssertResult(call, outcome)\n entry.error = finalized.error\n if (!entry.screenshot && screenshot) {\n entry.screenshot = screenshot\n }\n // Position the row on its REAL execution window (Nightwatch enqueues all\n // asserts at once, so the call-time timestamp clustered them). The row was\n // never streamed, so send it now — a single emit with the final outcome.\n if (timing) {\n entry.startTime = timing.startTime\n entry.timestamp =\n timing.endTime > timing.startTime ? timing.endTime : timing.startTime\n }\n capturer.captureAssertCommand(entry)\n log.info(`[assert] ${entry.title} → ${outcome.passed ? 'pass' : 'fail'}`)\n}\n\n/**\n * Emit the native assertion rows at test-end: correlate the recorded calls with\n * `results.assertions`, then send each row with pass/fail (`result`) + the\n * verbose failure message (`error`, failures only) + a screenshot + its real\n * execution window. Rows are buffered (not streamed) at call time — Nightwatch\n * has no per-assertion result hook, so streaming them during the run would show\n * every assert as a neutral (green) row before its outcome is known. A call with\n * no matching result is skipped (defensive).\n */\nexport async function captureNativeAssertions(\n capturer: SessionCapturer,\n browser: NightwatchBrowser,\n currentTest: NightwatchCurrentTest | undefined,\n testUid: string | undefined,\n calls: NativeAssertCall[]\n): Promise<void> {\n if (calls.length === 0) {\n return\n }\n // Boundary cast: `results` is Nightwatch's loosely-typed per-test bag; we read\n // only the assertions + commands arrays whose shapes are documented above.\n const results = currentTest?.results as\n | {\n assertions?: NwAssertionEntry[]\n commands?: NwCommandEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n const assertions = gatherResultAssertions(results)\n const outcomes = correlate(calls, assertions)\n const timings = assertCommandTimings(\n Array.isArray(results?.commands) ? results.commands : [],\n calls.length\n )\n // One shared screenshot for all rows — same DOM, ran consecutively.\n const screenshot = await resolveAssertionScreenshot(capturer, browser)\n\n calls.forEach((call, index) => {\n if (!call.entry) {\n return\n }\n // Results-bag correlation first (it also carries the verbose per-assertion\n // message, which a settlement can't supply for a pass); the observed\n // settlement is the fallback that makes cucumber rows coloured at all. The\n // timing comes from whichever source supplied the outcome — a correlated row\n // keeps its enqueue timestamp when the bag reports no window (the BDD\n // interface always does), which is what `sequence` ordering relies on.\n const correlated = outcomes[index]\n const outcome = correlated ?? call.observed\n if (outcome) {\n finalizeAssertionRow(\n capturer,\n call,\n outcome,\n correlated ? timings[index] : observedTiming(call),\n screenshot,\n testUid\n )\n return\n }\n // No execution outcome correlated to this call (defensive): emit the\n // buffered row in its neutral state so the assertion still appears — never\n // dropped, never mis-coloured as passed/failed. Rows are only buffered at\n // call time, so without this an uncorrelated assert would vanish.\n if (!call.entry.screenshot && screenshot) {\n call.entry.screenshot = screenshot\n }\n capturer.captureAssertCommand(call.entry)\n })\n}\n","/**\n * Call-time recorder for explicit `browser.assert.*` / `browser.verify.*`.\n *\n * Extracted from BrowserProxy: the recording Proxy, the per-test buffer and the\n * settlement observation are one concern (getting a native assertion's args,\n * source location and outcome), separate from driver-command interception.\n * `nativeAssertions.ts` consumes the drained buffer at test-end.\n */\n\nimport {\n observedAssertOutcome,\n latestResolvedScreenshot,\n pendingAssertionCommand\n} from './nativeAssertions.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport type { SessionCapturer } from '../session.js'\nimport type { NativeAssertCall, NightwatchBrowser } from '../types.js'\n\nexport class NativeAssertRecorder {\n /**\n * Per-test-unit buffer of recorded calls. Nightwatch exposes no per-assertion\n * hook and its test-end results carry no source location for passing\n * assertions, so call-time capture is the only way to get real args +\n * callSource. Drained once per test unit (`afterEach`, or the cucumber\n * scenario's pre-quit hook) — never per step.\n */\n private calls: NativeAssertCall[] = []\n\n constructor(\n private readonly capturer: SessionCapturer,\n private getCurrentTest: () => { uid?: string } | null,\n private nextSequence: () => number\n ) {}\n\n clear(): void {\n this.calls = []\n }\n\n /** Hand off this test's recorded calls and clear the buffer so the next test\n * unit starts fresh. */\n drain(): NativeAssertCall[] {\n const calls = this.calls\n this.calls = []\n return calls\n }\n\n /**\n * Replace `browser.assert` / `browser.verify` (both are Nightwatch Proxies\n * whose `get` returns a fresh function per access) with a recording Proxy:\n * on each method call it captures `{prefix, method, args, callSource}` from a\n * user-code frame, buffers a pending row, then delegates to the ORIGINAL\n * namespace method so Nightwatch's queue, chaining, and abortOnFailure\n * semantics (assert aborts, verify does not) are byte-for-byte unchanged.\n * Called once per browser.\n */\n wrapNamespaces(browser: NightwatchBrowser): void {\n // Cast once: the assert/verify namespaces aren't on the public type; each\n // is a dynamic method bag reached by property name.\n const b = browser as unknown as Record<string, unknown>\n ;(['assert', 'verify'] as const).forEach((prefix) => {\n const original = b[prefix]\n if (!original || typeof original !== 'object') {\n return\n }\n b[prefix] = this.namespaceProxy(original, prefix, [])\n })\n }\n\n /**\n * Recording Proxy over one assert/verify namespace. A function property\n * becomes a call-time recorder keyed by its full dotted path\n * (`titleContains`, `not.titleContains`); a nested namespace object recurses\n * through the SAME wrapper — Nightwatch exposes `assert.not` as its own Proxy,\n * so negated asserts are recorded via the identical mechanism as positive\n * ones instead of a parallel path. Non-method, non-namespace props pass\n * through untouched.\n */\n private namespaceProxy(\n target: object,\n prefix: 'assert' | 'verify',\n path: readonly string[]\n ): object {\n return new Proxy(target, {\n get: (t, name, receiver) => {\n const orig = Reflect.get(t, name, receiver)\n if (typeof name !== 'string') {\n return orig\n }\n if (orig !== null && typeof orig === 'object') {\n return this.namespaceProxy(orig, prefix, [...path, name])\n }\n if (typeof orig !== 'function') {\n return orig\n }\n return (...args: unknown[]) => {\n const callInfo = getCallSourceFromStack()\n const call =\n callInfo.filePath === undefined\n ? undefined\n : this.buffer({\n prefix,\n method: [...path, name].join('.'),\n args,\n callSource: callInfo.callSource,\n timestamp: Date.now()\n })\n const result = (orig as (...a: unknown[]) => unknown)(...args)\n if (call) {\n this.observe(call, result)\n }\n return result\n }\n }\n })\n }\n\n /** Buffer an explicit assert/verify call at invocation time (prebuilding its\n * row with args/callSource/screenshot), but do NOT stream it yet. Nightwatch\n * exposes no per-assertion result hook, so streaming here would show every\n * assert as a neutral (green) row while the test is still Running — before\n * its pass/fail is known. `captureNativeAssertions` emits the rows at\n * test-end with real outcomes + execution timing instead. */\n private buffer(call: NativeAssertCall): NativeAssertCall {\n const entry = pendingAssertionCommand(\n call,\n this.getCurrentTest()?.uid,\n latestResolvedScreenshot(this.capturer)\n )\n entry.sequence = this.nextSequence()\n call.entry = entry\n this.calls.push(call)\n return call\n }\n\n /**\n * Watch the assertion's OWN returned promise for its outcome — see\n * `observedAssertOutcome` for the settlement semantics and why this is the\n * cucumber runner's only pass/fail source. `result` is handed back to the\n * caller untouched; this only adds a second handler, so Nightwatch's own\n * chaining and rejection propagation are unchanged. A non-thenable result\n * (Nightwatch's synchronous `api` return, when commands aren't ES6-async)\n * leaves the call unobserved and the results-bag path supplies the outcome.\n */\n private observe(call: NativeAssertCall, result: unknown): void {\n const thenable = result as { then?: unknown } | null | undefined\n if (!thenable || typeof thenable.then !== 'function') {\n return\n }\n void (result as Promise<unknown>).then(\n (value) => {\n call.observed = observedAssertOutcome({\n rejected: false,\n value,\n settledAt: Date.now()\n })\n },\n (reason) => {\n call.observed = observedAssertOutcome({\n rejected: true,\n value: reason,\n settledAt: Date.now()\n })\n }\n )\n }\n}\n","/**\n * Binds the screencast recorder to a browser session, and rebinds it — and\n * nothing else — when that session is replaced mid-run.\n *\n * Nightwatch raises no session event, and `end` sits in\n * INTERNAL_COMMANDS_TO_IGNORE, so the command proxy never sees the call that\n * destroys the session. The BDD `describe/it` interface compounds it: the\n * plugin's `beforeEach` fires once per MODULE, so `ensureSessionInitialized` —\n * the only place that detects a session change — is never re-entered for a\n * `browser.end()` between `it`s. A command executing under a different\n * `sessionId` is the sole evidence the session was replaced.\n *\n * Rotation touches the recorder and nothing else — the run's SessionCapturer\n * outlives every session it is pointed at. It leaves `lastSessionId` alone\n * because that field is `ensureSessionInitialized`'s own bookkeeping. The other\n * half of a session rebind, re-arming BiDi and the collector preload, hangs off\n * the same detection in `session-init.ts` `rearmCaptureForSession`. Both halves\n * are self-guarding, so `ensureSessionInitialized` reuses them for the session\n * replacement it detects itself.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { ScreencastRecorder } from './screencast.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:screencast-session')\n\n/**\n * Start a fresh recorder for `sessionId` — every session gets its own, matching\n * the WDIO service. Polling mode only (Nightwatch exposes no stable CDP escape\n * hatch). Idempotent, so bringup and rotation share one path.\n */\nexport async function startScreencast(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n sessionId: string | undefined\n): Promise<void> {\n if (!ctx.screencastOptions.enabled || ctx.screencastRecorder || !sessionId) {\n return\n }\n ctx.screencastRecorder = new ScreencastRecorder(\n ctx.sessionCapturer,\n ctx.screencastOptions\n )\n ctx.screencastSessionId = sessionId\n log.info(`🎬 Starting screencast for session ${sessionId}`)\n await ctx.screencastRecorder.start(browser)\n}\n\n/**\n * Whether the recorder is bound to a session the browser has already left.\n * Requires BOTH a live recorder and a live session id: starting one from\n * nothing would strand a poll interval past the plugin's own teardown, which\n * finalizes the recorder before its last browser calls.\n */\nexport function needsScreencastRotation(\n ctx: Pick<SessionInitCtx, 'screencastRecorder' | 'screencastSessionId'>,\n sessionId: string | undefined\n): boolean {\n return (\n ctx.screencastRecorder !== undefined &&\n sessionId !== undefined &&\n ctx.screencastSessionId !== sessionId\n )\n}\n\n/**\n * Observe one command. Rotation is fire-and-forget — a command must not wait on\n * a video encode — and parked on `ctx.screencastRotation`, which both latches\n * out the flood of commands arriving before it completes and gives teardown\n * something to await, so a rotation cannot start a recorder after the plugin\n * has already finalized.\n */\nexport function rotateScreencastForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n if (ctx.screencastRotation || !ctx.screencastOptions.enabled) {\n return\n }\n const sessionId = browser.sessionId ?? undefined\n if (!needsScreencastRotation(ctx, sessionId)) {\n return\n }\n log.info(`🎬 Session replaced — rotating screencast to ${sessionId}`)\n ctx.screencastRotation = ctx\n .finalizeCurrentScreencast()\n .then(() => startScreencast(ctx, browser, sessionId))\n .catch((err) =>\n log.warn(`Screencast rotation failed: ${errorMessage(err)}`)\n )\n .finally(() => {\n ctx.screencastRotation = undefined\n })\n}\n","import logger from '@wdio/logger'\nimport { ScreencastRecorderBase, errorMessage } from '@wdio/devtools-core'\nimport type { ScreencastOptions } from '@wdio/devtools-shared'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:ScreencastRecorder')\n\n/**\n * Nightwatch screencast recorder. Polling-only — Nightwatch doesn't expose a\n * stable CDP escape hatch the way WDIO (getPuppeteer) and Selenium\n * (createCDPConnection) do.\n *\n * `browser.takeScreenshot()` goes through Nightwatch's command queue and is\n * unreliable for polling (the existing code has `takeScreenshotViaHttp` for\n * the same reason — see session.ts). The recorder delegates to that helper\n * instead so screenshots fire directly over the WebDriver HTTP transport.\n */\nexport class ScreencastRecorder extends ScreencastRecorderBase<NightwatchBrowser> {\n readonly #sessionCapturer: SessionCapturer\n\n constructor(sessionCapturer: SessionCapturer, options: ScreencastOptions) {\n super(options)\n this.#sessionCapturer = sessionCapturer\n }\n\n protected override onPollingStarted(intervalMs: number): void {\n log.info(\n `✓ Screencast recording started (polling mode, ${intervalMs} ms interval)`\n )\n }\n\n protected override onPollingStopped(frameCount: number): void {\n log.info(`✓ Screencast stopped — ${frameCount} frame(s) collected`)\n }\n\n protected override onUnavailable(err: unknown): void {\n log.warn(\n `Screencast unavailable (${errorMessage(err)}). Recording skipped.`\n )\n }\n\n protected override async takeScreenshot(): Promise<string | null> {\n const browser = this.driver\n if (!browser) {\n return null\n }\n return this.#sessionCapturer.takeScreenshotViaHttp(browser)\n }\n}\n","/**\n * The plugin's screencast seam: what a run's mode and filmstrip/video options\n * imply for the recorder, plus the per-run frame buffer the trace filmstrip and\n * the per-test video slices are cut from.\n *\n * The recorder itself is `screencast.ts`, and binding it to a session — or\n * rotating it when the session is replaced — is `screencast-session.ts`. This is\n * the plugin-level state those two hang off, split out of `index.ts` so the\n * plugin keeps only its accessor bag and thin delegators.\n */\n\nimport { accumulatedScreencastFrames } from '@wdio/devtools-core'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { finalizeCurrentScreencast } from './session-init.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n ScreencastOptions,\n TraceVideoPolicy\n} from './types.js'\n\n/** The already-defaulted options the recorder decision is made from — read the\n * resolved `filmstrip`/`video`, never the raw `options.filmstrip`, or a\n * default-on filmstrip leaves the recorder inert while the plugin's own options\n * claim it is running. */\nexport interface ScreencastResolutionInput {\n mode: DevToolsMode\n filmstrip: boolean\n video: TraceVideoPolicy\n screencast: ScreencastOptions | undefined\n}\n\nexport interface ResolvedScreencast {\n /** What the plugin exposes as `options.screencast`. */\n options: ScreencastOptions\n /** Config warning to log, when the mode makes the option meaningless. */\n warning?: string\n}\n\n/**\n * Whether this run records a screencast, and under which options. Filmstrip OR a\n * produce-only per-test video drives the recorder in trace mode; bare screencast\n * with neither is a live-mode feature and is dropped there.\n */\nexport function resolveScreencastOptions(\n input: ScreencastResolutionInput\n): ResolvedScreencast {\n const screencast = input.screencast ?? {}\n if (input.mode !== 'trace') {\n return { options: screencast }\n }\n if (input.filmstrip || input.video !== 'off') {\n return { options: { ...screencast, enabled: true } }\n }\n if (screencast.enabled === true) {\n return {\n options: {},\n warning: 'trace mode: ignoring screencast option (live-mode feature)'\n }\n }\n return { options: screencast }\n}\n\n/**\n * One run's screencast state: the live recorder and the session it is bound to,\n * an in-flight rotation, and the frames drained from recorders already finalized.\n */\nexport class PluginScreencast {\n /** Recorder options with the shared defaults applied. */\n readonly options: ScreencastOptions\n recorder?: ScreencastRecorder\n sessionId?: string\n /** In-flight recorder rotation — see `screencast-session.ts`. */\n rotation?: Promise<void>\n\n // Snapshotted before each recorder is nulled, so the export isn't blank.\n #accumulated: ScreencastFrame[] = []\n readonly #keepFrames: boolean\n\n /** `keepFrames` when the trace filmstrip or a per-test video will consume\n * them; without either, a finalized recorder's frames are of no further use. */\n constructor(options: ScreencastOptions, keepFrames: boolean) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n this.#keepFrames = keepFrames\n }\n\n /** Every frame this run has produced — the finalized recorders' plus the live\n * one's, so a mid-run slice flush (which fires before the recorder is\n * drained) isn't blank. */\n get frames(): ScreencastFrame[] {\n return accumulatedScreencastFrames(this.#accumulated, this.recorder)\n }\n\n /** Drain the live recorder into the run buffer, then stop and clear it. The\n * drain has to precede the stop: `finalizeCurrentScreencast` nulls the\n * recorder, and with it the only reference to its frames. */\n async finalize(ctx: SessionInitCtx): Promise<void> {\n if (this.#keepFrames && this.recorder) {\n this.#accumulated.push(...this.recorder.frames)\n }\n await finalizeCurrentScreencast(ctx)\n }\n}\n","/**\n * Nightwatch eventHub integration.\n *\n * Extracted from the plugin class so registration stays out of the\n * file-size cap and the metadata-forwarding behavior is unit-testable\n * without standing up the whole plugin. The plugin's\n * `registerEventHandlers(eventHub)` delegates here.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchEventHub } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:event-hub')\n\nexport interface EventHubBindings {\n /** Live session capturer — may be undefined until bringup completes. */\n getSessionCapturer(): SessionCapturer | undefined\n /** Builds the `options` field forwarded with the metadata payload. */\n buildMetadataOptions(): unknown\n /** Lets the plugin flip the cucumber-runner flag on detection. */\n setCucumberRunner(value: boolean): void\n}\n\nfunction makeSessionMetadataHandler(\n bindings: EventHubBindings\n): (data: unknown) => void {\n return (data: unknown) => {\n try {\n const md =\n ((data as { metadata?: Record<string, unknown> } | undefined)\n ?.metadata as Record<string, unknown> | undefined) ?? {}\n const capturer = bindings.getSessionCapturer()\n const sessionCapabilities = md.sessionCapabilities\n const sessionId = md.sessionId as string | undefined\n if (!capturer || (!sessionCapabilities && !sessionId)) {\n return\n }\n capturer.sendUpstream('metadata', {\n type: TraceType.Testrunner,\n capabilities: sessionCapabilities ?? {},\n sessionId,\n testEnv: md.testEnv as string | undefined,\n host: md.host as string | undefined,\n modulePath: md.modulePath as string | undefined,\n options: bindings.buildMetadataOptions()\n })\n } catch (err) {\n log.error(`Error in event handler: ${errorMessage(err)}`)\n }\n }\n}\n\nexport function registerEventHandlers(\n eventHub: NightwatchEventHub,\n bindings: EventHubBindings\n): void {\n bindings.setCucumberRunner(eventHub.runner === 'cucumber')\n if (eventHub.runner === 'cucumber') {\n log.info('✓ Cucumber runner detected via NightwatchEventHub')\n }\n log.info('✓ NightwatchEventHub registered — enriched metadata enabled')\n const handler = makeSessionMetadataHandler(bindings)\n eventHub.on('TestSuiteStarted', handler)\n eventHub.on('TestRunStarted', handler)\n}\n","/**\n * Cucumber lifecycle for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to isolate the cucumber-specific orchestration from the per-test\n * (Mocha/Jasmine-style) lifecycle.\n *\n * The plugin passes itself as a `CucumberLifecycleCtx` — a narrow interface\n * exposing only the fields and methods this module needs. The lifecycle\n * helpers mutate the plugin's \"current execution\" state via the accessors on\n * the ctx (they can't reach the plugin's private fields directly).\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport {\n WS_SCOPE,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\n\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type { NightwatchBrowser, SuiteStats, TestStats } from './types.js'\nimport { TEST_STATE } from './constants.js'\nimport {\n closeOpenSteps,\n cucumberResultToTestState\n} from './helpers/cucumberResult.js'\nimport { buildCucumberScenarioSuite } from './helpers/cucumberScenarioBuilder.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\nimport {\n recordTestSliceBoundary,\n flushTestSlice,\n type TestSliceCtx\n} from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Cucumber's result shape varies across major versions; we pin only the\n * status field we read. Pickle shapes come from shared. */\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx extends TestSliceCtx {\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy | undefined\n setCucumberRunner(v: boolean): void\n ensureSessionInitialized(browser: NightwatchBrowser): Promise<void>\n wrapBrowserOnce(browser: NightwatchBrowser): void\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n getCurrentScenarioSuite(): SuiteStats | null\n setCurrentScenarioSuite(s: SuiteStats | null): void\n setCurrentStep(s: unknown): void\n getCurrentStep(): unknown\n setCurrentTest(t: unknown): void\n recordAttempt(uid: string, specFile?: string): number\n recordOutcome(uid: string, state: TestStats['state']): void\n emitTestArtifacts(uid: string | undefined, failed: boolean): Promise<void>\n}\n\ntype MutStep = {\n title?: string\n state?: string\n start?: Date | null\n end?: Date | null\n _duration?: number\n}\n\nfunction attachScenarioToFeature(\n ctx: CucumberLifecycleCtx,\n featureSuite: SuiteStats,\n scenarioSuite: SuiteStats\n): void {\n // If a suite with this uid already exists, this is a RETRY of the same\n // scenario — clear execution data so only the latest attempt shows.\n const existingIdx = featureSuite.suites.findIndex(\n (s: SuiteStats) => s.uid === scenarioSuite.uid\n )\n if (existingIdx !== -1) {\n featureSuite.suites[existingIdx] = scenarioSuite\n // Pass the specific scenario uid so only this scenario's execution data\n // is reset — a uid-less clearExecutionData would mark ALL suites as\n // running, destroying the previous terminal states of sibling scenarios.\n ctx.sessionCapturer.sendUpstream(WS_SCOPE.clearExecutionData, {\n uid: scenarioSuite.uid,\n entryType: 'suite'\n })\n } else {\n featureSuite.suites.push(scenarioSuite)\n }\n}\n\nfunction createFeatureSuite(\n ctx: CucumberLifecycleCtx,\n featureUri: string,\n featureName: string,\n featureContent: string,\n featureAbsPath: string,\n scenarioName: string,\n steps: Array<{ text: string }>\n): {\n featureSuite: SuiteStats\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const featureSuite = ctx.suiteManager.getOrCreateSuite(\n featureUri,\n featureName,\n featureUri,\n []\n )\n ctx.suiteManager.markSuiteAsRunning(featureSuite)\n const { featureLine, scenarioLine, stepLines, stepKeywords } =\n parseCucumberScenario(\n featureContent,\n scenarioName,\n steps.map((s) => s.text)\n )\n if (featureAbsPath && featureLine > 0) {\n featureSuite.callSource = `${featureAbsPath}:${featureLine}`\n }\n return { featureSuite, scenarioLine, stepLines, stepKeywords }\n}\n\nfunction normalizeSteps(\n pickleSteps: CucumberPickleStep[] | undefined\n): Array<{ text: string }> {\n return (pickleSteps ?? []).map((s) => ({ text: s.text ?? '' }))\n}\n\nfunction captureFeatureSources(\n ctx: CucumberLifecycleCtx,\n paths: string[]\n): void {\n for (const p of paths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\n}\n\nexport async function initCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n await ctx.ensureSessionInitialized(browser)\n const featureUri: string = pickle.uri ?? 'unknown.feature'\n const scenarioName: string = pickle.name ?? 'Unknown Scenario'\n const steps = normalizeSteps(pickle.steps)\n const {\n featureName,\n featureContent,\n featureAbsPath,\n stepDefFiles,\n capturedPaths\n } = scanFeatureFile(featureUri)\n captureFeatureSources(ctx, capturedPaths)\n const { featureSuite, scenarioLine, stepLines, stepKeywords } =\n createFeatureSuite(\n ctx,\n featureUri,\n featureName,\n featureContent,\n featureAbsPath,\n scenarioName,\n steps\n )\n const scenarioSuite = buildCucumberScenarioSuite({\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords,\n scenarioLine,\n parentFeatureSuiteUid: featureSuite.uid,\n recordAttempt: (uid, specFile) => ctx.recordAttempt(uid, specFile)\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n // The scenario is the `test` unit; its steps are the leaf metadata entries.\n recordTestSliceBoundary(ctx, featureUri, scenarioSuite.uid)\n ctx.setCurrentScenarioSuite(scenarioSuite)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n ctx.testReporter.updateSuites()\n ctx.wrapBrowserOnce(browser)\n log.info(`🥒 Scenario: ${scenarioName}`)\n}\n\nexport async function finalizeCucumberScenario(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle | undefined\n): Promise<void> {\n try {\n const scenarioState = cucumberResultToTestState(result)\n const scenario = ctx.getCurrentScenarioSuite()\n if (scenario) {\n const now = new Date()\n const duration =\n now.getTime() - (scenario.start?.getTime() ?? now.getTime())\n scenario.state = scenarioState\n scenario.end = now\n scenario._duration = duration\n // Stamp this attempt's real outcome so spec/session retention doesn't\n // collapse to the retry-stable suite's last-overwritten state.\n ctx.recordOutcome(scenario.uid, scenarioState)\n closeOpenSteps(scenario, scenarioState, now)\n\n const featureUri: string = pickle?.uri ?? 'unknown.feature'\n ctx.testManager.markTestAsProcessed(featureUri, pickle?.name ?? '')\n\n const featureSuite = ctx.suiteManager.getSuite(featureUri)\n if (featureSuite) {\n // Finalize is not called until all scenarios are done — just update state.\n ctx.suiteManager.finalizeSuiteState(featureSuite)\n }\n\n ctx.incrementCount(scenarioState)\n const icon = ctx.testIcon(scenarioState)\n log.info(\n ` ${icon} ${pickle?.name ?? 'Unknown'} (${(duration / 1000).toFixed(2)}s)`\n )\n\n ctx.testReporter.updateSuites()\n ctx.setCurrentScenarioSuite(null)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n }\n // The trace capture, slice flush, and per-test artifacts need a live\n // WebDriver session — by this hook (cucumber After order:-1) the browser is\n // already quit. They run in captureCucumberScenarioBeforeQuit (order:1000,\n // pre-quit); this hook keeps only the settled-outcome bookkeeping.\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\n/**\n * Live-session work for a cucumber scenario — runs at the cucumber `After`\n * `order:1000` hook, BEFORE Nightwatch quits the browser (its quit hook is\n * `order:0`; `finalizeCucumberScenario` is `order:-1`, after quit, so it reads\n * the settled outcome). Trace capture, the slice flush, and per-test artifacts\n * all need a live WebDriver session — at finalize the session is gone and the\n * flush bails, writing no zip. Trace test-state may be pre-outcome for a\n * scenario that fails on a Nightwatch assertion (see CLAUDE.md known debt).\n */\nexport async function captureCucumberScenarioBeforeQuit(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n try {\n // Emit native asserts (assert.titleContains, …) while the session is live —\n // Nightwatch's afterEach path skips the cucumber runner, so this is the only\n // place scenario asserts get captured. drainNativeAssertCalls() is empty when\n // captureAssertions is off. `currentTest` is undefined because a cucumber run\n // installs Nightwatch's no-op SimplifiedReporter and never fills a results\n // bag; each row's pass/fail comes from its own settled promise instead\n // (`observedAssertOutcome`).\n const assertCalls = ctx.browserProxy?.drainNativeAssertCalls() ?? []\n if (assertCalls.length > 0) {\n await captureNativeAssertions(\n ctx.sessionCapturer,\n browser,\n undefined,\n scenario?.uid,\n assertCalls\n )\n }\n // Anchored: a scenario ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await ctx.sessionCapturer.captureTrace(browser, true)\n flushTestSlice(ctx)\n await ctx.emitTestArtifacts(\n scenario?.uid,\n cucumberResultToTestState(result) === TEST_STATE.FAILED\n )\n } catch (err) {\n log.error(\n `Failed to capture Cucumber scenario before quit: ${errorMessage(err)}`\n )\n }\n}\n\nexport async function cucumberBeforeStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n if (!scenario) {\n return\n }\n // Reset per-step dedup tracking so commands in step N are never\n // mistaken for retries of identically-signatured commands from step N-1.\n ctx.browserProxy?.resetCommandTracking()\n\n const stepText: string = pickleStep?.text ?? ''\n const step = (scenario.tests as Array<MutStep | string>).find(\n (t): t is MutStep =>\n typeof t !== 'string' &&\n (t.title?.endsWith(stepText) === true || t.title === stepText)\n )\n if (step) {\n step.state = TEST_STATE.RUNNING\n step.start = new Date()\n step.end = null\n ctx.setCurrentStep(step)\n ctx.testReporter.updateSuites()\n }\n}\n\nexport async function cucumberAfterStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const step = ctx.getCurrentStep() as MutStep | null\n if (!step) {\n return\n }\n const status = String(result?.status ?? 'UNKNOWN').toUpperCase()\n const stepState: TestStats['state'] =\n status === 'PASSED'\n ? TEST_STATE.PASSED\n : status === 'SKIPPED'\n ? TEST_STATE.SKIPPED\n : TEST_STATE.FAILED\n step.state = stepState\n step.end = new Date()\n step._duration = Date.now() - (step.start?.getTime() ?? Date.now())\n ctx.setCurrentStep(null)\n ctx.testReporter.updateSuites()\n void pickleStep\n}\n\nexport async function cucumberBefore(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n ctx.setCucumberRunner(true)\n await initCucumberScenario(ctx, browser, pickle)\n}\n\nexport async function cucumberAfter(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n): Promise<void> {\n await finalizeCucumberScenario(ctx, browser, result, pickle)\n}\n","import { TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\n\n/**\n * Map a Cucumber Pickle step result's `status` field (PASSED/FAILED/SKIPPED/\n * UNKNOWN/PENDING/AMBIGUOUS/UNDEFINED) to the dashboard's TestStats state.\n * Everything non-passed and non-skipped is treated as FAILED for the UI —\n * the underlying error/message survives in the pickle for the Compare view.\n */\nexport function cucumberResultToTestState(result: unknown): TestStats['state'] {\n const status = String(\n (result as { status?: unknown })?.status ?? 'UNKNOWN'\n ).toUpperCase()\n if (status === 'PASSED') {\n return TEST_STATE.PASSED\n }\n if (status === 'SKIPPED') {\n return TEST_STATE.SKIPPED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * Cucumber's After hook fires with the scenario's final status, but any\n * still-running or pending steps (e.g. an early failure short-circuited\n * the rest) need to be closed too. Mark them PASSED only when the whole\n * scenario passed; FAILED otherwise. Pure in-place mutation — the suite's\n * `tests` array references are the same TestStats objects the dashboard\n * already received via earlier WS broadcasts.\n */\nexport function closeOpenSteps(\n suite: SuiteStats,\n scenarioState: TestStats['state'],\n now: Date = new Date()\n): void {\n for (const step of suite.tests) {\n if (typeof step === 'string') {\n continue\n }\n if (step.state === 'running' || step.state === 'pending') {\n step.state =\n scenarioState === TEST_STATE.PASSED\n ? TEST_STATE.PASSED\n : TEST_STATE.FAILED\n step.end = now\n }\n }\n}\n","import { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats } from '../types.js'\nimport { deterministicUid, findStepDefinitionLine } from './utils.js'\n\nexport interface CucumberScenarioBuildInput {\n /** Cucumber pickle URI — the .feature path. */\n featureUri: string\n scenarioName: string\n featureName: string\n /** Absolute file path used for callSource resolution (may be unresolved). */\n featureAbsPath?: string\n /** Step-definition files discovered for the feature (passed to find each step's source location). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Pickle steps as parsed by Cucumber. */\n steps: Array<{ text: string }>\n /** Per-step source line numbers from the .feature file (parsed once by the caller). */\n stepLines: number[]\n /** Per-step Gherkin keywords (Given/When/Then/And/But) parsed alongside `stepLines`. */\n stepKeywords: string[]\n /** Scenario header line number in the .feature file (0 if unresolvable). */\n scenarioLine: number\n /** Parent feature-suite uid — scenarios nest under this. */\n parentFeatureSuiteUid: string\n /** Records the scenario start under its (retry-stable) uid + feature file and\n * returns the 0-based attempt number stamped on every step. Omitted → 0. */\n recordAttempt?: (scenarioUid: string, specFile?: string) => number\n}\n\n/**\n * Build a fully-populated scenario sub-suite (with its `tests` array of step\n * TestStats entries) for one Cucumber scenario. Pure factory — no side effects;\n * the caller decides whether to push into the feature suite or replace an\n * existing entry (retry case).\n *\n * Extracted from `NightwatchDevToolsPlugin.#initCucumberScenario` — the\n * scenario+steps construction was ~70 lines of object-literal building that\n * doesn't touch plugin state.\n */\nfunction buildScenarioStepTest(\n input: CucumberScenarioBuildInput,\n scenarioUid: string,\n attempt: number,\n i: number\n): SuiteStats['tests'][number] {\n const {\n featureUri,\n scenarioName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords\n } = input\n const step = steps[i]\n const keyword = stepKeywords[i] || ''\n const stepLabel = keyword ? `${keyword} ${step.text}` : step.text\n const stepDefLoc = findStepDefinitionLine(stepDefFiles, step.text)\n const callSource = stepDefLoc\n ? `${stepDefLoc.filePath}:${stepDefLoc.line}`\n : featureAbsPath && stepLines[i] > 0\n ? `${featureAbsPath}:${stepLines[i]}`\n : undefined\n return {\n // Scope by the scenario uid (which carries the scenario line) so identical\n // step text in sibling scenarios and outline example rows stays distinct.\n uid: deterministicUid(featureUri, `step:${scenarioUid}:${step.text}`),\n cid: DEFAULTS.CID,\n title: stepLabel,\n fullTitle: `${scenarioName} ${stepLabel}`,\n parent: scenarioUid,\n state: TEST_STATE.PENDING,\n start: new Date(),\n end: null,\n type: 'test' as const,\n file: featureUri,\n // Scenario-level attempt from the tracker (0 first run, +1 per retry);\n // flows to TestOutcome.attempt via collectSuiteTestMetadata.\n retries: attempt,\n _duration: 0,\n hooks: [],\n callSource\n }\n}\n\nexport function buildCucumberScenarioSuite(\n input: CucumberScenarioBuildInput\n): SuiteStats {\n const {\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n steps,\n scenarioLine,\n parentFeatureSuiteUid\n } = input\n // deterministicUid (no counter) so the SAME scenario gets the SAME uid\n // across retries — that's what makes retry-coalescing work upstream. The\n // scenario line disambiguates outline example rows that share a name.\n const scenarioUid = deterministicUid(\n featureUri,\n `scenario:${scenarioName}:${scenarioLine}`\n )\n const attempt = input.recordAttempt?.(scenarioUid, featureUri) ?? 0\n const scenarioSuite: SuiteStats = {\n uid: scenarioUid,\n cid: DEFAULTS.CID,\n title: scenarioName,\n fullTitle: `${featureName} ${scenarioName}`,\n parent: parentFeatureSuiteUid,\n type: 'suite' as const,\n file: featureUri,\n start: new Date(),\n state: TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource:\n featureAbsPath && scenarioLine > 0\n ? `${featureAbsPath}:${scenarioLine}`\n : undefined\n }\n for (let i = 0; i < steps.length; i++) {\n scenarioSuite.tests.push(\n buildScenarioStepTest(input, scenarioUid, attempt, i)\n )\n }\n return scenarioSuite\n}\n","import fs from 'fs'\nimport path from 'node:path'\n\nexport interface FeatureFileScan {\n /** Header `Feature:` value, or the filename basename if unreadable. */\n featureName: string\n /** Raw `.feature` file contents (empty when unreadable). */\n featureContent: string\n /** Absolute path to the `.feature` file (resolved from cwd + uri). */\n featureAbsPath: string\n /** Sibling step-definition files (under `step_definitions`/`steps`/`support`). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Paths the caller should feed to `sessionCapturer.captureSource` so the\n * dashboard's Source panel can render them. */\n capturedPaths: string[]\n}\n\n/**\n * Scan a Cucumber feature file and its sibling step-definitions. Pure I/O —\n * the caller invokes `sessionCapturer.captureSource` for each path in\n * `capturedPaths` so this helper stays free of the session capturer.\n */\nexport function scanFeatureFile(featureUri: string): FeatureFileScan {\n const featureAbsPath = path.resolve(process.cwd(), featureUri)\n const result: FeatureFileScan = {\n featureName: path.basename(featureUri, '.feature'),\n featureContent: '',\n featureAbsPath,\n stepDefFiles: [],\n capturedPaths: []\n }\n\n if (featureUri === 'unknown.feature' || !fs.existsSync(featureAbsPath)) {\n return result\n }\n\n result.featureContent = fs.readFileSync(featureAbsPath, 'utf-8')\n const match = result.featureContent.match(/^\\s*Feature:\\s*(.+)/m)\n if (match) {\n result.featureName = match[1].trim()\n }\n result.capturedPaths.push(featureAbsPath)\n\n const featureDir = path.dirname(featureAbsPath)\n const stepDirCandidates = ['step_definitions', 'steps', 'support']\n for (const candidate of stepDirCandidates) {\n const stepDir = path.join(featureDir, candidate)\n if (!fs.existsSync(stepDir) || !fs.statSync(stepDir).isDirectory()) {\n continue\n }\n for (const entry of fs.readdirSync(stepDir)) {\n if (!/\\.(js|ts|mjs|cjs)$/.test(entry)) {\n continue\n }\n const stepFilePath = path.join(stepDir, entry)\n result.capturedPaths.push(stepFilePath)\n try {\n result.stepDefFiles.push({\n filePath: stepFilePath,\n content: fs.readFileSync(stepFilePath, 'utf-8')\n })\n } catch {\n // skip unreadable files\n }\n }\n }\n\n return result\n}\n","/**\n * Trace-slice boundary recording for `spec` and `test` granularity.\n *\n * `spec` slices are recorded/flushed at each spec-file transition (unchanged\n * from the original inline `beforeEach` logic). For `test`, Nightwatch builds\n * test outcomes into the suite tree in place — a retry overwrites the previous\n * attempt's state (regular tests reuse the same test object; cucumber replaces\n * the scenario suite). So each attempt's slice is flushed at its own\n * test/scenario END, before the next attempt can overwrite the outcome the\n * flush reads. Regular tests drive this from test-lifecycle, cucumber scenarios\n * from cucumber-lifecycle; both share this module.\n */\n\nimport {\n findFlushableRange,\n recordSliceBoundary,\n recordSpecBoundary,\n type SpecBoundaryContext,\n type SpecRange,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type { TraceGranularity } from './types.js'\n\nexport interface TestSliceCtx {\n readonly sessionCapturer: SessionCapturer\n readonly traceMode: boolean\n readonly traceGranularity: TraceGranularity\n readonly specRanges: SpecRange[]\n readonly flushedSpecs: Set<string>\n flushTraceRange(range: SpecRange): Promise<TraceArtifact | undefined>\n}\n\nfunction sliceActive(ctx: TestSliceCtx): boolean {\n return ctx.traceMode && ctx.traceGranularity === 'test'\n}\n\nfunction boundaryContext(ctx: TestSliceCtx): SpecBoundaryContext {\n return {\n specRanges: ctx.specRanges,\n flushedSpecs: ctx.flushedSpecs,\n capturer: ctx.sessionCapturer,\n // Stamped onto the range so its own flush waits for exactly these.\n pendingCaptures: ctx.sessionCapturer.snapshotCaptures\n }\n}\n\nfunction flushPrevious(ctx: TestSliceCtx, prevRange: SpecRange | null): void {\n if (!prevRange) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(prevRange)\n}\n\n/**\n * Record a spec-file boundary at test/scenario start and eagerly flush the\n * previous spec's slice. No-op for `session`/`test` granularity (core returns\n * null). Preserves the original `beforeEach` behavior verbatim.\n */\nexport function recordSpecSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string\n): void {\n const prevRange = recordSpecBoundary(\n boundaryContext(ctx),\n specFile,\n ctx.traceGranularity\n )\n flushPrevious(ctx, prevRange)\n}\n\n/**\n * Record a per-test slice boundary at test/scenario start. No-op outside trace\n * mode + `test` granularity. Retries push a distinct range (core keys a repeat\n * `${testUid}-retry${n}`), so each attempt becomes its own artifact.\n */\nexport function recordTestSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string,\n testUid: string\n): void {\n if (!sliceActive(ctx)) {\n return\n }\n recordSliceBoundary(boundaryContext(ctx), 'test', specFile, testUid)\n}\n\n/**\n * Flush the current test's slice at test/scenario end — before a retry can\n * overwrite its outcome in the suite tree. Fire-and-forget; the finalize pass\n * is the safety net for any range this misses. No-op outside trace + `test`.\n */\nexport function flushTestSlice(ctx: TestSliceCtx): void {\n if (!sliceActive(ctx)) {\n return\n }\n const range = findFlushableRange(ctx.specRanges)\n if (!range) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(range)\n}\n","/**\n * Test (Mocha/Jasmine-style) lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to keep the per-test orchestration distinct from the cucumber path.\n *\n * The plugin passes itself as a `TestLifecycleCtx` — a narrow interface that\n * exposes only the fields and methods these helpers need.\n */\n\nimport logger from '@wdio/logger'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchTestCase,\n NightwatchTestResults,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { DEFAULTS, TIMING, TEST_STATE } from './constants.js'\nimport { resolveSpecFilePath } from './helpers/specFileResolver.js'\nimport { closePreviousTest } from './helpers/closePreviousTest.js'\nimport { matchTestName } from './helpers/runningTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\nimport { recordTestSliceBoundary, type TestSliceCtx } from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx extends TestSliceCtx {\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy\n readonly srcFolders: string[]\n isScriptInjected: boolean\n getRerunLabel(): string | undefined\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n setCurrentTest(t: unknown): void\n recordAttempt(uid: string, specFile?: string): number\n}\n\ninterface SuiteMetadata {\n testFile: string\n fullPath: string | null\n suiteTitle: string\n testNames: string[]\n suiteLine: number | null\n testLines: number[]\n}\n\nexport function resolveSuiteMetadata(\n ctx: TestLifecycleCtx,\n currentTest: NightwatchCurrentTest\n): SuiteMetadata {\n const moduleName = currentTest.module ?? ''\n const testFile =\n moduleName.split('/').pop() || moduleName || DEFAULTS.FILE_NAME\n const fullPath = resolveSpecFilePath(\n testFile,\n moduleName,\n ctx.srcFolders,\n ctx.browserProxy.getCurrentTestFullPath() || undefined\n )\n if (!fullPath) {\n log.warn(\n `[beforeEach] Could not resolve file path for \"${testFile}\" — source view will be unavailable`\n )\n }\n let suiteTitle = testFile\n let testNames: string[] = []\n let suiteLine: number | null = null\n let testLines: number[] = []\n if (fullPath) {\n const parsed = extractTestMetadata(fullPath)\n if (parsed.suiteTitle) {\n suiteTitle = parsed.suiteTitle\n }\n testNames = parsed.testNames\n suiteLine = parsed.suiteLine\n testLines = parsed.testLines\n }\n const rerunLabel = ctx.getRerunLabel()\n if (rerunLabel) {\n const targetIndex = testNames.findIndex((name) => name === rerunLabel)\n if (targetIndex !== -1) {\n testNames = [testNames[targetIndex]]\n testLines = testLines[targetIndex] ? [testLines[targetIndex]] : []\n }\n }\n return { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines }\n}\n\nexport function pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n): string | undefined {\n return (\n matchTestName(currentTest?.name, testNames) ||\n testNames.find((name) => !processedTests.has(name))\n )\n}\n\nexport async function startNextTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>,\n specFile: string | null\n): Promise<void> {\n if (processedTests.size === 0) {\n ctx.suiteManager.markSuiteAsRunning(currentSuite)\n }\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n // Nightwatch has no per-test retry index; the tracker is the retry signal.\n test.retries = ctx.recordAttempt(test.uid, specFile ?? undefined)\n if (specFile) {\n recordTestSliceBoundary(ctx, specFile, test.uid)\n }\n test.state = TEST_STATE.RUNNING as TestStats['state']\n test.start = new Date()\n test.end = null\n ctx.testReporter.onTestStart(test)\n ctx.setCurrentTest(test)\n log.info(` ▶ ${currentTestName}`)\n await new Promise((resolve) => setTimeout(resolve, TIMING.TEST_START_DELAY))\n } else {\n log.warn(\n `Test \"${currentTestName}\" not found in suite \"${currentSuite.title}\"`\n )\n ctx.setCurrentTest(null)\n }\n}\n\nexport async function closePreviousRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n): Promise<void> {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest) {\n return\n }\n await closePreviousTest({\n runningTest,\n testFile,\n testcases: currentTest?.results?.testcases || {},\n testManager: ctx.testManager,\n incrementCount: (state) => ctx.incrementCount(state),\n testIcon: (state) => ctx.testIcon(state)\n })\n}\n\nexport function wrapBrowserOnce(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): void {\n if (!ctx.isScriptInjected) {\n ctx.browserProxy.wrapUrlMethod(browser)\n ctx.isScriptInjected = true\n }\n ctx.browserProxy.resetTestTracking()\n ctx.browserProxy.wrapBrowserCommands(browser)\n}\n\nfunction closeUnreportedRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n results: NightwatchTestResults,\n processedTests: Set<string>\n): void {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest || processedTests.has(runningTest.title)) {\n return\n }\n const failed = (results.errors ?? 0) > 0 || (results.failed ?? 0) > 0\n const testState: TestStats['state'] = failed\n ? TEST_STATE.FAILED\n : TEST_STATE.PASSED\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n ctx.testManager.updateTestState(runningTest, testState, endTime, duration)\n ctx.testManager.markTestAsProcessed(testFile, runningTest.title)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n}\n\nasync function closeReportedTestcases(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n testcases: Record<string, NightwatchTestCase>,\n processedTests: Set<string>\n): Promise<void> {\n const testcaseNames = Object.keys(testcases)\n const unprocessedTests = testcaseNames.filter(\n (name) => !processedTests.has(name)\n )\n for (const currentTestName of unprocessedTests) {\n const testcase = testcases[currentTestName]\n const testState = determineTestState(testcase)\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n const dur = parseFloat(testcase.time || '0') * 1000\n ctx.testManager.updateTestState(test, testState, new Date(), dur)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${currentTestName} (${(dur / 1000).toFixed(2)}s)`)\n }\n ctx.testManager.markTestAsProcessed(testFile, currentTestName)\n }\n if (processedTests.size === testcaseNames.length) {\n ctx.suiteManager.finalizeSuite(currentSuite)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n }\n}\n\nexport async function closeOutTestcases(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentTest = (browser.currentTest ?? {}) as NightwatchCurrentTest\n const results: NightwatchTestResults = currentTest.results ?? {}\n const moduleName = currentTest.module ?? ''\n const testFile = moduleName.split('/').pop() || DEFAULTS.FILE_NAME\n const testcases = results.testcases ?? {}\n const currentSuite = ctx.suiteManager.getSuite(testFile)\n if (!currentSuite) {\n return\n }\n const processedTests = ctx.testManager.getProcessedTests(testFile)\n if (Object.keys(testcases).length === 0) {\n closeUnreportedRunningTest(\n ctx,\n currentSuite,\n testFile,\n results,\n processedTests\n )\n } else {\n await closeReportedTestcases(\n ctx,\n currentSuite,\n testFile,\n testcases,\n processedTests\n )\n }\n}\n","import fs from 'fs'\nimport path from 'node:path'\nimport { findTestFileFromStack } from './utils.js'\n\n/**\n * Resolve a Nightwatch test's `currentTest.module` to an absolute spec-file\n * path on disk. Priority:\n * 1. Walk the runtime stack for a user frame.\n * 2. A cached path from a previous command on the same browser (browserProxy).\n * 3. Cartesian search across the user's `src_folders` + cwd fallbacks.\n *\n * Used by `beforeEach` to find the file that `extractTestMetadata` should\n * parse for test names + suite/test line numbers. Returns `null` when the\n * file can't be located on disk (source view falls back to \"unavailable\").\n */\nexport function resolveSpecFilePath(\n testFile: string,\n modulePath: string | undefined,\n srcFolders: string[],\n cachedPath: string | undefined\n): string | null {\n let fullPath: string | null = findTestFileFromStack() || null\n if (!fullPath && cachedPath && cachedPath.includes(testFile)) {\n fullPath = cachedPath\n }\n if (fullPath) {\n return fullPath\n }\n if (!testFile) {\n return null\n }\n\n const workspaceRoot = process.cwd()\n // `currentTest.module` is relative to a src_folder, e.g. `basic/ecosia`.\n // We try each src_folder + cwd-level fallback. Use `path.resolve` (not\n // `path.join`) so absolute src_folders entries — like\n // `path.resolve(__dirname, 'tests')` from a nightwatch.conf.cjs living\n // outside the package — bypass `workspaceRoot` correctly.\n const normalized = (modulePath || '').replace(/\\\\/g, '/')\n const srcFolderPaths = srcFolders.flatMap((sf) =>\n normalized\n ? [\n path.resolve(workspaceRoot, sf, normalized + '.js'),\n path.resolve(workspaceRoot, sf, normalized + '.ts'),\n path.resolve(workspaceRoot, sf, normalized + '.cjs'),\n path.resolve(workspaceRoot, sf, normalized)\n ]\n : []\n )\n const possiblePaths = [\n ...srcFolderPaths,\n // Treat module path as relative to cwd (works when src_folders isn't nested)\n ...(normalized\n ? [\n path.resolve(workspaceRoot, normalized + '.js'),\n path.resolve(workspaceRoot, normalized + '.ts'),\n path.resolve(workspaceRoot, normalized + '.cjs'),\n path.resolve(workspaceRoot, normalized)\n ]\n : []),\n path.resolve(workspaceRoot, 'tests', testFile + '.js'),\n path.resolve(workspaceRoot, 'test', testFile + '.js'),\n path.resolve(workspaceRoot, testFile + '.js')\n ]\n\n for (const candidate of possiblePaths) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n","import logger from '@wdio/logger'\nimport { TIMING, TEST_STATE } from '../constants.js'\nimport type { NightwatchTestCase, TestStats } from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestManager } from './testManager.js'\n\nconst log = logger('@wdio/nightwatch-devtools:closePreviousTest')\n\nexport interface ClosePreviousTestInput {\n runningTest: TestStats\n testFile: string\n /** `currentTest?.results?.testcases || {}` — Nightwatch's per-suite testcase results. */\n testcases: Record<string, NightwatchTestCase>\n testManager: TestManager\n /** Plugin-side pass/fail/skip counter — called once with the resolved final state. */\n incrementCount: (state: TestStats['state']) => void\n /** Plugin-side icon resolver for the log line (✅/❌/⏭ etc.). */\n testIcon: (state: TestStats['state']) => string\n}\n\n/**\n * Close out the previously-running test when beforeEach fires for the next\n * one. Resolves the final state from Nightwatch's testcases bag if available\n * (preferred — has real timing), otherwise assumes PASSED (the runner only\n * advances to beforeEach if the prior test didn't throw). Returns nothing —\n * pure side-effect orchestration over the test, testManager, and the plugin's\n * counter / icon helpers.\n *\n * Extracted from `NightwatchDevToolsPlugin.beforeEach` — the ~37-line block\n * was the second-densest section of the 142-line method.\n */\nexport async function closePreviousTest(\n input: ClosePreviousTestInput\n): Promise<void> {\n const {\n runningTest,\n testFile,\n testcases,\n testManager,\n incrementCount,\n testIcon\n } = input\n\n if (testcases[runningTest.title]) {\n const testcase = testcases[runningTest.title]\n const testState = determineTestState(testcase)\n runningTest.state = testState\n runningTest.end = new Date()\n runningTest._duration = parseFloat(testcase.time || '0') * 1000\n testManager.updateTestState(runningTest, testState)\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(testState)\n log.info(\n ` ${testIcon(testState)} ${runningTest.title} (${(runningTest._duration / 1000).toFixed(2)}s)`\n )\n } else {\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n testManager.updateTestState(\n runningTest,\n TEST_STATE.PASSED as TestStats['state'],\n endTime,\n duration\n )\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(TEST_STATE.PASSED as TestStats['state'])\n log.info(` ✅ ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n }\n // Brief settle so the dashboard renders the terminal state before the next\n // test's \"running\" update arrives.\n await new Promise((resolve) => setTimeout(resolve, TIMING.UI_RENDER_DELAY))\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,SAAS,qBAAqB;;;ACDvB,IAAM,eAAe;;;ACQrB,IAAM,cAAc;AAAA;AAAA,EAEzB,iBAAiB;AAAA;AAAA,EAEjB,kBAAkB;AAAA;AAAA,EAElB,sBAAsB;AAAA;AAAA,EAEtB,oBAAoB;AAAA;AAAA,EAEpB,yBAAyB;AAAA;AAAA,EAEzB,uBAAuB;AACzB;AAEO,IAAM,gBAAgB;AAAA;AAAA,EAE3B,KAAK;AAAA;AAAA,EAEL,SAAS;AAAA;AAAA,EAET,UAAU;AACZ;;;ACSO,SAAS,kBACd,OACAA,YACoB;AACpB,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,QAAQ,MAAM;AAAA,IACd,UAAU,MAAM;AAAA,IAChB,iBAAiB,MAAM,SAAS;AAAA,IAChC,WAAAA;AAAA,IACA,cAAc,yBAAyB,MAAM,MAAM;AAAA,IACnD,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,IACf,kBAAkB,MAAM;AAAA,IACxB,kBAAkB,MAChB,wBAAwB;AAAA,MACtB,cAAc,MAAM;AAAA,MACpB,YAAY,MAAM;AAAA,IACpB,CAAC;AAAA,IACH,cAAc,CAAC,GAAG,MAAM,SAAS,kBAAkB,GAAG,MAAM,YAAY;AAAA,IACxE,iBAAiB,MAAM,SAAS;AAAA;AAAA;AAAA,IAGhC,sBAAsB;AAAA;AAAA;AAAA,IAGtB,cAAc,MAAM,yBAAyB;AAAA,IAC7C,oBAAoB,MAAM;AAAA,IAC1B,YAAY,CAAC,MAAM,MAAM,UAAU,KAAK,CAAC;AAAA,IACzC,KAAK,MAAM;AAAA,EACb;AACF;;;ACvBA,eAAsB,kBACpB,OACe;AACf,QAAM,2BAA2B;AAAA,IAC/B,MAAM,MAAM;AAAA,IACZ,aAAa,MAAM;AAAA,IACnB,QAAQ,MAAM;AAAA,IACd,QAAQ,MAAM;AAAA,IACd,kBAAkB;AAAA,MAChB,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,WAAW,MAAM;AAAA,IACjB,SAAS,MAAM;AAAA,IACf,QAAQ;AAAA,IACR,YAAY,MAAM;AAAA,IAClB,OAAO,MAAM;AAAA,EACf,CAAC;AACD,QAAM,sBAAsB;AAAA,IAC1B,MAAM,MAAM;AAAA,IACZ,aAAa,MAAM;AAAA,IACnB,QAAQ,MAAM;AAAA,IACd,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,UAAU,MAAM;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,WAAW,MAAM;AAAA,IACjB,SAAS,MAAM;AAAA,IACf,WAAW,MAAM;AAAA,IACjB,eAAe,MAAM;AAAA,IACrB,QAAQ;AAAA,IACR,YAAY,MAAM;AAAA,IAClB,OAAO,MAAM;AAAA,EACf,CAAC;AACH;;;ACvFA,OAAO,YAAY;AASnB,IAAM,MAAM,OAAO,yCAAyC;AAMrD,SAAS,kBACd,aACA,YACM;AACN;AAAA,IACE,CAAC,QAAQ,cAAc,YAAY,GAAG,WAAW,GAAG,GAAG;AAAA,IACvD,CAAC,OAAO,YAAY,IAAI,KAAK,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,IAItC,CAAC,SAAS,4BAA4B,IAAI;AAAA,EAC5C;AACF;AAEA,SAAS,cACP,UACA,SACA,KACM;AACN,MAAI,CAAC,UAAU;AACb;AAAA,EACF;AACA,WACG,eAAe,IAAI,SAAS,IAAI,MAAM,IAAI,QAAQ,IAAI,OAAO;AAAA,IAC5D;AAAA,IACA,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA;AAAA;AAAA,IAGf,UAAU,IAAI;AAAA,EAChB,CAAC,EACA;AAAA,IAAM,CAAC,QACN,IAAI,KAAK,qBAAqB,IAAI,OAAO,KAAK,aAAa,GAAG,CAAC,EAAE;AAAA,EACnE;AAEF,QAAM,OAAO,SAAS,YAAY,SAAS,YAAY,SAAS,CAAC;AACjE,MAAI,MAAM;AACR,aAAS,YAAY,IAAI;AAAA,EAC3B;AACF;;;ALpCA,SAAS,QAAQ,mBAAmB;AAMpC,OAAOC,cAAY;;;AMpBnB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAY,QAAQ;AACpB,OAAOC,aAAY;AACnB,SAAS,cAAc;AAGvB,SAAS,OAAO,YAAY;;;ACLrB,IAAM,oBAAoB;;;ACP1B,IAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAaO,IAAM,WAAW;AAAA,EACtB,GAAG;AAAA,EACH,WAAW;AAAA,EACX,WAAW;AACb;AAEO,IAAM,SAAS;AAAA,EACpB,GAAG;AAAA;AAAA,EAEH,oBAAoB;AACtB;AAOO,IAAM,0BACX;AAEK,IAAM,sBAAsB,CAAC,OAAO,YAAY,YAAY;AAU5D,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AClFA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,SAAS,uBAAuB;AAyBlC,SAAS,mBACd,UACiC;AACjC,MAAI,SAAS,WAAW,KAAK,SAAS,WAAW,GAAG;AAClD,WAAO;AAAA,EACT;AACA,SAAO,SAAS,SAAS,KAAK,SAAS,WAAW,IAAI,WAAW;AACnE;AAEO,SAAS,YAAY,OAAmC;AAC7D,SAAO,UAAU,WAAW,WAAM,UAAU,YAAY,WAAM;AAChE;AAEO,SAAS,kBACd,UACA,OACM;AACN,MAAI,UAAU,UAAU;AACtB,aAAS;AAAA,EACX,WAAW,UAAU,WAAW;AAC9B,aAAS;AAAA,EACX,OAAO;AACL,aAAS;AAAA,EACX;AACF;AAKO,SAAS,mBAAmB,kBAAyC;AAC1E,SAAO,mBAAmB,wBAAwB;AACpD;AAEO,SAAS,2BAA2B,OAGxC;AACD,SAAO;AAAA,IACL,WAAW,mBAAmB,MAAM,gBAAgB;AAAA,IACpD,YAAY,MAAM;AAAA,IAClB,SAAS,QAAQ,IAAI;AAAA,IACrB,iBAAiB;AAAA,MACf,cAAc;AAAA,MACd,aAAa,CAAC,MAAM;AAAA,MACpB,WAAW;AAAA,IACb;AAAA,EACF;AACF;AASO,SAASC,mBACd,YACA,MACQ;AACR,MAAI,MAAc;AAClB,MACE,OAAO,eAAe,YACtB,eAAe,QACf,SAAS,QACT;AACA,WAAO,WAAW,QAAQ;AAC1B,eAAW,OAAO,WAAW,aAAa,WAAW,KAAK;AAAA,EAC5D,OAAO;AACL,WAAQ,cAAyB;AACjC,eAAW,OAAO,QAAQ,EAAE;AAAA,EAC9B;AACA,SAAO,kBAA4B,MAAM,QAAQ;AACnD;AAMO,SAAS,wBAA4C;AAC1D,QAAM,QAAQ,IAAI,MAAM,EAAE;AAC1B,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAMA,QAAM,SAAS,gBAAgB,KAAK;AACpC,QAAM,YAAY,OAAO;AAAA,IACvB,CAAC,MAAM,gBAAgB,CAAC,KAAK,aAAkB,KAAK,EAAE,IAAI;AAAA,EAC5D;AACA,QAAM,QAAQ,aAAa,OAAO,KAAK,eAAe;AACtD,MAAI,CAAC,OAAO,MAAM;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,kBAAkB,MAAM,IAAI;AAC7C,SAAU,cAAW,QAAQ,IAAI,WAAW;AAC9C;AAOO,SAAS,oBAAoB,UAAoC;AACtE,QAAM,OAAO,oBAAoB,UAAU;AAAA,IACzC,8BAA8B;AAAA,EAChC,CAAC;AACD,QAAM,aAAa,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AACtD,QAAM,QAAQ,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM;AAClD,SAAO;AAAA,IACL,YAAY,YAAY,SAAS;AAAA,IACjC,WAAW,YAAY,QAAQ;AAAA,IAC/B,WAAW,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,IACnC,WAAW,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACpC;AACF;AAwEA,SAAS,0BACP,OACA,eACA,WACiD;AACjD,QAAM,SAAS;AACf,QAAM,YAAsB,CAAC;AAC7B,QAAM,eAAyB,CAAC;AAChC,WACM,IAAI,gBAAgB,GACxB,IAAI,MAAM,UAAU,UAAU,SAAS,WACvC,KACA;AACA,QAAI,8BAA8B,KAAK,MAAM,CAAC,CAAC,GAAG;AAChD;AAAA,IACF;AACA,UAAM,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC;AAC9B,QAAI,GAAG;AACL,gBAAU,KAAK,IAAI,CAAC;AACpB,mBAAa,KAAK,EAAE,CAAC,CAAC;AAAA,IACxB;AAAA,EACF;AACA,SAAO,EAAE,WAAW,aAAa;AACnC;AAEO,SAAS,sBACd,gBACA,cACA,WAMA;AACA,QAAM,YAAY,UAAU;AAC5B,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,WAAW,CAAC;AAAA,MACZ,cAAc,MAAc,SAAS,EAAE,KAAK,EAAE;AAAA,IAChD;AAAA,EACF;AACA,QAAM,QAAQ,eAAe,MAAM,IAAI;AACvC,MAAI,cAAc;AAClB,MAAI,eAAe;AACnB,MAAI,YAAsB,CAAC;AAC3B,MAAI,eAAyB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,UAAU,IAAI;AACpB,QAAI,gBAAgB,KAAK,gBAAgB,KAAK,IAAI,GAAG;AACnD,oBAAc;AACd;AAAA,IACF;AACA,QAAI,iBAAiB,KAAK,IAAI,KAAK,KAAK,SAAS,YAAY,GAAG;AAC9D,qBAAe;AACf,YAAM,YAAY,0BAA0B,OAAO,GAAG,SAAS;AAC/D,kBAAY,UAAU;AACtB,qBAAe,UAAU;AACzB;AAAA,IACF;AAAA,EACF;AACA,SAAO,aAAa,SAAS,WAAW;AACtC,iBAAa,KAAK,EAAE;AAAA,EACtB;AACA,SAAO,EAAE,aAAa,cAAc,WAAW,aAAa;AAC9D;AAGA,IAAM,yBAAiD;AAAA,EACrD,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AACR;AAOA,SAAS,0BAA0B,SAAiB,MAAuB;AACzE,MAAI,YAAY,MAAM;AACpB,WAAO;AAAA,EACT;AACA,QAAM,cAAc,QACjB,MAAM,aAAa,EACnB,IAAI,CAAC,MAAM,QAAQ;AAClB,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,OAAO,KAAK,MAAM,GAAG,EAAE;AAC7B,aAAO,uBAAuB,IAAI,KAAK;AAAA,IACzC;AACA,WAAO,KAAK,QAAQ,uBAAuB,MAAM;AAAA,EACnD,CAAC,EACA,KAAK,EAAE;AACV,MAAI;AACF,WAAO,IAAI,OAAO,IAAI,WAAW,GAAG,EAAE,KAAK,IAAI;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,uBACd,cACA,UACqB;AACrB,QAAM,YAAY;AAElB,aAAW,EAAE,UAAU,QAAQ,KAAK,cAAc;AAChD,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,CAAC,UAAU,KAAK,IAAI,GAAG;AACzB;AAAA,MACF;AAGA,YAAM,eAAe,KAAK;AAAA,QACxB;AAAA,MACF;AACA,UAAI,cAAc;AAChB,YAAI,0BAA0B,aAAa,CAAC,GAAG,QAAQ,GAAG;AACxD,iBAAO,EAAE,UAAU,MAAM,IAAI,EAAE;AAAA,QACjC;AACA;AAAA,MACF;AAGA,YAAM,aAAa,KAAK;AAAA,QACtB;AAAA,MACF;AACA,UAAI,YAAY;AACd,YAAI;AACF,cAAI,IAAI,OAAO,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,KAAK,QAAQ,GAAG;AAC3D,mBAAO,EAAE,UAAU,MAAM,IAAI,EAAE;AAAA,UACjC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,0BAA8C;AAG5D,QAAM,gBAAgB,QAAQ,KAAK;AAAA,IACjC,CAAC,QAAQ,QAAQ,cAAc,QAAQ;AAAA,EACzC;AACA,MAAI,kBAAkB,MAAM,QAAQ,KAAK,gBAAgB,CAAC,GAAG;AAC3D,UAAM,aAAa,QAAQ,KAAK,gBAAgB,CAAC;AACjD,UAAM,WAAgB,gBAAW,UAAU,IACvC,aACK,aAAQ,QAAQ,IAAI,GAAG,UAAU;AAC1C,QAAO,cAAW,QAAQ,GAAG;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,MAAM,QAAQ,IAAI;AACtB,QAAM,OAAY,WAAM,GAAG,EAAE;AAC7B,SAAO,OAAO,QAAQ,MAAM;AAC1B,eAAW,QAAQ,kBAAkB;AACnC,YAAM,YAAiB,UAAK,KAAK,IAAI;AACrC,UAAO,cAAW,SAAS,GAAG;AAC5B,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAc,aAAQ,GAAG;AAC/B,QAAI,WAAW,KAAK;AAClB;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACA,SAAO;AACT;;;AHxXA,IAAMC,OAAMC,QAAO,yCAAyC;AAgBrD,SAAS,gBAAgB,KAA4B;AAC1D,MAAI,QAAQ,WAAW,QAAQ,IAAI,UAAU,IAAI;AACjD,MAAI,QAAQ,OAAO,OAAO,QAAQ,IAAI,UAAU,IAAI,CAAC;AACrD,EAAAD,KAAI;AAAA,IACF,uCAAkC,IAAI,QAAQ,QAAQ,IAAI,IAAI,QAAQ,IAAI;AAAA,EAC5E;AAGA,MAAI,IAAI,cAAc;AACpB,QAAI,mBAAmB;AACvB,QAAI,YAAY;AAChB,QAAI,YAAY;AAChB,QAAI,YAAY;AAChB,IAAAA,KAAI,KAAK,kCAAkC;AAAA,EAC7C;AACF;AAUA,eAAsB,gBAAgB,KAAqC;AAGzE,QAAM,UACJ,QAAQ,IAAI,UAAU,KAAK,MAAM,OACjC,CAAC,CAAC,QAAQ,IAAI,UAAU,IAAI,KAC5B,CAAC,CAAC,QAAQ,IAAI,UAAU,IAAI;AAC9B,MAAI,SAAS;AACX,QAAI,YAAY;AAAA,EAClB;AACA,QAAM,aAAa,wBAAwB;AAC3C,MAAI,cAAc,UAAU;AAC5B,MAAI,YAAY;AACd,IAAAA,KAAI,KAAK,kBAAa,UAAU,EAAE;AAAA,EACpC,OAAO;AACL,IAAAA,KAAI;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS;AACX;AAAC,IAAC,WAAuC,iBAAiB,IAAI,IAAI;AAClE;AAAA,EACF;AACA,MAAI;AAIF,QAAI,IAAI,QAAQ,SAAS,SAAS;AAChC,MAAAA,KAAI,KAAK,4DAAuD;AAAA,IAClE,OAAO;AACL,UAAI,QAAQ,OAAO,MAAM;AAAA,QACvB,IAAI,QAAQ;AAAA,QACZ,IAAI,QAAQ;AAAA,MACd;AACA,MAAAA,KAAI,KAAK,wCAAiC;AAC1C,YAAM,EAAE,KAAK,IAAI,MAAM,MAAM,IAAI,OAAO;AACxC,UAAI,QAAQ,OAAO;AACnB,YAAM,MAAM,UAAU,IAAI,QAAQ,QAAQ,IAAI,IAAI,QAAQ,IAAI;AAC9D,MAAAA,KAAI,KAAK,kCAA6B,IAAI,QAAQ,IAAI,EAAE;AACxD,MAAAA,KAAI,KAAK,kBAAkB,GAAG,EAAE;AAChC,YAAM,IAAI,sBAAsB,GAAG;AACnC,YAAM,IAAI;AAAA,QAAQ,CAACE,aACjB,WAAWA,UAAS,OAAO,kBAAkB;AAAA,MAC/C;AAAA,IACF;AACA;AAAC,IAAC,WAAuC,iBAAiB,IAAI,IAAI;AAAA,EACpE,SAAS,KAAK;AACZ,IAAAF,KAAI,MAAM,4BAA4B,aAAa,GAAG,CAAC,EAAE;AACzD,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,oBACpB,KACA,KACe;AACf,MAAI;AAEF,QAAI,cAAmB;AAAA,MAClB,UAAO;AAAA,MACV,uBAAuB,IAAI,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC;AAAA,IACvD;AACA,QAAI,CAAI,eAAW,IAAI,WAAW,GAAG;AACnC,MAAG,cAAU,IAAI,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,IACnD;AACA,QAAI,kBAAkB,MAAM,OAAO;AAAA,MACjC,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB,cAAc;AAAA,QACZ,aAAa;AAAA,QACb,sBAAsB;AAAA,UACpB,MAAM;AAAA,YACJ;AAAA,YACA,mBAAmB,IAAI,WAAW;AAAA,YAClC;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA,wBAAwB;AAAA,UACtB,mBAAmB,CAAC,qBAAqB;AAAA,QAC3C;AAAA,MACF;AAAA,IACF,CAAC;AACD,UAAM,IAAI,gBAAgB,IAAI,GAAG;AAAA,EACnC,SAAS,KAAK;AACZ,IAAAA,KAAI,MAAM,+BAA+B,aAAa,GAAG,CAAC,EAAE;AAC5D,IAAAA,KAAI,KAAK,yBAAyB,GAAG,EAAE;AAAA,EACzC;AACF;AAEA,eAAsB,kBACpB,KACA,SACe;AACf,QAAM,cAAc,SAAS;AAC7B,QAAM,YAAY,aAAa,SAAS,aAAa,CAAC;AACtD,aAAW,CAAC,EAAE,KAAK,MACjB,IAAI,cAAc,aAAa,KAAK,oBAAI,IAAI,GAC5C,QAAQ,GAAG;AACX,QAAI,YAAY,mBAAmB,OAAO,SAAS;AACnD,UAAM,IAAI;AAAA,MAAQ,CAACE,aACjB,WAAWA,UAAS,OAAO,oBAAoB;AAAA,IACjD;AACA,QAAI,cAAc,cAAc,KAAK;AAAA,EACvC;AACA,QAAM,IAAI;AAAA,IAAQ,CAACA,aACjB,WAAWA,UAAS,OAAO,oBAAoB;AAAA,EACjD;AACF;AAEO,SAAS,cAAc,KAA4B;AACxD,QAAM,UAAU;AAAA,IACd,IAAI,YAAY,IAAI,GAAG,IAAI,SAAS,YAAY;AAAA,IAChD,IAAI,YAAY,IAAI,GAAG,IAAI,SAAS,YAAY;AAAA,IAChD,IAAI,YAAY,IAAI,GAAG,IAAI,SAAS,aAAa;AAAA,EACnD,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,EAAAF,KAAI,KAAK,GAAG,IAAI,YAAY,IAAI,WAAM,QAAG,qBAAqB,OAAO,EAAE;AACvE,EAAAA,KAAI,KAAK,0BAA0B,IAAI,QAAQ,QAAQ,IAAI,IAAI,QAAQ,IAAI,EAAE;AAC/E;AAEA,eAAsB,4BACpB,KACe;AACf,MAAI,CAAC,IAAI,iBAAiB;AACxB;AAAA,EACF;AACA;AAAC,EAACC,QAA2D;AAAA,IAC3D;AAAA,IACA;AAAA,EACF;AACA,MAAI,eAAe;AACnB,QAAM,gBAAgB,MAAM;AAC1B,mBAAe;AACf,IAAAD,KAAI,KAAK,qDAAgD;AACzD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,KAAK,UAAU,aAAa;AACpC,UAAQ,KAAK,WAAW,aAAa;AACrC,SAAO,MAAM;AACX,QAAI;AACF,YAAM,IAAI,gBAAgB,SAAS;AACnC,YAAM,IAAI,QAAQ,CAAC,QAAQ,WAAW,KAAK,OAAO,qBAAqB,CAAC;AAAA,IAC1E,QAAQ;AACN,UAAI,CAAC,cAAc;AACjB,QAAAA,KAAI,KAAK,8CAA8C;AACvD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,cAAc;AAChB;AAAA,EACF;AACA,UAAQ,eAAe,UAAU,aAAa;AAC9C,UAAQ,eAAe,WAAW,aAAa;AAC9C,EAACC,QAA2D;AAAA,IAC3D;AAAA,IACA;AAAA,EACF;AACA,MAAI;AACF,UAAM,IAAI,gBAAgB,cAAc;AAAA,EAC1C,QAAQ;AAAA,EAER;AACA,QAAM,KAAK;AACX,UAAQ,KAAK,CAAC;AAChB;;;AIjOA,OAAOE,cAAY;;;ACPnB,OAAOC,aAAY;AAQnB,IAAMC,OAAMC,QAAO,yCAAyC;AAI5D,IAAM,UAAU,CAAC,KAAc,QAC7B,OAAO,OAAO,QAAQ,WAAY,IAAiB,GAAG,IAAI;AAE5D,IAAM,UAAU,CAAC,QAAiBC,UAChCA,MAAK,OAAgB,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAG;AAEvD,IAAM,eAAe,CAAC,QAAiB,SAA4B;AACjE,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,WAAO;AAAA,EACT;AACA,QAAM,MAAM;AACZ,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,MAAM,UAAa,MAAM,MAAM;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,wBAAwB,SAGtC;AACA,QAAM,oBACJ,QAAQ,SAAS,aAAa,YAAY,WAAW,KACrD,QAAQ,SAAS,SAAS,aAAa,YAAY,WAAW,KAC9D;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,KACA,CAAC;AACH,QAAM,OAAO,QAAQ,SAAS,SAAS,KAAK,CAAC;AAC7C,QAAM,qBACJ,QAAQ,SAAS,sBAAsB,UAAU,KACjD,QAAQ,SAAS,WAAW,sBAAsB,UAAU,KAC5D,CAAC;AACH,QAAM,aAAa;AAAA,IACjB,aAAa,mBAAmB,QAAQ,gBAAgB,KACtD,aAAa,QAAQ,MAAM,WAAW,GAAG,MAAM,KAC/C,aAAa,QAAQ,oBAAoB,WAAW,GAAG,MAAM,KAC7D;AAAA,EACJ;AACA,QAAM,aAAa;AAAA,IACjB,aAAa,mBAAmB,MAAM,KACpC,aAAa,QAAQ,MAAM,WAAW,GAAG,MAAM,KAC/C,aAAa,QAAQ,oBAAoB,WAAW,GAAG,MAAM,KAC7D;AAAA,EACJ;AACA,SAAO,EAAE,YAAY,WAAW;AAClC;AAIA,SAAS,QAAQ,SAA8C;AAC7D,QAAM,EAAE,YAAY,WAAW,IAAI,wBAAwB,OAAO;AAClE,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,CAAC,YAAoBF,KAAI,KAAK,OAAO;AAAA,EAC/C;AACF;AAEA,SAAS,UAAU,SAAgD;AACjE,SAAQ,QAA8C;AACxD;AAGO,SAASG,cACd,SACAD,OACmB;AACnB,QAAM,KAAK,UAAU,OAAO;AAC5B,SAAO,KAAK,aAAW,QAAQ,OAAO,GAAG,IAAIA,KAAI,IAAI,QAAQ,QAAQ,IAAI;AAC3E;AAGO,SAASE,eACd,SACAF,OACA,MACmB;AACnB,QAAM,KAAK,UAAU,OAAO;AAC5B,SAAO,KACH,cAAY,QAAQ,OAAO,GAAG,IAAIA,OAAM,IAAI,IAC5C,QAAQ,QAAQ,IAAI;AAC1B;AAIO,SAAS,iBACd,SACA,MACmB;AACnB,SAAOE,eAAiB,SAAS,gBAAgB,EAAE,QAAQ,MAAM,MAAM,CAAC,EAAE,CAAC;AAC7E;;;AC5HA,OAAOC,aAAY;;;ACcZ,SAASC,uBACd,SACA,SACA,WACA,QACA,SAAS,OACuB;AAKhC,SAAO,sBAAY;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,MAAMC,cAAqB,SAAS,YAAY;AAAA,IAChE,GAAI,SACA,CAAC,IACD;AAAA,MACE,WAAW,CAAC,QACV,iBAAiB,SAAS,WAAW,GAAG,GAAG;AAAA,MAC7C,QAAQ,MAAMA,cAAqB,SAAS,KAAK;AAAA,MACjD,UAAU,MAAMA,cAAqB,SAAS,OAAO;AAAA,IACvD;AAAA,EACN,CAAC;AACH;;;ACyBA,SAAS,sBAAsB,GAAiB,UAA6B;AAC3E,QAAM,kBAA0C,CAAC;AACjD,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,WAAW,CAAC,CAAC,GAAG;AAC3D,oBAAgB,EAAE,YAAY,CAAC,IAAI,OAAO,CAAC;AAAA,EAC7C;AACA,IAAE,SAAS,SAAS;AACpB,IAAE,aAAa,SAAS;AACxB,IAAE,kBAAkB;AACpB,IAAE,WAAW,SAAS;AACtB,IAAE,OAAO,eAAe,EAAE,KAAK,SAAS,QAAQ;AAClD;AAMA,SAAS,YAAY,QAAwB,OAA6B;AACxE,SAAO,OAAO,OAAO,cAAc,WAC/B,KAAK,MAAM,OAAO,YAAY,GAAI,IAClC,MAAM;AACZ;AAEA,SAAS,oBACP,QACA,OACA,SACM;AACN,QAAM,EAAE,WAAW,SAAS,KAAK,UAAU,IAAI;AAC/C,MAAI,CAAC,OAAO,OAAO,cAAc,UAAU;AACzC;AAAA,EACF;AACA,QAAM,YAAY,YAAY,QAAQ,KAAK;AAC3C,UAAQ,IAAI,WAAW;AAAA,IACrB,IAAI,GAAG,MAAM,SAAS,IAAI,SAAS;AAAA,IACnC,KAAK,IAAI;AAAA,IACT,QAAQ,IAAI;AAAA,IACZ,gBAAgB,IAAI,WAAW,CAAC;AAAA,IAChC,WAAW;AAAA,IACX;AAAA,EACF,CAAC;AACH;AAEA,SAAS,mBACP,QACA,QACA,OACA,SACA,WACM;AACN,MAAI,WAAW,6BAA6B;AAC1C,wBAAoB,QAAQ,OAAO,OAAO;AAC1C;AAAA,EACF;AACA,MAAI,WAAW,4BAA4B;AACzC,UAAM,IAAI,QAAQ,IAAI,OAAO,SAAS;AACtC,QAAI,KAAK,OAAO,UAAU;AACxB,4BAAsB,GAAG,OAAO,QAAQ;AAAA,IAC1C;AACA;AAAA,EACF;AACA,MAAI,WAAW,2BAA2B;AACxC,UAAM,IAAI,QAAQ,IAAI,OAAO,SAAS;AACtC,QAAI,KAAK,EAAE,WAAW,QAAW;AAC/B,QAAE,OAAO,OAAO;AAChB,QAAE,UAAU,YAAY,QAAQ,KAAK;AACrC,QAAE,OAAO,KAAK,IAAI,GAAG,EAAE,UAAU,EAAE,SAAS;AAC5C,gBAAU,KAAK,EAAE,GAAG,EAAE,CAAC;AACvB,cAAQ,OAAO,OAAO,SAAS;AAAA,IACjC;AACA;AAAA,EACF;AACA,MAAI,WAAW,yBAAyB;AACtC,UAAM,IAAI,QAAQ,IAAI,OAAO,SAAS;AACtC,QAAI,GAAG;AACL,QAAE,QAAQ,OAAO;AACjB,QAAE,UAAU,YAAY,QAAQ,KAAK;AACrC,QAAE,OAAO,KAAK,IAAI,GAAG,EAAE,UAAU,EAAE,SAAS;AAC5C,gBAAU,KAAK,EAAE,GAAG,EAAE,CAAC;AACvB,cAAQ,OAAO,OAAO,SAAS;AAAA,IACjC;AAAA,EACF;AACF;AAEO,SAAS,yBAAyB,MAAsC;AAC7E,QAAM,UAAU,oBAAI,IAA0B;AAC9C,QAAM,YAA4B,CAAC;AACnC,aAAW,SAAS,MAAM;AACxB,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,MAAM,OAAO;AAAA,IACnC,QAAQ;AACN;AAAA,IACF;AAIA,UAAM,UAAW,QAAoC;AACrD,UAAM,SAAS,SAAS;AACxB,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,UAAU,CAAC,QAAQ;AACtB;AAAA,IACF;AACA,uBAAmB,QAAQ,QAAQ,OAAO,SAAS,SAAS;AAAA,EAC9D;AACA,SAAO;AACT;AASO,SAAS,sBACd,UACA,UACgB;AAChB,QAAM,YAAY,CAAC,UAAgC;AACjD,QAAI;AACF,YAAM,IAAI,IAAI,IAAI,MAAM,GAAG;AAC3B,aAAO,OAAO,MAAM,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,QAAQ;AAAA,IACrD,QAAQ;AACN,aAAO,OAAO,MAAM,MAAM,IAAI,MAAM,GAAG;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,cAAc,IAAI;AAAA,IACtB,SAAS;AAAA,MAAI,CAAC,MACZ,EAAE,UAAU,SACR,UAAU,CAAC,IACX,MAAM,EAAE,MAAM,IAAI,EAAE,GAAG,IAAI,EAAE,SAAS;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,UAA0B,CAAC;AACjC,QAAM,oBAAoB,oBAAI,IAAoB;AAElD,aAAW,SAAS,UAAU;AAC5B,QAAI,MAAM,UAAU,QAAW;AAC7B,YAAM,MAAM,UAAU,KAAK;AAC3B,UAAI,YAAY,IAAI,GAAG,GAAG;AACxB;AAAA,MACF;AACA,YAAM,cAAc,kBAAkB,IAAI,GAAG;AAC7C,UAAI,gBAAgB,QAAW;AAC7B,gBAAQ,WAAW,IAAI;AAAA,MACzB,OAAO;AACL,0BAAkB,IAAI,KAAK,QAAQ,MAAM;AACzC,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAAA,IACF,OAAO;AACL,YAAM,MAAM,MAAM,MAAM,MAAM,IAAI,MAAM,GAAG,IAAI,MAAM,SAAS;AAC9D,UAAI,CAAC,YAAY,IAAI,GAAG,GAAG;AACzB,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AF3LA,IAAMC,OAAMC,QAAO,2CAA2C;AAO9D,SAAS,kBAA+B,QAAoB;AAC1D,MAAI,UAAU,OAAO,WAAW,YAAY,WAAW,QAAQ;AAC7D,WAAQ,OAAwB;AAAA,EAClC;AACA,SAAO;AACT;AAEO,IAAM,kBAAN,cAA8B,oBAAoB;AAAA,EACvD;AAAA;AAAA;AAAA,EAIA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAMb,oBAAoB;AAAA;AAAA,EAGpB,YAA0B;AAAA;AAAA;AAAA,EAI1B,SAAuB,mBAAmB,KAAK;AAAA,EACtC,kBAAoC,CAAC;AAAA,EACrC,mBAAoC,CAAC;AAAA,EAE9C,YACE,kBAA0C,CAAC,GAC3C,SACA;AACA,UAAM,eAAe;AACrB,SAAK,WAAW;AAChB,SAAK,aAAa;AAClB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,SAAkC;AAC3C,SAAK,WAAW;AAAA,EAClB;AAAA,EAEmB,WAAiB;AAClC,IAAAD,KAAI,KAAK,8CAAyC;AAAA,EACpD;AAAA,EAEmB,UAAU,KAAoB;AAC/C,IAAAA,KAAI,MAAM,yCAAyC,aAAa,GAAG,CAAC,EAAE;AAAA,EACxE;AAAA,EAEmB,YAAkB;AACnC,IAAAA,KAAI,KAAK,+BAA+B;AAAA,EAC1C;AAAA,EAEA,MAAM,eACJ,SACA,MACA,QACA,OACA,OAA2B,CAAC,GACV;AAElB,UAAM,kBAAkB,eAAe,KAAK;AAE5C,UAAM,YAAY,KAAK;AACvB,UAAM,kBAAiD;AAAA,MACrD,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,MACtC,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,IACjB;AAEA,SAAK,YAAY,KAAK,eAAe;AAErC,UAAM,sBAAsB,oBAAoB;AAAA,MAAK,CAAC,QACpD,QAAQ,YAAY,EAAE,SAAS,IAAI,YAAY,CAAC;AAAA,IAClD;AACA,QAAI,uBAAuB,KAAK,YAAY,CAAC,OAAO;AAClD,WAAK,wBAAwB,iBAAiB,IAAI,EAAE,MAAM,CAAC,QAAQ;AACjE,QAAAA,KAAI,KAAK,uCAAuC,aAAa,GAAG,CAAC,EAAE;AAAA,MACrE,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,OAAO;AACV,WAAK,qBAAqB,SAAS,gBAAgB,SAAS;AAAA,IAC9D;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,SAAiB,WAAyB;AAC7D,QACE,KAAK,cAAc,WACnB,CAAC,KAAK,YACN,CAAC,mBAAmB,OAAO,GAC3B;AACA;AAAA,IACF;AACA,SAAK,iBAAiB;AAAA,MACpBE;AAAA,QACE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MACP,EAAE,KAAK,CAAC,SAAS;AACf,YAAI,MAAM;AACR,gCAAsB,KAAK,iBAAiB,IAAI;AAAA,QAClD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,wBACJ,iBACA,MACA;AAGA,QAAI,KAAK,oBAAoB;AAC3B;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,GAAG,CAAC;AACvD,UAAM,MAAM,MAAM,KAAK,SAAU,QAAQ,0BAA0B;AACnE,UAAM,UAAU;AAAA,MACd;AAAA,IACF;AACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,OAAO,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,IAAI;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,qBAAqB,OAAyB;AAC5C,UAAM,SAAS;AACf,WAAO,MAAM,KAAK;AAClB,WAAO,KAAK,OAAO;AACnB,SAAK,YAAY,KAAK,MAAM;AAS5B,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eACE,OACA,SACA,MACA,QACA,OACA,OAA2B,CAAC,GACoC;AAEhE,UAAM,MAAM,KAAK,YAAY;AAAA,MAC3B,CAAC,MAAO,EAAoC,QAAQ;AAAA,IACtD;AACA,UAAM,eACJ,QAAQ,KAAM,KAAK,YAAY,GAAG,GAAG,aAAa,IAAK;AACzD,QAAI,QAAQ,IAAI;AACd,WAAK,YAAY,OAAO,KAAK,CAAC;AAAA,IAChC;AAEA,SAAK,eAAe,OAAO,KAAK;AAEhC,UAAM,kBAAkB,eAAe,KAAK;AAC5C,UAAM,YAAY,KAAK;AACvB,UAAM,QAAuC;AAAA,MAC3C,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,MACtC,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,IACjB;AACA,SAAK,YAAY,KAAK,KAAK;AAC3B,WAAO,EAAE,OAAO,aAAa;AAAA,EAC/B;AAAA;AAAA;AAAA,EAIQ;AAAA;AAAA;AAAA,EAIR,MAAc,eACZ,SAC6B;AAC7B,WAAO,iBAAyB,SAAS,gCAAgC,EACtE,KAAK,CAAC,WAAW,UAAU,MAAS,EACpC,MAAM,MAAM,MAAS;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,sBAAsB,SAA2C;AAGrE,QAAI,KAAK,oBAAoB;AAC3B;AAAA,IACF;AACA,UAAM,SAAS,KAAK;AACpB,UAAM,WAAW,MAAM;AAAA,MACrB,YAAY;AACV,cAAM,MAAM,MAAM,KAAK,eAAe,OAAO;AAC7C,YAAI,QAAQ,QAAW;AACrB,iBAAO;AAAA,QACT;AACA,aAAK,qBAAqB;AAC1B,eAAO,WAAW,UAAa,QAAQ;AAAA,MACzC;AAAA,MACA,EAAE,UAAU,GAAG,YAAY,IAAI;AAAA,IACjC;AACA,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAgBA,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,YAAY,KAAK,YAAY,SAAS,CAAC,GAAG;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB,SAAoD;AACxE,WAAOC,cAAqB,SAAS,YAAY;AAAA,EACnD;AAAA,EAEmB,kBAAkB,UAAkB,KAAoB;AACzE,IAAAJ,KAAI,KAAK,8BAA8B,QAAQ,KAAK,aAAa,GAAG,CAAC,EAAE;AAAA,EACzE;AAAA,EAEmB,eACjB,OACA,QACA,KACM;AACN,QAAI,WAAW,cAAc;AAC3B,MAAAA,KAAI,KAAK,8BAA8B,KAAK,MAAM,aAAa,GAAG,CAAC,EAAE;AACrE;AAAA,IACF;AACA,QAAI,KAAK,iBAAiB,GAAG;AAC3B,MAAAA,KAAI,KAAK,kDAA6C,KAAK,SAAS;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,SAA4B;AAC7C,QAAI,KAAK,oBAAoB;AAC3B;AAAA,IACF;AACA,QAAI;AAKF,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA,UAAU,0BAA0B;AAAA,MACtC;AACA,UAAI,mBAAmB,MAAM;AAC3B;AAAA,MACF;AACA,YAAM,gBAAgB,MAAM,qBAAqB;AAIjD,YAAM,kBAAkB;AAAA;AAAA,+BAEC,KAAK,UAAU,aAAa,CAAC;AAAA;AAAA;AAAA;AAItD,YAAM,iBAAiB,SAAS,eAAe;AAE/C,YAAM,eAAe,MAAM;AAAA,QACzB,YACG,MAAM;AAAA,UACL;AAAA,UACA,UAAU,0BAA0B;AAAA,QACtC,MAAO;AAAA,MACX;AAEA,UAAI,cAAc;AAChB,QAAAA,KAAI,KAAK,4CAAuC;AAAA,MAClD,OAAO;AACL,QAAAA,KAAI,KAAK,6DAAwD;AAAA,MACnE;AAAA,IACF,SAAS,KAAK;AACZ,MAAAA,KAAI,MAAM,4BAA4B,aAAa,GAAG,CAAC,EAAE;AACzD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAmB,SAA4B;AACnD,QAAI;AACF,YAAM,UAAU,MACd,QACA,OAAO,SAAS;AAClB,YAAM,OACJ,kBAEE,OAAO;AAEX,UAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AAC7C;AAAA,MACF;AAEA,YAAM,UAAU,qBAAqB,IAAI;AACzC,WAAK,YAAY,KAAK,GAAG,OAAO;AAChC,WAAK,aAAa,eAAe,OAAO;AAAA,IAC1C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kCAAkC,SAA4B;AAElE,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAU,MACd,QACA,OAAO,aAAa;AACtB,YAAM,OAAO,kBAAkC,OAAO;AAEtD,UAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AAC7C;AAAA,MACF;AAEA,YAAM,iBAAiB,yBAAyB,IAAI;AACpD,UAAI,eAAe,WAAW,GAAG;AAC/B;AAAA,MACF;AAEA,YAAM,UAAU;AAAA,QACd;AAAA,QACA,KAAK;AAAA,MACP;AACA,UAAI,QAAQ,SAAS,GAAG;AAGtB,cAAM,aAAa,QAAQ,IAAI,CAAC,OAAO;AAAA,UACrC,GAAG;AAAA,UACH,MAAM,EAAE,QAAQ;AAAA,QAClB,EAAE;AACF,aAAK,gBAAgB,KAAK,GAAG,UAAU;AACvC,aAAK,aAAa,mBAAmB,UAAU;AAAA,MACjD;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,MAAM,aAAa,GAAG,KAAK;AAEjC,UAAI,CAAC,IAAI,SAAS,UAAU,KAAK,CAAC,IAAI,SAAS,aAAa,GAAG;AAC7D,QAAAA,KAAI,KAAK,mCAAmC,GAAG,EAAE;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACJ,SACA,cAAc,OACd,iBACA;AAIA,QAAI,KAAK,oBAAoB;AAC3B;AAAA,IACF;AAGA,UAAM,KAAK,eAAe,SAAS,aAAa,eAAe;AAC/D,UAAM,KAAK,kCAAkC,OAAO;AAAA,EACtD;AAAA,EAEA,MAAc,eACZ,SACA,aACA,iBACA;AAKA,QAAI;AACF,YAAM,YAAY,MAAM,2BAA2B;AAAA,QACjD,OAAO,MACL;AAAA,UACE;AAAA,UACA,yBAAyB,WAAW;AAAA,QACtC;AAAA,QACF,2BAA2B,MAAM,KAAK,aAAa,OAAO;AAAA,QAC1D,YAAY,MACV,iBAAyB,SAAS,uBAAuB,EAAE;AAAA,UACzD,CAAC,MAAM,KAAK;AAAA,QACd;AAAA,QACF,KAAK,CAAC,OAAO,YAAYA,KAAI,KAAK,EAAE,OAAO;AAAA,MAC7C,CAAC;AAED,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AAEA,WAAK,oBAAoB,WAAW,EAAE,gBAAgB,CAAC;AACvD,YAAM,gBAAgB,MAAM;AAAA,QACzB,UAAsC;AAAA,MACzC,IACK,UAAuC,UAAU,SAClD;AACJ,UAAI,gBAAgB,GAAG;AACrB,QAAAA,KAAI,KAAK,oBAAoB,aAAa,kBAAkB;AAAA,MAC9D;AAAA,IACF,SAAS,KAAK;AACZ,MAAAA,KAAI;AAAA,QACF,iDAAiD,aAAa,GAAG,CAAC;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACF;;;AGrjBA,OAAOK,aAAY;AAOnB,IAAMC,OAAMC,QAAO,oCAAoC;AAEhD,IAAM,eAAN,cAA2B,iBAAiB;AAAA,EACjD;AAAA,EACA,kBAAkB,oBAAI,IAAsB;AAAA,EAC5C;AAAA,EACA;AAAA,EAEA,YACE,QACA,YACA;AACA,UAAM,MAAM;AACZ,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,aAAa,YAAwB;AACnC,SAAK,mBAAmB,WAAW;AACnC,SAAK,gBAAgB;AACrB,UAAM,aACJ,QAAQ,IAAI,UAAU,gBAAgB,MAAM,SACxC,QAAQ,IAAI,UAAU,WAAW,GAAG,KAAK,IACzC;AAEN,QAAI,CAAC,WAAW,KAAK;AACnB,iBAAW,MAAMC,mBAAkB,UAAU;AAAA,IAC/C;AAEA,QACE,KAAK,oBACL,CAAC,KAAK,gBAAgB,IAAI,KAAK,gBAAgB,GAC/C;AACA,YAAM,WAAW,oBAAoB,KAAK,gBAAgB;AAC1D,YAAM,YAAY,aACd,SAAS,UAAU,OAAO,CAAC,SAAS,SAAS,UAAU,IACvD,SAAS;AACb,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,gBAAgB,IAAI,KAAK,kBAAkB,SAAS;AACzD,QAAAF,KAAI;AAAA,UACF,uBAAgB,UAAU,MAAM,oBAAoB,KAAK,gBAAgB;AAAA,QAC3E;AAAA,MACF;AAAA,IACF;AAEA,SAAK,UAAU,KAAK,UAAU;AAC9B,SAAK,aAAa;AAAA,EACpB;AAAA,EAES,qBAAqB;AAC5B,UAAM,mBAAmB;AACzB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,gBAAgB;AACrB,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,kBAA0C;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,YAAY,WAAsB;AAChC,QAAI,CAAC,UAAU,OAAO,UAAU,IAAI,SAAS,OAAO,GAAG;AACrD,gBAAU,MAAME,mBAAkB,SAAS;AAAA,IAC7C;AAEA,eAAW,SAAS,KAAK,WAAW;AAClC,YAAM,YAAY,MAAM,MAAM,UAAU,CAAC,MAAM;AAC7C,YAAI,OAAO,MAAM,UAAU;AACzB,iBAAO;AAAA,QACT;AACA,eAAO,EAAE,UAAU,UAAU,SAAS,EAAE,WAAW,MAAM;AAAA,MAC3D,CAAC;AACD,UAAI,cAAc,IAAI;AACpB,cAAM,MAAM,SAAS,IAAI;AACzB,aAAK,aAAa;AAClB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,MAAM,KAAK,SAAS;AAAA,IACzC;AACA,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGS,WAAW,YAAwB;AAC1C,UAAM,cAAc,KAAK,gBAAgB,IAAI,WAAW,IAAI,KAAK,CAAC;AAClE,UAAM,qBAAqB,IAAI;AAAA,MAC7B,WAAW,MACR,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAM,EAChD,OAAO,CAAC,UAA2B,QAAQ,KAAK,CAAC;AAAA,IACtD;AAEA,gBAAY,QAAQ,CAAC,aAAa;AAChC,UAAI,CAAC,mBAAmB,IAAI,QAAQ,GAAG;AACrC,cAAM,MAAMA,mBAAkB;AAAA,UAC5B,MAAM,WAAW;AAAA,UACjB,WAAW,GAAG,WAAW,KAAK,IAAI,QAAQ;AAAA,QAC5C,CAAc;AACd,cAAM,cAAyB;AAAA,UAC7B;AAAA,UACA,KAAK,SAAS;AAAA,UACd,OAAO;AAAA,UACP,WAAW,GAAG,WAAW,KAAK,IAAI,QAAQ;AAAA,UAC1C,QAAQ,WAAW;AAAA,UACnB,OAAO;AAAA,UACP,OAAO,oBAAI,KAAK;AAAA,UAChB,KAAK,oBAAI,KAAK;AAAA,UACd,MAAM;AAAA,UACN,MAAM,WAAW;AAAA,UACjB,SAAS,KAAK,YAAY,GAAG,KAAK,SAAS;AAAA,UAC3C,WAAW,SAAS;AAAA,UACpB,OAAO,CAAC;AAAA,QACV;AACA,mBAAW,MAAM,KAAK,WAAW;AACjC,QAAAF,KAAI,KAAK,yBAAyB,QAAQ,oBAAoB;AAAA,MAChE;AAAA,IACF,CAAC;AAED,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,YAAY,YAAwB;AAClC,UAAM,QAAQ,KAAK,UAAU,UAAU,CAAC,MAAM,EAAE,SAAS,WAAW,IAAI;AACxE,QAAI,UAAU,IAAI;AAChB,WAAK,UAAU,OAAO,OAAO,CAAC;AAAA,IAChC;AACA,SAAK,UAAU,KAAK,UAAU;AAC9B,SAAK,gBAAgB;AACrB,SAAK,aAAa;AAAA,EACpB;AACF;;;ACtHO,SAAS,cACd,aACA,WACoB;AACpB,QAAM,OAAO,OAAO,gBAAgB,WAAW,YAAY,KAAK,IAAI;AACpE,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,SAAO,UAAU,KAAK,CAAC,UAAU,SAAS,SAAS,KAAK,SAAS,IAAI,KAAK,EAAE,CAAC;AAC/E;AAUO,SAAS,gBACd,OACA,aACuB;AACvB,QAAM,SAAS,OAAO,SAAS,CAAC,GAAG;AAAA,IACjC,CAAC,MAAsB,OAAO,MAAM;AAAA,EACtC;AACA,QAAM,QAAQ;AAAA,IACZ,aAAa;AAAA,IACb,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,EAC1B;AACA,SAAO,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI;AACxD;;;ACzCO,IAAM,cAAN,MAAkB;AAAA,EAIvB,YACU,cAEA,eACR;AAHQ;AAEA;AAAA,EACP;AAAA,EAHO;AAAA,EAEA;AAAA,EANF,iBAAiB,oBAAI,IAAyB;AAAA,EAC9C,oBAAmC;AAAA;AAAA;AAAA;AAAA,EAW3C,gBACE,MACA,OACA,SACA,UACM;AACN,SAAK,QAAQ;AACb,QAAI,SAAS;AACX,WAAK,MAAM;AAAA,IACb;AACA,QAAI,aAAa,QAAW;AAC1B,WAAK,YAAY;AAAA,IACnB;AAEA,QAAI,UAAU,WAAW,QAAQ;AAC/B,WAAK,aAAa,UAAU,IAAI;AAAA,IAClC,WAAW,UAAU,WAAW,QAAQ;AACtC,WAAK,aAAa,UAAU,IAAI;AAAA,IAClC,WAAW,UAAU,WAAW,SAAS;AACvC,WAAK,aAAa,YAAY,IAAI;AAAA,IACpC;AAEA,QAAI,UAAU,WAAW,SAAS;AAChC,WAAK,aAAa,UAAU,IAAI;AAChC,WAAK,gBAAgB,KAAK,KAAK,KAAK;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,OAAmB,WAA0C;AAC3E,WAAO,MAAM,MAAM;AAAA,MACjB,CAAC,MAAsB,OAAO,MAAM,YAAY,EAAE,UAAU;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,UAAkB,WAAyB;AAC7D,QAAI,CAAC,KAAK,eAAe,IAAI,QAAQ,GAAG;AACtC,WAAK,eAAe,IAAI,UAAU,oBAAI,IAAI,CAAC;AAAA,IAC7C;AACA,SAAK,eAAe,IAAI,QAAQ,EAAG,IAAI,SAAS;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,UAAkB,WAA4B;AAC5D,WAAO,KAAK,eAAe,IAAI,QAAQ,GAAG,IAAI,SAAS,KAAK;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,UAA+B;AAC/C,WAAO,KAAK,eAAe,IAAI,QAAQ,KAAK,oBAAI,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,uBAAsD;AACvE,UAAM,kBAAkB,uBAAuB,QAAQ,SAAS;AAGhE,QACE,KAAK,qBACL,oBAAoB,KAAK,qBACzB,oBAAoB,SAAS,WAC7B;AACA,YAAM,WACJ,sBAAsB,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK,SAAS;AAC7D,YAAM,eAAe,KAAK,aAAa,gBAAgB;AAEvD,UAAI,cAAc;AAChB,cAAM,WAAW,KAAK;AAAA,UACpB;AAAA,UACA,KAAK;AAAA,QACP;AACA,YAAI,YAAY,SAAS,UAAU,WAAW,SAAS;AACrD,gBAAM,eACJ,sBAAsB,SAAS,YAAY,KAAK,iBAAiB;AAEnE,cAAI,cAAc;AAChB,kBAAM,YAAY,mBAAmB,YAAY;AACjD,iBAAK;AAAA,cACH;AAAA,cACA;AAAA,cACA,oBAAI,KAAK;AAAA,cACT,WAAW,aAAa,QAAQ,GAAG,IAAI;AAAA,YACzC;AACA,iBAAK,oBAAoB,UAAU,KAAK,iBAAiB;AAAA,UAC3D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,oBAAoB,SAAS,WAAW;AAC1C,WAAK,oBAAoB;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YACE,aACuB;AACvB,WAAO,gBAAgB,KAAK,aAAa,gBAAgB,GAAG,WAAW;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,iBAA+B;AAChD,QACE,oBAAoB,SAAS,aAC7B,KAAK,sBAAsB,iBAC3B;AACA;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,aAAa,gBAAgB;AACvD,QAAI,CAAC,cAAc;AACjB;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,gBAAgB,cAAc,eAAe;AAC/D,QAAI,QAAQ,KAAK,UAAU,WAAW,SAAS;AAC7C,WAAK,QAAQ,oBAAI,KAAK;AACtB,WAAK,MAAM;AACX,WAAK,gBAAgB,MAAM,WAAW,OAA6B;AAAA,IACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBACE,OACA,WACM;AACN,UAAM,MAAM,QAAQ,CAAC,UAAU;AAC7B,UAAI,OAAO,UAAU,UAAU;AAC7B;AAAA,MACF;AACA,YAAM,OAAO;AACb,UAAI,KAAK,UAAU,WAAW,WAAW,KAAK,OAAO;AAEnD,aAAK,QAAQ,WAAW;AACxB,qBAAa,IAAI;AACjB,aAAK,gBAAgB,MAAM,WAAW,MAA4B;AAAA,MACpE,WAAW,KAAK,UAAU,WAAW,SAAS;AAC5C,cAAM,WAAW,UAAU,KAAK,KAAK;AACrC,YAAI,UAAU;AACZ,gBAAM,YAAY,mBAAmB,QAAQ;AAC7C,eAAK,QAAQ,KAAK,SAAS,oBAAI,KAAK;AACpC,eAAK;AAAA,YACH;AAAA,YACA;AAAA,YACA,oBAAI,KAAK;AAAA,YACT,WAAW,SAAS,QAAQ,GAAG,IAAI;AAAA,UACrC;AAAA,QACF,OAAO;AAEL,eAAK;AAAA,YACH;AAAA,YACA,WAAW;AAAA,YACX,oBAAI,KAAK;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,oBAAoB;AAAA,EAC3B;AACF;;;ACtMO,IAAM,eAAN,MAAmB;AAAA,EAGxB,YAAoB,cAA4B;AAA5B;AAAA,EAA6B;AAAA,EAA7B;AAAA,EAFZ,qBAAqB,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAQzD,qBAAqB;AACnB,SAAK,mBAAmB,MAAM;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,iBACE,UACA,YACA,UACA,WACA,WACA,WACY;AACZ,QAAI,CAAC,KAAK,mBAAmB,IAAI,QAAQ,GAAG;AAC1C,YAAM,OAAO,YAAY;AACzB,YAAM,aAAa,iBAAiB;AAAA,QAClC,KAAKG,mBAAkB,MAAM,UAAU;AAAA,QACvC,KAAK,SAAS;AAAA,QACd,OAAO;AAAA,QACP;AAAA,QACA,OAAO,WAAW;AAAA,QAClB,YACE,aAAa,WAAW,GAAG,QAAQ,IAAI,SAAS,KAAK;AAAA,MACzD,CAAC;AAGD,eAAS,MAAM,GAAG,MAAM,UAAU,QAAQ,OAAO;AAC/C,cAAM,WAAW,UAAU,GAAG;AAC9B,cAAM,WAAW,YAAY,GAAG;AAChC,cAAM,YAAY,GAAG,UAAU,IAAI,QAAQ;AAC3C,cAAM,YAAY,gBAAgB;AAAA,UAChC,KAAKA,mBAAkB,MAAM,SAAS;AAAA,UACtC,KAAK,SAAS;AAAA,UACd,OAAO;AAAA,UACP;AAAA,UACA;AAAA,UACA,QAAQ,WAAW;AAAA,UACnB,OAAO,WAAW;AAAA,UAClB,YACE,YAAY,WAAW,GAAG,QAAQ,IAAI,QAAQ,KAAK;AAAA,QACvD,CAAC;AACD,mBAAW,MAAM,KAAK,SAAS;AAAA,MACjC;AAEA,WAAK,mBAAmB,IAAI,UAAU,UAAU;AAChD,WAAK,aAAa,aAAa,UAAU;AAAA,IAC3C;AAEA,WAAO,KAAK,mBAAmB,IAAI,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,UAA0C;AACjD,WAAO,KAAK,mBAAmB,IAAI,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,OAAyB;AAC1C,UAAM,QAAQ,WAAW;AACzB,UAAM,MAAM;AACZ,SAAK,aAAa,aAAa;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,OAAyB;AAC1C,UAAM,QAAQ,yBAAyB,KAAK;AAC5C,SAAK,aAAa,aAAa;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,OAAyB;AACrC,QAAI,MAAM,KAAK;AACb;AAAA,IACF;AACA,kBAAc,KAAK;AACnB,UAAM,QAAQ,6BAA6B,KAAK;AAChD,SAAK,aAAa,WAAW,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AACF;;;AC1HA,OAAOC,aAAY;;;ACQZ,SAAS,cAAc,QAAiB,QAAQ,GAAsB;AAC3E,MAAI,kBAAkB,OAAO;AAC3B,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAMV,MAAI,WAAW,GAAG;AAChB,WAAO,QAAQ,IAAI,cAAc,EAAE,OAAO,QAAQ,CAAC,IAAI;AAAA,EACzD;AACA,MAAI,YAAY,KAAK,CAAC,EAAE,OAAO;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI,MAAM,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC;AAClD,MAAI,OAAO,EAAE,UAAU,UAAU;AAC/B,QAAI,QAAQ,EAAE;AAAA,EAChB;AACA,SAAO;AACT;;;ACTA,IAAM,4BAAiD,oBAAI,IAAI;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAUD,IAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASD,IAAM,0BAA+C,oBAAI,IAAI;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAID,SAAS,WAAW,QAAwB;AAC1C,SAAO,OAAO,WAAW,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM,IAAI;AACnE;AASA,SAAS,mBAAmB,YAAyC;AACnE,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO,cAAc;AAAA,EACvB;AACA,MAAI,cAAc,OAAO,eAAe,UAAU;AAChD,UAAM,WAAY,WAAsC;AACxD,QAAI,OAAO,aAAa,YAAY,UAAU;AAC5C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,qBACd,QACA,MACoB;AACpB,MAAI,CAAC,0BAA0B,IAAI,WAAW,MAAM,CAAC,GAAG;AACtD,WAAO;AAAA,EACT;AACA,SAAO,mBAAmB,KAAK,CAAC,CAAC;AACnC;AAOO,SAAS,mBACd,QACA,MACoB;AACpB,MACE,CAAC,sBAAsB,IAAI,MAAM,KACjC,CAAC,wBAAwB,IAAI,MAAM,GACnC;AACA,WAAO;AAAA,EACT;AACA,SAAO,mBAAmB,KAAK,CAAC,CAAC;AACnC;AAMO,SAAS,sBACd,QACA,MACoB;AACpB,MAAI,CAAC,sBAAsB,IAAI,MAAM,GAAG;AACtC,WAAO;AAAA,EACT;AACA,SAAO,mBAAmB,KAAK,CAAC,CAAC;AACnC;;;AChKO,SAAS,uBACd,gBACA,YACS;AACT,MAAI,mBAAmB,QAAQ,mBAAmB,QAAW;AAC3D,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,wBAAwB,KAAK,UAAU;AAKhE,MAAI,OAAO,mBAAmB,UAAU;AACtC,UAAM,IAAI;AACV,QAAI,YAAY,GAAG;AACjB,aAAO,EAAE,SACL,OACA;AAAA,QACE,QAAQ;AAAA,QACR,QAAQ,EAAE;AAAA,QACV,UAAU,EAAE;AAAA,QACZ,SAAS,EAAE;AAAA,MACb;AAAA,IACN;AACA,QAAI,WAAW,GAAG;AAChB,YAAM,MAAM,EAAE;AACd,aAAO,QAAQ,QAAQ,mBAAmB,QAAQ;AAAA,IACpD;AAAA,EACF;AAEA,MAAI,OAAO,mBAAmB,YAAY;AACxC,QAAI;AACF,aAAO,KAAK,MAAM,KAAK,UAAU,cAAc,CAAC;AAAA,IAClD,QAAQ;AACN,aAAO,OAAO,cAAc;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AACT;;;AChCA,OAAOC,aAAY;AAmBnB,IAAMC,OAAMC,QAAO,4CAA4C;AAwB/D,IAAM,gBAAgB;AAQtB,SAAS,qBACP,UACA,OACsD;AACtD,QAAM,WAAW,SAAS;AAAA,IACxB,CAAC,MAAM,OAAO,GAAG,SAAS,YAAY,cAAc,KAAK,EAAE,IAAI;AAAA,EACjE;AACA,MAAI,SAAS,WAAW,OAAO;AAC7B,WAAO,IAAI,MAAM,KAAK,EAAE,KAAK,IAAI;AAAA,EACnC;AACA,SAAO,SAAS;AAAA,IAAI,CAAC,MACnB,OAAO,EAAE,cAAc,YAAY,OAAO,EAAE,YAAY,WACpD,EAAE,WAAW,EAAE,WAAW,SAAS,EAAE,QAAQ,IAC7C;AAAA,EACN;AACF;AAOA,SAAS,mBAAmB,OAAyB,MAA0B;AAC7E,QAAM,OAAO,OAAO,MAAM,WAAW,MAAM,WAAW,EAAE;AACxD,QAAM,WAAW,KAAK;AAAA,IACpB,CAAC,MAA4B,OAAO,MAAM,YAAY,OAAO,MAAM;AAAA,EACrE;AACA,SAAO,SAAS,SAAS,KAAK,SAAS,MAAM,CAAC,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC;AAC9E;AAYO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAAwD;AACtD,MAAI,YAAY,iBAAiB,OAAO;AACtC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,UAAU,aAAa,KAAK,CAAC,EAAE,KAAK;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,SAAO,EAAE,QAAQ,MAAM,SAAS,IAAI,UAAU;AAChD;AAKA,SAAS,eACP,MAC+C;AAC/C,SAAO,KAAK,WACR,EAAE,WAAW,KAAK,WAAW,SAAS,KAAK,SAAS,UAAU,IAC9D;AACN;AAYA,SAAS,uBACP,SAMoB;AACpB,QAAM,YAAY,SAAS;AAC3B,MAAI,aAAa,OAAO,cAAc,UAAU;AAC9C,UAAM,MAA0B,CAAC;AACjC,eAAW,MAAM,OAAO,OAAO,SAAS,GAAG;AACzC,UAAI,MAAM,QAAQ,IAAI,UAAU,GAAG;AACjC,YAAI,KAAK,GAAI,GAAG,UAAiC;AAAA,MACnD;AAAA,IACF;AACA,QAAI,IAAI,SAAS,GAAG;AAClB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,MAAM,QAAQ,SAAS,UAAU,IAAI,QAAQ,aAAa,CAAC;AACpE;AAUA,SAAS,UACP,OACA,YACuB;AACvB,QAAM,WAAW,IAAI,MAAM,WAAW,MAAM,EAAE,KAAK,KAAK;AACxD,QAAM,YAAY,CAAC,QAAyB;AAC1C,aAAS,GAAG,IAAI;AAChB,UAAM,QAAQ,WAAW,GAAG;AAC5B,WAAO;AAAA,MACL,QAAQ,CAAC,MAAM;AAAA,MACf,SAAS,UAAU,OAAO,MAAM,WAAW,MAAM,WAAW,EAAE,CAAC,EAAE,KAAK;AAAA,IACxE;AAAA,EACF;AACA,QAAM,UAAU,MAAM,IAAI,CAAC,SAAS;AAClC,UAAM,MAAM,WAAW;AAAA,MACrB,CAAC,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,mBAAmB,OAAO,KAAK,IAAI;AAAA,IACnE;AACA,WAAO,QAAQ,KAAK,OAAO,UAAU,GAAG;AAAA,EAC1C,CAAC;AACD,SAAO,QAAQ,IAAI,CAAC,YAAY;AAC9B,QAAI,SAAS;AACX,aAAO;AAAA,IACT;AACA,UAAM,MAAM,SAAS,UAAU,CAAC,SAAS,CAAC,IAAI;AAC9C,WAAO,QAAQ,KAAK,OAAO,UAAU,GAAG;AAAA,EAC1C,CAAC;AACH;AAKO,SAAS,yBACd,UACe;AACf,WAAS,IAAI,SAAS,YAAY,SAAS,GAAG,KAAK,GAAG,KAAK;AACzD,UAAM,OAAO,SAAS,YAAY,CAAC,GAAG;AACtC,QAAI,MAAM;AACR,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAIA,eAAe,2BACb,UACA,SACwB;AACxB,SACE,yBAAyB,QAAQ,KAChC,MAAM,SAAS,sBAAsB,OAAO;AAEjD;AAIA,SAAS,aAAa,MAAgC;AACpD,QAAM,UAAU,KAAK,KAClB;AAAA,IAAI,CAAC,MACJ,OAAO,MAAM,WACT,IAAI,CAAC,MACL,MAAM,QAAQ,OAAO,MAAM,WACzB,WACA,OAAO,CAAC;AAAA,EAChB,EACC,KAAK,IAAI;AACZ,SAAO,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM,IAAI,OAAO;AACjD;AAUO,SAAS,wBACd,MACA,SACA,YACY;AACZ,QAAM,QAAoB;AAAA,IACxB,SAAS,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM;AAAA,IACtC,MAAM,KAAK,KAAK,IAAI,sBAAsB;AAAA,IAC1C,OAAO,aAAa,IAAI;AAAA,IACxB,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,EAClB;AACA,MAAI,KAAK,YAAY;AACnB,UAAM,aAAa,KAAK;AAAA,EAC1B;AACA,MAAI,SAAS;AACX,UAAM,UAAU;AAAA,EAClB;AACA,MAAI,YAAY;AACd,UAAM,aAAa;AAAA,EACrB;AAGA,QAAM,WAAW,qBAAqB,KAAK,QAAQ,KAAK,IAAI;AAC5D,MAAI,UAAU;AACZ,UAAM,WAAW;AAAA,EACnB;AACA,SAAO;AACT;AAMA,SAAS,uBAAuB,SAAqC;AACnE,QAAM,SAAS;AACf,QAAM,MAAM,QAAQ,YAAY,MAAM;AACtC,MAAI,QAAQ,IAAI;AACd,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,MAAM,MAAM,OAAO,MAAM,EAAE,KAAK;AACnD,SAAO,KAAK,QAAQ,eAAe,EAAE,EAAE,KAAK;AAC5C,SAAO,KAAK,QAAQ,SAAS,EAAE,EAAE,QAAQ,SAAS,EAAE,EAAE,KAAK;AAC3D,SAAO,QAAQ;AACjB;AAMA,SAAS,sBACP,MACA,SACuB;AACvB,QAAM,SAAgC;AAAA,IACpC,QAAQ,QAAQ;AAAA,IAChB,UAAU,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK;AAAA,EACxD;AAGA,MAAI,QAAQ,SAAS;AACnB,WAAO,UAAU,QAAQ;AAAA,EAC3B;AACA,QAAM,SAAS,QAAQ,SACnB,SACA,uBAAuB,QAAQ,OAAO;AAC1C,MAAI,WAAW,QAAW;AACxB,WAAO,SAAS;AAAA,EAClB;AACA,SAAO;AACT;AAKA,SAAS,qBACP,UACA,MACA,SACA,QACA,YACA,SACM;AACN,QAAM,QAAQ,KAAK;AACnB,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM;AAAA,MACzD,YAAY,KAAK;AAAA,MACjB,OAAO,MAAM;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAIA,QAAM,SAAS,sBAAsB,MAAM,OAAO;AAClD,QAAM,QAAQ,UAAU;AACxB,MAAI,CAAC,MAAM,cAAc,YAAY;AACnC,UAAM,aAAa;AAAA,EACrB;AAIA,MAAI,QAAQ;AACV,UAAM,YAAY,OAAO;AACzB,UAAM,YACJ,OAAO,UAAU,OAAO,YAAY,OAAO,UAAU,OAAO;AAAA,EAChE;AACA,WAAS,qBAAqB,KAAK;AACnC,EAAAD,KAAI,KAAK,YAAY,MAAM,KAAK,WAAM,QAAQ,SAAS,SAAS,MAAM,EAAE;AAC1E;AAWA,eAAsB,wBACpB,UACA,SACA,aACA,SACA,OACe;AACf,MAAI,MAAM,WAAW,GAAG;AACtB;AAAA,EACF;AAGA,QAAM,UAAU,aAAa;AAO7B,QAAM,aAAa,uBAAuB,OAAO;AACjD,QAAM,WAAW,UAAU,OAAO,UAAU;AAC5C,QAAM,UAAU;AAAA,IACd,MAAM,QAAQ,SAAS,QAAQ,IAAI,QAAQ,WAAW,CAAC;AAAA,IACvD,MAAM;AAAA,EACR;AAEA,QAAM,aAAa,MAAM,2BAA2B,UAAU,OAAO;AAErE,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,QAAI,CAAC,KAAK,OAAO;AACf;AAAA,IACF;AAOA,UAAM,aAAa,SAAS,KAAK;AACjC,UAAM,UAAU,cAAc,KAAK;AACnC,QAAI,SAAS;AACX;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,QAAQ,KAAK,IAAI,eAAe,IAAI;AAAA,QACjD;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAKA,QAAI,CAAC,KAAK,MAAM,cAAc,YAAY;AACxC,WAAK,MAAM,aAAa;AAAA,IAC1B;AACA,aAAS,qBAAqB,KAAK,KAAK;AAAA,EAC1C,CAAC;AACH;;;AC9aO,IAAM,uBAAN,MAA2B;AAAA,EAUhC,YACmB,UACT,gBACA,cACR;AAHiB;AACT;AACA;AAAA,EACP;AAAA,EAHgB;AAAA,EACT;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EALF,QAA4B,CAAC;AAAA,EAQrC,QAAc;AACZ,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA;AAAA;AAAA,EAIA,QAA4B;AAC1B,UAAM,QAAQ,KAAK;AACnB,SAAK,QAAQ,CAAC;AACd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,eAAe,SAAkC;AAG/C,UAAM,IAAI;AACT,IAAC,CAAC,UAAU,QAAQ,EAAY,QAAQ,CAAC,WAAW;AACnD,YAAM,WAAW,EAAE,MAAM;AACzB,UAAI,CAAC,YAAY,OAAO,aAAa,UAAU;AAC7C;AAAA,MACF;AACA,QAAE,MAAM,IAAI,KAAK,eAAe,UAAU,QAAQ,CAAC,CAAC;AAAA,IACtD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,eACN,QACA,QACAE,OACQ;AACR,WAAO,IAAI,MAAM,QAAQ;AAAA,MACvB,KAAK,CAAC,GAAG,MAAM,aAAa;AAC1B,cAAM,OAAO,QAAQ,IAAI,GAAG,MAAM,QAAQ;AAC1C,YAAI,OAAO,SAAS,UAAU;AAC5B,iBAAO;AAAA,QACT;AACA,YAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;AAC7C,iBAAO,KAAK,eAAe,MAAM,QAAQ,CAAC,GAAGA,OAAM,IAAI,CAAC;AAAA,QAC1D;AACA,YAAI,OAAO,SAAS,YAAY;AAC9B,iBAAO;AAAA,QACT;AACA,eAAO,IAAI,SAAoB;AAC7B,gBAAM,WAAW,uBAAuB;AACxC,gBAAM,OACJ,SAAS,aAAa,SAClB,SACA,KAAK,OAAO;AAAA,YACV;AAAA,YACA,QAAQ,CAAC,GAAGA,OAAM,IAAI,EAAE,KAAK,GAAG;AAAA,YAChC;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,WAAW,KAAK,IAAI;AAAA,UACtB,CAAC;AACP,gBAAM,SAAU,KAAsC,GAAG,IAAI;AAC7D,cAAI,MAAM;AACR,iBAAK,QAAQ,MAAM,MAAM;AAAA,UAC3B;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,OAAO,MAA0C;AACvD,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,KAAK,eAAe,GAAG;AAAA,MACvB,yBAAyB,KAAK,QAAQ;AAAA,IACxC;AACA,UAAM,WAAW,KAAK,aAAa;AACnC,SAAK,QAAQ;AACb,SAAK,MAAM,KAAK,IAAI;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,QAAQ,MAAwB,QAAuB;AAC7D,UAAM,WAAW;AACjB,QAAI,CAAC,YAAY,OAAO,SAAS,SAAS,YAAY;AACpD;AAAA,IACF;AACA,SAAM,OAA4B;AAAA,MAChC,CAAC,UAAU;AACT,aAAK,WAAW,sBAAsB;AAAA,UACpC,UAAU;AAAA,UACV;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,MACA,CAAC,WAAW;AACV,aAAK,WAAW,sBAAsB;AAAA,UACpC,UAAU;AAAA,UACV,OAAO;AAAA,UACP,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ALnIA,IAAMC,OAAMC,QAAO,wCAAwC;AAEpD,IAAM,eAAN,MAAmB;AAAA,EAiCxB,YACmB,iBACT,aACA,gBACA,oBAAoB,MACpB,gBACR;AALiB;AACT;AACA;AACA;AACA;AAER,SAAK,iBAAiB,IAAI;AAAA,MACxB;AAAA,MACA,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK;AAAA,IACb;AAAA,EACF;AAAA,EAXmB;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EApCF,kBAAkB,oBAAI,QAAgB;AAAA,EACtC,eAAoC,CAAC;AAAA,EACrC,iBAAgC;AAAA,EAChC,sBAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,eAAe,IAAI,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhC,eAAe;AAAA,EAEf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBpC,UAAmC;AACzC,WACE,KAAK,YAAY,YAAY,KAAK,SAAS,WAAW,KACtD,KAAK,eAAe;AAAA,EAExB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAiC;AAC/B,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA6B;AAC3B,SAAK,eAAe,CAAC;AACrB,SAAK,iBAAiB;AACtB,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA,EAIA,oBAA0B;AACxB,SAAK,qBAAqB;AAC1B,SAAK,eAAe,MAAM;AAAA,EAC5B;AAAA;AAAA;AAAA,EAIA,yBAA6C;AAC3C,WAAO,KAAK,eAAe,MAAM;AAAA,EACnC;AAAA,EAEA,yBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,uBAAuBC,OAA2B;AAChD,SAAK,sBAAsBA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,cAAc,SAAkC;AAC9C,UAAM,kBAAkB,KAAK;AAO7B,UAAM,IAAI;AAKV,UAAM,UAAU,CAAC,eAAuB;AACtC,UAAI,OAAO,EAAE,UAAU,MAAM,YAAY;AACvC;AAAA,MACF;AACA,YAAM,WAAW,EAAE,UAAU,EAAE,KAAK,OAAO;AAE3C,QAAE,UAAU,IAAI,YAAa,MAAiB;AAO5C,YAAI,OAAO,KAAK,CAAC,MAAM,UAAU;AAC/B,iBAAO,SAAS,GAAG,IAAI;AAAA,QACzB;AACA,cAAM,SAAS,SAAS,GAAG,IAAI;AAE/B,cAAM,mBAAmB,MAAM;AAC7B,UAAAF,KAAI,KAAK,SAAS,UAAU,IAAI,KAAK,CAAC,KAAK,EAAE,2BAAsB;AACnE,iBACE,gBACG,aAAa,OAAO,EAIpB,KAAK,MAAM,gBAAgB,aAAa,SAAS,IAAI,CAAC,EACtD;AAAA,YAAM,CAAC,QACNA,KAAI,MAAM,4BAA6B,IAAc,OAAO,EAAE;AAAA,UAChE;AAAA,QAEN;AAEA,cAAM,YAAY;AAClB,YAAI,aAAa,OAAO,UAAU,YAAY,YAAY;AAYxD,eAAK,QAAQ,QAAQ,MAAM,EACxB,KAAK,gBAAgB,EACrB,MAAM,MAAM;AAAA,UAAC,CAAC;AACjB,iBAAO;AAAA,QACT;AAOA,eAAO,QAAQ,QAAQ,MAAM,EAC1B,KAAK,gBAAgB,EACrB,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACnB;AAAA,IACF;AAEA,YAAQ,KAAK;AACb,YAAQ,UAAU;AAClB,YAAQ,YAAY;AAEpB,IAAAA,KAAI,KAAK,iCAA4B;AAAA,EACvC;AAAA,EAEA,oBAAoB,SAAkC;AAIpD,SAAK,UAAU;AACf,QAAI,KAAK,gBAAgB,IAAI,OAAiB,GAAG;AAC/C;AAAA,IACF;AAKA,UAAM,aAAa;AAInB,UAAM,aAAa,oBAAI,IAAI;AAAA,MACzB,GAAG,OAAO,KAAK,OAAO;AAAA,MACtB,GAAG,OAAO,oBAAoB,OAAO,eAAe,OAAO,CAAC;AAAA,IAC9D,CAAC;AACD,UAAM,iBAA2B,CAAC;AAElC,eAAW,QAAQ,CAAC,eAAe;AACjC,UACE,eAAe,iBACf,OAAO,WAAW,UAAU,MAAM,YAClC;AACA;AAAA,MACF;AAEA,UACG,4BAAkD;AAAA,QACjD;AAAA,MACF,KACA,WAAW,WAAW,IAAI,GAC1B;AACA;AAAA,MACF;AAEA,YAAM,iBAAiB,WAAW,UAAU,EAAE,KAAK,OAAO;AAE1D,iBAAW,UAAU,IAAI,IAAI,SAAoB;AAC/C,eAAO,KAAK;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,qBAAe,KAAK,UAAU;AAAA,IAChC,CAAC;AAID,QAAI,KAAK,mBAAmB;AAC1B,WAAK,eAAe,eAAe,OAAO;AAAA,IAC5C;AACA,SAAK,gBAAgB,IAAI,OAAiB;AAC1C,IAAAA,KAAI,KAAK,kBAAa,eAAe,MAAM,kBAAkB;AAAA,EAC/D;AAAA,EAEQ,uBACN,SACA,YACA,SACA,kBACA,MACA,cACM;AAGN,UAAM,EAAE,OAAO,aAAa,IAAI,KAAK,gBAAgB;AAAA,MACnD,KAAK,aAAa;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,SAAK,aAAa,UAAU,MAAM,OAAO,IAAI;AAC7C,SAAK,gBAAgB,mBAAmB,cAAc,KAAK;AAC3D,SAAK,iBAAiB,SAAS,OAAO,YAAY,UAAU;AAAA,EAC9D;AAAA,EAEQ,oBACN,SACA,YACA,SACA,kBACA,MACA,QACA,cACM;AAON,SAAK,aAAa,WAAW,MAAM;AACnC,SAAK,aAAa,UAAU,IAAI;AAEhC,SAAK,kBAAkB,SAAS,UAAU;AAC1C,SAAK,gBACF,eAAe,YAAY,SAAS,kBAAkB,cAAc,IAAI,EACxE;AAAA,MAAM,CAAC,QACNA,KAAI,MAAM,qBAAqB,UAAU,KAAM,IAAc,OAAO,EAAE;AAAA,IACxE;AACF,UAAM,cACJ,KAAK,gBAAgB,YACnB,KAAK,gBAAgB,YAAY,SAAS,CAC5C;AACF,QAAI,aAAa;AACf,WAAK,aAAa,UAAW,YAAiC,OAAO,IAAI;AACzE,WAAK,gBAAgB,YAAY,WAAW;AAC5C,MAAAA,KAAI,KAAK,aAAa,UAAU,EAAE;AAClC,WAAK,iBAAiB,SAAS,aAAa,UAAU;AAAA,IACxD;AACA,SAAK,kBAAkB,SAAS,UAAU;AAAA,EAC5C;AAAA,EAEQ,cAAc,YAA6B;AACjD,WACG,oBAA0C,SAAS,UAAU,KAC9D;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,SAAS,UAAU;AAAA,EAEzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBQ,kBACN,SACA,YACM;AACN,QAAI,CAAC,KAAK,cAAc,UAAU,GAAG;AACnC;AAAA,IACF;AACA,SAAK,gBAAgB,aAAa,SAAS,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,kBACN,SACA,YACM;AACN,QAAI,CAAC,KAAK,cAAc,UAAU,GAAG;AACnC;AAAA,IACF;AAGA,SAAK,gBAAgB,iBAAiB;AAAA,MACpC,KAAK,gBAAgB,sBAAsB,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACpE;AAAA,EACF;AAAA,EAEQ,yBAAyB,YAAoB,QAAsB;AACzE,UAAM,aAAa,KAAK,aAAa,KAAK,aAAa,SAAS,CAAC;AACjE,QAAI,YAAY,YAAY,cAAc,WAAW,cAAc,QAAQ;AACzE,WAAK,aAAa,IAAI;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,4BACN,YACA,SACA,gBACS;AACT,UAAM,SAAS,uBAAuB,gBAAgB,UAAU;AAChE,sBAAkB,QAAQ,sBAAsB,YAAY,OAAO,CAAC;AACpE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIQ,oBACN,SACA,YACA,SACA,QACA,YACA,eACA,WACA,UACA,SACA,cACsC;AACtC,WAAO,CAAC,mBAA4B;AASlC,YAAM,mBAAmB,KAAK,IAAI;AAClC,WAAK,yBAAyB,YAAY,MAAM;AAChD,YAAM,eAAe,cAAc,cAAc;AACjD,YAAM,mBAAmB,eACrB,SACA,KAAK,4BAA4B,YAAY,SAAS,cAAc;AACxE,YAAM,eAAe,KAAK,WAAW,KAAK;AAM1C,UAAI,gBAAgB,eAAe;AACjC,cAAM,OAA2B;AAAA,UAC/B,SAAS;AAAA,UACT;AAAA,UACA,WAAW;AAAA,UACX,WAAW;AAAA,UACX;AAAA,UACA,UAAU,mBAAmB,YAAY,OAAO;AAAA,QAClD;AACA,YAAI,KAAK,aAAa,QAAQ,MAAM,GAAG;AACrC,eAAK;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AACL,eAAK;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,UAAI,cAAc;AAChB,eAAO,aAAa,cAAc;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,sBACN,YACA,QACA,YACM;AACN,QAAI,KAAK,mBAAmB,QAAQ;AAClC;AAAA,IACF;AACA,SAAK,aAAa,KAAK;AAAA,MACrB,SAAS;AAAA,MACT;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEQ,mBACN,YACA,MACmB;AACnB,UAAM,WAAW,uBAAuB;AACxC,QAAI,SAAS,YAAY,CAAC,KAAK,qBAAqB;AAClD,WAAK,sBAAsB,SAAS;AAAA,IACtC;AACA,UAAM,UAAU,KAAK,KAAK,SAAS,CAAC;AACpC,UAAM,kBAAkB,OAAO,YAAY;AAC3C,UAAM,UAAU,kBAAkB,KAAK,MAAM,GAAG,EAAE,IAAI;AACtD,UAAM,SAAS,KAAK,UAAU;AAAA,MAC5B,SAAS;AAAA,MACT,MAAM;AAAA,MACN,KAAK,SAAS;AAAA,IAChB,CAAC;AACD,SAAK,sBAAsB,YAAY,QAAQ,SAAS,UAAU;AAClE,WAAO;AAAA,MACL,cAAc,kBACT,UACD;AAAA,MACJ;AAAA,MACA;AAAA,MACA,YAAY,SAAS;AAAA,MACrB,eAAe,SAAS,aAAa;AAAA,IACvC;AAAA,EACF;AAAA,EAEQ,uBACN,SACA,YACA,YACA,gBACA,MACS;AAGT,SAAK,iBAAiB,OAAO;AAC7B,SAAK,YAAY;AAAA,MACf,KAAK,YAAY;AAAA,QACf,WAAW;AAAA,MACb;AAAA,IACF;AAEA,UAAM,EAAE,cAAc,SAAS,QAAQ,YAAY,cAAc,IAC/D,KAAK,mBAAmB,YAAY,IAAI;AAG1C,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,WAAW,KAAK;AACtB,UAAM,kBAAkB,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,WAAW;AAAA,MAChB;AAAA,IACF;AAOA,UAAM,mBAAmB,mBAAmB,UAAU;AACtD,UAAM,gBAAgB,CAAC,mBAA4B;AACjD,uBAAiB;AACjB,aAAO,gBAAgB,cAAc;AAAA,IACvC;AACA,UAAM,eAAe,CAAC,GAAG,SAAS,aAAa;AAC/C,QAAI;AACF,aAAO,eAAe,GAAG,YAAY;AAAA,IACvC,SAAS,OAAO;AACd,uBAAiB;AACjB,WAAK,yBAAyB,YAAY,MAAM;AAChD,WAAK,oBAAoB,YAAY,SAAS,OAAO,UAAU;AAC/D,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,oBACN,YACA,MACA,OACA,YACM;AACN,UAAM,cAAc,KAAK,QAAQ;AACjC,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,UAAM,kBAAkB,QAAQ,KAAK;AACrC,IAAAA,KAAI,MAAM,mBAAmB,UAAU,KAAK,gBAAgB,OAAO,EAAE;AAErE,SAAK,gBACF,eAAe,YAAY,MAAM,QAAW,iBAAiB;AAAA,MAC5D,SAAS,YAAY;AAAA,MACrB;AAAA,MACA,UAAU,mBAAmB,YAAY,IAAI;AAAA,IAC/C,CAAC,EACA;AAAA,MAAM,CAAC,QACNA,KAAI,MAAM,qBAAqB,UAAU,KAAM,IAAc,OAAO,EAAE;AAAA,IACxE;AAEF,UAAM,cACJ,KAAK,gBAAgB,YACnB,KAAK,gBAAgB,YAAY,SAAS,CAC5C;AACF,QAAI,aAAa;AACf,WAAK,gBAAgB,YAAY,WAAW;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,UAAU,SAAqC;AAC7C,WAAO,KAAK,gBAAgB,IAAI,OAAiB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBACN,SACA,OACA,YACA,SAAS,IACH;AACN,UAAM,KAAK,MAAM,aAAa;AAC9B,SAAK,gBACF,sBAAsB,OAAO,EAC7B,KAAK,CAAC,eAAe;AACpB,UAAI,YAAY;AACd,cAAM,aAAa;AACnB,aAAK,gBAAgB,mBAAmB,IAAI,KAAmB;AAC/D,QAAAA,KAAI,KAAK,4BAA4B,UAAU,GAAG,MAAM,EAAE;AAAA,MAC5D;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB;AACF;;;AM3oBA,OAAOG,aAAY;;;ACrBnB,OAAOC,aAAY;AAMnB,IAAMC,OAAMC,QAAO,8CAA8C;AAY1D,IAAM,qBAAN,cAAiC,uBAA0C;AAAA,EACvE;AAAA,EAET,YAAY,iBAAkC,SAA4B;AACxE,UAAM,OAAO;AACb,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEmB,iBAAiB,YAA0B;AAC5D,IAAAD,KAAI;AAAA,MACF,sDAAiD,UAAU;AAAA,IAC7D;AAAA,EACF;AAAA,EAEmB,iBAAiB,YAA0B;AAC5D,IAAAA,KAAI,KAAK,oCAA0B,UAAU,qBAAqB;AAAA,EACpE;AAAA,EAEmB,cAAc,KAAoB;AACnD,IAAAA,KAAI;AAAA,MACF,2BAA2B,aAAa,GAAG,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAyB,iBAAyC;AAChE,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AACA,WAAO,KAAK,iBAAiB,sBAAsB,OAAO;AAAA,EAC5D;AACF;;;ADtBA,IAAME,OAAMC,QAAO,8CAA8C;AAOjE,eAAsB,gBACpB,KACA,SACAC,YACe;AACf,MAAI,CAAC,IAAI,kBAAkB,WAAW,IAAI,sBAAsB,CAACA,YAAW;AAC1E;AAAA,EACF;AACA,MAAI,qBAAqB,IAAI;AAAA,IAC3B,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AACA,MAAI,sBAAsBA;AAC1B,EAAAF,KAAI,KAAK,6CAAsCE,UAAS,EAAE;AAC1D,QAAM,IAAI,mBAAmB,MAAM,OAAO;AAC5C;AAQO,SAAS,wBACd,KACAA,YACS;AACT,SACE,IAAI,uBAAuB,UAC3BA,eAAc,UACd,IAAI,wBAAwBA;AAEhC;AASO,SAAS,2BACd,KACA,SACM;AACN,MAAI,IAAI,sBAAsB,CAAC,IAAI,kBAAkB,SAAS;AAC5D;AAAA,EACF;AACA,QAAMA,aAAY,QAAQ,aAAa;AACvC,MAAI,CAAC,wBAAwB,KAAKA,UAAS,GAAG;AAC5C;AAAA,EACF;AACA,EAAAF,KAAI,KAAK,4DAAgDE,UAAS,EAAE;AACpE,MAAI,qBAAqB,IACtB,0BAA0B,EAC1B,KAAK,MAAM,gBAAgB,KAAK,SAASA,UAAS,CAAC,EACnD;AAAA,IAAM,CAAC,QACNF,KAAI,KAAK,+BAA+B,aAAa,GAAG,CAAC,EAAE;AAAA,EAC7D,EACC,QAAQ,MAAM;AACb,QAAI,qBAAqB;AAAA,EAC3B,CAAC;AACL;;;AflDA,IAAMG,QAAMC,SAAO,wCAAwC;AAuC3D,SAAS,kBAAkB,KAA2B;AAKpD,MAAI,eAAe,IAAI;AAAA,IACrB,CAAC,eAAe;AACd,UAAI,IAAI,iBAAiB;AACvB,YAAI,gBAAgB,aAAa,UAAU,UAAU;AAAA,MACvD;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,IAAI,WAAW,GAAG;AAAA,EAC7B;AACA,MAAI,cAAc,IAAI;AAAA,IAAY,IAAI;AAAA,IAAc,CAAC,KAAK,UACxD,IAAI,cAAc,KAAK,KAAK;AAAA,EAC9B;AACA,MAAI,eAAe,IAAI,aAAa,IAAI,YAAY;AACpD,MAAI,eAAe,IAAI;AAAA,IACrB,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,MAAM,IAAI,eAAe,KAAK,IAAI,wBAAwB;AAAA,IAC1D,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,IAKJ,CAAC,YAAY;AACX,iCAA2B,KAAK,OAAO;AACvC,6BAAuB,KAAK,OAAO;AAAA,IACrC;AAAA,EACF;AACF;AAIA,SAAS,aAAa,SAA4B;AAChD,SAAO,gBAAgB,QAAQ,gBAAgB,CAAC,GAAG;AAAA,IACjD,WAAW,CAAC,SAAS,iBAAiB,SAAS,IAAI;AAAA,IACnD,eAAe,MAAMC,cAAa,SAAS,aAAa;AAAA,IACxD,QAAQ,CAAC,YAAYF,MAAI,KAAK,OAAO;AAAA,EACvC,CAAC;AACH;AAEA,eAAe,yBACb,KACA,SACe;AACf,QAAM,eAAe,QAAQ,gBAAgB,CAAC;AAC9C,QAAM,sBAAsB,QAAQ,uBAAuB,CAAC;AAC5D,QAAMG,aAAY,QAAQ;AAC1B,QAAM,OAAO,QAAQ,WAAW,CAAC;AAEjC,MAAI,IAAI,WAAW,WAAW,GAAG;AAC/B,UAAM,KAAM,KAA6C;AACzD,QAAI,aAAa,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,EACzD;AAEA,QAAM,WAAW,MAAM,aAAa,OAAO;AAC3C,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B;AAAA,IACA,WAAAA;AAAA,IACA,SAAS,KAAK;AAAA,IACd,MAAM,KAAK,WAAW;AAAA,IACtB,SAAS,IAAI,qBAAqB;AAAA,IAClC,QAAQ,IAAI;AAAA,IACZ,KAAK;AAAA,EACP;AAEA,MAAI,gBAAgB,WAAW;AAC/B,MAAI,gBAAgB,aAAa,YAAY,QAAQ;AAErD,QAAM,cACJ,aAAa,eAAe,oBAAoB,eAAe;AACjE,QAAM,iBACJ,aAAa,kBACZ,aAAsC,WACvC;AACF,EAAAH,MAAI;AAAA,IACF,mBAAc,WAAW,GAAG,iBAAiB,MAAM,iBAAiB,EAAE,cAAcG,UAAS;AAAA,EAC/F;AAEA,QAAM,eAAiB,aACrB,mBACF,KACG,oBAAgD,mBAAmB,KACpE,CAAC;AACH,MAAI,CAAC,aAAa,eAAe,CAAC,IAAI,aAAa;AACjD,IAAAH,MAAI;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMA,eAAe,cACb,KACA,SACe;AACf,MAAI,CAAC,IAAI,aAAa;AACpB;AAAA,EACF;AACA,QAAM,SAAU,QAAiC;AACjD,MAAI,CAAC,QAAQ;AACX,IAAAA,MAAI,KAAK,8DAAyD;AAClE;AAAA,EACF;AACA,QAAM,EAAE,oBAAoB,eAAe,IAAI,MAAM,OAAO,oBAAW;AACvE,QAAM,KAAK,MAAM;AAAA,IACf;AAAA,IACA,eAAe,IAAI,eAAe;AAAA,EACpC;AACA,MAAI,IAAI;AACN,QAAI,gBAAgB,aAAa;AACjC,IAAAA,MAAI,KAAK,+DAAqD;AAAA,EAChE;AACF;AAcA,eAAe,mBACb,KACA,SACe;AACf,QAAM,SAAU,QAAiC;AACjD,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AACA,MAAI,gBAAgB,oBAAoB,MAAM;AAAA,IAC5C;AAAA,IACA,CAAC,OAAO,YAAYA,MAAI,KAAK,EAAE,OAAO;AAAA,EACxC;AACF;AAoBA,eAAe,qBACb,KACA,SACe;AACf,MAAI,iBAAiB,QAAQ,aAAa;AAC1C,QAAM,mBAAmB,KAAK,OAAO;AACrC,QAAM,cAAc,KAAK,OAAO;AAClC;AAOO,SAAS,kBACd,KACAG,YACS;AACT,SACE,QAAQ,IAAI,eAAe,KAC3BA,eAAc,UACd,IAAI,mBAAmB,UACvB,IAAI,mBAAmBA;AAE3B;AAaA,eAAe,mBACb,KACA,SACe;AACf,QAAMA,aAAY,QAAQ,aAAa;AACvC,MAAI,CAAC,kBAAkB,KAAKA,UAAS,GAAG;AACtC;AAAA,EACF;AAMA,MAAI,gBAAgB,oBAAoB;AACxC,MAAI,gBAAgB,aAAa;AACjC,EAAAH,MAAI,KAAK,iDAA4CG,UAAS,EAAE;AAChE,QAAM,qBAAqB,KAAK,OAAO;AACzC;AAIO,SAAS,uBACd,KACA,SACM;AACN,OAAK,mBAAmB,KAAK,OAAO,EAAE;AAAA,IAAM,CAAC,QAC3CH,MAAI,KAAK,0BAA0B,aAAa,GAAG,CAAC,EAAE;AAAA,EACxD;AACF;AAKA,eAAe,uBACb,KACA,SACe;AACf,EAAAA,MAAI,KAAK,8DAAyD;AAGlE,MAAI,mBAAmB;AACvB,QAAM,yBAAyB,KAAK,OAAO;AAC3C,QAAM,mBAAmB,KAAK,OAAO;AACrC,6BAA2B,KAAK,OAAO;AACvC,QAAM,IAAI;AACZ;AAEA,eAAsB,yBACpB,KACA,SACe;AACf,QAAM,mBAAmB,QAAQ;AACjC,QAAM,kBACJ,oBACA,IAAI,iBACJ,qBAAqB,IAAI;AAC3B,MAAI,gBAAgB,oBAAoB;AACxC,MAAI,IAAI,iBAAiB;AAGvB,QAAI,gBAAgB,WAAW,OAAO;AACtC,QAAI,iBAAiB;AACnB,YAAM,uBAAuB,KAAK,OAAO;AAAA,IAC3C;AACA;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IAAQ,CAACI,aACjB,WAAWA,UAAS,OAAO,uBAAuB;AAAA,EACpD;AAGA,MAAI,kBAAkB,IAAI;AAAA,IACxB,IAAI,SAAS,UAAU,CAAC,IAAI,EAAE,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS;AAAA,IACrE;AAAA,EACF;AACA,MAAI,gBAAgB,YAAY,IAAI;AACpC,MAAI,gBAAgB,SAAS,IAAI;AACjC,MAAI,IAAI,SAAS,SAAS;AACxB,UAAM,YAAY,MAAM,IAAI,gBAAgB,kBAAkB,GAAI;AAClE,QAAI,CAAC,WAAW;AACd,MAAAJ,MAAI,MAAM,4CAAuC;AAAA,IACnD;AAAA,EACF;AACA,oBAAkB,GAAG;AACrB,QAAM,yBAAyB,KAAK,OAAO;AAC3C,QAAM,qBAAqB,KAAK,OAAO;AACvC,QAAM,gBAAgB,KAAK,SAAS,QAAQ,SAAS;AACvD;AAEA,eAAsB,0BACpB,KACe;AACf,MAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,qBAAqB;AACvD;AAAA,EACF;AACA,MAAI,IAAI,SAAS,SAAS;AAIxB,UAAM,IAAI,mBAAmB,KAAK;AAAA,EACpC,OAAO;AACL,UAAM,mBAAmB;AAAA,MACvB,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,MACf,gBAAgB;AAAA,MAChB,WAAW,wBAAwB;AAAA,QACjC,cAAc,IAAI,cAAc,yBAAyB,KAAK;AAAA,QAC9D,YAAY,IAAI;AAAA,MAClB,CAAC;AAAA,MACD,eAAe,IAAI,kBAAkB;AAAA,MACrC,cAAc,CAAC,OAAO,SACpB,IAAI,iBAAiB,aAAa,OAAO,IAAI;AAAA,MAC/C,OAAO,CAAC,OAAO,YAAYA,MAAI,KAAK,EAAE,OAAO;AAAA,IAC/C,CAAC;AAAA,EACH;AACA,MAAI,qBAAqB;AACzB,MAAI,sBAAsB;AAC5B;;;AiBxWO,SAAS,yBACd,OACoB;AACpB,QAAM,aAAa,MAAM,cAAc,CAAC;AACxC,MAAI,MAAM,SAAS,SAAS;AAC1B,WAAO,EAAE,SAAS,WAAW;AAAA,EAC/B;AACA,MAAI,MAAM,aAAa,MAAM,UAAU,OAAO;AAC5C,WAAO,EAAE,SAAS,EAAE,GAAG,YAAY,SAAS,KAAK,EAAE;AAAA,EACrD;AACA,MAAI,WAAW,YAAY,MAAM;AAC/B,WAAO;AAAA,MACL,SAAS,CAAC;AAAA,MACV,SAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,EAAE,SAAS,WAAW;AAC/B;AAMO,IAAM,mBAAN,MAAuB;AAAA;AAAA,EAEnB;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAGA,eAAkC,CAAC;AAAA,EAC1B;AAAA;AAAA;AAAA,EAIT,YAAY,SAA4B,YAAqB;AAC3D,SAAK,UAAU,EAAE,GAAG,qBAAqB,GAAG,QAAQ;AACpD,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAA4B;AAC9B,WAAO,4BAA4B,KAAK,cAAc,KAAK,QAAQ;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,KAAoC;AACjD,QAAI,KAAK,eAAe,KAAK,UAAU;AACrC,WAAK,aAAa,KAAK,GAAG,KAAK,SAAS,MAAM;AAAA,IAChD;AACA,UAAM,0BAA0B,GAAG;AAAA,EACrC;AACF;;;AC/FA,OAAOK,cAAY;AAMnB,IAAMC,QAAMC,SAAO,qCAAqC;AAWxD,SAAS,2BACP,UACyB;AACzB,SAAO,CAAC,SAAkB;AACxB,QAAI;AACF,YAAM,KACF,MACE,YAAoD,CAAC;AAC3D,YAAM,WAAW,SAAS,mBAAmB;AAC7C,YAAM,sBAAsB,GAAG;AAC/B,YAAMC,aAAY,GAAG;AACrB,UAAI,CAAC,YAAa,CAAC,uBAAuB,CAACA,YAAY;AACrD;AAAA,MACF;AACA,eAAS,aAAa,YAAY;AAAA,QAChC;AAAA,QACA,cAAc,uBAAuB,CAAC;AAAA,QACtC,WAAAA;AAAA,QACA,SAAS,GAAG;AAAA,QACZ,MAAM,GAAG;AAAA,QACT,YAAY,GAAG;AAAA,QACf,SAAS,SAAS,qBAAqB;AAAA,MACzC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,MAAAF,MAAI,MAAM,2BAA2B,aAAa,GAAG,CAAC,EAAE;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,SAAS,sBACd,UACA,UACM;AACN,WAAS,kBAAkB,SAAS,WAAW,UAAU;AACzD,MAAI,SAAS,WAAW,YAAY;AAClC,IAAAA,MAAI,KAAK,wDAAmD;AAAA,EAC9D;AACA,EAAAA,MAAI,KAAK,uEAA6D;AACtE,QAAM,UAAU,2BAA2B,QAAQ;AACnD,WAAS,GAAG,oBAAoB,OAAO;AACvC,WAAS,GAAG,kBAAkB,OAAO;AACvC;;;ACtDA,OAAOG,cAAY;;;ACJZ,SAAS,0BAA0B,QAAqC;AAC7E,QAAM,SAAS;AAAA,IACZ,QAAiC,UAAU;AAAA,EAC9C,EAAE,YAAY;AACd,MAAI,WAAW,UAAU;AACvB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,WAAW,WAAW;AACxB,WAAO,WAAW;AAAA,EACpB;AACA,SAAO,WAAW;AACpB;AAUO,SAAS,eACd,OACA,eACA,MAAY,oBAAI,KAAK,GACf;AACN,aAAW,QAAQ,MAAM,OAAO;AAC9B,QAAI,OAAO,SAAS,UAAU;AAC5B;AAAA,IACF;AACA,QAAI,KAAK,UAAU,aAAa,KAAK,UAAU,WAAW;AACxD,WAAK,QACH,kBAAkB,WAAW,SACzB,WAAW,SACX,WAAW;AACjB,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AACF;;;ACTA,SAAS,sBACP,OACA,aACA,SACA,GAC6B;AAC7B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,OAAO,MAAM,CAAC;AACpB,QAAM,UAAU,aAAa,CAAC,KAAK;AACnC,QAAM,YAAY,UAAU,GAAG,OAAO,IAAI,KAAK,IAAI,KAAK,KAAK;AAC7D,QAAM,aAAa,uBAAuB,cAAc,KAAK,IAAI;AACjE,QAAM,aAAa,aACf,GAAG,WAAW,QAAQ,IAAI,WAAW,IAAI,KACzC,kBAAkB,UAAU,CAAC,IAAI,IAC/B,GAAG,cAAc,IAAI,UAAU,CAAC,CAAC,KACjC;AACN,SAAO;AAAA;AAAA;AAAA,IAGL,KAAK,iBAAiB,YAAY,QAAQ,WAAW,IAAI,KAAK,IAAI,EAAE;AAAA,IACpE,KAAK,SAAS;AAAA,IACd,OAAO;AAAA,IACP,WAAW,GAAG,YAAY,IAAI,SAAS;AAAA,IACvC,QAAQ;AAAA,IACR,OAAO,WAAW;AAAA,IAClB,OAAO,oBAAI,KAAK;AAAA,IAChB,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA;AAAA;AAAA,IAGN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,OAAO,CAAC;AAAA,IACR;AAAA,EACF;AACF;AAEO,SAAS,2BACd,OACY;AACZ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAIJ,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,YAAY,YAAY,IAAI,YAAY;AAAA,EAC1C;AACA,QAAM,UAAU,MAAM,gBAAgB,aAAa,UAAU,KAAK;AAClE,QAAM,gBAA4B;AAAA,IAChC,KAAK;AAAA,IACL,KAAK,SAAS;AAAA,IACd,OAAO;AAAA,IACP,WAAW,GAAG,WAAW,IAAI,YAAY;AAAA,IACzC,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,oBAAI,KAAK;AAAA,IAChB,OAAO,WAAW;AAAA,IAClB,KAAK;AAAA,IACL,OAAO,CAAC;AAAA,IACR,QAAQ,CAAC;AAAA,IACT,OAAO,CAAC;AAAA,IACR,WAAW;AAAA,IACX,YACE,kBAAkB,eAAe,IAC7B,GAAG,cAAc,IAAI,YAAY,KACjC;AAAA,EACR;AACA,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,kBAAc,MAAM;AAAA,MAClB,sBAAsB,OAAO,aAAa,SAAS,CAAC;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;;;AClIA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAqBV,SAAS,gBAAgB,YAAqC;AACnE,QAAM,iBAAiBA,MAAK,QAAQ,QAAQ,IAAI,GAAG,UAAU;AAC7D,QAAM,SAA0B;AAAA,IAC9B,aAAaA,MAAK,SAAS,YAAY,UAAU;AAAA,IACjD,gBAAgB;AAAA,IAChB;AAAA,IACA,cAAc,CAAC;AAAA,IACf,eAAe,CAAC;AAAA,EAClB;AAEA,MAAI,eAAe,qBAAqB,CAACD,IAAG,WAAW,cAAc,GAAG;AACtE,WAAO;AAAA,EACT;AAEA,SAAO,iBAAiBA,IAAG,aAAa,gBAAgB,OAAO;AAC/D,QAAM,QAAQ,OAAO,eAAe,MAAM,sBAAsB;AAChE,MAAI,OAAO;AACT,WAAO,cAAc,MAAM,CAAC,EAAE,KAAK;AAAA,EACrC;AACA,SAAO,cAAc,KAAK,cAAc;AAExC,QAAM,aAAaC,MAAK,QAAQ,cAAc;AAC9C,QAAM,oBAAoB,CAAC,oBAAoB,SAAS,SAAS;AACjE,aAAW,aAAa,mBAAmB;AACzC,UAAM,UAAUA,MAAK,KAAK,YAAY,SAAS;AAC/C,QAAI,CAACD,IAAG,WAAW,OAAO,KAAK,CAACA,IAAG,SAAS,OAAO,EAAE,YAAY,GAAG;AAClE;AAAA,IACF;AACA,eAAW,SAASA,IAAG,YAAY,OAAO,GAAG;AAC3C,UAAI,CAAC,qBAAqB,KAAK,KAAK,GAAG;AACrC;AAAA,MACF;AACA,YAAM,eAAeC,MAAK,KAAK,SAAS,KAAK;AAC7C,aAAO,cAAc,KAAK,YAAY;AACtC,UAAI;AACF,eAAO,aAAa,KAAK;AAAA,UACvB,UAAU;AAAA,UACV,SAASD,IAAG,aAAa,cAAc,OAAO;AAAA,QAChD,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACnCA,SAAS,YAAY,KAA4B;AAC/C,SAAO,IAAI,aAAa,IAAI,qBAAqB;AACnD;AAEA,SAAS,gBAAgB,KAAwC;AAC/D,SAAO;AAAA,IACL,YAAY,IAAI;AAAA,IAChB,cAAc,IAAI;AAAA,IAClB,UAAU,IAAI;AAAA;AAAA,IAEd,iBAAiB,IAAI,gBAAgB;AAAA,EACvC;AACF;AAEA,SAAS,cAAc,KAAmB,WAAmC;AAC3E,MAAI,CAAC,WAAW;AACd;AAAA,EACF;AAEA,OAAK,IAAI,gBAAgB,SAAS;AACpC;AAOO,SAAS,wBACd,KACA,UACM;AACN,QAAM,YAAY;AAAA,IAChB,gBAAgB,GAAG;AAAA,IACnB;AAAA,IACA,IAAI;AAAA,EACN;AACA,gBAAc,KAAK,SAAS;AAC9B;AAOO,SAAS,wBACd,KACA,UACA,SACM;AACN,MAAI,CAAC,YAAY,GAAG,GAAG;AACrB;AAAA,EACF;AACA,sBAAoB,gBAAgB,GAAG,GAAG,QAAQ,UAAU,OAAO;AACrE;AAOO,SAAS,eAAe,KAAyB;AACtD,MAAI,CAAC,YAAY,GAAG,GAAG;AACrB;AAAA,EACF;AACA,QAAM,QAAQ,mBAAmB,IAAI,UAAU;AAC/C,MAAI,CAAC,OAAO;AACV;AAAA,EACF;AAEA,OAAK,IAAI,gBAAgB,KAAK;AAChC;;;AJ9DA,IAAME,QAAMC,SAAO,oCAAoC;AAoCvD,SAAS,wBACP,KACA,cACA,eACM;AAGN,QAAM,cAAc,aAAa,OAAO;AAAA,IACtC,CAAC,MAAkB,EAAE,QAAQ,cAAc;AAAA,EAC7C;AACA,MAAI,gBAAgB,IAAI;AACtB,iBAAa,OAAO,WAAW,IAAI;AAInC,QAAI,gBAAgB,aAAa,SAAS,oBAAoB;AAAA,MAC5D,KAAK,cAAc;AAAA,MACnB,WAAW;AAAA,IACb,CAAC;AAAA,EACH,OAAO;AACL,iBAAa,OAAO,KAAK,aAAa;AAAA,EACxC;AACF;AAEA,SAAS,mBACP,KACA,YACA,aACA,gBACA,gBACA,cACA,OAMA;AACA,QAAM,eAAe,IAAI,aAAa;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA,CAAC;AAAA,EACH;AACA,MAAI,aAAa,mBAAmB,YAAY;AAChD,QAAM,EAAE,aAAa,cAAc,WAAW,aAAa,IACzD;AAAA,IACE;AAAA,IACA;AAAA,IACA,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACzB;AACF,MAAI,kBAAkB,cAAc,GAAG;AACrC,iBAAa,aAAa,GAAG,cAAc,IAAI,WAAW;AAAA,EAC5D;AACA,SAAO,EAAE,cAAc,cAAc,WAAW,aAAa;AAC/D;AAEA,SAAS,eACP,aACyB;AACzB,UAAQ,eAAe,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,EAAE;AAChE;AAEA,SAAS,sBACP,KACA,OACM;AACN,aAAW,KAAK,OAAO;AACrB,QAAI,gBAAgB,cAAc,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACrD;AACF;AAEA,eAAsB,qBACpB,KACA,SACA,QACe;AACf,QAAM,IAAI,yBAAyB,OAAO;AAC1C,QAAM,aAAqB,OAAO,OAAO;AACzC,QAAM,eAAuB,OAAO,QAAQ;AAC5C,QAAM,QAAQ,eAAe,OAAO,KAAK;AACzC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,gBAAgB,UAAU;AAC9B,wBAAsB,KAAK,aAAa;AACxC,QAAM,EAAE,cAAc,cAAc,WAAW,aAAa,IAC1D;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF,QAAM,gBAAgB,2BAA2B;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,uBAAuB,aAAa;AAAA,IACpC,eAAe,CAAC,KAAK,aAAa,IAAI,cAAc,KAAK,QAAQ;AAAA,EACnE,CAAC;AACD,0BAAwB,KAAK,cAAc,aAAa;AAExD,0BAAwB,KAAK,YAAY,cAAc,GAAG;AAC1D,MAAI,wBAAwB,aAAa;AACzC,MAAI,eAAe,IAAI;AACvB,MAAI,eAAe,IAAI;AACvB,MAAI,aAAa,aAAa;AAC9B,MAAI,gBAAgB,OAAO;AAC3B,EAAAD,MAAI,KAAK,uBAAgB,YAAY,EAAE;AACzC;AAEA,eAAsB,yBACpB,KACA,UACA,QACA,QACe;AACf,MAAI;AACF,UAAM,gBAAgB,0BAA0B,MAAM;AACtD,UAAM,WAAW,IAAI,wBAAwB;AAC7C,QAAI,UAAU;AACZ,YAAM,MAAM,oBAAI,KAAK;AACrB,YAAM,WACJ,IAAI,QAAQ,KAAK,SAAS,OAAO,QAAQ,KAAK,IAAI,QAAQ;AAC5D,eAAS,QAAQ;AACjB,eAAS,MAAM;AACf,eAAS,YAAY;AAGrB,UAAI,cAAc,SAAS,KAAK,aAAa;AAC7C,qBAAe,UAAU,eAAe,GAAG;AAE3C,YAAM,aAAqB,QAAQ,OAAO;AAC1C,UAAI,YAAY,oBAAoB,YAAY,QAAQ,QAAQ,EAAE;AAElE,YAAM,eAAe,IAAI,aAAa,SAAS,UAAU;AACzD,UAAI,cAAc;AAEhB,YAAI,aAAa,mBAAmB,YAAY;AAAA,MAClD;AAEA,UAAI,eAAe,aAAa;AAChC,YAAM,OAAO,IAAI,SAAS,aAAa;AACvC,MAAAA,MAAI;AAAA,QACF,KAAK,IAAI,IAAI,QAAQ,QAAQ,SAAS,MAAM,WAAW,KAAM,QAAQ,CAAC,CAAC;AAAA,MACzE;AAEA,UAAI,aAAa,aAAa;AAC9B,UAAI,wBAAwB,IAAI;AAChC,UAAI,eAAe,IAAI;AACvB,UAAI,eAAe,IAAI;AAAA,IACzB;AAAA,EAKF,SAAS,KAAK;AACZ,IAAAA,MAAI,MAAM,yCAAyC,aAAa,GAAG,CAAC,EAAE;AAAA,EACxE;AACF;AAWA,eAAsB,kCACpB,KACA,SACA,QACe;AACf,QAAM,WAAW,IAAI,wBAAwB;AAC7C,MAAI;AAQF,UAAM,cAAc,IAAI,cAAc,uBAAuB,KAAK,CAAC;AACnE,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM;AAAA,QACJ,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAGA,UAAM,IAAI,gBAAgB,aAAa,SAAS,IAAI;AACpD,mBAAe,GAAG;AAClB,UAAM,IAAI;AAAA,MACR,UAAU;AAAA,MACV,0BAA0B,MAAM,MAAM,WAAW;AAAA,IACnD;AAAA,EACF,SAAS,KAAK;AACZ,IAAAA,MAAI;AAAA,MACF,oDAAoD,aAAa,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAEA,eAAsB,mBACpB,KACA,UACA,YACA,SACe;AACf,QAAM,WAAW,IAAI,wBAAwB;AAC7C,MAAI,CAAC,UAAU;AACb;AAAA,EACF;AAGA,MAAI,cAAc,qBAAqB;AAEvC,QAAM,WAAmB,YAAY,QAAQ;AAC7C,QAAM,OAAQ,SAAS,MAAkC;AAAA,IACvD,CAAC,MACC,OAAO,MAAM,aACZ,EAAE,OAAO,SAAS,QAAQ,MAAM,QAAQ,EAAE,UAAU;AAAA,EACzD;AACA,MAAI,MAAM;AACR,SAAK,QAAQ,WAAW;AACxB,SAAK,QAAQ,oBAAI,KAAK;AACtB,SAAK,MAAM;AACX,QAAI,eAAe,IAAI;AACvB,QAAI,aAAa,aAAa;AAAA,EAChC;AACF;AAEA,eAAsB,kBACpB,KACA,UACA,QACA,YACA,SACe;AACf,QAAM,OAAO,IAAI,eAAe;AAChC,MAAI,CAAC,MAAM;AACT;AAAA,EACF;AACA,QAAM,SAAS,OAAO,QAAQ,UAAU,SAAS,EAAE,YAAY;AAC/D,QAAM,YACJ,WAAW,WACP,WAAW,SACX,WAAW,YACT,WAAW,UACX,WAAW;AACnB,OAAK,QAAQ;AACb,OAAK,MAAM,oBAAI,KAAK;AACpB,OAAK,YAAY,KAAK,IAAI,KAAK,KAAK,OAAO,QAAQ,KAAK,KAAK,IAAI;AACjE,MAAI,eAAe,IAAI;AACvB,MAAI,aAAa,aAAa;AAC9B,OAAK;AACP;AAEA,eAAsB,eACpB,KACA,SACA,QACe;AACf,MAAI,kBAAkB,IAAI;AAC1B,QAAM,qBAAqB,KAAK,SAAS,MAAM;AACjD;AAEA,eAAsB,cACpB,KACA,SACA,QACA,QACe;AACf,QAAM,yBAAyB,KAAK,SAAS,QAAQ,MAAM;AAC7D;;;AKvWA,OAAOE,cAAY;;;ACVnB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAcV,SAAS,oBACd,UACA,YACA,YACA,YACe;AACf,MAAI,WAA0B,sBAAsB,KAAK;AACzD,MAAI,CAAC,YAAY,cAAc,WAAW,SAAS,QAAQ,GAAG;AAC5D,eAAW;AAAA,EACb;AACA,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,QAAQ,IAAI;AAMlC,QAAM,cAAc,cAAc,IAAI,QAAQ,OAAO,GAAG;AACxD,QAAM,iBAAiB,WAAW;AAAA,IAAQ,CAAC,OACzC,aACI;AAAA,MACEC,MAAK,QAAQ,eAAe,IAAI,aAAa,KAAK;AAAA,MAClDA,MAAK,QAAQ,eAAe,IAAI,aAAa,KAAK;AAAA,MAClDA,MAAK,QAAQ,eAAe,IAAI,aAAa,MAAM;AAAA,MACnDA,MAAK,QAAQ,eAAe,IAAI,UAAU;AAAA,IAC5C,IACA,CAAC;AAAA,EACP;AACA,QAAM,gBAAgB;AAAA,IACpB,GAAG;AAAA;AAAA,IAEH,GAAI,aACA;AAAA,MACEA,MAAK,QAAQ,eAAe,aAAa,KAAK;AAAA,MAC9CA,MAAK,QAAQ,eAAe,aAAa,KAAK;AAAA,MAC9CA,MAAK,QAAQ,eAAe,aAAa,MAAM;AAAA,MAC/CA,MAAK,QAAQ,eAAe,UAAU;AAAA,IACxC,IACA,CAAC;AAAA,IACLA,MAAK,QAAQ,eAAe,SAAS,WAAW,KAAK;AAAA,IACrDA,MAAK,QAAQ,eAAe,QAAQ,WAAW,KAAK;AAAA,IACpDA,MAAK,QAAQ,eAAe,WAAW,KAAK;AAAA,EAC9C;AAEA,aAAW,aAAa,eAAe;AACrC,QAAIC,IAAG,WAAW,SAAS,GAAG;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACvEA,OAAOC,cAAY;AAMnB,IAAMC,QAAMC,SAAO,6CAA6C;AAyBhE,eAAsB,kBACpB,OACe;AACf,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI,UAAU,YAAY,KAAK,GAAG;AAChC,UAAM,WAAW,UAAU,YAAY,KAAK;AAC5C,UAAM,YAAY,mBAAmB,QAAQ;AAC7C,gBAAY,QAAQ;AACpB,gBAAY,MAAM,oBAAI,KAAK;AAC3B,gBAAY,YAAY,WAAW,SAAS,QAAQ,GAAG,IAAI;AAC3D,gBAAY,gBAAgB,aAAa,SAAS;AAClD,gBAAY,oBAAoB,UAAU,YAAY,KAAK;AAC3D,mBAAe,SAAS;AACxB,IAAAD,MAAI;AAAA,MACF,KAAK,SAAS,SAAS,CAAC,IAAI,YAAY,KAAK,MAAM,YAAY,YAAY,KAAM,QAAQ,CAAC,CAAC;AAAA,IAC7F;AAAA,EACF,OAAO;AACL,UAAM,UAAU,oBAAI,KAAK;AACzB,UAAM,WAAW,QAAQ,QAAQ,KAAK,YAAY,OAAO,QAAQ,KAAK;AACtE,gBAAY;AAAA,MACV;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF;AACA,gBAAY,oBAAoB,UAAU,YAAY,KAAK;AAC3D,mBAAe,WAAW,MAA4B;AACtD,IAAAA,MAAI,KAAK,YAAO,YAAY,KAAK,MAAM,WAAW,KAAM,QAAQ,CAAC,CAAC,IAAI;AAAA,EACxE;AAGA,QAAM,IAAI,QAAQ,CAACE,aAAY,WAAWA,UAAS,OAAO,eAAe,CAAC;AAC5E;;;AFzCA,IAAMC,QAAMC,SAAO,0CAA0C;AAyBtD,SAAS,qBACd,KACA,aACe;AACf,QAAM,aAAa,YAAY,UAAU;AACzC,QAAM,WACJ,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK,cAAc,SAAS;AACxD,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,IAAI;AAAA,IACJ,IAAI,aAAa,uBAAuB,KAAK;AAAA,EAC/C;AACA,MAAI,CAAC,UAAU;AACb,IAAAD,MAAI;AAAA,MACF,iDAAiD,QAAQ;AAAA,IAC3D;AAAA,EACF;AACA,MAAI,aAAa;AACjB,MAAI,YAAsB,CAAC;AAC3B,MAAI,YAA2B;AAC/B,MAAI,YAAsB,CAAC;AAC3B,MAAI,UAAU;AACZ,UAAM,SAAS,oBAAoB,QAAQ;AAC3C,QAAI,OAAO,YAAY;AACrB,mBAAa,OAAO;AAAA,IACtB;AACA,gBAAY,OAAO;AACnB,gBAAY,OAAO;AACnB,gBAAY,OAAO;AAAA,EACrB;AACA,QAAM,aAAa,IAAI,cAAc;AACrC,MAAI,YAAY;AACd,UAAM,cAAc,UAAU,UAAU,CAAC,SAAS,SAAS,UAAU;AACrE,QAAI,gBAAgB,IAAI;AACtB,kBAAY,CAAC,UAAU,WAAW,CAAC;AACnC,kBAAY,UAAU,WAAW,IAAI,CAAC,UAAU,WAAW,CAAC,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AACA,SAAO,EAAE,UAAU,UAAU,YAAY,WAAW,WAAW,UAAU;AAC3E;AAEO,SAAS,oBACd,aACA,WACA,gBACoB;AACpB,SACE,cAAc,aAAa,MAAM,SAAS,KAC1C,UAAU,KAAK,CAAC,SAAS,CAAC,eAAe,IAAI,IAAI,CAAC;AAEtD;AAEA,eAAsB,cACpB,KACA,cACA,iBACA,gBACA,UACe;AACf,MAAI,eAAe,SAAS,GAAG;AAC7B,QAAI,aAAa,mBAAmB,YAAY;AAAA,EAClD;AACA,QAAM,OAAO,IAAI,YAAY,gBAAgB,cAAc,eAAe;AAC1E,MAAI,MAAM;AAER,SAAK,UAAU,IAAI,cAAc,KAAK,KAAK,YAAY,MAAS;AAChE,QAAI,UAAU;AACZ,8BAAwB,KAAK,UAAU,KAAK,GAAG;AAAA,IACjD;AACA,SAAK,QAAQ,WAAW;AACxB,SAAK,QAAQ,oBAAI,KAAK;AACtB,SAAK,MAAM;AACX,QAAI,aAAa,YAAY,IAAI;AACjC,QAAI,eAAe,IAAI;AACvB,IAAAA,MAAI,KAAK,YAAO,eAAe,EAAE;AACjC,UAAM,IAAI,QAAQ,CAACE,aAAY,WAAWA,UAAS,OAAO,gBAAgB,CAAC;AAAA,EAC7E,OAAO;AACL,IAAAF,MAAI;AAAA,MACF,SAAS,eAAe,yBAAyB,aAAa,KAAK;AAAA,IACrE;AACA,QAAI,eAAe,IAAI;AAAA,EACzB;AACF;AAEA,eAAsB,yBACpB,KACA,cACA,UACA,aACe;AACf,QAAM,cAAc,aAAa,MAAM;AAAA,IACrC,CAAC,MACC,OAAO,MAAM,YAAY,EAAE,UAAU,WAAW;AAAA,EACpD;AACA,MAAI,CAAC,aAAa;AAChB;AAAA,EACF;AACA,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,WAAW,aAAa,SAAS,aAAa,CAAC;AAAA,IAC/C,aAAa,IAAI;AAAA,IACjB,gBAAgB,CAAC,UAAU,IAAI,eAAe,KAAK;AAAA,IACnD,UAAU,CAAC,UAAU,IAAI,SAAS,KAAK;AAAA,EACzC,CAAC;AACH;AAEO,SAAS,gBACd,KACA,SACM;AACN,MAAI,CAAC,IAAI,kBAAkB;AACzB,QAAI,aAAa,cAAc,OAAO;AACtC,QAAI,mBAAmB;AAAA,EACzB;AACA,MAAI,aAAa,kBAAkB;AACnC,MAAI,aAAa,oBAAoB,OAAO;AAC9C;AAEA,SAAS,2BACP,KACA,cACA,UACA,SACA,gBACM;AACN,QAAM,cAAc,aAAa,MAAM;AAAA,IACrC,CAAC,MACC,OAAO,MAAM,YAAY,EAAE,UAAU,WAAW;AAAA,EACpD;AACA,MAAI,CAAC,eAAe,eAAe,IAAI,YAAY,KAAK,GAAG;AACzD;AAAA,EACF;AACA,QAAM,UAAU,QAAQ,UAAU,KAAK,MAAM,QAAQ,UAAU,KAAK;AACpE,QAAM,YAAgC,SAClC,WAAW,SACX,WAAW;AACf,QAAM,UAAU,oBAAI,KAAK;AACzB,QAAM,WAAW,QAAQ,QAAQ,KAAK,YAAY,OAAO,QAAQ,KAAK;AACtE,MAAI,YAAY,gBAAgB,aAAa,WAAW,SAAS,QAAQ;AACzE,MAAI,YAAY,oBAAoB,UAAU,YAAY,KAAK;AAC/D,MAAI,eAAe,SAAS;AAC5B,QAAM,OAAO,IAAI,SAAS,SAAS;AACnC,EAAAA,MAAI,KAAK,KAAK,IAAI,IAAI,YAAY,KAAK,MAAM,WAAW,KAAM,QAAQ,CAAC,CAAC,IAAI;AAC9E;AAEA,eAAe,uBACb,KACA,cACA,UACA,WACA,gBACe;AACf,QAAM,gBAAgB,OAAO,KAAK,SAAS;AAC3C,QAAM,mBAAmB,cAAc;AAAA,IACrC,CAAC,SAAS,CAAC,eAAe,IAAI,IAAI;AAAA,EACpC;AACA,aAAW,mBAAmB,kBAAkB;AAC9C,UAAM,WAAW,UAAU,eAAe;AAC1C,UAAM,YAAY,mBAAmB,QAAQ;AAC7C,UAAM,OAAO,IAAI,YAAY,gBAAgB,cAAc,eAAe;AAC1E,QAAI,MAAM;AACR,YAAM,MAAM,WAAW,SAAS,QAAQ,GAAG,IAAI;AAC/C,UAAI,YAAY,gBAAgB,MAAM,WAAW,oBAAI,KAAK,GAAG,GAAG;AAChE,UAAI,eAAe,SAAS;AAC5B,YAAM,OAAO,IAAI,SAAS,SAAS;AACnC,MAAAA,MAAI,KAAK,KAAK,IAAI,IAAI,eAAe,MAAM,MAAM,KAAM,QAAQ,CAAC,CAAC,IAAI;AAAA,IACvE;AACA,QAAI,YAAY,oBAAoB,UAAU,eAAe;AAAA,EAC/D;AACA,MAAI,eAAe,SAAS,cAAc,QAAQ;AAChD,QAAI,aAAa,cAAc,YAAY;AAC3C,UAAM,IAAI;AAAA,MAAQ,CAACE,aACjB,WAAWA,UAAS,OAAO,oBAAoB;AAAA,IACjD;AAAA,EACF;AACF;AAEA,eAAsB,kBACpB,KACA,SACe;AACf,QAAM,cAAe,QAAQ,eAAe,CAAC;AAC7C,QAAM,UAAiC,YAAY,WAAW,CAAC;AAC/D,QAAM,aAAa,YAAY,UAAU;AACzC,QAAM,WAAW,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK,SAAS;AACzD,QAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,QAAM,eAAe,IAAI,aAAa,SAAS,QAAQ;AACvD,MAAI,CAAC,cAAc;AACjB;AAAA,EACF;AACA,QAAM,iBAAiB,IAAI,YAAY,kBAAkB,QAAQ;AACjE,MAAI,OAAO,KAAK,SAAS,EAAE,WAAW,GAAG;AACvC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AlCtLA,IAAMC,QAAMC,SAAO,2BAA2B;AAE9C,IAAM,2BAAN,MAA+B;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EAC3B,eAAwB;AAAA,EACxB,wBAA2C;AAAA,EAC3C,eAAwB;AAAA,EACxB,iBAAgC;AAAA,EAChC;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb;AAAA,EACA,cAAwB,CAAC;AAAA;AAAA,EAGzB,cAA2B,CAAC;AAAA;AAAA,EAG5B,gBAAgB,oBAAI,IAAY;AAAA;AAAA;AAAA,EAIhC,aAA8B,CAAC;AAAA;AAAA;AAAA,EAI/B,gBAAoC,CAAC;AAAA,EAErC,iBAAiB;AACf,WAAO,QAAQ,IAAI,UAAU,gBAAgB,MAAM,SAC/C,QAAQ,IAAI,UAAU,WAAW,GAAG,KAAK,IACzC;AAAA,EACN;AAAA,EAEA;AAAA,EACA,eAAe;AAAA,EACf;AAAA;AAAA;AAAA,EAIA,4BAA4B;AAAA;AAAA;AAAA,EAI5B,kBAAkB,IAAI,mBAAmB;AAAA,EAEzC,YAAY,UAA2B,CAAC,GAAG;AACzC,UAAM,OAAO,QAAQ,QAAQ;AAK7B,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,aAAa,yBAAyB;AAAA,MAC1C;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,QAAQ;AAAA,IACtB,CAAC;AACD,QAAI,WAAW,SAAS;AACtB,MAAAD,MAAI,KAAK,WAAW,OAAO;AAAA,IAC7B;AACA,SAAK,UAAU;AAAA,MACb,MAAM,QAAQ,QAAQ;AAAA,MACtB,UAAU,QAAQ,YAAY;AAAA,MAC9B,YAAY,WAAW;AAAA,MACvB,MAAM,QAAQ,QAAQ;AAAA,MACtB,mBAAmB,QAAQ,qBAAqB;AAAA,MAChD;AAAA,MACA,aAAa,QAAQ,eAAe;AAAA,MACpC,kBAAkB,QAAQ,oBAAoB;AAAA,MAC9C,aAAa,QAAQ,eAAe;AAAA,MACpC;AAAA,MACA,YAAY,QAAQ,cAAc;AAAA,MAClC;AAAA;AAAA;AAAA,MAGA,uBAAuB,QAAQ,yBAAyB;AAAA,IAC1D;AACA,UAAM,gBAAgB,uBAAuB,QAAQ,aAAa,IAAI;AACtE,QAAI,eAAe;AACjB,MAAAA,MAAI,KAAK,aAAa;AAAA,IACxB;AACA,SAAK,cAAc,IAAI;AAAA,MACrB,WAAW;AAAA,MACX,aAAa,UAAU;AAAA,IACzB;AACA,SAAK,eAAe,QAAQ,SAAS;AAAA,EACvC;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA;AAAA,EAIA,gBAAiC;AAC/B,QAAI,KAAK,YAAY;AACnB,aAAO,KAAK;AAAA,IACd;AACA,UAAM,OAAO;AACb,SAAK,aAAa;AAAA,MAChB,IAAI,UAAU;AACZ,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,WAAW;AACb,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,MACA,IAAI,OAAO;AACT,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,MACA,IAAI,OAAO;AACT,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,MACA,IAAI,oBAAoB;AACtB,eAAO,KAAK,YAAY;AAAA,MAC1B;AAAA,MACA,IAAI,cAAc;AAChB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,oBAAoB;AACtB,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,MACA,IAAI,kBAAkB;AACpB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,gBAAgB,GAAG;AACrB,aAAK,kBAAkB;AAAA,MACzB;AAAA,MACA,IAAI,eAAe;AACjB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,aAAa,GAAG;AAClB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,IAAI,cAAc;AAChB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,YAAY,GAAG;AACjB,aAAK,cAAc;AAAA,MACrB;AAAA,MACA,IAAI,eAAe;AACjB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,aAAa,GAAG;AAClB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,IAAI,eAAe;AACjB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,aAAa,GAAG;AAClB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,IAAI,mBAAmB;AACrB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,iBAAiB,GAAG;AACtB,aAAK,mBAAmB;AAAA,MAC1B;AAAA,MACA,IAAI,kBAAkB;AACpB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,gBAAgB,GAAG;AACrB,aAAK,mBAAmB;AAAA,MAC1B;AAAA,MACA,IAAI,cAAc;AAChB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,YAAY,GAAG;AACjB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,IAAI,YAAY;AACd,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,UAAU,GAAG;AACf,aAAK,aAAa;AAAA,MACpB;AAAA,MACA,IAAI,YAAY;AACd,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,UAAU,GAAG;AACf,aAAK,aAAa;AAAA,MACpB;AAAA,MACA,IAAI,YAAY;AACd,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,UAAU,GAAG;AACf,aAAK,aAAa;AAAA,MACpB;AAAA,MACA,IAAI,gBAAgB;AAClB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,cAAc,GAAG;AACnB,aAAK,iBAAiB;AAAA,MACxB;AAAA,MACA,IAAI,iBAAiB;AACnB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,eAAe,GAAG;AACpB,aAAK,kBAAkB;AAAA,MACzB;AAAA,MACA,IAAI,aAAa;AACf,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,WAAW,GAAG;AAChB,aAAK,cAAc;AAAA,MACrB;AAAA,MACA,IAAI,qBAAqB;AACvB,eAAO,KAAK,YAAY;AAAA,MAC1B;AAAA,MACA,IAAI,mBAAmB,GAAG;AACxB,aAAK,YAAY,WAAW;AAAA,MAC9B;AAAA,MACA,IAAI,sBAAsB;AACxB,eAAO,KAAK,YAAY;AAAA,MAC1B;AAAA,MACA,IAAI,oBAAoB,GAAG;AACzB,aAAK,YAAY,YAAY;AAAA,MAC/B;AAAA,MACA,IAAI,qBAAqB;AACvB,eAAO,KAAK,YAAY;AAAA,MAC1B;AAAA,MACA,IAAI,mBAAmB,GAAG;AACxB,aAAK,YAAY,WAAW;AAAA,MAC9B;AAAA,MACA,IAAI,aAAa;AACf,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,SAAS;AACX,eAAO,mBAAmB,KAAK,iBAAiB;AAAA,MAClD;AAAA,MACA,gBAAgB,MAAM,KAAK;AAAA,MAC3B,yBAAyB,MAAM,KAAK;AAAA,MACpC,gBAAgB,MAAM,KAAK;AAAA,MAC3B,gBAAgB,CAAC,MAAM;AACrB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,yBAAyB,CAAC,MAAM;AAC9B,aAAK,wBAAwB;AAAA,MAC/B;AAAA,MACA,gBAAgB,CAAC,MAAM;AACrB,aAAK,eAAe;AAAA,MACtB;AAAA,MACA,oBAAoB,MAAM;AACxB,aAAK,aAAa,mBAAmB;AACrC,aAAK,aAAa,mBAAmB;AACrC,aAAK,gBAAgB,MAAM;AAAA,MAC7B;AAAA,MACA,eAAe,CAAC,KAAK,aACnB,KAAK,gBAAgB,YAAY,KAAK,QAAQ;AAAA,MAChD,eAAe,CAAC,KAAK,UACnB,KAAK,gBAAgB,cAAc,KAAK,KAAK;AAAA,MAC/C,YAAY,CAAC,QAAQ,KAAK,gBAAgB,WAAW,GAAG;AAAA,MACxD,sBAAsB,MAAM,KAAK,sBAAsB;AAAA,MACvD,0BAA0B,CAAC,MAAM,KAAK,0BAA0B,CAAC;AAAA,MACjE,2BAA2B,MAAM,KAAK,2BAA2B;AAAA,MACjE,iBAAiB,CAAC,MAAM,KAAK,iBAAiB,CAAC;AAAA,MAC/C,gBAAgB,CAAC,MAAM,KAAK,gBAAgB,CAAC;AAAA,MAC7C,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;AAAA,MACjC,mBAAmB,CAAC,MAAM;AACxB,aAAK,oBAAoB;AAAA,MAC3B;AAAA,MACA,eAAe,MAAM,KAAK,eAAe;AAAA,MACzC,IAAI,YAAY;AACd,eAAO,KAAK,QAAQ,SAAS;AAAA,MAC/B;AAAA,MACA,IAAI,mBAAmB;AACrB,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,MACA,IAAI,aAAa;AACf,eAAO,KAAK;AAAA,MACd;AAAA,MACA,IAAI,eAAe;AACjB,eAAO,KAAK;AAAA,MACd;AAAA,MACA,iBAAiB,CAAC,UAAU,KAAK,gBAAgB,KAAK;AAAA,MACtD,mBAAmB,CAAC,KAAK,WAAW,KAAK,mBAAmB,KAAK,MAAM;AAAA,IACzE;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,kBAAsC;AACpC,WAAQ,KAAK,cAA0C;AAAA,EACzD;AAAA,EAEA,mBAAyB;AACvB,oBAAgB,KAAK,cAAc,CAAC;AAAA,EACtC;AAAA,EAEA,MAAM,qBAAqB,KAA4B;AACrD,UAAM,oBAAoB,KAAK,cAAc,GAAG,GAAG;AAAA,EACrD;AAAA,EAEA,MAAM,SAAS;AACb,UAAM,YAAY,KAAK,cAAc;AACrC,cAAU,gBAAgB,CAAC,MAAM;AAC/B,WAAK,cAAc;AAAA,IACrB;AACA,cAAU,wBAAwB,CAAC,QAAQ,KAAK,qBAAqB,GAAG;AACxE,cAAU,cAAc,MAAM,KAAK,iBAAiB;AACpD,cAAU,SAAS;AACnB,UAAM,gBAAgB,SAAS;AAC/B,QAAI,KAAK,QAAQ,mBAAmB;AAClC;AAAA,QACE,MAAM,KAAK;AAAA;AAAA;AAAA;AAAA,QAIX,MAAM,KAAK,cAAc,WAAW,KAAK,KAAK,gBAAgB;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,0BAA0B,SAA4B;AAC1D,UAAM,yBAAyB,KAAK,cAAc,GAAG,OAAO;AAAA,EAC9D;AAAA,EAEA,MAAM,6BAA4C;AAChD,UAAM,KAAK,YAAY,SAAS,KAAK,cAAc,CAAC;AAAA,EACtD;AAAA,EAEA,MAAM,eAAe,SAA4B,QAAwB;AACvE,SAAK,4BAA4B,KAAK,IAAI;AAC1C,UAAM,eAAwB,KAAK,cAAc,GAAG,SAAS,MAAM;AAAA,EACrE;AAAA,EAEA,MAAM,mBAAmB,SAA4B,QAAwB;AAC3E,UAAM,kCAA4B,KAAK,cAAc,GAAG,SAAS,MAAM;AAAA,EACzE;AAAA,EAEA,MAAM,cACJ,SACA,QACA,QACA;AACA,UAAM,cAAuB,KAAK,cAAc,GAAG,SAAS,QAAQ,MAAM;AAAA,EAC5E;AAAA,EAEA,MAAM,mBACJ,SACA,YACA,QACA;AACA,UAAM;AAAA,MACJ,KAAK,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,kBACJ,SACA,QACA,YACA,QACA;AACA,UAAM;AAAA,MACJ,KAAK,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,sBAAsB,aAAoC;AACxD,WAAO,qBAAqB,KAAK,cAAc,GAAG,WAAW;AAAA,EAC/D;AAAA,EAEA,qBACE,aACA,WACA,gBACoB;AACpB,WAAO,oBAAoB,aAAa,WAAW,cAAc;AAAA,EACnE;AAAA,EAEA,MAAM,eACJ,cACA,iBACA,gBACA,UACe;AACf,UAAM;AAAA,MACJ,KAAK,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,0BACJ,cACA,UACA,aACe;AACf,UAAM;AAAA,MACJ,KAAK,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB,SAAkC;AACjD,oBAAgB,KAAK,cAAc,GAAG,OAAO;AAAA,EAC/C;AAAA,EAEA,MAAM,WAAW,SAA4B;AAC3C,QAAI,KAAK,mBAAmB;AAC1B;AAAA,IACF;AACA,SAAK,4BAA4B,KAAK,IAAI;AAC1C,UAAM,KAAK,0BAA0B,OAAO;AAE5C,UAAM,cAAc,QAAQ;AAC5B,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,UAAM,EAAE,UAAU,UAAU,YAAY,WAAW,WAAW,UAAU,IACtE,KAAK,sBAAsB,WAAW;AACxC,UAAM,eAAe,KAAK,aAAa;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,YAAY,SAAS,SAAS,GAAG,GAAG;AACtC,WAAK,gBAAgB,cAAc,QAAQ,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC7D;AAEA,QAAI,UAAU;AACZ,8BAAwB,KAAK,cAAc,GAAG,QAAQ;AAAA,IACxD;AAEA,UAAM,KAAK,0BAA0B,cAAc,UAAU,WAAW;AAExE,UAAM,iBAAiB,KAAK,YAAY,kBAAkB,QAAQ;AAClE,UAAM,kBAAkB,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,iBAAiB,OAAO;AAAA,EAC/B;AAAA,EAEA,MAAM,UAAU,SAA4B;AAE1C,QAAI,KAAK,mBAAmB;AAC1B;AAAA,IACF;AAEA,QAAI,WAAW,KAAK,iBAAiB;AACnC,UAAI;AACF,cAAM,KAAK,mBAAmB,OAAO;AACrC,YAAI,KAAK,QAAQ,mBAAmB;AAClC,gBAAM;AAAA,YACJ,KAAK;AAAA,YACL;AAAA,YACA,QAAQ;AAAA,YACR,KAAK,gBAAgB;AAAA,YACrB,KAAK,aAAa,uBAAuB;AAAA,UAC3C;AAAA,QACF;AAGA,cAAM,KAAK,gBAAgB,aAAa,SAAS,IAAI;AAErD,uBAAe,KAAK,cAAc,CAAC;AACnC,cAAM,UAAW,QAAQ,aAAuC;AAChE,cAAM,SACJ,CAAC,CAAC,aAAa,QAAQ,UAAU,KAAK,MAAM,QAAQ,UAAU,KAAK;AACrE,cAAM,KAAK,mBAAmB,KAAK,gBAAgB,GAAG,MAAM;AAAA,MAC9D,SAAS,KAAK;AACZ,QAAAA,MAAI,MAAM,4BAA4B,aAAa,GAAG,CAAC,EAAE;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,SAA2C;AAClE,UAAM,kBAAkB,KAAK,cAAc,GAAG,OAAO;AAAA,EACvD;AAAA;AAAA;AAAA,EAIA,IAAI,aAAqB;AACvB,WAAO,wBAAwB;AAAA,MAC7B,cAAc,KAAK,cAAc,yBAAyB,KAAK;AAAA,MAC/D,YAAY,KAAK;AAAA,IACnB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,KACA,QACe;AACf,UAAM,UAAU,MAAM,KAAK,gBAAgB,WAAW,GAAG,IAAI;AAC7D,UAAM,kBAAkB;AAAA,MACtB,MAAM,KAAK,QAAQ;AAAA,MACnB,aAAa,KAAK,QAAQ;AAAA,MAC1B,kBAAkB,KAAK,QAAQ;AAAA,MAC/B,aAAa,KAAK,QAAQ;AAAA,MAC1B;AAAA,MACA,iBAAiB,KAAK,gBAAgB;AAAA,MACtC,QAAQ,KAAK,YAAY;AAAA,MACzB,eAAe,KAAK;AAAA,MACpB,UAAU,MAAM,KAAK,gBAAgB,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAC9D;AAAA,MACA;AAAA,MACA,WAAW,KAAK,iBAAiB,UAAU;AAAA,MAC3C,WAAW,KAAK;AAAA,MAChB,eAAe,KAAK,YAAY,QAAQ;AAAA,MACxC,YAAY,CAAC,MAAM,KAAK,WAAW,KAAK,CAAC;AAAA,MACzC,OAAO,CAAC,OAAO,QAAQA,MAAI,KAAK,EAAE,GAAG;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,SAA6B;AAGvC,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,2BAA2B;AACtC,QAAI;AACF,YAAM,KAAK,mBAAmB,OAAO;AACrC,WAAK,eAAe;AACpB,UAAI,KAAK,QAAQ,SAAS,SAAS;AACjC,cAAM,KAAK,oBAAoB;AAC/B,cAAM,KAAK,iBAAiB,eAAe;AAC3C,cAAM,YAAY;AAClB;AAAA,MACF;AACA,UAAI,CAAC,KAAK,kBAAkB;AAG1B,aAAK,cAAc,aAAa;AAChC,QAAAA,MAAI,KAAK,qDAA2C;AACpD,cAAM,KAAK,iBAAiB,eAAe;AAC3C;AAAA,MACF;AACA,MAAAA,MAAI,KAAK,iEAA0D;AACnE,YAAM,KAAK,6BAA6B;AAAA,IAC1C,SAAS,KAAK;AACZ,MAAAA,MAAI,MAAM,2BAA2B,aAAa,GAAG,CAAC,EAAE;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,SAA4C;AACnE,UAAM,kBAAkB,KAAK,cAAc,GAAG,OAAO;AAAA,EACvD;AAAA,EAEA,iBAAuB;AACrB,kBAAc,KAAK,cAAc,CAAC;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,OAAsD;AACpE,UAAME,aAAY,KAAK,gBAAgB,UAAU;AACjD,QAAI,CAACA,YAAW;AACd,aAAO,QAAQ,QAAQ,MAAS;AAAA,IAClC;AAGA,UAAM,QAAQ,iBAAiB,KAAK,cAAcA,UAAS,GAAG,KAAK;AACnE,SAAK,cAAc,KAAK,KAAK;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,cAAcA,YAAuC;AACnD,WAAO;AAAA,MACL;AAAA,QACE,MAAM,KAAK,QAAQ;AAAA,QACnB,QAAQ,KAAK,QAAQ;AAAA,QACrB,aAAa,KAAK,QAAQ;AAAA,QAC1B,QAAQ,KAAK,QAAQ;AAAA,QACrB,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK,aAAa,aAAa,EAAE,OAAO;AAAA,QAChD,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK;AAAA,QACd,WAAW,KAAK;AAAA,QAChB,cAAc,KAAK;AAAA,QACnB,uBAAuB,KAAK,QAAQ;AAAA,QACpC,kBAAkB,KAAK,QAAQ,YAC3B,KAAK,YAAY,SACjB;AAAA,QACJ,YAAY,KAAK;AAAA,QACjB,cACE,KAAK,cAAc,yBAAyB,KAAK;AAAA,QACnD,KAAK,CAAC,OAAO,QAAQF,MAAI,KAAK,EAAE,GAAG;AAAA,MACrC;AAAA,MACAE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,sBAAqC;AACzC,UAAMA,aAAY,KAAK,iBAAiB,UAAU;AAClD,QAAI,KAAK,QAAQ,SAAS,WAAW,CAAC,KAAK,mBAAmB,CAACA,YAAW;AACxE;AAAA,IACF;AACA,UAAM,oBAAoB,KAAK,cAAcA,UAAS,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,+BAA8C;AAClD,UAAM,4BAA4B,KAAK,cAAc,CAAC;AAAA,EACxD;AAAA,EAEA,wBAAwB;AACtB,WAAO,2BAA2B;AAAA,MAChC,kBAAkB,KAAK;AAAA,MACvB,YAAY,KAAK;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB,OAAiC;AAC/C,UAAM,WAAW;AAAA,MACf,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,IAClB;AACA,sBAAkB,UAAU,KAAK;AACjC,SAAK,aAAa,SAAS;AAC3B,SAAK,aAAa,SAAS;AAC3B,SAAK,aAAa,SAAS;AAAA,EAC7B;AAAA,EAEA,UAAU,OAAmC;AAC3C,WAAO,YAAY,KAAK;AAAA,EAC1B;AAAA,EAEA,sBAAsB,UAAoC;AACxD,0BAA0B,UAAU;AAAA,MAClC,oBAAoB,MAAM,KAAK;AAAA,MAC/B,sBAAsB,MAAM,KAAK,sBAAsB;AAAA,MACvD,mBAAmB,CAAC,MAAe;AACjC,aAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAM,oBAAoB;AAAA,EAC/B,IAAI,IAAI,+BAA+B,YAAY,GAAG;AACxD;AAEe,SAAR,yBAA0C,SAA2B;AAC1E,QAAM,SAAS,IAAI,yBAAyB,OAAO;AAEnD,SAAO;AAAA;AAAA;AAAA,IAGL,kBAAkB;AAAA,IAElB,QAAQ,iBAA+B;AACrC,YAAM,OAAO,OAAO;AAAA,IACtB;AAAA,IACA,YAAY,eAA+B,SAA4B;AACrE,YAAM,OAAO,WAAW,OAAO;AAAA,IACjC;AAAA,IACA,WAAW,eAA+B,SAA4B;AACpE,YAAM,OAAO,UAAU,OAAO;AAAA,IAChC;AAAA,IACA,OAAO,iBAA+B;AACpC,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,IAEA,uBAAuB,SAAU,UAA8B;AAC7D,aAAO,sBAAsB,QAAQ;AAAA,IACvC;AAAA,EACF;AACF;","names":["sessionId","logger","fs","path","logger","generateStableUid","log","logger","resolve","logger","logger","log","logger","path","webdriverGet","webdriverPost","logger","captureActionSnapshot","webdriverGet","log","logger","captureActionSnapshot","resolve","webdriverGet","logger","log","logger","generateStableUid","generateStableUid","logger","logger","log","logger","path","log","logger","path","logger","logger","log","logger","log","logger","sessionId","log","logger","webdriverGet","sessionId","resolve","logger","log","logger","sessionId","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","log","logger","sessionId"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../shared/src/files.ts","../../shared/src/timing.ts","../src/trace-context.ts","../src/test-artifacts.ts","../src/helpers/assertCapture.ts","../src/run-lifecycle.ts","../src/plugin-global-key.ts","../src/constants.ts","../src/helpers/utils.ts","../src/session-init.ts","../src/helpers/webdriverHttp.ts","../src/session.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/runningTest.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/callbackError.ts","../src/helpers/assertTarget.ts","../src/helpers/serializeCommandResult.ts","../src/helpers/nativeAssertions.ts","../src/helpers/nativeAssertRecorder.ts","../src/screencast-session.ts","../src/screencast.ts","../src/plugin-screencast.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/trace-slices.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.ts"],"sourcesContent":["/**\n * Nightwatch DevTools Plugin\n *\n * Integrates Nightwatch with WebdriverIO DevTools following the WDIO service pattern.\n * Captures commands, network requests, and console logs during test execution in real-time.\n */\n\nimport { fileURLToPath } from 'node:url'\nimport {\n errorMessage,\n finalizeTraceExport,\n flushRangeLogged,\n resolveAdapterOutputDir,\n TestAttemptTracker,\n tracePolicyModeWarning,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport { buildTraceContext } from './trace-context.js'\nimport { emitTestArtifacts } from './test-artifacts.js'\nimport { wireAssertCapture } from './helpers/assertCapture.js'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport {\n REUSE_ENV,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\nimport logger from '@wdio/logger'\nimport {\n handleReuseMode,\n openDevtoolsBrowser,\n finalizeAllSuites,\n logRunSummary,\n waitForDevtoolsBrowserClose,\n runPluginBefore,\n type PluginBeforeCtx\n} from './run-lifecycle.js'\nimport type { PluginInternals } from './plugin-internals.js'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport {\n PluginScreencast,\n resolveScreencastOptions\n} from './plugin-screencast.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n DevToolsOptions,\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchEventHub,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n captureCucumberScenarioBeforeQuit as cucumberLifecycleBeforeQuit,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\n type CucumberResult\n} from './cucumber-lifecycle.js'\nimport {\n resolveSuiteMetadata,\n pickCurrentTestName,\n startNextTest,\n closePreviousRunningTest,\n wrapBrowserOnce,\n closeOutTestcases\n} from './test-lifecycle.js'\nimport { ensureSessionInitialized } from './session-init.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { flushTestSlice, recordSpecSliceBoundary } from './trace-slices.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions,\n nightwatchRunnerId\n} from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools')\n\nclass NightwatchDevToolsPlugin {\n private options: Required<DevToolsOptions>\n private sessionCapturer!: SessionCapturer\n private testReporter!: TestReporter\n private testManager!: TestManager\n private suiteManager!: SuiteManager\n private browserProxy!: BrowserProxy\n private isScriptInjected = false\n #currentTest: unknown = null\n #currentScenarioSuite: SuiteStats | null = null\n #currentStep: unknown = null\n #lastSessionId: string | null = null\n #devtoolsBrowser?: WebdriverIO.Browser\n #userDataDir?: string\n #isCucumberRunner = false\n #passCount = 0\n #failCount = 0\n #skipCount = 0\n #configPath: string | undefined\n #srcFolders: string[] = []\n\n /** Index ranges into the session capturer's flat arrays, one per spec file. */\n #specRanges: SpecRange[] = []\n\n /** Set of spec files already flushed to disk. */\n #flushedSpecs = new Set<string>()\n\n /** Every trace/video artifact seen this run (retained or not), for the\n * end-of-run artifacts manifest. Populated via the context's onArtifact. */\n #artifacts: TraceArtifact[] = []\n\n /** In-flight fire-and-forget eager/boundary slice flushes, awaited at finalize\n * so the last slice's write + its manifest entry land before teardown. */\n #traceFlushes: Promise<unknown>[] = []\n\n #getRerunLabel() {\n return process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n }\n\n #screencast: PluginScreencast\n #bidiEnabled = false\n #armedSessionId: string | undefined\n\n /** Wall-clock ms at the current test/scenario start — the lower bound of that\n * test's action-snapshot and video-frame window (per-test artifact slicing). */\n #currentTestStartWallTime = 0\n\n // Nightwatch `--retries` and cross-worker reruns may reset this in-process\n // tracker; only retries that re-enter this process's start hook are counted.\n #attemptTracker = new TestAttemptTracker()\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n // Resolve the defaults ONCE and gate on the resolved values: reading the raw\n // `options.filmstrip === true` left the default-on filmstrip inert unless a\n // config set it explicitly, so the recorder never started while\n // `this.options.filmstrip` claimed it was on.\n const filmstrip = options.filmstrip ?? true\n const video = options.video ?? 'off'\n const screencast = resolveScreencastOptions({\n mode,\n filmstrip,\n video,\n screencast: options.screencast\n })\n if (screencast.warning) {\n log.warn(screencast.warning)\n }\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast: screencast.options,\n bidi: options.bidi ?? false,\n captureAssertions: options.captureAssertions ?? true,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session',\n tracePolicy: options.tracePolicy ?? 'on',\n filmstrip,\n screenshot: options.screenshot ?? 'off',\n video,\n // No live Allure signal in Nightwatch (produce-only), so no auto-detect:\n // off by default, opt-in via the option.\n emitArtifactsManifest: options.emitArtifactsManifest ?? false\n }\n const policyWarning = tracePolicyModeWarning(options.tracePolicy, mode)\n if (policyWarning) {\n log.warn(policyWarning)\n }\n this.#screencast = new PluginScreencast(\n screencast.options,\n filmstrip || video !== 'off'\n )\n this.#bidiEnabled = options.bidi === true\n }\n\n // Single internals \"bag\" — structurally satisfies all 4 lifecycle ctx\n // interfaces. Lifecycle modules cast it to their narrow type at call time.\n #internals: PluginInternals | undefined\n // Declarative accessor map — splitting this purely to satisfy the\n // line-count rule hurts readability; the body is mechanical wiring.\n // eslint-disable-next-line max-lines-per-function\n #getInternals(): PluginInternals {\n if (this.#internals) {\n return this.#internals\n }\n const self = this\n this.#internals = {\n get options() {\n return self.options\n },\n get hostname() {\n return self.options.hostname\n },\n get port() {\n return self.options.port\n },\n get mode() {\n return self.options.mode\n },\n get screencastOptions() {\n return self.#screencast.options\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get captureAssertions() {\n return self.options.captureAssertions\n },\n get sessionCapturer() {\n return self.sessionCapturer\n },\n set sessionCapturer(v) {\n self.sessionCapturer = v\n },\n get testReporter() {\n return self.testReporter\n },\n set testReporter(v) {\n self.testReporter = v\n },\n get testManager() {\n return self.testManager\n },\n set testManager(v) {\n self.testManager = v\n },\n get suiteManager() {\n return self.suiteManager\n },\n set suiteManager(v) {\n self.suiteManager = v\n },\n get browserProxy() {\n return self.browserProxy\n },\n set browserProxy(v) {\n self.browserProxy = v\n },\n get isScriptInjected() {\n return self.isScriptInjected\n },\n set isScriptInjected(v) {\n self.isScriptInjected = v\n },\n get devtoolsBrowser() {\n return self.#devtoolsBrowser\n },\n set devtoolsBrowser(v) {\n self.#devtoolsBrowser = v\n },\n get userDataDir() {\n return self.#userDataDir\n },\n set userDataDir(v) {\n self.#userDataDir = v\n },\n get passCount() {\n return self.#passCount\n },\n set passCount(v) {\n self.#passCount = v\n },\n get failCount() {\n return self.#failCount\n },\n set failCount(v) {\n self.#failCount = v\n },\n get skipCount() {\n return self.#skipCount\n },\n set skipCount(v) {\n self.#skipCount = v\n },\n get lastSessionId() {\n return self.#lastSessionId\n },\n set lastSessionId(v) {\n self.#lastSessionId = v\n },\n get armedSessionId() {\n return self.#armedSessionId\n },\n set armedSessionId(v) {\n self.#armedSessionId = v\n },\n get srcFolders() {\n return self.#srcFolders\n },\n set srcFolders(v) {\n self.#srcFolders = v\n },\n get screencastRecorder() {\n return self.#screencast.recorder\n },\n set screencastRecorder(v) {\n self.#screencast.recorder = v\n },\n get screencastSessionId() {\n return self.#screencast.sessionId\n },\n set screencastSessionId(v) {\n self.#screencast.sessionId = v\n },\n get screencastRotation() {\n return self.#screencast.rotation\n },\n set screencastRotation(v) {\n self.#screencast.rotation = v\n },\n get configPath() {\n return self.#configPath\n },\n get runner() {\n return nightwatchRunnerId(self.#isCucumberRunner)\n },\n getCurrentTest: () => self.#currentTest,\n getCurrentScenarioSuite: () => self.#currentScenarioSuite,\n getCurrentStep: () => self.#currentStep,\n setCurrentTest: (t) => {\n self.#currentTest = t\n },\n setCurrentScenarioSuite: (s) => {\n self.#currentScenarioSuite = s\n },\n setCurrentStep: (s) => {\n self.#currentStep = s\n },\n clearExecutionData: () => {\n self.testReporter.clearExecutionData()\n self.suiteManager.clearExecutionData()\n self.#attemptTracker.reset()\n },\n recordAttempt: (uid, specFile) =>\n self.#attemptTracker.recordStart(uid, specFile),\n recordOutcome: (uid, state) =>\n self.#attemptTracker.recordOutcome(uid, state),\n attemptFor: (uid) => self.#attemptTracker.attemptFor(uid),\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n finalizeCurrentScreencast: () => self.#finalizeCurrentScreencast(),\n wrapBrowserOnce: (b) => self.#wrapBrowserOnce(b),\n incrementCount: (s) => self.#incrementCount(s),\n testIcon: (s) => self.#testIcon(s),\n setCucumberRunner: (v) => {\n self.#isCucumberRunner = v\n },\n getRerunLabel: () => self.#getRerunLabel(),\n get traceMode() {\n return self.options.mode === 'trace'\n },\n get traceGranularity() {\n return self.options.traceGranularity\n },\n get specRanges() {\n return self.#specRanges\n },\n get flushedSpecs() {\n return self.#flushedSpecs\n },\n flushTraceRange: (range) => self.#flushSpecTrace(range),\n emitTestArtifacts: (uid, failed) => self.#emitTestArtifacts(uid, failed)\n }\n return this.#internals\n }\n\n /** Boundary cast: currentTest is Nightwatch's loose bag; only uid is read. */\n #currentTestUid(): string | undefined {\n return (this.#currentTest as { uid?: string } | null)?.uid\n }\n\n #handleReuseMode(): void {\n handleReuseMode(this.#getInternals())\n }\n\n async #openDevtoolsBrowser(url: string): Promise<void> {\n await openDevtoolsBrowser(this.#getInternals(), url)\n }\n\n async before() {\n const internals = this.#getInternals() as unknown as PluginBeforeCtx\n internals.setConfigPath = (v) => {\n this.#configPath = v\n }\n internals.openDevtoolsBrowserAt = (url) => this.#openDevtoolsBrowser(url)\n internals.handleReuse = () => this.#handleReuseMode()\n internals.plugin = this\n await runPluginBefore(internals)\n if (this.options.captureAssertions) {\n wireAssertCapture(\n () => this.sessionCapturer,\n // Rows are tagged per-`it` (BrowserProxy.rowTestUid); `#currentTestUid`\n // — the lifecycle test, which the BDD interface pins to the module's\n // first `it` — is only the fallback, and stays the artifact/slice key.\n () => this.browserProxy?.rowTestUid() ?? this.#currentTestUid()\n )\n }\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser)\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await this.#screencast.finalize(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n this.#currentTestStartWallTime = Date.now()\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberBeforeQuit(browser: NightwatchBrowser, result: CucumberResult) {\n await cucumberLifecycleBeforeQuit(this.#getInternals(), browser, result)\n }\n\n async cucumberAfter(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfter(this.#getInternals(), browser, result, pickle)\n }\n\n async cucumberBeforeStep(\n browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleBeforeStep(\n this.#getInternals(),\n browser,\n pickleStep,\n pickle\n )\n }\n\n async cucumberAfterStep(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfterStep(\n this.#getInternals(),\n browser,\n result,\n pickleStep,\n pickle\n )\n }\n\n #resolveSuiteMetadata(currentTest: NightwatchCurrentTest) {\n return resolveSuiteMetadata(this.#getInternals(), currentTest)\n }\n\n #pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n ): string | undefined {\n return pickCurrentTestName(currentTest, testNames, processedTests)\n }\n\n async #startNextTest(\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>,\n specFile: string | null\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests,\n specFile\n )\n }\n\n async #closePreviousRunningTest(\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n ): Promise<void> {\n await closePreviousRunningTest(\n this.#getInternals(),\n currentSuite,\n testFile,\n currentTest\n )\n }\n\n #wrapBrowserOnce(browser: NightwatchBrowser): void {\n wrapBrowserOnce(this.#getInternals(), browser)\n }\n\n async beforeEach(browser: NightwatchBrowser) {\n if (this.#isCucumberRunner) {\n return\n }\n this.#currentTestStartWallTime = Date.now()\n await this.#ensureSessionInitialized(browser)\n\n const currentTest = browser.currentTest as NightwatchCurrentTest | undefined\n if (!currentTest) {\n return\n }\n\n const { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines } =\n this.#resolveSuiteMetadata(currentTest)\n const currentSuite = this.suiteManager.getOrCreateSuite(\n testFile,\n suiteTitle,\n fullPath,\n testNames,\n suiteLine,\n testLines\n )\n if (fullPath && fullPath.includes('/')) {\n this.sessionCapturer.captureSource(fullPath).catch(() => {})\n }\n\n if (fullPath) {\n recordSpecSliceBoundary(this.#getInternals(), fullPath)\n }\n\n await this.#closePreviousRunningTest(currentSuite, testFile, currentTest)\n\n const processedTests = this.testManager.getProcessedTests(testFile)\n const currentTestName = this.#pickCurrentTestName(\n currentTest,\n testNames,\n processedTests\n )\n if (currentTestName) {\n await this.#startNextTest(\n currentSuite,\n currentTestName,\n processedTests,\n fullPath\n )\n }\n this.#wrapBrowserOnce(browser)\n }\n\n async afterEach(browser: NightwatchBrowser) {\n // Cucumber runner manages its own lifecycle via cucumberHooks.cjs\n if (this.#isCucumberRunner) {\n return\n }\n\n if (browser && this.sessionCapturer) {\n try {\n await this.#closeOutTestcases(browser)\n if (this.options.captureAssertions) {\n await captureNativeAssertions(\n this.sessionCapturer,\n browser,\n browser.currentTest as NightwatchCurrentTest | undefined,\n this.#currentTestUid(),\n this.browserProxy.drainNativeAssertCalls()\n )\n }\n // Anchored: a test ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await this.sessionCapturer.captureTrace(browser, true)\n // Flush this test's slice before the next test overwrites its outcome.\n flushTestSlice(this.#getInternals())\n const results = (browser.currentTest as NightwatchCurrentTest)?.results\n const failed =\n !!results && ((results.errors ?? 0) > 0 || (results.failed ?? 0) > 0)\n await this.#emitTestArtifacts(this.#currentTestUid(), failed)\n } catch (err) {\n log.error(`Failed to capture trace: ${errorMessage(err)}`)\n }\n }\n }\n\n async #closeOutTestcases(browser: NightwatchBrowser): Promise<void> {\n await closeOutTestcases(this.#getInternals(), browser)\n }\n\n /** Directory for produced artifacts — next to the test file, else the config,\n * else cwd (mirrors #traceContext's resolution). */\n get #outputDir(): string {\n return resolveAdapterOutputDir({\n testFilePath: this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: this.#configPath\n })\n }\n\n /** Produce (never attach — no live Allure API) this test's per-test screenshot\n * and video slice. No-op outside trace mode + `test` granularity (core-gated).\n * Shared by the per-test `afterEach` and the cucumber per-scenario finalize. */\n async #emitTestArtifacts(\n uid: string | undefined,\n failed: boolean\n ): Promise<void> {\n const attempt = uid ? this.#attemptTracker.attemptFor(uid) : undefined\n await emitTestArtifacts({\n mode: this.options.mode,\n granularity: this.options.traceGranularity,\n screenshotPolicy: this.options.screenshot,\n videoPolicy: this.options.video,\n failed,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n frames: this.#screencast.frames,\n startWallTime: this.#currentTestStartWallTime,\n outcomes: uid ? this.#attemptTracker.forTest(uid, attempt) : [],\n uid,\n attempt,\n sessionId: this.sessionCapturer?.metadata?.sessionId,\n outputDir: this.#outputDir,\n captureFormat: this.#screencast.options.captureFormat,\n onArtifact: (a) => this.#artifacts.push(a),\n onLog: (level, msg) => log[level](msg)\n })\n }\n\n async after(browser?: NightwatchBrowser) {\n // Drain first: a rotation still in flight would otherwise start its new\n // recorder after this finalize and strand the poll interval.\n await this.#screencast.rotation\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceIfNeeded()\n await this.sessionCapturer?.closeWebSocket()\n await stopBackend()\n return\n }\n if (!this.#devtoolsBrowser) {\n // Reuse mode: force one final suites broadcast so the UI reflects the\n // actual outcome before the process exits.\n this.testReporter?.updateSuites()\n log.info('♻ Rerun complete — flushing WebSocket...')\n await this.sessionCapturer?.closeWebSocket()\n return\n }\n log.info('💡 Please close the DevTools browser window to finish...')\n await this.#waitForDevtoolsBrowserClose()\n } catch (err) {\n log.error(`Failed to stop backend: ${errorMessage(err)}`)\n }\n }\n\n async #finalizeAllSuites(browser?: NightwatchBrowser): Promise<void> {\n await finalizeAllSuites(this.#getInternals(), browser)\n }\n\n #logRunSummary(): void {\n logRunSummary(this.#getInternals())\n }\n\n /** Thin wrapper so boundary flushes and the final flush share one path.\n * flushRangeLogged logs+swallows a failed flush (shared spec/test string) so\n * the fire-and-forget boundary callers don't each re-implement the guard. */\n #flushSpecTrace(range: SpecRange): Promise<TraceArtifact | undefined> {\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return Promise.resolve(undefined)\n }\n // Track the promise (before it's added to awaitPending) so finalize awaits\n // this fire-and-forget flush — its context snapshot already excludes it.\n const flush = flushRangeLogged(this.#traceContext(sessionId), range)\n this.#traceFlushes.push(flush)\n return flush\n }\n\n /** Assemble the framework-agnostic trace-export context from plugin state.\n * Output dir ignores the spec range — nightwatch writes next to config. */\n #traceContext(sessionId: string): TraceExportContext {\n return buildTraceContext(\n {\n mode: this.options.mode,\n policy: this.options.tracePolicy,\n granularity: this.options.traceGranularity,\n format: this.options.traceFormat,\n capturer: this.sessionCapturer,\n suites: this.suiteManager.getAllSuites().values(),\n outcomes: this.#attemptTracker,\n ranges: this.#specRanges,\n flushed: this.#flushedSpecs,\n artifacts: this.#artifacts,\n traceFlushes: this.#traceFlushes,\n emitArtifactsManifest: this.options.emitArtifactsManifest,\n screencastFrames: this.options.filmstrip\n ? this.#screencast.frames\n : undefined,\n configPath: this.#configPath,\n testFilePath:\n this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n log: (level, msg) => log[level](msg)\n },\n sessionId\n )\n }\n\n async #writeTraceIfNeeded(): Promise<void> {\n const sessionId = this.sessionCapturer?.metadata?.sessionId\n if (this.options.mode !== 'trace' || !this.sessionCapturer || !sessionId) {\n return\n }\n await finalizeTraceExport(this.#traceContext(sessionId))\n }\n\n async #waitForDevtoolsBrowserClose(): Promise<void> {\n await waitForDevtoolsBrowserClose(this.#getInternals())\n }\n\n #buildMetadataOptions() {\n return buildPluginMetadataOptions({\n isCucumberRunner: this.#isCucumberRunner,\n configPath: this.#configPath\n })\n }\n\n #incrementCount(state: TestStats['state']): void {\n const counters = {\n passCount: this.#passCount,\n failCount: this.#failCount,\n skipCount: this.#skipCount\n }\n incrementCounters(counters, state)\n this.#passCount = counters.passCount\n this.#failCount = counters.failCount\n this.#skipCount = counters.skipCount\n }\n\n #testIcon(state: TestStats['state']): string {\n return getTestIcon(state)\n }\n\n registerEventHandlers(eventHub: NightwatchEventHub): void {\n registerEventHandlersImpl(eventHub, {\n getSessionCapturer: () => this.sessionCapturer,\n buildMetadataOptions: () => this.#buildMetadataOptions(),\n setCucumberRunner: (v: boolean) => {\n this.#isCucumberRunner = v\n }\n })\n }\n}\n\nexport const cucumberHooksPath = fileURLToPath(\n new URL('./helpers/cucumberHooks.cjs', import.meta.url)\n)\n\nexport default function createNightwatchDevTools(options?: DevToolsOptions) {\n const plugin = new NightwatchDevToolsPlugin(options)\n\n return {\n // Set long timeout to allow user to review DevTools UI\n // The after() hook waits for the browser window to be closed\n asyncHookTimeout: 3600000,\n\n before: async function (this: unknown) {\n await plugin.before()\n },\n beforeEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.beforeEach(browser)\n },\n afterEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.afterEach(browser)\n },\n after: async function (this: unknown) {\n await plugin.after()\n },\n\n registerEventHandlers: function (eventHub: NightwatchEventHub) {\n plugin.registerEventHandlers(eventHub)\n }\n }\n}\n\nexport { NightwatchDevToolsPlugin }\n","// File-pattern regexes shared across packages — keep one canonical form\n// per concept so changes to \"what counts as a test file\" or \"what counts\n// as a Cucumber feature file\" propagate everywhere. See CLAUDE.md §2.1.\n\n/** Matches `*.test.ts`, `*.spec.ts`, `*.test.cjs`, etc. — the test-runner\n * convention used by every adapter to recognize spec files. */\nexport const SPEC_FILE_RE = /\\.(?:test|spec)\\.[cm]?[jt]sx?$/i\n\n/** Matches `*.feature` — Cucumber feature files. */\nexport const FEATURE_FILE_RE = /\\.feature$/i\n","/**\n * Cross-adapter timing + identifier defaults.\n *\n * The TIMING values are wall-clock milliseconds the adapter's plugin loop\n * waits for (UI render, test/suite boundaries, browser close, WS connection\n * setup). The DEFAULTS provide the canonical TestStats / SuiteStats field\n * defaults so every adapter produces the same shape on the wire.\n *\n * Adapters spread these into their local TIMING / DEFAULTS objects so they\n * can override per-framework values (Nightwatch's UI_CONNECTION_WAIT is\n * higher because its boot is slower; each adapter has its own SESSION_TITLE\n * / TEST_NAME placeholder).\n */\n\nexport const TIMING_BASE = {\n /** ms to let the dashboard render between rapid state updates. */\n UI_RENDER_DELAY: 150,\n /** ms to wait after a test starts before flushing initial state. */\n TEST_START_DELAY: 100,\n /** ms gap between consecutive suite-finalize broadcasts. */\n SUITE_COMPLETE_DELAY: 200,\n /** ms allowed for the browser-close handshake before forcing teardown. */\n BROWSER_CLOSE_WAIT: 2000,\n /** ms before first worker WS connection attempt — lets backend bind. */\n INITIAL_CONNECTION_WAIT: 500,\n /** ms between browser-window-alive polls. */\n BROWSER_POLL_INTERVAL: 1000\n} as const\n\nexport const DEFAULTS_BASE = {\n /** Synthetic capability ID — present on every CommandLog / TestStats. */\n CID: '0-0',\n /** TestStats.retries default — adapters that surface retry counts override. */\n RETRIES: 0,\n /** _duration placeholder for not-yet-finalized stats. */\n DURATION: 0\n} as const\n","/**\n * Assembles the framework-agnostic trace-export context from plugin state.\n *\n * Extracted from `NightwatchDevToolsPlugin.#traceContext` so the assembly is\n * unit-testable and to keep `index.ts` under the file-size cap. Both the\n * per-spec boundary flush and the final trace write share this one builder.\n */\n\nimport {\n collectSuiteTestMetadata,\n resolveAdapterOutputDir,\n type RetryOutcomeView,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n SuiteStats,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from './types.js'\n\nexport interface TraceContextInput {\n mode: DevToolsMode\n policy: TraceRetentionPolicy\n granularity: TraceGranularity\n format: TraceFormat\n capturer: SessionCapturer\n suites: Iterable<SuiteStats>\n outcomes?: RetryOutcomeView\n ranges: SpecRange[]\n flushed: Set<string>\n artifacts: TraceArtifact[]\n traceFlushes: Promise<unknown>[]\n screencastFrames?: readonly ScreencastFrame[]\n configPath: string | undefined\n testFilePath: string | undefined\n emitArtifactsManifest?: boolean\n log: (level: 'info' | 'warn', msg: string) => void\n}\n\nexport function buildTraceContext(\n input: TraceContextInput,\n sessionId: string\n): TraceExportContext {\n return {\n mode: input.mode,\n policy: input.policy,\n granularity: input.granularity,\n format: input.format,\n capturer: input.capturer,\n actionSnapshots: input.capturer.actionSnapshots,\n sessionId,\n testMetadata: collectSuiteTestMetadata(input.suites),\n outcomes: input.outcomes,\n ranges: input.ranges,\n flushed: input.flushed,\n screencastFrames: input.screencastFrames,\n resolveOutputDir: () =>\n resolveAdapterOutputDir({\n testFilePath: input.testFilePath,\n configPath: input.configPath\n }),\n awaitPending: [...input.capturer.snapshotCaptures, ...input.traceFlushes],\n pendingCaptures: input.capturer.snapshotCaptures,\n // Nightwatch feeds real per-test attempt numbers via TestAttemptTracker\n // (B4), so retry-aware policies use per-test attempts, not the fallback.\n attemptInfoAvailable: true,\n // Off by default; opt-in only — Nightwatch has no live Allure signal to\n // auto-detect against (produce-only Allure), unlike WDIO/Selenium.\n emitManifest: input.emitArtifactsManifest ?? false,\n collectedArtifacts: input.artifacts,\n onArtifact: (a) => input.artifacts.push(a),\n log: input.log\n }\n}\n","/**\n * Produce-only per-test artifacts (screenshot + video) for the Nightwatch\n * plugin.\n *\n * Nightwatch has no live Allure attach API — its official `nightwatch-allure`\n * reporter is post-hoc, and `allure-js-commons`' `attachment()` no-ops in a\n * Nightwatch run (nothing wires the global test runtime). So we pass NO sink\n * (`attach: undefined`), which the core treats as produce-only: it writes the\n * file and records the manifest artifact via `onArtifact`, but skips the attach.\n *\n * Both the per-test (Mocha/Jasmine `afterEach`) and cucumber (per-scenario\n * finalize) paths funnel through here so the two produce calls live once. Core\n * gates both to trace mode + `test` granularity, so this no-ops otherwise.\n */\n\nimport {\n captureAndAttachScreenshot,\n captureAndAttachVideo,\n lastRenderedScreenshot,\n type TestOutcome,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\n\nexport interface EmitTestArtifactsInput {\n mode: DevToolsMode\n granularity: TraceGranularity\n screenshotPolicy: TraceScreenshotPolicy\n videoPolicy: TraceVideoPolicy\n failed: boolean\n actionSnapshots: readonly ActionSnapshot[]\n frames: readonly ScreencastFrame[]\n startWallTime: number\n /** This test's attempt slots (already scoped via `forTest`) for retention. */\n outcomes: TestOutcome[]\n uid: string | undefined\n attempt: number | undefined\n sessionId: string | undefined\n outputDir: string\n captureFormat?: 'jpeg' | 'png'\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Produce this test's screenshot then its video slice, both with `attach:\n * undefined` (produce-only). Each core call self-gates on mode/granularity/\n * policy, so a non-trace run or a non-`test` granularity is a no-op.\n */\nexport async function emitTestArtifacts(\n input: EmitTestArtifactsInput\n): Promise<void> {\n await captureAndAttachScreenshot({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.screenshotPolicy,\n failed: input.failed,\n screenshotBase64: lastRenderedScreenshot(\n input.actionSnapshots,\n input.startWallTime\n ),\n sessionId: input.sessionId,\n outputDir: input.outputDir,\n testUid: input.uid,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n await captureAndAttachVideo({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.videoPolicy,\n frames: input.frames,\n startWallTime: input.startWallTime,\n outcomes: input.outcomes,\n attempt: input.attempt,\n outputDir: input.outputDir,\n testUid: input.uid,\n sessionId: input.sessionId,\n captureFormat: input.captureFormat,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n}\n","// node:assert capture wiring: routes patched assertions into the session\n// capturer through the same captureCommand path driver commands use, so\n// retry bookkeeping and trace-mode action snapshots behave identically.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n patchNodeAssert,\n resolveAssertTargetFromArgs,\n type CapturedAssert\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:assertCapture')\n\n/**\n * Patch node:assert once per process. Getters are read at capture time — the\n * capturer is created lazily in session init and the test UID changes per test.\n */\nexport function wireAssertCapture(\n getCapturer: () => SessionCapturer | undefined,\n getTestUid: () => string | undefined\n): void {\n patchNodeAssert(\n (cmd) => captureAssert(getCapturer(), getTestUid(), cmd),\n (level, message) => log[level](message),\n // No handle resolver: Nightwatch commands take selector strings, so an\n // element object never reaches an assert — only the value's provenance,\n // recorded by the command path (BrowserProxy), can name the target.\n (args) => resolveAssertTargetFromArgs(args)\n )\n}\n\nfunction captureAssert(\n capturer: SessionCapturer | undefined,\n testUid: string | undefined,\n cmd: CapturedAssert\n): void {\n if (!capturer) {\n return\n }\n capturer\n .captureCommand(cmd.command, cmd.args, cmd.result, cmd.error, {\n testUid,\n callSource: cmd.callSource,\n timestamp: cmd.timestamp,\n // The assert's args hold the compared VALUES, so provenance — recorded by\n // the command path — is the row's only route to a locator.\n selector: cmd.selector\n })\n .catch((err) =>\n log.warn(`Failed to capture ${cmd.command}: ${errorMessage(err)}`)\n )\n // captureCommand pushes synchronously; mirror captureCommandError's send.\n const last = capturer.commandsLog[capturer.commandsLog.length - 1]\n if (last) {\n capturer.sendCommand(last)\n }\n}\n","/**\n * Run-level lifecycle helpers for the Nightwatch plugin — reuse-mode setup,\n * DevTools-browser spawning, end-of-run summary, and the post-run \"wait for\n * browser close\" loop.\n *\n * Extracted from `index.ts` to keep that file under the file-size cap.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport logger from '@wdio/logger'\nimport { remote } from 'webdriverio'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { start, stop } from '@wdio/devtools-backend'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n NightwatchCurrentTest\n} from './types.js'\nimport { TIMING, PLUGIN_GLOBAL_KEY } from './constants.js'\nimport { findFreePort, resolveNightwatchConfig } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:run-lifecycle')\n\nexport interface RunLifecycleCtx {\n options: { hostname: string; port: number; mode?: DevToolsMode }\n readonly testReporter: TestReporter | undefined\n readonly suiteManager: SuiteManager | undefined\n readonly testManager: TestManager\n readonly sessionCapturer: SessionCapturer | undefined\n devtoolsBrowser: WebdriverIO.Browser | undefined\n userDataDir: string | undefined\n passCount: number\n failCount: number\n skipCount: number\n clearExecutionData(): void\n}\n\nexport function handleReuseMode(ctx: RunLifecycleCtx): void {\n ctx.options.hostname = process.env[REUSE_ENV.HOST]!\n ctx.options.port = Number(process.env[REUSE_ENV.PORT])\n log.info(\n `♻ Reusing DevTools backend at ${ctx.options.hostname}:${ctx.options.port}`\n )\n // Clear execution data from the previous run when rerunning so test-name\n // caches and suites are fresh for the new run.\n if (ctx.testReporter) {\n ctx.clearExecutionData()\n ctx.passCount = 0\n ctx.failCount = 0\n ctx.skipCount = 0\n log.info('Cleared execution data for rerun')\n }\n}\n\nexport interface PluginBeforeCtx extends RunLifecycleCtx {\n setConfigPath(v: string | undefined): void\n openDevtoolsBrowserAt(url: string): Promise<void>\n handleReuse(): void\n // The plugin instance to assign to the global slot for cucumber hooks.\n plugin: unknown\n}\n\nexport async function runPluginBefore(ctx: PluginBeforeCtx): Promise<void> {\n // When relaunched by the DevTools UI rerun button the backend is already\n // running — skip startup and just connect the WebSocket worker.\n const isReuse =\n process.env[REUSE_ENV.REUSE] === '1' &&\n !!process.env[REUSE_ENV.HOST] &&\n !!process.env[REUSE_ENV.PORT]\n if (isReuse) {\n ctx.handleReuse()\n }\n const configPath = resolveNightwatchConfig()\n ctx.setConfigPath(configPath)\n if (configPath) {\n log.info(`✓ Config: ${configPath}`)\n } else {\n log.warn(\n 'Could not find nightwatch config — test rerun will be unavailable'\n )\n }\n if (isReuse) {\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n return\n }\n try {\n // Trace mode: skip backend port-bind and UI entirely — matches the WDIO\n // launcher gate. SessionCapturer construction in session-init also gates\n // its WS init off in trace mode.\n if (ctx.options.mode === 'trace') {\n log.info('Trace mode — skipping backend port-bind and UI window')\n } else {\n ctx.options.port = await findFreePort(\n ctx.options.port,\n ctx.options.hostname\n )\n log.info('🚀 Starting DevTools backend...')\n const { port } = await start(ctx.options)\n ctx.options.port = port\n const url = `http://${ctx.options.hostname}:${ctx.options.port}`\n log.info(`✓ Backend started on port ${ctx.options.port}`)\n log.info(` DevTools UI: ${url}`)\n await ctx.openDevtoolsBrowserAt(url)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.UI_CONNECTION_WAIT)\n )\n }\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n } catch (err) {\n log.error(`Failed to start backend: ${errorMessage(err)}`)\n throw err\n }\n}\n\nexport async function openDevtoolsBrowser(\n ctx: RunLifecycleCtx,\n url: string\n): Promise<void> {\n try {\n // Unique user data directory per instance to prevent conflicts.\n ctx.userDataDir = path.join(\n os.tmpdir(),\n `nightwatch-devtools-${ctx.options.port}-${Date.now()}`\n )\n if (!fs.existsSync(ctx.userDataDir)) {\n fs.mkdirSync(ctx.userDataDir, { recursive: true })\n }\n ctx.devtoolsBrowser = await remote({\n logLevel: 'info',\n capabilities: {\n browserName: 'chrome',\n 'goog:chromeOptions': {\n excludeSwitches: ['enable-automation'],\n args: [\n '--window-size=1600,1200',\n `--user-data-dir=${ctx.userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ]\n }\n }\n })\n await ctx.devtoolsBrowser.url(url)\n } catch (err) {\n log.error(`Failed to open DevTools UI: ${errorMessage(err)}`)\n log.info(`Please manually open: ${url}`)\n }\n}\n\nexport async function finalizeAllSuites(\n ctx: RunLifecycleCtx,\n browser?: NightwatchBrowser\n): Promise<void> {\n const currentTest = browser?.currentTest as NightwatchCurrentTest | undefined\n const testcases = currentTest?.results?.testcases ?? {}\n for (const [, suite] of (\n ctx.suiteManager?.getAllSuites() ?? new Map()\n ).entries()) {\n ctx.testManager.finalizeSuiteTests(suite, testcases)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n ctx.suiteManager?.finalizeSuite(suite)\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n}\n\nexport function logRunSummary(ctx: RunLifecycleCtx): void {\n const summary = [\n ctx.passCount > 0 ? `${ctx.passCount} passed` : null,\n ctx.failCount > 0 ? `${ctx.failCount} failed` : null,\n ctx.skipCount > 0 ? `${ctx.skipCount} skipped` : null\n ]\n .filter(Boolean)\n .join(' ')\n log.info(`${ctx.failCount > 0 ? '❌' : '✅'} Tests complete! ${summary}`)\n log.info(` DevTools UI: http://${ctx.options.hostname}:${ctx.options.port}`)\n}\n\nexport async function waitForDevtoolsBrowserClose(\n ctx: RunLifecycleCtx\n): Promise<void> {\n if (!ctx.devtoolsBrowser) {\n return\n }\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'webdriver',\n 'warn'\n )\n let exitBySignal = false\n const signalHandler = () => {\n exitBySignal = true\n log.info('\\n✓ Exiting... Browser window will remain open')\n process.exit(0)\n }\n process.once('SIGINT', signalHandler)\n process.once('SIGTERM', signalHandler)\n while (true) {\n try {\n await ctx.devtoolsBrowser.getTitle()\n await new Promise((res) => setTimeout(res, TIMING.BROWSER_POLL_INTERVAL))\n } catch {\n if (!exitBySignal) {\n log.info('Browser window closed, stopping DevTools app')\n break\n }\n }\n }\n if (exitBySignal) {\n return\n }\n process.removeListener('SIGINT', signalHandler)\n process.removeListener('SIGTERM', signalHandler)\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'webdriver',\n 'info'\n )\n try {\n await ctx.devtoolsBrowser.deleteSession()\n } catch {\n /* session already closed */\n }\n await stop()\n process.exit(0)\n}\n","/**\n * Global key the plugin uses to hand itself to the Cucumber hooks\n * (`helpers/cucumberHooks.cts`). Single source of truth, re-exported by\n * `constants.ts`.\n *\n * It lives in its own leaf module with NO imports on purpose: the Cucumber\n * hooks are bundled standalone into a CJS file, and importing this from the\n * full `constants.ts` would drag in its ESM dependency graph (→ core), which\n * uses `import.meta.url` / `createRequire` and throws when bundled to CJS.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\n","/**\n * Internal Nightwatch commands to exclude from capture\n */\nexport const INTERNAL_COMMANDS_TO_IGNORE = [\n 'isAppiumClient',\n 'isSafari',\n 'isChrome',\n 'isFirefox',\n 'isEdge',\n 'isMobile',\n 'isAndroid',\n 'isIOS',\n 'session',\n 'sessions',\n 'timeouts',\n 'timeoutsAsyncScript',\n 'timeoutsImplicitWait',\n 'getLog',\n 'getLogTypes',\n 'screenshot',\n 'availableContexts',\n 'currentContext',\n 'setChromeOptions',\n 'setDeviceName',\n 'perform',\n 'execute',\n 'executeAsync',\n 'executeScript',\n // Internal Nightwatch transport commands (used for log capture, not user actions)\n 'sessionLog',\n 'sessionLogTypes',\n 'isLogAvailable',\n 'end'\n] as const\n\n// Console capture constants are defined in @wdio/devtools-core; re-exported\n// here so existing imports from ./constants.js continue to work.\nexport {\n ANSI_REGEX,\n CONSOLE_METHODS,\n LOG_LEVEL_PATTERNS,\n LOG_SOURCES\n} from '@wdio/devtools-core'\n\nimport { DEFAULTS_BASE, TIMING_BASE } from '@wdio/devtools-shared'\n\nexport const DEFAULTS = {\n ...DEFAULTS_BASE,\n TEST_NAME: 'unknown',\n FILE_NAME: 'unknown'\n} as const\n\nexport const TIMING = {\n ...TIMING_BASE,\n /** Nightwatch boots slower than selenium — give the dashboard 10s. */\n UI_CONNECTION_WAIT: 10000\n} as const\n\nexport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Generic pattern matching Nightwatch commands whose result is a boolean.\n */\nexport const BOOLEAN_COMMAND_PATTERN =\n /^waitFor|^is[A-Z]|^has[A-Z]|(Visible|Present|Enabled|Selected|NotVisible|NotPresent)$/\n\nexport const NAVIGATION_COMMANDS = ['url', 'navigate', 'navigateTo'] as const\n\nexport { SPINNER_RE } from '@wdio/devtools-core'\n\n/** Matches file names that follow the *.test.ts / *.spec.js naming\n * convention. Re-exported from @wdio/devtools-shared (single source of\n * truth — service uses the same pattern under the name SPEC_FILE_RE). */\nexport { SPEC_FILE_RE as TEST_FILE_PATTERN } from '@wdio/devtools-shared'\n\n/** Nightwatch config file names to search for, in priority order. */\nexport const CONFIG_FILENAMES = [\n 'nightwatch.conf.cjs',\n 'nightwatch.conf.js',\n 'nightwatch.conf.ts',\n 'nightwatch.conf.mjs',\n 'nightwatch.json'\n] as const\n\n/**\n * Global key used to share the plugin instance with Cucumber hooks.\n * Defined in its own leaf module so the Cucumber hooks can bundle it standalone\n * (see plugin-global-key.ts); re-exported here for the rest of the plugin.\n */\nexport { PLUGIN_GLOBAL_KEY } from './plugin-global-key.js'\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { parse as parseStackTrace } from 'stacktrace-parser'\nimport {\n findTestDefinitions,\n generateStableUid as generateStableUidByFileName,\n isUserCodeFrame,\n normalizeFilePath\n} from '@wdio/devtools-core'\nimport { TEST_FILE_PATTERN, CONFIG_FILENAMES } from '../constants.js'\nimport type {\n NightwatchTestCase,\n TestFileMetadata,\n StepLocation,\n TestRunnerId,\n TestStats\n} from '../types.js'\n\n// These three are pure re-exports — adapters use the core implementations\n// directly, no wrapper logic. Single-line re-exports keep the indirection\n// visible without introducing dummy variables.\nexport {\n deterministicUid,\n getCallSourceFromStack,\n resetSignatureCounters\n} from '@wdio/devtools-core'\n\nexport function determineTestState(\n testcase: NightwatchTestCase\n): 'passed' | 'failed' | 'skipped' {\n if (testcase.passed === 0 && testcase.failed === 0) {\n return 'skipped'\n }\n return testcase.passed > 0 && testcase.failed === 0 ? 'passed' : 'failed'\n}\n\nexport function getTestIcon(state: TestStats['state']): string {\n return state === 'passed' ? '✅' : state === 'skipped' ? '⏭' : '❌'\n}\n\nexport function incrementCounters(\n counters: { passCount: number; failCount: number; skipCount: number },\n state: TestStats['state']\n): void {\n if (state === 'passed') {\n counters.passCount++\n } else if (state === 'skipped') {\n counters.skipCount++\n } else {\n counters.failCount++\n }\n}\n\n/** This adapter's runner identity. Both Nightwatch runners take the same\n * locator dialect, but the id also reaches the dashboard's run capabilities,\n * where the cucumber variant differs. */\nexport function nightwatchRunnerId(isCucumberRunner: boolean): TestRunnerId {\n return isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch'\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: nightwatchRunnerId(input.isCucumberRunner),\n configFile: input.configPath,\n baseDir: process.cwd(),\n runCapabilities: {\n canRunSuites: true,\n canRunTests: !input.isCucumberRunner,\n canRunAll: false\n }\n }\n}\n\n/**\n * Generate stable UID for test/suite.\n * Accepts either (item: SuiteStats | TestStats) or (file: string, name: string).\n * Hashing is delegated to @wdio/devtools-core; this wrapper preserves the\n * dual-signature convenience used by the Nightwatch suite/test managers.\n */\ntype StableUidSource = { file?: string; fullTitle?: string; title?: string }\nexport function generateStableUid(\n itemOrFile: string | StableUidSource,\n name?: string\n): string {\n let file: string, testName: string\n if (\n typeof itemOrFile === 'object' &&\n itemOrFile !== null &&\n name === undefined\n ) {\n file = itemOrFile.file || ''\n testName = String(itemOrFile.fullTitle || itemOrFile.title)\n } else {\n file = (itemOrFile as string) || ''\n testName = String(name || '')\n }\n return generateStableUidByFileName(file, testName)\n}\n\n/**\n * Find test file from stack trace.\n * Parses call stack to find the first frame that looks like a test file.\n */\nexport function findTestFileFromStack(): string | undefined {\n const stack = new Error().stack\n if (!stack) {\n return undefined\n }\n\n // Prefer a frame whose filename matches *.test.ts / *.spec.ts (strong\n // signal). Fall back to the first plain user-code frame so arbitrary\n // project layouts (e.g. Cucumber step files under `src/steps/`) still\n // surface their owning file instead of going undetected.\n const frames = parseStackTrace(stack)\n const preferred = frames.find(\n (f) => isUserCodeFrame(f) && TEST_FILE_PATTERN.test(f.file)\n )\n const frame = preferred ?? frames.find(isUserCodeFrame)\n if (!frame?.file) {\n return undefined\n }\n\n const filePath = normalizeFilePath(frame.file)\n return fs.existsSync(filePath) ? filePath : undefined\n}\n\n/**\n * Extract suite and test names — and their line numbers — from a test file.\n * Returns a TestFileMetadata object used to build `callSource` strings for\n * the TestLens eye-icon navigation.\n */\nexport function extractTestMetadata(filePath: string): TestFileMetadata {\n const defs = findTestDefinitions(filePath, {\n includeNightwatchObjectStyle: true\n })\n const firstSuite = defs.find((d) => d.kind === 'suite')\n const tests = defs.filter((d) => d.kind === 'test')\n return {\n suiteTitle: firstSuite?.title ?? null,\n suiteLine: firstSuite?.line ?? null,\n testNames: tests.map((t) => t.title),\n testLines: tests.map((t) => t.line)\n }\n}\n\n/**\n * Find test file by searching the workspace for a matching filename.\n * Used when the stack trace doesn't have the file yet (e.g. in beforeEach).\n */\nexport function findTestFileByName(\n filename: string,\n workspaceRoot?: string\n): string | undefined {\n if (!filename || !workspaceRoot) {\n return undefined\n }\n\n const baseFilename = filename.replace(/\\.[cm]?[jt]sx?$/, '')\n\n function searchDir(dir: string, depth = 0): string | undefined {\n if (depth > 5) {\n return undefined\n }\n try {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n const found = searchDir(fullPath, depth + 1)\n if (found) {\n return found\n }\n } else if (\n TEST_FILE_PATTERN.test(entry.name) &&\n entry.name.replace(TEST_FILE_PATTERN, '') === baseFilename\n ) {\n return fullPath\n }\n }\n } catch {\n // Permission denied or other error — skip\n }\n return undefined\n }\n\n return searchDir(workspaceRoot)\n}\n\n// ---------------------------------------------------------------------------\n// Console / log helpers (used by SessionCapturer)\n// ---------------------------------------------------------------------------\n\n// Console helpers come from @wdio/devtools-core. `stripAnsiCodes` is the\n// local name kept for backwards compatibility with existing import sites.\nexport {\n stripAnsi as stripAnsiCodes,\n detectLogLevel,\n createConsoleLogEntry\n} from '@wdio/devtools-core'\n\nexport { chromeLogLevelToLogLevel } from '@wdio/devtools-core'\n\n/** Derive a human-readable request type from URL and MIME type. */\nexport { getRequestType } from '@wdio/devtools-core'\n\n// ---------------------------------------------------------------------------\n// Cucumber helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a feature file for a given scenario in a single pass, returning:\n * - `featureLine` — 1-based line of the `Feature:` declaration\n * - `scenarioLine` — 1-based line of the matching `Scenario:` block\n * - `stepLines` — 1-based line numbers for each step (for TestLens navigation)\n * - `stepKeywords` — BDD keyword (Given/When/Then/And/But) for each step (for labels)\n */\nfunction collectStepsAfterScenario(\n lines: string[],\n scenarioIndex: number,\n stepCount: number\n): { stepLines: number[]; stepKeywords: string[] } {\n const stepRe = /^\\s*(Given|When|Then|And|But)\\s+/i\n const stepLines: number[] = []\n const stepKeywords: string[] = []\n for (\n let j = scenarioIndex + 1;\n j < lines.length && stepLines.length < stepCount;\n j++\n ) {\n if (/^\\s*(?:Scenario:|Feature:)/i.test(lines[j])) {\n break\n }\n const m = stepRe.exec(lines[j])\n if (m) {\n stepLines.push(j + 1)\n stepKeywords.push(m[1])\n }\n }\n return { stepLines, stepKeywords }\n}\n\nexport function parseCucumberScenario(\n featureContent: string,\n scenarioName: string,\n stepTexts: string[]\n): {\n featureLine: number\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const stepCount = stepTexts.length\n if (!featureContent) {\n return {\n featureLine: 1,\n scenarioLine: 1,\n stepLines: [],\n stepKeywords: Array<string>(stepCount).fill('')\n }\n }\n const lines = featureContent.split('\\n')\n let featureLine = 1\n let scenarioLine = 1\n let stepLines: number[] = []\n let stepKeywords: string[] = []\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n if (featureLine === 1 && /^\\s*Feature:/i.test(line)) {\n featureLine = lineNum\n continue\n }\n if (/^\\s*Scenario:/i.test(line) && line.includes(scenarioName)) {\n scenarioLine = lineNum\n const collected = collectStepsAfterScenario(lines, i, stepCount)\n stepLines = collected.stepLines\n stepKeywords = collected.stepKeywords\n break\n }\n }\n while (stepKeywords.length < stepCount) {\n stepKeywords.push('')\n }\n return { featureLine, scenarioLine, stepLines, stepKeywords }\n}\n\n/** Maps Cucumber parameter type placeholder names to their regex equivalents. */\nconst CUCUMBER_TYPE_PATTERNS: Record<string, string> = {\n int: '-?\\\\d+',\n float: '-?\\\\d+(?:\\\\.\\\\d+)?',\n string: '(?:\"[^\"]*\"|\\'[^\\']*\\')',\n word: '\\\\S+'\n}\n\n/**\n * Convert a Cucumber expression pattern to a regular expression and test it\n * against the given step text. Handles {int}, {float}, {string}, {word}, and\n * arbitrary {type} placeholders.\n */\nfunction matchesCucumberExpression(pattern: string, text: string): boolean {\n if (pattern === text) {\n return true\n }\n const regexSource = pattern\n .split(/(\\{[^}]*\\})/)\n .map((part, idx) => {\n if (idx % 2 === 1) {\n const type = part.slice(1, -1)\n return CUCUMBER_TYPE_PATTERNS[type] ?? '[\\\\s\\\\S]+'\n }\n return part.replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&')\n })\n .join('')\n try {\n return new RegExp(`^${regexSource}$`).test(text)\n } catch {\n return false\n }\n}\n\n/**\n * Find the file and 1-based line number of the step definition that matches\n * the given step text. Searches the provided step definition files in order.\n * Handles both string/Cucumber-expression patterns and regex literals.\n */\nexport function findStepDefinitionLine(\n stepDefFiles: Array<{ filePath: string; content: string }>,\n stepText: string\n): StepLocation | null {\n const stepDefRe = /(?:Given|When|Then|And|But)\\s*\\(/i\n\n for (const { filePath, content } of stepDefFiles) {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!stepDefRe.test(line)) {\n continue\n }\n\n // String / Cucumber-expression: Given('pattern', ...) or Given(\"pattern\", ...)\n const literalMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*(['\"`])(.*?)\\1/i\n )\n if (literalMatch) {\n if (matchesCucumberExpression(literalMatch[2], stepText)) {\n return { filePath, line: i + 1 }\n }\n continue\n }\n\n // Regex literal: Given(/pattern/flags, ...)\n const regexMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*\\/(.+?)\\/([gimy]*)/i\n )\n if (regexMatch) {\n try {\n if (new RegExp(regexMatch[1], regexMatch[2]).test(stepText)) {\n return { filePath, line: i + 1 }\n }\n } catch {\n // invalid regex — skip\n }\n }\n }\n }\n return null\n}\n\nexport { isPortInUse, findFreePort } from '@wdio/devtools-core'\n\nexport function resolveNightwatchConfig(): string | undefined {\n // Prefer the config explicitly passed via -c / --config to avoid picking up\n // an unrelated config file that happens to sit higher in the directory tree.\n const configFlagIdx = process.argv.findIndex(\n (arg) => arg === '--config' || arg === '-c'\n )\n if (configFlagIdx !== -1 && process.argv[configFlagIdx + 1]) {\n const argvConfig = process.argv[configFlagIdx + 1]\n const resolved = path.isAbsolute(argvConfig)\n ? argvConfig\n : path.resolve(process.cwd(), argvConfig)\n if (fs.existsSync(resolved)) {\n return resolved\n }\n }\n\n // Fallback: walk up the directory tree\n let dir = process.cwd()\n const root = path.parse(dir).root\n while (dir && dir !== root) {\n for (const file of CONFIG_FILENAMES) {\n const candidate = path.join(dir, file)\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n const parent = path.dirname(dir)\n if (parent === dir) {\n break\n }\n dir = parent\n }\n return undefined\n}\n","/**\n * Session-initialization lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size\n * cap. The plugin passes itself as a `SessionInitCtx` — a narrow interface\n * exposing only the fields and methods these helpers need.\n *\n * Includes:\n * - Per-run bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Re-targeting that one capturer when the session under it is replaced\n * - Re-arming the per-session capture channels when a session is replaced\n * - Screencast finalize-and-clear\n *\n * The recorder's own session binding lives in `screencast-session.ts`.\n */\n\nimport logger from '@wdio/logger'\nimport { resolveViewport } from '@wdio/devtools-core'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport {\n errorMessage,\n finalizeScreencast,\n registerCollectorPreload,\n resolveAdapterOutputDir\n} from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport { TIMING } from './constants.js'\nimport { SessionCapturer } from './session.js'\nimport { TestReporter } from './reporter.js'\nimport { TestManager } from './helpers/testManager.js'\nimport { SuiteManager } from './helpers/suiteManager.js'\nimport { BrowserProxy } from './helpers/browserProxy.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport {\n rotateScreencastForSession,\n startScreencast\n} from './screencast-session.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats,\n TestRunnerId,\n TestStats\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:session-init')\n\nexport interface SessionInitCtx {\n readonly hostname: string\n readonly port: number\n readonly screencastOptions: ScreencastOptions\n readonly bidiEnabled: boolean\n readonly mode: DevToolsMode\n readonly captureAssertions: boolean\n readonly runner: TestRunnerId\n\n sessionCapturer: SessionCapturer\n testReporter: TestReporter\n testManager: TestManager\n suiteManager: SuiteManager\n browserProxy: BrowserProxy\n isScriptInjected: boolean\n\n lastSessionId: string | null\n /** Session the BiDi attach + collector preload were last armed for — the\n * once-per-session guard for both, and the rotation detector. */\n armedSessionId: string | undefined\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n /** In-flight recorder rotation — see screencast-session.ts. */\n screencastRotation: Promise<void> | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n attemptFor(uid: string): number | undefined\n recordOutcome(uid: string, state: TestStats['state']): void\n /** Drains the live recorder's frames into the run's filmstrip buffer, then\n * stops and clears it. */\n finalizeCurrentScreencast(): Promise<void>\n}\n\nfunction initReporterChain(ctx: SessionInitCtx): void {\n // First-time setup: create reporter chain once for the entire run.\n // These must NOT be recreated on session change — doing so generates a\n // new feature suite with a fresh start timestamp, which DataManager sees\n // as a new run and wipes all accumulated commands.\n ctx.testReporter = new TestReporter(\n (suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n },\n (uid) => ctx.attemptFor(uid)\n )\n ctx.testManager = new TestManager(ctx.testReporter, (uid, state) =>\n ctx.recordOutcome(uid, state)\n )\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite(),\n ctx.captureAssertions,\n // Nightwatch raises no session event and never wraps `end`, so a command\n // running under a new sessionId is the only trace a mid-run\n // `browser.end()` leaves behind. Both re-bindings hang off it; each latches\n // and gates itself, so neither can suppress the other.\n (browser) => {\n rotateScreencastForSession(ctx, browser)\n rearmCaptureForSession(ctx, browser)\n }\n )\n}\n\n/** The run's geometry, over the raw WebDriver transport: `browser.*` commands\n * are QUEUED, so a read issued here would sit behind the command in flight. */\nfunction readViewport(browser: NightwatchBrowser) {\n return resolveViewport(browser.capabilities || {}, {\n runScript: (body) => webdriverExecute(browser, body),\n getWindowSize: () => webdriverGet(browser, 'window/rect'),\n onWarn: (message) => log.warn(message)\n })\n}\n\n/** Whether the session was created asking for Chrome's `performance` log, from\n * either side of the capability pair. A perf log exists only because a session\n * asked for one, so this decides both the warning and whether the per-command\n * fetch runs at all — one reading, two consumers. */\nexport function wantsPerformanceLog(\n capabilities: Record<string, unknown>,\n desiredCapabilities: Record<string, unknown>\n): boolean {\n const loggingPrefs = (capabilities['goog:loggingPrefs'] ||\n desiredCapabilities['goog:loggingPrefs'] ||\n {}) as { performance?: string }\n return Boolean(loggingPrefs.performance)\n}\n\nasync function broadcastSessionMetadata(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const capabilities = browser.capabilities || {}\n const desiredCapabilities = browser.desiredCapabilities || {}\n const sessionId = browser.sessionId\n const opts = browser.options || {}\n\n if (ctx.srcFolders.length === 0) {\n const sf = (opts as { src_folders?: string | string[] }).src_folders\n ctx.srcFolders = Array.isArray(sf) ? sf : sf ? [sf] : []\n }\n\n const viewport = await readViewport(browser)\n const metadata = {\n type: TraceType.Testrunner,\n capabilities,\n ...(viewport ? { viewport } : {}),\n desiredCapabilities,\n sessionId,\n testEnv: opts.testEnv,\n host: opts.webdriver?.host,\n options: ctx.buildMetadataOptions(),\n runner: ctx.runner,\n url: ''\n }\n // Single-valued, so a run-spanning trace carries the LAST session's identity.\n ctx.sessionCapturer.metadata = metadata\n ctx.sessionCapturer.sendUpstream('metadata', metadata)\n\n const browserName =\n capabilities.browserName || desiredCapabilities.browserName || 'unknown'\n const browserVersion =\n capabilities.browserVersion ||\n (capabilities as { version?: string }).version ||\n ''\n log.info(\n `✓ Browser: ${browserName}${browserVersion ? ' ' + browserVersion : ''} (session: ${sessionId})`\n )\n\n const wantsPerfLog = wantsPerformanceLog(\n capabilities as Record<string, unknown>,\n desiredCapabilities as Record<string, unknown>\n )\n // The same reading decides whether the capture path runs at all: without the\n // capability there is nothing to fetch, and asking anyway costs a failed\n // round trip per command — see `SessionCapturer.perfLogsRequested`.\n ctx.sessionCapturer.perfLogsRequested = wantsPerfLog\n if (!wantsPerfLog && !ctx.bidiEnabled) {\n log.warn(\n \"⚠ Network tab will be empty — add 'goog:loggingPrefs': { performance: 'ALL' } to your capabilities (or enable bidi:true)\"\n )\n }\n}\n\n// BiDi: opt-in. Requires `webSocketUrl: true` capability + a BiDi-capable\n// chromedriver. Attempted once per session (the caller's `armedSessionId` stamp\n// is that guard); on failure or unavailability the perf-log fallback path\n// continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled) {\n return\n }\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n log.warn('bidi:true set but browser.driver unavailable — skipping')\n return\n }\n const { attachBidiHandlers, buildBidiSinks } = await import('./bidi.js')\n const ok = await attachBidiHandlers(\n driver,\n buildBidiSinks(ctx.sessionCapturer)\n )\n if (ok) {\n ctx.sessionCapturer.bidiActive = true\n log.info('✓ BiDi attached — perf-log network capture disabled')\n }\n}\n\n/**\n * Register the collector to run at document-start, so every document — including\n * the ones a navigation creates without us noticing — instruments and anchors\n * itself.\n *\n * Deliberately NOT gated on the `bidi` option: that option exists to avoid\n * double-reporting console/network against the perf-log path, whereas this needs\n * nothing but a session created with `webSocketUrl: true`. Gating DOM capture on\n * an unrelated opt-in that defaults to false would leave the race in place for\n * almost every user. Self-degrades to the per-document `<script>` injection when\n * BiDi isn't there.\n */\nasync function tryRegisterPreload(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n return\n }\n ctx.sessionCapturer.preloadRegistered = await registerCollectorPreload(\n driver,\n (level, message) => log[level](message)\n )\n}\n\n/**\n * Arm the two capture channels that belong to ONE WebDriver session: the BiDi\n * inspectors (bound to that session's driver object) and the document-start\n * collector preload (registered on that session's BiDi channel). Neither\n * survives the session, so a replaced session needs both again.\n *\n * The stamp is written before any await: on rotation this runs from the command\n * hook, where the next command arrives long before a BiDi round trip completes,\n * and the stamp is what keeps that flood from re-arming.\n *\n * The preload goes FIRST. Behind the attach it lost the race it exists to win —\n * measured on a re-arm, the network subscribe took 6.2 s while the command that\n * triggered the re-arm navigated, so the registration landed two seconds after\n * the document it was meant to instrument was born. Sequential rather than\n * parallel because both steps open the session's BiDi channel through the same\n * unsynchronized `getBidi()` cache, and racing them opens two websockets of\n * which `quit()` closes one.\n */\nasync function armCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n ctx.armedSessionId = browser.sessionId ?? undefined\n await tryRegisterPreload(ctx, browser)\n await tryAttachBidi(ctx, browser)\n}\n\n/**\n * Whether the browser is executing under a session the capture channels were\n * never armed for. Requires an already-armed session: before bringup there is no\n * capturer to arm onto, and `ensureSessionInitialized` owns that first arm.\n */\nexport function needsCaptureRearm(\n ctx: Pick<SessionInitCtx, 'armedSessionId' | 'sessionCapturer'>,\n sessionId: string | undefined\n): boolean {\n return (\n Boolean(ctx.sessionCapturer) &&\n sessionId !== undefined &&\n ctx.armedSessionId !== undefined &&\n ctx.armedSessionId !== sessionId\n )\n}\n\n/**\n * Re-arm BiDi and the collector preload after the session was replaced. Without\n * it both are established exactly once, at bringup, and sessions 2..N silently\n * fall back to `<script>` injection — the whole navigation race class the\n * preload exists to remove comes back for the rest of the run.\n *\n * The `needsCaptureRearm` guard is what makes it idempotent: the command hook\n * and `ensureSessionInitialized` both detect the same replacement, and\n * `armCaptureForSession` stamps `armedSessionId` before its first await, so\n * whichever gets there first latches the other out.\n */\nasync function armReplacedSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const sessionId = browser.sessionId ?? undefined\n if (!needsCaptureRearm(ctx, sessionId)) {\n return\n }\n // Both claims belong to the session that just died. Until the new\n // registration lands this session's documents are back on the `<script>`\n // path — exactly what `anchorAfterNavigation` polls for — and a stale\n // `bidiActive` would keep the perf-log network path gated off behind\n // inspectors that no longer receive anything.\n ctx.sessionCapturer.preloadRegistered = false\n ctx.sessionCapturer.bidiActive = false\n log.info(`Session replaced — re-arming capture for ${sessionId}`)\n await armCaptureForSession(ctx, browser)\n}\n\n/** Fire-and-forget {@link armReplacedSession}, for the command hook — a command\n * must not wait on a BiDi handshake. */\nexport function rearmCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n void armReplacedSession(ctx, browser).catch((err) =>\n log.warn(`Capture re-arm failed: ${errorMessage(err)}`)\n )\n}\n\n/** Re-run the per-session bringup against a replaced session, keeping the run's\n * one capturer. Both halves are the guarded helpers the command hook uses, so a\n * replacement both paths see is armed and rotated once. */\nasync function rebindSessionToBrowser(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n log.info('Browser session changed — re-targeting the run capturer')\n // Also gates `wrapUrlMethod`, which is per browser OBJECT — cucumber hands\n // over a new one per scenario.\n ctx.isScriptInjected = false\n await broadcastSessionMetadata(ctx, browser)\n await armReplacedSession(ctx, browser)\n rotateScreencastForSession(ctx, browser)\n await ctx.screencastRotation\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n // Unconditional: cucumber hands over a new browser object per scenario, and\n // a stale reference answers neither probe the capturer runs against it.\n ctx.sessionCapturer.setBrowser(browser)\n if (isSessionChange) {\n await rebindSessionToBrowser(ctx, browser)\n }\n return\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.INITIAL_CONNECTION_WAIT)\n )\n // Trace mode: empty opts skip SessionCapturerBase's WS init — no backend\n // to forward events to anyway.\n ctx.sessionCapturer = new SessionCapturer(\n ctx.mode === 'trace' ? {} : { port: ctx.port, hostname: ctx.hostname },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n ctx.sessionCapturer.runner = ctx.runner\n if (ctx.mode !== 'trace') {\n const connected = await ctx.sessionCapturer.waitForConnection(3000)\n if (!connected) {\n log.error('❌ Worker WebSocket failed to connect!')\n }\n }\n initReporterChain(ctx)\n await broadcastSessionMetadata(ctx, browser)\n await armCaptureForSession(ctx, browser)\n await startScreencast(ctx, browser, browser.sessionId)\n}\n\nexport async function finalizeCurrentScreencast(\n ctx: SessionInitCtx\n): Promise<void> {\n if (!ctx.screencastRecorder || !ctx.screencastSessionId) {\n return\n }\n if (ctx.mode === 'trace') {\n // Trace mode: the per-test video is written by the produce path and the\n // filmstrip frames are embedded in the trace itself, so stop the recorder\n // without encoding a session .webm nothing references (orphan file).\n await ctx.screencastRecorder.stop()\n } else {\n await finalizeScreencast({\n recorder: ctx.screencastRecorder,\n sessionId: ctx.screencastSessionId,\n filenamePrefix: 'nightwatch-video',\n outputDir: resolveAdapterOutputDir({\n testFilePath: ctx.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: ctx.configPath\n }),\n captureFormat: ctx.screencastOptions.captureFormat,\n sendUpstream: (scope, data) =>\n ctx.sessionCapturer?.sendUpstream(scope, data),\n onLog: (level, message) => log[level](message)\n })\n }\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","// Direct WebDriver HTTP transport for the capture probes.\n//\n// Every probe the trace capture makes — url, title, screenshot, in-page script\n// — has to bypass Nightwatch's command queue. `browser.*` commands are QUEUED,\n// so a probe the plugin issues from inside its own command hook enqueues behind\n// the command that is still running and cannot resolve until the queue drains.\n// Unguarded, one such probe stranded the whole per-action snapshot capture; the\n// in-page script probe merely timed out, leaving empty a11y trees.\n\nimport logger from '@wdio/logger'\nimport {\n webdriverGet as coreGet,\n webdriverPost as corePost,\n type WebDriverAddress\n} from '@wdio/devtools-core'\nimport type { NightwatchBrowser } from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:webdriverHttp')\n\ntype LooseRec = Record<string, unknown>\n\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\n\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\n\nconst firstDefined = (obj: unknown, ...keys: string[]): unknown => {\n if (!obj || typeof obj !== 'object') {\n return undefined\n }\n const rec = obj as LooseRec\n for (const k of keys) {\n const v = rec[k]\n if (v !== undefined && v !== null) {\n return v\n }\n }\n return undefined\n}\n\n/**\n * Walks Nightwatch's internal config (transport / queue.transport /\n * nightwatchInstance — none of which are on the public NightwatchBrowser type)\n * to find the underlying WebDriver host+port.\n */\nexport function resolveWebDriverAddress(browser: NightwatchBrowser): {\n driverHost: string\n driverPort: number\n} {\n const transportSettings =\n getPath(browser, 'transport', 'settings', 'webdriver') ||\n getPath(browser, 'queue', 'transport', 'settings', 'webdriver') ||\n getPath(\n browser,\n 'nightwatchInstance',\n 'transport',\n 'settings',\n 'webdriver'\n ) ||\n {}\n const opts = getProp(browser, 'options') ?? {}\n const nightwatchSettings =\n getPath(browser, 'nightwatchInstance', 'settings') ||\n getPath(browser, 'globals', 'nightwatchInstance', 'settings') ||\n {}\n const driverHost = String(\n firstDefined(transportSettings, 'host', 'server_address') ||\n firstDefined(getProp(opts, 'webdriver'), 'host') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'host') ||\n 'localhost'\n )\n const driverPort = Number(\n firstDefined(transportSettings, 'port') ||\n firstDefined(getProp(opts, 'webdriver'), 'port') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'port') ||\n 9515\n )\n return { driverHost, driverPort }\n}\n\n/** The core transport's address, plus this adapter's logger. Nightwatch always\n * speaks plain http to a local driver — it has no cloud-grid path of its own. */\nfunction address(browser: NightwatchBrowser): WebDriverAddress {\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return {\n hostname: driverHost,\n port: driverPort,\n onWarn: (message: string) => log.warn(message)\n }\n}\n\nfunction sessionId(browser: NightwatchBrowser): string | undefined {\n return (browser as unknown as { sessionId?: string }).sessionId\n}\n\n/** GET `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverGet<T>(\n browser: NightwatchBrowser,\n path: string\n): Promise<T | null> {\n const id = sessionId(browser)\n return id ? coreGet<T>(address(browser), id, path) : Promise.resolve(null)\n}\n\n/** POST `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverPost<T>(\n browser: NightwatchBrowser,\n path: string,\n body: unknown\n): Promise<T | null> {\n const id = sessionId(browser)\n return id\n ? corePost<T>(address(browser), id, path, body)\n : Promise.resolve(null)\n}\n\n/** Run a script in the page, outside the command queue. `body` is a function\n * body, matching what `browser.execute` accepts. */\nexport function webdriverExecute<T>(\n browser: NightwatchBrowser,\n body: string\n): Promise<T | null> {\n return webdriverPost<T>(browser, 'execute/sync', { script: body, args: [] })\n}\n","import logger from '@wdio/logger'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n COLLECTOR_READY_EXPRESSION,\n SessionCapturerBase,\n applyPerformanceData,\n collectorDrainExpression,\n drainCollectorWithRecovery,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n mapCommandToAction,\n pollUntilReady,\n serializeError,\n upsertRichestSnapshot,\n type CapturedPerformancePayload,\n type SessionCapturerOptions\n} from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { nightwatchRunnerId } from './helpers/utils.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type {\n ActionSnapshot,\n CommandCaptureMeta,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser,\n TestRunnerId\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:SessionCapturer')\n\n/**\n * WebDriver responses are sometimes wrapped as `{ value: T }` (the W3C\n * protocol shape) and sometimes flat. This helper unwraps the value field\n * if present, otherwise returns the input as-is.\n */\nfunction unwrapDriverValue<T = unknown>(result: unknown): T {\n if (result && typeof result === 'object' && 'value' in result) {\n return (result as { value: T }).value\n }\n return result as T\n}\n\nexport class SessionCapturer extends SessionCapturerBase {\n #browser: NightwatchBrowser | undefined\n\n // True once BiDi inspectors are attached — the per-command perf-log network\n // capture path skips when set, so we don't double-emit network requests.\n bidiActive = false\n\n /** Whether the session was created with `goog:loggingPrefs.performance`, set\n * from the capabilities at session init. A perf log exists only because a\n * session asked for one, so without it every fetch is a round trip that can\n * only fail — and the driver's own transport prints each failure, so the\n * cost is a wall of errors on a run that passed: measured on iOS Safari,\n * 21 of them in a two-test run, where XCUITest lists `performance` among its\n * supported types and then serves none of it. */\n perfLogsRequested = false\n\n /** True once the collector is registered to run at document-start. Every\n * document then instruments and anchors itself, so the paths that exist to\n * notice a navigation after the fact — re-injection and the settle poll — have\n * nothing left to do and stand down. */\n preloadRegistered = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n\n /** Which Nightwatch runner is driving — set by the plugin, and passed to the\n * per-action capture so the injected scripts emit this runner's dialect. */\n runner: TestRunnerId = nightwatchRunnerId(false)\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: SessionCapturerOptions = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n /** Re-target at the browser the run now drives — the capturer's only\n * session-bound state, so a replaced session is a re-target, not a rebuild\n * (mirrors selenium's `setDriver`). */\n setBrowser(browser: NightwatchBrowser): void {\n this.#browser = browser\n }\n\n protected override onWsOpen(): void {\n log.info('✓ Worker WebSocket connected to backend')\n }\n\n protected override onWsError(err: unknown): void {\n log.error(`Couldn't connect to devtools backend: ${errorMessage(err)}`)\n }\n\n protected override onWsClose(): void {\n log.info('Worker WebSocket disconnected')\n }\n\n async captureCommand(\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n meta: CommandCaptureMeta = {}\n ): Promise<boolean> {\n // Serialize error properly (Error objects don't JSON.stringify well)\n const serializedError = serializeError(error)\n\n const commandId = this.commandCounter++\n const commandLogEntry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n\n this.commandsLog.push(commandLogEntry)\n\n const isNavigationCommand = NAVIGATION_COMMANDS.some((cmd) =>\n command.toLowerCase().includes(cmd.toLowerCase())\n )\n if (isNavigationCommand && this.#browser && !error) {\n this.#capturePerformanceData(commandLogEntry, args).catch((err) => {\n log.warn(`Failed to capture performance data: ${errorMessage(err)}`)\n })\n }\n\n if (!error) {\n this.#queueActionSnapshot(command, commandLogEntry.timestamp)\n }\n\n return true\n }\n\n /** Fire-and-forget post-action snapshot, drained at finalize. Stamped with the\n * logged command's timestamp, not the capture's own: the capture resolves\n * after the command completes, and export claims snapshots by exact equality\n * with the command timestamp (FrameSnapshotIndex.claimAfter / elementsAt). */\n #queueActionSnapshot(command: string, timestamp: number): void {\n if (\n this.traceMode !== 'trace' ||\n !this.#browser ||\n !mapCommandToAction(command)\n ) {\n return\n }\n this.snapshotCaptures.push(\n captureActionSnapshot(\n this.#browser,\n command,\n timestamp,\n this.runner,\n this.isNativeAppSession\n ).then((snap) => {\n if (snap) {\n upsertRichestSnapshot(this.actionSnapshots, snap)\n }\n })\n )\n }\n\n async #capturePerformanceData(\n commandLogEntry: CommandLog & { _id?: number },\n args: unknown[]\n ) {\n // Page script, and the 500 ms settle below would be spent to reach a\n // document that does not exist.\n if (this.isNativeAppSession) {\n return\n }\n await new Promise((resolve) => setTimeout(resolve, 500))\n const raw = await this.#browser!.execute(CAPTURE_PERFORMANCE_SCRIPT)\n const payload = unwrapDriverValue<CapturedPerformancePayload | undefined>(\n raw\n )\n applyPerformanceData(\n commandLogEntry,\n payload,\n typeof args[0] === 'string' ? args[0] : undefined\n )\n }\n\n /**\n * Ingest a pre-built assertion entry (native `browser.assert`/`verify`\n * synthesized from Nightwatch's results, or a node:assert capture) into the\n * same command stream driver commands use. Unlike {@link captureCommand} this\n * preserves the entry's `title` (the human assertion message) and never\n * dedups — each call is a distinct action row. Assigns a fresh `_id` and a\n * matching stable public `id` (so a later `sendReplaceCommand` can update\n * this exact row in place — native asserts stream a pending row at call time,\n * then finalize their pass/fail), pushes, and broadcasts.\n */\n captureAssertCommand(entry: CommandLog): void {\n const withId = entry as CommandLog & { _id?: number }\n withId._id = this.commandCounter++\n withId.id = withId._id\n this.commandsLog.push(withId)\n // Deliberately no snapshot capture. These rows are emitted in a batch at\n // test-end but positioned back on their real execution window, so capturing\n // here would probe the page as it is NOW and stamp it at a moment seconds\n // earlier — an assert that ran on /secure rendered the /login page it later\n // logged out to. An assertion reads the page rather than changing it, so the\n // exporter lets it inherit the preceding action's capture instead\n // (FrameSnapshotIndex.claimAfter). The row's own `screenshot` is already\n // attached by the caller from the last resolved capture.\n this.sendCommand(withId)\n }\n\n /**\n * Replace an already-captured command entry (used for retried commands so\n * only the final execution result is shown in the UI).\n * Removes the old entry from commandsLog, revokes its sent-status so the\n * replacement can be sent, and returns the new entry together with the\n * old entry's timestamp (so the UI can locate and replace it in-place).\n */\n replaceCommand(\n oldId: number,\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n meta: CommandCaptureMeta = {}\n ): { entry: CommandLog & { _id?: number }; oldTimestamp: number } {\n // Remove the superseded entry and capture its timestamp for the UI\n const idx = this.commandsLog.findIndex(\n (c) => (c as CommandLog & { _id?: number })._id === oldId\n )\n const oldTimestamp: number =\n idx !== -1 ? (this.commandsLog[idx]?.timestamp ?? 0) : 0\n if (idx !== -1) {\n this.commandsLog.splice(idx, 1)\n }\n // Allow the slot to be re-used by a new entry\n this.sentCommandIds.delete(oldId)\n\n const serializedError = serializeError(error)\n const commandId = this.commandCounter++\n const entry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /** Birth time of the document this capturer last observed, so a replacement\n * is still visible to a poll that starts after the navigation committed. */\n private lastDocumentOrigin: number | undefined\n\n /** The current document's own birth time, which is what a DOM anchor is\n * stamped with. Undefined when the session cannot answer. */\n private async documentOrigin(\n browser: NightwatchBrowser\n ): Promise<number | undefined> {\n return webdriverExecute<number>(browser, 'return performance.timeOrigin;')\n .then((origin) => origin ?? undefined)\n .catch(() => undefined)\n }\n\n /**\n * Wait for a navigation to actually replace the document, then drain with a\n * forced anchor so the destination's DOM enters the stream.\n *\n * A Nightwatch click resolves BEFORE its navigation commits, so the drain that\n * runs at command completion still finds the outgoing page's collector: it\n * recovers nothing, and the destination is never anchored — every action on the\n * new page then replays the page it came from.\n *\n * The replacement signal is the document's own `performance.timeOrigin`\n * changing, NOT the collector going missing. Under the preload every document\n * instruments itself at document-start, so the collector is never absent and\n * that older signal stood permanently false — the destination was then only\n * ever anchored by a drain that happened to be slow enough to land after the\n * navigation, and tightening that latency lost the destination page entirely.\n * A same-URL re-navigation is a new document and a new origin, so this covers\n * it too; a command that navigated nowhere polls out and costs one drain of an\n * empty buffer.\n *\n * The baseline is the last origin THIS capturer saw, never one read on entry.\n * Reading it here would compare the document against itself: a command that\n * completes AFTER its navigation committed (`browser.url` waits for load, so\n * that is the normal case) would baseline the destination and then report no\n * change, dropping the very anchor this exists to take. With no baseline yet\n * a navigation cannot be ruled out either, so the first DOM-mutating command\n * of a run anchors unconditionally rather than guessing — `captureCurrentDom`\n * is idempotent per document, so an already-anchored page costs one drain.\n */\n async anchorAfterNavigation(browser: NightwatchBrowser): Promise<void> {\n // Polls the page for its own document identity, so there is nothing to\n // poll and nothing to anchor without one.\n if (this.isNativeAppSession) {\n return\n }\n const before = this.lastDocumentOrigin\n const replaced = await pollUntilReady(\n async () => {\n const now = await this.documentOrigin(browser)\n if (now === undefined) {\n return false\n }\n this.lastDocumentOrigin = now\n return before === undefined || now !== before\n },\n { attempts: 5, intervalMs: 150 }\n )\n if (!replaced) {\n return\n }\n // captureTrace's own recovery injects into the fresh document and re-drains,\n // and it gates that on the url being worth instrumenting — so a torn-down\n // session falls out here rather than logging an injection failure.\n //\n // The attribution is the newest command at THIS moment, not the one whose\n // completion started the poll: several polls overlap, so a field edit's poll\n // routinely observes the navigation the following click caused and would\n // credit itself — which pulled the anchor back before the edit's own row and\n // moved the wrong-DOM row one earlier instead of fixing it. Read at detection\n // time it is right for whichever poll gets there first.\n //\n // Unlike the inference in `anchorOwnerTimestamp`, this does not need the\n // \"nothing completed after the anchor\" guard: we just watched the document be\n // replaced, so a navigation demonstrably happened and the newest command is\n // the one that caused it. The guard exists for the case where that is a guess.\n await this.captureTrace(\n browser,\n true,\n this.commandsLog[this.commandsLog.length - 1]?.timestamp\n )\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly, bypassing\n * Nightwatch's command queue so the request can't be appended after\n * `end()` / `quit()`.\n */\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n return webdriverGet<string>(browser, 'screenshot')\n }\n\n protected override onSourceReadError(filePath: string, err: unknown): void {\n log.warn(`Failed to read source file ${filePath}: ${errorMessage(err)}`)\n }\n\n protected override onUpstreamDrop(\n event: string,\n reason: 'closed' | 'send-error',\n err?: unknown\n ): void {\n if (reason === 'send-error') {\n log.warn(`[upstream] Failed to send \"${event}\": ${errorMessage(err)}`)\n return\n }\n if (this.hasEverConnected()) {\n log.warn(`[upstream] WebSocket not open — dropping \"${event}\" event`)\n }\n }\n\n /**\n * Inject the WDIO devtools script into the browser page\n */\n async injectScript(browser: NightwatchBrowser) {\n if (this.isNativeAppSession) {\n return\n }\n try {\n // Injecting over a live collector replaces `window.wdioTraceCollector`\n // with a fresh instance and DISCARDS whatever it had buffered — including\n // the destination's full-DOM anchor. Several paths can reach the same\n // document (navigation proxy, drain recovery), so guard here once.\n const alreadyPresent = await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )\n if (alreadyPresent === true) {\n return\n }\n const scriptContent = await loadInjectableScript()\n // Inlined as a literal rather than passed via `arguments[0]`: the raw\n // /execute/sync transport takes its args separately and this keeps the\n // injection a single self-contained body.\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = ${JSON.stringify(scriptContent)};\n document.head.appendChild(script);\n return true;\n `\n await webdriverExecute(browser, injectionScript)\n\n const hasCollector = await pollUntilReady(\n async () =>\n (await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )) === true\n )\n\n if (hasCollector) {\n log.info('✓ Script injected and collector ready')\n } else {\n log.warn('Script injection may have failed — collector not found')\n }\n } catch (err) {\n log.error(`Failed to inject script: ${errorMessage(err)}`)\n throw err\n }\n }\n\n /**\n * Capture Chrome DevTools browser console logs via WebDriver log API.\n * Requires loggingPrefs: { browser: 'ALL' } in Chrome capabilities.\n */\n async captureBrowserLogs(browser: NightwatchBrowser) {\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('browser')\n const logs =\n unwrapDriverValue<\n Array<{ level: string; message: string; timestamp: number }>\n >(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const entries = mapChromeBrowserLogs(logs)\n this.consoleLogs.push(...entries)\n this.sendUpstream('consoleLogs', entries)\n } catch {\n // Browser log capture not available (loggingPrefs not set or not supported)\n }\n }\n\n /**\n * Parse Chrome performance logs to extract network request entries.\n * Requires loggingPrefs: { performance: 'ALL' } in Chrome capabilities.\n */\n async captureNetworkFromPerformanceLogs(browser: NightwatchBrowser) {\n // BiDi network inspector is the source of truth when attached.\n if (this.bidiActive || !this.perfLogsRequested) {\n return\n }\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('performance')\n const logs = unwrapDriverValue<PerfLogEntry[]>(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const networkEntries = parseNetworkFromPerfLogs(logs)\n if (networkEntries.length === 0) {\n return\n }\n\n const deduped = dedupeNetworkRequests(\n networkEntries,\n this.networkRequests as NetworkEntry[]\n )\n if (deduped.length > 0) {\n // A perf-log entry that never saw its response event carries no type;\n // default it to the vocabulary's residual at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'other'\n }))\n this.networkRequests.push(...normalized)\n this.sendUpstream('networkRequests', normalized)\n }\n } catch (err) {\n const msg = errorMessage(err) ?? ''\n // Silently skip when performance logging was not enabled in capabilities\n if (!msg.includes('log type') && !msg.includes('performance')) {\n log.warn(`Performance log capture failed: ${msg}`)\n }\n }\n }\n\n /** `forceAnchor` forces a full-DOM anchor of the current document before\n * draining, so a freshly injected collector's async anchor isn't lost.\n * `anchorTimestamp` is the command this drain's anchors were CAUSED by — pass\n * it only when the caller observed the document being replaced right after\n * that command, since it overrides the inference at ingest. */\n async captureTrace(\n browser: NightwatchBrowser,\n forceAnchor = false,\n anchorTimestamp?: number\n ) {\n // A native app has no document to drain. Inside the method, because four\n // call sites reach it — the command hook, the navigation proxy, the\n // cucumber pre-quit hook and finalize.\n if (this.isNativeAppSession) {\n return\n }\n // Performance logs only accumulate, so they lose nothing by waiting, while\n // the drain is racing the page it reads from (`drainOutgoingPage`).\n await this.drainCollector(browser, forceAnchor, anchorTimestamp)\n await this.captureNetworkFromPerformanceLogs(browser)\n }\n\n private async drainCollector(\n browser: NightwatchBrowser,\n forceAnchor: boolean,\n anchorTimestamp?: number\n ) {\n // Try the injected wdioTraceCollector script for XHR/fetch and mutations.\n // Atomic check+read — the inline `typeof === 'undefined' → null` guard is\n // the only safe form; a separate existence check would race page navigation\n // (the collector can disappear between the two round-trips).\n try {\n const traceData = await drainCollectorWithRecovery({\n drain: () =>\n webdriverExecute<Record<string, unknown> | null>(\n browser,\n collectorDrainExpression(forceAnchor)\n ),\n injectIntoCurrentDocument: () => this.injectScript(browser),\n currentUrl: () =>\n webdriverExecute<string>(browser, 'return location.href;').then(\n (u) => u ?? undefined\n ),\n log: (level, message) => log[level](message)\n })\n\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData, { anchorTimestamp })\n const mutationCount = Array.isArray(\n (traceData as { mutations?: unknown }).mutations\n )\n ? (traceData as { mutations: unknown[] }).mutations.length\n : 0\n if (mutationCount > 0) {\n log.info(`[trace] Captured ${mutationCount} DOM mutation(s)`)\n }\n } catch (err) {\n log.error(\n `Failed to capture trace from injected script: ${errorMessage(err)}`\n )\n }\n }\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n// Every probe goes over the raw WebDriver HTTP transport rather than `browser.*`:\n// those are QUEUED commands, so a probe issued from inside the plugin's own\n// command hook enqueues behind the command still running and can't resolve until\n// the queue drains. `browser.getCurrentUrl()`/`getTitle()` that way left every\n// snapshot with `url: undefined` (so `frameUrl` fell back to `about:blank`) and\n// hung the capture outright; the queued `execute` merely timed out, leaving an\n// empty a11y tree and no element rects.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type { NightwatchBrowser, TestRunnerId } from './types.js'\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n timestamp?: number,\n runner?: TestRunnerId,\n native = false\n): Promise<ActionSnapshot | null> {\n // The screenshot is the only one of these a native app can serve. The rest\n // are page reads — an injected script plus url and title — and they fire on\n // EVERY action, so they are the densest source of round trips that can only\n // fail on a session with no document.\n return coreCapture({\n command,\n timestamp,\n runner,\n takeScreenshot: () => webdriverGet<string>(browser, 'screenshot'),\n ...(native\n ? {}\n : {\n runScript: (src: string) =>\n webdriverExecute(browser, `return (${src})`),\n getUrl: () => webdriverGet<string>(browser, 'url'),\n getTitle: () => webdriverGet<string>(browser, 'title')\n })\n })\n}\n","import type { RequestType } from '@wdio/devtools-shared'\n\nimport { getRequestType } from './utils.js'\n\n/**\n * Pure parsers for Chrome's `performance` log (the format `browser.getLog('performance')`\n * returns). Separated from the SessionCapturer so they're testable and the\n * capture method stays focused on state + I/O.\n */\n\nexport interface PerfLogEntry {\n level: string\n message: string\n timestamp: number\n}\n\nexport interface NetworkEntry {\n id: string\n url: string\n method: string\n requestHeaders: Record<string, string>\n timestamp: number\n startTime: number\n status?: number\n statusText?: string\n responseHeaders?: Record<string, string>\n mimeType?: string\n /** Set only by `getRequestType`, so it carries the shared vocabulary; absent\n * until the response event arrives. */\n type?: RequestType\n size?: number\n endTime?: number\n time?: number\n error?: string\n}\n\n/**\n * Parse CDP `Network.*` events out of Chrome performance log entries into a\n * flat array of network entries. Builds up a per-requestId pending map as it\n * sees `requestWillBeSent` → `responseReceived` → `loadingFinished` events,\n * and emits the completed entry on the terminal event.\n */\ninterface CdpResponse {\n status?: number\n statusText?: string\n headers?: Record<string, unknown>\n mimeType?: string\n}\n\ninterface CdpRequest {\n url: string\n method: string\n headers?: Record<string, string>\n}\n\ninterface CdpEventParams {\n requestId: string\n request?: CdpRequest\n response?: CdpResponse\n timestamp?: number\n encodedDataLength?: number\n errorText?: string\n}\n\nfunction applyResponseReceived(p: NetworkEntry, response: CdpResponse): void {\n const responseHeaders: Record<string, string> = {}\n for (const [k, v] of Object.entries(response.headers || {})) {\n responseHeaders[k.toLowerCase()] = String(v)\n }\n p.status = response.status\n p.statusText = response.statusText\n p.responseHeaders = responseHeaders\n p.mimeType = response.mimeType\n p.type = getRequestType(p.url, response.mimeType)\n}\n\n/** CDP event time in ms. Uses the event's own monotonic `params.timestamp`\n * (what Chrome DevTools times requests with) rather than the perf-log wrapper\n * timestamp, which WebDriver stamps per batch — so request/response diffs in\n * the same batch would otherwise be 0. */\nfunction eventTimeMs(params: CdpEventParams, entry: PerfLogEntry): number {\n return typeof params.timestamp === 'number'\n ? Math.round(params.timestamp * 1000)\n : entry.timestamp\n}\n\nfunction startPendingRequest(\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>\n): void {\n const { requestId, request: req, timestamp } = params\n if (!req || typeof timestamp !== 'number') {\n return\n }\n const startTime = eventTimeMs(params, entry)\n pending.set(requestId, {\n id: `${entry.timestamp}-${requestId}`,\n url: req.url,\n method: req.method,\n requestHeaders: req.headers ?? {},\n timestamp: startTime,\n startTime\n })\n}\n\nfunction handlePerfLogEvent(\n method: string,\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>,\n completed: NetworkEntry[]\n): void {\n if (method === 'Network.requestWillBeSent') {\n startPendingRequest(params, entry, pending)\n return\n }\n if (method === 'Network.responseReceived') {\n const p = pending.get(params.requestId)\n if (p && params.response) {\n applyResponseReceived(p, params.response)\n }\n return\n }\n if (method === 'Network.loadingFinished') {\n const p = pending.get(params.requestId)\n if (p && p.status !== undefined) {\n p.size = params.encodedDataLength\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n return\n }\n if (method === 'Network.loadingFailed') {\n const p = pending.get(params.requestId)\n if (p) {\n p.error = params.errorText\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n }\n}\n\nexport function parseNetworkFromPerfLogs(logs: PerfLogEntry[]): NetworkEntry[] {\n const pending = new Map<string, NetworkEntry>()\n const completed: NetworkEntry[] = []\n for (const entry of logs) {\n let parsed: unknown\n try {\n parsed = JSON.parse(entry.message)\n } catch {\n continue\n }\n type ParsedShape = {\n message?: { method?: string; params?: CdpEventParams }\n }\n const message = (parsed as ParsedShape | undefined)?.message\n const method = message?.method\n const params = message?.params\n if (!method || !params) {\n continue\n }\n handlePerfLogEvent(method, params, entry, pending, completed)\n }\n return completed\n}\n\n/**\n * Dedupe incoming network entries against ones the session already holds.\n * Successful requests dedupe by (method, url, timestamp). Failed requests\n * collapse by (method, origin, pathname) — parallel autocomplete/prefetch\n * requests to the same path (e.g. `/search?q=W`, `/search?q=We`) otherwise\n * spam the network panel.\n */\nexport function dedupeNetworkRequests(\n incoming: NetworkEntry[],\n existing: NetworkEntry[]\n): NetworkEntry[] {\n const failedKey = (entry: NetworkEntry): string => {\n try {\n const u = new URL(entry.url)\n return `err:${entry.method}:${u.origin}${u.pathname}`\n } catch {\n return `err:${entry.method}:${entry.url}`\n }\n }\n\n const alreadySeen = new Set(\n existing.map((r) =>\n r.error !== undefined\n ? failedKey(r)\n : `ok:${r.method}:${r.url}:${r.timestamp}`\n )\n )\n\n const deduped: NetworkEntry[] = []\n const seenFailedInBatch = new Map<string, number>()\n\n for (const entry of incoming) {\n if (entry.error !== undefined) {\n const key = failedKey(entry)\n if (alreadySeen.has(key)) {\n continue\n }\n const existingIdx = seenFailedInBatch.get(key)\n if (existingIdx !== undefined) {\n deduped[existingIdx] = entry // replace with latest failure\n } else {\n seenFailedInBatch.set(key, deduped.length)\n deduped.push(entry)\n }\n } else {\n const key = `ok:${entry.method}:${entry.url}:${entry.timestamp}`\n if (!alreadySeen.has(key)) {\n deduped.push(entry)\n }\n }\n }\n\n return deduped\n}\n","import logger from '@wdio/logger'\nimport { TestReporterBase, type ReporterUpstream } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { DEFAULTS } from './constants.js'\nimport { extractTestMetadata, generateStableUid } from './helpers/utils.js'\nimport type { SuiteStats, TestStats } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:Reporter')\n\nexport class TestReporter extends TestReporterBase {\n #currentSpecFile?: string\n #testNamesCache = new Map<string, string[]>()\n #currentSuite?: SuiteStats\n #attemptFor: (uid: string) => number | undefined\n\n constructor(\n report: ReporterUpstream,\n attemptFor: (uid: string) => number | undefined\n ) {\n super(report)\n this.#attemptFor = attemptFor\n }\n\n onSuiteStart(suiteStats: SuiteStats) {\n this.#currentSpecFile = suiteStats.file\n this.#currentSuite = suiteStats\n const rerunLabel =\n process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n\n if (!suiteStats.uid) {\n suiteStats.uid = generateStableUid(suiteStats)\n }\n\n if (\n this.#currentSpecFile &&\n !this.#testNamesCache.has(this.#currentSpecFile)\n ) {\n const metadata = extractTestMetadata(this.#currentSpecFile)\n const testNames = rerunLabel\n ? metadata.testNames.filter((name) => name === rerunLabel)\n : metadata.testNames\n if (testNames.length > 0) {\n this.#testNamesCache.set(this.#currentSpecFile, testNames)\n log.info(\n `📝 Extracted ${testNames.length} test names from ${this.#currentSpecFile}`\n )\n }\n }\n\n this.allSuites.push(suiteStats)\n this.sendUpstream()\n }\n\n override clearExecutionData() {\n super.clearExecutionData()\n this.#testNamesCache.clear()\n this.#currentSuite = undefined\n this.#currentSpecFile = undefined\n }\n\n getCurrentSuite(): SuiteStats | undefined {\n return this.#currentSuite\n }\n\n /** Find by title within parent suite — Nightwatch retries reuse the title slot. */\n onTestStart(testStats: TestStats) {\n if (!testStats.uid || testStats.uid.includes('temp-')) {\n testStats.uid = generateStableUid(testStats)\n }\n\n for (const suite of this.allSuites) {\n const testIndex = suite.tests.findIndex((t) => {\n if (typeof t === 'string') {\n return false\n }\n return t.title === testStats.title && t.parent === suite.uid\n })\n if (testIndex !== -1) {\n suite.tests[testIndex] = testStats\n this.sendUpstream()\n return\n }\n }\n\n if (this.#currentSuite) {\n this.#currentSuite.tests.push(testStats)\n }\n this.sendUpstream()\n }\n\n /** Synthesize `skipped` entries for tests that never executed. */\n override onSuiteEnd(suiteStats: SuiteStats) {\n const cachedNames = this.#testNamesCache.get(suiteStats.file) || []\n const processedTestNames = new Set(\n suiteStats.tests\n .map((t) => (typeof t === 'string' ? t : t.title))\n .filter((title): title is string => Boolean(title))\n )\n\n cachedNames.forEach((testName) => {\n if (!processedTestNames.has(testName)) {\n const uid = generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats)\n const skippedTest: TestStats = {\n uid,\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle: `${suiteStats.title} ${testName}`,\n parent: suiteStats.uid,\n state: 'skipped',\n start: new Date(),\n end: new Date(),\n type: 'test',\n file: suiteStats.file,\n retries: this.#attemptFor(uid) ?? DEFAULTS.RETRIES,\n _duration: DEFAULTS.DURATION,\n hooks: []\n }\n suiteStats.tests.push(skippedTest)\n log.info(`Created skipped test \"${testName}\" (never executed)`)\n }\n })\n\n this.sendUpstream()\n }\n\n /** Replace a suite when its UID changes mid-run (after spec rescan). */\n updateSuite(suiteStats: SuiteStats) {\n const index = this.allSuites.findIndex((s) => s.file === suiteStats.file)\n if (index !== -1) {\n this.allSuites.splice(index, 1)\n }\n this.allSuites.push(suiteStats)\n this.#currentSuite = suiteStats\n this.sendUpstream()\n }\n}\n","/**\n * Which `it` the runner is executing RIGHT NOW.\n *\n * The plugin's `beforeEach` fires once per MODULE on the BDD `describe/it`\n * interface, so the lifecycle-level current test is the module's first `it` for\n * every row in the file. Nightwatch's own `setReporterCurrentTest()` — \"called\n * for every test case (not for hooks)\" — runs per `it` and installs\n * `browser.currentTest` as a live getter over `reporter.currentTest`, so the\n * running testcase's NAME is readable as state on every command. That name is\n * the per-`it` signal no hook or event exposes; mapping it back onto the\n * `TestStats` the suite already registered gives each `it` its own uid, and with\n * it its own group in the trace's action tree.\n */\n\nimport type { NightwatchCurrentTest, SuiteStats, TestStats } from '../types.js'\n\n/**\n * The registered test title a runtime testcase name refers to. Nightwatch\n * reports either the bare `it` title or one prefixed with its parents, so a\n * whole-title suffix match is accepted; anything else is no match — this never\n * guesses at \"the next unprocessed test\" the way the lifecycle picker does.\n */\nexport function matchTestName(\n runtimeName: string | undefined,\n testNames: readonly string[]\n): string | undefined {\n const name = typeof runtimeName === 'string' ? runtimeName.trim() : undefined\n if (!name) {\n return undefined\n }\n return testNames.find((title) => name === title || name.endsWith(` ${title}`))\n}\n\n/**\n * The suite's registered `TestStats` for the testcase Nightwatch reports as\n * running. Undefined when nothing matches: a command issued from a hook (where\n * Nightwatch leaves the previous testcase in place), a spec whose source\n * couldn't be parsed into test names, or the cucumber runner — whose client\n * carries no `currentTest` at all, which is what keeps its per-scenario\n * structure on the lifecycle path untouched.\n */\nexport function findRunningTest(\n suite: SuiteStats | undefined,\n currentTest: NightwatchCurrentTest | undefined\n): TestStats | undefined {\n const tests = (suite?.tests ?? []).filter(\n (t): t is TestStats => typeof t !== 'string'\n )\n const title = matchTestName(\n currentTest?.name,\n tests.map((t) => t.title)\n )\n return title ? tests.find((t) => t.title === title) : undefined\n}\n","import { stampTestEnd } from '@wdio/devtools-core'\nimport { TEST_STATE, DEFAULTS } from '../constants.js'\nimport {\n type TestStats,\n type SuiteStats,\n type NightwatchCurrentTest,\n type NightwatchTestCase\n} from '../types.js'\nimport { determineTestState } from './utils.js'\nimport { findRunningTest } from './runningTest.js'\nimport type { TestReporter } from '../reporter.js'\n\nexport class TestManager {\n private processedTests = new Map<string, Set<string>>()\n private lastKnownTestName: string | null = null\n\n constructor(\n private testReporter: TestReporter,\n /** Stamps a test's resolved terminal state onto the retry ledger. */\n private recordOutcome?: (uid: string, state: TestStats['state']) => void\n ) {}\n\n /**\n * Update test state and report to UI\n */\n updateTestState(\n test: TestStats,\n state: TestStats['state'],\n endTime?: Date,\n duration?: number\n ): void {\n test.state = state\n if (endTime) {\n test.end = endTime\n }\n if (duration !== undefined) {\n test._duration = duration\n }\n\n if (state === TEST_STATE.PASSED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.FAILED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.RUNNING) {\n this.testReporter.onTestStart(test)\n }\n\n if (state !== TEST_STATE.RUNNING) {\n this.testReporter.onTestEnd(test)\n this.recordOutcome?.(test.uid, state)\n }\n }\n\n /**\n * Find test in suite by title\n */\n findTestInSuite(suite: SuiteStats, testTitle: string): TestStats | undefined {\n return suite.tests.find(\n (t): t is TestStats => typeof t !== 'string' && t.title === testTitle\n )\n }\n\n /**\n * Mark test as processed to avoid duplicate reporting\n */\n markTestAsProcessed(testFile: string, testTitle: string): void {\n if (!this.processedTests.has(testFile)) {\n this.processedTests.set(testFile, new Set())\n }\n this.processedTests.get(testFile)!.add(testTitle)\n }\n\n /**\n * Check if test has been processed\n */\n isTestProcessed(testFile: string, testTitle: string): boolean {\n return this.processedTests.get(testFile)?.has(testTitle) ?? false\n }\n\n /**\n * Get processed tests for a file\n */\n getProcessedTests(testFile: string): Set<string> {\n return this.processedTests.get(testFile) || new Set()\n }\n\n /**\n * Detect test boundary and finalize previous test if needed\n * Returns the current test name\n */\n detectTestBoundary(currentNightwatchTest: NightwatchCurrentTest): string {\n const currentTestName = currentNightwatchTest?.name || DEFAULTS.TEST_NAME\n\n // If test name changed, finalize previous test\n if (\n this.lastKnownTestName &&\n currentTestName !== this.lastKnownTestName &&\n currentTestName !== DEFAULTS.TEST_NAME\n ) {\n const testFile =\n currentNightwatchTest.module?.split('/').pop() || DEFAULTS.FILE_NAME\n const currentSuite = this.testReporter.getCurrentSuite()\n\n if (currentSuite) {\n const prevTest = this.findTestInSuite(\n currentSuite,\n this.lastKnownTestName\n )\n if (prevTest && prevTest.state === TEST_STATE.RUNNING) {\n const prevTestCase =\n currentNightwatchTest.results?.testcases?.[this.lastKnownTestName]\n\n if (prevTestCase) {\n const testState = determineTestState(prevTestCase)\n this.updateTestState(\n prevTest,\n testState,\n new Date(),\n parseFloat(prevTestCase.time || '0') * 1000\n )\n this.markTestAsProcessed(testFile, this.lastKnownTestName)\n }\n }\n }\n }\n\n // Update last known test name\n if (currentTestName !== DEFAULTS.TEST_NAME) {\n this.lastKnownTestName = currentTestName\n }\n\n return currentTestName\n }\n\n /**\n * The registered test for the testcase Nightwatch reports as running, read\n * from live `browser.currentTest` state rather than from a hook — the only\n * per-`it` signal the BDD interface gives a plugin (see ./runningTest.ts).\n * Synchronous by construction: the command path resolves a row's test on the\n * invocation stack, where an await would reorder capture.\n */\n runningTest(\n currentTest: NightwatchCurrentTest | undefined\n ): TestStats | undefined {\n return findRunningTest(this.testReporter.getCurrentSuite(), currentTest)\n }\n\n /**\n * Start a pending test if this is its first command\n */\n startTestIfPending(currentTestName: string): void {\n if (\n currentTestName === DEFAULTS.TEST_NAME ||\n this.lastKnownTestName !== currentTestName\n ) {\n return\n }\n\n const currentSuite = this.testReporter.getCurrentSuite()\n if (!currentSuite) {\n return\n }\n\n const test = this.findTestInSuite(currentSuite, currentTestName)\n if (test && test.state === TEST_STATE.PENDING) {\n test.start = new Date()\n test.end = null\n this.updateTestState(test, TEST_STATE.RUNNING as TestStats['state'])\n }\n }\n\n /**\n * Finalize all incomplete tests in a suite\n */\n finalizeSuiteTests(\n suite: SuiteStats,\n testcases: Record<string, NightwatchTestCase>\n ): void {\n suite.tests.forEach((entry) => {\n if (typeof entry === 'string') {\n return\n }\n const test = entry\n if (test.state === TEST_STATE.RUNNING && test.start) {\n // Test was started but never finished - assume passed\n test.state = TEST_STATE.PASSED\n stampTestEnd(test)\n this.updateTestState(test, TEST_STATE.PASSED as TestStats['state'])\n } else if (test.state === TEST_STATE.PENDING) {\n const testcase = testcases[test.title]\n if (testcase) {\n const testState = determineTestState(testcase)\n test.start = test.start || new Date()\n this.updateTestState(\n test,\n testState,\n new Date(),\n parseFloat(testcase.time || '0') * 1000\n )\n } else {\n // Test never ran - mark as skipped\n this.updateTestState(\n test,\n TEST_STATE.SKIPPED as TestStats['state'],\n new Date(),\n 0\n )\n }\n }\n })\n }\n\n /**\n * Reset internal state for new test files\n */\n reset(): void {\n this.lastKnownTestName = null\n }\n}\n","/**\n * Suite Manager — Nightwatch flavor.\n * Maintains one suite per test file (Nightwatch runs each file independently).\n * Shares the suite factory + state-computation logic with selenium via\n * @wdio/devtools-core's suite-helpers; the storage strategy (Map by file)\n * is Nightwatch-specific and stays here.\n */\n\nimport {\n computeSuiteFinalStateStrict,\n computeSuiteRunningState,\n createSuiteStats,\n createTestStats,\n stampSuiteEnd\n} from '@wdio/devtools-core'\nimport { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\nimport type { TestReporter } from '../reporter.js'\nimport { generateStableUid } from './utils.js'\n\nexport class SuiteManager {\n private currentSuiteByFile = new Map<string, SuiteStats>()\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Clear execution data when a rerun starts.\n * Resets all cached suites so they're recreated fresh during the new run.\n */\n clearExecutionData() {\n this.currentSuiteByFile.clear()\n }\n\n /**\n * Get or create suite for a test file\n */\n getOrCreateSuite(\n testFile: string,\n suiteTitle: string,\n fullPath: string | null,\n testNames: string[],\n suiteLine?: number | null,\n testLines?: number[]\n ): SuiteStats {\n if (!this.currentSuiteByFile.has(testFile)) {\n const file = fullPath || testFile\n const suiteStats = createSuiteStats({\n uid: generateStableUid(file, suiteTitle),\n cid: DEFAULTS.CID,\n title: suiteTitle,\n file,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n suiteLine && fullPath ? `${fullPath}:${suiteLine}` : undefined\n })\n\n // Create test entries with pending state\n for (let idx = 0; idx < testNames.length; idx++) {\n const testName = testNames[idx]\n const testLine = testLines?.[idx]\n const fullTitle = `${suiteTitle} ${testName}`\n const testEntry = createTestStats({\n uid: generateStableUid(file, fullTitle),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle,\n file,\n parent: suiteStats.uid,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n testLine && fullPath ? `${fullPath}:${testLine}` : undefined\n })\n suiteStats.tests.push(testEntry)\n }\n\n this.currentSuiteByFile.set(testFile, suiteStats)\n this.testReporter.onSuiteStart(suiteStats)\n }\n\n return this.currentSuiteByFile.get(testFile)!\n }\n\n /**\n * Get suite for a test file\n */\n getSuite(testFile: string): SuiteStats | undefined {\n return this.currentSuiteByFile.get(testFile)\n }\n\n /**\n * Mark suite as running.\n * Clears `end` so that the `finalizeSuite` guard (`if (suite.end) return`)\n * does not skip re-finalization during a rerun.\n */\n markSuiteAsRunning(suite: SuiteStats): void {\n suite.state = TEST_STATE.RUNNING\n suite.end = null\n this.testReporter.updateSuites()\n }\n\n /**\n * Update suite state from current children without marking it as ended.\n * Used during Cucumber runs to keep the feature-level suite state fresh.\n */\n finalizeSuiteState(suite: SuiteStats): void {\n suite.state = computeSuiteRunningState(suite)\n this.testReporter.updateSuites()\n }\n\n /**\n * Finalize suite with test results\n */\n finalizeSuite(suite: SuiteStats): void {\n if (suite.end) {\n return\n }\n stampSuiteEnd(suite)\n suite.state = computeSuiteFinalStateStrict(suite)\n this.testReporter.onSuiteEnd(suite)\n }\n\n /**\n * Get all suites\n */\n getAllSuites(): Map<string, SuiteStats> {\n return this.currentSuiteByFile\n }\n}\n","/**\n * Browser Proxy\n * Handles browser command interception and tracking\n */\n\nimport logger from '@wdio/logger'\nimport {\n INTERNAL_COMMANDS_TO_IGNORE,\n NAVIGATION_COMMANDS\n} from '../constants.js'\nimport { callbackError } from './callbackError.js'\nimport { commandRowSelector, commandTargetSelector } from './assertTarget.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { NativeAssertRecorder } from './nativeAssertRecorder.js'\nimport {\n RetryTracker,\n beginInputDispatch,\n rememberReadValue,\n toError\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandCaptureMeta,\n CommandInvocation,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchChainable,\n NightwatchCurrentTest,\n CommandStackFrame\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:browserProxy')\n\nexport class BrowserProxy {\n /** Tracks which browser *instances* have already been proxied to avoid double-wrapping. */\n private proxiedBrowsers = new WeakSet<object>()\n private commandStack: CommandStackFrame[] = []\n private lastCommandSig: string | null = null\n private currentTestFullPath: string | null = null\n /**\n * Tracks the last captured command so that consecutive retries of the same\n * command (e.g. getText inside a waitFor loop) overwrite the previous entry\n * rather than appending, showing only the final execution result.\n */\n private retryTracker = new RetryTracker()\n\n /**\n * Monotonic issue-order counter. Stamped when the test ISSUES a row — a\n * driver command at invocation, an assert at enqueue — which is source order.\n * Both are recorded into `commandsLog` much later and out of that order (a\n * driver command at completion, an assert in the test-end batch), so this is\n * what lets the exporter recover the order the test wrote. Shared with the\n * assert recorder so both row kinds draw from one sequence.\n */\n private issueCounter = 0\n\n private assertRecorder: NativeAssertRecorder\n\n /**\n * The browser every captured row is issued on. Nightwatch mutates ONE browser\n * object in place — even across a mid-run session replacement — so this stays\n * valid for the whole run, and it is how `browser.currentTest` is read from\n * the assert path, which runs outside any command callback.\n */\n private browser: NightwatchBrowser | null = null\n\n constructor(\n private readonly sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null,\n private captureAssertions = true,\n private observeSession?: (browser: NightwatchBrowser) => void\n ) {\n this.assertRecorder = new NativeAssertRecorder(\n sessionCapturer,\n () => this.rowTest(),\n () => this.issueCounter++\n )\n }\n\n /**\n * The test a captured row belongs to: the `it` Nightwatch reports as running\n * right now, falling back to the lifecycle test. On the BDD `describe/it`\n * interface the lifecycle test is the module's FIRST `it` for every row in the\n * file (its `beforeEach` fires once per module), so without the live read\n * every command in the module carried one uid and the trace's action tree\n * collapsed to a single test group. The fallback is what the runners with no\n * per-testcase state keep using — cucumber has no `currentTest` at all.\n */\n private rowTest(): { uid?: string } | null {\n return (\n this.testManager.runningTest(this.browser?.currentTest) ??\n this.getCurrentTest()\n )\n }\n\n /** The uid rows are stamped with — see {@link rowTest}. Public so the\n * node:assert capture wiring, which has no command of its own, tags its rows\n * onto the same test the surrounding commands landed on. */\n rowTestUid(): string | undefined {\n return this.rowTest()?.uid\n }\n\n /** Per-command-scope dedup state only. Cucumber calls this per STEP, so it\n * must not touch the native-assert buffer — that buffer spans the whole\n * scenario and is drained once, at the scenario's pre-quit hook. */\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n /** Everything scoped to one test unit: dedup state plus any native-assert\n * calls a previous unit left behind because its drain never ran. */\n resetTestTracking(): void {\n this.resetCommandTracking()\n this.assertRecorder.clear()\n }\n\n /** Hand off this test's recorded native-assertion calls and clear the\n * buffer so the next test starts fresh. */\n drainNativeAssertCalls(): NativeAssertCall[] {\n return this.assertRecorder.drain()\n }\n\n getCurrentTestFullPath(): string | null {\n return this.currentTestFullPath\n }\n\n setCurrentTestFullPath(path: string | null): void {\n this.currentTestFullPath = path\n }\n\n /**\n * Wrap browser navigation methods (url / navigate / navigateTo) to inject\n * the DevTools script after every navigation.\n *\n * Uses `browser` from the closure (not `this` inside perform) so it works\n * for both standard Nightwatch (chainable API) and Cucumber async/await mode\n * where `this` inside a perform callback is not the browser.\n */\n wrapUrlMethod(browser: NightwatchBrowser): void {\n const sessionCapturer = this.sessionCapturer\n\n // Cast once for dynamic method access — Nightwatch's typed surface\n // doesn't enumerate every command, but they all live on the same object.\n // Return type is `unknown` because wrapNav has to handle both\n // Nightwatch's chainable API (returns a chainable with `.perform`) and\n // Cucumber async/await (returns a Promise) — we narrow at each branch.\n const b = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n\n const wrapNav = (methodName: string) => {\n if (typeof b[methodName] !== 'function') {\n return\n }\n const original = b[methodName].bind(browser)\n\n b[methodName] = function (...args: unknown[]) {\n // `browser.url()` is BOTH a navigation and a getter: with no url it just\n // reads the current one, which is how `assert.urlContains` reads it from\n // inside Nightwatch's own queue. Treating that read as a navigation\n // re-injected the collector and forced an anchored drain mid-test — real\n // round trips against the page for a command that changed nothing — and\n // logged the capture callback's whole function body as the \"url\".\n if (typeof args[0] !== 'string') {\n return original(...args)\n }\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return (\n sessionCapturer\n .injectScript(browser)\n // Anchored: the just-injected collector anchors the document\n // asynchronously, so an unanchored drain beats it and this page's\n // DOM never reaches the trace.\n .then(() => sessionCapturer.captureTrace(browser, true))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n )\n }\n\n const chainable = result as NightwatchChainable | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API). Hung off the command's OWN\n // thenable, not a queued `perform()`: a queued callback is discarded\n // when a failing test aborts the rest of the queue, so the destination\n // of a navigation in a failing test was never instrumented at all\n // (measured: 2 of 3 navigations skipped, and with them the first\n // document's entire DOM). The chainable is itself thenable, and\n // resolving it means the navigation finished — the same signal\n // `perform` gave, minus the dependency on the queue surviving.\n //\n // `result` is returned unchanged so `browser.url(…).waitFor…()`\n // chaining still works; the injection runs as a side effect.\n void Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n return result\n }\n // Cucumber async/await: result is a Promise (or thenable).\n // Return the AUGMENTED promise so that `await browser.url(...)` in\n // the step definition waits for injectAndCapture to finish before\n // Cucumber moves to the next step. Without this the injection races\n // with the next step's commands (e.g. setValue), causing stale-element\n // errors because the script-tag insertion mutates the DOM mid-form.\n return Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n }\n }\n\n wrapNav('url')\n wrapNav('navigate')\n wrapNav('navigateTo')\n\n log.info('✓ Script injection wrapped')\n }\n\n wrapBrowserCommands(browser: NightwatchBrowser): void {\n // Held before the already-wrapped guard: the reference is what `rowTest`\n // reads `currentTest` off, and a re-entry is exactly a run that never\n // re-wraps.\n this.browser = browser\n if (this.proxiedBrowsers.has(browser as object)) {\n return\n }\n\n // Single widening: Nightwatch's `browser` is a dynamic command bag —\n // every wrapped lookup below is property-name → function. Casting once\n // keeps the wrap loop readable.\n const browserAny = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const allMethods = new Set([\n ...Object.keys(browser),\n ...Object.getOwnPropertyNames(Object.getPrototypeOf(browser))\n ])\n const wrappedMethods: string[] = []\n\n allMethods.forEach((methodName) => {\n if (\n methodName === 'constructor' ||\n typeof browserAny[methodName] !== 'function'\n ) {\n return\n }\n\n if (\n (INTERNAL_COMMANDS_TO_IGNORE as readonly string[]).includes(\n methodName\n ) ||\n methodName.startsWith('__')\n ) {\n return\n }\n\n const originalMethod = browserAny[methodName].bind(browser)\n\n browserAny[methodName] = (...args: unknown[]) => {\n return this.handleCommandExecution(\n browser,\n browserAny,\n methodName,\n originalMethod,\n args\n )\n }\n\n wrappedMethods.push(methodName)\n })\n\n // captureAssertions:false → leave assert/verify original so no native\n // assertion rows are recorded (the finalize paths are gated to match).\n if (this.captureAssertions) {\n this.assertRecorder.wrapNamespaces(browser)\n }\n this.proxiedBrowsers.add(browser as object)\n log.info(`✓ Wrapped ${wrappedMethods.length} browser methods`)\n }\n\n private handleRetryReplacement(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n meta: CommandCaptureMeta,\n commandError: Error | undefined\n ): void {\n // Same command fired again (internal retry) — replace the previous\n // entry so only the final result appears in the UI.\n const { entry, oldTimestamp } = this.sessionCapturer.replaceCommand(\n this.retryTracker.lastId!,\n methodName,\n logArgs,\n serializedResult,\n commandError,\n meta\n )\n this.retryTracker.setLastId(entry._id ?? null)\n this.sessionCapturer.sendReplaceCommand(oldTimestamp, entry)\n this.attachScreenshot(browser, entry, methodName, ' (retry)')\n }\n\n private captureFreshCommand(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n meta: CommandCaptureMeta,\n cmdSig: string,\n commandError: Error | undefined\n ): void {\n // captureCommand() pushes the entry to commandsLog synchronously before\n // any async work (navigation perf capture), so we can grab the ID\n // immediately after the call — before any microtask fires. This avoids\n // the race where a Nightwatch retry callback executes before .then() sets\n // lastId, causing missed dedup. Stage the sig now, set the id after the\n // synchronous push lands.\n this.retryTracker.setLastSig(cmdSig)\n this.retryTracker.setLastId(null)\n // Issued first, and only this one — see `drainOutgoingPage`.\n this.drainOutgoingPage(browser, methodName)\n this.sessionCapturer\n .captureCommand(methodName, logArgs, serializedResult, commandError, meta)\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.retryTracker.setLastId((lastCommand as { _id?: number })._id ?? null)\n this.sessionCapturer.sendCommand(lastCommand)\n log.info(`[command] ${methodName}`)\n this.attachScreenshot(browser, lastCommand, methodName)\n }\n this.anchorDestination(browser, methodName)\n }\n\n private isDomMutating(methodName: string): boolean {\n return (\n (NAVIGATION_COMMANDS as readonly string[]).includes(methodName) ||\n [\n 'click',\n 'doubleClick',\n 'rightClick',\n 'setValue',\n 'clearValue',\n 'sendKeys',\n 'submitForm',\n 'back',\n 'forward',\n 'refresh'\n ].includes(methodName)\n )\n }\n\n /**\n * Drains the OUTGOING page's field edits, which die with the document the\n * moment a submit navigates. It goes over the raw HTTP transport, so it\n * touches no Nightwatch queue, and a driver serialises requests per session —\n * so it is served before the next queued command only for as long as it stays\n * the FIRST request this class issues when a command completes. That call\n * position, and the drain-before-perf-logs order inside `captureTrace`, are\n * both load-bearing: behind the snapshot probes and the screenshot it needed\n * about 130 ms of head start and got 18 ms, and the last field a test filled\n * before submitting replayed as an empty box.\n *\n * It deliberately carries only this one request. `anchorAfterNavigation` is\n * issued later, from `anchorDestination`, so the row's own screenshot and DOM\n * snapshot are not pushed behind two round trips.\n */\n private drainOutgoingPage(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n this.sessionCapturer.captureTrace(browser, true).catch(() => {})\n }\n\n /**\n * Anchors the DESTINATION, and it has to wait: a Nightwatch click resolves\n * before its navigation commits, so the outgoing drain still finds the old\n * page's collector, recovers nothing, and the new document is never anchored —\n * every action on it then replayed the page it came from (measured: 5 of 20\n * rows). `anchorAfterNavigation` keys off the document actually being replaced\n * rather than off which command ran, so it covers any route to a new document —\n * a submit click, a JS redirect, a same-command re-navigation — without a list\n * of commands to keep in sync.\n */\n private anchorDestination(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n // Tracked so finalize awaits it — an untracked anchor drain can still be in\n // flight when the trace is written, which loses the document entirely.\n this.sessionCapturer.snapshotCaptures.push(\n this.sessionCapturer.anchorAfterNavigation(browser).catch(() => {})\n )\n }\n\n private popCommandStackIfMatches(methodName: string, cmdSig: string): void {\n const stackFrame = this.commandStack[this.commandStack.length - 1]\n if (stackFrame?.command === methodName && stackFrame.signature === cmdSig) {\n this.commandStack.pop()\n }\n }\n\n /** Serialize the result and record which element produced it, so a later\n * node:assert on that value can name its target (core's value→locator\n * registry). Fed for every command, so a page-level read records \"no\n * element\" — see `rememberReadValue` for why that sentinel matters. Folded\n * into one call because `makeCaptureCallback` is already over the line cap. */\n private serializeAndAttributeResult(\n methodName: string,\n logArgs: unknown[],\n callbackResult: unknown\n ): unknown {\n const result = serializeCommandResult(callbackResult, methodName)\n rememberReadValue(result, commandTargetSelector(methodName, logArgs))\n return result\n }\n\n // Result-capturing callback factory — called by Nightwatch's async queue\n // when the command completes. This is where we get the *actual* result.\n private makeCaptureCallback(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n cmdSig: string,\n callSource: string | undefined,\n hasUserSource: boolean,\n invokedAt: number,\n sequence: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n // Stamped HERE, when the command actually finished in the browser — not\n // when the test enqueued it. Nightwatch runs commands off a queue, so\n // invocation leads real execution by hundreds of ms, and the page-side\n // mutation stream is on real time: a row stamped at invocation replayed\n // the page from BEFORE its own effect (the `#username` fill rendered an\n // empty field, the `#password` fill rendered only the username). The\n // invocation time becomes the row's `startTime`, so the row also spans its\n // real duration instead of a synthetic 1ms.\n const commandTimestamp = Date.now()\n this.popCommandStackIfMatches(methodName, cmdSig)\n const commandError = callbackError(callbackResult)\n const serializedResult = commandError\n ? undefined\n : this.serializeAndAttributeResult(methodName, logArgs, callbackResult)\n const effectiveUid = this.rowTestUid() ?? testUid\n // Only surface commands that originate from a user-code frame. Commands\n // Nightwatch issues from inside its own queue (e.g. the getTitle a\n // `browser.assert.titleContains` runs) execute in a detached tick with no\n // user frame on the stack, so they'd otherwise leak as top-level actions\n // with an \"unknown\" source. Mirrors the service's user-spec-source guard.\n if (effectiveUid && hasUserSource) {\n const meta: CommandCaptureMeta = {\n testUid: effectiveUid,\n callSource,\n timestamp: commandTimestamp,\n startTime: invokedAt,\n sequence,\n selector: commandRowSelector(methodName, logArgs)\n }\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n commandError\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n cmdSig,\n commandError\n )\n }\n }\n if (userCallback) {\n return userCallback(callbackResult)\n }\n }\n }\n\n private pushCommandStackIfNew(\n methodName: string,\n cmdSig: string,\n callSource: string | undefined\n ): void {\n if (this.lastCommandSig === cmdSig) {\n return\n }\n this.commandStack.push({\n command: methodName,\n callSource,\n signature: cmdSig\n })\n this.lastCommandSig = cmdSig\n }\n\n private describeInvocation(\n methodName: string,\n args: unknown[]\n ): CommandInvocation {\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n return {\n userCallback: hasUserCallback\n ? (lastArg as (result: unknown) => unknown)\n : null,\n logArgs,\n cmdSig,\n callSource: callInfo.callSource,\n hasUserSource: callInfo.filePath !== undefined\n }\n }\n\n private handleCommandExecution(\n browser: NightwatchBrowser,\n browserAny: Record<string, unknown>,\n methodName: string,\n originalMethod: Function,\n args: unknown[]\n ): unknown {\n // The command's session id is the only evidence a mid-run `browser.end()`\n // leaves behind — Nightwatch raises no event and `end` is never wrapped.\n this.observeSession?.(browser)\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const { userCallback, logArgs, cmdSig, callSource, hasUserSource } =\n this.describeInvocation(methodName, args)\n // Invocation time only — the row's real completion stamp is taken inside the\n // capture callback (see makeCaptureCallback).\n const invokedAt = Date.now()\n const sequence = this.issueCounter++\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n hasUserSource,\n invokedAt,\n sequence,\n this.rowTestUid(),\n userCallback\n )\n // Suppress screencast polling while this command may be dispatching input: a\n // screenshot landing mid-click makes the click activate nothing. Enqueue is\n // the only hook the adapter has — Nightwatch executes off a queue it exposes\n // no start event for — so an awaited call (enqueue ≈ execution) is covered,\n // while one queued behind a long wait can see its window expire before it\n // runs. The bound is what keeps that from stalling the recorder instead.\n const closeInputWindow = beginInputDispatch(methodName)\n const gatedCallback = (callbackResult: unknown) => {\n closeInputWindow()\n return captureCallback(callbackResult)\n }\n const modifiedArgs = [...logArgs, gatedCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n closeInputWindow()\n this.popCommandStackIfMatches(methodName, cmdSig)\n this.captureCommandError(methodName, logArgs, error, callSource)\n throw error\n }\n }\n\n private captureCommandError(\n methodName: string,\n args: unknown[],\n error: unknown,\n callSource: string | undefined\n ): void {\n const currentTest = this.rowTest()\n if (!currentTest) {\n return\n }\n\n const normalizedError = toError(error)\n log.error(`[command error] ${methodName}: ${normalizedError.message}`)\n\n this.sessionCapturer\n .captureCommand(methodName, args, undefined, normalizedError, {\n testUid: currentTest.uid,\n callSource,\n selector: commandRowSelector(methodName, args)\n })\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.sessionCapturer.sendCommand(lastCommand)\n }\n }\n\n isProxied(browser: NightwatchBrowser): boolean {\n return this.proxiedBrowsers.has(browser as object)\n }\n\n /**\n * Fire-and-forget: pull a screenshot via the WebDriver HTTP endpoint and\n * attach it to an already-captured command entry. The `suffix` is appended\n * to the log line so retried-command screenshots show `(retry)`. Errors\n * are silently swallowed — screenshots are best-effort and shouldn't fail\n * the run.\n */\n private attachScreenshot(\n browser: NightwatchBrowser,\n entry: { timestamp?: number; screenshot?: string | null },\n methodName: string,\n suffix = ''\n ): void {\n const ts = entry.timestamp ?? 0\n this.sessionCapturer\n .takeScreenshotViaHttp(browser)\n .then((screenshot) => {\n if (screenshot) {\n entry.screenshot = screenshot\n this.sessionCapturer.sendReplaceCommand(ts, entry as CommandLog)\n log.info(`[screenshot] Attached to ${methodName}${suffix}`)\n }\n })\n .catch(() => {})\n }\n}\n","/**\n * The failure a Nightwatch command reports by invoking its callback with an\n * error-shaped result instead of throwing.\n *\n * Only a SYNCHRONOUS failure reaches the try/catch around the wrapped method; an\n * async one (a command that times out waiting for an element) arrives here as a\n * result object. Left in `result`, the row kept `error: undefined` and rendered\n * as a success — the failure was readable only as raw text in the result pane,\n * with no red row and nothing in the Errors tab.\n *\n * `passed` present means this is an assertion result, which carries its own\n * pass/fail handling and must not be reinterpreted here.\n */\nexport function callbackError(result: unknown, depth = 0): Error | undefined {\n if (result instanceof Error) {\n return result\n }\n if (!result || typeof result !== 'object') {\n return undefined\n }\n const r = result as Record<string, unknown>\n // W3C driver responses nest their payload under `value` and Nightwatch passes\n // that wrapper through untouched, so the failure sits one level down — reading\n // only the top level found no `error` and the row stayed green. Depth-bounded:\n // this runs inside the command hook, so a self-referencing result must not be\n // able to overflow the stack and take the user's test down with it.\n if ('value' in r) {\n return depth < 2 ? callbackError(r.value, depth + 1) : undefined\n }\n if ('passed' in r || !r.error) {\n return undefined\n }\n const err = new Error(String(r.message ?? r.error))\n if (typeof r.stack === 'string') {\n err.stack = r.stack\n }\n return err\n}\n","// Which element a Nightwatch row is about — for a native `browser.assert.*` /\n// `browser.verify.*` row, and for a driver command whose result an assertion may\n// later be made on.\n//\n// Nightwatch names the target in the CALL: every element assertion in\n// `lib/api/assertions/*.js` takes the element definition as its FIRST argument\n// (`textContains(definition, expected)`), while the page-level ones take the\n// expected value there instead (`urlContains(expected)`). Element commands\n// follow the same shape (`getText(definition, callback)`), so both answers come\n// off arg 0 — under a per-kind allowlist, because the same namespaces also carry\n// calls whose arg 0 is a plain value or a url.\n//\n// node:assert is the case this can't answer: it takes VALUES and names no\n// element, so `commandTargetSelector` feeds core's value→locator registry from\n// the command path and the assert row reads its target back from there.\n//\n// Neither allowlist is derivable from shared's `ACTION_MAP`: that table says how\n// a command RENDERS (its `Element` entries include WDIO commands called on a\n// handle, with no selector argument at all), not whether arg 0 is a definition.\n\n/**\n * Assertions whose first argument is an element definition, transcribed from\n * Nightwatch's own assertion signatures. An allowlist, not a heuristic: the same\n * namespace also carries the page-level assertions (`titleContains`,\n * `urlContains`) and node:assert's value-based mirrors (`ok`, `equal`, …), whose\n * first argument is a plain value — attaching it as a locator is how a\n * page-title assertion ends up boxing an unrelated element.\n */\nconst ELEMENT_TARGET_ASSERTIONS: ReadonlySet<string> = new Set([\n 'attributeContains',\n 'attributeEquals',\n 'attributeMatches',\n 'containsText',\n 'cssClassNotPresent',\n 'cssClassPresent',\n 'cssProperty',\n 'domPropertyContains',\n 'domPropertyEquals',\n 'domPropertyMatches',\n 'elementNotPresent',\n 'elementPresent',\n 'elementsCount',\n 'enabled',\n 'hasAttribute',\n 'hasClass',\n 'hasDescendants',\n 'hidden',\n 'selected',\n 'textContains',\n 'textEquals',\n 'textMatches',\n 'value',\n 'valueContains',\n 'valueEquals',\n 'visible'\n])\n\n/**\n * Commands whose RESULT is a read of the element named by their first argument,\n * transcribed from Nightwatch's element-command signatures. An allowlist for the\n * same reason as above: `browser.url('https://…')` takes a string first too, and\n * reading it as a locator would attribute the page's own reads to an element.\n * Everything absent — every driver-level read included — records its value as\n * belonging to no element (core's `rememberReadValue` sentinel).\n */\nconst ELEMENT_READ_COMMANDS: ReadonlySet<string> = new Set([\n 'getAccessibleName',\n 'getAriaRole',\n 'getAttribute',\n 'getCssProperty',\n 'getElementProperty',\n 'getElementSize',\n 'getLocation',\n 'getLocationInView',\n 'getRect',\n 'getTagName',\n 'getText',\n 'getValue',\n 'isEnabled',\n 'isPresent',\n 'isSelected',\n 'isVisible',\n 'waitForElementNotPresent',\n 'waitForElementNotVisible',\n 'waitForElementPresent',\n 'waitForElementVisible'\n])\n\n/**\n * Element commands that ACT on the element named by their first argument.\n * Deliberately a second set rather than an extension of the reads above: that\n * one also decides which results enter the value→locator registry, and a click's\n * result is not a value any assertion can be made about. Same allowlist\n * discipline — `browser.keys('abc')` takes a string first too.\n */\nconst ELEMENT_ACTION_COMMANDS: ReadonlySet<string> = new Set([\n 'clearValue',\n 'click',\n 'doubleClick',\n 'moveToElement',\n 'rightClick',\n 'setValue',\n 'submitForm',\n 'updateValue'\n])\n\n/** `assert.not.visible('#x')` is recorded under the dotted path `not.visible`;\n * negating an assertion doesn't change which argument is the element. */\nfunction baseMethod(method: string): string {\n return method.startsWith('not.') ? method.slice('not.'.length) : method\n}\n\n/**\n * The selector an element definition carries: a raw string, or the `selector`\n * property of a `{selector, locateStrategy}` bag / a Nightwatch `Element`\n * (duck-typed, so no dependency on Nightwatch's class identity). A definition\n * built from an already-resolved handle carries no selector at all — such a row\n * gets none rather than a wrong one.\n */\nfunction definitionSelector(definition: unknown): string | undefined {\n if (typeof definition === 'string') {\n return definition || undefined\n }\n if (definition && typeof definition === 'object') {\n const selector = (definition as { selector?: unknown }).selector\n if (typeof selector === 'string' && selector) {\n return selector\n }\n }\n return undefined\n}\n\n/** Locator of the element a native assertion was about, or undefined when the\n * assertion targets no element (page title, url, a value comparison). Lands on\n * `CommandLog.selector` so the player's overlay boxes the target the way it\n * does for WDIO's folded matcher rows. */\nexport function assertTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_TARGET_ASSERTIONS.has(baseMethod(method))) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator a driver-command row is about — the element it read or acted on.\n * Lands on `CommandLog.selector` so the row says what it targeted instead of\n * leaving the exporter to fall back to `args[0]`. Wider than\n * {@link commandTargetSelector}, which answers the narrower provenance question\n * (whose read produced this value) for the value→locator registry. */\nexport function commandRowSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (\n !ELEMENT_READ_COMMANDS.has(method) &&\n !ELEMENT_ACTION_COMMANDS.has(method)\n ) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator of the element a driver command read from, or undefined when the\n * command reads the page rather than an element. Feeds core's value→locator\n * registry: `undefined` records the result as belonging to no element, which is\n * the sentinel a later node:assert on a page-level value relies on. */\nexport function commandTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_READ_COMMANDS.has(method)) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n","import { BOOLEAN_COMMAND_PATTERN } from '../constants.js'\n\n/**\n * Convert the raw value Nightwatch's async queue hands back to a\n * UI-friendly JSON-safe representation. Three special cases:\n *\n * - Nightwatch assertion objects `{ passed, actual, expected, message }`\n * collapse to `true` on pass, or the structured failure record on fail.\n * - Driver-result wrappers `{ value: <raw> }` unwrap to the inner value.\n * `null` on a boolean-semantic command (e.g. `waitForExist`) means\n * \"timed out / not found\" — coerce to `false` so the UI doesn't render\n * `null`.\n * - Plain objects are deep-cloned via JSON.parse/stringify so the UI can\n * safely serialize them; functions and circular references fall back to\n * `String(value)`.\n */\nexport function serializeCommandResult(\n callbackResult: unknown,\n methodName: string\n): unknown {\n if (callbackResult === null || callbackResult === undefined) {\n return undefined\n }\n\n const isBooleanCommand = BOOLEAN_COMMAND_PATTERN.test(methodName)\n\n // After the typeof + null guard above, the value is a non-null object —\n // safe to widen via `Record<string, unknown>` and probe for the discriminator\n // properties without per-access `as any`.\n if (typeof callbackResult === 'object') {\n const r = callbackResult as Record<string, unknown>\n if ('passed' in r) {\n return r.passed\n ? true\n : {\n passed: false,\n actual: r.actual,\n expected: r.expected,\n message: r.message\n }\n }\n if ('value' in r) {\n const raw = r.value\n return raw === null && isBooleanCommand ? false : raw\n }\n }\n\n if (typeof callbackResult !== 'function') {\n try {\n return JSON.parse(JSON.stringify(callbackResult))\n } catch {\n return String(callbackResult)\n }\n }\n\n return undefined\n}\n","// Native-assertion capture: turns explicit `browser.assert.*` /\n// `browser.verify.*` calls into concise trace/UI action rows with real args,\n// a clickable source location, and pass/fail colour.\n//\n// Nightwatch exposes no per-assertion hook. Each explicit call is intercepted\n// at CALL TIME (NativeAssertRecorder) and BUFFERED — concise title, real args,\n// callSource, the per-test testUid, and the preceding screenshot — but NOT\n// streamed, because streaming a neutral row mid-run would render every assert\n// green before its outcome is known. At test-end `captureNativeAssertions` reads\n// the pass/fail truth + verbose failure message and emits each row once,\n// positioned on its real execution window (the exporter re-sorts by the buffered\n// call timestamp). Correlating the recorded calls (not the raw results) also\n// excludes Nightwatch's implicit command-generated assertions (e.g.\n// waitForElementVisible's \"element was visible\" entry).\n//\n// Two outcome sources, in this order: the per-test results bag (`correlate`),\n// then the settlement of the call's own promise (`observedAssertOutcome`). The\n// cucumber runner has no results bag at all, so the settlement is its only\n// source; the describe/it interface has a bag but no per-assertion window.\n//\n// The plugin `afterEach` fires once per describe-suite (not per `it`), so the\n// finalize reads assertions from `results.testcases` (all tests) — see\n// gatherResultAssertions.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n matcherAssertionToCommandLog,\n safeSerializeAssertArg,\n stripAnsi\n} from '@wdio/devtools-core'\nimport { assertTargetSelector } from './assertTarget.js'\nimport type { SessionCapturer } from '../session.js'\nimport type {\n AssertSettlement,\n CollapsedAssertResult,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchCurrentTest,\n ObservedAssertOutcome\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:nativeAssertions')\n\n/**\n * One entry Nightwatch pushes to `results.assertions` (from\n * `NightwatchAssertion.getAssertResult`, lib/assertion/assertion.js). Carries\n * only a human message — no method/args. `failure === false` is the reliable\n * pass signal; any truthy `failure` (message string, or `true`) means failed.\n */\ninterface NwAssertionEntry {\n message?: string\n fullMsg?: string\n failure?: string | boolean\n}\n\n/** One entry Nightwatch pushes to `results.commands` (lib/reporter/index.js\n * logCommandResult). For an assert/verify `name` is the namespaced method and\n * `startTime`/`endTime` are the real queue-execution window in ms\n * (treenode.js). Read only to position the finalized row on the timeline. */\ninterface NwCommandEntry {\n name?: string\n startTime?: number\n endTime?: number\n}\n\nconst ASSERT_CMD_RE = /^(assert|verify)\\.\\w+$/\n\n/** Real per-assertion execution windows, in call order, aligned to `count`\n * recorded calls. Nightwatch enqueues assertions synchronously (all at once)\n * but runs them later one at a time, so the enqueue timestamp clusters the\n * rows; this recovers each row's true execution time so the trace timeline\n * spreads them out. Returns `null` per slot when the executed-command count\n * doesn't line up (e.g. retries) — the enqueue timestamp is kept then. */\nfunction assertCommandTimings(\n commands: NwCommandEntry[],\n count: number\n): Array<{ startTime: number; endTime: number } | null> {\n const executed = commands.filter(\n (c) => typeof c?.name === 'string' && ASSERT_CMD_RE.test(c.name)\n )\n if (executed.length !== count) {\n return new Array(count).fill(null)\n }\n return executed.map((c) =>\n typeof c.startTime === 'number' && typeof c.endTime === 'number'\n ? { startTime: c.startTime, endTime: c.endTime }\n : null\n )\n}\n\n/** Nightwatch embeds the assertion arguments in the message text\n * (AssertionInstance.initialize → Logger.formatMessage), so a passing entry\n * for `titleContains('Example')` reads \"Testing if the page title contains\n * 'Example'\". Match a call to its result entry when every string/number arg\n * appears in the message. */\nfunction messageMatchesArgs(entry: NwAssertionEntry, args: unknown[]): boolean {\n const text = String(entry.fullMsg ?? entry.message ?? '')\n const literals = args.filter(\n (a): a is string | number => typeof a === 'string' || typeof a === 'number'\n )\n return literals.length > 0 && literals.every((a) => text.includes(String(a)))\n}\n\ninterface Outcome {\n passed: boolean\n message: string\n}\n\n/** Pass/fail read off the assertion's own promise — the only outcome evidence\n * cucumber has, since its client is built with no reporter so `results` is\n * never populated. A fulfilment of `undefined` stays neutral: that is an\n * assertion enqueued but never executed, not a pass. Semantics and measurements\n * in CLAUDE.md § Known debt. */\nexport function observedAssertOutcome({\n rejected,\n value,\n settledAt\n}: AssertSettlement): ObservedAssertOutcome | undefined {\n if (rejected || value instanceof Error) {\n return {\n passed: false,\n message: stripAnsi(errorMessage(value)).trim(),\n settledAt\n }\n }\n if (value === undefined) {\n return undefined\n }\n return { passed: true, message: '', settledAt }\n}\n\n/** Execution window recovered from the observed settlement: enqueue → settle.\n * The cucumber runner logs no commands, so `assertCommandTimings` yields\n * nothing there and the row would otherwise span a synthetic 1 ms. */\nfunction observedTiming(\n call: NativeAssertCall\n): { startTime: number; endTime: number } | null {\n return call.observed\n ? { startTime: call.timestamp, endTime: call.observed.settledAt }\n : null\n}\n\n/**\n * All assertion entries for the suite, in declaration order. Nightwatch's\n * plugin `afterEach` fires once per describe-suite (not per `it`), so the flat\n * `results.assertions` reflects only the last testcase; the full per-test\n * breakdown lives in `results.testcases[title].assertions`. Flattening every\n * testcase's entries lets each buffered call correlate to its own test's\n * outcome (without this, only the last test's asserts get a pass/fail and the\n * rest render neutral). Falls back to the flat list for single-test modules or\n * older Nightwatch that doesn't populate `testcases`.\n */\nfunction gatherResultAssertions(\n results:\n | {\n assertions?: NwAssertionEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n): NwAssertionEntry[] {\n const testcases = results?.testcases\n if (testcases && typeof testcases === 'object') {\n const all: NwAssertionEntry[] = []\n for (const tc of Object.values(testcases)) {\n if (Array.isArray(tc?.assertions)) {\n all.push(...(tc.assertions as NwAssertionEntry[]))\n }\n }\n if (all.length > 0) {\n return all\n }\n }\n return Array.isArray(results?.assertions) ? results.assertions : []\n}\n\n/**\n * Pair each recorded call with its `results.assertions` entry for pass/fail +\n * verbose message. Both lists are in call/execution order and each explicit\n * call produces exactly one assertion entry, so: match by args-in-message\n * first (most specific, skips interleaved implicit entries), then fall back to\n * the next unconsumed entry positionally. A call with no matching entry left\n * (never happens for a real assertion) is dropped (`null`).\n */\nfunction correlate(\n calls: NativeAssertCall[],\n assertions: NwAssertionEntry[]\n): Array<Outcome | null> {\n const consumed = new Array(assertions.length).fill(false)\n const toOutcome = (idx: number): Outcome => {\n consumed[idx] = true\n const entry = assertions[idx]\n return {\n passed: !entry.failure,\n message: stripAnsi(String(entry.fullMsg ?? entry.message ?? '')).trim()\n }\n }\n const matched = calls.map((call) => {\n const idx = assertions.findIndex(\n (entry, i) => !consumed[i] && messageMatchesArgs(entry, call.args)\n )\n return idx === -1 ? null : toOutcome(idx)\n })\n return matched.map((outcome) => {\n if (outcome) {\n return outcome\n }\n const idx = consumed.findIndex((used) => !used)\n return idx === -1 ? null : toOutcome(idx)\n })\n}\n\n/** Last already-resolved screenshot in the command log — the DOM the assertion\n * evaluated against (title/most asserts don't mutate it). Synchronous, so it's\n * usable from the call-time wrapper. */\nexport function latestResolvedScreenshot(\n capturer: SessionCapturer\n): string | null {\n for (let i = capturer.commandsLog.length - 1; i >= 0; i--) {\n const shot = capturer.commandsLog[i]?.screenshot\n if (shot) {\n return shot\n }\n }\n return null\n}\n\n/** Reuse the nearest preceding command's screenshot; if the fire-and-forget\n * capture hasn't resolved yet (race), fall back to a fresh end-of-test one. */\nasync function resolveAssertionScreenshot(\n capturer: SessionCapturer,\n browser: NightwatchBrowser\n): Promise<string | null> {\n return (\n latestResolvedScreenshot(capturer) ??\n (await capturer.takeScreenshotViaHttp(browser))\n )\n}\n\n/** `assert.titleContains('SOFT_FAIL_ME')` — the concise row label. Strings are\n * quoted, objects elided; never the verbose failure message. */\nfunction conciseTitle(call: NativeAssertCall): string {\n const preview = call.args\n .map((a) =>\n typeof a === 'string'\n ? `'${a}'`\n : a !== null && typeof a === 'object'\n ? '…'\n : String(a)\n )\n .join(', ')\n return `${call.prefix}.${call.method}(${preview})`\n}\n\n/**\n * Build the neutral \"pending\" row emitted the moment an assert/verify is\n * called — everything known at call time (concise title, real args, callSource,\n * screenshot) but NO result/error yet, so it renders neutral (not red/green)\n * and streams in like a normal in-flight command. `startTime`/`timestamp` are\n * the call time; the row is finalized in place later by\n * {@link captureNativeAssertions}.\n */\nexport function pendingAssertionCommand(\n call: NativeAssertCall,\n testUid: string | undefined,\n screenshot: string | null\n): CommandLog {\n const entry: CommandLog = {\n command: `${call.prefix}.${call.method}`,\n args: call.args.map(safeSerializeAssertArg),\n title: conciseTitle(call),\n timestamp: call.timestamp,\n startTime: call.timestamp\n }\n if (call.callSource) {\n entry.callSource = call.callSource\n }\n if (testUid) {\n entry.testUid = testUid\n }\n if (screenshot) {\n entry.screenshot = screenshot\n }\n // Only element assertions name a target; a title/url/value one gets none, so\n // the overlay never boxes an element the assertion wasn't about.\n const selector = assertTargetSelector(call.method, call.args)\n if (selector) {\n entry.selector = selector\n }\n return entry\n}\n\n/** Nightwatch failure messages end with `… but got: \"<actual>\"`. Pull out the\n * real observed value: Nightwatch passes only the EXPECTED as an arg, so the\n * actual lives in the message (and only on failure). Undefined when absent.\n * Uses indexOf + slice (no backtracking-prone regex) on the message tail. */\nfunction parseActualFromMessage(message: string): string | undefined {\n const marker = 'but got:'\n const idx = message.lastIndexOf(marker)\n if (idx === -1) {\n return undefined\n }\n let rest = message.slice(idx + marker.length).trim()\n rest = rest.replace(/ \\(\\d+ms\\)$/, '').trim() // drop trailing \"(123ms)\"\n rest = rest.replace(/^[\"']/, '').replace(/[\"']$/, '').trim() // strip quotes\n return rest || undefined\n}\n\n/** Build the collapsed `{passed, expected, actual?, message}` result core's\n * `buildAssertParams` prefers over the positional `[actual, expected]` arg\n * convention — which is wrong for Nightwatch, whose asserts pass only the\n * expected value (`titleContains('x')`), never the actual. */\nfunction collapsedAssertResult(\n call: NativeAssertCall,\n outcome: Outcome\n): CollapsedAssertResult {\n const result: CollapsedAssertResult = {\n passed: outcome.passed,\n expected: call.args.length <= 1 ? call.args[0] : call.args\n }\n // Omitted rather than empty for a pass observed off the settlement: the\n // promise carries no prose, and an empty message would render as a blank row.\n if (outcome.message) {\n result.message = outcome.message\n }\n const actual = outcome.passed\n ? undefined\n : parseActualFromMessage(outcome.message)\n if (actual !== undefined) {\n result.actual = actual\n }\n return result\n}\n\n/** Emit one assertion row at test-end: apply pass/fail + verbose error, a\n * screenshot, and its real execution window, then send it. Rows are NOT\n * streamed at call time, so this is the single emit (no neutral pending row). */\nfunction finalizeAssertionRow(\n capturer: SessionCapturer,\n call: NativeAssertCall,\n outcome: Outcome,\n timing: { startTime: number; endTime: number } | null,\n screenshot: string | null,\n testUid: string | undefined\n): void {\n const entry = call.entry!\n const finalized = matcherAssertionToCommandLog(\n {\n prefix: call.prefix,\n method: call.method,\n args: call.args,\n passed: outcome.passed,\n message: outcome.message || `${call.prefix}.${call.method} failed`,\n callSource: call.callSource,\n title: entry.title\n },\n testUid\n )\n // Collapsed object result (not the plain 'passed' string) so the trace's\n // action params show a true actual-vs-expected diff instead of mislabelling\n // Nightwatch's single expected-arg as the actual.\n entry.result = collapsedAssertResult(call, outcome)\n entry.error = finalized.error\n if (!entry.screenshot && screenshot) {\n entry.screenshot = screenshot\n }\n // Position the row on its REAL execution window (Nightwatch enqueues all\n // asserts at once, so the call-time timestamp clustered them). The row was\n // never streamed, so send it now — a single emit with the final outcome.\n if (timing) {\n entry.startTime = timing.startTime\n entry.timestamp =\n timing.endTime > timing.startTime ? timing.endTime : timing.startTime\n }\n capturer.captureAssertCommand(entry)\n log.info(`[assert] ${entry.title} → ${outcome.passed ? 'pass' : 'fail'}`)\n}\n\n/**\n * Emit the native assertion rows at test-end: correlate the recorded calls with\n * `results.assertions`, then send each row with pass/fail (`result`) + the\n * verbose failure message (`error`, failures only) + a screenshot + its real\n * execution window. Rows are buffered (not streamed) at call time — Nightwatch\n * has no per-assertion result hook, so streaming them during the run would show\n * every assert as a neutral (green) row before its outcome is known. A call with\n * no matching result is skipped (defensive).\n */\nexport async function captureNativeAssertions(\n capturer: SessionCapturer,\n browser: NightwatchBrowser,\n currentTest: NightwatchCurrentTest | undefined,\n testUid: string | undefined,\n calls: NativeAssertCall[]\n): Promise<void> {\n if (calls.length === 0) {\n return\n }\n // Boundary cast: `results` is Nightwatch's loosely-typed per-test bag; we read\n // only the assertions + commands arrays whose shapes are documented above.\n const results = currentTest?.results as\n | {\n assertions?: NwAssertionEntry[]\n commands?: NwCommandEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n const assertions = gatherResultAssertions(results)\n const outcomes = correlate(calls, assertions)\n const timings = assertCommandTimings(\n Array.isArray(results?.commands) ? results.commands : [],\n calls.length\n )\n // One shared screenshot for all rows — same DOM, ran consecutively.\n const screenshot = await resolveAssertionScreenshot(capturer, browser)\n\n calls.forEach((call, index) => {\n if (!call.entry) {\n return\n }\n // Results-bag correlation first (it also carries the verbose per-assertion\n // message, which a settlement can't supply for a pass); the observed\n // settlement is the fallback that makes cucumber rows coloured at all. The\n // timing comes from whichever source supplied the outcome — a correlated row\n // keeps its enqueue timestamp when the bag reports no window (the BDD\n // interface always does), which is what `sequence` ordering relies on.\n const correlated = outcomes[index]\n const outcome = correlated ?? call.observed\n if (outcome) {\n finalizeAssertionRow(\n capturer,\n call,\n outcome,\n correlated ? timings[index] : observedTiming(call),\n screenshot,\n testUid\n )\n return\n }\n // No execution outcome correlated to this call (defensive): emit the\n // buffered row in its neutral state so the assertion still appears — never\n // dropped, never mis-coloured as passed/failed. Rows are only buffered at\n // call time, so without this an uncorrelated assert would vanish.\n if (!call.entry.screenshot && screenshot) {\n call.entry.screenshot = screenshot\n }\n capturer.captureAssertCommand(call.entry)\n })\n}\n","/**\n * Call-time recorder for explicit `browser.assert.*` / `browser.verify.*`.\n *\n * Extracted from BrowserProxy: the recording Proxy, the per-test buffer and the\n * settlement observation are one concern (getting a native assertion's args,\n * source location and outcome), separate from driver-command interception.\n * `nativeAssertions.ts` consumes the drained buffer at test-end.\n */\n\nimport {\n observedAssertOutcome,\n latestResolvedScreenshot,\n pendingAssertionCommand\n} from './nativeAssertions.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport type { SessionCapturer } from '../session.js'\nimport type { NativeAssertCall, NightwatchBrowser } from '../types.js'\n\nexport class NativeAssertRecorder {\n /**\n * Per-test-unit buffer of recorded calls. Nightwatch exposes no per-assertion\n * hook and its test-end results carry no source location for passing\n * assertions, so call-time capture is the only way to get real args +\n * callSource. Drained once per test unit (`afterEach`, or the cucumber\n * scenario's pre-quit hook) — never per step.\n */\n private calls: NativeAssertCall[] = []\n\n constructor(\n private readonly capturer: SessionCapturer,\n private getCurrentTest: () => { uid?: string } | null,\n private nextSequence: () => number\n ) {}\n\n clear(): void {\n this.calls = []\n }\n\n /** Hand off this test's recorded calls and clear the buffer so the next test\n * unit starts fresh. */\n drain(): NativeAssertCall[] {\n const calls = this.calls\n this.calls = []\n return calls\n }\n\n /**\n * Replace `browser.assert` / `browser.verify` (both are Nightwatch Proxies\n * whose `get` returns a fresh function per access) with a recording Proxy:\n * on each method call it captures `{prefix, method, args, callSource}` from a\n * user-code frame, buffers a pending row, then delegates to the ORIGINAL\n * namespace method so Nightwatch's queue, chaining, and abortOnFailure\n * semantics (assert aborts, verify does not) are byte-for-byte unchanged.\n * Called once per browser.\n */\n wrapNamespaces(browser: NightwatchBrowser): void {\n // Cast once: the assert/verify namespaces aren't on the public type; each\n // is a dynamic method bag reached by property name.\n const b = browser as unknown as Record<string, unknown>\n ;(['assert', 'verify'] as const).forEach((prefix) => {\n const original = b[prefix]\n if (!original || typeof original !== 'object') {\n return\n }\n b[prefix] = this.namespaceProxy(original, prefix, [])\n })\n }\n\n /**\n * Recording Proxy over one assert/verify namespace. A function property\n * becomes a call-time recorder keyed by its full dotted path\n * (`titleContains`, `not.titleContains`); a nested namespace object recurses\n * through the SAME wrapper — Nightwatch exposes `assert.not` as its own Proxy,\n * so negated asserts are recorded via the identical mechanism as positive\n * ones instead of a parallel path. Non-method, non-namespace props pass\n * through untouched.\n */\n private namespaceProxy(\n target: object,\n prefix: 'assert' | 'verify',\n path: readonly string[]\n ): object {\n return new Proxy(target, {\n get: (t, name, receiver) => {\n const orig = Reflect.get(t, name, receiver)\n if (typeof name !== 'string') {\n return orig\n }\n if (orig !== null && typeof orig === 'object') {\n return this.namespaceProxy(orig, prefix, [...path, name])\n }\n if (typeof orig !== 'function') {\n return orig\n }\n return (...args: unknown[]) => {\n const callInfo = getCallSourceFromStack()\n const call =\n callInfo.filePath === undefined\n ? undefined\n : this.buffer({\n prefix,\n method: [...path, name].join('.'),\n args,\n callSource: callInfo.callSource,\n timestamp: Date.now()\n })\n const result = (orig as (...a: unknown[]) => unknown)(...args)\n if (call) {\n this.observe(call, result)\n }\n return result\n }\n }\n })\n }\n\n /** Buffer an explicit assert/verify call at invocation time (prebuilding its\n * row with args/callSource/screenshot), but do NOT stream it yet. Nightwatch\n * exposes no per-assertion result hook, so streaming here would show every\n * assert as a neutral (green) row while the test is still Running — before\n * its pass/fail is known. `captureNativeAssertions` emits the rows at\n * test-end with real outcomes + execution timing instead. */\n private buffer(call: NativeAssertCall): NativeAssertCall {\n const entry = pendingAssertionCommand(\n call,\n this.getCurrentTest()?.uid,\n latestResolvedScreenshot(this.capturer)\n )\n entry.sequence = this.nextSequence()\n call.entry = entry\n this.calls.push(call)\n return call\n }\n\n /**\n * Watch the assertion's OWN returned promise for its outcome — see\n * `observedAssertOutcome` for the settlement semantics and why this is the\n * cucumber runner's only pass/fail source. `result` is handed back to the\n * caller untouched; this only adds a second handler, so Nightwatch's own\n * chaining and rejection propagation are unchanged. A non-thenable result\n * (Nightwatch's synchronous `api` return, when commands aren't ES6-async)\n * leaves the call unobserved and the results-bag path supplies the outcome.\n */\n private observe(call: NativeAssertCall, result: unknown): void {\n const thenable = result as { then?: unknown } | null | undefined\n if (!thenable || typeof thenable.then !== 'function') {\n return\n }\n void (result as Promise<unknown>).then(\n (value) => {\n call.observed = observedAssertOutcome({\n rejected: false,\n value,\n settledAt: Date.now()\n })\n },\n (reason) => {\n call.observed = observedAssertOutcome({\n rejected: true,\n value: reason,\n settledAt: Date.now()\n })\n }\n )\n }\n}\n","/**\n * Binds the screencast recorder to a browser session, and rebinds it — and\n * nothing else — when that session is replaced mid-run.\n *\n * Nightwatch raises no session event, and `end` sits in\n * INTERNAL_COMMANDS_TO_IGNORE, so the command proxy never sees the call that\n * destroys the session. The BDD `describe/it` interface compounds it: the\n * plugin's `beforeEach` fires once per MODULE, so `ensureSessionInitialized` —\n * the only place that detects a session change — is never re-entered for a\n * `browser.end()` between `it`s. A command executing under a different\n * `sessionId` is the sole evidence the session was replaced.\n *\n * Rotation touches the recorder and nothing else — the run's SessionCapturer\n * outlives every session it is pointed at. It leaves `lastSessionId` alone\n * because that field is `ensureSessionInitialized`'s own bookkeeping. The other\n * half of a session rebind, re-arming BiDi and the collector preload, hangs off\n * the same detection in `session-init.ts` `rearmCaptureForSession`. Both halves\n * are self-guarding, so `ensureSessionInitialized` reuses them for the session\n * replacement it detects itself.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { ScreencastRecorder } from './screencast.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:screencast-session')\n\n/**\n * Start a fresh recorder for `sessionId` — every session gets its own, matching\n * the WDIO service. Polling mode only (Nightwatch exposes no stable CDP escape\n * hatch). Idempotent, so bringup and rotation share one path.\n */\nexport async function startScreencast(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n sessionId: string | undefined\n): Promise<void> {\n if (!ctx.screencastOptions.enabled || ctx.screencastRecorder || !sessionId) {\n return\n }\n ctx.screencastRecorder = new ScreencastRecorder(\n ctx.sessionCapturer,\n ctx.screencastOptions\n )\n ctx.screencastSessionId = sessionId\n log.info(`🎬 Starting screencast for session ${sessionId}`)\n await ctx.screencastRecorder.start(browser)\n}\n\n/**\n * Whether the recorder is bound to a session the browser has already left.\n * Requires BOTH a live recorder and a live session id: starting one from\n * nothing would strand a poll interval past the plugin's own teardown, which\n * finalizes the recorder before its last browser calls.\n */\nexport function needsScreencastRotation(\n ctx: Pick<SessionInitCtx, 'screencastRecorder' | 'screencastSessionId'>,\n sessionId: string | undefined\n): boolean {\n return (\n ctx.screencastRecorder !== undefined &&\n sessionId !== undefined &&\n ctx.screencastSessionId !== sessionId\n )\n}\n\n/**\n * Observe one command. Rotation is fire-and-forget — a command must not wait on\n * a video encode — and parked on `ctx.screencastRotation`, which both latches\n * out the flood of commands arriving before it completes and gives teardown\n * something to await, so a rotation cannot start a recorder after the plugin\n * has already finalized.\n */\nexport function rotateScreencastForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n if (ctx.screencastRotation || !ctx.screencastOptions.enabled) {\n return\n }\n const sessionId = browser.sessionId ?? undefined\n if (!needsScreencastRotation(ctx, sessionId)) {\n return\n }\n log.info(`🎬 Session replaced — rotating screencast to ${sessionId}`)\n ctx.screencastRotation = ctx\n .finalizeCurrentScreencast()\n .then(() => startScreencast(ctx, browser, sessionId))\n .catch((err) =>\n log.warn(`Screencast rotation failed: ${errorMessage(err)}`)\n )\n .finally(() => {\n ctx.screencastRotation = undefined\n })\n}\n","import logger from '@wdio/logger'\nimport { ScreencastRecorderBase, errorMessage } from '@wdio/devtools-core'\nimport type { ScreencastOptions } from '@wdio/devtools-shared'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:ScreencastRecorder')\n\n/**\n * Nightwatch screencast recorder. Polling-only — Nightwatch doesn't expose a\n * stable CDP escape hatch the way WDIO (getPuppeteer) and Selenium\n * (createCDPConnection) do.\n *\n * `browser.takeScreenshot()` goes through Nightwatch's command queue and is\n * unreliable for polling (the existing code has `takeScreenshotViaHttp` for\n * the same reason — see session.ts). The recorder delegates to that helper\n * instead so screenshots fire directly over the WebDriver HTTP transport.\n */\nexport class ScreencastRecorder extends ScreencastRecorderBase<NightwatchBrowser> {\n readonly #sessionCapturer: SessionCapturer\n\n constructor(sessionCapturer: SessionCapturer, options: ScreencastOptions) {\n super(options)\n this.#sessionCapturer = sessionCapturer\n }\n\n protected override onPollingStarted(intervalMs: number): void {\n log.info(\n `✓ Screencast recording started (polling mode, ${intervalMs} ms interval)`\n )\n }\n\n protected override onPollingStopped(frameCount: number): void {\n log.info(`✓ Screencast stopped — ${frameCount} frame(s) collected`)\n }\n\n protected override onUnavailable(err: unknown): void {\n log.warn(\n `Screencast unavailable (${errorMessage(err)}). Recording skipped.`\n )\n }\n\n protected override async takeScreenshot(): Promise<string | null> {\n const browser = this.driver\n if (!browser) {\n return null\n }\n return this.#sessionCapturer.takeScreenshotViaHttp(browser)\n }\n}\n","/**\n * The plugin's screencast seam: what a run's mode and filmstrip/video options\n * imply for the recorder, plus the per-run frame buffer the trace filmstrip and\n * the per-test video slices are cut from.\n *\n * The recorder itself is `screencast.ts`, and binding it to a session — or\n * rotating it when the session is replaced — is `screencast-session.ts`. This is\n * the plugin-level state those two hang off, split out of `index.ts` so the\n * plugin keeps only its accessor bag and thin delegators.\n */\n\nimport { accumulatedScreencastFrames } from '@wdio/devtools-core'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { finalizeCurrentScreencast } from './session-init.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n ScreencastOptions,\n TraceVideoPolicy\n} from './types.js'\n\n/** The already-defaulted options the recorder decision is made from — read the\n * resolved `filmstrip`/`video`, never the raw `options.filmstrip`, or a\n * default-on filmstrip leaves the recorder inert while the plugin's own options\n * claim it is running. */\nexport interface ScreencastResolutionInput {\n mode: DevToolsMode\n filmstrip: boolean\n video: TraceVideoPolicy\n screencast: ScreencastOptions | undefined\n}\n\nexport interface ResolvedScreencast {\n /** What the plugin exposes as `options.screencast`. */\n options: ScreencastOptions\n /** Config warning to log, when the mode makes the option meaningless. */\n warning?: string\n}\n\n/**\n * Whether this run records a screencast, and under which options. Filmstrip OR a\n * produce-only per-test video drives the recorder in trace mode; bare screencast\n * with neither is a live-mode feature and is dropped there.\n */\nexport function resolveScreencastOptions(\n input: ScreencastResolutionInput\n): ResolvedScreencast {\n const screencast = input.screencast ?? {}\n if (input.mode !== 'trace') {\n return { options: screencast }\n }\n if (input.filmstrip || input.video !== 'off') {\n return { options: { ...screencast, enabled: true } }\n }\n if (screencast.enabled === true) {\n return {\n options: {},\n warning: 'trace mode: ignoring screencast option (live-mode feature)'\n }\n }\n return { options: screencast }\n}\n\n/**\n * One run's screencast state: the live recorder and the session it is bound to,\n * an in-flight rotation, and the frames drained from recorders already finalized.\n */\nexport class PluginScreencast {\n /** Recorder options with the shared defaults applied. */\n readonly options: ScreencastOptions\n recorder?: ScreencastRecorder\n sessionId?: string\n /** In-flight recorder rotation — see `screencast-session.ts`. */\n rotation?: Promise<void>\n\n // Snapshotted before each recorder is nulled, so the export isn't blank.\n #accumulated: ScreencastFrame[] = []\n readonly #keepFrames: boolean\n\n /** `keepFrames` when the trace filmstrip or a per-test video will consume\n * them; without either, a finalized recorder's frames are of no further use. */\n constructor(options: ScreencastOptions, keepFrames: boolean) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n this.#keepFrames = keepFrames\n }\n\n /** Every frame this run has produced — the finalized recorders' plus the live\n * one's, so a mid-run slice flush (which fires before the recorder is\n * drained) isn't blank. */\n get frames(): ScreencastFrame[] {\n return accumulatedScreencastFrames(this.#accumulated, this.recorder)\n }\n\n /** Drain the live recorder into the run buffer, then stop and clear it. The\n * drain has to precede the stop: `finalizeCurrentScreencast` nulls the\n * recorder, and with it the only reference to its frames. */\n async finalize(ctx: SessionInitCtx): Promise<void> {\n if (this.#keepFrames && this.recorder) {\n this.#accumulated.push(...this.recorder.frames)\n }\n await finalizeCurrentScreencast(ctx)\n }\n}\n","/**\n * Nightwatch eventHub integration.\n *\n * Extracted from the plugin class so registration stays out of the\n * file-size cap and the metadata-forwarding behavior is unit-testable\n * without standing up the whole plugin. The plugin's\n * `registerEventHandlers(eventHub)` delegates here.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchEventHub } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:event-hub')\n\nexport interface EventHubBindings {\n /** Live session capturer — may be undefined until bringup completes. */\n getSessionCapturer(): SessionCapturer | undefined\n /** Builds the `options` field forwarded with the metadata payload. */\n buildMetadataOptions(): unknown\n /** Lets the plugin flip the cucumber-runner flag on detection. */\n setCucumberRunner(value: boolean): void\n}\n\nfunction makeSessionMetadataHandler(\n bindings: EventHubBindings\n): (data: unknown) => void {\n return (data: unknown) => {\n try {\n const md =\n ((data as { metadata?: Record<string, unknown> } | undefined)\n ?.metadata as Record<string, unknown> | undefined) ?? {}\n const capturer = bindings.getSessionCapturer()\n const sessionCapabilities = md.sessionCapabilities\n const sessionId = md.sessionId as string | undefined\n if (!capturer || (!sessionCapabilities && !sessionId)) {\n return\n }\n capturer.sendUpstream('metadata', {\n type: TraceType.Testrunner,\n capabilities: sessionCapabilities ?? {},\n sessionId,\n testEnv: md.testEnv as string | undefined,\n host: md.host as string | undefined,\n modulePath: md.modulePath as string | undefined,\n options: bindings.buildMetadataOptions()\n })\n } catch (err) {\n log.error(`Error in event handler: ${errorMessage(err)}`)\n }\n }\n}\n\nexport function registerEventHandlers(\n eventHub: NightwatchEventHub,\n bindings: EventHubBindings\n): void {\n bindings.setCucumberRunner(eventHub.runner === 'cucumber')\n if (eventHub.runner === 'cucumber') {\n log.info('✓ Cucumber runner detected via NightwatchEventHub')\n }\n log.info('✓ NightwatchEventHub registered — enriched metadata enabled')\n const handler = makeSessionMetadataHandler(bindings)\n eventHub.on('TestSuiteStarted', handler)\n eventHub.on('TestRunStarted', handler)\n}\n","/**\n * Cucumber lifecycle for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to isolate the cucumber-specific orchestration from the per-test\n * (Mocha/Jasmine-style) lifecycle.\n *\n * The plugin passes itself as a `CucumberLifecycleCtx` — a narrow interface\n * exposing only the fields and methods this module needs. The lifecycle\n * helpers mutate the plugin's \"current execution\" state via the accessors on\n * the ctx (they can't reach the plugin's private fields directly).\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport {\n WS_SCOPE,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\n\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type { NightwatchBrowser, SuiteStats, TestStats } from './types.js'\nimport { TEST_STATE } from './constants.js'\nimport {\n closeOpenSteps,\n cucumberResultToTestState\n} from './helpers/cucumberResult.js'\nimport { buildCucumberScenarioSuite } from './helpers/cucumberScenarioBuilder.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\nimport {\n recordTestSliceBoundary,\n flushTestSlice,\n type TestSliceCtx\n} from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Cucumber's result shape varies across major versions; we pin only the\n * status field we read. Pickle shapes come from shared. */\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx extends TestSliceCtx {\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy | undefined\n setCucumberRunner(v: boolean): void\n ensureSessionInitialized(browser: NightwatchBrowser): Promise<void>\n wrapBrowserOnce(browser: NightwatchBrowser): void\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n getCurrentScenarioSuite(): SuiteStats | null\n setCurrentScenarioSuite(s: SuiteStats | null): void\n setCurrentStep(s: unknown): void\n getCurrentStep(): unknown\n setCurrentTest(t: unknown): void\n recordAttempt(uid: string, specFile?: string): number\n recordOutcome(uid: string, state: TestStats['state']): void\n emitTestArtifacts(uid: string | undefined, failed: boolean): Promise<void>\n}\n\ntype MutStep = {\n title?: string\n state?: string\n start?: Date | null\n end?: Date | null\n _duration?: number\n}\n\nfunction attachScenarioToFeature(\n ctx: CucumberLifecycleCtx,\n featureSuite: SuiteStats,\n scenarioSuite: SuiteStats\n): void {\n // If a suite with this uid already exists, this is a RETRY of the same\n // scenario — clear execution data so only the latest attempt shows.\n const existingIdx = featureSuite.suites.findIndex(\n (s: SuiteStats) => s.uid === scenarioSuite.uid\n )\n if (existingIdx !== -1) {\n featureSuite.suites[existingIdx] = scenarioSuite\n // Pass the specific scenario uid so only this scenario's execution data\n // is reset — a uid-less clearExecutionData would mark ALL suites as\n // running, destroying the previous terminal states of sibling scenarios.\n ctx.sessionCapturer.sendUpstream(WS_SCOPE.clearExecutionData, {\n uid: scenarioSuite.uid,\n entryType: 'suite'\n })\n } else {\n featureSuite.suites.push(scenarioSuite)\n }\n}\n\nfunction createFeatureSuite(\n ctx: CucumberLifecycleCtx,\n featureUri: string,\n featureName: string,\n featureContent: string,\n featureAbsPath: string,\n scenarioName: string,\n steps: Array<{ text: string }>\n): {\n featureSuite: SuiteStats\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const featureSuite = ctx.suiteManager.getOrCreateSuite(\n featureUri,\n featureName,\n featureUri,\n []\n )\n ctx.suiteManager.markSuiteAsRunning(featureSuite)\n const { featureLine, scenarioLine, stepLines, stepKeywords } =\n parseCucumberScenario(\n featureContent,\n scenarioName,\n steps.map((s) => s.text)\n )\n if (featureAbsPath && featureLine > 0) {\n featureSuite.callSource = `${featureAbsPath}:${featureLine}`\n }\n return { featureSuite, scenarioLine, stepLines, stepKeywords }\n}\n\nfunction normalizeSteps(\n pickleSteps: CucumberPickleStep[] | undefined\n): Array<{ text: string }> {\n return (pickleSteps ?? []).map((s) => ({ text: s.text ?? '' }))\n}\n\nfunction captureFeatureSources(\n ctx: CucumberLifecycleCtx,\n paths: string[]\n): void {\n for (const p of paths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\n}\n\nexport async function initCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n await ctx.ensureSessionInitialized(browser)\n const featureUri: string = pickle.uri ?? 'unknown.feature'\n const scenarioName: string = pickle.name ?? 'Unknown Scenario'\n const steps = normalizeSteps(pickle.steps)\n const {\n featureName,\n featureContent,\n featureAbsPath,\n stepDefFiles,\n capturedPaths\n } = scanFeatureFile(featureUri)\n captureFeatureSources(ctx, capturedPaths)\n const { featureSuite, scenarioLine, stepLines, stepKeywords } =\n createFeatureSuite(\n ctx,\n featureUri,\n featureName,\n featureContent,\n featureAbsPath,\n scenarioName,\n steps\n )\n const scenarioSuite = buildCucumberScenarioSuite({\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords,\n scenarioLine,\n parentFeatureSuiteUid: featureSuite.uid,\n recordAttempt: (uid, specFile) => ctx.recordAttempt(uid, specFile)\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n // The scenario is the `test` unit; its steps are the leaf metadata entries.\n recordTestSliceBoundary(ctx, featureUri, scenarioSuite.uid)\n ctx.setCurrentScenarioSuite(scenarioSuite)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n ctx.testReporter.updateSuites()\n ctx.wrapBrowserOnce(browser)\n log.info(`🥒 Scenario: ${scenarioName}`)\n}\n\nexport async function finalizeCucumberScenario(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle | undefined\n): Promise<void> {\n try {\n const scenarioState = cucumberResultToTestState(result)\n const scenario = ctx.getCurrentScenarioSuite()\n if (scenario) {\n const now = new Date()\n const duration =\n now.getTime() - (scenario.start?.getTime() ?? now.getTime())\n scenario.state = scenarioState\n scenario.end = now\n scenario._duration = duration\n // Stamp this attempt's real outcome so spec/session retention doesn't\n // collapse to the retry-stable suite's last-overwritten state.\n ctx.recordOutcome(scenario.uid, scenarioState)\n closeOpenSteps(scenario, scenarioState, now)\n\n const featureUri: string = pickle?.uri ?? 'unknown.feature'\n ctx.testManager.markTestAsProcessed(featureUri, pickle?.name ?? '')\n\n const featureSuite = ctx.suiteManager.getSuite(featureUri)\n if (featureSuite) {\n // Finalize is not called until all scenarios are done — just update state.\n ctx.suiteManager.finalizeSuiteState(featureSuite)\n }\n\n ctx.incrementCount(scenarioState)\n const icon = ctx.testIcon(scenarioState)\n log.info(\n ` ${icon} ${pickle?.name ?? 'Unknown'} (${(duration / 1000).toFixed(2)}s)`\n )\n\n ctx.testReporter.updateSuites()\n ctx.setCurrentScenarioSuite(null)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n }\n // The trace capture, slice flush, and per-test artifacts need a live\n // WebDriver session — by this hook (cucumber After order:-1) the browser is\n // already quit. They run in captureCucumberScenarioBeforeQuit (order:1000,\n // pre-quit); this hook keeps only the settled-outcome bookkeeping.\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\n/**\n * Live-session work for a cucumber scenario — runs at the cucumber `After`\n * `order:1000` hook, BEFORE Nightwatch quits the browser (its quit hook is\n * `order:0`; `finalizeCucumberScenario` is `order:-1`, after quit, so it reads\n * the settled outcome). Trace capture, the slice flush, and per-test artifacts\n * all need a live WebDriver session — at finalize the session is gone and the\n * flush bails, writing no zip. Trace test-state may be pre-outcome for a\n * scenario that fails on a Nightwatch assertion (see CLAUDE.md known debt).\n */\nexport async function captureCucumberScenarioBeforeQuit(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n try {\n // Emit native asserts (assert.titleContains, …) while the session is live —\n // Nightwatch's afterEach path skips the cucumber runner, so this is the only\n // place scenario asserts get captured. drainNativeAssertCalls() is empty when\n // captureAssertions is off. `currentTest` is undefined because a cucumber run\n // installs Nightwatch's no-op SimplifiedReporter and never fills a results\n // bag; each row's pass/fail comes from its own settled promise instead\n // (`observedAssertOutcome`).\n const assertCalls = ctx.browserProxy?.drainNativeAssertCalls() ?? []\n if (assertCalls.length > 0) {\n await captureNativeAssertions(\n ctx.sessionCapturer,\n browser,\n undefined,\n scenario?.uid,\n assertCalls\n )\n }\n // Anchored: a scenario ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await ctx.sessionCapturer.captureTrace(browser, true)\n flushTestSlice(ctx)\n await ctx.emitTestArtifacts(\n scenario?.uid,\n cucumberResultToTestState(result) === TEST_STATE.FAILED\n )\n } catch (err) {\n log.error(\n `Failed to capture Cucumber scenario before quit: ${errorMessage(err)}`\n )\n }\n}\n\nexport async function cucumberBeforeStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n if (!scenario) {\n return\n }\n // Reset per-step dedup tracking so commands in step N are never\n // mistaken for retries of identically-signatured commands from step N-1.\n ctx.browserProxy?.resetCommandTracking()\n\n const stepText: string = pickleStep?.text ?? ''\n const step = (scenario.tests as Array<MutStep | string>).find(\n (t): t is MutStep =>\n typeof t !== 'string' &&\n (t.title?.endsWith(stepText) === true || t.title === stepText)\n )\n if (step) {\n step.state = TEST_STATE.RUNNING\n step.start = new Date()\n step.end = null\n ctx.setCurrentStep(step)\n ctx.testReporter.updateSuites()\n }\n}\n\nexport async function cucumberAfterStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const step = ctx.getCurrentStep() as MutStep | null\n if (!step) {\n return\n }\n const status = String(result?.status ?? 'UNKNOWN').toUpperCase()\n const stepState: TestStats['state'] =\n status === 'PASSED'\n ? TEST_STATE.PASSED\n : status === 'SKIPPED'\n ? TEST_STATE.SKIPPED\n : TEST_STATE.FAILED\n step.state = stepState\n step.end = new Date()\n step._duration = Date.now() - (step.start?.getTime() ?? Date.now())\n ctx.setCurrentStep(null)\n ctx.testReporter.updateSuites()\n void pickleStep\n}\n\nexport async function cucumberBefore(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n ctx.setCucumberRunner(true)\n await initCucumberScenario(ctx, browser, pickle)\n}\n\nexport async function cucumberAfter(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n): Promise<void> {\n await finalizeCucumberScenario(ctx, browser, result, pickle)\n}\n","import { TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\n\n/**\n * Map a Cucumber Pickle step result's `status` field (PASSED/FAILED/SKIPPED/\n * UNKNOWN/PENDING/AMBIGUOUS/UNDEFINED) to the dashboard's TestStats state.\n * Everything non-passed and non-skipped is treated as FAILED for the UI —\n * the underlying error/message survives in the pickle for the Compare view.\n */\nexport function cucumberResultToTestState(result: unknown): TestStats['state'] {\n const status = String(\n (result as { status?: unknown })?.status ?? 'UNKNOWN'\n ).toUpperCase()\n if (status === 'PASSED') {\n return TEST_STATE.PASSED\n }\n if (status === 'SKIPPED') {\n return TEST_STATE.SKIPPED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * Cucumber's After hook fires with the scenario's final status, but any\n * still-running or pending steps (e.g. an early failure short-circuited\n * the rest) need to be closed too. Mark them PASSED only when the whole\n * scenario passed; FAILED otherwise. Pure in-place mutation — the suite's\n * `tests` array references are the same TestStats objects the dashboard\n * already received via earlier WS broadcasts.\n */\nexport function closeOpenSteps(\n suite: SuiteStats,\n scenarioState: TestStats['state'],\n now: Date = new Date()\n): void {\n for (const step of suite.tests) {\n if (typeof step === 'string') {\n continue\n }\n if (step.state === 'running' || step.state === 'pending') {\n step.state =\n scenarioState === TEST_STATE.PASSED\n ? TEST_STATE.PASSED\n : TEST_STATE.FAILED\n step.end = now\n }\n }\n}\n","import { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats } from '../types.js'\nimport { deterministicUid, findStepDefinitionLine } from './utils.js'\n\nexport interface CucumberScenarioBuildInput {\n /** Cucumber pickle URI — the .feature path. */\n featureUri: string\n scenarioName: string\n featureName: string\n /** Absolute file path used for callSource resolution (may be unresolved). */\n featureAbsPath?: string\n /** Step-definition files discovered for the feature (passed to find each step's source location). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Pickle steps as parsed by Cucumber. */\n steps: Array<{ text: string }>\n /** Per-step source line numbers from the .feature file (parsed once by the caller). */\n stepLines: number[]\n /** Per-step Gherkin keywords (Given/When/Then/And/But) parsed alongside `stepLines`. */\n stepKeywords: string[]\n /** Scenario header line number in the .feature file (0 if unresolvable). */\n scenarioLine: number\n /** Parent feature-suite uid — scenarios nest under this. */\n parentFeatureSuiteUid: string\n /** Records the scenario start under its (retry-stable) uid + feature file and\n * returns the 0-based attempt number stamped on every step. Omitted → 0. */\n recordAttempt?: (scenarioUid: string, specFile?: string) => number\n}\n\n/**\n * Build a fully-populated scenario sub-suite (with its `tests` array of step\n * TestStats entries) for one Cucumber scenario. Pure factory — no side effects;\n * the caller decides whether to push into the feature suite or replace an\n * existing entry (retry case).\n *\n * Extracted from `NightwatchDevToolsPlugin.#initCucumberScenario` — the\n * scenario+steps construction was ~70 lines of object-literal building that\n * doesn't touch plugin state.\n */\nfunction buildScenarioStepTest(\n input: CucumberScenarioBuildInput,\n scenarioUid: string,\n attempt: number,\n i: number\n): SuiteStats['tests'][number] {\n const {\n featureUri,\n scenarioName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords\n } = input\n const step = steps[i]\n const keyword = stepKeywords[i] || ''\n const stepLabel = keyword ? `${keyword} ${step.text}` : step.text\n const stepDefLoc = findStepDefinitionLine(stepDefFiles, step.text)\n const callSource = stepDefLoc\n ? `${stepDefLoc.filePath}:${stepDefLoc.line}`\n : featureAbsPath && stepLines[i] > 0\n ? `${featureAbsPath}:${stepLines[i]}`\n : undefined\n return {\n // Scope by the scenario uid (which carries the scenario line) so identical\n // step text in sibling scenarios and outline example rows stays distinct.\n uid: deterministicUid(featureUri, `step:${scenarioUid}:${step.text}`),\n cid: DEFAULTS.CID,\n title: stepLabel,\n fullTitle: `${scenarioName} ${stepLabel}`,\n parent: scenarioUid,\n state: TEST_STATE.PENDING,\n start: new Date(),\n end: null,\n type: 'test' as const,\n file: featureUri,\n // Scenario-level attempt from the tracker (0 first run, +1 per retry);\n // flows to TestOutcome.attempt via collectSuiteTestMetadata.\n retries: attempt,\n _duration: 0,\n hooks: [],\n callSource\n }\n}\n\nexport function buildCucumberScenarioSuite(\n input: CucumberScenarioBuildInput\n): SuiteStats {\n const {\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n steps,\n scenarioLine,\n parentFeatureSuiteUid\n } = input\n // deterministicUid (no counter) so the SAME scenario gets the SAME uid\n // across retries — that's what makes retry-coalescing work upstream. The\n // scenario line disambiguates outline example rows that share a name.\n const scenarioUid = deterministicUid(\n featureUri,\n `scenario:${scenarioName}:${scenarioLine}`\n )\n const attempt = input.recordAttempt?.(scenarioUid, featureUri) ?? 0\n const scenarioSuite: SuiteStats = {\n uid: scenarioUid,\n cid: DEFAULTS.CID,\n title: scenarioName,\n fullTitle: `${featureName} ${scenarioName}`,\n parent: parentFeatureSuiteUid,\n type: 'suite' as const,\n file: featureUri,\n start: new Date(),\n state: TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource:\n featureAbsPath && scenarioLine > 0\n ? `${featureAbsPath}:${scenarioLine}`\n : undefined\n }\n for (let i = 0; i < steps.length; i++) {\n scenarioSuite.tests.push(\n buildScenarioStepTest(input, scenarioUid, attempt, i)\n )\n }\n return scenarioSuite\n}\n","import fs from 'fs'\nimport path from 'node:path'\n\nexport interface FeatureFileScan {\n /** Header `Feature:` value, or the filename basename if unreadable. */\n featureName: string\n /** Raw `.feature` file contents (empty when unreadable). */\n featureContent: string\n /** Absolute path to the `.feature` file (resolved from cwd + uri). */\n featureAbsPath: string\n /** Sibling step-definition files (under `step_definitions`/`steps`/`support`). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Paths the caller should feed to `sessionCapturer.captureSource` so the\n * dashboard's Source panel can render them. */\n capturedPaths: string[]\n}\n\n/**\n * Scan a Cucumber feature file and its sibling step-definitions. Pure I/O —\n * the caller invokes `sessionCapturer.captureSource` for each path in\n * `capturedPaths` so this helper stays free of the session capturer.\n */\nexport function scanFeatureFile(featureUri: string): FeatureFileScan {\n const featureAbsPath = path.resolve(process.cwd(), featureUri)\n const result: FeatureFileScan = {\n featureName: path.basename(featureUri, '.feature'),\n featureContent: '',\n featureAbsPath,\n stepDefFiles: [],\n capturedPaths: []\n }\n\n if (featureUri === 'unknown.feature' || !fs.existsSync(featureAbsPath)) {\n return result\n }\n\n result.featureContent = fs.readFileSync(featureAbsPath, 'utf-8')\n const match = result.featureContent.match(/^\\s*Feature:\\s*(.+)/m)\n if (match) {\n result.featureName = match[1].trim()\n }\n result.capturedPaths.push(featureAbsPath)\n\n const featureDir = path.dirname(featureAbsPath)\n const stepDirCandidates = ['step_definitions', 'steps', 'support']\n for (const candidate of stepDirCandidates) {\n const stepDir = path.join(featureDir, candidate)\n if (!fs.existsSync(stepDir) || !fs.statSync(stepDir).isDirectory()) {\n continue\n }\n for (const entry of fs.readdirSync(stepDir)) {\n if (!/\\.(js|ts|mjs|cjs)$/.test(entry)) {\n continue\n }\n const stepFilePath = path.join(stepDir, entry)\n result.capturedPaths.push(stepFilePath)\n try {\n result.stepDefFiles.push({\n filePath: stepFilePath,\n content: fs.readFileSync(stepFilePath, 'utf-8')\n })\n } catch {\n // skip unreadable files\n }\n }\n }\n\n return result\n}\n","/**\n * Trace-slice boundary recording for `spec` and `test` granularity.\n *\n * `spec` slices are recorded/flushed at each spec-file transition (unchanged\n * from the original inline `beforeEach` logic). For `test`, Nightwatch builds\n * test outcomes into the suite tree in place — a retry overwrites the previous\n * attempt's state (regular tests reuse the same test object; cucumber replaces\n * the scenario suite). So each attempt's slice is flushed at its own\n * test/scenario END, before the next attempt can overwrite the outcome the\n * flush reads. Regular tests drive this from test-lifecycle, cucumber scenarios\n * from cucumber-lifecycle; both share this module.\n */\n\nimport {\n findFlushableRange,\n recordSliceBoundary,\n recordSpecBoundary,\n type SpecBoundaryContext,\n type SpecRange,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type { TraceGranularity } from './types.js'\n\nexport interface TestSliceCtx {\n readonly sessionCapturer: SessionCapturer\n readonly traceMode: boolean\n readonly traceGranularity: TraceGranularity\n readonly specRanges: SpecRange[]\n readonly flushedSpecs: Set<string>\n flushTraceRange(range: SpecRange): Promise<TraceArtifact | undefined>\n}\n\nfunction sliceActive(ctx: TestSliceCtx): boolean {\n return ctx.traceMode && ctx.traceGranularity === 'test'\n}\n\nfunction boundaryContext(ctx: TestSliceCtx): SpecBoundaryContext {\n return {\n specRanges: ctx.specRanges,\n flushedSpecs: ctx.flushedSpecs,\n capturer: ctx.sessionCapturer,\n // Stamped onto the range so its own flush waits for exactly these.\n pendingCaptures: ctx.sessionCapturer.snapshotCaptures\n }\n}\n\nfunction flushPrevious(ctx: TestSliceCtx, prevRange: SpecRange | null): void {\n if (!prevRange) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(prevRange)\n}\n\n/**\n * Record a spec-file boundary at test/scenario start and eagerly flush the\n * previous spec's slice. No-op for `session`/`test` granularity (core returns\n * null). Preserves the original `beforeEach` behavior verbatim.\n */\nexport function recordSpecSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string\n): void {\n const prevRange = recordSpecBoundary(\n boundaryContext(ctx),\n specFile,\n ctx.traceGranularity\n )\n flushPrevious(ctx, prevRange)\n}\n\n/**\n * Record a per-test slice boundary at test/scenario start. No-op outside trace\n * mode + `test` granularity. Retries push a distinct range (core keys a repeat\n * `${testUid}-retry${n}`), so each attempt becomes its own artifact.\n */\nexport function recordTestSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string,\n testUid: string\n): void {\n if (!sliceActive(ctx)) {\n return\n }\n recordSliceBoundary(boundaryContext(ctx), 'test', specFile, testUid)\n}\n\n/**\n * Flush the current test's slice at test/scenario end — before a retry can\n * overwrite its outcome in the suite tree. Fire-and-forget; the finalize pass\n * is the safety net for any range this misses. No-op outside trace + `test`.\n */\nexport function flushTestSlice(ctx: TestSliceCtx): void {\n if (!sliceActive(ctx)) {\n return\n }\n const range = findFlushableRange(ctx.specRanges)\n if (!range) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(range)\n}\n","/**\n * Test (Mocha/Jasmine-style) lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to keep the per-test orchestration distinct from the cucumber path.\n *\n * The plugin passes itself as a `TestLifecycleCtx` — a narrow interface that\n * exposes only the fields and methods these helpers need.\n */\n\nimport logger from '@wdio/logger'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchTestCase,\n NightwatchTestResults,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { DEFAULTS, TIMING, TEST_STATE } from './constants.js'\nimport { resolveSpecFilePath } from './helpers/specFileResolver.js'\nimport { closePreviousTest } from './helpers/closePreviousTest.js'\nimport { matchTestName } from './helpers/runningTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\nimport { recordTestSliceBoundary, type TestSliceCtx } from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx extends TestSliceCtx {\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy\n readonly srcFolders: string[]\n isScriptInjected: boolean\n getRerunLabel(): string | undefined\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n setCurrentTest(t: unknown): void\n recordAttempt(uid: string, specFile?: string): number\n}\n\ninterface SuiteMetadata {\n testFile: string\n fullPath: string | null\n suiteTitle: string\n testNames: string[]\n suiteLine: number | null\n testLines: number[]\n}\n\nexport function resolveSuiteMetadata(\n ctx: TestLifecycleCtx,\n currentTest: NightwatchCurrentTest\n): SuiteMetadata {\n const moduleName = currentTest.module ?? ''\n const testFile =\n moduleName.split('/').pop() || moduleName || DEFAULTS.FILE_NAME\n const fullPath = resolveSpecFilePath(\n testFile,\n moduleName,\n ctx.srcFolders,\n ctx.browserProxy.getCurrentTestFullPath() || undefined\n )\n if (!fullPath) {\n log.warn(\n `[beforeEach] Could not resolve file path for \"${testFile}\" — source view will be unavailable`\n )\n }\n let suiteTitle = testFile\n let testNames: string[] = []\n let suiteLine: number | null = null\n let testLines: number[] = []\n if (fullPath) {\n const parsed = extractTestMetadata(fullPath)\n if (parsed.suiteTitle) {\n suiteTitle = parsed.suiteTitle\n }\n testNames = parsed.testNames\n suiteLine = parsed.suiteLine\n testLines = parsed.testLines\n }\n const rerunLabel = ctx.getRerunLabel()\n if (rerunLabel) {\n const targetIndex = testNames.findIndex((name) => name === rerunLabel)\n if (targetIndex !== -1) {\n testNames = [testNames[targetIndex]]\n testLines = testLines[targetIndex] ? [testLines[targetIndex]] : []\n }\n }\n return { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines }\n}\n\nexport function pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n): string | undefined {\n return (\n matchTestName(currentTest?.name, testNames) ||\n testNames.find((name) => !processedTests.has(name))\n )\n}\n\nexport async function startNextTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>,\n specFile: string | null\n): Promise<void> {\n if (processedTests.size === 0) {\n ctx.suiteManager.markSuiteAsRunning(currentSuite)\n }\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n // Nightwatch has no per-test retry index; the tracker is the retry signal.\n test.retries = ctx.recordAttempt(test.uid, specFile ?? undefined)\n if (specFile) {\n recordTestSliceBoundary(ctx, specFile, test.uid)\n }\n test.state = TEST_STATE.RUNNING as TestStats['state']\n test.start = new Date()\n test.end = null\n ctx.testReporter.onTestStart(test)\n ctx.setCurrentTest(test)\n log.info(` ▶ ${currentTestName}`)\n await new Promise((resolve) => setTimeout(resolve, TIMING.TEST_START_DELAY))\n } else {\n log.warn(\n `Test \"${currentTestName}\" not found in suite \"${currentSuite.title}\"`\n )\n ctx.setCurrentTest(null)\n }\n}\n\nexport async function closePreviousRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n): Promise<void> {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest) {\n return\n }\n await closePreviousTest({\n runningTest,\n testFile,\n testcases: currentTest?.results?.testcases || {},\n testManager: ctx.testManager,\n incrementCount: (state) => ctx.incrementCount(state),\n testIcon: (state) => ctx.testIcon(state)\n })\n}\n\nexport function wrapBrowserOnce(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): void {\n if (!ctx.isScriptInjected) {\n ctx.browserProxy.wrapUrlMethod(browser)\n ctx.isScriptInjected = true\n }\n ctx.browserProxy.resetTestTracking()\n ctx.browserProxy.wrapBrowserCommands(browser)\n}\n\nfunction closeUnreportedRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n results: NightwatchTestResults,\n processedTests: Set<string>\n): void {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest || processedTests.has(runningTest.title)) {\n return\n }\n const failed = (results.errors ?? 0) > 0 || (results.failed ?? 0) > 0\n const testState: TestStats['state'] = failed\n ? TEST_STATE.FAILED\n : TEST_STATE.PASSED\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n ctx.testManager.updateTestState(runningTest, testState, endTime, duration)\n ctx.testManager.markTestAsProcessed(testFile, runningTest.title)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n}\n\nasync function closeReportedTestcases(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n testcases: Record<string, NightwatchTestCase>,\n processedTests: Set<string>\n): Promise<void> {\n const testcaseNames = Object.keys(testcases)\n const unprocessedTests = testcaseNames.filter(\n (name) => !processedTests.has(name)\n )\n for (const currentTestName of unprocessedTests) {\n const testcase = testcases[currentTestName]\n const testState = determineTestState(testcase)\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n const dur = parseFloat(testcase.time || '0') * 1000\n ctx.testManager.updateTestState(test, testState, new Date(), dur)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${currentTestName} (${(dur / 1000).toFixed(2)}s)`)\n }\n ctx.testManager.markTestAsProcessed(testFile, currentTestName)\n }\n if (processedTests.size === testcaseNames.length) {\n ctx.suiteManager.finalizeSuite(currentSuite)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n }\n}\n\nexport async function closeOutTestcases(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentTest = (browser.currentTest ?? {}) as NightwatchCurrentTest\n const results: NightwatchTestResults = currentTest.results ?? {}\n const moduleName = currentTest.module ?? ''\n const testFile = moduleName.split('/').pop() || DEFAULTS.FILE_NAME\n const testcases = results.testcases ?? {}\n const currentSuite = ctx.suiteManager.getSuite(testFile)\n if (!currentSuite) {\n return\n }\n const processedTests = ctx.testManager.getProcessedTests(testFile)\n if (Object.keys(testcases).length === 0) {\n closeUnreportedRunningTest(\n ctx,\n currentSuite,\n testFile,\n results,\n processedTests\n )\n } else {\n await closeReportedTestcases(\n ctx,\n currentSuite,\n testFile,\n testcases,\n processedTests\n )\n }\n}\n","import fs from 'fs'\nimport path from 'node:path'\nimport { findTestFileFromStack } from './utils.js'\n\n/**\n * Resolve a Nightwatch test's `currentTest.module` to an absolute spec-file\n * path on disk. Priority:\n * 1. Walk the runtime stack for a user frame.\n * 2. A cached path from a previous command on the same browser (browserProxy).\n * 3. Cartesian search across the user's `src_folders` + cwd fallbacks.\n *\n * Used by `beforeEach` to find the file that `extractTestMetadata` should\n * parse for test names + suite/test line numbers. Returns `null` when the\n * file can't be located on disk (source view falls back to \"unavailable\").\n */\nexport function resolveSpecFilePath(\n testFile: string,\n modulePath: string | undefined,\n srcFolders: string[],\n cachedPath: string | undefined\n): string | null {\n let fullPath: string | null = findTestFileFromStack() || null\n if (!fullPath && cachedPath && cachedPath.includes(testFile)) {\n fullPath = cachedPath\n }\n if (fullPath) {\n return fullPath\n }\n if (!testFile) {\n return null\n }\n\n const workspaceRoot = process.cwd()\n // `currentTest.module` is relative to a src_folder, e.g. `basic/ecosia`.\n // We try each src_folder + cwd-level fallback. Use `path.resolve` (not\n // `path.join`) so absolute src_folders entries — like\n // `path.resolve(__dirname, 'tests')` from a nightwatch.conf.cjs living\n // outside the package — bypass `workspaceRoot` correctly.\n const normalized = (modulePath || '').replace(/\\\\/g, '/')\n const srcFolderPaths = srcFolders.flatMap((sf) =>\n normalized\n ? [\n path.resolve(workspaceRoot, sf, normalized + '.js'),\n path.resolve(workspaceRoot, sf, normalized + '.ts'),\n path.resolve(workspaceRoot, sf, normalized + '.cjs'),\n path.resolve(workspaceRoot, sf, normalized)\n ]\n : []\n )\n const possiblePaths = [\n ...srcFolderPaths,\n // Treat module path as relative to cwd (works when src_folders isn't nested)\n ...(normalized\n ? [\n path.resolve(workspaceRoot, normalized + '.js'),\n path.resolve(workspaceRoot, normalized + '.ts'),\n path.resolve(workspaceRoot, normalized + '.cjs'),\n path.resolve(workspaceRoot, normalized)\n ]\n : []),\n path.resolve(workspaceRoot, 'tests', testFile + '.js'),\n path.resolve(workspaceRoot, 'test', testFile + '.js'),\n path.resolve(workspaceRoot, testFile + '.js')\n ]\n\n for (const candidate of possiblePaths) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n","import logger from '@wdio/logger'\nimport { TIMING, TEST_STATE } from '../constants.js'\nimport type { NightwatchTestCase, TestStats } from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestManager } from './testManager.js'\n\nconst log = logger('@wdio/nightwatch-devtools:closePreviousTest')\n\nexport interface ClosePreviousTestInput {\n runningTest: TestStats\n testFile: string\n /** `currentTest?.results?.testcases || {}` — Nightwatch's per-suite testcase results. */\n testcases: Record<string, NightwatchTestCase>\n testManager: TestManager\n /** Plugin-side pass/fail/skip counter — called once with the resolved final state. */\n incrementCount: (state: TestStats['state']) => void\n /** Plugin-side icon resolver for the log line (✅/❌/⏭ etc.). */\n testIcon: (state: TestStats['state']) => string\n}\n\n/**\n * Close out the previously-running test when beforeEach fires for the next\n * one. Resolves the final state from Nightwatch's testcases bag if available\n * (preferred — has real timing), otherwise assumes PASSED (the runner only\n * advances to beforeEach if the prior test didn't throw). Returns nothing —\n * pure side-effect orchestration over the test, testManager, and the plugin's\n * counter / icon helpers.\n *\n * Extracted from `NightwatchDevToolsPlugin.beforeEach` — the ~37-line block\n * was the second-densest section of the 142-line method.\n */\nexport async function closePreviousTest(\n input: ClosePreviousTestInput\n): Promise<void> {\n const {\n runningTest,\n testFile,\n testcases,\n testManager,\n incrementCount,\n testIcon\n } = input\n\n if (testcases[runningTest.title]) {\n const testcase = testcases[runningTest.title]\n const testState = determineTestState(testcase)\n runningTest.state = testState\n runningTest.end = new Date()\n runningTest._duration = parseFloat(testcase.time || '0') * 1000\n testManager.updateTestState(runningTest, testState)\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(testState)\n log.info(\n ` ${testIcon(testState)} ${runningTest.title} (${(runningTest._duration / 1000).toFixed(2)}s)`\n )\n } else {\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n testManager.updateTestState(\n runningTest,\n TEST_STATE.PASSED as TestStats['state'],\n endTime,\n duration\n )\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(TEST_STATE.PASSED as TestStats['state'])\n log.info(` ✅ ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n }\n // Brief settle so the dashboard renders the terminal state before the next\n // test's \"running\" update arrives.\n await new Promise((resolve) => setTimeout(resolve, TIMING.UI_RENDER_DELAY))\n}\n"],"mappings":"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","names":["sessionId","logger","fs","path","logger","generateStableUid","log","logger","resolve","logger","logger","log","logger","path","webdriverGet","webdriverPost","logger","captureActionSnapshot","webdriverGet","log","logger","captureActionSnapshot","resolve","webdriverGet","logger","log","logger","generateStableUid","generateStableUid","logger","logger","log","logger","path","log","logger","path","logger","logger","log","logger","log","logger","sessionId","log","logger","webdriverGet","sessionId","resolve","logger","log","logger","sessionId","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","log","logger","sessionId"]}
|