Skip to content

feat(dev): support external app previews with a shared Bun runtime - #78

Merged
francoischalifour merged 5 commits into
mainfrom
fc/bun-preview-runtime
Oct 14, 2026
Merged

francoischalifour merged 5 commits into
mainfrom
fc/bun-preview-runtime

Conversation

@francoischalifour

Copy link
Copy Markdown
Member

Context

Package contributors need to preview data apps outside this repository against the current checkout. This includes both a single-file app intended for hosted execution and a local app directory with its own Bun server.

The existing gallery is useful for package development, but it does not provide that workflow. External previews need to resolve the checkout’s package and React dependencies, preserve the app’s configuration, connect to the selected lakehouse, and recover reliably as files change.

The CLI needs the same single-file preview behavior. Putting the runtime in the package gives checkout tooling and the CLI one implementation for bundling, iframe hosting, SQL delivery, navigation, exports, and reloads. Launchers retain responsibility for installation, profiles, and credentials.

Closes #74 by exposing a real API instead of re-implementing in the Altertable CLI too.

External preview commands

Add two contributor commands:

bun run dev:file /absolute/path/index.tsx
bun run dev:dir /absolute/path/app-directory

Both resolve @altertable/data-app and React from the checkout build, allowing external apps to run without installing the package themselves. They build the package if dist is absent; subsequent package source changes require bun run build.

dev:file launches the shared preview runtime. It bundles the app for browser execution and renders it through the existing sandboxed iframe bridge.

dev:dir runs the app’s own src/server.ts with Bun’s watch mode. A temporary, typed TypeScript configuration combines the app’s inherited aliases with checkout package paths. Checkout mappings take precedence, while the app’s original configuration and bunfig.toml remain in use.

Lakehouse setup returns a normalized environment and a description of the selected connection without mutating process.env. A CLI proxy takes precedence over direct credentials.

Shared Bun preview API

Expose the runtime through a dedicated entry:

import { serveAppFile } from '@altertable/data-app/server/bun/preview';

const preview = await serveAppFile({
  file: '/absolute/path/index.tsx',
  lakehouse,
  port: 0,
});

console.log(preview.url);
await preview.stop();

file identifies the app source. The port defaults to 25837; 0 selects an available port.

The ownership boundary is explicit:

Caller owns Preview runtime owns
Dependency installation and version selection Browser bundling and embedded host assets
Profile and app-scope validation Sandboxed iframe hosting
Credentials and lakehouse construction SQL delivery to the supplied lakehouse
Process signals and launcher lifecycle File watching, reload connections, and resource cleanup

The host supports URL-backed navigation and CSV/ZIP downloads through existing bridge contracts. App edits replace the iframe while preserving host URL filters. Bundles include inline source maps and injected app styles.

Reliable reloads and recovery

Watch imported app files through their directories so editor saves that rename files still trigger rebuilds. Exclude package build files and node_modules from app watching.

Capture dependencies before parsing and observe candidate import directories before resolution. This lets an initially broken preview recover when an imported file is fixed or a missing target is created, including relative imports and TypeScript aliases outside the entry directory.

Failed builds retain earlier watcher directories because parsing may stop before rediscovering the full dependency graph. Successful builds prune obsolete watches. Rebuilds are debounced and serialized.

The host refreshes the bundle on every SSE ready event, catching edits missed while disconnected. A changed server session triggers a full-page reload. Concurrent bundle fetches only apply the latest response, and build or fetch failures appear in the host.

Focused shared helpers

Share three private helpers where there are existing consumers:

  • A bounded request-body reader for portable operation requests and preview SQL. It counts encoded bytes while streaming and responds without waiting for an oversized upload to finish.
  • A reload channel for the preview server and development gallery. It owns session identifiers, notifications, client disconnection, and shutdown.
  • A browser downloadBlob() helper for standalone CSV exports and preview-host downloads.

Server entry points remain separate. Callers retain their routing, authorization, response formats, watchers, and debounce behavior.

Runtime and dependency boundaries

The preview server binds to 127.0.0.1, validates the request host, and accepts SQL only from its own origin. SQL requests have a 100,000-byte UTF-8 body limit. Oversized requests return a JSON query_rejected response so the host preserves the typed error.

Preview functionality is exported only through the Bun subpath. Browser entries remain free of Bun/server implementations and credentials. Production dependencies are unchanged.

get-tsconfig remains a development dependency and is bundled into the Bun preview artifact. It preserves JSONC, inherited configuration, baseUrl, and wildcard mappings without introducing a custom configuration parser or an installed frontend dependency.

Release coordination

The companion CLI change consumes this API as a thin launcher. We need to publish a package version containing the preview entry before updating the CLI’s pinned package version.

francoischalifour and others added 3 commits October 14, 2026 15:13
Add dev:file and dev:dir for apps outside the package checkout. Resolve the
package and React from the checkout build while preserving inherited app
aliases and the app's native Bun configuration.

Return the lakehouse environment explicitly, watch imported app files,
preserve URL filters when replacing the preview iframe, and recover from
build errors without restarting the launcher. Include browser and directory
consumer regressions.

Co-authored-by: Sylvain Utard <sylvain.utard@gmail.com>
Export serveAppFile() from the Bun-only server/bun/preview entry so the CLI
and checkout tooling can launch the same runtime. The caller supplies its
lakehouse and dependency directory; the runtime owns bundling, imported-file
watching, iframe reloads, SQL delivery, exports, and shutdown.

Keep server entry points separate while sharing bounded body reads and
reload channels. Share browser blob download cleanup. Preserve structured
SQL rejections, recover missing relative and tsconfig-aliased imports, and
refresh missed edits when the reload stream reconnects to the same server.
Keep startup failure alerts over the full-screen iframe without host overflow.

Bundle the existing tsconfig parser only into the Bun preview artifact.
Browser consumers gain no installed dependencies or server implementation.
Verify public workflows, packed exports, frontend isolation, and CLI usage.
Remove dependenciesFrom from serveAppFile(). Importing the preview entry already selects the runtime installation, so resolve the package and React relative to import.meta.dir.

Update the checkout launcher, public consumer tests, packed-package check, and server guide. Verify packed CLI integration with the simplified API.
Comment thread scripts/dev-dir.ts Fixed
Comment thread scripts/dev-file.ts Fixed
Comment thread tests/public-api/dev-app.test.ts Fixed

@robinvrd robinvrd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, let's just verify running without credentials gives correct guidance for it to work

@albert20260301 albert20260301 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Blocking until CI is clean: the required source/package suite fails in dev:file replaces the rendered iframe and preserves URL filters after edits (the replacement iframe never renders Second preview). CodeQL also reports three new alerts in this PR, including one critical severity alert. Please resolve both and rerun the required checks before re-review.

Wait for rendered reload results instead of a one-second polling deadline. Keep preview connection logs free of environment values and fix the reconnect test proxy to its trusted upstream.
Validate credentials when constructing the local lakehouse and before serving local apps. Report required environment variables for direct credentials or a selected CLI proxy, and stop preview launchers before starting a server.
@francoischalifour
francoischalifour merged commit a0af75c into main Oct 14, 2026
6 checks passed
@francoischalifour
francoischalifour deleted the fc/bun-preview-runtime branch October 14, 2026 14:43
@github-actions github-actions Bot mentioned this pull request Oct 14, 2026
francoischalifour pushed a commit that referenced this pull request Oct 14, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.69.0](v0.68.0...v0.69.0)
(2026-10-14)


### ⚠ BREAKING CHANGES

* remove operation output parsers; data is what run returns
([#75](#75))
* **react:** catch missing filter bindings at compile time
([#67](#67))
* **react:** unify search, filters, and suggestions
([#66](#66))
* **react:** remove obsolete host skeleton APIs
([#63](#63))

### Features

* **dev:** support external app previews with a shared Bun runtime
([#78](#78))
([a0af75c](a0af75c))
* **embed:** make bridge and query failures debuggable
([#76](#76))
([a3bcf77](a3bcf77))
* **react:** add gradient scroll cues to horizontal tabs
([#73](#73))
([446fced](446fced))
* **react:** unify search, filters, and suggestions
([#66](#66))
([5fb5387](5fb5387))
* remove operation output parsers; data is what run returns
([#75](#75))
([4411506](4411506))


### Bug Fixes

* **annotations:** restore widget targets, dismissal, and scrolling
([#64](#64))
([71d6940](71d6940))
* **charts:** improve inspection, legends, and temporal axes
([#77](#77))
([75852ad](75852ad))
* **charts:** use square corners for stacked bars
([#68](#68))
([bb27ab9](bb27ab9))
* **react:** catch missing filter bindings at compile time
([#67](#67))
([cc4d9ed](cc4d9ed))
* **react:** stretch widgets inside grid items
([#71](#71))
([b8c5475](b8c5475))
* use full widget title width and official lakehouse SDK
([#70](#70))
([51ae42a](51ae42a))
* widen app layouts and wrap grid rows sooner
([#72](#72))
([fa9fa19](fa9fa19))


### Code Refactoring

* **react:** remove obsolete host skeleton APIs
([#63](#63))
([2fa4efd](2fa4efd))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.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.

4 participants