feat: Memoize widget resolution to scan web/dist once#1076
Draft
jirispilka wants to merge 1 commit into
Draft
Conversation
What we're solving: ActorsMcpServer.resolveWidgets() ran two dynamic imports and an fs existsSync scan of web/dist (via resolveAvailableWidgets) on every initialize AND every connect — i.e. once per HTTP session. Widgets are static build artifacts and the scan depends only on a constant dirName (from import.meta.url), so the result never changes within a process. How: add a module-level cached promise in server.ts and a loadAvailableWidgetsOnce() helper that assigns the promise synchronously before its first await, so concurrent sessions share one in-flight scan instead of each starting their own. resolveWidgets() keeps its APPS-mode early return and soft-fail try/catch; it now awaits the shared cache and assigns this.availableWidgets. The ready/missing-widget logging moves into the once-path, so it logs once per process instead of per session. On a rejected scan the cache is cleared so a later session can retry. The low-level resolveAvailableWidgets helper is left pure (unmemoized) so its direct unit tests keep working. Alternatives considered: caching only the resolved value (rejected — concurrent sessions would both scan before either finished); memoizing inside resolveAvailableWidgets (rejected — its unit tests mock the filesystem per test). Note: logging frequency for the "ready/missing widgets" lines changes from per-session to once-per-process (content unchanged) — relevant to hosted- server log consumers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016pXZvhEoE6Lz3DpBSL3n1Y
9 tasks
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.
Part of #1066 (checkbox: memoize
resolveWidgets()).What we're solving
ActorsMcpServer.resolveWidgets()ran two dynamic imports and anfs.existsSyncscan ofweb/dist(viaresolveAvailableWidgets) on everyinitializeand everyconnect()— i.e. once per HTTP session. Widgets are static build artifacts and the scan depends only on a constantdirName(fromimport.meta.url), so the result never changes within a process.How
Add a module-level cached promise in
server.tsand aloadAvailableWidgetsOnce()helper that assigns the promise synchronously before its firstawait, so concurrent sessions share one in-flight scan instead of each starting their own.resolveWidgets()keeps its APPS-mode early return and soft-fail try/catch; it now awaits the shared cache and assignsthis.availableWidgets. The ready/missing-widget logging moves into the once-path. On a rejected scan the cache is cleared so a later session can retry. The low-levelresolveAvailableWidgetshelper is left pure (unmemoized) so its direct unit tests keep working.Alternatives considered
resolveAvailableWidgets— rejected; its unit tests mock the filesystem per test.The "ready widgets" / "some widgets are not ready" log lines now fire once per process instead of once per session. Log content is unchanged — only frequency. Flagging per CLAUDE.md's public/internal rules since the hosted server (
apify-mcp-server-internal) consumes these logs.Verification
tests/unit/mcp.server.resolve_widgets_memo.test.tsasserts the scan runs exactly once across two APPS-mode server initializations.test:unit71 files / 960 passed / 2 skipped, format no-op.resolveAvailableWidgets(widgets.ts) untouched;utils.widgets.test.tsandmcp.server.capability_gating.test.tspass unmodified.src/mcp/server.ts+ the new test.🤖 Generated with Claude Code
https://claude.ai/code/session_016pXZvhEoE6Lz3DpBSL3n1Y
Generated by Claude Code