Skip to content

feat: Memoize widget resolution to scan web/dist once#1076

Draft
jirispilka wants to merge 1 commit into
masterfrom
claude/apify-mcp-quick-wins-fysrwa-memoize-widgets
Draft

feat: Memoize widget resolution to scan web/dist once#1076
jirispilka wants to merge 1 commit into
masterfrom
claude/apify-mcp-quick-wins-fysrwa-memoize-widgets

Conversation

@jirispilka

Copy link
Copy Markdown
Collaborator

Part of #1066 (checkbox: memoize resolveWidgets()).

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. 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.

⚠️ Log-frequency note (hosted-server log consumers)

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

  • New test tests/unit/mcp.server.resolve_widgets_memo.test.ts asserts the scan runs exactly once across two APPS-mode server initializations.
  • Oracle green: type-check clean, lint 0/0, check:agents passed, test:unit 71 files / 960 passed / 2 skipped, format no-op.
  • resolveAvailableWidgets (widgets.ts) untouched; utils.widgets.test.ts and mcp.server.capability_gating.test.ts pass unmodified.
  • Scope: src/mcp/server.ts + the new test.

🤖 Generated with Claude Code

https://claude.ai/code/session_016pXZvhEoE6Lz3DpBSL3n1Y


Generated by Claude Code

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
@jirispilka jirispilka changed the title perf: Memoize widget resolution to scan web/dist once feat: Memoize widget resolution to scan web/dist once Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants