refactor(trace): extract the trace-format transforms into their own package, and export traces from the backend - #333
Merged
Conversation
Greptile SummaryThe PR extracts trace-generation transforms into a private
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains within the provided follow-up-review scope. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/backend/src/trace-export-message.ts | Adds parsing, dispatch, and response handling for worker-requested trace exports; no follow-up-eligible issue was established. |
| packages/backend/src/trace-export.ts | Converts accumulated backend run state into the trace package’s exporter input and writes the requested artifact. |
| packages/backend/src/baselineStore.ts | Extends active-run accumulation and snapshot access to support backend-side trace reconstruction. |
| packages/shared/src/trace-export.ts | Defines the shared request and result contract for trace-export worker messages. |
| packages/trace/src/trace-exporter.ts | Moves the existing trace assembly implementation into the new framework-independent trace package. |
| packages/service/vite.config.ts | Updates private-workspace-package handling so shared, core, and trace code remain bundled into the service distribution. |
| packages/backend/package.json | Adds the private trace package as a build-time dependency and keeps the CJS zip writer externalized as a runtime dependency. |
Sequence Diagram
sequenceDiagram
participant A as Adapter worker
participant B as Backend
participant S as Baseline store
participant T as Trace package
participant F as Trace artifact
A->>B: Captured event frames
B->>S: Accumulate active run
A->>B: traceExport request
B->>S: Read active-run snapshot
B->>T: Build and write trace
T->>F: trace.zip
B-->>A: Export result
Reviews (3): Last reviewed commit: "fix(backend): externalize yazl so the sh..." | Re-trigger Greptile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Two related changes, split into eight reviewable commits. Part of #301.
1. The trace transforms move to a new workspace-internal package,
@wdio/devtools-trace(commits 1–4).coreis the one layer every adapter needs and Python cannot reach — it isTypeScript. So everything it provides was written a second time in Python
(~1,200 lines that are not language-specific), and that gap has already cost
us: the force-anchor drain (#285), the recovery drain (#286) and document-start
preload (#284) were each fixed in the JS adapters and silently never reached
Python. Trace assembly is another ~2,000 lines; ported, it becomes the second
copy, and Java would make it the third.
The backend could host it once for every language, but
backend → coreisforbidden (
eslint.config.cjs, CLAUDE.md §2.2) because core is adapterlogic. Rather than weaken that rule, the transforms move below it: walking the
import graph, every one of them depends on nothing but
shared,yazlandnode builtins — none touches a driver or a framework, which is what made the
split possible at all. Layering is now
shared ← trace ← core ← adaptersandshared ← trace ← backend, enforced in ESLint.Adapter-side orchestration and policy stay in
core:trace-finalizer,spec-trace-helpers, andtrace-retention— which despite its name governsscreenshot and video retention too, so it is not a trace transform.
2. The backend can build a trace from the run it already accumulates
(commits 5–8).
Over the worker socket, using those same transforms. The JS adapters keep
calling them in-process, so backend-free trace mode (
launcher.ts: "Tracemode — skipping backend and Chrome window") is untouched and its output stays
byte-stable. This exists for an adapter that streams but cannot transform.
Type of change
Packages touched
shared(types and contracts)core(framework-agnostic capture/reporting)elements(published element/snapshot API —@wdio/elements)service(WebdriverIO adapter)nightwatch-devtools(Nightwatch adapter)selenium-devtools(Selenium adapter)backend(server)app(UI)script(page-injected runtime)selenium-devtools-pyPlus a new
tracepackage.serviceis config-only — its viteexternalcallback enumerated the private packages by name, so a fourth would have been
silently externalized and the dist would have died at install with
ERR_MODULE_NOT_FOUND. It is a list now.Notes for reviewers
No adapter source file changed. The 13 moved files are byte-identical to
their originals (verified by checksum), and
corere-exports them per modulerather than as one
export *so its public surface is provably unchanged —diffed module-by-module against the parent commit,
trace-transcriptstilldeliberately absent as before. Test count was identical either side of the
move.
This carries a refactor and a feature in one PR, which CLAUDE.md says
should be two. The seam is clean and verified — commits 1–4 pass on their own
(163 files / 2171 tests, typecheck and lint clean at
a3fb35b) — so it canstill be split if a reviewer would rather. Merging as one was a deliberate
call, not an oversight.
Two duplications dissolved by the move.
trace-reader-utils.tscarried alocal copy of
sha1Hexwhose comment named the cause ("the backend onlyimports from shared"), and hand-built the writer's
src@<sha1>.txttemplateinline. Those had to agree or source recovery in the viewer silently returns
nothing, with no test able to catch a rename on one side. Both now resolve to
sourceResourceName.One divergence from the #298 decision record, which listed
trace-retentionas moving: it stays incore, per the reasoning above.Known gaps in the backend export, both already tracked on #301.
actionSnapshotsdoes not travel (the exporter synthesizes from commandscarrying screenshots), and neither do
screencastFrames— thescreencastscope carries video metadata, not frames. A stream-fed export matches
in-process fidelity only once those travel. Nothing sends the export frame yet;
the Python side is #288.
yazlhad to become a runtimedependencyofbackend— the inverse ofthe workspace-internal rule, and worth knowing about. Importing the trace
writer pulled yazl into the ESM bundle, and yazl is CJS: esbuild rewrote its
requireinto a shim that threwDynamic require of "fs" is not supported,killing
dist/show-trace.json every invocation. Neitherpnpm build,pnpm testnor the workspace-leak grep noticed — all three pass on a dist that dieson first import.
packages/backend/tests/dist-bundling.test.tsnow asserts theshim is absent; verified by rebuilding without the fix and watching it fail.
Verification
pnpm test— 166 files, 2197 testspnpm test:ui— 31/31pnpm lint— 0 errors, no new warningspnpm build+tsc --noEmit— cleantraceincludedsources, console and network; accepting a partial frame; removing the
try/catch; deleting the dispatch branch)
pnpm show-tracerun against a real cucumber trace zip — player serves,/api/tracereturns 200Screenshots / recordings
Not applicable — no UI change.