Commit bd1dce0
* feat(http): add uWebSockets.js request adapter (spike, #914)
Spike for evaluating uWS as a per-worker HTTP server on the plaintext-UDS
path behind symphony (TLS/mTLS/HTTP-2 terminated upstream). Adds:
- UwsRequest in Request.ts: a Harper request adapter modeled on BunRequest,
sourced from uWS-extracted method/url/headers/body. Real client IP comes
from X-Forwarded-For; peerCertificate/authorized are null (terminated
upstream).
- uwsServer.ts: createUwsServer(), a non-SSL uWS App on a unix socket that
bridges each request through httpChain[port] and serializes the Harper
response descriptor back onto the uWS HttpResponse.
Benchmarks (CPU-µs/request, vs Node http on the same UDS) show uWS holds a
~1.56x efficiency edge with the real Request abstraction in the loop. Not yet
wired into getUwsHTTPServer/threadServer.js; uWS is not yet a dependency.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(http): wire uWS UDS server behind HARPER_UWS_UDS flag (spike, #914)
Makes the per-worker plaintext-UDS mirror optionally served by uWebSockets.js
instead of a Node http server, gated behind the HARPER_UWS_UDS env flag
(default off -> no behavior change). When set:
- getHTTPServer registers a uwsServeConfigs entry for the UDS path instead of
creating the Node udsServer.
- makeUwsHandler mirrors the Bun fetchHandler's post-processing (httpChain,
unhandled, universalHeaders, Server-Timing, analytics, logging) and returns a
Harper response descriptor; createUwsServer serializes it onto the uWS res.
- threadServer.listenOnPorts() starts the uWS UDS servers from uwsServeConfigs.
- uWebSockets.js added as an optionalDependency (GitHub tag; ABI-locked, no
musl build -> CI must build per Node major).
Symphony must use sourceAddressHeader 'xForwardedFor' for these sockets (uWS
does not parse the PROXY protocol). Fastify status===-1 fallback and response
streaming are not wired in this spike. Type-checks clean (tsc --noEmit); not
yet exercised against a live booted Harper.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(http): null-guard request._nodeRequest in unhandled() (spike, #914)
unhandled() (the middleware-chain terminal) set request._nodeRequest.user when
an authenticated request hit no route, to hand auth to a Node fallback server.
_nodeRequest is null for both BunRequest and UwsRequest, so an authenticated
request to an unmatched route threw "Cannot set properties of null (setting
'user')" -> 500. Latent on the Bun path; surfaced by the live uWS-UDS bench.
Guard on _nodeRequest: the handoff only applies to the Node fallback path; the
Bun/uWS adapters have no Node fallback server. With this, the uWS UDS path
returns 404 like Node. Verified on a live booted Harper.
* fix(#914): harden uWS UDS adapter for production + add adapter unit test
Graduates the uWS-behind-symphony spike toward landing by fixing the
correctness issues surfaced in review and adding a regression suite.
- Request body corruption (critical): Buffer.from(arrayBuffer) aliased
uWS's receive buffer, which is neutered/reused once the onData callback
returns while the body is read asynchronously in the handler. Multi-chunk
POST/PUT bodies came back truncated/corrupt. Copy the bytes out
synchronously via Buffer.from(new Uint8Array(chunk)).
- Duplicate request headers were clobbered (headers[k] = v, last wins);
accumulate repeats into an array like the Node path.
- Empty reason phrase for uncommon status codes ("429 "); derive the
status line from node:http STATUS_CODES with an "Unknown" fallback.
- Route by method rather than a single app.any(hasBody:true) so bodyless
methods dispatch immediately and unknown methods can't stall a connection.
- Collapse of streaming/iterable response bodies now bails when the client
disconnects (thread the request AbortSignal into uwsBodyToBuffer).
- Refresh the stale adapter header comment (wiring is done).
Adds unitTests/server/serverHelpers/uwsServer.test.js: exercises GET,
bodyless OPTIONS, multi-chunk POST round-trip (guards the aliasing bug),
duplicate headers, 404, thrown->500, and reason-phrase serialization over
a real UDS. Skips gracefully when the uWebSockets.js optional dep is absent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#914): address cross-model review of uWS UDS adapter
Follow-up to the adapter hardening, resolving issues surfaced by the
cross-model review (Codex + Gemini + Harper-domain adjudication).
- WHATWG Response return path (significant): makeUwsHandler mutated
response.status/response.body, which throws for a handler that returns a
standard Response (read-only accessors) — a divergence from the Node/Bun
paths, which build a fresh descriptor. Return a new descriptor instead of
mutating the chain's result.
- Write-method throttling was dropped on the uWS UDS mirror: the Node UDS
path routes non-GET/OPTIONS/HEAD through the request-queue throttle (503 on
overflow), the uWS path bypassed it. Restore parity via throttle() so
data-modifying bursts shed instead of saturating a worker.
- QUERY (and other non-standard body-bearing methods) had their body
silently dropped: the per-method routing sent the any() fallback down the
bodyless path. Route known-bodyless methods explicitly and treat the
fallback as body-bearing (uWS still fires onData(len=0) for bodyless).
- Shutdown shim entered the Node keep-alive drain loop and force-exited
noisily every shutdown (uWS close() takes no callback): wrap close() to
invoke the callback and omit closeIdleConnections so the drain is skipped.
- UwsRequestBody now extends Readable, matching the RequestBody/BunRequestBody
contract (for-await async iteration + destroy(), not a duck-typed subset).
- Tidy: remove abort listener on the stream-error path in uwsBodyToBuffer,
drop the unused AbortController param from writeResponse, add the
uWebSockets.js optionalDependency to package-lock.json.
Adds QUERY-body-routing and 413-over-limit tests; suite now 9 green.
The WHATWG-Response, throttle, and shutdown-teardown paths live above the
adapter unit boundary — flagged for the integration bench in the PR.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#914): plaintext uWS-over-HTTP path + full streaming responses
Extends the uWS adapter beyond the symphony-UDS mirror toward a fully
capable HTTP backend.
Plaintext TCP path (HARPER_UWS_HTTP):
- createUwsServer now accepts a `port`/`host` (app.listen, SO_REUSEPORT by
default) in addition to `socketPath`, so uWS can back a non-secure HTTP
TCP port directly — not just the UDS mirror.
- getHTTPServer registers a uWS TCP config (and skips the Node server) for
non-secure app HTTP ports when HARPER_UWS_HTTP is set; threadServer's
start loop is generalized to UDS- or port-keyed configs.
- This is the flag used to run the integration suite through uWS: a
representative slice passes 45/45 (REST/SQL, data types, dates, arrays,
binary/Brotli blob responses byte-exact, Content-Encoding, caching).
Streaming responses:
- normalizeUwsBody (was uwsBodyToBuffer) now passes Node streams and
async-iterables through as a Readable instead of buffering — buffering an
SSE/event-stream body never returns.
- writeResponse streams a Readable body to uWS with real backpressure
(res.write + res.onWritable pause/resume) and omits Content-Length so uWS
uses chunked encoding. uWS only flushes headers on the first body write,
so text/event-stream responses emit a spec-valid ':\n\n' comment to open
the stream immediately (fixes SSE "headers never flushed"). Client abort
or a source error destroys the source and stops writing.
- Verified: MCP SSE integration test passes 4/4 (headers flushed up front);
3 new adapter unit tests cover SSE, a plain Readable, and a 4 MiB
backpressure stream. Suite now 12 green.
Remaining: WebSocket upgrade (MQTT-over-WS/subscriptions) — next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#914): WebSocket upgrade support on the uWS path
Completes uWS as a full HTTP+WS backend. uWS owns its sockets, so WS can't
be delegated to the ws library's WebSocketServer; instead the adapter uses
uWS's native app.ws() and bridges each connection to a ws-library-shaped
object that Harper's existing websocket chain consumes unchanged.
- UwsWebSocket (server/serverHelpers/uwsServer.ts): adapts a uWS WebSocket
to the subset of the ws interface Harper uses — send/close/terminate/ping,
'message'/'close' events, readyState, and a _socket shim exposing
remoteAddress + backpressure (writableNeedDrain/'drain' via
getBufferedAmount + the drain callback). Inbound frames are copied out of
uWS's neutered buffer.
- createUwsServer accepts a wsHandler; when set it registers app.ws('/*')
(capturing the upgrade request's url/headers/ip, IPv4-mapped address
normalized) alongside the HTTP routes — both coexist on one port.
- onWebSocket (server/http.ts) detects a uWS-backed port and wires the
wsHandler (build a WS UwsRequest, run httpChain auth, invoke
websocketChains) instead of the Node ws.WebSocketServer + 'upgrade' event.
Previously this crashed under HARPER_UWS_HTTP ("server.on is not a
function"), failing MQTT component load; also guards a NaN-port config.
Validated through the real harness: MQTT-over-WS passes 11/11 (RS256 JWT
auth, topic ACLs, pub/sub, $SYS monitoring); SSE 4/4 and HTTP unaffected
(24/24 combined). 2 new adapter unit tests (HTTP+WS coexistence on one port;
upgrade + text/binary frame round-trip); suite now 14 green.
With this, the full integration slice runs over uWS: HTTP, SSE/streaming,
and WebSocket subscriptions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#914): address cross-model review of plaintext/streaming/WS uWS work
Findings from the Codex+Gemini+domain review of the streaming/WS commits.
- Client IP on the direct-TCP path (P1, Codex+sweep): the uWS HTTP handler
never captured the peer address, so request.ip was '' and local auth
(security/auth.ts AUTHORIZE_LOCAL, request.ip.includes('127.0.0.')) failed
— anonymous localhost requests got "Must login". The integration sweep hit
this: early-hints/redirector/risk-query (pass on baseline, "pass 0" under
the flag). Fix: capture res.getRemoteAddressAsText() for the TCP path
(left unset for UDS). AND flip UwsRequest.ip to prefer the real socket
address over X-Forwarded-For, so a direct client can't spoof
`X-Forwarded-For: 127.0.0.1` to satisfy local auth; XFF is trusted only on
the symphony-UDS path (where the socket has no client address).
- HEAD body (P2, Codex): uWS has no ServerResponse HEAD guard, so a handler
returning a body on HEAD would send it. REST already nulls HEAD bodies;
enforce it in writeResponse for any other handler.
- WebSocket maxPayload (P2, Codex): the onWebSocket uWS branch didn't forward
options.maxPayload, so a configured smaller WS frame limit wasn't enforced
(defaulted to 100 MiB). Thread it through as wsMaxPayload.
Gemini's headline "Buffer.from(new Uint8Array(message)) aliases uWS memory"
blocker is a false positive (same conflation as last review): it COPIES —
proven (survives source neutering) and corroborated by MQTT-over-WS 11/11
with async frame processing.
Noted, not fixed (out of scope / parity): GraphQL POST reads _nodeRequest
which is null on uWS AND Bun (pre-existing non-Node-adapter gap, needs a
body-based deserialize); a raw Fastify server registered on a uWS-backed
port could collide in SERVERS (low reachability; MCP Fastify passes).
Integration sweep: 43/43 pass under HARPER_UWS_HTTP after the IP fix.
Adapter unit suite now 16 (adds request.ip + HEAD-suppression tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#914): refresh uwsServer header (TCP+streaming+WS, not UDS-only)
* fix(#914): deserialize GraphQL POST body via request.body
The GraphQL POST handler read the body from request._nodeRequest, the raw
Node IncomingMessage. That is null on the Bun and uWS request adapters, so
GraphQL POST 500'd off the Node path. Read through request.body instead —
a Readable-compatible body stream on every adapter, matching how REST.ts
already deserializes bodies. Verified 24/24 graphql integration tests on
both the Node and HARPER_UWS_HTTP paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#914): don't let raw-Fastify listeners collide with the uWS HTTP port
Under HARPER_UWS_HTTP the app port is backed by uWebSockets.js and getHTTPServer
early-returns a { uws: true } marker before it would have called
registerServer(server, port). SERVERS[port] therefore stays empty. If a legacy
Fastify-routes app is then deployed, fastifyRoutes registers its raw http.Server
via server.http(fastify.server); with the port looking unused, registerServer set
SERVERS[port] = fastifyServer and threadServer bound a Node http server competing
with uWS on the same TCP port (Codex P2).
Mirror the Bun path: divert non-function listeners on a uWS-backed port into the
fallback map instead of registerServer(), so nothing lands in SERVERS to double
-bind. Renamed bunFallbackServers -> fallbackServers since the map is now shared
by both non-Node backends. Request-time delegation to this fallback is not yet
wired on the uWS handler, so raw-Fastify routes are unreachable (clean, not a
competing bind) under this flag - an accepted limitation of the bench vehicle,
noted for a parity follow-up.
Verified: components.test.mjs (deploys a Fastify-routes component) 25/25 on both
the Node and HARPER_UWS_HTTP paths, with the Fastify registration diverting
cleanly and no bind collision.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#914): delegate to the Fastify fallback from the uWS HTTP path
Completes the raw-Fastify story on HARPER_UWS_HTTP. Previously a legacy
custom-function route (server.http(fastify.server)) was diverted to the fallback
map to avoid a competing bind, but the uWS handler had no way to reach it, so the
route 404'd. Now, when the chain doesn't handle a request (status === -1) and a
Fastify instance is registered for the port, the uWS handler delegates via
fastify.inject() — its internal router, no socket — mirroring the Bun path,
including SSE streaming and the AUTHORIZE_LOCAL pre-auth user forward.
- Extracted the shared inject core into injectToFastify() and routed both the Bun
and uWS delegation paths through it (strip forged pre-auth header, forward
resolved user when no Authorization, payloadAsStream for SSE).
- fastifyRoutes now registers its app instance for the http port(s); it only ever
registered the http.Server, so neither Bun nor uWS could delegate to legacy
routes. Renamed bunFastifyInstances -> fastifyInstances /
registerBunFastifyInstance -> registerFastifyInstance (shared, not Bun-only).
- UwsRequest exposes rawBody for the inject payload.
Verified: fastifyRoutes-test.mjs (GET /testApp/ping -> 'pong' + REST on the same
component) passes on BOTH the Node and HARPER_UWS_HTTP paths; under uWS the route
is served purely via inject-delegation. graphql 24/24, components 25/25,
mcp/sse-listchanged 4/4 under the flag; 16 uWS unit tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci(#914): run the full integration suite under HARPER_UWS_HTTP
Adds a run-integration-tests-uws job mirroring the existing Bun variant: the same
6-shard test:integration:all on Node 24, but with HARPER_UWS_HTTP=1 so the
plaintext app HTTP port(s) are served by uWebSockets.js. Secure/replication/ops
paths keep running on Node, so this gives continuous coverage of the uWS
request/streaming/WS/GraphQL/Fastify-fallback path across the whole suite instead
of relying on a manual local flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci(#914): make the uWS integration job informational (non-blocking)
The first full-suite run under HARPER_UWS_HTTP surfaced two known uWS-path gaps
(Bun and Node are green on the same tests):
- static-file serving via `send` never flushes headers on the uWS response
(client HeadersTimeout) — the deploy/static-access tests hang;
- multiple Set-Cookie headers collapse to one (the WHATWG Headers comma-join
limitation Harper-on-Bun already skips).
Neither is a regression from the Fastify-delegation work. Mark the job
continue-on-error so it reports the per-shard uWS signal without gating merges;
remove once the gaps are closed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#914): serve static files (send SendStream) on the uWS HTTP path
Static handlers return a `send` SendStream, which only begins work when piped to
a Node ServerResponse and writes its own headers there. The uWS path has no such
object: it treated the stream as a plain Readable and attached .on('data') (which
never starts a SendStream), and uWS only flushes status/headers on the first body
write — so static responses hung and the client saw a HeadersTimeout. This is why
every deploy+access integration test (deployed apps serve a static site) timed out
under the flag.
Pipe the SendStream into a Writable shim that captures the headers it writes
(setHeader/writeHead) onto the response Headers and buffers the file, mirroring the
Bun fetchHandler's SendStream path (incl. finished:false so on-finished doesn't
tear down early). Gated on handlesHeaders, which only static.ts sets, so real
streaming/SSE bodies keep streaming through normalizeUwsBody.
Verified: deploy/deploy-from-source.test.ts (deploys an app with a web/ static
site, polls the static index, asserts the served HTML) now passes 4/4 under
HARPER_UWS_HTTP — previously deploy+access both hung ~300s.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#914): preserve multiple Set-Cookie headers on the uWS HTTP path
A WHATWG Headers comma-joins Set-Cookie when iterated, which merges multiple
cookies into one and corrupts values containing commas (e.g. `expires=` dates).
The uWS response path iterated the headers directly (and writeResponse converted a
WHATWG Headers via `new Headers()`, comma-joining before serialization), so a
response setting N cookies reached the client as 1.
- writeHeaders now emits Set-Cookie individually via getSetCookie() when present
(WHATWG), skipping the joined entry; a Harper Headers stores them as an array,
already handled by the array branch.
- writeResponse keeps an existing Headers-like object (Harper or WHATWG) as-is
instead of round-tripping a WHATWG Headers through `new Headers()` (which would
comma-join before writeHeaders could split it), wrapping only plain objects.
- the Fastify-delegation path keeps Set-Cookie multi-valued instead of comma-
joining inject()'s array.
This is the multi-Set-Cookie limitation Harper-on-Bun documents and skips; uWS now
handles it correctly. Verified: headers.test.mjs 2/2 under HARPER_UWS_HTTP (was
0/2); graphql/components/mcp-sse/deploy-from-source/fastifyRoutes all green under
the flag; 16 uWS unit tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci(#914): gate on the uWS integration job (full suite now green)
The full test:integration:all suite passes on all 6 shards under HARPER_UWS_HTTP
(CI run 28724670219) now that the static-`send` and multiple-Set-Cookie gaps are
fixed, so the job no longer needs continue-on-error — make it a required check
alongside the Node and Bun variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#914): address cross-model review of the uWS Fastify/static/header work
Cross-model review (Codex + Gemini + Harper-domain adjudication) of the new uWS
work. Both outside-model legs led with false positives (request.headers.asObject
'undefined' → auth bypass, and Set-Cookie comma-coercion) — both refuted: headers
is a RequestHeaders with a real .asObject used across the REST path, and the uWS
Headers is Harper's Map-based class that preserves Set-Cookie arrays. The
INTERNAL_USER_HEADER pre-auth forward was probed and is spoof-safe (client-supplied
header is stripped before the user is re-added). Real items addressed:
- bufferSendStream no longer swallows send's status: capture statusCode / writeHead
status and return it, so a 304 (conditional GET) or 206/416 (Range) is honored
instead of flattened to 200. (End-to-end 304/Range is still gated upstream by
send not reading Harper's RequestHeaders — a pre-existing limitation on all
backends incl. Node, verified by probe; left as a separate follow-up.)
- avoid re-copying already-Buffer chunks when draining a delegated Fastify response.
- document the lowercased-'authorization' contract in injectToFastify.
- refresh the fallback-divert comment: request-time delegation IS now wired, and
the { uws: true } marker is guaranteed set by the getServer(port) call above.
Regression under HARPER_UWS_HTTP: deploy-from-source 4/4 (static), headers 2/2
(Set-Cookie), fastifyRoutes 2/2 (delegation), 16 uWS unit tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#914): stream uWS request bodies + address review comments
Feed the uWS request body into a push-based Readable and dispatch the
handler on headers instead of buffering the whole body and dispatching on
the last chunk. streamToBuffer (contentTypes.ts) already owns
concatenation and the HTTP_MAXREQUESTBODYSIZE limit and is the entry point
for the upcoming streaming deserializers, so the adapter no longer
concatenates (drops the O(n^2) Buffer.concat) or enforces its own body
limit; maxBodyBytes is demoted to a coarse socket-level DoS ceiling since
uWS offers no inbound backpressure. The Fastify-delegation path passes the
body stream to inject() (light-my-request consumes it), so rawBody is gone.
Also address review feedback:
- use when() so a synchronous handler stays synchronous (no extra promise)
- rename logBunRequest -> logHttpRequest (shared Bun/uWS path)
- correct the stale "WebSocket upgrades are not yet wired" comment
- reword the SPIKE/spike comments now that this is graduating
- document uWebSockets.js in dependencies.md
Adds a test asserting the handler is dispatched before the request body
ends (proves streaming, not full buffering).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(uws): guard 413 write against completed response + test XFF spoofing defense (#914)
Address cb1kenobi PR review:
- Track responseCompleted in onRequest and guard all three response-write
sites (handler result, error, 413). The handler can respond (or start
streaming) without consuming the body; a later over-limit 413 would then
write to an already-completed uWS response and abort the process.
- Add unit tests for request.ip trust boundary: a spoofed X-Forwarded-For
must not override the authoritative TCP peer address, while the UDS path
(no socket peer) still honors the trusted proxy's XFF.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: sync package-lock with merged package.json (prettier 3.9.5, globals 17.7.0, aws-sdk lib-storage 3.1076.0)
The npm-merge-driver left the lock resolved to the branch's older
versions while package.json took main's bumps, breaking npm ci.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style: reformat uwsServer.ts per prettier 3.9.5 (trailing comment placement)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Kris Zyp <kris@harperdb.io>
1 parent ba2a484 commit bd1dce0
12 files changed
Lines changed: 1496 additions & 65 deletions
File tree
- .github/workflows
- server
- serverHelpers
- threads
- unitTests/server/serverHelpers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
60 | 69 | | |
61 | 70 | | |
62 | 71 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
| 255 | + | |
255 | 256 | | |
256 | 257 | | |
257 | 258 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
47 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
48 | 57 | | |
49 | 58 | | |
50 | 59 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
559 | 559 | | |
560 | 560 | | |
561 | 561 | | |
562 | | - | |
563 | | - | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
564 | 565 | | |
565 | 566 | | |
566 | 567 | | |
| |||
0 commit comments