Skip to content

fix(deployer): preserve safe externals with custom bundler externals#17120

Open
Akash504-ai wants to merge 1 commit into
mastra-ai:mainfrom
Akash504-ai:fix/safe-runtime-externals
Open

fix(deployer): preserve safe externals with custom bundler externals#17120
Akash504-ai wants to merge 1 commit into
mastra-ai:mainfrom
Akash504-ai:fix/safe-runtime-externals

Conversation

@Akash504-ai

@Akash504-ai Akash504-ai commented May 27, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes a regression where custom bundler.externals could override Mastra’s safe runtime externals and allow internal runtime packages to be bundled into generated chunks.

This could rewrite internal dynamic imports such as:

await import('@mastra/core/storage')

into generated self-imports like:

await import('./index.mjs')

or:

await import('./mastra.mjs')

reintroducing the ESM top-level-await deadlock fixed in #14860 / #14863.

This change centralizes safe runtime externals, preserves user-provided externals additively, and ensures protected Mastra runtime packages remain externalized even when custom bundler.externals are configured.

Related issue(s)

Fixes #17115

Related:

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update

What changed

  • added centralized MASTRA_RUNTIME_EXTERNALS
  • added mergeBundlerExternals()
  • added getSafeBundlerExternals()
  • changed bundler external merging from replacement semantics to additive semantics
  • preserved existing externals: true behavior
  • protected Mastra runtime subpath imports such as @mastra/core/storage

Regression coverage added

Added tests covering:

  • preserving user externals while keeping safe runtime externals
  • protecting @mastra/core/storage subpath imports
  • custom externals + top-level dynamic imports
  • generated bundle output inspection
  • preventing generated self-imports (./index.mjs, ./mastra.mjs)
  • TLA mastra.mjs chunk safety

Validation

Passed:

corepack pnpm --filter ./packages/deployer test src/build/analyze.test.ts
corepack pnpm --filter ./packages/deployer test src/build/analyze/bundleExternals.test.ts
corepack pnpm --filter ./packages/deployer lint
git diff --check

Generated bundle output was also inspected directly to verify:

  • internal Mastra dynamic imports remain externalized
  • no generated self-import rewrites occur
  • no circular TLA chunk dependencies are introduced

Checklist

  • I have linked the related issue(s) in the description above
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works
  • I have addressed all Coderabbit comments on this PR

ELI5

This PR fixes a bug where custom external package configurations could accidentally bundle Mastra's internal packages into the output, breaking the application. The fix ensures Mastra's core internal packages always stay external (not bundled) while still allowing you to specify your own external packages.

Overview

Fixes a regression in the deployer bundler where user-provided bundler.externals could override Mastra's safe runtime externals, causing internal runtime packages to be bundled into generated chunks. This could rewrite internal dynamic imports (e.g., await import('@mastra/core/storage')) into self-imports (e.g., await import('./index.mjs')), reintroducing an ESM top-level-await deadlock previously fixed in #14860/#14863.

Changes

New utilities in packages/deployer/src/build/analyze/constants.ts:

  • Added MASTRA_RUNTIME_EXTERNALS constant to centralize safe runtime externals for Mastra packages
  • Added mergeBundlerExternals() function to deduplicate and flatten external package lists
  • Added getSafeBundlerExternals() function to produce the final externals set by combining global externals, optional deprecated externals, Mastra runtime externals, and user-provided externals

Updated integration points:

  • bundleExternals.ts: Now uses getSafeBundlerExternals() instead of manual concatenation of constants
  • analyze.ts: Now uses getSafeBundlerExternals() to derive allExternals from user-provided externals, replacing prior manual concatenation and filtering logic

Test coverage in packages/deployer/src/build/analyze.test.ts:

  • Added helper readGeneratedModules to load and concatenate generated .mjs module contents
  • New safe runtime externals test suite validating:
    • getSafeBundlerExternals preserves user externals while ensuring Mastra runtime packages remain externalized and deduplicated
    • analyzeBundle marks @mastra/core as external even with custom externals, preventing rewritten internal dynamic imports
    • Final bundling output preserves dynamic imports to @mastra/core/storage without generating self-imports
    • Mastra subpath dynamic imports in TLA-driven chunks are not rewritten to relative imports

Changeset:

  • Updated .changeset/lucky-places-wonder.md documenting the patch-level fix for the bundler regression

Key Improvements

  • Changed external merging from replacement semantics to additive semantics
  • Preserved existing externals: true behavior
  • Protected Mastra runtime subpath imports (e.g., @mastra/core/storage)
  • Prevents generated self-imports and circular TLA chunk dependencies

Review Change Stack

@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

@Akash504-ai is attempting to deploy a commit to the Mastra Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented May 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b595184

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
Name Type
@mastra/deployer Patch
mastra Patch
@mastra/deployer-cloud Patch
@mastra/deployer-cloudflare Patch
@mastra/deployer-netlify Patch
@mastra/deployer-vercel Patch
@mastra/temporal Patch
create-mastra Patch
@mastra/core Patch
@mastra/server Patch
@internal/playground Patch
mastracode Patch
@mastra/mcp-docs-server Patch
@mastra/client-js Patch
@mastra/opencode Patch
@mastra/longmemeval Patch
@mastra/express Patch
@mastra/fastify Patch
@mastra/hono Patch
@mastra/koa Patch
@mastra/nestjs Patch
@mastra/playground-ui Patch
@mastra/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 87a360ee-d9bf-42ed-8802-e3ab7f8b8b2d

📥 Commits

Reviewing files that changed from the base of the PR and between 40f9ea0 and b595184.

📒 Files selected for processing (5)
  • .changeset/lucky-places-wonder.md
  • packages/deployer/src/build/analyze.test.ts
  • packages/deployer/src/build/analyze.ts
  • packages/deployer/src/build/analyze/bundleExternals.ts
  • packages/deployer/src/build/analyze/constants.ts

Walkthrough

This PR fixes a bundler regression where user externals could override Mastra's runtime externals, risking ESM top-level await deadlocks. It introduces getSafeBundlerExternals to ensure Mastra packages always remain externalized, refactors the analyzer and bundler to use this utility, and adds comprehensive tests verifying the fix preserves dynamic imports without generating self-imports.

Changes

Safe Runtime Externals Fix

Layer / File(s) Summary
Safe externals contract and utilities
packages/deployer/src/build/analyze/constants.ts
Introduces MASTRA_RUNTIME_EXTERNALS constant listing packages that must remain external, and adds mergeBundlerExternals and getSafeBundlerExternals helpers to safely combine global, deprecated, runtime, and user externals with deduplication.
Analyzer and bundler integration
packages/deployer/src/build/analyze.ts, packages/deployer/src/build/analyze/bundleExternals.ts
Updates analyzeBundle and bundleExternals to use getSafeBundlerExternals instead of manual concatenation, ensuring Mastra runtime packages are always externalized. Changes externals passing logic to only use boolean true when explicitly set by user.
Test coverage for safe externals
packages/deployer/src/build/analyze.test.ts
Adds helper function to read generated modules and comprehensive test suite validating that getSafeBundlerExternals preserves user externals while ensuring Mastra packages remain external, analyzeBundle marks Mastra packages as external even with custom externals, generated bundles contain dynamic imports to Mastra without self-imports, and TLA-driven chunks preserve Mastra subpath imports without rewriting to relative paths.
Release documentation
.changeset/lucky-places-wonder.md
Documents the patch release fix for the deployer bundling regression affecting interaction between user-supplied bundler externals and Mastra's safe runtime externals.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • wardpeet
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main fix: preserving safe runtime externals when custom bundler externals are provided, directly addressing the regression documented in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: lockfile failed supply-chain policy check. Run pnpm install locally to update the lockfile.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@dane-ai-mastra dane-ai-mastra Bot added the complexity: low Low-complexity PR label May 27, 2026
@dane-ai-mastra

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check passed (#17115).

Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.


PR complexity score

Factor Value Score impact
Files changed 5 +10
Lines changed 283 +15
Author merged PRs 10 -10
Test files changed Yes -10
Final score 5

Applied label: complexity: low


Changed test gate

Changed Test Gate is pending. The Changed Test Gate / changed-tests check will update the test label when it completes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: low Low-complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Custom bundler.externals re-introduces the #14860 ESM TLA deadlock (via @mastra/pg's internal import('@mastra/core/storage'))

1 participant