Repository navigation
fix(mcp): resolve docs path from package root instead of import.meta.url depth - #4
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the void mcp CLI docs resolution to be build-depth agnostic by resolving the docs directory from the nearest package root (via walking up to package.json), fixing crashes when running from dist/ or an npm install.
Changes:
- Add
findPackageRoot()and resolve bundled/monorepo docs paths relative to the detected package root inpackages/void/src/cli/mcp.ts. - Fix the unit test docs fallback path to point at the monorepo
docs/directory (though the current relative path still appears incorrect). - Add a new dist-level integration test to validate MCP responses when running the built CLI from
dist/.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/void/src/cli/mcp.ts | Resolve docs directory from package root rather than relying on import.meta.url depth. |
| packages/void/test/unit/mcp.test.ts | Adjust test docs fallback path for monorepo docs lookup. |
| packages/void/test/integration/mcp-dist.test.ts | New integration coverage for MCP behavior when invoked via dist/cli/cli.mjs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…url depth Fixes voidzero-dev/void-platform#18 `resolveDocsDir()` used relative `../..` from `import.meta.url` to locate docs, which only worked at the source depth (src/cli/mcp.ts — 2 levels). The dist build bundles into dist/mcp-*.mjs (1 level), so `../..` overshoots the package root. Replace with a `findPackageRoot()` helper that walks up from the current file to find the nearest package.json — depth-agnostic and correct from both source and dist. Also fixes the monorepo fallback path from `website/docs` to `../../docs` (correct relative path to framework/docs/). Adds a dist integration test that spawns the built CLI and verifies the MCP server can find docs and respond correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hyfdev
force-pushed
the
fix/mcp-docs-path
branch
from
February 26, 2026 14:41
312bd1b to
078f20d
Compare
- Fix test fallback path: ../../../docs → ../../../../docs (4 levels from test/unit/ to framework root) - Buffer stdout in sendRpc until newline to avoid partial-chunk flakiness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
yyx990803
added a commit
that referenced
this pull request
Mar 7, 2026
- Remove unused imports (UserConfig, DeploymentResponse, DeploymentListResponse, eq)
- Rename unused parameter id -> _id in plugin-drizzle load handler
- Suppress intentional no-control-regex (Vite virtual module \0 prefix)
- Suppress intentional no-disabled-tests on vinext test suites
- Add error message matchers to bare toThrow() calls in runner-pg tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: resolve pg from void deps during build for PG projects
The virtual:void-db module imports pg as a bare specifier, but pg is a
dependency of void, not the user project. Rolldown cannot resolve it
during vite build. Add a resolveId hook that resolves pg to void own
copy during build. Dev mode is unaffected (Vite SSR dep optimizer
handles the bare import via node_modules traversal).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: send full asset manifest during preflight deploys
When preflight was used, the API merged the entire previous deployment's
asset manifest to fill in skipped assets. This caused stale entries from
old builds (renamed/deleted hashed files) to accumulate in the serving
manifest and DB, growing forever across deploys.
Now the CLI sends the full asset manifest as a FormData field alongside
the changed files. The API uses this as the authoritative path list
instead of blindly merging from the previous deployment. Falls back to
old behavior for older CLIs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: fix dead links to non-existent orms-and-external-dbs page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: replace empty outro calls with "Done."
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: `void db seed` works with PostgreSQL (use SQL seed file)
- Convert pg-test seed.ts to seed.sql (CLI executes raw SQL, not TS)
- Extract resetDatabase() so runSeed avoids double outro
- Deduplicate reset logic between SQLite and PostgreSQL paths
- Fix docs: default seed path is db/seed.sql, not migrations/seed.sql
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: split void/schema-helpers into void/schema-d1 and void/schema-pg
Replace the single void/schema-helpers export (which required a virtual
module to switch between SQLite and PG types at runtime) with two
dialect-specific exports that have correct types by construction.
- void/schema-d1 re-exports drizzle-orm/sqlite-core
- void/schema-pg re-exports drizzle-orm/pg-core
- Remove virtual module machinery from plugin-drizzle.ts
- Update gen.ts, tests, all playground schemas, and docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ignore worktrees
adjust docs navigation
feat: PostgreSQL support, useForm fix, PGlite tests, pg-test playground
- PostgreSQL dialect via Hyperdrive: CLI (db commands, deploy, gen),
plugin-drizzle virtual module, plugin-migration with runner-pg,
config schema, migration-handler for worker-side migrations
- React useForm: fix stale data when setData + post called synchronously
(use ref updated synchronously instead of useState closure)
- PGlite-based tests: unit tests for applyPgMigrations (8 tests),
integration test for pg-test playground with pglite-server TCP (6 tests)
- pg-test playground: React Pages mode app with PostgreSQL CRUD
- Defer dev server status messages (remote bindings, AI, missing
DATABASE_URL) until after Vite "ready" banner
- Docs: split database guide into overview + D1 + PostgreSQL pages,
remove ORMs integration page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: versioned deployments — rollback CLI, manifest v3, worker-side migrations
- Add `void project rollback [deployId]` command with interactive selection
- Add migration handler runtime for worker-side D1 migrations (v3 flow)
- Update deploy manifest: migrations as `{ dialect, pending }`, hyperdrive as `string[]`
- Add `--deployment` filter to `void project logs`
- Show live/retained/evicted status in `void project status`
- Inject `__void/migrate` route into compiled worker entry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update claude.md about new cli commands
feat: add query code mode tool to MCP server
- Export handleRequest with McpContext (pages + auth token)
- Add query tool: executes JS against read-only PlatformClient SDK
- Fix loadDocs OOM: replace readdirSync({recursive}) with findMdFiles
that skips node_modules (prevents OOM in fresh worktrees)
- Tests: remove inline handleRequest copy, add 5 query tool tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: nest reference structure/inference items under General group
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: resolve circular dep in drizzle virtual module + update framework docs
- Add importer check in resolveId to break circular dependency when
schema files import sql from void/db
- Add hook filters to resolveId and load hooks for performance
- Cache drizzle instance in both virtual module and real db.ts
- Update framework integration docs to use correct Drizzle query syntax
- Document Nuxt relational query limitation and @schema alias config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add footer to doc pages, update cloudflare integration docs
- Add DocFooter component with FooterNav on doc pages (light/dark aware)
- Reorder nav: Reference before Integrations
- Update cloudflare.md: add AI + Queue bindings, fix config structure
(inference.bindings), update compat date, recommend .env over worker.vars,
add meta-framework warning
- Add meta-framework warning to orms-and-external-dbs.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add void db studio to CLI help, database guide, and CLI reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: move sql export from void/schema-helpers to void/db
schema-helpers should only re-export drizzle-orm/sqlite-core (table
and column builders). The sql tag belongs with query operators in
void/db, where it was already exported.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add `void db studio` + fix drizzle-kit binary resolution
- Add `void db studio` command to open Drizzle Studio for local D1
- Resolve drizzle-kit binary via import.meta.resolve instead of npx
(fixes "command not found" when drizzle-kit is a dep of void, not
the user project)
- Add CJS shim + require export for void/schema-helpers so drizzle-kit
can load user schema files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add Remote Development guide
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: recommend schema-derived validators, add VoidAppFileTree component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: resolve typecheck errors across framework
- Add esbuild override to deduplicate Vite type instantiations caused
by drizzle-kit pulling in esbuild 0.25.4 alongside 0.27.3
- Gate db.d.ts inclusion in .void/tsconfig.json on hasDrizzle flag
instead of unconditionally including it
- Add drizzle-orm utility re-exports (eq, desc, sql, etc.) to the
generated db.d.ts ambient module declaration
- Narrow KVNamespaceListResult discriminated union when accessing cursor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: first-class Drizzle ORM integration
- Replace raw D1 wrapper with Drizzle ORM (virtual void/db module)
- Schema discovery from db/schema.ts or db/schema/ directory
- Auto-generate .void/db.d.ts with typed Drizzle instance
- Re-export Drizzle query operators from void/db
- Add void/schema-helpers subpath for schema definition helpers
- @schema Vite alias + resolveId for user schema imports
- void db push/generate/migrate/status/reset/seed/execute/export CLI
- void gen model scaffolds Drizzle tables + CRUD routes
- Schema drift detection on dev startup and deploy
- Use requireRuntimeBinding for env resolution (works across
SvelteKit, Astro, Nuxt, and all other meta-framework SSR contexts)
- Update docs: database, type-safety, structure, API reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: remove no longer needed comment
feat: add /__void/* binding handler for WfP dispatch
Deployed user workers now expose /__void/d1/*, /__void/kv/*, /__void/r2/*
routes that handle binding operations using native CF bindings. This allows
the proxy to dispatch operations via Workers for Platforms (~1-5ms) instead
of the Cloudflare REST API (~50-200ms).
- binding-handler.ts: Hono app with D1/KV/R2 handlers, auth via __VOID_PROXY_TOKEN
- compile.ts: inject /__void/* catch-all in compiled route table (CF targets only)
- plugin-routing.ts: resolve binding handler path
- tsdown.config.ts + package.json: build entry + subpath export
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs(skill): improve CLI task routing and add no-guess rules
Add explicit routing entries for operational CLI queries (project
status, logs, secrets, domains, db ops, auth) and strengthen working
rules to always consult cli.md before running commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update homepage features — merge framework/rendering, add AI-Native
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate workflows to Blacksmith (#17)
Co-authored-by: blacksmith-sh[bot] <157653362+blacksmith-sh[bot]@users.noreply.github.com>
feat: show plan in whoami output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(ci): install Playwright for metaframeworks test suite
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: skip SSR minification for static output (fixes prerender)
Miniflare cannot resolve template-literal dynamic imports produced
by the minifier, breaking prerendering for output: "static" apps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: remove Vinext from public docs, skip integration tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: generate wrapper in-place without re-bundling
The wrapper now operates directly in the worker directory:
- Renames original entry to __original.{ext}
- Compiles only the small cron/queue handlers (separate Vite build)
- Writes wrapper module alongside original worker files
Bundle and wrap are independent composable steps:
1. Bundle (if fwPreset.bundle) - resolves bare imports
2. Wrap (if crons/queues) - in-place, never re-bundles worker code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: SvelteKit deploy support
- Separate wrapper generation from bundling (two composable steps)
- Add bundleForWorkerd helper with caches.default shim for WfP
- Add isWorkerModule filter for CF-compatible extensions
- Handle overlapping worker/assets directories (SvelteKit)
- Add bundle: true to SvelteKit preset for bare import resolution
- Enable SSR environment build minification for smaller worker bundles
- Read actual output filename from build instead of hardcoding extension
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tweak docs taglines
docs: update tagline
fix: update Astro preset to match @astrojs/cloudflare output paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: remove internal framework class labels from user-facing logs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: use void/db instead of raw env.DB in playgrounds and docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add React Router v7 playground and integration test
- Add playground/react-router with SSR, D1, and KV smoke test
- Generate worker entry for React Router (uses createRequestHandler +
virtual:react-router/server-build since react-router lacks a
server-entry export)
- Add framework presets for all Class A frameworks (tanstack-start,
react-router, vinext-app, vinext-pages) so build output detection
is framework-informed with specific error messages
- Split vinext detection into vinext-app vs vinext-pages based on
whether app/ directory exists
- Replace guessing-based detectBuildDir/detectWorkerDir with
preset-driven resolveDistDir/resolveWorkerDirName
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
test: navigate to smoke route from home page in integration tests
Add smoke test links to astro/nuxt/sveltekit playground home pages and
update integration tests to use Playwright for click-through navigation.
Nuxt and SvelteKit tests assert SPA navigation (no full page reload).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: remove unsupported single-method endpoint file convention
The route compiler determines HTTP methods from named exports (GET, POST,
etc.), not from filename suffixes. Updated the file tree example and
structure reference to reflect actual behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: remove dead email_conflict handler from CLI auth
The API no longer returns email_conflict errors — it transparently
links multiple OAuth providers to the same account instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: support routing.headers and routing.redirects for node/bun/deno targets
Generate Hono middleware in the route table that applies header and redirect
rules when targeting non-CF runtimes. Redirects short-circuit before route
handlers (last match wins); headers are applied to all responses. Pattern
matching mirrors the dispatch worker glob-to-regex logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: tweak home demo
tweak docs style
fix type error
fix skill sync script
docs: use favicon
docs homepage
docs: add sidebar icons and restructure nav/sidebar item sharing
Add light/dark icon variants for framework logos (Astro, React Router).
Add icons to all integration sidebar items (sidebar-only, not nav).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: move guide content into guide/ subdirectory
Move all root-level .md files, pages-routing/, and edge/ into docs/guide/.
Update all cross-links, VitePress sidebar/nav config, and skill docs
sync script to recursively glob nested .md files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: move reference & integrations to dedicated sidebars with nav dropdowns
Move reference pages (structure, config, resource-inference, api, cli)
into docs/reference/ and integrations into their own sidebar. Both get
top-nav dropdowns. API and CLI sidebar entries expand to show h2 anchors.
Fix all relative markdown links across 10 files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add FileTree component with adapter tabs, global code group sync
- Add FileTree.vue with adapterTabs prop for switching React/Vue/Svelte/Solid file extensions
- Add CodeGroupSync.vue for bidirectional sync between adapter tabs and code group tabs
- Persist selected adapter to localStorage, shared key with VitePress code groups
- Simplify PagesFileTree.vue and add RoutesFileTree.vue
- Convert static file tree items to dynamic functions in layouts, islands, and markdown docs
- Remove per-page code group sync scripts (now handled globally by CodeGroupSync)
- Rename cache/ → edge/, add structure.md, update sidebar config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: align file structure with sidebar hierarchy
Move islands.md, headers.md, redirects.md into their sidebar parent
directories (pages-routing/, cache/). Remove orphan pages-routing.md.
Update sidebar links and cross-references.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(cli): add .wrangler to scaffolded .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update static assets caching for deploy-versioned edge cache
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(cli): auto create project when deploying with explciit project name in non-TTY
feat(kitchen-sink): add POST /api/reset endpoint to clear all state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(cli): add --force flag to `void project delete` for CI usage
Skips the interactive confirmation prompt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor(cli): rename `project rm` and `domain rm` to `delete`
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor(kitchen-sink): store queue results in D1 instead of KV
Strongly consistent reads for both the UI and smoke tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: restructure void.json config into routing/inference namespaces
Group 14 flat root-level fields into 8 by introducing `routing` and
`inference` namespaces, rename `wrangler` to `worker`. Migration errors
guide users from old field locations. Update all source call sites,
JSON schema, tests, sidebar structure, and docs (including ISR KV
migration and static assets caching).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(cli): add void project logs command
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(cli): show error message for failed deployments in `project status`
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: multi-provider AI — BYOK support for 20+ third-party providers
Add third-party AI provider support via CF AI Gateway. Users can call
ai.run("openai/gpt-4o", { messages: [...] }) with their own API keys.
Includes typed overloads, provider key resolution, docs, and kitchen
sink demo with model selector.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add missing `output` field to config reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: route ISR revalidation through proxy, rename __AI_PROXY → __VOID_PROXY
- Rename __AI_PROXY → __VOID_PROXY, __AI_TOKEN → __VOID_PROXY_TOKEN
- Rewrite revalidate() to call __VOID_PROXY service binding (no-op in local dev)
- Remove VOID_PROJECT_ID, VOID_REVALIDATE_TOKEN, VOID_API_URL from env.d.ts
- Log revalidation errors in kitchen-sink playground
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: prevent collectPrerenderPaths crash with FetchableDevEnvironment
fix #12, close #10
collectPrerenderPaths() creates a temporary Vite dev server to call
ssrLoadModule() on pages that export getPrerenderPaths. Without
configFile: false, it loads the user's vite.config which may include
@cloudflare/vite-plugin — this replaces the SSR environment with a
FetchableDevEnvironment whose hot channel lacks the `api.outsideEmitter`
property that SSRCompatModuleRunner unconditionally accesses, causing a
TypeError crash.
Adding configFile: false skips the user's plugin chain entirely. The
temporary server only needs basic Vite module loading (TypeScript +
node_modules resolution), which works without any config file.
Adds a test fixture (posts/[slug] with getPrerenderPaths) and
integration test covering the previously untested ssrLoadModule path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: queue consumer feature parity — per-message ack/retry, consumer options
- Add ack(), retry(), ackAll(), retryAll() methods to QueueMessage/QueueBatch types
- Update /__queue codegen with decision-tracking protocol (first-call-wins semantics)
- Update queue() export to capture response and apply decisions to real CF MessageBatch
- Add maxRetries/retryDelay extraction from queue file exports
- Update docs with per-message API and new consumer options
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
use ofetch 2.0
feat: store auth tokens in system keychain via @napi-rs/keyring (#3)
Merge feat/keyring-credentials into main with conflict resolution.
Tokens stored in OS keychain (macOS Keychain, Linux Secret Service,
Windows Credential Manager) instead of plain text in ~/.void/config.json.
Falls back to file-based storage when native addon is unavailable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add /__scheduled HTTP route for platform cron invocation
When cron jobs are defined, generate a POST /__scheduled route that
accepts { cron, scheduledTime } and delegates to the scheduled handler.
Added in both compile.ts (dev server) and wrapper.ts (deploy builds).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: deduplicate X-Powered-By header in protocol.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: set X-Powered-By header on SSR responses only, not global middleware
The global Hono middleware crashed on workerd asset responses (immutable
headers from env.ASSETS), causing all JS/CSS assets to return 500 in
production preview — breaking client hydration.
Move the header to the pages protocol response creation sites instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add X-Powered-By: Void response header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: emit x-void-page-data header on non-deferred HTML responses
Adds JSON-serialized page object as x-void-page-data response header on
non-deferred SSR HTML responses. This enables dispatch to populate a separate
JSON edge cache for pages protocol (SPA) navigation without corrupting the
HTML cache. Skipped for streaming/deferred responses where props aren't resolved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: stop spinner before log.info to avoid extra │ in deploy output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: AI proxy service binding path missing /ai/ prefix
The service binding backend was fetching https://ai-proxy/{action} but the
proxy worker mounts AI routes at /ai/{action}, causing a 404 on deployed apps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: skip staging banner for mcp command to avoid corrupting JSON-RPC stdout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: document vinext support
feat: vinext support
feat: stream deploy events via NDJSON — async generator client + streamDeploy helper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: deploy detection — SSR flag for pages mode, dedupe framework detect, simplify preset ternary
- Set manifest.ssr = true for pages-mode apps (hasPages && output !== "static")
- Lift Class A framework detection to runDeploy() so detectFramework() runs once
- Simplify redundant `options?.spa ? "spa" : "spa"` to `"spa"` in detectPreset()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: make manifest.framework a string containing the framework name
Instead of `framework: true`, the deploy manifest now carries the
detected framework name (e.g. "sveltekit", "astro", "tanstack-start").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: rename project mode to type in DeployManifest and CLI
The manifest field and all TypeScript code now use `type` instead of
`mode` for project classification (void, spa, static, framework).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
test: update secret-args tests for `list` subcommand
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: include AUTH_KV in deploy manifest when auth is detected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add `void secret list` CLI command
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: auth — lower PBKDF2 to 100k iterations, always compile OAuth routes, add env guards
- PBKDF2 iterations 310k → 100k (CF Workers WebCrypto limit)
- Include all auth providers (email/github/google) when auth is inferred,
so OAuth routes are compiled even without .dev.vars at build time
- Remove wrapper /auth/* interception — the compiled Hono app already
handles all auth routes, the wrapper was shadowing them
- Add runtime guards on OAuth routes that return 500 with clear error
message when GITHUB_OAUTH_ID/SECRET or GOOGLE_OAUTH_ID/SECRET are missing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: SSG presets registry for static site detection
Replace hardcoded VitePress detection with a registry-based approach
(SSG_PRESETS), matching the FRAMEWORK_PRESETS pattern. Adds Docusaurus
as a second SSG preset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: interactive top-level menu for bare `void` command
- Bare `void` (TTY) shows interactive select with all commands
- Subcommand groups show hint text (e.g. "login | logout | whoami | token")
- Selecting a group chains to its subcommand dropdown
- `help` option at the bottom prints full help text
- Extract printHelp() to deduplicate help text rendering
- `void --help` / `void help` still print static help text
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: update all references to renamed CLI commands
void login → void auth login, void link → void project link,
void list → void project status, etc. across docs, error messages,
skills, and runtime warnings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add follow-up prompts, auth group, and consolidate project commands
- Add multi-select and repeat-input follow-up phases to subcommand prompt
(gen model → column input, gen route → method selection)
- Move login/logout/whoami/token under `void auth` subcommand group
- Move list/link under `void project` (status/link subcommands)
- Support optional args ([name]) that allow empty submit in prompt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: restructure project-related CLI commands
- void projects → void project list
- void delete → void project rm (with interactive dropdown)
- void domain remove → void domain rm
- Remove void ls alias (keep void list)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: interactive subcommand prompt for two-level CLI commands
When users type `void secret`, `void domain`, `void db`, or `void gen`
without a subcommand in a TTY, show an interactive prompt with arrow-key
navigation, descriptions, and inline text input for required args.
Subcommands with optional flags (e.g. `void db export`) transition to a
second multi-select phase for flag toggling with inline value input.
Extracts `runSubcommandGroup` helper to deduplicate the interactive/
non-interactive branching, arg construction, and error handling across
all four command groups.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: replace _redirects generation with redirect rule collection in deploy manifest
Same approach as header rules: parse framework _redirects files at deploy
time, merge with void.json redirects config, and include RedirectRule[]
in the deploy manifest. Moves headers.md and redirects.md docs to top-level.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: fix outdated test
refactor: replace _headers generation with header rule collection in deploy manifest
Remove generateHeadersFile — Workers for Platforms does not respect _headers
files. Instead, parse framework-generated _headers files and void.json headers
config at deploy time, then include headerRules in the DeployManifest so the
platform can apply them at the edge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: add hashedAssetsPrefix to static site deploy path
packageStaticBuild and runStaticDeploy were missing the
hashedAssetsPrefix parameter, so purely static sites (VitePress,
SPAs) never got edge-cached hashed assets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: resolve oxlint warnings
Move no-control-regex disable comments to correct lines and add
toThrow message for prefetch test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: node-static playground + integration test, fix Node prerender build order and manifest injection
Add playground/node-static (React Pages, target: node, output: static) with two
pages that prove prerendering runs in Node.js via process.versions.node.
Fix two bugs in the Node prerender pipeline:
- Swap buildApp order to SSR-first so dist/ssr/app.js exists when the
prerender plugin fires in the client closeBundle hook.
- Inject globalThis.__VITE_MANIFEST__ in the prerender runner so the SSR
renderer can generate production script/link asset tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: auto-detect fully-static output and deploy without worker
When output: "static" and all pages are prerendered with no backend
features (routes, middleware, crons, queues), skip the full deploy
pipeline and delegate to runStaticDeploy with the pre-built client dir.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: rename app types — fullstack → void, prebuilt → static
Aligns naming across config, CLI, preset detection, and docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add hashedAssetsPrefix to deploy manifest
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add serveWithAssets helper with cache headers for static assets
Route all requests through the user worker (run_worker_first: ["/**"])
so the entry can set proper Cache-Control headers on static assets.
Hashed assets get immutable/1yr, non-hashed get s-maxage=60.
Edge caching will be added at the dispatch layer (design doc 0042).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: deploy asset dedup via preflight hash comparison
Add client-side asset hashing and preflight support to reduce deploy
upload size. CLI computes SHA-256 hashes locally, asks API which assets
changed since last deploy, and only uploads the diff.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
adjust docs re deployment and framework relationships
test: use dynamic ports for integration tests to avoid conflicts
Create shared test/integration-helpers.ts with getPort(), waitForServer(),
and killChild() helpers. Update all 13 integration test files to use
OS-assigned ephemeral ports instead of hardcoded ones, eliminating
failures when tests run in parallel or multiple agents run concurrently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add Node.js/Bun/Deno integration guide
Covers target configuration, supported runtimes, build output, what
works vs what differs from CF mode, and a full example with API routes
and pages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add target config for Node.js, Bun, and Deno deployment
Add `target` field to void.json (`"node"`, `"bun"`, `"deno"`) that
replaces the Cloudflare vite-plugin with a lightweight nodeTargetPlugin.
This enables deploying Void apps to any Node-compatible runtime without
CF bindings.
- New `plugin-node-target.ts`: binding guard, dev server (Hono via SSR),
preview server, dual-environment build (client + SSR) for pages apps
- Split build output into `app.js` (Hono app export) and `index.js`
(HTTP server startup) for programmatic use and vite preview
- `compileNodeApp`, `compileNodeServer`, `compileNodeClient` in compile.ts
- CF-only imports (void/db, void/kv, etc.) produce clear compile errors
- Integration tests and playground/node-app (React Pages + API routes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: remote D1/KV/R2 bindings for local development
Add proxy-backed binding implementations (ProxyD1Database, ProxyKVNamespace,
ProxyR2Bucket) that forward operations to the platform proxy over HTTPS.
When `remote: true` is set in void.json (or VOID_REMOTE=1), the plugin
wraps the worker entry point to replace local miniflare bindings with
proxy-backed versions, allowing local dev against real deployed resources.
- ProxyClient: HTTP client with JWT auth and projectId injection
- createRemoteEnv: wires proxy-backed bindings into worker env
- Entry-point wrapping for both fetch and queue handlers
- Console output showing active remote bindings
- Updated AI proxy URL to unified proxy (proxy.void.cloud/ai)
- Integration test with mock HTTP server (15 tests)
- Unit tests for all proxy-backed bindings (47 tests)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: expand wrangler config merging with per-mode behavior and build output details
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: deduplicate ~3,000 lines of shared adapter code into void core
Extract duplicated code from vue/react/svelte/solid adapters into shared
modules in the void core package:
- void/pages: generateComponentManifest (was 4x114 lines)
- void/pages-prefetch: PrefetchCache, parseCacheFor (was 4x120 lines)
- void/pages-client: createPageRouter factory, VoidRouter, ssrProxy,
form utils, island hydration helpers (was ~430 lines of untyped
template strings per adapter, now ~70 lines each)
- void/pages-islands-plugin: resolveSpecifier, needsIslandClientBundle,
CSS helpers, computePagesNeedingClientJS, configureIslandCssServer
(was ~280 identical lines per adapter)
~350 lines of previously untyped template literal code is now real,
type-checked TypeScript in void/pages-client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: fix dead link + db intro
fix: remove Workers AI binding fallback that breaks CI
The direct AI binding fallback triggered the @cloudflare/vite-plugin remote
proxy session, which requires Cloudflare login — failing in CI. AI in dev
now requires Void credentials via the HTTPS proxy path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
test: replace auth/queue integration tests with kitchen-sink e2e suite
Playwright-based e2e tests covering SSR, SPA navigation, layout
persistence, DB, KV, Storage, Env, Queues, and Auth — all exercised
through the browser UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add auth to kitchen-sink, remove standalone ai/auth/queues playgrounds
The kitchen-sink now exercises every major feature (DB, KV, R2, AI, Auth,
Cron, Queues, Env, ISR) so the standalone playgrounds are redundant.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(kv): default get() return type to string instead of unknown
KV natively stores strings, so `kv.get()` should return `string | null`
by default. Users can still override with `kv.get<MyType>()` for JSON.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: auto-detect file uploads in useForm across all adapters
When router.visit() data contains File, Blob, or FileList objects,
automatically convert to multipart/form-data instead of JSON. Follows
the Inertia.js approach with recursive hasFiles() detection and
objectToFormData() conversion supporting nested objects and arrays.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(vue): make useRouter() safe to call during SSR
Instead of throwing when called during SSR, return a proxy that throws
descriptive errors only when methods are actually invoked (e.g.
"router.reload() is not available during SSR"). This lets components
call useRouter() in setup() without crashing server-side rendering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor(kv): rename set() to put() to align with Cloudflare KV API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add create-void package
Enables `npm create void@latest` one-liner to scaffold a new Void project.
Prompts for project name, fetches latest dep versions from npm registry
in parallel, writes package.json, installs, and runs `void init`.
Zero runtime deps — @clack/prompts and picocolors are inlined via tsdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: remove run_worker_first in dev, rely on viteEnvironment routing
With viteEnvironment: { name: "ssr" }, the CF plugin's final handler
routes unmatched requests to workerd automatically. Vite's middleware
handles virtual modules (/@vite/*, /__uno.css, /src/*, etc.) first,
so no exclusion patterns are needed in dev. run_worker_first is now
only used in production for CF edge routing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
use Vite Environment API for default mode
Add viteEnvironment: { name: "ssr" } to the default mode cloudflare()
call, matching framework mode. This uses CloudflareDevEnvironment for
better HMR transport and produces a predictable dist/ssr/ output
directory. Simplify detectWorkerDir to just check for dist/ssr/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update framework guides
fix: separate dev and prod run_worker_first exclusions
Production builds now only include `["/**", "!/assets/*"]`, preventing
dev-only patterns (like `!/src/*`) from shadowing prerendered pages.
Dev exclusions (`!/@*`, `!/__*`, `!/src/*`, `!/node_modules/*`,
`!/pages/*`) are added dynamically when `command === "serve"`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: rename db.md → database.md, orms.md → orms-and-external-dbs.md
Rename doc files for clarity and write a full guide on using Drizzle ORM
with D1 in Void, covering schema definition, client setup, CRUD queries,
joins, batch operations, and migrations. Also covers Kysely, raw D1, and
external databases via Hyperdrive. Updates all cross-references in docs,
VitePress config, and skill metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: support auxiliaryWorkers option in void plugin
ci: add workflow_dispatch to docs workflows
ci: bump hook timeout
add fail-fast: false to ci
voidcloud.dev -> void.app
update lockfile
split test suite into vitest projects for CI and local parallelism
Define five named vitest projects (unit, void-integration, metaframeworks,
pages-react-vue, pages-svelte-solid) with fileParallelism disabled for
integration groups. CI now references npm scripts instead of raw vitest
patterns, which also fixes md integration tests never running in CI.
Fix port 5221 collision between named-layouts and markdown-blog tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main'
feat: named layouts — page-level layout selection via `_layouts/` directory
Pages can opt into named layouts via `export const layout = "name"` or
markdown frontmatter. Supports replacing innermost layout, exclusive mode
(`!name`), and opting out entirely (`layout: false`). Named layouts live
in `_layouts/` directories and resolve by walking up the directory tree.
Includes integration test with playground app exercising all modes, unit
tests for scanner and resolution, and docs update.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: update build script
feat: add defineHead<P>() typed wrapper for page head exports
Adds a defineHead identity function (like defineHandler, defineMiddleware,
etc.) that provides CloudContext + props type inference for head() exports
in .server.ts files. Also adds HeadDescriptor type export and head schema
to void.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
skip client JS bundle for zero-JS pages
Split assetTags from { head, body } to { css, preloads, body } so island
renderers can conditionally omit JS modulepreload links on pages that need
no client JavaScript.
- Skip pages-client build entry when all pages are islands
- Skip islands-client build entry when no island imports, CSS, or md scripts
- Conditional preloads in island renderers based on __pagesNeedingClientJS
- Add zero-js playground to verify the optimization
- Update SSR scaffold templates, docs, and tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: md plugin owns its client entry, all adapters get md script support
Move md client script loading (script map, data-md-script lookup, dynamic
import) from the Vue adapter's island client entry into the md plugin's own
virtual:void-md-client module. This gives all four framework adapters
(Vue, React, Solid, Svelte) automatic md client script support.
- md plugin generates and registers virtual:void-md-client as a separate
build input alongside virtual:void-islands-client
- Routing plugin passes md client entry ID to compile options
- compile.ts emits md client script tag in asset tags (dev + prod)
- All 4 adapters query md plugin API for data-md-script attribute and
computePagesNeedingClientJS includes pages with md scripts
- Vue adapter cleaned up: removed md code from island client entry
- Added integration tests for md client entry separation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: rename `mode` to `appType` in void.json config
Avoids confusion with Vite mode and dev/prod mode. The field specifies
the application type for deployment, so `appType` is clearer. Old
`mode` key now throws a migration error pointing users to `appType`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: mention static output deployability in app-types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add Head Management page under Pages Routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: head management for Pages mode
Add server-side head resolution with three-layer merge precedence
(page head() > middleware headDefaults > void.json head config).
- HeadDescriptor/HeadConfig types with title, meta, link, script,
htmlAttrs, bodyAttrs support
- resolveHead() merge logic with titleTemplate, meta dedup by
name/property, link/script concatenation, attrs shallow merge
- renderHeadToString/renderHtmlAttrs/renderBodyAttrs serialization
- head() export in .server.ts files receives (c, props)
- void.json head config with titleTemplate support
- Middleware headDefaults via c.set("headDefaults", {...})
- Client-side head updates on SPA navigation (data-void-head attr)
- Markdown auto-head from frontmatter title/description
- All 8 SSR renderers (4 frameworks × pages + islands) updated
- Unit tests (28) and integration tests for react-pages + markdown-blog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update app-types for per-worker assets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add README for @void-x/md package
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
make the markdown blog app static
feat: generate _headers and _redirects from void.json config
Wire void.json `headers` and `redirects` config into the deploy pipeline
by generating _headers and _redirects files in the build output. These are
natively supported by Cloudflare Workers Static Assets.
- Add generateHeadersFile() and generateRedirectsFile() to headers.ts
- Replace hardcoded _headers generation in deploy with config-driven generation
- User-provided _headers/_redirects in public/ take precedence (skip generation)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(vue): extract JS-style CSS imports in island production builds
The production client entry only extracted <style> blocks via
?vue&type=style queries, missing JS-style CSS imports like
`import "void/md/theme.css"` in <script setup>. The dev path
worked because extractCss() handles both types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: rename mode "ssg" to "prebuilt", split SSG docs
- Rename `mode: "ssg"` to `mode: "prebuilt"` across config, preset
detection, packaging, schema, tests, and docs
- Rename prerendering.md to "Edge Prerendering" (deploy-time only)
- Extract build-time SSG content into new docs/ssg.md page
- Add SSG as last item in Routing & Rendering sidebar group
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add `output: "static"` for build-time prerendering
Adds a new `output` config option to void.json. When set to `"static"`,
`vite build` prerenders all pages by spinning up Miniflare with the built
worker, writing production HTML files to `dist/client/`. The output is
self-contained and works for `wrangler deploy`, self-hosting, or `void deploy`.
- Add `output` to VoidConfig with "server" | "static" validation
- Modify scanPages() to default prerender=true in static mode
- Extract collectPrerenderPaths() into shared module (used by plugin + deploy CLI)
- Add prerender engine (Miniflare + ASSETS service binding for production asset URLs)
- Add Vite plugin (closeBundle hook, gated on output: "static" + client env)
- Add setShowTimestamp() to log module for cleaner build output
- Update deploy CLI to skip deploy-time prerender when output is static
- Add redirects support to config validation
- Add JSON schema entry for output field
- Add static-pages playground and integration test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: rewrite coding agents page with skills and MCP sections
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: exclude .env.local files from deploy manifest
loadEnv() loaded all .env variants including .local files, silently
bundling secrets as plain_text bindings. Replace with loadDeployEnv()
that only reads .env and .env.production.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs updates
fix: resolve island IDs to root-relative paths in markdown pages
Island components imported in markdown <script> blocks were not hydrating
because the SSR output used raw relative specifiers (e.g. "../_ThemeToggle")
as data-island IDs, while the client manifest used root-relative paths
(e.g. "pages/_ThemeToggle"). The hydration lookup failed silently.
Resolve specifiers in the md transform hook (where importer path and root
are available) and pass the resolved ID through IslandImport. Also fix
test assertions to match actual script output and add island interactivity
test that clicks the ThemeToggle button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(mcp): resolve docs path from package root instead of import.meta.url depth (#4)
* fix(mcp): resolve docs path from package root instead of import.meta.url depth
Fixes voidzero-dev/void-platform#18
`resolveDocsDir()` used relative `../..` from `import.meta.url` to locate docs,
which only worked at the source depth (src/cli/mcp.ts — 2 levels). The dist
build bundles into dist/mcp-*.mjs (1 level), so `../..` overshoots the package
root. Replace with a `findPackageRoot()` helper that walks up from the current
file to find the nearest package.json — depth-agnostic and correct from both
source and dist.
Also fixes the monorepo fallback path from `website/docs` to `../../docs`
(correct relative path to framework/docs/).
Adds a dist integration test that spawns the built CLI and verifies the MCP
server can find docs and respond correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address review comments
- Fix test fallback path: ../../../docs → ../../../../docs (4 levels from
test/unit/ to framework root)
- Buffer stdout in sendRpc until newline to avoid partial-chunk flakiness
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
fix: rebuild island CSS map on file changes for react, solid, svelte
Same fix as the Vue islands plugin — the islandCssMap and virtual
renderer module were computed once at buildStart and never updated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tweak markdown blog example
fix unit test
docs: update md theme exports to .css extension, expand docs
Theme exports now use explicit .css extension (./theme.css,
./theme-content.css) so the islands CSS detection regex can
match them. Docs updated with client scripts section, two
theme entry points, CSS variables reference, and Svelte/Solid
layout examples.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
md default theme include reset
fix: rebuild island CSS map on file changes in dev
The islandCssMap and virtual renderer module were computed once at
buildStart and never updated, so adding/removing <style> tags in
island components had no effect until a full server restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: support client scripts in markdown pages
Non-island code in markdown <script> blocks is now bundled and executed
in the browser when the page loads. Island imports (with { island })
continue to work as before — everything else becomes client-side code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: escape newline in island renderer CSS join for rolldown compat
The .join("\n") inside the outer template literal in
generateIslandRenderer() produced a literal newline in the generated
code, which rolldown parser rejected as an unterminated string.
React and Vue already used the correct "\\n" - Svelte and Solid
were missing the extra escape.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add prefetch support to React, Svelte, and Solid adapters
Port the prefetch system from the Vue adapter to all three remaining
framework adapters. Includes PrefetchCache with SWR, Link component
prefetch strategies (hover/click/mount/visible), route matching for
client-side prefetch, and cache hit logic in visit() with deferred
prop handling. Updates playground apps to use prefetch props matching
the Vue playground patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add dark mode toggle to markdown-blog playground
Add data-theme attribute support to all @void-x/md theme CSS so manual
light/dark override works alongside prefers-color-scheme. Each dark
media query is gated behind :root:not([data-theme="light"]) and
duplicated under :root[data-theme="dark"].
Add ThemeToggle island component that cycles system/dark/light with
localStorage persistence, imported into the blog layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: use client transform for dev CSS middleware + skip duplicate CSS imports
Vite 8 SSR transform returns empty code for CSS modules, breaking the
virtual .vue.css middleware that serves render-blocking stylesheets.
Switch to client transform which produces __vite__css = "...".
Also skip ?vue&type=style imports in the island client entry during dev
since CSS is already served via render-blocking link tags -- prevents
duplicate style injection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
perf: switch shiki to JS RegExp engine with lazy language loading
Replace @shikijs/markdown-it with direct shiki highlighter using
@shikijs/engine-javascript (no WASM). Languages are loaded on demand
at first use instead of all upfront. Fence renderer is now async to
support lazy loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: skip prefetch for current page + handle deferred props in prefetch cache
- Link skips prefetch when href matches current URL
- Server keeps deferredKeys in prefetch responses
- Client wraps pre-resolved deferred props in deferred refs on cache hit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add prefetch to vue-pages playground
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add Link prefetch with hover/click/mount/visible strategies
Inertia-style prefetch for the Pages router. The <Link> component
accepts prefetch and cacheFor props to preload component JS and page
data on hover, click, mount, or viewport visibility. Cached responses
use TTL with stale-while-revalidate. Server resolves deferred props
inline when it sees the Purpose: prefetch header. Island routes fall
back to <link rel="prefetch"> for HTML prefetch via build-time route
metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
perf: defer Shiki init to first .md transform (~3x faster dev startup)
The md plugin was eagerly initializing Shiki (WASM grammar + theme loading)
and fully compiling all .md files during buildStart just to extract metadata.
Now scanMdPages uses a lightweight extractor (gray-matter + markdown-it-anchor
only), and the full Shiki compiler is loaded lazily on first transform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: restore HeaderRule type removed in manifest v2 migration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add dev server restart plugin with binding detection
Add restartPlugin for controlled dev server restart via the browser,
avoiding the race condition where Vite auto-reconnect fires before
miniflare is ready. New binding imports (void/db, void/kv, etc.) are
detected mid-session and surfaced as a terminal log + browser toast
with click-to-restart.
- Extract restart endpoints and binding-hint virtual module into plugin-restart.ts
- Inject binding-hint via virtual module in compiled dev HTML (SPA, SSR, Pages)
- Add shared voidLog/voidWarn/voidError logging utilities
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
test: add integration tests for markdown-blog playground
Cover style injection, sidebar rendering, post navigation, markdown
content, and blog index in both dev and production (build + preview).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add markdown plugin documentation page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: migrate to per-worker static assets (manifest v2)
Remove R2-based asset management from CLI in favor of Cloudflare native
per-worker static assets. CLI no longer computes asset hashes, checks
for needed assets, or manages headers — CF handles all natively via
_headers files and asset config.
- Bump DeployManifest to version 2, add AssetConfig type
- Remove computeAssetHashes, checkAssets, HeaderRule from CLI
- Remove globalThis.__VITE_MANIFEST__ inlining hack
- Generate _headers file for cache control in dist/client/
- Pass assetConfig instead of headers/assetsDir/assetHashes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: prevent FOUC on island pages in dev mode
Inject render-blocking <link rel="stylesheet"> tags in island page HTML
during dev. Virtual CSS modules extract styles from page/layout source
files, served through Vite's SSR transform pipeline to ensure full CSS
processing (@import resolution, PostCSS, pre-processors).
- Add configureServer middleware to serve virtual CSS as text/css
- Add islandCssFiles/islandCssMap for dev CSS collection in buildStart
- Vue adapter detects <style> blocks in addition to JS CSS imports
- resolveId returns absolute paths for correct @import resolution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: make void init framework-aware
Detect meta-frameworks during init and adapt the setup flow:
- Skip empty-project scaffolding and demo for meta-framework projects
- Create/update wrangler.jsonc with compatibility_date and nodejs_als
for class B/C frameworks (SvelteKit, Nuxt, Astro)
- Show framework-specific dev command in outro (nuxt dev, astro dev)
- Fix playground tsconfigs to extend .void/tsconfig.json and include
void/env types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update meta-framework integration docs for runtime helper support
Nuxt and Astro void/db works in dev now — update overview table, simplify
Nuxt examples, reorder sidebar by CF-nativeness, remove dev-mode caveat section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: remove dead __void_cf_env fallback, update docs
Remove getVoidDevEnv() and globalThis.__void_cf_env — nothing sets this
global after the svelteKitEnvBridgePlugin was replaced with hooks injection.
Simplified env fallback chain:
1. AsyncLocalStorage (SvelteKit dev via hooks injection)
2. globalThis.__env__ (Nuxt dev)
3. cloudflare:workers (production workerd)
Update framework docs to describe actual mechanisms instead of the old
getPlatformProxy() shim approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: migrate userspace domain from vitecloud.app to voidcloud.dev
Temporary domain until void.app domain transfer completes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: replace SvelteKit env bridge with hooks injection
Replace svelteKitEnvBridgePlugin (getPlatformProxy + globalThis.__void_cf_env)
with svelteKitHooksInjectPlugin that transforms get_hooks() to wrap handle with
withRuntimeEnv(event.platform.env, ...). This uses AsyncLocalStorage to make
void/db work in SvelteKit dev without a second Miniflare instance or private APIs.
- Add env.ts as named tsdown entry (produces dist/runtime/env.mjs)
- Add void/_env subpath export for hooks inject plugin consumption
- Remove @sveltejs/kit/internal/server dependency from env.ts
- Simplify fallback chain: ALS → __void_cf_env → __env__ → cloudflare:workers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
clean up before nuxt test
default to writing wrangler.jsonc instead of json in framework mode
make sure to tearndown wrangler config before astro integration test
update framework interop docs
ignore wrangler configs for framework integration tests
fix(void): inject nodejs_als in early wrangler hook
astro playground working
fix: infer bindings from astro frontmatter
fix: align class b/c migrations with wrangler state
bump wrangler
fix: reload browser on .md page edits in dev
Markdown pages are implicit islands (server-rendered, no client JS) but
the file watcher only triggered reload for `.island.*` filenames.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: make island css work during dev
fix: unify compat date resolution with platform default
readWranglerCompat() now merges void.json > wrangler.jsonc > built-in
default (2026-02-12), always returning a date. Both deploy paths
(full and framework) pass the resolved value to the manifest. Added
YYYY-MM-DD format validation in void.json and wrangler config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
remove void.json (nuxt has auto inference)
fix(env): ensure platform built-ins work in Nuxt and SvelteKit
fix theme css
feat(md): add markdown emitters for react svelte solid
feat: @void-x/md plugin for markdown support
remove platformProxy hack
fix asm warning
fix: inject CSS from island page/layout files for React, Svelte, Solid
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: inject CSS from island page/layout .vue files on client
The islands client entry only imported interactive island components,
leaving page and layout CSS missing in both dev and production. Add a
?island-css virtual query plugin that extracts <style> block imports
from .vue files, allowing @vitejs/plugin-vue to handle them natively.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: reposition as SDK, add platform section to introduction
Reframe Void from "framework" to "SDK" to reduce competitive tension
with other Vite-based frameworks. Lead with DX motivation, present
framework compatibility as the default path, and add a dedicated
platform section covering edge deployment, caching, and scaling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: meta-framework integration tests (SvelteKit, Nuxt, Astro)
Add integration test playgrounds and tests for SvelteKit (Class B),
Nuxt (Class C), and Astro (Class C). Each validates binding sync,
SSR, and D1/KV smoke routes.
Adds cfWorkersShimPlugin to Class B/C voidPlugin() to resolve
cloudflare:workers via getPlatformProxy() during Vite dev. Nuxt and
Astro use framework-native binding access in dev (Nitro/Astro SSR
bypass Vite plugin pipeline). Docs updated with dev-mode caveats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: auto-static should not gate on island imports
Island imports add client-side hydration but do not affect whether the
server-rendered HTML is deterministic. Simplify the rule: island page +
no loader + no params = auto-prerender, regardless of island imports.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: dead links in docs (cache directory, false positive index)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cleanup default export usage after merge
Merge remote-tracking branch 'origin/main'
docs update on metaframework support
feat: meta-framework static asset caching (_headers + hashedAssetsPrefix)
Add hashedAssetsPrefix to FrameworkPreset (SvelteKit: _app/immutable,
Nuxt: _nuxt, Astro: _astro) — sets manifest.assetsDir so dispatch uses
correct immutable cache headers and stable edge cache keys.
Parse framework-generated _headers files during deploy, merge with
void.json headers (framework first, void.json overrides). Handles
SvelteKit's X-Robots-Tag and Nuxt's per-path cache overrides.
Exclude _headers/_redirects from asset uploads.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
breaking: Move `voidPlugin` from default to a named export. (#1)
feat: auto-static detection for island pages + prerender max revalidate
Island pages with no `with { island }` imports, no loader, and no
dynamic params are pure static HTML — auto-set prerender = true.
Any page with prerender = true defaults to max revalidate (1 year)
unless the user explicitly sets a revalidate value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add getUser() universal auth API
Shared AsyncLocalStorage-backed authContext in void/auth that both the
Hono session middleware (Void apps) and the deploy wrapper
(meta-frameworks) populate. getUser() reads from it, providing a
single framework-agnostic function for accessing the authenticated user.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: multi-framework deploy support (SvelteKit, Nuxt, Astro)
Add support for deploying SvelteKit, Nuxt, and Astro apps via `void deploy`.
Framework detection reads package.json dependencies and routes to the
appropriate build + packaging pipeline. voidPlugin() provides binding
inference, DB types, migrations, and wrangler.jsonc sync for all three.
- Framework presets with known build output conventions per framework
- Wrangler.jsonc reader (JSONC parser) + binding sync for local dev
- Wrapper entry generation for auth/crons/queues (bundled with Vite)
- void.json: string binding names, `build`, `cache.prerender`, `scanDirs`
- DeployManifest: workerMain, compatibilityDate, compatibilityFlags
- packageFrameworkBuild() handles worker/asset dir overlap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cli: randomize ascii art in help banner
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix inference scan dirs, add resource inference docs, improve cli docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
better docs structure
ci: auto-retry flaky adapter integration tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add VoidZero credit in aside slot
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
type Cloudflare.Env to eliminate (env as any) casts in runtime
Extends the Cloudflare.Env declaration with platform-injected bindings
(__AI_PROXY, __VOID_TOKEN, etc.), ISR bindings, and a template literal
index signature for QUEUE_* bindings. Adds env.d.ts to tsconfig include.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ci: simplify playwright install to single --with-deps command
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
move docs location
docs: adjust structure
feat(vue): runtime props patching for InferProps support
Strip defineProps<T>() generics at compile time (enforce: "pre" transform)
so the Vue SFC compiler never sees unresolvable types like InferProps.
Patch component props at runtime from loader results before first render.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add deferred props section and InferProps examples
Add deferred props documentation to pages-routing.md covering defer(),
Deferred<T> discriminated union, component handling in all 4 frameworks,
and grouped deferred props. Also add InferProps<typeof loader> pattern
to the server handlers section.
Fix SSR missing prop warning by including loading-state placeholders in
immediateProps from separateDeferred().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: deferred props with discriminated union type and integration tests
Add defer() API for streaming deferred props from loaders. The Deferred<T>
type is a discriminated union (loading/resolved/rejected) enabling exhaustive
narrowing without assertions. Includes SSR streaming via script tags with a
queue mechanism for the race condition between inline scripts and module load,
NDJSON streaming for SPA navigation, and integration tests across all four
framework adapters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat!: remove <script server> support in pages mode
Simplifies the framework by removing the <script server> SFC block
extraction pipeline. Companion .server.ts files are the sole mechanism
for colocated server logic in pages.
Removes: plugin-script-server.ts, virtual ?server module resolution,
inline server block detection in scanner, type extraction/rewriting in
codegen, and all associated tests.
Migrates vue-pages playground from <script server> to .server.ts files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add islands mode documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: Solid islands support -- partial hydration for @void-x/solid
Adds islands mode to the Solid adapter, matching existing React/Vue/Svelte
support. Island components hydrate independently using configurable strategies
(load, visible, idle, media) while the rest of the page stays static HTML.
Key design: virtual .tsx wrapper modules (/@void-island-wrap/) that
vite-plugin-solid compiles for both SSR and client. Uses render() instead
of hydrate() to avoid Solid tree-position-dependent hydration …
Brooooooklyn
added a commit
that referenced
this pull request
Jul 9, 2026
…t KV FIX #3 — segment-aware revalidate (parity with void-platform dispatch): - collectRevalidate (isr-config.ts): only named/anonymous catch-alls (:name{.+}, *) collapse to '*'; single-segment params (:slug) stay as :slug so the matcher can anchor them to ONE path segment. - matchLiteralPattern (isr-cache.ts): whole-segment :name -> [^/]+ via a U+E000 private-use sentinel (survives regex-special escaping, expanded after). Mid-segment literal ':' (e.g. /api/v1:health) left alone. '*' still crosses '/'. Strict SUPERSET of the old '*'-only matcher, so old '*'-only manifests match identically (rollout safety). Byte-identical to the shared cross-repo matcher spec. - Manifest emission changes again (single-seg pages now :slug, not '*'); updated the pinned keys in isr-config/package tests. FIX #4 — host-scoped self-host ISR KV keys + host registry: - isrKey -> void-isr/<id>/<host>/<path>; readIsrCache/writeIsrCache/ deleteIsrCache/handleIsr/revalidateInBackground thread `host` (url.hostname). One host never reads/serves another host's cached bytes. - writeIsrCache registers the host under void-isr-hosts/<id>/<host> (guarded by an in-isolate Set, fail-open). New listIsrHosts(kv, id) enumerates them. - revalidate() self-host purge (isr.ts): {paths} enumerates registered hosts (union __VOID_ISR_HOST) and purges each host's KV + edge; {all} splits the host off the scanned key tail before denormalizing. KV purge never depends on caches.default (edge purge guarded, fail-open). Empty host set no-ops. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cpojer
pushed a commit
that referenced
this pull request
Sep 11, 2026
cpojer
pushed a commit
that referenced
this pull request
Sep 11, 2026
* fix(dispatch): forward x-void-internal token on queue relay
The framework-generated /__void/queue handler on the user worker
requires an x-void-internal header matching __VOID_PROXY_TOKEN. The
dispatch queue relay was omitting that header, so every queue message
hit 401, dispatch called retryAll, and messages exhausted CF retries
silently (no DLQ, consumer never ran, user-side job rows stuck at
status=queued, attempts=0).
- packages/api: include proxyToken in the ROUTING[queue:*] entry,
sourced from the __VOID_PROXY_TOKEN worker binding.
- packages/dispatch: read proxyToken from the routing entry and set
x-void-internal on both the /__void/queue and legacy /__queue relay
requests.
Existing deployments need one redeploy to populate proxyToken in their
KV entry. Older entries without the token are tolerated (header
omitted) so a rolling rollout does not break backward compatibility.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(observability): surface queue-path failures to users (bump framework)
Close the three silent failure modes we found while diagnosing the
missing x-void-internal header bug:
1. packages/tail/src/index.ts parseScriptName: recognize void-dispatch
and void-dispatch-staging under reserved __platform-dispatch* project
keys. Previously all dispatch tail events were dropped at ingest, so
relay failures were invisible even to the admin UI.
2. packages/api/src/provisioning/cloudflare-api.ts addQueueConsumer:
replace the silent console.warn + return path with a discriminated
return value (created | updated | conflict). Callers can now react
to a different script owning the consumer.
3. packages/api/src/routes/deploy.ts: when addQueueConsumer returns
conflict, emit a new queue_consumer_conflict deploy event so it
reaches the CLI. Deploy still completes so other queues can succeed.
Paired with the framework submodule bump (8088bb9) which adds the
queue_setup and queue_consumer_conflict cases in the CLI switch and
makes the conflict a log.warn so it persists past the spinner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(queue-relay): address Codex review findings on proxyToken rollout
Four changes driven by the adversarial review of the original relay-
token fix:
1. Rollback persists proxyToken too. packages/api/src/routes/projects.ts
rewrites every queue:* KV entry on rollback; it now writes the same
deterministic hmacSignState(projectId, JWT_SECRET) token the deploy
path writes, so a rollback no longer silently regresses queue
delivery to the 401/retry loop. Integration test now asserts
proxyToken on the rolled-back entry.
2. Dispatch logs structured relay failures. packages/dispatch/src/
index.ts emits console.error on missing-routing, non-2xx response
(with status + 200-char body snippet), and thrown fetch paths before
calling batch.retryAll. Tail already ingests void-dispatch* under
__platform-dispatch, so these surface in operator logs instead of
burning retries silently.
3. queue_consumer_conflict is now fatal. packages/api/src/routes/
deploy.ts collects conflicts during the Promise.all, still emits the
per-queue event so CLI can surface it via log.warn, then throws
after the sweep so the SSE stream ends with an error event instead
of done. CI and automation clients now see a non-success signal.
4. Admin-triggered backfill for legacy queue:* KV entries. New
packages/api/src/admin/backfill-queue-tokens.ts iterates with
pagination and stamps the deterministic token on any entry missing
one. Exposed as POST /admin/backfill-queue-tokens so operators can
run it once after the rollout without a full redeploy sweep.
Idempotent — re-running only touches entries still missing the
token. Fully unit-tested.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(queue-relay): close the three findings from Codex review pass 2
1. Two-pass queue setup (critical). packages/api/src/routes/deploy.ts
previously wrote ROUTING[queue:*] inside the Promise.all, then threw
after the sweep if any queue reported conflict — overwriting live
routes with pointers to a worker the failure cleanup was about to
tear down. Split the loop: pass 1 registers consumers and collects
conflicts, throws early if any, then pass 2 flips KV only for a
known-good deployment. New regression test in api-deploy.test.ts
seeds a live queue route, runs a conflicting deploy, and asserts
the live route is untouched.
2. Proxy tokens read from worker bindings, not re-derived. Rollback
and backfill previously called hmacSignState(projectId, JWT_SECRET)
to produce the token dispatch hands to the user worker. That only
works if JWT_SECRET has not rotated since that worker was deployed
— otherwise the worker's baked __VOID_PROXY_TOKEN binding does not
match and every /__void/queue relay 401s. New
getDispatchWorkerProxyToken() fetches the exact baked value via CF
API /workers/dispatch/.../bindings. rollback and backfill both use
it with a re-derivation fallback (and warning) for legacy workers
missing the binding. Tests assert the exact baked value flows
through.
3. Backfill re-reads before write. packages/api/src/admin/backfill-
queue-tokens.ts snapshots each queue:* entry, resolves the token,
then re-reads right before the put. If the entry changed in between
(concurrent deploy or rollback) the write is skipped and counted
under a new 'raced' field. Mitigates the KV write-write race
without requiring a maintenance lock. Concurrency test simulates
the exact interleaving and asserts the concurrent writer's value
survives.
Backfill endpoint signature changed: now takes {accountId, apiToken,
dispatchNamespace, jwtSecret} + optional fetchToken override for tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(queue-relay): typed proxy-token lookup + cron rollback uses baked token
Address the remaining two findings from Codex review pass 3:
Finding 2 (cron rollback still re-derives): rollback's cron DO sync
previously called hmacSignState(projectId, JWT_SECRET). After a
JWT_SECRET rotation that would 401 every tick of a rolled-back
worker's crons (the framework validates x-void-internal against the
worker's baked __VOID_PROXY_TOKEN binding, not the current secret).
Both queue and cron rollback now call a shared resolveRollbackProxyToken
helper that fetches the exact baked token via CF API. New integration
test asserts the cron DO sync body carries the mocked baked value.
Finding 3 (binding-read errors silently fall back to re-derivation):
getDispatchWorkerProxyToken now returns a discriminated
ProxyTokenLookup ({kind: 'found' | 'missing' | 'error'}). Callers
distinguish:
- found: use the baked token.
- missing: legacy worker; fall back to re-derivation with a visible
warning (operator responsibility to redeploy if secret has rotated).
- error: transient CF failure; rollback re-throws (transaction
aborts), backfill logs + counts under new 'errored' field and
skips the entry so a 429/5xx cannot masquerade as a successful
migration.
Backfill result shape gains `errored` to separate transient CF
incidents from legitimate skips. New unit test covers the error path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: file Codex review-3 deferred findings as design docs
Findings #1 (transactional queue_setup) and #4 (backfill concurrency
safety) from Codex adversarial review pass 3 are legitimate but deserve
their own scoped design passes rather than more incremental patches on
the current branch.
- 0062-transactional-queue-setup.md: documents the residual window
between queue_setup pass 2 and routingSwitched where a cron_setup or
cancellation failure can leave KV pointing at a torn-down worker.
Three approaches (compensating restore, deferred writes, Postgres
source of truth) with B recommended short-term, C long-term.
- 0063-backfill-concurrency-safety.md: documents the bulk-migration
race with concurrent deploys/rollbacks. KV has no CAS; re-read-before-
put shrinks but does not close the window. Four approaches (global
lock, per-project lock, Postgres reconciler, maintenance-gate) with D
recommended today and C long-term.
No code change. Follow-ups are tracked in these docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update framework
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cpojer
pushed a commit
that referenced
this pull request
Sep 11, 2026
…host metadata (#132) P0 prod hotfix. PR #131's cross-script DO host architecture was 100% broken for sandbox deploys: every user-worker PUT was rejected by CF with `DURABLE_OBJECT_NOT_CONTAINER_ENABLED`. Root cause: putSandboxHostScript's metadata omitted the `containers: [{ class_name: 'Sandbox' }]` declaration. CF requires the script that OWNS a DO class to declare it as container-enabled — without that, cross-script DO bindings to that class are rejected. Fix: * Add `containers: [{ class_name: 'Sandbox' }]` to host metadata in the base object (applies to both first-create and re-PUT branches of the migration_tag protocol). Tells CF the Sandbox class is container-backed; the actual container app is still POSTed via the /containers/applications API. * Hoist `putSandboxHostScript` to run UNCONDITIONALLY at the start of `provisionSandboxHost` (after lock + status check, before resource lookup). The PUT is idempotent (migration_tag preserves namespace), and ensures host-metadata fixes propagate to existing tenants on their next deploy. The cached fast path can short-circuit the namespace lookup + container POST as before; only the host PUT becomes mandatory. * Drop redundant putSandboxHostScript call from create-or-recover branch. * Drop the redundant host-script HEAD check in verifyAndReconcileCached (we just PUT it). The live binding probe stays — still validates external host delete + recreate. Mock CF API hardening: helpers.ts now rejects user-worker PUTs that have a cross-script DO binding (script_name set) when the referenced host's last-PUT metadata.containers does NOT contain a matching class_name. Mirrors CF's actual validation; would have caught this regression in tests had it been in place. CF-shaped error envelope with code 10103 and the literal `DURABLE_OBJECT_NOT_CONTAINER_ENABLED` message. Tests: * Unit #2/#3: assert ONE host PUT (was zero), body OMITS migrations (tag preserved), INCLUDES containers signal. * Unit #4 retitled — with always-PUT, cached fast path now succeeds. * Unit #9/#10/#10b: assert containers signal present in BOTH migration_tag branches. * Integration #2/#3: assert ONE host PUT per redeploy, no namespace rotation, no container POST/DELETE/PATCH on unchanged-config redeploys. * Integration #12: seedHostScript now includes containers metadata so the success-path sanity check survives the new mock validation. Verification: pnpm check / lint / fmt --check all clean. Unit sandbox-host: 20/20. Integration api-sandbox: 11/11 + 1 skipped. api-deploy + api-delete: 169/169 unchanged. Co-authored-by: Claude Opus 4.7 <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.
Fixes voidzero-dev/void-platform#18
Problem
void mcpcrashes with "Void docs directory not found" when run from a dist build or npm install.resolveDocsDir()usesimport.meta.urlto find the docs directory by counting relative..segments:But the build bundles into
dist/mcp-*.mjs(1 level deep instead of 2), so the same../..overshoots:The monorepo fallback also uses the wrong path (
website/docsinstead ofdocs).Fix
Walk up the directory tree from the current file to find the nearest
package.json, giving us a depth-agnostic package root. Then resolve docs paths relative to that root.Also fixes the monorepo fallback path from
website/docs→../../docs(correct relative path toframework/docs/).Test plan
pnpm -C packages/void build— rebuild succeedspnpm test packages/void/test/unit/mcp.test.ts— 11/11 unit tests passpnpm test packages/void/test/integration/mcp-dist.test.ts— 2/2 dist integration tests pass (new)pnpm lint— 0 warnings, 0 errorspnpm fmt— cleanecho '{"jsonrpc":"2.0","id":1,...}' | node dist/cli/cli.mjs mcpresponds correctly🤖 Generated with Claude Code