Skip to content

Move the Worker API clients into core, on the config seam - #24

Open
playforge-coding wants to merge 2 commits into
masterfrom
core-api-clients
Open

Move the Worker API clients into core, on the config seam#24
playforge-coding wants to merge 2 commits into
masterfrom
core-api-clients

Conversation

@playforge-coding

@playforge-coding playforge-coding commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Phase 1 of decoupling apps/web from its bundler — the prerequisite for the SvelteKit plan, and worth doing on its own regardless.

What moved

Twelve modules — lessons, comments, users, profile, notifications, moderation, pixabay, aiSuggest, spellingWords, mcpOAuth, imagesClient, git/remote — each opened with:

const API_URL = import.meta.env.VITE_API_URL;

That's Rsbuild-specific, substituted at build time, and absent in Node, the Worker, or any other bundler. They now read apiUrl() / hasApi() through the config seam introduced with the browser tier, and move to core.

Four names for one predicate

lessonHubEnabled, notificationsEnabled, profilesEnabled and gitRemoteEnabled were all Boolean(API_URL) — four exported flags computing the identical thing. Replaced by core's existing hasApi() at 43 call sites.

They had to go regardless: each was evaluated at module scope, so under a lazily-resolved config every one of them would have been false.

The lint rule earned its keep

packages/core is linted against the worker env, and it immediately rejected lessons.js and users.js for using DOMParser — so neither is runtime-neutral, which review would not have caught.

On inspection both were parsing an Atom feed, with the same parser, the same entry shape and the same entryText helper. A third copy-paste pair, after wikimedia and richText.

That reading moves to browser/feeds.js as one implementation, with the error wording injected so each caller keeps the message its UI already showed. The URL builders (lessonsFeedUrl, userFeedUrl) stay neutral, which keeps the layering one-way: the browser tier imports the neutral tier, never the reverse.

Import-hoisting safety

The seam only works because reads are lazy — configureCore runs after every module in the graph is evaluated. Every apiUrl() / hasApi() call site was checked to sit inside a function body rather than at module scope, in both core and web.

Also

  • Drops a now-redundant const base = () => apiUrl() wrapper in users.js.
  • Retires the "VITE_API_URL is not configured." error text, which named a variable core cannot see.

Verification

pnpm install --frozen-lockfile, pnpm lint, pnpm test (core 38, api 27, mcp 14), pnpm build, pnpm build:docs — all pass. Bundle total unchanged.

What's left in phase 1

supabase, turnstile and googleDrive still read import.meta.env. supabase.js is the awkward one — it builds its client at module scope, so it needs a lazy singleton rather than a straight substitution. That's the next PR.

Phase 1 of decoupling apps/web from its bundler. Twelve modules — lessons,
comments, users, profile, notifications, moderation, pixabay, aiSuggest,
spellingWords, mcpOAuth, imagesClient and git/remote — each opened with

  const API_URL = import.meta.env.VITE_API_URL;

which is Rsbuild-specific, substituted at build time, and absent everywhere
else. They now read apiUrl()/hasApi() through the seam added with the browser
tier, and move to core.

Four exported flags turned out to be the same predicate under four names:
lessonHubEnabled, notificationsEnabled, profilesEnabled and gitRemoteEnabled
were all Boolean(API_URL). They are replaced by core's existing hasApi() at 43
call sites. They also had to go regardless: each was evaluated at module scope,
so under a lazily-resolved config they would every one have been false.

The lint rule from the browser-tier PR then caught what review would not have:
lessons.js and users.js use DOMParser, so neither is runtime-neutral. Both were
parsing an Atom feed, and on inspection with the same parser, the same entry
shape and the same helper — a third copy-paste pair. That reading moves to
browser/feeds.js as one implementation with the error wording injected, so each
caller keeps the message its UI already showed. The URL builders stay neutral,
which keeps the layering one-way: the browser tier imports the neutral tier,
never the reverse.

Every apiUrl()/hasApi() call site was checked to be inside a function body
rather than at module scope, which is what makes the lazy read safe against
import hoisting.

Also drops a redundant `const base = () => apiUrl()` wrapper in users.js, and
retires the "VITE_API_URL is not configured" error text, which named a variable
core cannot see.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@playforge-coding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 94a817ab-48ea-4704-b840-8e771c98c60a

📥 Commits

Reviewing files that changed from the base of the PR and between cde68dc and c94ae2f.

📒 Files selected for processing (43)
  • apps/docs/docs/monorepo/version-history.md
  • apps/docs/docs/web-app/ai-lesson-ideas.md
  • apps/docs/docs/web-app/lesson-hub-and-accounts.md
  • apps/docs/docs/web-app/moderation.md
  • apps/docs/docs/web-app/notifications.md
  • apps/docs/docs/web-app/pages-and-routing.md
  • apps/docs/docs/web-app/profiles-and-display-names.md
  • apps/docs/docs/web-app/project-structure.md
  • apps/web/src/components/AiLessonIdeaDialog.jsx
  • apps/web/src/components/AiQuestionDialog.jsx
  • apps/web/src/components/AiTextDialog.jsx
  • apps/web/src/components/BioDialog.jsx
  • apps/web/src/components/CollaborateDialog.jsx
  • apps/web/src/components/CommentsSection.jsx
  • apps/web/src/components/DisplayNameDialog.jsx
  • apps/web/src/components/FloatingWords.jsx
  • apps/web/src/components/FollowListDialog.jsx
  • apps/web/src/components/ImageSearchDialog.jsx
  • apps/web/src/components/NotificationBell.jsx
  • apps/web/src/lib/auth.jsx
  • apps/web/src/lib/git/sync.js
  • apps/web/src/lib/git/useLessonGit.js
  • apps/web/src/pages/EditorPage.jsx
  • apps/web/src/pages/HomePage.jsx
  • apps/web/src/pages/HubPage.jsx
  • apps/web/src/pages/LessonPage.jsx
  • apps/web/src/pages/ModerationPage.jsx
  • apps/web/src/pages/OAuthAuthorizePage.jsx
  • apps/web/src/pages/ProfilePage.jsx
  • packages/core/package.json
  • packages/core/src/aiSuggest.js
  • packages/core/src/browser/feeds.js
  • packages/core/src/comments.js
  • packages/core/src/git/remote.js
  • packages/core/src/imagesClient.js
  • packages/core/src/lessons.js
  • packages/core/src/mcpOAuth.js
  • packages/core/src/moderation.js
  • packages/core/src/notifications.js
  • packages/core/src/pixabay.js
  • packages/core/src/profile.js
  • packages/core/src/spellingWords.js
  • packages/core/src/users.js

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant