-
-
Notifications
You must be signed in to change notification settings - Fork 0
Replace Web Worker with subprocess-based execution architecture #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR replaces the Web Worker-based execution architecture with a subprocess-based approach to address critical limitations in Deno's Worker API, particularly around module resolution and CLI flag propagation.
Key Changes:
- Implemented subprocess execution infrastructure with NDJSON-based protocol for bidirectional communication
- Added template resolution system that embeds dependencies at build time for distributable CLI
- Migrated
runandlistcommands to use isolated Deno subprocesses instead of Web Workers
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
src/cli/subprocess.ts |
Core subprocess spawning utilities with NDJSON stream processing |
src/cli/subprocess_template.ts |
Template resolution system that converts bare specifiers to versioned URLs |
src/cli/_templates/run.ts |
Subprocess entry point for scenario execution with stdin/stdout communication |
src/cli/_templates/list.ts |
Subprocess entry point for scenario listing |
src/cli/_templates/run_protocol.ts |
Protocol definitions and serialization for run command communication |
src/cli/_templates/list_protocol.ts |
Protocol definitions for list command communication |
src/cli/_templates/utils.ts |
Shared subprocess utilities for stdin/stdout and logging configuration |
src/cli/_templates/README.md |
Documentation for subprocess template architecture |
src/cli/commands/run.ts |
Refactored to spawn subprocess instead of Worker for scenario execution |
src/cli/commands/list.ts |
Refactored to use subprocess for scenario loading and filtering |
src/cli/utils.ts |
Added extractDenoOptions function to extract --deno-* CLI flags |
scripts/build_subprocess_templates.ts |
Build script that generates embedded templates for compiled binaries |
src/cli/_embedded_templates.ts |
Auto-generated file containing resolved template sources |
src/cli/commands/run/worker_test.ts |
Removed legacy Worker tests (~831 lines) |
src/cli/commands/run/worker.ts |
Removed legacy Worker implementation (~231 lines) |
src/cli/_templates/run_protocol_test.ts |
Updated import path from old protocol location |
deno.json |
Added dependencies and compile task for subprocess template embedding |
deno.lock |
Updated lockfile with new dependencies |
.gitignore |
Added patterns for dist/ and embedded templates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8a7f2a6 to
b8b3b48
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 19 out of 21 changed files in this pull request and generated 9 comments.
Comments suppressed due to low confidence (1)
src/cli/_templates/run_protocol.ts:258
- The createReporter function accepts a synchronous callback (output: (message: RunOutput) => void), but this is being passed an async function (writeOutput) in run.ts. This type signature mismatch means errors from writeOutput won't propagate correctly, and the Reporter won't know if IPC writes fail. Consider changing the signature to accept an async callback or handling the promise appropriately.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 19 out of 21 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Introduces subprocess execution architecture to replace Web Workers, enabling better process isolation and dependency management. New Components: - subprocess.ts: Core subprocess spawning and NDJSON communication - subprocess_template.ts: Template resolution and embedding system - _templates/: Worker templates for run and list commands - _embedded_templates.ts: Pre-built template data for CLI distribution - build_subprocess_templates.ts: Build script for template embedding Why Subprocess over Worker: Web Workers cannot resolve bare specifiers dynamically, requiring all dependencies to be bundled. Subprocesses allow each worker to use `deno run` with proper module resolution, simplifying dependency management and enabling independent Deno permission control. Technical Details: - NDJSON protocol for structured subprocess communication - Template system resolves import maps before embedding - Ready-signal pattern ensures subprocess initialization - Graceful error propagation via ErrorObject serialization
…ution Refactors run and list commands to use the new subprocess infrastructure, removing all Web Worker-related code. Changes: - run.ts: Replace Worker with subprocess spawning and NDJSON streams - list.ts: Add subprocess-based scenario loading with filtering - utils.ts: Add extractDenoOptions() for --unstable-* flag support - Delete run/worker.ts, run/worker_test.ts (replaced by _templates/run.ts) - Delete run/protocol.ts, run/protocol_test.ts (moved to _templates/) Why This Change: The Worker-based approach required complex serialization boundaries and couldn't pass Deno flags to workers. Subprocess approach enables natural flag forwarding (--unstable-kv, permissions) and simplifies error handling through structured NDJSON communication.
…or subprocess Adds required dependencies and build tooling configuration to support the subprocess-based execution architecture. Changes: - deno.json: Add subprocess dependencies (@deno/graph, @std/streams, etc.) - deno.json: Add "compile" task for template embedding and CLI build - .gitignore: Exclude dist/ and generated _embedded_templates.ts - deno.lock: Update lock file with new dependencies Why These Dependencies: - @deno/graph: Analyze and resolve module dependencies for templates - @std/streams, @std/json: NDJSON communication with subprocesses - @core/streamutil: Stream utilities for subprocess I/O
…P sockets The previous stdin/stdout-based IPC prevented subprocesses from using console.log freely, as it would corrupt NDJSON message streams. This migration enables: - Subprocess console output for debugging (stdout/stderr inherited) - Parent-subprocess communication via dedicated TCP channel - Elimination of "ready" signal (connection itself signals readiness) Technical changes: - Subprocess connects to parent's ephemeral TCP server (--ipc-port arg) - NDJSON protocol unchanged, transport changed from stdio to TCP - IpcConnection abstraction provides typed read/write streams - Exclusive writer usage prevents concurrent write corruption
…ementation - Make createReporter methods async to properly await IPC writes, ensuring message ordering and error propagation - Add explicit process exit to list.ts subprocess to prevent hangs when async operations keep event loop alive - Update documentation comments to reflect TCP-based IPC (not stdin/stdout)
High priority fixes: - Add response.ok check for fetch in subprocess template resolution - Add IPC connection timeout with subprocess exit detection to prevent indefinite hangs when subprocess crashes before connecting Medium priority fixes: - Validate --deno-* options require =value syntax for non-boolean flags - Clarify misleading comment about allScenarios vs filteredScenarios - Improve abort result comment explaining count approximations
3e4c196 to
47c8fb5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 19 out of 21 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove unnecessary type casting in list.ts (use spread instead) - Clarify comment about specifier resolution in subprocess_template.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 19 out of 21 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
JSON.stringify cannot handle various JavaScript types (BigInt, Symbol, Function, circular references, etc.), which could cause failures in IPC-based step context passing. These scenarios verify the framework handles these edge cases correctly. Covers: BigInt, Function, Symbol, undefined, circular references, Map, Set, RegExp, Date, Error, TypedArray, ArrayBuffer, DataView. Addresses #80
Standard JSON.stringify fails on BigInt, circular references, Symbol, Function, Map, Set, RegExp, TypedArray, and other JavaScript types. This causes step context values to be lost or corrupted when passed between processes via IPC. The custom serializer uses a marker object pattern ($type property) to preserve type information across the IPC boundary, enabling steps to receive BigInt, Map, circular references, and other complex values through ctx.previous and ctx.results. Handles: BigInt, Symbol, Function, undefined, circular references, Map, Set, WeakMap, WeakSet, RegExp, Date, Error, TypedArray, ArrayBuffer, DataView, WeakRef. Addresses #80
JSON.stringify cannot handle various JavaScript types (BigInt, Symbol, Function, circular references, etc.), which could cause failures in IPC-based step context passing. These scenarios verify the framework handles these edge cases correctly. Covers: BigInt, Function, Symbol, undefined, circular references, Map, Set, RegExp, Date, Error, TypedArray, ArrayBuffer, DataView. Addresses #80
Standard JSON.stringify fails on BigInt, circular references, Symbol, Function, Map, Set, RegExp, TypedArray, and other JavaScript types. This causes step context values to be lost or corrupted when passed between processes via IPC. The custom serializer uses a marker object pattern ($type property) to preserve type information across the IPC boundary, enabling steps to receive BigInt, Map, circular references, and other complex values through ctx.previous and ctx.results. Handles: BigInt, Symbol, Function, undefined, circular references, Map, Set, WeakMap, WeakSet, RegExp, Date, Error, TypedArray, ArrayBuffer, DataView, WeakRef. Addresses #80
Summary
runandlistcommands to use subprocesses instead of Web WorkersWhy
Web Workers in Deno have critical limitations for our CLI use case:
Problem with Workers:
Subprocess Benefits:
deno runwith full module resolution--unstable-*flags propagate to scenario executionTechnical Implementation:
Test Plan
deno task verifyto confirm no regressionsprobitas runwith --unstable-kv flagprobitas listwith selectors