feat: Cloudflare Workers SSR support — createServerClient + worker-safe analytics#221
Open
netanelgilad wants to merge 1 commit into
Open
feat: Cloudflare Workers SSR support — createServerClient + worker-safe analytics#221netanelgilad wants to merge 1 commit into
netanelgilad wants to merge 1 commit into
Conversation
…fe analytics
Make the SDK safe and usable inside Cloudflare Workers SSR:
- Analytics is now a no-op outside a browser (no heartbeat setInterval or
processing timers without window), and a new disableAnalytics client
config option skips it entirely. Browser behavior is unchanged when
analytics is enabled.
- createAxiosClient accepts an optional axios adapter, exposed as an
adapter field on CreateClientConfig, so edge runtimes can force the
fetch adapter instead of relying on axios adapter auto-detection.
- New createServerClient({ request, env, ... }) entry point resolves
config from explicit options -> env (BASE44_*) -> Base44-* request
headers (same contract as createClientFromRequest) -> Authorization
header / base44_access_token cookie for the user token, and forces
disabled analytics, the fetch adapter, and requiresAuth: false.
- saveAccessToken/removeAccessToken (and auth.setToken/logout) now mirror
the token into a base44_access_token cookie (path=/; SameSite=Lax;
Secure on https) so SSR document requests carry auth; localStorage
behavior is unchanged.
The Deno backend-functions contract (createClientFromRequest headers) is
untouched, and no package.json exports map was added so existing deep
imports keep working.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DvhQfqxACcq25XAQRpoSh9
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/sdk@0.8.35-pr.221.5933e35Prefer not to change any import paths? Install using npm alias so your code still imports npm i "@base44/sdk@npm:@base44-preview/sdk@0.8.35-pr.221.5933e35"Or add it to your {
"dependencies": {
"@base44/sdk": "npm:@base44-preview/sdk@0.8.35-pr.221.5933e35"
}
}
Preview published to npm registry — try new features instantly! |
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.
Cloudflare Workers SSR support
Motivation
Full-stack hosting POC: running app SSR on Cloudflare Workers for Platforms. Today the SDK cannot be safely instantiated inside a Worker request handler:
createClient()unconditionally constructs the analytics module, whose heartbeat processor runs a rawsetInterval. In Workers,setIntervalat global scope throws, and inside a request it leaks past the request lifetime — and there was no config option to disable analytics.localStorage, so SSR document requests carry no auth at all.Changes
src/modules/analytics.ts): the analytics module is now a no-op outside a browser (typeof window === 'undefined') — no heartbeat interval, no processing timers,track()queues nothing. A newdisableAnalytics?: booleanonCreateClientConfigskips analytics explicitly; the server client sets it automatically.src/utils/axios-client.ts,src/client.ts):createAxiosClientaccepts an optionaladapter, exposed as anadapterfield onCreateClientConfigand forwarded to all four internal axios clients. The server client forcesadapter: 'fetch'.createServerClient(options)(src/server.ts, exported from the main entry): builds a per-request client for SSR/edge runtimes. Each config value resolves in order: explicit option →env(BASE44_APP_ID,BASE44_API_URL,BASE44_SERVICE_TOKEN,BASE44_FUNCTIONS_VERSION) → the sameBase44-*request headerscreateClientFromRequestreads (so it degrades gracefully behind the existing proxy); the user token resolves option →Authorization: Bearerheader →base44_access_tokencookie (internal dependency-free cookie parser handles quoted + URL-encoded values). Throws a clear error when no appId resolves or when the serverUrl isn't absolute; forces analytics off, thefetchadapter, andrequiresAuth: false— nowindow/localStorageaccess anywhere in the path. Also propagatesBase44-StatelikecreateClientFromRequest.src/utils/auth-utils.ts,src/modules/auth.ts):saveAccessTokennow also sets abase44_access_tokencookie (path=/; SameSite=Lax, plusSecureon https) so subsequent document requests carry the token to SSR;removeAccessTokenclears it.auth.setToken()andauth.logout()(which write localStorage inline) mirror/clear the same cookie so login and logout flows stay consistent with SSR. All cookie writes are guarded ontypeof document !== 'undefined'; localStorage behavior is byte-for-byte unchanged.Compatibility guarantees
createClientFromRequestand itsAuthorization/Base44-Service-Authorization/Base44-App-Id/Base44-Api-Url/Base44-Functions-Version/Base44-Stateheader handling are untouched.exportsmap added to package.json — consumers that deep-import (e.g.@base44/sdk/dist/utils/axios-client) keep working.createServerClientis exported from the main entry only.windowor whendisableAnalyticsis set.Test evidence
npm test(typecheck viatsc --noEmit+ vitest): 19 files, 197 tests passed (was 15 files / ~171 before this PR).npm run lint(eslint): clean.npm run build(tsc): clean.tests/unit/server-client.test.ts: full resolution precedence (options → env → headers), cookie fallback incl. quoted/URL-encoded values,Authorization-over-cookie priority, malformed-header handling, appId-missing throws, non-absolute serverUrl throws,fetchadapter forced on all four axios clients,Base44-Statepropagation, and a worker simulation proving no timers start andtrack()is a safe no-op withoutwindow.tests/unit/auth-utils-cookie.test.ts: cookie-string builders (attributes, Secure, encoding, custom name, clearing) plus save/remove mirror behavior in a simulated browser and SSR no-op guards.tests/unit/analytics.test.ts: updated to simulate browser globals (analytics is browser-only now) and extended with heartbeat-preserved-in-browser anddisableAnalyticsno-op tests, plus a no-window worker-safety test.Note: this PR triggers an automatic
@base44-preview/sdknpm preview publish for easy testing in the Workers POC.🤖 Generated with Claude Code
https://claude.ai/code/session_01DvhQfqxACcq25XAQRpoSh9
Generated by Claude Code