Add browser-compatible Worker polyfill and WPT coverage - #8
Draft
matthargett wants to merge 37 commits into
Draft
Conversation
matthargett
force-pushed
the
feature/web-worker
branch
16 times, most recently
from
July 21, 2026 09:41
167128e to
2eb0e3b
Compare
matthargett
force-pushed
the
feature/web-worker
branch
from
July 22, 2026 08:12
2eb0e3b to
e303855
Compare
matthargett
force-pushed
the
feature/web-worker
branch
from
July 22, 2026 08:19
e303855 to
b848ded
Compare
matthargett
force-pushed
the
feature/web-worker
branch
from
July 26, 2026 09:57
f1c3052 to
ce0e0d2
Compare
matthargett
force-pushed
the
feature/web-worker
branch
from
July 26, 2026 10:27
ce0e0d2 to
325600d
Compare
matthargett
force-pushed
the
feature/web-worker
branch
from
July 26, 2026 11:33
c689930 to
32e5a97
Compare
Exercise the deployed webapp's module-worker startup contract with an app-derived integration fixture, provide the cache-oriented in-memory IndexedDB subset needed by pipelineCreate, accept WHATWG-normalized app:/ worker URLs, and preserve actionable startup errors across engines. Also separate graceful WorkerGlobalScope.close() from forced parent termination so same-task messages and errors remain observable.
Vendor the ES5 web-streams-polyfill 4.3.0 ponyfill and expose an idempotent Streams initializer that preserves constructors supplied by the selected JavaScript engine. Cover readable, writable, transform, BYOB, error, tee, subclassing, and host-constructor behavior with focused ports from WPT plus Firefox and Chromium regression tests. Validate the implementation on JavaScriptCore under ASan/UBSan and QuickJS Release.
Add optional CompressionStream and DecompressionStream polyfills for gzip, deflate, and raw deflate on top of WHATWG Streams. Reuse one native output buffer per active codec, borrow input views only synchronously, and defer JavaScript enqueue callbacks until zlib has finished consuming each input. Cover constructor and BufferSource behavior, split and empty chunks, large flushes, corrupt/truncated/trailing input, reentrant input mutation, and repeated stream teardown with focused WPT and browser-engine regression tests. Preserve host constructors and use platform zlib where available with a pinned fallback.
Implement iterable BlobPart conversion, MIME and endings normalization, zero-copy Blob composition and slicing, and lazy 64 KiB byte streams. Delegate File streaming and slicing to its backing Blob while preserving browser class identity.\n\nUse immutable shared segments so nested Blob and slice construction do not duplicate payload bytes. Add focused WPT, WebKit, and Firefox regression coverage for constructor ordering, iterator closure, UTF-8 decoding, BYOB reads, cancellation, lifetime safety, and large nested streams.
Replace the ad hoc buffered fetch response object with standard-shaped Headers and Response classes backed by ReadableStream bodies. Preserve host constructors, normalize and validate header values, enforce single-use bodies, and expose native responses with one transport-to-JavaScript copy. Reuse normalized header views between mutations, compact header storage in place, retain stream chunks only until consumption, and allocate a contiguous body result at most once when required. Add focused WPT, WebKit, Firefox, and Chromium regression coverage plus JavaScriptCore/QuickJS and initialization tests.
Close byte streams for zero-length BufferSource bodies without enqueueing an invalid empty chunk. Preserve the non-null body and bodyUsed semantics, and add focused regression coverage alongside the WPT empty-response cases.
Resolve percent-encoded and base64 data URLs without entering the network transport. Use a validating WHATWG-style forgiving-base64 decoder, strip URL fragments from payloads, and preserve response metadata through the standard Response path. Decode into pre-sized native storage and copy the completed body once into JavaScript-owned memory. Cover representative WPT data URL and forgiving-base64 vectors, plus Chromium whitespace cases.
Feed percent-decoded bytes directly into validating and decoding passes for Base64 data URLs. Allocate the final native payload at its exact size, avoiding the previous percent-decoded staging vector and any vector growth before the single JavaScript ownership copy.
Replace a partial or null host Streams surface as a complete constructor suite so stream products retain compatible instanceof relationships. Preserve a complete suite on repeated initialization.\n\nHarden the native initialization test so N-API failures complete the test promise instead of hanging, and cover partial host replacement plus null handling and cross-constructor compatibility.
Use ReadableByteStreamController.byobRequest views directly and respond with the produced byte count instead of allocating and enqueueing a 64 KiB intermediate chunk. Keep the existing bounded allocation path for default readers.\n\nAdd WPT-derived coverage for small offset BYOB views that cross immutable Blob segment boundaries.
Observe standard ReadableStream consumption paths with weak per-stream state instead of depending on web-streams-polyfill's private _disturbed field. Instrument stream readers and piping once during Fetch initialization, preserve stream identity, and cache the initialized Fetch implementation so repeated initialization does not stack wrappers or replace its internal state. Treat Headers and Response as an implementation pair when either host global is missing or null. Harden native initialization tests against unhandled N-API failures and add WPT-derived coverage for reads, cancellation, piping, and streams disturbed before Response construction.
matthargett
force-pushed
the
feature/web-worker
branch
from
July 26, 2026 12:01
32e5a97 to
18b0b60
Compare
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.
Dependency and stack
This is the Worker follow-up to BabylonJS/JsRuntimeHost#189. It is built on the refreshed
napi-v7head (231fa40), which incorporates the issue-7 Node-API work, the latest audited CTS updates, and the deterministic JavaScriptCore follow-up collection needed by the CTS finalizer contract. The refreshed base is merged as a parent so the existing Worker review history remains intact; the resulting tree exactly matches the locally tested restack.The Worker branch composes the independently reviewable browser-polyfill work while those PRs are pending:
Worker initializes those targets in browser-compatible dependency order. It no longer embeds reduced private copies of Streams, Blob.stream, Response, decompression, or IndexedDB, and no longer links zlib itself.
Browser-compatible Worker surface
AppRuntime, engine realm, and event loop per dedicated Worker.Worker,EventTarget,Event,MessageEvent,ErrorEvent,DOMException, handler attributes,postMessage, andterminate.DedicatedWorkerGlobalScopewithself,location,name,navigator,close, synchronousimportScripts, timers, fetch/XHR, URL, Blob/File, text codecs, WebSocket, performance, abort APIs, Streams, CompressionStream, and IndexedDB.DataCloneErrorpaths.app:///and relative scripts/assets, explicitfile://, percent-encodeddata:, and a thread-safe packaged-asset resolver.System JavaScriptCore uses its execution-time-limit ABI to interrupt top-level infinite evaluation. No JSC binary is downloaded or vendored: the Linux lanes install the OS
libjavascriptcoregtk-4.1-devpackage. QuickJS remains the additional race/conformance test engine.Visualization startup and playback integration
The reduced integration fixture is pinned to rebeckerspecialties/webapp@3765b13, specifically
GithubPortfolio.worker.ts, its parent interop, andIndexedDBCache.The source trace found two blockers beyond the base Worker interface:
pipelineCreatesynchronously constructs the IndexedDB cache, so a missingindexedDBstops startup before playback can fall back to live data.${owner}.${repo}.gz, then runsBlob.stream().pipeThrough(new DecompressionStream("gzip")), constructs aResponse, parses the cache, and writes the records in one IndexedDB transaction.The fixture constructs a named
{ type: "module" }Worker, queuespipelineCreateandsubscribewhile the bundle is evaluating, hydratesrebeckerLoaderCacheStore, and plays three 128-item streams. It verifies relative fetch, gzip rejection behavior, transaction completion, Date/Map/Set/cyclic/shared-alias clone fidelity, 384 updated items, abort reasons, and browser-style constructor feature detection.The deployed GitHub Pages origin itself was not launched in this environment, so this is a source-pinned, app-derived integration test rather than a claim about an end-to-end XR render. The tested contract reaches
pipelineCreated, emits the three playback streams, and preserves the data required by the XR consumer.Browser regression hardening
The regression fixtures carry their source links and guard known browser implementation failures:
close()behavior: same-task messages/errors surviveclose(); later tasks are discarded.postMessage()is queued and delivered asynchronously.The JSC sanitizer pass also exposed a per-stream implementation cost in
Blob.stream(): capturing node-addon-api callbacks attach hidden callback-data properties to each generated function, causing two extra wrappers and JSC Function structure transitions per stream. The standalone Blob branch now uses a shared internal stream-source prototype, so its nativepull/cancelcallbacks are created once per realm instead of once per stream; the same commit is composed here and keeps the existing BYOB, cancellation, and lifetime tests.The restack also caught an integration-specific variant of the same “polyfill replaces a host primitive” antipattern: Worker event glue replaced IndexedDB’s existing
DOMException, breakingDataCloneError instanceof DOMException. Worker now preserves a host constructor, with a dedicated native regression test.Tests
The vendored Worker subset pins web-platform-tests/wpt@4809b72, its license, and
testharness.js.Fresh local QuickJS results after the restack:
The general JavaScript suite was also started, but this container’s external XHR cases block on network access before reaching the remaining suites; the focused pass above runs the complete changed-feature set. GitHub Actions supplies the system-JSC, ASan/UBSan, and QuickJS-TSan gates.
Deliberate first-cut boundaries
type: "module"accepts self-contained script-compatible bundles; imports/exports must be flattened because public JSC has no module-loader hook.unhandledrejectionsynthesis, SharedArrayBuffer, MessagePort transfer, Blob object URLs, nested Workers, and full module-graph loading are not included yet.