Skip to content

Conversation

@grypez
Copy link
Contributor

@grypez grypez commented Jan 22, 2026

Replace @endo/bundle-source with Vite for vat bundling and @endo/import-bundle with direct Compartment.evaluate() for bundle loading.

Motivation

The Endo bundling tools have complex dependency chains and produce large bundles. Vite provides a simpler, faster bundling pipeline with better tree-shaking and smaller output.

Changes

Bundling

  • New bundleVat function using Vite's build() API with IIFE output
  • Custom Rollup plugins for export metadata extraction and comment stripping
  • CLI commands (bundle, watch) updated to use Vite bundler

Bundle Loading

  • loadBundle() evaluates bundles via Compartment.evaluate()
  • Validates moduleFormat, code, exports, and modules properties
  • Passes endowments and inescapable globals to the compartment

Dependencies

  • Removed: @endo/bundle-source (except from @metamask/kernel-shims), @endo/import-bundle
  • Added: vite, acorn (for comment stripping)

Bundle Format

{
  "moduleFormat": "iife",
  "code": "var __vatExports__ = (function() { ... })();",
  "exports": ["buildRootObject"],
  "modules": { "./foo.js": { "renderedExports": [...], "removedExports": [...] } }
}

Migration

Existing bundles using endoZipBase64 format are no longer supported. Regenerate bundles using ocap bundle <entry>.

Closes: #742


Note

High Risk
High risk because it changes the vat bundle format and the security-sensitive code-loading path (replacing @endo/import-bundle with Compartment.evaluate()), which could break existing bundles or alter sandboxing behavior.

Overview
Replaces Endo-based vat bundling/loading with a Vite + IIFE pipeline. The CLI bundle/watch flow now uses a new Vite-based bundleVat builder (plus Rollup plugins to strip comments and capture export metadata) and drops @endo/bundle-source/@endo/init in favor of @metamask/kernel-shims/endoify.

Updates the kernel to consume the new bundle format. VatSupervisor now fetches bundle content as text and loads it via a new loadBundle() helper that validates moduleFormat: "iife" and evaluates the bundle in a SES Compartment, and kernel-utils adds a VatBundle type + isVatBundle guard with tests. Integration/fixture bundles and tests are updated accordingly, and @endo/import-bundle is removed from ocap-kernel dependencies.

Written by Cursor Bugbot for commit 4d3ef97. This will update automatically on new commits. Configure here.

@grypez grypez force-pushed the grypez/bundle-with-vite branch 3 times, most recently from 504110d to 1a13965 Compare January 22, 2026 15:17
@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 88.45%
⬇️ -0.14%
5807 / 6565
🔵 Statements 88.33%
⬇️ -0.14%
5901 / 6680
🔵 Functions 87.44%
⬇️ -0.22%
1512 / 1729
🔵 Branches 84.58%
⬇️ -0.25%
2101 / 2484
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/cli/src/app.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
15-155
packages/cli/src/commands/bundle.ts 92.3%
🟰 ±0%
50%
🟰 ±0%
100%
🟰 ±0%
92.3%
🟰 ±0%
72
packages/cli/src/commands/watch.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/cli/src/vite/export-metadata-plugin.ts 0% 0% 0% 0% 19-30
packages/cli/src/vite/strip-comments-plugin.ts 100% 100% 100% 100%
packages/cli/src/vite/vat-bundler.ts 0% 0% 0% 0% 18-54
packages/kernel-utils/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-utils/src/vat-bundle.ts 100% 100% 100% 100%
packages/ocap-kernel/src/vats/VatSupervisor.ts 74.64%
🟰 ±0%
44.82%
🟰 ±0%
58.33%
🟰 ±0%
74.64%
🟰 ±0%
122, 133, 141, 179, 217-221, 232, 241-242, 263-265, 268, 272-274, 309, 326-334
packages/ocap-kernel/src/vats/bundle-loader.ts 100% 100% 100% 100%
Generated in workflow #3446 for commit 4d3ef97 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

@grypez grypez marked this pull request as ready for review January 22, 2026 18:29
@grypez grypez requested a review from a team as a code owner January 22, 2026 18:29
@grypez grypez force-pushed the grypez/bundle-with-vite branch from 1a13965 to b8e02c0 Compare January 22, 2026 18:45
Copy link
Member

@rekmarks rekmarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice!

cursor[bot]

This comment was marked as outdated.

@grypez grypez force-pushed the grypez/bundle-with-vite branch from fcdd1f3 to cc26d68 Compare January 23, 2026 23:03
@grypez grypez force-pushed the grypez/bundle-with-vite branch 3 times, most recently from daa5828 to 6cd04a3 Compare January 26, 2026 23:28
@grypez grypez disabled auto-merge January 27, 2026 16:47
@grypez grypez enabled auto-merge January 27, 2026 16:47
@grypez
Copy link
Contributor Author

grypez commented Jan 27, 2026

We could remove the 'modules' key from the new bundle format. It is included here as a PoC for attaching metadata to the bundles.

@grypez grypez force-pushed the grypez/bundle-with-vite branch from 6cd04a3 to a3838dc Compare January 28, 2026 17:28
@grypez
Copy link
Contributor Author

grypez commented Jan 28, 2026

PR History: #763 · build: Bundle vats with vite

Reviewer Comments Status
rekmarks 5/5 ● re-approval needed
cursor 9/9 ●
Jan 22 · rekmarks · CHANGES_REQUESTED — 5/5 ●
File Comment Resolution
bundle-loader.ts:L9 Remove Vite-specific naming from type 99c6775d
README.md:L5 nit: documentation wording 99c6775d
serve.test.ts:L23 Import VatBundle type, relocate to kernel-utils 61af648a
vat-bundler.ts:L56 Comment should note import only in comments #772
vat-bundler.ts:L8 Code smell: moduleFormat declared multiple times 61af648a
Jan 22 · cursor · COMMENTED — 2/2 ●
File Comment Resolution
vat-bundler.ts:L56 Import pattern regex may break .import() calls #772
bundle-loader.ts:L33 Missing code property validation 1424d588
Jan 23 · cursor · COMMENTED — 4/4 ●
File Comment Resolution
bundle.test.ts:L70 Test uses incorrect moduleFormat 'vite-iife' f80313b7
strip-comments-plugin.ts:L53 Regex literals not handled #772
strip-comments-plugin.ts:L46 Template literal expressions not handled #772
strip-comments-plugin.ts:L63 Multi-line comment removal creates syntax errors #772
Jan 23 · cursor · COMMENTED — 2/2 ●
File Comment Resolution
vat-bundle.ts:L30 isVatBundle doesn't validate all properties db40d34d
strip-comments-plugin.ts:L26 Wrong sourceType for IIFE parsing 6cd04a3f
Jan 23 · cursor · COMMENTED — 1/1 ●
File Comment Resolution
bundle-loader.ts:L33 inescapableGlobalProperties treated as escapable Rejected
Resolved ● Unresolved ○
14 total 0 total
Fixed ✓ 13
Rejected ✗ 1
Punted ⤳ 0
Void ∅ 0
New ◇ 0
Pending → 0
Discussing ⇄ 0
Deferred ↓ 0

Copy link
Member

@rekmarks rekmarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of nits!

Comment on lines 22 to 31
export const isVatBundle = (value: unknown): value is VatBundle =>
isObject(value) &&
hasProperty(value, 'moduleFormat') &&
value.moduleFormat === 'iife' &&
hasProperty(value, 'code') &&
typeof value.code === 'string' &&
hasProperty(value, 'exports') &&
Array.isArray(value.exports) &&
hasProperty(value, 'external') &&
Array.isArray(value.external);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done with superstruct

Comment on lines 36 to 37
stripCommentsPlugin() as unknown as PluginOption,
metadataPlugin as unknown as PluginOption,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odd that we have to cast them here.

grypez and others added 21 commits January 29, 2026 11:54
Reuses the acorn parsing dependency from `@ocap/kernel-agents-repl` to authoritatively scrub comments from vat bundles.

Refs #770

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces the comment scrubber with an AST-based implementation to
reliably remove comments (including those containing `import(`) from
bundled code.
> 
> - Refactors `vite/strip-comments-plugin` to use Acorn (`parse` with
`onComment`) and return unchanged code when no comments are found
> - Adds unit tests for `strip-comments-plugin` covering
single/multi-line comments, strings, regex, templates, and empty input
> - Adds `acorn` dependency in `@ocap/cli` and updates `yarn.lock`
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3a59ba8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
status: substantiated

### Incomplete type guard doesn't validate all VatBundle properties

**Medium Severity**

The `isVatBundle` type guard asserts that a value is a `VatBundle`, but only validates `moduleFormat` and `code` properties. The `VatBundle` type also requires `exports: string[]` and `modules: Record<...>` properties. Code that uses this type guard will believe it has a complete `VatBundle` after the check, but accessing `.exports` or `.modules` could return `undefined` if an incomplete object passed the check.

Location: packages/kernel-utils/src/vat-bundle.ts#L24-L30
status: substantiated

### Missing validation of `code` property in bundle loader

**Medium Severity**

The `loadBundle` function validates that `moduleFormat` equals `'vite-iife'` but does not validate that `code` exists or is a string before using it in `compartment.evaluate`. If a malformed bundle is loaded (e.g., corrupted file, incompatible version) with correct `moduleFormat` but missing or non-string `code`, the template literal interpolation would produce invalid JavaScript (like `"undefined"` or `"[object Object]"`) leading to confusing evaluation errors or silently returning `undefined` instead of proper exports.

Location: packages/ocap-kernel/src/vats/bundle-loader.ts#L29-L33
status: substantiated

### Wrong sourceType for parsing IIFE bundle output

**Low Severity**

The `stripCommentsPlugin` uses `sourceType: 'module'` to parse vite's IIFE output, but IIFE bundles are scripts, not modules. Module parsing implies strict mode, which rejects certain legacy JavaScript patterns (octal literals, `with` statements, etc.). If bundled dependencies contain non-strict-mode code that wasn't transpiled, `parse()` throws a SyntaxError, preventing comment stripping and causing the build to fail unexpectedly.

Location: packages/cli/src/vite/strip-comments-plugin.ts#L21-L26
Add validation that code exists and is a string before calling
compartment.evaluate in loadBundle.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add validation for exports (Array.isArray) and modules (object && !== null)
properties in the isVatBundle type guard.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Change sourceType from 'module' to 'script' in stripCommentsPlugin
to correctly handle IIFE bundles that may contain non-strict-mode
patterns like octal literals and with statements.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove `export` keyword from BundleMetadata type (only used internally)
- Replace modules metadata collection with simple `external: []` field
- Update VatBundle type and isVatBundle type guard accordingly
- Update vat-bundle tests to use external instead of modules

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update the test bundle fixture to use `external: []` instead of
`modules: {}` to match the updated VatBundle type definition.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Use parsed.code directly after validation instead of casting to an
intermediate VatBundle type that is only used for its code property.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Address rekmarks review comments:
- Use superstruct for VatBundle type guard instead of manual checks
- Import Plugin from vite instead of rollup to remove unnecessary casts

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@grypez grypez force-pushed the grypez/bundle-with-vite branch from d511472 to c0cf139 Compare January 29, 2026 17:56
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

grypez and others added 2 commits January 29, 2026 12:21
Tests that loadBundle throws clear validation error for malformed content.
Currently fails with TypeError instead of descriptive error message.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Validate that parsed JSON is a non-null object before accessing properties.
Prevents TypeError when bundle content is "null" or other non-object JSON.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@grypez grypez added this pull request to the merge queue Jan 29, 2026
Merged via the queue into main with commit fa7d847 Jan 29, 2026
32 checks passed
@grypez grypez deleted the grypez/bundle-with-vite branch January 29, 2026 18:49
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.

build: Bundle vats with vite

3 participants