Repository navigation
fix(frameworks): type scanner entrypoints and cancel scans on disposal - #256
Merged
truthixify merged 3 commits intoOct 17, 2026
Conversation
Framework adapters drifted from the SDK's typed exports: sdk-react cast Horizon balances, scanner options, the shared cache global and caught errors to `any`, sdk-vue selected chain URLs and key fields through repeated `as any`, and none of the four adapters aborted work when its consumer was disposed. - Export `ScanCancellationOptions`, `getUnknownErrorMessage` and `isAbortError` from the package root so React, Vue, Svelte and Solid share one cancellation shape and one way to narrow caught values. - sdk-react: type the scanner cache global, read Horizon balances through `Horizon.HorizonApi.BalanceLine`, drop the remaining casts, narrow caught values, and abort on unmount and dependency changes so stale results cannot reach published state. Also fix `IDBCache`'s in-memory `commit()`, which referenced IndexedDB-only fields and threw without `indexedDB`. - sdk-vue: model scanner options as a discriminated union by chain, pair each chain with its own announcement and key types, keep runtime `TypeError` guards for JavaScript consumers, and abort on effect scope disposal. - sdk-svelte and sdk-solid: accept the same signal, abort on `dispose()` or owner cleanup, and discard superseded results. - Add compile-time tests for invalid scanner options and runtime tests for unmount/scope/root disposal and stale results across all four adapters. - Point the framework agent hooks at `@wraith-protocol/sdk-agent` and type-check the framework packages in CI. Closes wraith-protocol#243 Closes wraith-protocol#244 Closes wraith-protocol#245
|
@Moonwalker-rgb Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
The SSR compatibility harness listed sdk-solid, sdk-svelte and sdk-vue as known import failures because they still pulled `Wraith`, `WraithAgent` and `Chain` from the core package after the agent client moved to `@wraith-protocol/sdk-agent`. They now import the agent client from its own package, so the harness failed with "imports cleanly now; remove it from KNOWN_IMPORT_FAILURES". Drop the entries and let the check import and render all three companions server-side.
The four framework adapters now share one cancellation option shape and a stale-result guarantee that callers have to reason about, and the Vue scanner API changed shape. Add a guide covering the per-framework lifecycle, the abort semantics (AbortError, no error state, superseded results), which chain requests actually cancel, the chain-discriminated Vue options and the positional-argument migration, and link it from the README and changelog.
truthixify
approved these changes
Oct 17, 2026
truthixify
left a comment
Contributor
There was a problem hiding this comment.
Framework scanner cancellation passes the focused React, Vue, Solid, and core build checks. Approved.
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 #243
Closes #244
Closes #245
The four framework adapters —
@wraith-protocol/sdk-react,@wraith-protocol/sdk-vue,@wraith-protocol/sdk-svelteand@wraith-protocol/sdk-solid— had drifted away from the typed SDK exports, and none of them cancelled work when their consumer went away. This PR replaces the casts with SDK types, makes the Vue scanner options chain-discriminated, and gives all four adapters one cancellation contract with a stale-result guarantee.sdk-react— noanyleft in the network hooks; typed scanner options, typed Horizon balances, shared error narrowing, and abort on unmount / dependency change.sdk-vue— scanner and activity inputs are a discriminated union by chain; mismatches are compile-time errors in TypeScript andTypeErrors at runtime for JavaScript consumers.sdk-svelte/sdk-solid— same cancellation shape, abort on owner disposal, superseded results discarded.sdk-vue's declaration build; they now use@wraith-protocol/sdk-agent, and the SSR compatibility check imports and renders all three packages instead of xfailing them.#243 — Remove unsafe
anyfromsdk-reactnetwork hooksDone when (from the issue):
UseScannerOptions.chainis'stellar'(React's scanner reads Stellar announcements only); balances are read throughHorizon.HorizonApi.BalanceLineinstead of(b: any)packages/sdk-react/test/useScanner.test-d.tsgetUnknownErrorMessage()andisAbortError()are exported from the package root and used inhooks.ts,hooks/useScanner.tshooks.test.tsx,useScanner.test.tsxpackages/sdk-react/test/useScanner.test-d.tspnpm test:typesCompile-time tests reject unsupported chains, non-string wallets, non-numeric
fromLedgerand non-AbortSignalsignals:Two behaviour fixes came out of this work:
useStellarNamedefaulted tochain = 'testnet', but the only Stellar deployment key is'stellar', sogetDeployment()threw on every default call. The default is now'stellar'.IDBCache's in-memory fallback (used wheneverindexedDBis unavailable, including SSR) had acommit()copied from the IndexedDB path that referencedthis.db,this.storeNameandthis.fallback— none of which exist onMemoryCache. It threw at runtime and failedtsc; it now delegates toset()and advances the watermark.#244 — Make
sdk-vuescanner and activity keys chain-discriminatedDone when (from the issue):
ScannerFetchOptions = EvmFetchOptions | StellarFetchOptions | SolanaFetchOptions;ScannerScanInput,ScannerScanWithKeysInput,ScannerCheckInputare unions overEvmStealthKeys/StellarStealthKeys/SolanaStealthKeyspackages/sdk-vue/test/scanner.test-d.tsscanAnnouncements,checkAddress,scanWithKeysscanner.test-d.ts(@ts-expect-errorcases)assertScannerChain,assertScannerFetchOptions,assertScannerScanInput,assertScannerScanWithKeysInput,assertScannerCheckInputuseScanner.test.ts,useActivity.test.tsMismatches are rejected in both directions:
useActivity().scanAnnouncements()takes the same{ chain, announcements, keys }union, so activity and scanner inputs cannot disagree about a chain, and theas any[]/Record<string, string>casts are gone.Breaking change on
0.1.x(called out in the changelog): positional arguments are replaced by options objects because the positional signature could not express the chain/key relationship.#245 — Make framework scanner cancellation consistent
Done when (from the issue):
ScanCancellationOptions({ signal?: AbortSignal }), accepted by React's hook andscan, Vue's fetch inputs, Svelte's factory andscan, and Solid's factory andscanchain/walletchange; Vue:onScopeDispose; Solid:onCleanup; Svelte: factorysignal+dispose(); all four abort the previous scan on restartuseScanner.test.tsx,useScanner.test.ts,useStellarAnnouncementScan.test.ts,createScanner.test.tsCancellation semantics:
AbortError(DOMException).errorstate, so the UI does not flash a failure when a user navigates away.The dedicated test for the last point starts two scans, releases the second, then releases the first, and asserts the published list is still the second scan's result:
Where cancellation actually reaches the network
signalis passed intoFetchAnnouncementsOptions, so in-flight Soroban RPC and Horizon requests abort (src/chains/stellar/announcements.ts).fetchAnnouncements()in the core has nosignalparameter yet; the Vue adapter races the promise against the signal. An aborted fetch rejects immediately and cannot publish; the HTTP request completes in the background.The caller-visible guarantee is identical for all three chains.
Prerequisite: framework packages now import the agent client from its own package
Wraith,WraithAgent,Chainand the agent config types moved out of the core package in #193, butuseWraith(Vue, Svelte) andcreateActivity(Solid) still imported them from@wraith-protocol/sdk. Their unit tests passed because the module was mocked, which hid the problem from everything excepttscand the declaration build:The imports, test mocks, package dependencies,
tsupexternals andtsconfigpaths now point at@wraith-protocol/sdk-agent. The compatibility harness previously listed all three packages underKNOWN_IMPORT_FAILURES("Node refuses to link them"); it now imports and server-renders them, so those entries are removed rather than skipped.Public API additions
ScanCancellationOptions@wraith-protocol/sdkgetUnknownErrorMessage(error: unknown): string@wraith-protocol/sdkErrorthrowsisAbortError(error: unknown): boolean@wraith-protocol/sdkNew Vue exports:
ScannerChain,ScannerFetchOptions,ScannerScanInput,ScannerScanWithKeysInput,ScannerCheckInput,EvmScanInput,StellarScanInput,SolanaScanInput,ScanActivityInput, the fiveassert*guards.etc/sdk.api.mdis regenerated.Files worth reading first
src/scanner/unified.tsScanCancellationOptionssrc/chains/errorUtils.tsisAbortError()packages/sdk-react/src/hooks/useScanner.tspackages/sdk-vue/src/composables/useScanner.tsrunCancellable()packages/sdk-vue/src/composables/useActivity.tspackages/sdk-svelte/src/primitives/useStellarAnnouncementScan.tsdispose(), stale guardpackages/sdk-solid/src/primitives/createScanner.tsonCleanup(dispose), signal, stale guarddocs/guides/framework-scanner-cancellation.mdxscripts/compat/verify-ssr-imports.mjsTesting
New test files and what they prove:
packages/sdk-react/test/useScanner.test.tsx— publish, unmount abort, superseded scan whose source ignores the abort, per-call signal, hook-level signal.packages/sdk-react/test/useScanner.test-d.ts— compile-time rejection of unsupported chains, non-string wallets and malformed scan options.packages/sdk-vue/test/useScanner.test.ts— discriminated fetch/scan/check inputs, runtimeTypeErrorfor mismatched keys and unknown chains, effect-scope disposal, caller signal, superseded scan.packages/sdk-vue/test/scanner.test-d.ts— compile-time rejection of mismatched keys, unknown chains, wrong option fields per chain.packages/sdk-vue/test/useActivity.test.ts— same union for activity, runtime rejection of mismatched keys.packages/sdk-svelte/test/useStellarAnnouncementScan.test.ts— dispose, caller signal, factory signal, superseded scan.packages/sdk-solid/test/createScanner.test.ts— root disposal viacreateRoot, caller signal, superseded scan.pnpm format:checkpnpm buildpnpm test:types*.test-d.tssuitespnpm api:checketc/sdk.api.mdupdated)pnpm testpnpm test:exportspnpm test:compatpnpm sizepnpm --filter @wraith-protocol/sdk-{react,vue,solid,svelte} build+ typechecksCI on this pull request: 23 checks, 21 pass, 2 nightly jobs skipped, 0 failures.
Reviewer notes
### Changed) and with a before/after in the new guide.signalsupport later, the guard stays correct and the race in the adapter can be deleted.useScanner's hook-levelsignalaborts in-flight work but does not permanently dispose the hook; pass the signal toscan()if a scan should never outlive a specific call.docs/guides/framework-scanner-cancellation.mdxmirrors the implementation; if the semantics change, update it in the same PR.