Repository navigation
Conversation
…wser The Production Agent groups runtime errors from published apps into issues, and needs to know which frontend build produced each browser-side error so a tab still running an old bundle can't reopen a fixed issue. The publish pipeline sets VITE_BASE44_BUILD_COMMIT for every build. The SDK now reads it itself via `import.meta.env.VITE_BASE44_BUILD_COMMIT`, which Vite statically replaces inside node_modules in both `vite build` and the dev server's prebundled deps, so no app code changes. Every API call from a browser client (entities, functions.invoke, functions.fetch, and the rest of the axios path) carries the header when the value is exactly 40 lowercase hex chars; otherwise it is omitted. `import.meta` lives only in build-commit-source.browser.js, reached through the package's `browser` field. The default file is a stub, so Node, Deno and SSR never see it, and the `react-native` field maps Metro back to the stub, since Hermes (Expo 54) rejects `import.meta` outright. Never sent from createClientFromRequest, Node/Deno, or React Native. scripts/verify-build-commit.sh (npm run test:build-commit) proves it against the packed tarball: Vite 6 and 7 build + dev, and Expo 54 with --expo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…port
The `browser` field broke Expo web: Metro's web resolver ignores the
`react-native` field and follows `browser`, so it bundled the
`import.meta` reader into a classic <script>, and the page failed to parse
("Cannot use 'import.meta' outside a module").
Now `#build-commit-source` in the package's `imports` field gives the reader
only to the `module` condition (Vite, webpack, esbuild), and everything
else, including Metro on native and web, Node and Deno, gets the fallback.
tsconfig `paths` and a Vitest alias point the specifier at the source file.
verify-build-commit.sh --expo now also exports Expo 54 web and parses the
bundle as a classic script; that check fails on the previous routing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
functions.fetch() copied the baked-in build commit directly, so
createClient({ headers: { "X-Base44-Build": override } }) sent the override
on entity calls and functions.invoke() but the baked-in commit on
functions.fetch(), attributing one client's requests to two builds. A
lowercase override also landed next to the baked-in key instead of
replacing it.
The effective header is now resolved once: the caller's value, matched
case-insensitively, else the baked-in commit. Regression tests cover each
casing, with and without a baked-in commit, across entities,
functions.invoke() and functions.fetch().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
Base44 groups runtime errors from published apps into issues, and treats an issue as fixed once a new version of the app ships without the error. Browsers don't reload when an app is republished, though. A user who opened the app before the fix keeps running the old bundle, and keeps hitting and reporting the old error. Those reports look identical to new ones, so a fixed issue appears to come straight back.
To tell the two apart, the backend needs to know which build of the frontend sent each request. Only the build knows that. The publish pipeline knows the app's git commit when it builds the bundle, but nothing in the bundle carries it to the API afterwards.
This PR has the SDK send that commit as an
X-Base44-Buildheader on its API calls. The backend can then attribute each browser-side error to the build that produced it, and ignore errors from bundles older than the fix. The SDK is the right place for it:The publish pipeline provides the commit at build time as
VITE_BASE44_BUILD_COMMIT. The API acceptsX-Base44-Buildonly as an exact 40-character lowercase hex commit and ignores anything else. The header is optional: requests without it, from older SDK versions or builds that don't set a commit, behave exactly as before.Route: the SDK reads
import.meta.envitselfApp source is never rewritten at build time, so the SDK reads
import.meta.env.VITE_BASE44_BUILD_COMMITdirectly. Vite replaces that expression statically insidenode_modules:vite build: the commit is inlined as a string literal, with noimport.metaleft in the bundle. When the var is unset, it becomesvoid 0.import.meta.env = {…}literal to the prebundled.vite/deps/@base44_sdk.js, so the value is present there too.This covers web apps and game apps, which have no
@base44/vite-plugin. A plugindefineroute would have missed game apps, so I didn't use it.Keeping
import.metaaway from Metro, Node and Denoimport.metabreaks Metro on both platforms. Expo 54's Hermes build fails on anyimport.metain a dependency ("import.metais not supported in Hermes"). On Expo web, Metro ships a classic<script>, whereimport.metais a SyntaxError and the page doesn't render. Every mobile build bumps the SDK, so a bare read would break them all. The reader is therefore reached only through a package import:utils/build-commit-source.js: theimport.meta.envread, in atrymodulecondition (Vite, webpack, esbuild)utils/build-commit-source.fallback.js: returnsundefinedA tsconfig
pathsentry and a Vitest alias point the specifier at the source file. The emitted.d.tsfiles don't reference it.Behaviour
createClient()in a browser (windowanddocumentpresent) addsX-Base44-Buildto the shared axios headers. That covers entity CRUD,functions.invoke, and every other axios call. It is also added tofunctions.fetch(). A caller-suppliedX-Base44-BuildincreateClient({ headers }), in any casing, replaces the baked-in commit on every call path, so one client never reports two builds.createClientFromRequest(), which opts out explicitly whatever runtime it is in, or from Node/Deno (nowindow) or React Native (nodocument).fetchWithAuth, sockets and the AI gateway are unchanged.createClientis that its body moved into a privatebuildClient(config, { sendBuildCommit }).Side effects checked
X-App-Id,X-Origin-URL), so they already preflight and the new header adds none. The one new preflight is an anonymous, header-lessfunctions.fetch()GET, which used to be a simple request. The server allows*, and the SDK has no allowlist that would strip the header.Tests
tests/unit/build-commit-header.test.ts(22 tests) checks that:functions.invokeandfunctions.fetchcreateClientFromRequest, Node and React Nativefunctions.invokeandfunctions.fetchalike, in any casingimport.meta.env's value, it is the only source file that usesimport.meta, the fallback returns nothing, and theimportsmap is correctnpm run test:build-commit(scripts/verify-build-commit.sh) packs the SDK, installs the tarball into fresh template-like apps (not linked), then:vite buildwith Vite 6.4 and Vite 7.3 and greps the bundle for a known commit (also checks the var-unset case)--expo, runs Expo 54 Android (Hermes) and web exports, checks the SDK is bundled without the reader, and parses the web bundle as a classic script (this check fails against the earlierbrowser-field routing)vite previewof the production build:entities.list/filter/createandfunctions.invokeall sendX-Base44-Build: <commit>.npm test: 353/353 pass.tscand the type tests are clean, and lint is clean.Risk
A tool that doesn't support package
importscan't resolve#build-commit-source, and the build fails. Examples are webpack 4, Jest before 28 and very old Metro. Expo 54 and 58, Vite 6 and 7, Node and webpack 5 all support it.Not covered
VITE_*vars and can't takeimport.meta, so they deliberately send nothing. Covering them would need the platform to expose the commit in a form Metro inlines, e.g.EXPO_PUBLIC_BASE44_BUILD_COMMITread throughprocess.envin areact-native-mapped source file. That is a follow-up if wanted.Release
No version bump here. A maintainer runs
manual-publishwithpatch, which should give@base44/sdk@0.8.54. No template or plugin change is needed.🤖 Generated with Claude Code