-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Fix/publish api manifest #11142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/publish api manifest #11142
Conversation
- Update package name and description - Update page title and meta description - Update UI placeholder text and header logo - Update AI system prompt identity - Update favicon and icons - Update README with attribution - Update GitHub issue templates - Add .gitattributes for LF enforcement Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Warp <agent@warp.dev>
- Add deploy-staging.yml for Cloudflare Pages deployment - Add CI status badge to README - Add staging URL and deployment documentation - Add development setup instructions Co-authored-by: Yosi Wizman <yosi@example.com> Co-authored-by: Warp <agent@warp.dev>
* ci: add staging deployment workflow and update README - Add deploy-staging.yml for Cloudflare Pages deployment - Add CI status badge to README - Add staging URL and deployment documentation - Add development setup instructions Co-Authored-By: Warp <agent@warp.dev> * docs: update README with production status and release process - Change staging to production status - Add live URL designation - Add release process documentation - Document safeguards Co-Authored-By: Warp <agent@warp.dev> --------- Co-authored-by: Yosi Wizman <yosi@example.com> Co-authored-by: Warp <agent@warp.dev>
- Add public/_headers with COOP/COEP headers for Cloudflare Pages - Update entry.server.tsx to use credentialless COEP - Add COOP/COEP headers to Cloudflare Pages function - Add COOP/COEP headers to Vite dev server - Add crossOriginIsolated verification warning in root.tsx - Document cross-origin isolation in README Headers: - Cross-Origin-Opener-Policy: same-origin - Cross-Origin-Embedder-Policy: credentialless Co-authored-by: Yosi Wizman <yosi@example.com> Co-authored-by: Warp <agent@warp.dev>
- Add publish.ts store for state management - Add api.publish.ts API endpoint for Cloudflare Pages deployment - Add PublishButton.client.tsx UI component - Update README with publish feature documentation Co-authored-by: Yosi Wizman <yosi@example.com> Co-authored-by: Warp <agent@warp.dev>
- Use empty strings as manifest values (per CF API spec) - Use file paths with leading slash as FormData field names - Add getContentType helper for proper MIME types - Remove unused hashContent function Co-Authored-By: Warp <agent@warp.dev>
- Use empty strings as manifest values (per CF API spec) - Use file paths with leading slash as FormData field names - Add getContentType helper for proper MIME types - Remove unused hashContent function Co-authored-by: Yosi Wizman <yosi@example.com> Co-authored-by: Warp <agent@warp.dev>
Co-Authored-By: Warp <agent@warp.dev>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR rebrands "Bolt" to "X Builder" and implements an MVP publish feature that deploys projects to Cloudflare Pages via the Direct Upload API. The changes include cross-origin isolation headers (COOP/COEP) required for WebContainers, CI/CD workflows for automated deployment, and comprehensive documentation updates.
Key changes:
- Complete rebrand from "Bolt" to "X Builder" across all user-facing text and branding assets
- New publish API endpoint (
/api/publish) for deploying to Cloudflare Pages - Cross-origin isolation headers configured across all entry points to enable SharedArrayBuffer support
- GitHub Actions workflows for CI and staging deployment
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| wrangler.toml | Updates worker name from "bolt" to "x-builder" |
| vite.config.ts | Adds COOP/COEP headers to development server |
| public/favicon.svg | Updates branding from lightning bolt to "X" logo |
| public/_headers | Adds COOP/COEP headers for Cloudflare Pages |
| package.json | Updates project metadata and adds smoke test script |
| icons/logo.svg | Updates branding to match new favicon |
| functions/[[path]].ts | Wraps Remix handler to inject COOP/COEP headers |
| app/routes/api.publish.ts | New API endpoint for Cloudflare Pages deployment |
| app/routes/_index.tsx | Updates page title and meta description |
| app/root.tsx | Adds crossOriginIsolated verification check |
| app/lib/stores/publish.ts | New state management for publish functionality |
| app/lib/.server/llm/prompts.ts | Updates AI assistant name in system prompt |
| app/entry.server.tsx | Changes COEP from require-corp to credentialless |
| app/components/workbench/PublishButton.client.tsx | New UI component for publishing projects |
| app/components/sidebar/Menu.client.tsx | Removes unused IconButton import |
| app/components/header/Header.tsx | Replaces logo with text branding |
| app/components/chat/BaseChat.tsx | Updates chat placeholder text |
| README.md | Complete rewrite with X Builder documentation |
| .github/workflows/deploy-staging.yml | New deployment workflow for Cloudflare Pages |
| .github/workflows/ci.yml | New CI workflow for linting and testing |
| .github/ISSUE_TEMPLATE/config.yml | Updates issue template links |
| .github/ISSUE_TEMPLATE/bug_report.yml | Updates references to X Builder |
| .gitattributes | New file enforcing LF line endings |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * - A "manifest" field containing JSON object mapping file paths to empty strings | ||
| * - Individual file fields where field name is the file path and value is file content | ||
| */ | ||
| export async function action({ context, request }: ActionFunctionArgs) { |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The publish API endpoint has no authentication or rate limiting. Any user can publish unlimited projects to your Cloudflare account, potentially incurring costs or resource exhaustion. Consider adding authentication checks or rate limiting.
| } | ||
|
|
||
| try { | ||
| const { files, projectName = 'x-builder-preview' } = await request.json<PublishRequest>(); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The projectName parameter lacks validation. Malicious values could potentially be used in path traversal or injection attacks when used in the API URL. Consider validating that it matches Cloudflare's project naming requirements (alphanumeric and hyphens only, specific length limits).
| if (!files || Object.keys(files).length === 0) { | ||
| return json({ error: 'No files provided for publishing' }, { status: 400 }); | ||
| } |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no validation on file sizes or total payload size. A user could upload extremely large files, potentially causing memory issues or exceeding Cloudflare's size limits. Consider adding reasonable size limits and validation.
| # Only deploy after CI passes | ||
| needs: [] |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment states "Only deploy after CI passes" but the needs array is empty. If there's a CI job that should run before deployment, it should be referenced here (e.g., needs: [ci]). Otherwise, deployments will run without waiting for CI validation.
| # Only deploy after CI passes | |
| needs: [] |
| "preview": "pnpm run build && pnpm run start", | ||
| "smoke:publish": "tsx scripts/smoke-test-publish.ts" |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script 'smoke:publish' references 'scripts/smoke-test-publish.ts' but this file does not exist in the repository. This script will fail when executed.
| "preview": "pnpm run build && pnpm run start", | |
| "smoke:publish": "tsx scripts/smoke-test-publish.ts" | |
| "preview": "pnpm run build && pnpm run start" |
| for (const [filePath, content] of Object.entries(files)) { | ||
| // normalize path to include leading slash (required by CF Pages) | ||
| const normalizedPath = filePath.startsWith('/') ? filePath : `/${filePath}`; |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File paths from the request are not validated for malicious content. A user could potentially include paths like '../../../etc/passwd' or other path traversal attempts. Consider validating that paths don't contain '..' segments and are within expected boundaries.
| const response = await handler(context); | ||
|
|
||
| // clone response to modify headers | ||
| const newResponse = new Response(response.body, response); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating a new Response with response.body may fail if the response body has already been consumed or is not readable. Consider using response.clone() before creating the new response to avoid potential stream consumption issues.
| const newResponse = new Response(response.body, response); | |
| const newResponse = response.clone(); |
| /** | ||
| * Wrap handler to add COOP/COEP headers for crossOriginIsolated. | ||
| */ | ||
| export const onRequest: PagesFunction = async (context) => { |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type PagesFunction is used but not imported. This will cause a TypeScript error. Import it from '@cloudflare/workers-types' or use the appropriate type definition.
| // project might already exist (409), which is fine | ||
| if (!projectResponse.ok && projectResponse.status !== 409) { | ||
| const errorData = await projectResponse.json(); | ||
| console.error('Failed to create project:', errorData); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the project creation fails (non-409 status), the error is logged but execution continues. This could lead to deployment attempts on non-existent projects. Consider returning an error response or at least validating the project exists before attempting deployment.
| console.error('Failed to create project:', errorData); | |
| console.error('Failed to create project:', errorData); | |
| return json( | |
| { | |
| error: 'Failed to create project before deployment.', | |
| details: errorData, | |
| }, | |
| { status: projectResponse.status || 502 }, | |
| ); |
| deploymentId: deployResult.result?.id, | ||
| }); | ||
| } catch (error) { | ||
| console.error('Publish error:', error); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generic catch block doesn't differentiate between JSON parsing errors from request.json() and other errors. If the request body is malformed JSON, a more specific error message would be helpful (e.g., 'Invalid request body').
| console.error('Publish error:', error); | |
| console.error('Publish error:', error); | |
| if (error instanceof SyntaxError) { | |
| // Likely caused by malformed JSON in the request body | |
| return json({ error: 'Invalid request body' }, { status: 400 }); | |
| } |
No description provided.