feat(midnight-js): adopt graphql-transport-ws+deflate subscription compression#977
Open
sp-io wants to merge 19 commits into
Open
feat(midnight-js): adopt graphql-transport-ws+deflate subscription compression#977sp-io wants to merge 19 commits into
sp-io wants to merge 19 commits into
Conversation
… inflate helper Introduces inflate.ts with a single zlib (RFC 1950) decompression path via the Web Streams DecompressionStream global, covering Node >= 18 and modern browsers (Chrome 80 / Firefox 113 / Safari 16.4+). Includes a 16 MiB hard cap on inflated payload size to prevent compression-bomb DoS vectors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d clarify deflate mode
… malformed inflate input
…flate subprotocol
…in deflate negotiation
…iption frames Extends DeflateWebSocket with binary frame inflation, mixed-frame ordering via a promise queue, transparent fallback when server picks plain subprotocol, Node Buffer/Uint8Array normalization, and close-gate to avoid post-teardown unhandled rejections. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions and listener-object inputs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…to Apollo transport Wrap the user-supplied WebSocket with wrapWithDeflate() before passing to graphql-ws createClient, enabling transparent perMessageDeflate compression negotiation on the subscription connection. Updated transport.test.ts assertions: the wrapped WebSocket is a subclass of the user-supplied implementation, not an exact reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sumed by deflate-wrap prototype check Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ing guarantee in transport
…dArrayBuffer backing Explicitly check that view.buffer is a plain ArrayBuffer before slicing, rather than relying on TypeScript's union type assignment. This narrows the type and eliminates the TS2322 warning about SharedArrayBuffer lacking ArrayBuffer properties. We never expect SharedArrayBuffer-backed views from WebSocket frames in production, and slicing into a fresh ArrayBuffer requires plain ArrayBuffer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s and fix mock.calls indexing - Replace DOM WebSocket type with isomorphic-ws.WebSocket in all test casts (5 occurrences) - Fix transport.test.ts mock.calls.at(-1) indexing by properly typing the tuple - Add explicit toBeDefined() check to fail fast if spy wasn't called - Update beforeEach return type to match wrapWithDeflate signature Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Contributor
Unit Test Results - MidnightJS 28 files 120 suites 5m 46s ⏱️ Results for commit da2dd78. ♻️ This comment has been updated with latest results. |
…tance to prevent prototype pollution Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…istener path, logger, typing, and asserts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck to first inflate call
…, exports, deterministic test waits) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
E2E Tests Resultse2e_tests_reports: Run #3999
❌ Some tests failed!
Suites120 passed, 1 failed, and 4 other
Github Test Reporter by CTRF 💚 🔄 This comment has been updated |
5a173bc to
03debab
Compare
The `.catch` in counter.ts called `logger.error('Counter test failed:',
error)` — pino renders that as the prefix string with no serialization
of the error argument, so CI lost the message, stack, and cause when
the spawned ESM/CJS counter scripts rejected. The flaky
`nodejs.it.test.ts > should run ESM module successfully` was
diagnostically dead from the captured stderr alone.
Routing the error through pino's `err` serializer (`logger.error({ err
}, msg)`) captures `message`, `stack`, and the `cause` chain in the
structured log, so the next CI failure surfaces the actual reason
counter() rejected between wallet sync completion and api.deploy().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Closes #976.
webSocketImplwrapper that offersgraphql-transport-ws+deflatefirst in the negotiation, alongside the standardgraphql-transport-ws.graphql-wsusingDecompressionStream('deflate')— a single Web Standard code path covering both Node ≥18 and modern browsers (nonode:zlib, no new runtime dependencies). Browser baseline is Chrome 80 / Firefox 113 / Safari 16.4+ (March 2023).graphql-transport-ws. No public API change.cross-fetchHTTPAccept-Encodingguarantee intransport.tsso the upcoming indexer 4.4.0 HTTP-response compression also takes effect.Hardening
inflate.tsthrows at import time with a descriptive message ifDecompressionStreamis unavailable.reader.cancel()before allocating a giant buffer.toArrayBuffer()accepts browserArrayBufferand NodeBuffer/Uint8Arrayshapes (narrowed againstSharedArrayBuffer), so binary frames from the Nodewspackage are not silently passed through.try/catchinside the delivery queue drops a single malformed/oversized frame without poisoning subsequent frames or surfacing an unhandled rejection.close, preventing unhandled rejections during graphql-ws teardown / reconnect cycles.EventListenerObjectsupport —addEventListeneroverride wraps both function-style andhandleEvent-style listeners so object listeners are not bypassed.DeflateWebSocketbecause theonmessageaccessor relies on a fixed two-level prototype walk.Out of scope (follow-ups)
testkit-js-e2e— tracked separately, awaiting CI image availability.webSocketfor their own impl if needed.pako/fflate) — module-load fail-fast makes the missing capability loud; revisit only if dApp telemetry shows meaningful traffic from <16.4 Safari.Test plan
addEventListenerandonmessagepaths, text passthrough, mixed-frame ordering, fallback to plain protocol,binaryType = arraybufferenforcement, Node Buffer normalization, post-close drop, inflate-throws frame-drop without queue poisoninginflatehelper — DecompressionStream guard, round-trip RFC 1950, round-trip repetitive payload, malformed-input zlibZ_DATA_ERRORcause, compression-bombMAX_INFLATED_BYTESguardyarn lint),tsc --noEmitclean, package build clean (nonode:*imports in browser-targeted production code)4.4.0-pre-alpha.7-...) — follow-up via testkit-js-e2e once the image is available in CI