Repository navigation
fix: restore green CI — 23 type errors and 28 failing tests - #1038
arkhindi12 wants to merge 2 commits into
Conversation
`main` has not typechecked since 2026-10-31; CI has been red for 36 consecutive runs. `Lint & typecheck` and `Test` both fail, which skips `Build`, `@streamfi/react`, and the bundle-size check. Most of these are not type-only problems: several reference identifiers that do not exist in scope, so the affected paths could not have executed since they were merged. Two features that were merged were then silently dropped by a later merge and are restored here from their original commits. Typecheck: 23 errors -> 0 Tests: 28 failed / 1615 passed -> 0 failed / 1643 passed Lint: 14 errors -> 13 (none introduced) Runtime bugs fixed ------------------ * custom-fetch: `sendOnce` referenced `controller`, `timer`, `onCallerAbort` and `registration`, none of which existed in the file. The function could not compile, so every custom-fetch RPC request was dead code. Rebuilt the abort plumbing around a single AbortController so the caller's signal and the request timeout both cancel the in-flight fetch, and wired up `registration.timeoutMs`, which was stored but never read. * soroban: `getLatestLedgerInfo` read `ledger.closedAt`, which does not exist. `getLatestLedger` returns `closeTime` (Unix seconds, as a string) — verified against a live testnet node and the JSON-RPC schema. `new Date(undefined)` yielded NaN, so the returned ledger timestamp was always NaN. Read the correct field and fall back to now when it is absent. * client: `streams`, `governor` and `tokens` were declared `readonly` and then reassigned in `tryFailoverRpc()`. That did not compile, which means RPC failover could never run. Dropped `readonly` (the fields are genuinely mutable across failover) and guarded the index lookup against `noUncheckedIndexedAccess` widening it to `undefined`. * with-retry: a server-supplied `Retry-After` was parsed and then destroyed by jitter — `Math.random() * delay` meant a 2s directive could retry after 0ms, immediately re-triggering the 429. Jitter now applies only to client-guessed backoff, since `Retry-After` is a directive rather than an estimate. Features restored (merged, then dropped by a later merge) --------------------------------------------------------- * `simulateBatch` / `BatchPayload` / `BatchSimulationSummary` (conduit-protocol#959): added by conduit-protocol#1034, removed when conduit-protocol#1026 rewrote `batch-tx.ts` from an older base. * `StreamStatus` / `deriveStreamStatus`: added by 1662982, absent from `types/index.ts` on main. Restored verbatim. Both were re-added from their original commits rather than reimplemented. Overload fixes -------------- `onStreamEvent` in both `events.ts` and `streams.ts` declared overloads whose narrower single-parameter callbacks were incompatible with the implementation signature under `strictFunctionTypes`. The implementation now takes the union of the two callback shapes. Test fixes ---------- * streams.test.ts: three call sites used a 55-character contract address, so token validation failed before reaching the duration/rate assertions. Swapped for the valid 56-character address already used elsewhere in the file. * soroban-retry-after.test.ts: the no-header fallback test asserted an exact 500ms boundary while the default `full` jitter picks a random delay in [0, 500]. Stubbed Math.random instead of changing the production default. * relayer-stress.test.ts: the heartbeat tests awaited `connect()` while fake timers were installed, but the mock fires `onopen` via `setTimeout(0)` — which never ran, so `connect()` hung until the 5s test timeout. Now advance timers around the await. File runtime drops from 26s to 1s. * freighter-wallet-adapter.test.ts / batch-tx-simulate-batch.test.ts: mock and fixture shapes adjusted for `exactOptionalPropertyTypes`.
`Lint & typecheck` runs lint before typecheck, so the 13 pre-existing lint
errors on main kept the job red even after the typecheck step passed.
Lint: 13 errors -> 0 (warnings unchanged at 54)
* unused imports dropped: `StrKey` (constants), `SorobanRpc` (errors — it only
appeared in prose and the `SorobanRpcError` class name, never as a value),
`TopUpEvent` / `ClawbackEvent` (events), `BatchWithdrawItem` (streams).
* client: `require("./soroban.js")` inside `tryFailoverRpc` replaced with a
static import of `getConsecutiveTimeouts`. `soroban.ts` does not import
`client.ts`, so there is no cycle to protect against with a lazy require.
* walletconnect: `(...args: any[])` -> `unknown[]` on the emitter shims. The
listener bodies never touch the arguments.
* builder: `(builder as any)._startTime` -> a narrow
`{ _startTime: number }` cast, keeping the accessed shape explicit.
* signer: the lazily imported `@ledgerhq` modules now carry a local
`LedgerStrApp` interface matching `LedgerDevice`, plus an explicit guard on
`TransportWebUSB.create`. The previous `any` was hiding the fact that a
missing `create` would throw a TypeError rather than the intended
`LedgerHardwareError`.
* indexer: `while (true)` -> `for (;;)` in `fetchAll`. Same semantics, but the
loop no longer trips `no-constant-condition`.
* streams: the operation bag stays `Record<string, any>` with a justification
comment and a scoped disable. A concrete type here would have to be a union
of every operation variant, and each switch branch already narrows the keys
it reads — tightening it to `unknown` produced 16 errors.
Verified: tsc --noEmit clean, eslint 0 errors, vitest 1643/1643, build ok.
Cross-PR verification: both green togetherThe two PRs are mutually blocking in CI, so neither can prove the other:
To prove they are green together rather than just locally, I built a throwaway branch on top of 7/7 pass, including the Local state on that combined branch: Merge order
The two branches touch disjoint files ( I am closing the temporary verification PR now; it contains no changes of its own. |
mainhas not typechecked since 2026-10-31. CI has been red for 36 consecutive runs:Lint & typecheckandTestboth fail, which skipsBuild,@streamfi/react, and the bundle-size check.CI on this branch — every job that
mainnever reached now passes:mainhas never gotten pastLint & typecheck, soBuild,@streamfi/react, the bundle-size check and the E2E suite have been skipped on every run since 2026-10-31. This PR is the first branch to reach them.Reproduced locally with a clean
npm ci, and the local output matches the CI log line for line — so this is the repo, not an environment difference.Most of these are runtime bugs, not type noise
Several errors reference identifiers that do not exist in scope, which means the affected paths cannot have executed since they were merged.
custom-fetch.ts— the whole adapter was dead codesendOncereferenced four names that exist nowhere in the file:It builds its abort plumbing from
controller,timerandonCallerAbort, and callsregistration.fetcheven thoughregistrationis local tofetchAdapter. As written the function cannot compile, so every custom-fetch RPC request was unreachable.src/tests/custom-fetch-gateway-retry.test.tsfailed as a direct consequence and passes now.Rebuilt around a single
AbortControllerso the caller's signal and the request timeout both cancel the in-flight fetch (they cannot be two independent signals), and wired upregistration.timeoutMs— stored byregisterCustomFetchbut never read, so the documented per-registration timeout silently did nothing.soroban.ts— ledger timestamp was alwaysNaNgetLatestLedgerreturnscloseTime, notclosedAt. Verified against a live testnet node:{"jsonrpc":"2.0","id":1,"result":{"id":"5fe0c99c…","protocolVersion":30, "sequence":5555414,"closeTime":"1793800710","headerXdr":"…"}}@stellar/stellar-sdk12.3.0 omits the field fromGetLatestLedgerResponseentirely, which is why this typechecked in isolation but failed at runtime.new Date(undefined)givesNaN, sogetLatestLedgerInforeturnedtimestamp: NaNon every uncached call. Now readscloseTimethrough a narrowed cast and falls back tonowwhen absent.client.ts— RPC failover could never runstreams,governorandtokenswerereadonlybut reassigned intryFailoverRpc(). That does not compile, so the failover path was unreachable — the client would keep hammering a failing RPC URL forever. Droppedreadonly(the fields are genuinely mutable across failover) and guarded the index lookup, whichnoUncheckedIndexedAccesswidens tostring | undefined.with-retry.ts—Retry-Afterwas parsed, then destroyedRateLimitErroralready carriedretryAfterMsandwithRetryalready read it — then ran it through jitter:With the default
fulljitter, aRetry-After: 2directive could retry after 0ms — immediately re-triggering the 429 it was told to back off from. A server-suppliedRetry-Afteris a directive, not an estimate; jitter now applies only to client-guessed backoff.Two merged features had been silently dropped
Worth flagging separately, because it suggests a process gap rather than a one-off.
simulateBatch(#959) — added by #1034 (edc14d66, merged 2026-10-22 21:56), then removed when #1026 (9c71ec1, committed 19:10 the same day) rewrotebatch-tx.tsfrom an older base:index.tskept exporting it, so consumers importingsimulateBatchfrom the package root gotundefined— a silently broken public API rather than a loud failure.StreamStatus/deriveStreamStatus— same shape. Added by1662982("feat(types): add StreamStatus enum and deriveStreamStatus helper"), absent fromtypes/index.tson main, withstream-status.test.tsleft behind testing it (7 failures).Both are restored verbatim from their original commits rather than reimplemented, so the semantics are exactly what was reviewed and merged.
preBatchSimulateandBatchSimulationResultwere unchanged since, so the restore is a clean re-application.Overload fixes
onStreamEventinevents.tsandstreams.tsdeclared overloads whose narrower single-parameter callbacks were incompatible with the implementation signature understrictFunctionTypes(TS2394). The implementation now takes the union of the two callback shapes.Test fixes
Four of these were tests asserting something the code never promised:
streams.test.ts— three call sites used a 55-character contract address, so token validation threw before reaching thedurationSeconds/ratePerSecondassertions. Confirmed invalid viaStrKey.isValidContract; swapped for the valid 56-character address already used elsewhere in the same file. The validation order is correct as-is.soroban-retry-after.test.ts— the no-header fallback test asserted an exact 500ms boundary while defaultfulljitter picks a random delay in[0, 500], so it passed only by luck. StubbedMath.randomrather than changing the production default, since every otherwithRetrytest already pinsjitter: 'none'explicitly.relayer-stress.test.ts— the heartbeat tests awaitedconnect()with fake timers installed, but the mock firesonopenviasetTimeout(0), which never ran.connect()therefore hung until the 5s test timeout. Timers are now advanced around the await. File runtime: 26s → 1s.freighter-wallet-adapter.test.ts,batch-tx-simulate-batch.test.ts— mock and fixture shapes adjusted forexactOptionalPropertyTypes.@streamfi/reactstill fails — pre-existing and separateOnce
Lint & typecheckstarted passing,@streamfi/reactran for the first time and failed with 20 type errors across 10 files. This PR does not touchpackages/reactat all —git diff --name-only origin/main...HEAD | grep packages/reactis empty — and none of the errors involve code I changed.The headline failure is an unfinished feature:
StreamFiProviderreadsrpcHeadersand callsmakeHeaderFetch:Neither exists —
rpcHeadersis not a member ofStreamFiProviderProps(which has onlyconfig,defaultNetwork,children,queryClient), andmakeHeaderFetchappears nowhere in the repo. So the header-forwarding feature was merged as a call site without its prop or its helper. The remaining errors are the familiarexactOptionalPropertyTypesandrootDirshapes.Reproducible on a clean checkout of this branch:
I left this alone deliberately: fixing it means deciding what
makeHeaderFetchshould do (merge headers intoConduitConfig.customFetch?) and adding a public prop — a design decision for whoever owns the React package, not something to invent inside a CI-recovery PR. Say the word and I will take it as a follow-up.Lint errors cleared
Lint & typecheckruns lint before typecheck, so the 13 pre-existing lint errors would have kept the job red even with a clean typecheck. Cleared all 13:StrKey,SorobanRpc(only ever appeared in prose and theSorobanRpcErrorclass name),TopUpEvent,ClawbackEvent,BatchWithdrawItem.client.ts—require("./soroban.js")insidetryFailoverRpcbecame a static import ofgetConsecutiveTimeouts;soroban.tsdoes not importclient.ts, so no cycle needed the lazy form.walletconnect.ts—(...args: any[])→unknown[]on the emitter shims.builder.ts—(builder as any)._startTime→ narrow{ _startTime: number }cast.signer.ts— the lazily imported@ledgerhqmodules now carry a localLedgerStrAppinterface matchingLedgerDevice, plus an explicitcreateguard. The oldanywas hiding that a missingcreatethrew aTypeErrorinstead of the intendedLedgerHardwareError.indexer.ts—while (true)→for (;;)infetchAll.streams.ts— the operation bag staysRecord<string, any>with a scoped disable and a justification comment; each switch branch narrows the keys it reads, and tightening it tounknownproduced 16 errors.Warnings are unchanged at 54.
Verified on a clean checkout:
tsc --noEmitclean,eslint src --ext .ts0 errors,vitest run1643/1643 across 150 files,npm run buildsucceeds.