mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-29 08:07:48 +09:00
codex/headers-settings-docs
906
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f467e415e1 | refactor: share published route parameter conversion | ||
|
|
68043499ca | refactor: share route overlap and test fixtures | ||
|
|
20758af52c | test: cover shared route matching edge cases | ||
|
|
d779985e8a | fix: match project rules with published routes | ||
|
|
3d0f19ffe0 |
fix: complete and speed up publish validation (#6478)
## Summary Complete Validate for large projects with linked MDX content, using the same validation API in the dialog and MCP. Publishing continues independently of diagnostics. - Load MDX sources omitted from the embedded artifact and report storage failures with typed diagnostic reasons. - Reuse parsed MDX and template scope indexes; cache source reads in the shared asset loader. - Exclude conservative non-MDX query candidates while retaining explicit invalid references for diagnostics. - Give validation requests a 60-second server execution budget. - Support both server bundles in E2E tests and make OAuth logging initialization repeatable. - Raise the project bundle import limit from 20 to 32 MiB to reproduce the customer project. ## TODO / acceptance checklist - [x] Import and inspect the customer project locally and on staging. - [x] Reproduce missing embedded MDX sources and slow template materialization. - [x] Load omitted MDX sources and discover nested template dependencies. - [x] Report source-read failures using typed reasons rather than matching error messages. - [x] Reuse template scope indexes and remove duplicate source caching. - [x] Route dialog Validate through the same API as MCP, after flushing editor content. - [x] Preserve publishing in parallel with diagnostics, including failed or unfinished checks. - [x] Fix JSON collection files being treated as MDX query candidates. - [x] Preserve explicit invalid file references so they still produce diagnostics. - [x] Fix E2E routing across both generated server bundles and repeated OAuth logging initialization. - [x] Demonstrate the new review regressions fail before the fixes and pass after them. - [x] Run targeted tests, relevant typechecks, lint, formatting, package boundaries, and generated API checks. - [x] Run the full fixture pipeline and production builder build; no generated fixture changes. - [x] Build the exact staging project locally after the final review changes. - [x] Validate the exact staging project through MCP/CLI after the final deployment. - [x] Complete an actual staging publish after the final deployment. - [x] Address PR comments: simplify the stream mock, explain every diagnostic reason, and retain the existing diagnostic code contract. - [x] Review documentation impact; these fixes restore expected behavior and require no user instruction changes. - [x] Finish CI on the final commit, including all six Builder E2E shards. ## Verification Local checks passed: 179 project-build tests, 78 CLI prebuild tests, 8 asset publication tests, 42 server tests, and 9 publish UI/helper tests. The imported staging project also builds successfully. The new explicit-reference regression and repeated-initialization regression both failed before their fixes and passed afterward. A production build serves the login route and dispatches tRPC requests to its separate server bundle. Validation on staging project `b9d23e54-5dbf-45c4-b6d8-bc4cf842a468` completed in 25.3 seconds after deployment and 23.5 seconds after publishing: 92 documents included, 0 omitted, and 0 MDX errors. The [original staging publish job rerun](https://github.com/webstudio-is/webstudio-saas/actions/runs/36647770120/job/109819143695) succeeded end to end with the final artifact, including CLI/Remix builds, worker deployment, and database updates. [CI on commit `01a9d772dc`](https://github.com/webstudio-is/webstudio/actions/runs/36696154831) is green, including all six Builder E2E shards and Windows/Ubuntu fixture checks. The comment follow-ups simplify the publication test mock and document all diagnostic reasons. Production behavior is unchanged. The 8 publication tests, 6 schema tests, package typecheck, formatter, and lint pass. Disabling the source cache makes the simplified regression fail as expected. Final staging validation passed in 27.1 seconds with 92 documents and no omissions or MDX errors. One additional publish attempt hit a database statement timeout during project sync, before the build. A [retry with no code changes succeeded end to end](https://github.com/webstudio-is/webstudio-saas/actions/runs/36647770120/job/109826767589). CI is green on `01a9d772dc`. Ref #3550 |
||
|
|
a6a9049399 |
fix: validate published MDX before publish (#6468)
## Related issue Related to #3550. ## Summary - Run the existing published-MDX materializer during pre-publish validation and surface its error diagnostics before starting dynamic or static publishing. - Reuse the dependency walk that builds the publication artifact to limit validation to reachable Content Blocks, including blocks whose asset is missing. - Add actionable fix hints for unavailable MDX assets and invalid HTML nesting. - Show a compact error/warning count in the publish dialog, with a details dialog that has counted Errors and Warnings tabs and hides empty tabs. - Include relevant IDs and navigation links in reports when available, with guidance when they are not; allow users to copy all reports and select the text. - Resolve missing-document IDs against loaded asset metadata and show the filename and Content Assets folder path when available. The page-tree pre-publish audit already checks HTML content models, but it cannot see content generated from MDX assets. This extends preflight to that content using the same materializer and diagnostics as the publish build. It also catches unavailable MDX assets before the publish job starts. ## Plan and status ### Implemented - [x] Reuse existing publish-time MDX discovery and materialization so preflight checks the content that will be published. - [x] Collect every page-tree audit error and warning, not only the first finding for each severity. - [x] Collect every MDX publish error and provide a location and remediation hint. - [x] Collect missing or ambiguous template warnings and content-database omission warnings. - [x] Show a compact error/warning total in the publish dialog. - [x] Add a details dialog with standard panel padding, spaced and separated findings, and counted Errors and Warnings tabs; hide empty tabs. - [x] Copy all reports from the dialog header and allow report text selection. - [x] Include diagnostic codes and relevant instance, asset, document, and resource IDs in reports. - [x] Keep “See issues” visible in the narrow publish panel. - [x] Keep element navigation available from page-tree audit findings and link content findings when the target is available. - [x] Use the same findings UI for dynamic and static publishing. - [x] Block publishing on errors and allow warnings to proceed. - [x] Keep unexpected preflight failures visible to the user. - [x] Show a preflight failure either in the open publish dialog or in a toast when the dialog is closed, avoiding duplicate reports. - [x] Replace opaque “Document … could not be loaded” errors with actionable guidance, including the asset filename and folder path when available. - [x] Load linked document sources during MDX validation and prebuild when they are absent from the compact content artifact. ### Regression checks completed - [x] No-findings validation preserves the normal publish path. - [x] Single and multiple findings show accurate totals and details. - [x] The “See issues” action is visible in the publish panel and opens diagnostics. - [x] “Show element” navigation remains wired to the affected element and closes the publish dialog. - [x] More than five MDX errors and more than ten missing-template warnings remain available in diagnostics. - [x] Mixed MDX errors, audit errors, template warnings, and database warnings are combined and counted. - [x] Errors stop dynamic and static publishing; warnings do not block publishing. - [x] Error-only, warning-only, and mixed states show the right tabs and counts, with empty tabs hidden. - [x] Copy-all includes every error and warning with context, and report text is selectable. - [x] An open dialog receives inline preflight failures without a toast; a closed dialog receives a toast without a duplicate inline report. - [x] Missing-document errors include filename/folder context when available and useful fallback guidance when metadata is unavailable. - [x] Reproduce the linked-author failure with the affected project’s hosted build, then confirm it builds successfully with the fix. ## Verification - `pnpm --filter @webstudio-is/project-build test -- src/runtime/published-mdx.test.ts` (26 tests passed) - `pnpm --filter @webstudio-is/project-build typecheck` - `pnpm --filter webstudio typecheck` - Affected project: `webstudio build --template react-router` reproduced the document load error before the fix and completed after it. - `pnpm --filter @webstudio-is/builder test -- app/builder/features/publish/publish-validation-results.test.tsx app/builder/features/publish/content-database-publish-warning.test.tsx app/builder/features/publish/publish-error.test.ts app/services/build-router.server.test.ts` (26 tests passed) - `pnpm --filter @webstudio-is/builder typecheck` - `pnpm exec oxfmt --check` on changed files - `git diff --check` - Reviewed dynamic/static preflight and element-navigation code paths. |
||
|
|
e1e5bc152e |
feat: append response header rules for last-match precedence (#6460)
Response headers now use list order when more than one rule sets the same name on a path: the last matching rule wins. New or edited rules currently move to the top of the Builder list, which would make older rules override them. Append those rules instead and explain the precedence in the settings UI. Authentication list behavior is unchanged. Validation: SDK response-header tests and typecheck, CLI response-header tests, SDK build and declaration build passed. |
||
|
|
9b1100f25b |
fix: Restore hosted Assets queries (#6454)
Hosted Assets queries started returning 502 in 0.301.0 because publish sync omitted their query database. Restore that database for hosted builds while keeping Markdown body-reference queries on demand. Static exports still prepare their database in the build runner. Reject builds with Assets queries but no database before changing generated files, so this failure cannot silently produce another broken deployment or erase the previous generated output. Uses the existing shared query compiler; no MCP-specific logic or API schema changes. Closes #6450 Ref #6427 Replaces #6453 with the same commits on `assets.staging`, which matches the staging deployment branch policy. ## Checklist - [x] Reproduce the reported 502 and verify the fix on project and custom origins. - [x] Preserve static export behavior and on-demand Markdown loading. - [x] Add a regression test for rejecting missing databases; demonstrate failure before the fix and success afterward. - [x] Review documentation impact and update the Content Engine publishing explanation. - [x] Run relevant tests, Builder/CLI typechecks, lint, formatting, and package-boundary checks. - [x] Run the full fixture link, sync, and build pipeline against the patched preview API. Regenerate all eight JSON snapshots with the current formatter; parsed data is unchanged and generated application files are unchanged. Validation: 88 CLI tests, 22 Builder tests, and four no-body-read publication tests pass. Fixture size checks were unavailable without compiled app bundles. Agent evaluations were not run. Existing affected sites need republishing after the fix is deployed. |
||
|
|
d78e770d82 |
perf: Keep article indexing out of publish API requests (#6427)
Ref #6416 The publish workflow was doing too much work in one short-lived API request. This moves content indexing into the build runner and keeps article bodies out of hosted/SSR sync. ## What changed - The build-bundle API can return the lightweight project bundle without preparing a full content index. - The CLI downloads required assets directly from storage and builds the index locally for SSG. Asset downloads remain bounded to 10 concurrent requests. - Hosted/SSR sync skips Markdown and MDX article downloads and does not compile an article index. It still downloads regular assets needed by the build. - Markdown body queries use stored frontmatter and fetch only the requested article at runtime. MDX still needs source downloads and compilation for SSG. - Public MCP and content-engine docs explain the Markdown/MDX tradeoff. This removes article parsing and article-body transfer from the tRPC request. SSG still downloads article sources from storage and compiles MDX in the build runner. Hosted runtime article reads remain on demand. ## Checklist - [x] Keep the tRPC bundle request limited to project metadata and necessary records. - [x] Prepare SSG content indexes in the build runner; keep hosted article bodies out of sync. - [x] Cover no-body query preparation, direct asset reads, and SSG-versus-hosted sync behavior with regression tests. - [x] Update public CLI, MCP, and content-engine guidance. - [x] Regenerate and verify fixtures. - [x] Run workspace tests, typechecks, lint, formatting, package-boundary, generated API/docs, and fixture-size checks. - [ ] Verify the deployed publish flow has comfortable margin below the hosted API timeout. ## Verification - `pnpm -r --workspace-concurrency=1 test`: passed, including all 3,082 Builder tests and 1,150 CLI tests. - `pnpm -r typecheck`, `pnpm lint`, `pnpm check:package-boundaries`, `pnpm check:generated-api`, `pnpm check:generated-docs`, `pnpm check:fixture-sizes`, and `pnpm exec oxfmt --check`: passed. - The new hosted article-download regression test was confirmed to fail with the old behavior and pass after the fix. - A deployed end-to-end publish timing check is still needed, so this PR remains draft. |
||
|
|
df7ed327ab |
refactor: clarify pathname rule matchers (#6446)
Authentication and custom response headers use the same route-rule syntax, but the shared matcher and validator had auth-specific names. Give those functions generic names in `wsauth` and update the Builder header UI and SDK response-header schema to call them directly. All basic-auth logic remains in `wsauth`; this change adds no package or re-export. The project-build page matcher is different: it uses `URLPattern` and returns decoded parameters. Rename it to `matchUrlPattern` and document the behavior difference. For example, `/docs/*` matches `/docs` for authentication and headers, but not for the page matcher. Tests cover both behaviors. This renames the two public `wsauth` route exports. Published workers pinned to the older package retain the old API; the SaaS worker can adopt the new names after this package is released. Validation: `wsauth` tests, including its packaged consumer test; SDK page and response-header tests; project-build route and redirect tests; SDK, `wsauth`, project-build, and Builder typechecks. |
||
|
|
be74d8d698 |
fix: Validate response headers and expose MCP rule edits (#6444)
Header rules previously accepted `null` values, which silently turned a rule into a header-removal instruction even though the settings UI only offers adding a string value or deleting a rule. Require a nonempty string throughout the SDK, Builder, CLI generator, and API contract. Expose configured rules to MCP through `list-response-headers`, `set-response-header`, and `delete-response-header`. Setting a rule adds or replaces the header for the same route and case-insensitive name; `/` and `/*` remain distinct. The Builder UI and MCP operations now share the same rule-key, edit, and Zod validation logic. The generated JSON Schema advertises the header-name pattern and nonempty value, while server-side Zod validation enforces route syntax, forbidden headers, duplicates, and total size. The generic `update-project-settings` operation still supports replacing the whole list. Validation: SDK, project-build runtime and MCP, CLI command and prebuild tests; SDK, Builder, project-build, CLI, and protocol typechecks; lint, package boundaries, generated API and docs checks. Local fixture sync against the development API is blocked by differing bundle versions; PR CI tests fixtures against its deployed Builder host. |
||
|
|
a4c3b37fc2 |
fix: Unblock reported MCP workflows and improve issue diagnostics (#6438)
## What this fixes
MCP authors can reuse an existing design token in JSX fragments, create
valid border shorthands with CSS variables, and bypass a font that never
settles during responsive screenshots. Common fixed page titles with
spaced separators are also accepted. Issue reports now include the
project ID and safe, bounded IDs from the failed operation, plus clearer
response and browser-startup diagnostics.
## Scope and tradeoffs
- `token("name")` attaches an existing design token; an unknown name
returns a not-found error. Empty token definitions from Builder/UI
remain definitions and follow the normal conflict rules.
- Border shorthands map a single unresolved `var()` to the remaining
width, style, or color field when the other fields are explicit.
- `waitForFonts: false` skips font readiness but still waits for stable
layout frames. The default remains unchanged.
- Fixed titles such as `Pricing - Plans` stay literal. Addition such as
`first + second` remains an expression. Wrap ambiguous subtraction in
parentheses, for example `(first - second)`. Other ambiguous title forms
may still need an exact report.
- Only recognized operation-level IDs are attached to a public GitHub
issue. Authored values, raw arguments, URLs, and credentials are
excluded. IDs from later successful diagnostic reads are not captured.
- This does not fix the underlying response-transform failure; it
improves future reports for that case.
Closes #6424
Closes #6425
Closes #6434
Closes #6439
Ref #6435
Ref #6430
Ref #6417
## Verification
- [x] Reproduced the privacy, page-expression, empty-token, and
content-mode token-reference regressions with failing tests, then
confirmed the fixes pass. A regression test also covers parenthesized
subtraction.
- [x] Full protocol, template, vision, project-build, and CLI test
suites pass; focused Builder issue-report and fragment tests pass. The
protocol, CLI, and project-build suites were rerun after the CI fix.
- [x] Relevant package typechecks, lint, package-boundary check, fixture
pipeline, generated API check, and generated docs check pass. Fixture
output has no diff.
- [x] Reviewed the authoritative MCP documentation and regenerated the
API metadata and MCP docs.
- [x] CSS parser regressions cover border, each side shorthand, and
unresolved variables in width, style, and color positions; css-data
tests, typecheck, and lint pass.
- [x] Fixed the local evaluation runner to pass the task and Webstudio
MCP tools to Luna/low. Each of the six fixtures passed at least once;
focused MCP/evaluator tests, CLI and project-build typechecks, lint, and
formatting pass.
- [x] The complete Luna/low suite passed all six fixtures in two
consecutive sequential runs. Baselines remain incompatible with this
model/effort override and were not updated.
## Follow-up
- If another fixed-title form still fails, use the new project-level
report context and the exact title shape to narrow the parser case.
|
||
|
|
2c9fd16689 |
feat: configure custom response headers by route (#6429)
## Description Project Settings starts with an empty response-header rule list. Users can add valid HTTP response headers for all paths (`/*`) or an Authentication-style route such as `/private/*`, and remove any configured rule. The Headers and Authentication views share the accessible rule-list form. The header form suggests standard response-header names and common values for the selected header, while accepting custom names and values. `Set-Cookie` is not suggested because a rule can store only one value per header and would replace existing cookies. `X-Powered-By`, `X-Content-Type-Options`, and `Strict-Transport-Security` are dispatcher-owned and cannot be configured. Users may configure `Content-Security-Policy`, `X-Frame-Options`, and `Referrer-Policy`; when absent, the dispatcher supplies their defaults. The SDK validates header names, values, routes, duplicates, and size limits. The CLI emits only configured rules. Customization requires Pro for custom-domain publishing, while unchanged defaults and staging remain available on Free plans. The server checks the saved production snapshot against the project owner's plan. Companion Cloud worker PR: https://github.com/webstudio-is/webstudio-saas/pull/664. Deploy its dispatcher support before exposing these settings in the Builder. Other hosts need an adapter that applies the generated rules; Webstudio Cloud dispatcher defaults do not apply automatically elsewhere. Static exports do not apply headers automatically. ## Validation - SDK, CLI, Builder browser, publish-warning, and production-build tests pass. - Builder, SDK, and CLI typechecks and lint pass. - The companion Cloud worker tests, typecheck, and lint pass. Replaces https://github.com/webstudio-is/webstudio/pull/6428 to use `headers.staging`. Closes https://github.com/webstudio-is/webstudio/issues/4748 |
||
|
|
cd64a6627e |
fix: Load unbound resource previews in the variable dialog (#6432)
New resources can fail to show data in the **New variable** dialog because a page-plan refresh cancels their preview request before it finishes. This change keeps explicitly requested previews alive until the dialog releases them. Changing inputs, changing resource type, or closing the dialog cancels obsolete work and prevents old data from showing. Unused page resources remain lazy; a preview still makes an on-demand request when **Load data** is clicked. The dialog stays usable while unrelated page resources are loading. Follow-up to #6030. ### Checklist - [x] Reproduce the cancellation with a regression test that fails before the fix. - [x] Keep dialog previews independent of page resource discovery, using the existing loader, cache, and request versions. - [x] Clear old previews on input changes and release requests on dialog close. - [x] Cover new Current date, Sitemap, HTTP, and existing unbound resources, plus repeated loads and stale responses. - [x] Keep tests for obsolete page-request cancellation and async resource dependencies passing. - [x] Test the unsaved dialog with an unrelated page request pending and a page-plan recalculation. - [x] Clear the old preview when switching resource types or starting an asynchronous body edit. - [x] Review documentation impact: no update needed; this restores the documented preview behavior. - [ ] Confirm GitHub CI passes. ### Verification - The dialog loading, type-switch, and body-edit regression tests failed before their fixes and passed afterward. - 82 focused tests and the settings-panel suite (588 tests) passed. - Builder typecheck and repository lint passed. - With preview retention temporarily disabled, all nine new loader regression cases failed as expected. |
||
|
|
3d0a0657aa |
feat: Support async resource dependencies (#6030)
## Summary Make `Resource<Document>` the lazy document-resolution abstraction shared by Content Engine graphs, generated sites, and Builder previews. Expressions use one async computation interface: referenced resource documents resolve first, independent requests run concurrently, and unused resources stay unloaded. Ref #6029 Ref #6031 ## Implementation checklist - [x] Define one resource contract for synchronous or asynchronous document resolution. - [x] Resolve explicit roots lazily with memoization, bounded concurrency, cancellation, and structured errors. - [x] Preserve cycle, duplicate, and missing-resource diagnostics. - [x] Extract resource dependencies with the existing expression AST. - [x] Discover consumed resource roots from the current page tree and page metadata. - [x] Generate a lazy request graph for published page data, including independent requests. - [x] Resolve dependency documents before evaluating dependent request expressions. - [x] Integrate dependency-first loading with Builder preview caching and invalidation. - [x] Expose safe Resource bindings in the editor and reject self/indirect cycles. - [x] Adapt REST, GraphQL, Assets, date, and sitemap requests through the same Resource contract. - [x] Cover laziness, chains, shared dependencies, concurrency, invalidation, cancellation, failures, and cycles with fail/pass tests. - [x] Review documentation impact and document dependent Resources in the Data variables guide. - [x] Track collection-item and component-parameter-dependent resources as follow-up #6031. - [x] Regenerate and review affected fixtures. - [x] Resolve action-resource request dependencies when an action is invoked. - [x] Reproduce and fix cached form submissions and multi-root dependency-cycle hangs. - [x] Document uncached form actions and regenerate affected fixtures. - [x] Preserve single and multi-selected content until a cut successfully writes to the clipboard. - [x] Preserve boolean expression values in the search-exclusion checkbox. - [x] Cancel cut deletion if project data changes while preparing or writing the clipboard; cover content/property edits and project switches for single and multi-selection, and document the retry behavior. - [x] Validate pending cuts after the final await, immediately before deletion, and reproduce the queued-update race for single and multi-selection. ## Technical direction The persisted, revisioned `DocumentGraph` remains the Content Engine artifact. `Resource<Document>` is the execution abstraction for obtaining a document; its resolver evaluates only the dependency closure reachable from requested roots. Static and remote documents therefore differ by Resource implementation, not by expression value type. Generated pages always emit a `ResourceRequestGraph`. Each node owns its output name, dependencies, and request factory. Roots come from resource variables actually referenced by the current page's metadata, props, actions, or expression children. Unused and off-page resources remain unresolved. The loader still accepts legacy request maps for compatibility. Builder preview uses the same page-root discovery as published generation. Its existing cache remains the lifecycle boundary: cached dependency documents unlock dependent requests, while invalidation removes downstream requests until their inputs resolve again. The separate canvas frame recomputes its local plan from the synchronized cache so rendered expressions receive those documents. Action requests and their dependencies resolve when a form is submitted. Each submission sends a new action request, bypassing Webstudio's resource cache. Dependency requests still use their configured cache lifetime. ## Review simplifications - Consolidated page resource-root discovery in the SDK and reused it in Builder. - Scoped root and action discovery to the current page tree. - Removed the redundant immediate-resource helper; `Resource.resolve` already accepts `Document | Promise<Document>`. - Moved output names onto graph nodes and removed the duplicate graph-level map. - Removed unused planner output and corrected transitive cycle discovery to include only Resource data sources. - Regenerated fixtures so independent resources also use lazy graph execution. - Reused one abort-aware concurrency limiter for document sessions and Resource resolution. - Collapsed duplicate generator action scans and removed redundant generated-resource bookkeeping. - Unified legacy request maps and request graphs through one resolution path. - Shared the 20-request concurrency limit between SDK, Builder preview, and the server batch boundary. - Preserved dependency failure paths and original causes, including the first cancellation reason. - Removed invalid-request logging that could expose Resource headers or request bodies. - Made editor cycle detection alias-safe by comparing underlying Resource IDs. - Memoized page settings values to prevent repeated async validation renders. ## Verification Latest local verification for `bbca6395e3`: - Both queued-update regression cases failed before the fix and passed afterward. Validation now runs synchronously immediately before deletion, after the final await, in both cut paths. - Passed: 255 copy/paste and command tests, Builder typecheck, lint, package boundaries, formatting, and diff checks. - Reviewed documentation: the existing copy/paste safety description remains accurate. Fixture inputs and outputs are unaffected; no fixture regeneration was needed. Agent evaluations were not run because separate approval is required. - CI for the new commit is pending. Previous local verification for `ffbc75b152`: - Six regression cases failed before the fix and passed afterward: content edits, property edits, and project switches during a pending cut, with both single and multiple selections. - Passed: 253 copy/paste and command tests, Builder typecheck, lint, package boundaries, source formatting, and diff checks. - An unchanged content-block test file initially failed to import in the browser. Its isolated rerun and the full 253-test rerun passed without code changes. - The guard conservatively treats any project-data change as a reason to keep the clipboard copy without deleting content. It shows a retry message. Copy/paste documentation now describes this behavior. - Fixture inputs and outputs are unaffected; fixtures were not regenerated. Agent evaluations were not run because separate approval is required. CI for the new commit is pending. Previous local verification for `3eb580697c`: - Reproduced both regressions with failing tests before fixing them: premature deletion during cut and false search exclusion appearing checked. - Passed: 203 copy/paste and page-settings tests, Builder typecheck, lint, package boundaries, formatting, and diff checks. - Cut coverage includes keyboard and programmatic entry points, single and multiple selections, delayed successful writes, rejected writes, and disabled copying. - Reviewed documentation impact: these fixes restore existing behavior; no documentation update is needed. Only Builder UI and clipboard files changed; fixture inputs and outputs are unaffected, so fixtures were not regenerated for this commit. - CI status for this older commit was recorded as pending at handoff. Agent evaluations were not run; they require separate approval. Previous local verification for `858b348ad`, rebased onto `cf606ca4c`: - Regression tests failed before each fix and passed afterward: repeated submissions in both generated route templates, plus three multi-root/branching cycle cases. - Passed: 590 SDK tests, 1,129 CLI tests, 72 focused Builder tests, SDK/CLI/Builder typechecks, lint, package boundaries, and fixture link/sync/build. - Initial concurrent runs hit timeouts. The complete CLI suite passed with one worker; the focused Builder suites passed without file parallelism. - Documentation and generated fixture changes reviewed. Agent evaluations were not run; they require separate approval. - CI for `858b348ad` passed: Main (including all six E2E shards), Visual Regression, Lint PR, and Check submodules. Earlier verification (before this update): TDD fail/pass evidence includes resolver creation, AST dependency discovery, published graph reachability, dependency-gated request generation, Builder dependency-first planning, Resource editor cycle filtering, page-tree root scoping, off-page action isolation, transitive Resource-only cycle discovery, abort/timeout precedence, server batch limits, and the rebased SSG fixture consuming its lazy Resource. - `pnpm checks` passed after rebasing onto `ce84ddcf12`; it covered workspace tests, typechecks, lint, package boundaries, generated API/docs validation, and fixture link/sync/build. - Earlier focused checks passed: Builder typecheck, 12 page-settings/pages unit tests, lint, formatting, and `git diff --check`. - Local generated-resource E2E passed for HTTP, GraphQL, and current-date resources (3 tests). - The page-actions and Marketplace page-settings persistence E2Es both pass locally. - CI for `6b8b08a70` passed: Main workflow (including all six Builder E2E shards), Visual Regression, Lint PR, and Check submodules. - Agent evaluations were not run because they require separate explicit approval. ## Current behavior and limitations - A failed dependency blocks its dependents and reports the resource path and cause. - One published resolution call is the memoization boundary; Builder maps dependency readiness onto its versioned request-cache lifecycle. - Scoped collection-item and component-parameter resources remain in #6031. - Builder skips cyclic dependency paths without blocking independent resources. |
||
|
|
fc15ebf81a |
perf: avoid repeated MDX parsing in publish diagnostics (#6407)
## Description The cold `build.contentDatabasePublishDiagnostics` path was parsing the same reachable MDX documents while the publication plan converged, then parsing them a third time to produce omission diagnostics. Content-heavy projects can approach or exceed the 15-second serverless limit on that first request. This change: - creates a request-scoped MDX dependency resolver that reuses parse results by asset revision while the publication plan converges; - carries the final template omissions out of the existing publication pass, so the diagnostics endpoint does not walk the artifact again; - keeps the existing sequential asset validation/preparation/stability checks unchanged; - does not use module-level caching or depend on a warm serverless instance; - leaves the timeout error handling from #6406 unchanged. ## Performance evidence An isolated cold-path benchmark used the same MDX payload (2,000 sections) and compared the previous three independent dependency-resolution walks with the new request-scoped convergence path: | path | time | | --- | ---: | | before | 1,178 ms | | after | 404 ms | That is a 66% reduction in the measured MDX resolution phase. The tests also assert the underlying work reduction directly: an unchanged revision is parsed once across convergence passes, while a changed revision is parsed again. For a stable 214-document candidate set, this removes up to 428 of the previous 642 parse operations. ## Verification - `pnpm --filter @webstudio-is/project-build test` — 2,420 tests passed - `pnpm --filter @webstudio-is/builder exec vitest run --project core-server` — 336 tests passed, including server-side e2e runner tests - `pnpm --filter @webstudio-is/project-build typecheck` - `pnpm --filter @webstudio-is/builder typecheck` - `pnpm lint` - `pnpm check:package-boundaries` - `pnpm check:generated-api` ## Code Review - [x] made a self-review - [x] added comments for the request-scoped cache and revision safety - [x] added tests for parse reuse, revision invalidation, and the optimized endpoint integration path ## Before merging - [ ] tested on a deployment preview with a production-sized content project ## Additional content-read optimization Successive compilation plans now share already-fetched, revision-keyed content bytes within one callback-scoped preparation session. The byte store: - is created inside one asset-stability attempt and discarded when that attempt finishes; - is not stored on the repository, in a module, or outside the request; - remains bounded by the existing 2 MiB hydration limit; - refetches changed revisions and starts clean on stability retries. Tests verify that two overlapping plans read unchanged content once, a revision change forces another read, a new session reads again, and a stability retry creates a new session. A fully overlapping two-pass workload therefore performs up to 50% fewer object-storage content reads. Additional verification: - `pnpm --filter @webstudio-is/asset-uploader test` — 292 tests passed - `pnpm --filter @webstudio-is/asset-uploader typecheck` ## Remaining optimization assessment The percentages below estimate whole-request improvement and are workload-dependent; they are not additive. | optimization | estimated improvement | complexity | decision | | --- | ---: | ---: | --- | | Request-local content-byte reuse | 10–35% | 3/10 | Implemented | | Stop when the initial dependency plan is already a fixed point | 5–15% for static/known MDX; ~0% for dynamic discovery | 2/10 | Implemented | | Reuse a canonical source snapshot/materialized entries across different plans | 15–30% | 8/10 | Deferred: large semantic and invalidation surface | | Reuse the bundle's initial asset-data snapshot | 1–5% | 5/10 | Not worthwhile | | Add a diagnostics-specific bundle loader | 2–8% | 6/10 | Not worthwhile for this PR | | Parallelize or skip unrelated user/project metadata | 0–3% | 2/10 | Not meaningful | | Combine collection validation and reserved-asset inspection | 0–5% normally | 4/10 | Only meaningful for projects with many collections | The fixed-point fast path is covered by a test asserting one artifact preparation and one dependency-resolution call when the original plan is already stable. |
||
|
|
0d448313ab |
fix: harden MCP mutations and validation (#6402)
## Summary Fixes the reproducible MCP regressions reported today: - validate font upload metadata locally instead of surfacing an HTTP 500 - replace only the addressed child when `insertIndex` is supplied - support cross-parent duplication - bind data-variable assignments in actions - reject string-valued `style` bindings - include descriptions for every advertised MCP tool - preserve resource data-source exposure on full updates - render mixed expression/element children in the Builder canvas - preserve explicit outer Collection references in nested scopes - reject unsupported `update-page-settings` fields instead of dropping them - reject unavailable asset-query variables and log server error details Closes #6390 Closes #6391 Closes #6392 Closes #6393 Closes #6394 Closes #6395 Closes #6398 Closes #6399 Closes #6401 Closes #6404 Closes #6405 ## Verification - 484 project-build tests - 62 CLI tests - 35 Builder browser tests - 25 SDK tests - project-build, Builder, CLI, and SDK type checks - root lint and generated runtime contracts |
||
|
|
b7a11e4bc2 |
feat: Delay canvas scripts until resources render (#6380)
HTML Embed scripts in canvas and preview now wait until the complete
resource queue settles. Their markup remains rendered while loading;
only executable scripts are delayed, so scripts can read the committed
resource-backed DOM without requiring a custom event.
## Implementation
- Add an internal `$resourcesState` atom with `pending` and `settled`
states, driven by the complete resource queue.
- Synchronize the atom from the Builder to the canvas iframe.
- At the Builder component-render boundary, pass only HTML Embed the
private prop `$ws$executeScripts={isSafeMode !== true && resourcesState
=== "settled"}`.
- In the SDK HTML Embed component, default `$ws$executeScripts` to
`true`, keep markup inert while it is false, and execute scripts after
React commits the transition to true.
- Treat failed requests as settled so scripts cannot wait forever.
- Keep the private prop out of generated component arguments and
metadata through the existing `$`-prefixed prop convention.
- Keep published rendering unchanged because published sites do not pass
the private Builder prop.
- Keep the existing standard `DOMContentLoaded` / `load` compatibility
and sequential script queue.
- Remove the canvas wrapper and component-registry override.
## Todo
- [x] Track collective resource readiness in the Builder.
- [x] Synchronize readiness into the canvas.
- [x] Delay HTML Embed scripts in both canvas and preview.
- [x] Combine safe mode and resource readiness into one private
execution prop.
- [x] Preserve rendered HTML while scripts are delayed.
- [x] Preserve pages with no resources and failed-resource behavior.
- [x] Keep the private prop out of user-facing component metadata.
- [x] Add resource-state, sync, Builder-prop, and browser regression
tests.
- [x] Review documentation impact; no user-facing documentation changes
are needed.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.
## Verification
- SDK HTML Embed browser tests — 44 passed
- Builder private-prop tests — 15 passed
- Resource tests — 28 passed
- Sync tests — 14 passed
- React SDK, SDK components, and Builder typechecks — passed
- Focused Oxlint and formatting — passed
Closes #6379
|
||
|
|
9963c95ef8 |
fix: Sync OAuth profile fields on login (#6376)
Closes #6375 ## Summary - Refresh username, avatar, and provider fields when an existing user logs in through OAuth. - Avoid unnecessary database writes when profile fields are unchanged. - Preserve existing development-login behavior. - Add regression coverage for changed GitHub profile data. ## Testing - `pnpm --filter @webstudio-is/builder exec vitest run --project core-server app/shared/db/user.server.test.ts` - `pnpm --filter @webstudio-is/builder typecheck` |
||
|
|
e69a3d2f8e |
feat: Create structured MDX entries from collection folders (#6317)
## Paragraph editing follow-up — |
||
|
|
722b3af113 |
feat: Copy links to instances from the context menu (#6342)
Right-click an instance in the Navigator and choose **Copy link to
instance** to share its full Builder URL. Opening it restores the page
and the same instance occurrence, including descendants of shared Slots.
The shared menu also exposes the action on the canvas.
Copying and opening a link use the same existing
`getDeepLinkedInstanceSelection` function, moved unchanged from
`use-switch-page.ts` into `shared/instance-utils/link.ts`, alongside URL
creation and the existing instance URL parser. It validates the instance
path and page access. The context menu and pre-publish “Show element”
links both call `getInstanceLink(instanceSelector)`. It lives in
`shared/instance-utils/link.ts` and owns validation and the complete URL
setup: project, page, full selector, origin, share token, editor mode,
and safe mode, using the existing `builderUrl`. There is no separate
link-permission implementation.
The link keeps the current share access and editor mode, clears
unrelated scroll anchors, and shows clipboard success or failure. Body
is supported; Global Root and multiple selections are unavailable.
Instance-only navigation now preserves loaded Builder data: the route
comparison recognizes the `instance` parameter as well as the older
`instanceId`. A functional regression test confirms that changing the
target does not change the data URL comparison, while changing share
access does.
MCP guidance explains how to extract the target ID from the link for
`inspect-instance`, verify the project and shared Slot context, and
avoid guessing when an instance no longer exists. This does not add a
URL-taking MCP endpoint.
1. [x] Add the menu action and reuse complete instance-link construction
with pre-publish links.
2. [x] Share existing instance-link validation between copying and
opening links.
3. [x] Update the Navigator and MCP guidance; regenerate CLI
documentation.
4. [x] Extend the existing functional test to round-trip a shared Slot
URL, including share access and editor mode. Verify it fails without the
instance parameter and passes after restoration.
5. [x] Remove the React/clipboard test harness and duplicate
permission-test matrix. Confirm no fixture inputs or outputs are
affected.
Validation: 28 focused Builder tests, Builder typecheck, repository
lint, and `git diff --check` pass on this revision. The earlier CLI
documentation checks passed and their inputs are unchanged. An earlier
GPT-5.4 mini subagent at low reasoning passed a focused native MCP test
against commit
|
||
|
|
05706b5425 |
feat: Make Content Block templates the MDX authoring interface (#6307)
Closes #6306 ## Outcome Content Block templates are now the public component model for MDX authoring. - Standard Markdown resolves through matching Content Block templates. - PascalCase JSX resolves to a named template first, then to a registered component. - Adding or changing a template immediately affects matching MDX, including content authored before that template existed. - Every supported standard MDX element is present in new Content Blocks by default. - Template `name` is a stable PascalCase JSX identifier. `label` remains optional display text only. - Lowercase JSX represents native HTML and SVG without exposing `ws.element` in authored syntax. - Unknown, ambiguous, and unsafe JSX stays in source, produces a source-ranged Builder diagnostic, and is omitted from published output. - Frontmatter images preserve exact `$ref` values and use the Asset description for `alt` text. - Keyboard and mouse insertion from the slash menu remove the trigger slash and preserve the selected template. ## Public MDX contract - Markdown is canonical for standard prose. - Lowercase JSX creates native elements: `<section><p>Text</p></section>`. - PascalCase JSX uses templates and registered components: `<PromotionCard />`, `<Heading tag="h1">Title</Heading>`, and `<YouTube src="https://youtu.be/KI5JHpzBK1s" />`. - Component-name collisions use stable qualified identifiers such as `Checkbox` and `RadixCheckbox`. - A self-closing template keeps its designed default subtree. An explicitly paired empty template clears it. - Static quoted values and bare booleans are supported. - Imports, exports, expressions, spreads, functions, executable JavaScript, unsafe attributes, `<$.*>`, and authored `<ws.element>` are rejected. - `ws` remains only for compiler primitives with no React component. ## Implementation - [x] Add stable, unique template names and keep them separate from labels. - [x] Parse and validate Markdown and JSX through their ASTs with precise source ranges. - [x] Share component identity between template JSX, Content MDX, Builder materialization, reload, copy, and publication. - [x] Resolve templates live after add, edit, rename, reorder, removal, undo/redo, collaboration updates, and reload. - [x] Preserve authored props, children, Asset references, template defaults, and the exact slash-menu template choice. - [x] Make one typed MDX semantic inventory drive default templates and slash-menu eligibility. - [x] Move component-dependent template assembly into the component registry. - [x] Replace production `$`, `animation`, and `radix` JSX namespaces with direct component imports. - [x] Keep only explicit compiler primitives in `ws` template JSX. - [x] Migrate repository templates, fixtures, stories, examples, MCP guidance, CLI guidance, and generated outputs. - [x] Add Builder unit and Playwright coverage for authoring, diagnostics, insertion, synchronization, reload, and publication. - [x] Review and update the authoritative Content Block and generated MCP documentation. ## Compatibility - Persisted project data continues to use namespaced component IDs and `ws:element` for generic HTML elements. No project-data migration is required. - Legacy `<ws.element ws:tag="…">`, `<ws.element ws:name="…">`, `<$.*>`, `<animation.*>`, and `<radix.*>` input remains readable during the compatibility window. - Current serializers and repository-owned sources emit only canonical Markdown, lowercase intrinsic JSX, or PascalCase identifiers. - Untouched user MDX is not rewritten automatically. Legacy syntax is canonicalized when its owning source is edited. - Deferred follow-up: remove legacy namespace readers and proxy support only after the canonical writer is deployed and existing projects are backfilled. This is intentionally outside this PR. ## Verification - [x] CI package tests, typechecks, lint, package boundaries, generated assets, migrations, and size checks pass. - [x] All six Builder E2E shards pass. - [x] Fixture checks pass on Ubuntu and Windows. - [x] Targeted local Content Engine, Template, SDK, registry, Project Build, migration, Builder, component, CLI, and Playwright tests pass. - [x] Runtime contracts, CLI docs, and MCP docs were regenerated from their owning sources. - [x] Local fixture generation completed without an unexpected fixture diff. - [x] Documentation impact reviewed and included in this PR. - [x] Review and approve the intentional List visual snapshot change. - [x] Run the complete agent evaluation suite after explicit approval. - [x] Run the network fixture link/sync pipeline against the matching PR deployment after explicit permission. ## Known visual change The generated List story now targets its actual default `ul` element instead of `ol`, so the default list styling appears in the visual snapshot. The visual change was reviewed and the approved visual check passes. |
||
|
|
55920c57c4 |
fix: Show all Markdown and Assets query diagnostics (#6308)
Ref #6305 ## Outcome Show actionable Markdown, MDX, and Assets query problems in the editor, query Diagnostics, API, and MCP. The current implementation substantially improves source diagnostics, query error aggregation, query scoping, exact paths, MCP feedback, and loading states. The exhaustive audit below also records what is not complete yet. ## Completion rule An error state is complete only when all applicable checks pass: - [x] The editor uses the production validator and shows every issue for the open file. - [x] Query execution uses the same validation and returns every issue for every file that matches the query, not only the returned page. - [ ] MCP can reproduce the state through a real tool call and returns the same code, severity, message, file, and location. - [x] Diagnostics shows fatal errors and nonfatal warnings in the correct place. - [x] Exact file is always present; line and column are present whenever the parser can determine them. - [x] Multiple issues in the same file, multiple files, and multiple validation phases are returned together. - [ ] A regression test proves failure without the behavior and success with it. ## Cross-surface contract ### Required validator parity - [x] Editor, query execution, and MCP file operations invoke the same exported production file-validation pipeline for byte decoding, source/frontmatter, Markdown/MDX, Content Block semantics, and references. Copied schemas, adapter-only checks, or reimplementations do not count as parity. - [x] Query builder, query execution, `validate-asset-query`, and `preview-asset-query` invoke the same exported structural and catalog-aware semantic query validator with the same project snapshot and context. - [x] Every surface preserves the same complete, deterministically ordered diagnostic array: severity, code, message, phase/scope, asset ID, file path, line/column/range, query path, and fatal-versus-warning/result behavior. UI, API, and MCP serializers must not drop or remap fields. - [ ] A table-driven contract suite calls the real editor, query, API, and MCP entry points—including MCP upload, update, download, validate, and preview—and compares full normalized arrays for multiple errors, multiple warnings, multiple files, and pre-pagination matches. - [x] Markdown and MDX source syntax/frontmatter use validateTextAssetSource in editor, query compilation, and MCP file tools. - [x] Query request execution and MCP local parsing use assetQueryRequest. - [x] Fatal query setup errors retain all detected semantic errors and accompanying warnings. - [x] Nonfatal query warnings retain a stable code and exact query path. - [x] Source diagnostics retain severity, phase, code, asset ID, path, and available source position. - [x] Query Diagnostics excludes diagnostics produced only by unrelated published queries. - [x] Query, Preview, and Diagnostics tabs show loading states. - [x] Authenticated diagnostics can show a sanitized internal message; public/pre-auth errors remain redacted. - [x] Share byte-level UTF-8 validation instead of decoding separately in query and MCP. - [x] Make the query editor and MCP call one literal shared query-validation function before execution. - [x] Share cross-file reference validation across editor, query, and MCP file operations. - [x] Share contextual Content Block/template validation across all applicable surfaces. - [x] Preserve an invalid stored query in the editor and explain it instead of replacing it with defaults. - [x] Define and enforce whether “matches the query” includes every pre-pagination match; current source validation follows returned/paginated IDs. - [x] Continue through source, reference, and runtime phases where safe so one phase does not hide later issues. - [x] Map every user-triggerable graph/storage/runtime failure to a structured public error instead of INTERNAL_ERROR. - [ ] Label candidate-file warnings separately from returned-file diagnostics. ## File transport and encoding - [x] Empty Markdown and MDX are accepted. - [x] UTF-8 BOM and CRLF are accepted. - [x] Whole Markdown/MDX file over 1 MiB is detected. - [x] Missing or unavailable selected content is reported. - [x] Invalid UTF-8 frontmatter returns the same structured diagnostic in query and MCP. - [x] Invalid UTF-8 body returns the same structured diagnostic in query and MCP. - [x] MCP download must use fatal UTF-8 decoding rather than replacement characters. - [x] Validate bytes before committing an upload, or return the committed Asset ID and explicit cleanup state when post-commit feedback fails. - [x] Reject filename/format mismatches instead of allowing either hint to bypass Markdown/MDX validation. - [x] Storage status, missing body, invalid metadata, revision mismatch, format mismatch, short read, and excess read each return structured file context. - [x] Content changing during validation has a clear retryable conflict diagnostic. ## Frontmatter - [x] No frontmatter. - [x] Empty frontmatter. - [x] Opening delimiter without a closing delimiter. - [x] Frontmatter over 64 KiB. - [x] Invalid YAML grammar. - [x] Multiple YAML parser errors. - [x] Duplicate keys. - [x] YAML aliases. - [x] Unsupported YAML tags. - [x] Scalar or array root instead of an object. - [x] Non-JSON values, NaN, Infinity, and unsafe integers. - [x] Nesting deeper than 8. - [x] More than 256 fields/array entries. - [x] One string over 16 KiB. - [x] Normalized indexed properties over 64 KiB. - [x] Frontmatter warnings remain nonfatal and visible. - [x] Collect all post-parse normalization/limit errors; this phase currently stops at the first. - [ ] Prove every state above through real MCP upload, update, download, query, and Diagnostics paths. ## Markdown body - [x] Treat ordinary CommonMark/GFM as permissive; malformed-looking Markdown and raw HTML are not automatically syntax errors. - [x] Preserve and validate the body when frontmatter is invalid. - [x] Detect the 1 MiB body/file limit. - [x] Handle body extraction and bounded excerpts. - [x] Return a shared structured invalid-UTF-8 body error from every byte-reading surface. - [ ] Distinguish author errors from internal excerpt-limit/configuration errors. - [ ] Prove body-size and decoding states through real MCP and Diagnostics. ## MDX grammar and safety - [x] Malformed MDX/JSX grammar. - [x] Imports. - [x] Exports. - [x] Executable flow expressions. - [x] Executable text expressions. - [x] JSX expression attributes. - [x] JSX spread attributes. - [x] Arbitrary components. - [x] HTML-looking JSX outside ws.element. - [x] Unsupported/raw MDX node types. - [x] Duplicate attributes and normalized alias collisions. - [x] Event-handler attributes. - [x] srcdoc, srcset, and dangerouslySetInnerHTML. - [x] Unsafe URL protocols in link/source/action-like props. - [x] Forbidden tags: base, link, meta, noscript, script, style, template, title. - [x] Conflicting, empty, or non-static ws:name and ws:tag. - [x] More than 20,000 nodes. - [x] Nesting deeper than 100. - [x] More than 4,000 props. - [x] Multiple recoverable unsafe-mdx warnings are collected. - [ ] Collect multiple fatal grammar/structural errors when parser recovery can do so; unrecoverable parsing currently stops at the first fatal error. - [x] Preserve sibling expression warnings when unsupported JSX is also present; {first()}, <Card />, {second()} currently reports only the Card warning. - [ ] Prove every state above through real MCP and Diagnostics, not only validator unit tests. ## Content Block semantics - [x] Unresolved template warning exists. - [x] Unknown template prop warning exists. - [x] Incompatible template prop warning exists. - [x] Design-only template prop warning exists. - [x] Stale template prop warning exists. - [x] Invalid materialized HTML content model error exists. - [x] Run these checks for an MDX file even when no active editor Content Block root has materialized it. - [x] Run applicable semantic checks in query Diagnostics. - [x] Run applicable semantic checks for MCP upload as well as contextual update/download. - [x] Return all Content Block frontmatter diagnostics; one path still throws only diagnostics[0]. - [ ] Add real MCP and UI coverage for every semantic state. ## Cross-file references - [x] Non-string $ref marker. - [x] Invalid reference URL. - [x] Invalid fragment encoding. - [x] Invalid fragment. - [x] Invalid JSON Pointer. - [x] Missing referenced document. - [x] Missing referenced JSON path. - [x] Missing referenced local Asset warning. - [x] Unsupported target representation. - [x] Reference cycle. - [x] Duplicate document ID or URL. - [x] Missing graph source or target. - [x] Root not found. - [x] Duplicate/unused/unresolved reference. - [x] Invalid resolved value. - [x] Content identity conflict. - [x] Target revision mismatch. - [x] Representation selector required/invalid. - [ ] Diagnose {$ref: value, extraKey: value} as an invalid marker or explicitly document it as ordinary data. - [x] Collect all bad references and targets instead of stopping at the first graph failure. - [x] Validate references from the editor and MCP file tools, not only query compilation. - [x] Map graph compilation/resolution, Markdown-document, and JSON-document errors to exact structured Diagnostics rows. - [x] Prevent an unrelated file's graph error or duplicate canonical URL from blocking an exact-file query. - [ ] Prove missing targets, cycles, bad representations, and bad JSON paths through real MCP. ## Query request and structure - [x] Invalid JSON, invalid request UTF-8, empty/non-object request, and request over 512 KiB. - [x] Missing query and unknown request/query keys. - [x] Invalid result mode. - [x] Malformed where/all/any/filter shape. - [x] More than 32 filters or children. - [x] Filter nesting deeper than 8. - [x] Empty field path or segment. - [x] Field path deeper than 9. - [x] Bare properties path. - [x] Unknown or nested standard field. - [x] Missing or unknown operator. - [x] Non-JSON operator value. - [x] Invalid in value or more than 1,000 in values. - [x] Non-boolean exists/isEmpty value. - [x] More than 8 sort entries. - [x] Invalid sort field or direction. - [x] first/last without a sort. - [x] Invalid output mode or includeMetadata. - [x] Duplicate output fields or more than 256 fields. - [x] No selected output. - [x] Negative, fractional, or over-1,000 limit. - [x] Negative, fractional, or over-1,000 offset. - [x] Invalid content mode. - [x] Invalid full/markdown-body-ref maxBytes outside 1..1 MiB. - [x] Invalid range offset/length or range over 256 KiB. - [x] Empty or over-255-character index revision. - [x] Multiple simultaneous schema issues retain exact query paths. - [x] Make content option objects strict; unknown keys are currently stripped. - [x] Show an invalid persisted resource body rather than silently loading the default query. - [x] Aggregate unknown-key errors with every other structural issue; MCP currently stops at the first unsupported key. - [x] Add field-value type validation, for example reject size gt "large" and id contains {}. - [x] Warn about unobserved dynamic fields selected only as output. - [ ] Prove the full structural matrix through the MCP server/API end to end; the current broad matrix tests the local adapter. ## Query semantic states - [x] INCOMPATIBLE_OPERATOR is fatal for standard fields. - [x] UNOBSERVED_FIELD is a nonfatal warning. - [x] INCOMPATIBLE_OBSERVED_TYPES is a nonfatal warning. - [x] STRUCTURED_SORT_ORDER is a nonfatal warning. - [x] Every incompatible standard-field operator is collected. - [x] Fatal semantic errors retain nonfatal warnings. - [x] Successful warnings appear with exact query paths. - [ ] Reproduce every catalog-dependent semantic state through real MCP. - [x] Test repeated warnings at distinct query paths without accidental deduplication. ## Query execution and hydration - [x] Stale index revision. - [x] result one matching multiple files, including matchedCount. - [x] Candidate document limit of 1,000. - [x] Result payload limit of 16 MiB. - [x] Hydrated file count limit of 20. - [x] Per-file hydration limit of 1 MiB. - [x] Total hydration limit of 2 MiB. - [x] Range limit of 256 KiB. - [x] Binary content requested as text. - [x] Invalid UTF-8 during hydration. - [x] Missing, stale, or conflicting content identity. - [x] Canonical metadata/byte-count mismatch. - [x] Storage returning more bytes than requested. - [x] Missing content reader. - [x] Selected content not returned. - [x] Runtime Asset URL unavailable. - [x] Document graph document-count/per-file/total limits. - [x] Classify candidate-document and result-byte limits as CONTENT_LIMIT_EXCEEDED instead of INVALID_REQUEST. - [x] Give unavailable URL, missing reader/content, storage, selection, and assembly failures stable codes and exact asset/file details instead of INTERNAL_ERROR. - [x] Never silently turn an exact-file unavailable-content failure into an empty successful result. - [x] Add direct regression tests for duplicate identity, excess bytes, and canonical byte mismatch. - [ ] Reproduce every runtime/hydration state through real MCP. ## Scoping and aggregation - [x] Errors and warnings from multiple loaded result files are returned together. - [x] Nonfatal errors do not suppress successful query results. - [x] Unrelated published-query diagnostics do not replace current-query diagnostics. - [x] File path and available line/column are shown. - [x] Exact nested query paths are shown. - [x] Validate and report every pre-pagination matching file if that is the product definition. - [ ] Explain candidate metadata warnings when invalid metadata prevents deciding whether a file matches. - [ ] Stop including METADATA_PREPARATION_FAILED globally unless the UI labels its candidate scope. - [x] Aggregate reference errors instead of stopping at the first. - [x] Aggregate across validation phases where safe. - [x] Add a matrix for same-file multiple errors, multiple files, outside-page errors, filter-only candidate files, and unrelated queries. ## Transport, authorization, cancellation, and internal failures - [x] UNAUTHORIZED and FORBIDDEN. - [x] REQUEST_CANCELLED in query HTTP paths. - [x] Sanitized authenticated internal message. - [x] Generic unauthenticated/public internal message. - [x] Preserve structured causes for document-load HTTP status/body/metadata failures. - [x] Preserve structured causes for object-storage read failures. - [x] Preserve structured causes for graph selection/assembly failures. - [ ] Test MCP/API cancellation directly. - [ ] Reproduce authorization, cancellation, storage, and sanitized-internal states through real MCP where safe. ## MCP reproduction - [x] Real MCP reproduction: multiple Markdown YAML errors. - [x] Real MCP reproduction: multiple simultaneous structural query errors. - [x] MCP update returns Markdown and MDX source diagnostics. - [x] MCP upload returns Markdown and MDX source diagnostics. - [x] MCP download returns Markdown and MDX source diagnostics. - [x] Temporary remote fixtures are removed after reproduction. - [ ] Real MCP reproduction for every frontmatter code. - [ ] Real MCP reproduction for every MDX fatal/warning class. - [ ] Real MCP reproduction for every Content Block semantic diagnostic. - [ ] Real MCP reproduction for every reference/graph error. - [ ] Real MCP reproduction for every semantic query warning/error. - [ ] Real MCP reproduction for every hydration/runtime/limit error. - [ ] Real MCP reproduction for auth, cancellation, storage, and internal errors where safe. - [ ] A durable table-driven test proves code/severity/message/path parity between editor, query, Diagnostics, and MCP. - [x] Preserve stable top-level error identity and structured details through MCP instead of flattening known errors to MCP_TOOL_FAILED. - [x] Prove failed post-commit MCP feedback reports whether a mutation committed and returns enough identity for cleanup. ## Real MCP recheck and follow-up fixes (2026-09-03) Ran the branch CLI against the PR preview backend. The first run exposed that the saved CLI origin pointed to a different backend, so those results were discarded. Every MCP result below came from the PR preview. All temporary remote and local fixtures were removed afterward. The failures found in that run were then fixed and covered by regression tests. - [x] Exact broken Markdown query returns FRONTMATTER_INVALID with severity, filename, message, line, and column. - [x] Exact MDX query returns unsafe-mdx with severity, filename, message, line, and column. - [x] A no-extension query returns warnings from both matching Markdown and MDX files. - [x] A Markdown-only query excludes the unrelated MDX warning. - [x] Two fatal MDX files inside the returned page are both reported; matching warnings remain attached. - [x] Structural query validation returns three independent errors with exact paths in one call. - [x] Semantic query validation returns three fatal operator errors and two warnings with exact paths in one call. - [x] MCP upload, update, download, and exact-file query return multiple source diagnostics for the same MDX source. - [x] Query preview reports a missing Asset reference as ASSET_NOT_FOUND with the source file and reference. - [x] All temporary MCP fixtures were deleted and the saved CLI connection was restored. - [x] Fixed after recheck: validate every matching file before pagination, including broken files outside the returned page. - [x] Fixed after recheck: unrelated graph failures no longer block an exact-file query. - [x] Fixed after recheck: update, download, editor, and query paths use shared reference validation. - [x] Fixed after recheck: graph failures map to structured diagnostics with the exact file. - [x] Fixed after recheck: shared fatal UTF-8 validation runs before upload writes and preserves structured error identity. - [x] Fixed after recheck: MCP rejects filename/format mismatches. - [x] Fixed after recheck: MDX validation keeps sibling and nested warnings around unsupported components. - [x] Fixed after recheck: MCP preserves stable top-level error codes and structured details. - [x] Fixed after recheck: query and MCP serializers preserve full normalized diagnostic fields. - [x] Fixed after recheck: unknown keys are aggregated with all other structural query errors. - [x] Fixed after recheck: shared query validation covers literal types, output-only fields, and strict content options. - [x] Fixed after recheck: unavailable selected content returns an exact-file diagnostic instead of a silent empty result. ## Tests and delivery - [x] Full Builder suite: 2,707 tests. - [x] Content Engine suite: 445 tests. - [x] Asset uploader suite: 211 tests. - [x] Project runtime/MCP targeted suite: 128 tests. - [x] CLI/MCP suite: 1,051 tests. - [x] Relevant package typechecks, lint, generated docs, and diff checks. - [x] Documentation impact reviewed and generated Content Engine/MCP reference updated. - [x] Add the missing cross-surface error matrix described above. - [ ] Demonstrate red/green behavior for every new matrix case. - [ ] Run the complete agent evaluation suite after explicit approval. - [ ] Re-run all checks after the remaining checklist is implemented. ## Compatibility and risk Normal query result and expression-binding shapes remain unchanged. Diagnostics add optional fields. Existing public/pre-auth error redaction remains intact. The remaining unchecked items are broader exhaustive real-MCP coverage, parser-recovery limits, candidate-warning labeling, cancellation coverage, and agent evaluations. |
||
|
|
d292502037 |
fix: Paste in Firefox navigator (#6329)
## Summary - Read every clipboard mime type before the first await in both paste handlers. - Call the paste handler registered by the active mode from `emitPaste` instead of dispatching a synthetic clipboard event. - Add regression coverage for both paths: a clipboard event that disconnects after dispatch, and a Firefox-shaped `ClipboardEvent`. ## Root cause Two independent Firefox behaviors, one per entry point. Keyboard paste: the plugin loop re-read `event.clipboardData` on every iteration. Firefox disconnects a paste event's `DataTransfer` as soon as the handler yields, so once `pageText` — first in the list, and async — awaited, every later plugin read an empty string and `instanceText` never received the payload. Chrome keeps the data readable across microtasks, which is why this only surfaced in Firefox. Introduced in #5798, which put `pageText` at the head of the plugin list. Context menu paste: `emitPaste` dispatched `new ClipboardEvent("paste", { clipboardData })`. Gecko's `ClipboardEventInit` has no `clipboardData` member, so the event arrived with a non-null but empty `clipboardData`. `validateClipboardEvent` only rejects a null one, so the paste silently did nothing — no toast, no error. Measured in Firefox 134 against Chrome and Edge 152: `getData` after one microtask returns the payload in Chromium and an empty string in Firefox; a synthetic event's `getData` likewise returns the payload in Chromium and an empty string in Firefox. No behavior change in Chromium. Every plugin returns `pasteIgnored` before its first await, so reading all mime types up front yields the same input the loop received before. ## Verification - `pnpm --filter @webstudio-is/builder exec vitest run app/shared/copy-paste app/builder/shared/asset-manager app/shared/commands-emitter.test.ts` — 335 tests passed. - `pnpm --filter @webstudio-is/builder typecheck` — passed. - `pnpm exec oxlint --deny-warnings apps/builder/app/shared/copy-paste/` — passed. - `pnpm exec oxfmt --check apps/builder/app/shared/copy-paste/copy-paste.ts apps/builder/app/shared/copy-paste/copy-paste.test.ts` — passed. - Reverted each fix in turn against the new tests: 13 failures for the keyboard path, 2 for the context menu, all passing again once restored. - e2e runs Chromium only, and the previous test `DataTransfer` never disconnected, so neither path was covered before. Closes #6288 --------- Co-authored-by: Anmol <anmol@fathershops.com> Co-authored-by: Oleg Isonen <oleg008@gmail.com> |
||
|
|
b832a8e808 |
refactor: Centralize Builder ID generation (#6323)
## Outcome Builder-owned code now routes standard ID generation through `@webstudio-is/sdk/createId` while preserving existing NanoID and UUID formats. Closes #6322 ## Technical choices - Use `createId("nano")` for Builder records, assets, transaction IDs, and other existing NanoID-backed values. - Use `createId()` for UUID-backed database records and browser-side client IDs. - Keep `BuilderRuntimeContext.createId` as an injectable dependency. Runtime operations, copy/paste, and deterministic tests rely on that injection. - Keep Node's `randomUUID` in CLI paths that explicitly support running without the Web Crypto global. - Keep specialized generators for custom-alphabet project domains and actual hash generation. - Remove direct `nanoid` dependencies from packages where no specialized use remains. - Do not migrate existing data or change database schemas. ## Todo - [x] Migrate Builder app ID creation. - [x] Migrate project runtime and template defaults. - [x] Migrate asset, project, domain, and authorization-token IDs. - [x] Preserve runtime ID injection. - [x] Review remaining direct generators as explicit exceptions. - [x] Add NanoID-format regression coverage for the default runtime context. - [x] Review documentation impact: no user-facing documentation changes are needed. - [x] Review fixture impact: no fixture input or generated output format changes are needed. - [x] Run targeted tests, typechecks, lint, and package-boundary checks. ## Verification - Targeted Builder tests: 153 passed after preserving runtime-context injection. - Project-build runtime tests: 270 passed. - Asset uploader tests: 61 passed. - Authorization-token tests: 17 passed. - Project tests: 52 passed. - Domain tests: 18 passed. - CLI tests: 32 passed. - Typechecks passed for Builder, project-build, asset-uploader, authorization-token, project, and domain. - `pnpm lint` passed. - `pnpm check:package-boundaries` passed. - The runtime-context format test was demonstrated to fail with a UUID default and pass with the NanoID default. - Agent evaluations were not run because they require separate explicit approval. |
||
|
|
d4b1994a6a |
fix: Improve editor selection and project tag contrast (#6302)
Closes #6301 ## Summary - Restore Dashboard project tags to the pre-color-refactor treatment: fixed white text over a 30% black scrim in both application themes. - Use one high-contrast text-selection treatment across native inputs, textareas, contenteditable fields, and every shared CodeMirror editor. - Preserve the existing subtle selection treatment for Navigator, Project Settings, command menus, Dashboard navigation, and other selected controls. - Add direct Storybook coverage for the CSS fragment editor. - Simplify chromatic color derivation from 571 to 329 lines without changing computed colors. ## Technical choices - Keep project tags independent from application-theme polarity because they overlay arbitrary thumbnail artwork. - Keep the existing `selectionBackground` control-state export unchanged at its original 12% light / 20% dark accent blend. - Define separate `--background-text-selection` and `--foreground-on-text-selection` tokens exclusively for selected text. - Use the bounded accent in light mode and a luminance-lifted accent in dark mode so selected text remains distinct even with extreme custom theme colors. - Style CodeMirror through its existing selection layer and `::selection` CSS hooks; no selection tracking or editor-state logic is added. - Leave canvas rich-text selection independent: authored canvas documents do not load the Builder UI theme stylesheet. - Replace five duplicated XYZ clamp formulas with the exact upper-luminance cap they reduce to. The removed lower-bound and white-mixing branches were unreachable because the minimum was fixed at zero. ## Todo - [x] Restore the original project-tag scrim and foreground in both themes. - [x] Inventory native, CodeMirror, and canvas rich-text selection paths. - [x] Unify native and shared CodeMirror text-selection colors. - [x] Keep text selection independent from selected control and navigation states. - [x] Add the missing CSS fragment editor story. - [x] Add light- and dark-mode browser regressions and confirm they fail before the fix. - [x] Enforce 4.5:1 selected-text contrast and 3:1 highlight-to-surface contrast across supported theme inputs. - [x] Remove duplicated and unreachable chromatic color derivation while preserving outputs. - [x] Review documentation impact; the design-system color-token reference is updated, with no user-facing product documentation change required. - [x] Verify fixture impact; no fixture inputs or generated outputs are affected. ## Verification - `pnpm --filter @webstudio-is/design-system test` — 227 tests passed, including the control/text separation regression, Navigator tree interaction, default-palette comparisons, and the full supported-theme contrast sweep - `pnpm --filter @webstudio-is/design-system typecheck` - `pnpm --filter @webstudio-is/builder exec vitest run app/shared/code-editor-base.browser.test.tsx --project app-browser-shared` - `pnpm --filter @webstudio-is/builder exec vitest run app/dashboard/projects/project-card.browser.test.tsx --project app-browser-canvas-dashboard` - `pnpm --filter @webstudio-is/builder typecheck` - Targeted `oxlint`, formatting, and `git diff --check` - Live dark-mode Dashboard verification of the restored subtle navigation selection - Storybook visual checks for the full color system and input/CodeMirror selection in light and dark modes |
||
|
|
f953d1be37 |
fix: Restore the login background gradient (#6286)
## Summary Restores the original Webstudio login gradient after the color-system migration added a dark scrim over it. Fixed Webstudio identity artwork now belongs to the Design System, while generic surfaces, content, translucency, and overlays use the semantic color system instead of a Builder-local brand color bag. ## Architecture - The Design System exports only the fixed Webstudio background gradient, progress gradient, and progress shadow as brand artwork. - Generic black, white, translucent panel, and scrim entries were removed rather than moved. - Login and error surfaces use semantic light/dark pairings. - Project-card tags derive their local 30% scrim from the semantic overlay color. ## Todo - [x] Restore the original login gradient without a scheme-dependent scrim. - [x] Move fixed identity artwork into the Design System package. - [x] Remove the Builder-local brand color registry. - [x] Replace generic fixed colors with semantic colors or local derivations. - [x] Add explicit dark-mode Storybook coverage for the login screen. - [x] Run Design System and Builder typechecks. - [x] Run Design System tests, scoped lint, formatting, and package-boundary validation. - [x] Review documentation impact and document ownership in the Design System color README. ## Verification - Design System tests: 222 passed. - Design System and Builder typechecks: passed. - Scoped Oxlint and formatting: passed. - Package-boundary validation: passed. - Focused visual regression: 23 affected stories compared; the newly added dark login story is the only difference. - Fixture impact: no fixture inputs or generated outputs are affected. Ref #3818 |
||
|
|
8718feb465 |
test: simplify browser testing infrastructure (#6271)
## Summary Unifies frontend testing around three intentional boundaries: - Vitest Node projects for pure and server tests - Vitest Browser Mode with Playwright/Chromium for DOM and component tests - Playwright Test for Builder end-to-end workflows The Builder no longer uses jsdom. Ordinary E2E workflows use an automatically managed isolated `page` fixture; explicit contexts remain only for simultaneous identities and suite-scoped setup. ## Technical choices - Split Builder tests into one Node project and independent browser projects so each Chromium/Vite process has a bounded workload. - Added shared Browser Mode configuration for the affected SDK React packages. - Preserve the Builder's production top-window bridge ownership; Browser Mode uses explicit test-only installation seams. - Centralized Playwright context options and kept action/navigation timeouts bounded. - Validate Playwright filters before Docker, migrations, browser installation, or Builder builds start. - Preserve the first failed E2E attempt's report, traces, screenshots, and videos when CI retries a shard. - Split static checks, typechecks, CLI tests, core package tests, browser package tests, and Builder projects into parallel CI jobs with five-minute hard ceilings. - Distribute Builder E2E tests across ten isolated shards with two workers; parallel suites create unique per-worker setup data. - Reuse color contrast DOM/canvas samples so the exhaustive theme sweep takes about four seconds instead of timing out at 30 seconds. - The removed cost is jsdom behavior drift, oversized serial check jobs, and unnecessary E2E startup for empty selections. Real browser startup and E2E environment setup remain. ## Todo - [x] Split Builder pure/server tests from browser/component tests. - [x] Migrate Builder DOM tests from jsdom to Vitest Browser Mode. - [x] Migrate affected SDK React DOM tests to the shared Browser Mode setup. - [x] Remove jsdom annotations, dependencies, and compatibility setup. - [x] Replace ordinary manual E2E contexts with the isolated `page` fixture. - [x] Keep explicit contexts only for concurrent identities and `beforeAll` setup. - [x] Preserve setup authentication isolation from workflow pages. - [x] Add bounded Playwright action and navigation timeouts. - [x] Preserve first-attempt E2E failure evidence across CI retry. - [x] Reject empty E2E selections before expensive environment setup. - [x] Parallelize repository checks into bounded CI shards. - [x] Isolate parallel E2E setup data across workers. - [x] Remove the color contrast sweep bottleneck. - [x] Update E2E contributor documentation. - [x] Review product documentation impact; no user-facing product docs are affected. - [x] Run the full repository tests and typechecks. - [x] Run formatting, lint, package-boundary, generated-file, and fixture checks. - [x] Complete final correctness and simplification review. ## Verification - `pnpm --filter @webstudio-is/builder test` - `pnpm -r typecheck` - `pnpm lint` - `pnpm check:package-boundaries` - `pnpm check:generated-api` - `pnpm check:generated-docs` - `pnpm fixtures` - Design-system: 222 tests pass locally. - Builder: all seven Vitest projects pass locally. - The formerly slow E2E shard passes locally in 1m55s. - Full database-backed Builder E2E execution passes in CI. - All CI jobs pass; the slowest completed in 4m58s. - No fixture inputs/outputs or generated files changed. Ref #6270 |
||
|
|
f6c524572d |
feat: add Builder dark theme and rebuild colors
## Summary
Rebuilds Webstudio’s color system, migrates the entire Builder to it,
and introduces a complete dark theme.
A small set of theme colors and contrast controls now drives the
interface through relative CSS colors:
```text
Theme colors and contrast
↓
Light/dark derivation bounds
↓
Derived color families
↓
Reusable semantic colors
↓
Component composition
↓
Builder interface
```
CSS is the runtime source of truth. Light and dark modes share the same
theme inputs, so changing a theme color automatically updates its
surfaces, content, borders, states, and component presentation. The
refactor turns dark mode into a first-class Builder theme rather than a
separately maintained palette.
This is an atomic migration. Legacy color names and compatibility
aliases will not remain after all consumers have moved.
## Scope
- The color source, derivation model, semantic vocabulary, validation,
typing, and Storybook overview.
- Every design-system component.
- Builder, dashboard, authentication, shared UI, Query Builder, stories,
and tests that consume the legacy color scale.
- Removal of the legacy Stitches color scale after its final consumer is
migrated.
- A complete dark theme across application UI while keeping authored
canvas content independent.
- Complete light and dark visual verification.
- Unified browser-test infrastructure: Vitest Browser Mode for Design
System browser contracts and standard Playwright Test for all Builder
E2E workflows.
Literal colors remain only where color is the actual content or belongs
to a deliberately separate system, such as user-authored values,
external previews, brand artwork, data visualization, functional canvas
overlays, and source assets. Every remaining exception must have an
explicit owner.
## Architecture decisions
- Theme authors control six color families and three bounded contrast
controls.
- Relative CSS colors derive the rest of the system.
- Dark mode changes derivation bounds instead of maintaining a second
independent palette.
- Components consume a small reusable semantic vocabulary.
- Component states and exceptional presentation are composed inside
components.
- Builder-specific categorical meaning uses focused composite recipes,
not intent semantics or a larger global vocabulary.
- TypeScript validates public semantic CSS-variable names but does not
generate runtime colors.
- No token-manager dependency, compatibility aliases, or
component-specific public color tokens.
- A new semantic or theme variable is added only when existing semantics
and local composition cannot represent a reusable cross-feature
decision.
## Todo
### Color-system foundation
- [x] Establish direct CSS as the runtime source of truth.
- [x] Define bounded theme colors and contrast controls.
- [x] Derive light and dark modes from the same theme inputs.
- [x] Reduce the semantic inventory to reusable decisions and explicit
accessibility pairs.
- [x] Generate only TypeScript names for public semantic CSS variables.
- [x] Validate layers, references, cycles, consumption, naming,
generated types, and browser-computed contrast.
- [x] Add the Storybook color-system overview and theme controls.
- [x] Document the architecture and its relationship to the public Craft
specification.
### Design-system migration
- [x] Replace every active legacy color consumer in design-system
components and Storybook chrome.
- [x] Compose component states locally without component-specific public
tokens.
- [x] Preserve intentional preview and sample-content colors.
- [x] Add complete positive, negative, warning, informative, and accent
accessibility pairings.
- [x] Validate light and dark scheme-bound references independently.
- [x] Verify the design-system package with typecheck, lint, formatting,
Storybook, and 222 tests.
### Inventory and contract
- [x] Inventory all 319 legacy references across 107 Builder, Query
Builder, story, test, and design-system files.
- [x] Classify every reference as a direct semantic replacement, local
composite recipe, documented literal exception, or removable code.
- [x] Record the intentional authored-content, external-preview,
functional-preview, data-visualization, brand, and source-asset
exceptions.
- [x] Treat the public semantic vocabulary as closed by default and
justify any proposed addition by cross-feature reuse and accessibility
need.
- [x] Keep Builder code limited to typed public variables accepted by
`cssVar()`.
### Theme infrastructure
- [x] Add `system` (default), `light`, and `dark` application
preferences.
- [x] Persist the preference in existing client settings and resolve
`system` through `matchMedia`.
- [x] Apply the resolved scheme before paint and respond to
operating-system changes.
- [x] Expose the persisted theme control under **View → Color scheme**.
- [x] Keep authored canvas documents independent from the application
theme.
- [x] Ensure portals, overlays, embedded surfaces, and third-party
controls receive the correct interface scheme.
### Generic interface migration
- [x] Migrate shared dialogs, project settings, notifications, controls,
and Query Builder.
- [x] Migrate dashboard, authentication, publishing, and shared
application UI.
- [x] Migrate the Builder loading state, topbar, sidebars, navigator,
workspace, footer, settings, pages, alerts, and menus.
- [x] Replace fixed interface literals alongside their owning surfaces.
- [x] Remove the old scrollbar variable and hardcoded loading color.
### Style panel and style-source recipes
- [x] Migrate ordinary style-panel inputs, labels, layouts, spacing,
alignment, menus, property states, and value editors.
- [x] Model style-source provenance, kind, interaction, and error as
separate axes.
- [x] Compose categorical style-source colors from neutral and
accent-relative colors.
- [x] Use negative semantics only for actual errors, not overwritten
provenance.
- [x] Reuse one feature-owned recipe across style-source controls,
badges, menus, and values without adding global tokens.
### Canvas and editors
- [x] Migrate canvas selection, reusable outlines, grid guides, resize
handles, labels, badges, and overlays.
- [x] Keep collaborator colors as data visualization while guaranteeing
readable labels.
- [x] Prevent Builder theme values from leaking into authored content.
- [x] Migrate editor surfaces, selections, autocomplete, lint states,
and tooltips.
- [x] Derive syntax visualization from neutral and accent-relative
colors; reserve intent colors for diagnostics.
### Explicit color domains
- [x] Isolate external search and social previews from the application
scheme.
- [x] Preserve user-authored CSS values, functional color previews, and
checkerboards.
- [x] Review brand surfaces independently in both schemes.
- [x] Document every retained literal in the exception ledger.
### Stories, tests, and legacy removal
- [x] Migrate all affected Builder stories and tests.
- [x] Replace the design-system test that intentionally references the
legacy scale.
- [x] Add theme-preference and scheme-resolution behavior tests.
- [x] Remove the legacy `design-tokens.color` registry.
- [x] Remove the Stitches `colors` scale and `rawTheme.colors`.
- [x] Remove color-name conversion code that exists only for the legacy
scale.
- [x] Add a focused repository check that rejects legacy references.
- [x] Confirm no compatibility mappings or deprecated aliases remain.
### Browser-test infrastructure
- [x] Run pure Design System tests in Node and browser-dependent tests
in Vitest Browser Mode with Playwright Chromium.
- [x] Migrate all 79 Builder E2E workflows from the custom harness to
standard Playwright Test.
- [x] Replace filename-based shard discovery with standard Playwright
sharding.
- [x] Preserve clean-backend shard retries and upload Playwright failure
artifacts in CI.
- [x] Remove the custom runner, registry, and timeout infrastructure.
### Verification and documentation
- [x] Compare light-mode visual regressions directly with `main`; review
every difference rather than accepting snapshots wholesale.
- [x] Add representative dark coverage for dashboard, Builder shell,
style panel, dialogs, editors, canvas tools, and status states.
- [x] Test default and extreme custom-theme presets.
- [x] Verify semantic and categorical-recipe contrast.
- [x] Test first paint, persistence, system changes, portals, and canvas
isolation.
- [x] Run affected tests, typechecks, lint, formatting, and Storybook
checks.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.
- [x] Review authoritative `docs/` content and update it where needed.
- [x] Review the final diff and make this checklist reflect delivered
scope.
## Current verification
- Design-system typecheck and generated-type freshness: passed.
- Design-system tests: 222 passed, including the full theme-parameter
contrast sweep and browser component coverage.
- Builder typecheck and production build: passed.
- Theme preference and scheme-resolution tests: 10 passed.
- Scoped Oxlint, formatting, and diff checks: passed.
- Design-system Storybook build and representative light/dark visual
review: passed.
- Builder E2E: 80/80 Playwright entries passed (setup plus 79
workflows).
Ref #3818
Closes #6267
|
||
|
|
f9681dc30f |
feat: MDX-backed Content Blocks (#6234)
## Summary Supersede #6182 with a clean implementation of MDX-backed Content Blocks on top of the unchanged `main` Content Block editing model. A connected `.mdx` Asset changes where authored children are loaded and saved; it must not change how Content mode inserts, edits, selects, moves, copies, or deletes instances. The previous implementation remains available in `agent/mdx-content-block-6124` and `agent/mdx-content-block-6124-review`. Reuse its independently tested parsers, converters, schemas, Asset repository, template resolution, publication, MCP, and UI work where those pieces fit the corrected architecture. Do not port its per-operation storage routing, projected editing stores, MDX undo/history, or atomic multi-root machinery. Ref #6124 Ref #6182 Closes #6261 ## Locked behavior - `.md` keeps its existing Markdown behavior; `.mdx` enables MDX. - Standard Markdown structures are stored as Markdown. JSX is used only for structures Markdown cannot represent losslessly. - Generic elements use `<ws.element ws:tag="…">`; template instances use `<ws.element ws:name="…">`. - Invalid, unsafe, or unsupported MDX never prevents editing the file or publishing the project. Builder and publication use the same recovery result: recoverable nodes are skipped with source-located warnings, while an unrecoverable document renders no authored file content in either environment. - Connecting a source never serializes existing Content Block children into the file. **Connect** removes ordinary persisted children, preserves Templates, sets `src`, and uses file content. - Disconnecting resets `src`, discards derived instances, leaves the Content Block empty, and never changes the Asset. - While connected, normal Content Block mutations update in-memory instances synchronously. Asset persistence happens afterward and never blocks rendering. - Genuine external revision conflicts use the standard reload-required flow. Rapid local edits are ordered/coalesced and must never conflict with each other. - Repeated scopes use Content Block ID plus Collection item identity. - Multiple Content Blocks may intentionally share one MDX Asset. They share one revisioned document session and edits propagate to every connected occurrence. - Missing templates show warnings/placeholders only in Builder and are skipped on published sites. ## Reuse strategy - [x] Audit all 62 original implementation commits and 5 later review/fix commits - [x] Reuse the shared Content Engine Markdown/MDX AST, parser, serializer, diagnostics, reference discovery, and tests - [x] Reuse SDK source schemas, instance-name utilities, unique template naming, and source-integrity validation - [x] Reuse template resolution/materialization and authored-tree conversion after removing storage-operation coupling - [x] Reuse revisioned Asset content repository, authorization, limits, and browser/server transport - [x] Reuse publication, document-graph, CLI, MCP, source-control UI, frontmatter UI, and E2E work where contracts remain valid - [x] Reuse tests as behavioral specifications; rewrite tests that assert the rejected projection/routing architecture - [x] Do not port per-operation MDX routing, alternate projected mutation stores, semantic reconciliation after each command, MDX Builder history/undo, atomic multi-file rollback, or canvas loading/saving UI ## Old-branch porting map — do not rewrite Before implementing any checklist batch, inspect the named implementation and tests on `agent/mdx-content-block-6124-review`. Move the implementation and its tests first; change only the boundary that conflicts with the rebuilt architecture. Do not create a second implementation under a new name. When a test encodes the rejected projected-store architecture, retain its behavioral assertion and replace only its setup. ### Already ported - `parseMarkdownAst`, `createMdxDocumentFromMarkdown`, `parseMdxDocument`, `serializeMdxDocument`, `preferMarkdownSyntax`, `discoverMdxBodyAssetReferences`, `discoverMdxAssetReferences`, and `previewMarkdownToMdxConversion` - Asset-reference, byte-stream, document-graph, Markdown, and MDX helpers included in commit `76bc388ba9` - Tests: the corresponding `packages/content-engine/src/**/*.test.ts` files already on this branch ### Port with their existing tests | Area | Functions to move or adapt | Tests to move with them | | --- | --- | --- | | Source contract and integrity | `parseContentBlockSourceProp`, `getContentBlockSource`, `isEqualContentBlockSource`, `formatContentBlockSourceIntegrityIssue`, `getContentBlockSourceIntegrityIssues`, `allocateUniqueContentBlockTemplateName` | `packages/sdk/src/content-block.test.ts`, `packages/sdk/src/schema/content-block.test.ts` | | Existing instance/name utilities | `findChildReferenceIndex`, `findParentInstanceReference`, `getInstanceName` | `packages/sdk/src/instances-utils.test.tsx` | | Template naming in mutations | `assignUniqueBlockTemplateNamesMutable`, `findBlockTemplateNameCollision`, `getSourceBackedBlockTemplateContext`, `getBlockTemplateNameConfirmation`, `getBlockTemplateInsertionIndex` | `packages/project-build/src/runtime/block.test.ts` | | Content-mode property permissions | `getContentModePropEligibility`; preserve its distinction between editable, design-only, incompatible, and unknown props | `packages/project-build/src/runtime/content-mode-permissions.test.ts` | | Template resolution | `resolveMdxTemplates` | `packages/project-build/src/runtime/mdx-template-resolution.test.ts` | | Template materialization | `createMdxScopeIdGenerator`, `materializeMdxTemplates` | `packages/project-build/src/runtime/mdx-materialization.test.ts` | | MDX → instances and instances → MDX | `isMaterializedMdxAuthoredContentRoot`, `preserveMdxAuthoredContentIds`, `materializeMdxAuthoredContent`, `reconcileMdxAuthoredContent`, `omitTransientEmptyParagraphs`, `serializeMdxTemplateInsertion` | `packages/project-build/src/runtime/mdx-authored-content.test.ts`; retain the pure serialization cases from `mdx-storage-adapter.test.ts` | | Revisioned Asset I/O | `createAssetContentRevision`, `readAssetContentBytes`, the package-owned `createHttpAssetContentRepository`, the thin Builder `createBuilderHttpAssetContentRepository`, `createAssetContentBridge`, `initAssetContentBridge`, `getAssetContentBridge`; keep one repository implementation rather than copying the old Builder duplicate | `packages/asset-uploader/src/content-repository.test.ts`, `apps/builder/app/builder/shared/assets/mdx-content-repository.test.ts`, `apps/builder/app/shared/asset-content-bridge.client.test.ts` | | Source lifecycle planning | `createContentBlockBodyRemoval`, `prepareContentBlockConnect`, `prepareContentBlockDisconnect`, `prepareContentBlockSwitch`, `ContentBlockSourceRevisionConflictError` | `packages/project-build/src/runtime/content-block-source-lifecycle.test.ts` | | Per-Asset revision/session behavior | `getContentBlockSessionSource`, `isContentBlockSessionSourceCommitted`; reuse the loading, authorization, revision, queue, conflict, retry, cancellation, and source-replacement internals of `createMdxAssetEditingSession`, but key the rebuilt session by Asset rather than Content Block/render root | Port the applicable cases from `packages/project-build/src/runtime/mdx-asset-session.test.ts`; replace root-projection setup with in-memory snapshot setup | | Template migrations | `validateMdxTemplateMigrationConfirmation`, `planMdxTemplateMigration` | `packages/project-build/src/runtime/mdx-template-migration.test.ts` | | Publication | `createPublishedMdxMaterializationCache`, `materializePublishedMdx`, `getPublishedMdxContentDatabaseMaxBytes`, `getDynamicPublishedMdxSourceBlockIds`, `hasDynamicPublishedMdxSources`, `createPublishedBuildContentCompilationPlan`, `resolvePublishedMdxDependencyClosure`, `resolvePublishedMdxAssetCandidates` | `packages/project-build/src/runtime/published-mdx.test.ts`, `packages/project-build/src/content-database.test.ts` | | Build/import integrity | `assertContentBlockSourceIntegrity` and the Content Block cases in `getBuildIntegrityIssues`/`formatBuildIntegrityIssue` | `packages/project-build/src/build-integrity.test.ts` | | MDX paste/MCP insertion | `insertMdxText` and its input/result schemas; retain parsing, confirmation, template-resolution, and insertion-index logic, then invoke the normal mutation path | `packages/project-build/src/runtime/mdx-paste.test.ts` | | Builder source control | `createContentBlockSourceController`, `createBuilderContentBlockSourceController` and the existing source-control components | `content-block-source-controller.test.ts`, `content-block-source.test.tsx` | | Diagnostics | `formatContentBlockDiagnostic`, `deduplicateContentBlockDiagnostics`, `takeNewContentBlockDiagnostics` | Port only their focused cases from `apps/builder/app/shared/content-block-content.test.ts` | ### Explicitly do not port - `projectContentStorageChanges`, `createContentStorageProjection`, `createContentStorageSelectorProjection`, `executeContentStorageMutation`, `executeContentStorageTextReplacement`, `executeContentStoragePropMutation`, and `executeContentStorageStructuralMutation` - `applyMdxContentStorageChanges` and `prepareMdxContentStorageWrites`; replace them with one post-mutation snapshot serializer built from the reusable pure `reconcileMdxAuthoredContent` converter - `createContentBlockApplicationOperations` and the persistence-plan wrappers that route individual editing commands - Builder `$runtime*` projected stores, materialized mutation functions, alternate selector/tree helpers, pending projected-change state, and MDX-specific mutation sequences from `content-block-content.ts` - Canvas loading/saving/open-file presentation state, MDX history/undo, and atomic rollback machinery ## Implementation checklist ### 1. Preserve the `main` editing architecture - [x] Keep normal synchronous insert, text, Enter, slash menu, property, delete, move, wrap, copy/paste, selection, and focus mutations unchanged - [x] Materialize connected content through the same runtime instance interfaces used by ordinary Content Blocks - [x] Keep connected authored children in memory and exclude them from persisted build data - [x] Route persistence only after a synchronous mutation has updated the canvas - [x] Never wait for Asset loading, conversion, or saving before rendering an interaction - [x] Add no MDX-specific implementation to individual editing commands ### 2. Shared Markdown and MDX conversion - [x] Parse Markdown and the Markdown portion of MDX through one shared AST and plugin configuration - [x] Implement one shared MDX-to-instances converter and one shared instances-to-MDX converter - [x] Serialize headings, paragraphs, links, images, lists, tables, task lists, blockquotes, thematic breaks, code, emphasis, strong, strikethrough, and other supported GFM structures as Markdown - [x] Serialize only non-lossless/custom structures as Webstudio JSX - [x] Preserve nested Markdown in MDX elements, frontmatter, comments, tight/loose list semantics, code whitespace, reference links, and Asset references - [x] Omit transient empty draft paragraphs instead of emitting `<ws.element ws:tag="p" />` - [x] Preserve unsupported source text and authored metadata whenever it cannot be converted safely - [x] Add deterministic round-trip and canonical-output coverage for every supported Markdown structure ### 3. Invalid and unsafe MDX - [x] Preserve file source exactly; never silently repair or rewrite invalid source - [x] Ignore imports, exports, expressions, executable JavaScript, invalid properties, and other unsupported nodes when they can be isolated - [x] Continue rendering valid siblings and report every skipped subtree once with filename, line, and column - [x] Use the same recovery pipeline in Builder and publication so both render the same authored result for the current file revision - [x] When structural syntax prevents recovery, render no authored file content in Builder or publication instead of retaining a stale materialization - [x] Skip an unrecoverable Content Block on published sites and continue the build with a warning - [x] Keep invalid and unsafe files editable and reconnectable - [x] Enforce UTF-8, byte-size, depth, node-count, property-count, and generated-output limits with source-located diagnostics where possible ### 4. File formats and Asset creation - [x] Keep `.md` parsing and raw-HTML behavior unchanged - [x] Support creating, opening, editing, deleting, and recreating `.mdx` files through the normal text-file UI - [x] Open `.mdx` in the same editor experience as `.md` - [x] Always create an independent empty MDX Asset even when identical empty files exist - [x] Allow renaming `.md` to `.mdx` without converting or rewriting its contents; after rename, handle invalid contents through the normal MDX diagnostics and recovery pipeline - [x] Update an already-open MDX editor immediately after canvas persistence - [x] Bound read/write buffering and verify the returned Asset identity and revision ### 5. Source contracts and lifecycle - [x] Support direct Asset bindings and dynamic expression bindings - [x] Keep source mutation controls available only in Design mode; show the connected filename and **Open** action read-only in Content mode - [x] Reject duplicate source props, unsupported prop types, missing Assets, non-MDX Assets, and unauthorized Assets - [x] Analyze selected file diagnostics before confirmation without modifying the block or file; MDX syntax diagnostics do not block connection - [x] For a non-empty block, show **Abort** and **Connect** and explain that ordinary children will be replaced - [x] On connect, remove every non-template child, preserve Templates, set `src`, and materialize file content - [x] Replace disconnect handoff with normal `src` reset; discard derived content and leave the block empty - [x] Remove disconnect confirmation and conversion; resetting a repeated binding leaves every occurrence empty - [x] Switching sources validates/loads the target before replacing the usable binding and leaves both files unchanged - [x] Revalidate Asset revision and project version when confirmation is submitted - [x] Make repeated connect/disconnect operations idempotent - [x] Prevent disposed, stale, or out-of-order loads from replacing the newest source - [x] Keep loading, saving, conflict, empty-file, and open-file actions off canvas ### 6. Asynchronous persistence and conflicts - [x] Snapshot the current in-memory authored root after normal mutations - [x] Serialize/coalesce rapid edits per resolved Asset and always prepare against the latest local snapshot - [x] Allow independent Assets to save concurrently without scope leakage - [x] Use one revisioned document session and one ordered save queue per Asset, even when multiple Content Blocks or render scopes reference it - [x] After a shared Asset changes, rematerialize every connected occurrence without persisting their generated instances to project data - [x] Pin the resolved Asset and exact loaded revision for each write - [x] Recheck authorization immediately before storage - [x] Ensure local queued edits never produce stale-revision conflicts with each other - [x] On a genuine external revision conflict, preserve local content, stop writes, and enter the standard reload-required state - [x] Keep failed non-conflicting writes retryable without discarding local content - [x] Finish already accepted persistence writes after a Content Block unmounts or switches source - [x] Cancel obsolete loads, materializations, and UI updates after unmount, reload, or source switch - [x] Dispose project-scoped roots, queues, parse caches, failure state, generations, and session subscriptions when the Asset session is replaced - [x] Invalidate Asset resources and synchronize open file editors after successful writes - [x] Keep multi-root persistence ordered, serial, and non-atomic; report partial completion without rollback - [x] Do not add Asset-history undo/redo; native text-editor undo remains local to the open editor ### 7. Content mode parity - [x] Empty connected blocks remain selectable and expose the normal Content-mode `+` affordance - [x] First insertion renders immediately - [x] Enter creates and focuses the next paragraph immediately - [x] Slash commands work in newly created empty paragraphs - [x] Deleting one selected instance deletes exactly that instance - [x] Mouse and keyboard moves use the same operation and permissions - [x] Preserve sibling reorder, nested reparenting, and moves between editable Content Blocks where `main` permits them - [x] Preserve content-editable props such as Link `href` - [x] Preserve copy, paste, cut, multi-cut, wrapping, unwrapping, grid insertion, selection, Navigator order, and canvas order - [x] Cover mutation routing centrally, retain the full unconnected Builder suite, and exercise representative connected interactions in real Builder E2E ### 8. Template names and permissions - [x] Resolve only exact user-facing names from the direct flat Templates list - [x] Enforce unique names within each Templates list - [x] Reject duplicate names during rename with inline validation - [x] Allocate unique names during insert, paste, duplicate, move, wrap, and unwrap - [x] Apply valid template properties using the same permissions as Content mode - [x] Preserve and warn about ignored design-only, invalid, or stale properties - [x] Preserve template shell provenance without serializing expanded template internals - [x] Generate deterministic materialized IDs from source identity, render scope, and authored usage path so focus/selection survives saves - [x] Warn before renaming or deleting referenced templates; files remain unchanged - [x] Provide shared AST-based rename/remove migration operations for MCP-assisted multi-file cleanup ### 9. Scope, frontmatter, and diagnostics - [x] Scope materialized content by Content Block ID plus exact Collection-item/render context - [x] Prevent repeated instances and dynamic candidates from leaking generated IDs, template resolution, or diagnostics across scopes while sharing the underlying Asset source and revision where applicable - [x] Remove frontmatter editing from Settings; edit the exact YAML through the MDX Asset file editor - [x] Preserve the exact frontmatter source byte-for-byte during canvas body saves - [x] Route canvas body changes and complete-file editor saves through one revisioned per-Asset coordinator - [x] Show a source warning indicator in Settings and precise source-located diagnostics in the Asset editor - [x] Deduplicate repeated diagnostics without losing distinct locations - [x] Keep unresolved-template placeholders selectable and accessible only in Builder ### 10. Asset references, transfer, and integrity - [x] Discover and rewrite Asset references in frontmatter, Markdown links/images, and MDX properties - [x] Preserve Asset references while editing unrelated content - [x] Include referenced Asset and template revisions in dependency/cache identity - [x] Route referenced Assets through published URL rewriting - [x] Validate Content Block source integrity during project import, fragment/page transfer, copy/paste, build integrity, and pre-publish audit - [x] Allow multiple Content Blocks and dynamic render scopes to reference the same MDX Asset without treating the connection as an integrity error - [x] Preserve Templates when copying a connected Content Block - [x] Preserve direct and dynamic Content Block source bindings when copying or pasting within the same project - [x] When copying only one rendered Collection item, resolve that item’s current MDX Asset and replace the copied Content Block’s dynamic source with a direct binding to that same Asset - [x] When copying an entire Collection, preserve the Collection, its instances, and its dynamic source bindings - [x] Preserve the complete MDX source and frontmatter by reference; copying a connected block must not rewrite the shared file - [x] During cross-project transfer, copy every discoverable distinct bound MDX Asset and rewrite transferred bindings to the destination Assets - [x] If some cross-project dynamic candidates cannot be discovered or copied, continue with successful copies, preserve what can be represented safely, and warn about skipped candidates - [x] Revalidate source revisions and the destination project before applying a cross-project transfer - [x] If cross-project Asset creation or project insertion fails, leave the source project unchanged and clean up any newly reserved destination Asset - [x] Use the same transfer operation through Builder and MCP ### 11. Dynamic sources and publication - [x] Resolve dynamic bindings once per render/save scope - [x] Require finite, discoverable candidate sets for publication and load no unrelated MDX - [x] Warn and skip unbounded, missing, unreadable, malformed, or unsafe sources without failing the complete build - [x] Materialize only rendered Content Blocks; ignore Templates definitions and `src` on non-Content-Block instances - [x] Skip unresolved template subtrees while keeping valid siblings - [x] Include selected template styles, fonts, resources, unresolved names, and referenced Asset revisions in generated dependency output - [x] Keep draft-only MDX Assets out of publication retention ### 12. Builder, MCP, CLI, and shared operations - [x] Use the same converters, source lifecycle, revision checks, persistence service, diagnostics, and template migrations in Builder and MCP - [x] Keep frontmatter editing file-based; retain whole-frontmatter runtime operations only where compatibility requires them - [x] Avoid Builder-only or MCP-only mutation/conversion implementations - [x] Regenerate runtime/API contracts from their source definitions - [x] Update CLI Content Engine reference and generated MCP documentation ### 13. Verification and delivery - [x] Add regression tests that reproduce failures from the previous implementation, including double deletion and blocked rendering - [x] Cover two Content Blocks sharing one Asset: edits from either block serialize once, update both canvases, preserve scope-specific Templates, and cannot overwrite a newer local snapshot - [x] Verify new tests fail against the broken implementation and pass with the corrected behavior - [x] Run SDK, Content Engine, Asset Uploader, Project Build, Builder, React SDK, CLI, Protocol, and HTTP Client affected suites and typechecks - [x] Run real Builder E2E for source lifecycle, live file refresh, empty insertion, Enter/slash focus, Link props, delete, keyboard reorder, switching, disconnect, conflicts, unresolved templates, dynamic sources, and repeated scopes - [x] Regenerate shared project fixture bundles and verify a second generation leaves the tree clean - [x] Run lint, package boundaries, `pnpm oxfmt --check`, and `git diff --check` - [x] Review and update authoritative `docs/` content and navigation - [ ] Run the complete agent evaluation suite — not run because it requires separate explicit approval ### 14. Content-mode parity and disconnect handoff - [x] Allow keyboard reordering in Content mode through the normal move command while keeping the editable Content Block boundary - [x] Let already accepted Asset writes drain independently before resetting the source; disconnect does not load, flush, or convert the file - [x] Remove MDX-to-instance copying from disconnect - [x] Reset `src`, remove the derived tree, and leave the ordinary Content Block empty - [x] Preserve automatic text-editor focus after inserting editable content from the template menu - [x] Remove Builder frontmatter-save UI and report complete-file save failures through the Asset editor - [x] Add regression coverage for keyboard movement, disconnect teardown, source switching, insertion focus, and paragraph deletion focus restoration - [x] Run the complete six-scenario MDX Content Block Builder E2E suite ### 15. Final architecture review fixes - [x] Keep one canonical ordered pending document per Asset so shared Content Block occurrences cannot conflict with one another - [x] Merge ordered local edits from different occurrences while preserving text changes, insertions, deletions, and sibling reordering; keep external revisions on the strict reload-required path - [x] Centralize typed render-scope parsing, repeated-occurrence detection, scoped variable lookup, and dynamic Asset resolution across Canvas, Settings, source lifecycle, and copy/paste - [x] Capture the owning Asset session when a save is accepted so normal unmount/source-switch teardown cannot redirect or lose it - [x] Dispose project-owned roots, queues, caches, generations, failure state, and subscriptions when replacing the project Asset session - [x] Add regression coverage for same-Asset edits from two occurrences, same-node last-write ordering, structural reorder, insertion preservation, scoped source resolution, and project cleanup - [x] Re-run affected full unit suites, typechecks, production build, static checks, and all six MDX Content Block Builder E2E scenarios ### 16. Final persistence and transfer hardening - [x] Drain every accepted per-Asset edit before project-session teardown while retaining the owning authorization context - [x] Drain canvas serialization queues and revisioned Asset writes before pre-publish audit and publication - [x] Block publication on a queued serialization failure or Asset revision conflict - [x] Copy actual Asset bytes during cross-project paste, preserve MDX reference names, rewrite direct and Collection-backed Asset IDs, and clean up newly created Assets when insertion fails - [x] Include every resolved dynamic MDX source when copying an entire Collection and warn while preserving successful copies when individual Assets cannot be transferred - [x] Keep MDX editor save failures visible with retry and route conflicts through the standard reload-required flow - [x] Give keyboard nested reparenting the same editable Content Block boundary used by pointer movement - [x] Add regression coverage and rerun the complete Content Engine and Builder unit suites plus type, lint, formatting, package-boundary, and diff checks ## Current rebuild status - The connected Content Block now owns one complete MDX document: frontmatter feeds designed-shell bindings and the explicit Body outlet owns the authored MDX body. - Builder and published output resolve the same frontmatter document graph, including nested document references and Asset references. - Direct frontmatter text/property edits use the existing ordered per-Asset session; referenced-document values remain file-owned. - Repeated Collection occurrences keep isolated runtime shell/Body identities and occurrence-specific document values. - The local blog article layout has been upgraded to the document/Body model and its bindings validate cleanly. - Full affected package suites and typechecks pass; every new regression was demonstrated fail/pass. - `pnpm lint`, package boundaries, `pnpm oxfmt --check`, generated API/docs checks, fixture builds, and `git diff --check` pass. - Full fixture synchronization is blocked by the development API rejecting this local CLI version before fixture generation; existing fixture bundles build successfully. - Complete agent evaluations remain pending explicit approval. ### 17. Treat Content Block source as a property - [x] Declare Content Block `src` in component metadata as a Design-mode initial property labeled **Source** - [x] Add the short property description: “Create an .mdx file in Assets, then select or bind it here.” - [x] Accept only a direct Asset or an expression resolving to an Asset ID; reject URL/string sources, missing Assets, and non-MDX Assets - [x] Extract and reuse the Image-style Asset picker and binding presentation without duplicating source-control UI - [x] Render Source through the normal properties section and remove the separate source-section placement and special `src` filtering - [x] Route Source changes through Content Block connect, switch, and disconnect lifecycle operations instead of generic prop updates - [x] Keep connect confirmation, Open, diagnostics, and persistence state attached to Source; remove frontmatter and disconnect UI - [x] Add regression coverage for direct Assets, dynamic Asset bindings, invalid resolved values, lifecycle operations, and Design/Content mode visibility - [x] Re-review authoritative `docs/` content for the Source-property UI change and update it if required ### 18. Source property UX - [x] Show one full-width bindable Connect .mdx file button while disconnected - [x] Show equal-width filename and Open buttons while connected - [x] Switch files from the filename button through the source lifecycle - [x] Override PropertyLabel reset and Alt-click with the disconnect lifecycle - [x] Preserve confirmation, pending, failure, repeated-scope, and dynamic-source behavior - [x] Add regression coverage for connect, switch, binding, reset, and disconnect - [x] Update Content Block documentation - [x] Run affected tests, typecheck, lint, formatting, package boundaries, and diff checks ### 19. Reduced file-owned frontmatter follow-up This checklist supersedes the earlier structured-frontmatter and disconnect-copy behavior. Frontmatter remains file-owned and is edited only in the MDX Asset editor. #### A. Remove structured frontmatter UI - [x] Remove the JSON frontmatter editor and unused Builder controller/state - [x] Keep source mutation Design-mode-only; show connected Source read-only in Content mode and allow authorized editors to open the complete file - [x] Retain runtime frontmatter APIs only where compatibility requires them #### B. Preserve source and separate validation - [x] Preserve the exact frontmatter envelope during every canvas body save without parsing/stringifying YAML - [x] Preserve comments, ordering, quoting, whitespace, BOM, newline style, delimiters, and invalid but closed YAML - [x] Keep the existing unsupported-node/property MDX recovery; do not add an ad hoc partial parser - [x] Parse body and frontmatter independently so either valid half remains usable - [x] Keep bounded Asset-query frontmatter extraction unchanged - [x] Treat structurally malformed MDX as having no safe body tree #### C. Share Asset state with the raw editor - [x] Route canvas and complete-file changes through one ordered, non-blocking per-Asset coordinator - [x] Update canvas and clean raw editors immediately from shared in-memory source - [x] Reject stale dirty-editor saves, preserve unsaved text, and require reload; never force-overwrite - [x] Drain accepted writes after source replacement/disconnect and report background failures globally - [x] Dispose sessions only after they have no consumers and no queued work #### D. Connect, replace, and disconnect - [x] Load and diagnose before connect; block only unreadable/unauthorized/non-MDX Assets - [x] Show **Abort / Connect** for non-empty blocks, including diagnostics and the recoverable-content warning - [x] Set `src` and delete persisted children through one runtime mutation - [x] Switch to a loaded source without waiting for the previous Asset's network save - [x] Remove disconnect confirmation and conversion - [x] Reset `src` immediately, discard the derived tree, leave the block empty, and preserve the Asset - [x] Resetting a bound source leaves every repeated occurrence empty #### E. Dynamic and shared sources - [x] Keep one source/revision state per Asset and a separate derived instances tree per occurrence - [x] Resolve Templates independently per Content Block and preserve Builder-only missing-template placeholders - [x] Resolve bound `src` from the selected Collection occurrence - [x] Make **Open** use the occurrence's resolved Asset; update the filename and disable Open when unresolved - [x] Add direct/bound tests including Collection item A opening `a.mdx` and item B opening `b.mdx` #### F. Diagnostics, MCP, and publication - [x] Show an accessible warning icon beside Source with the first diagnostic, line/column, and remaining count - [x] Show precise YAML/MDX diagnostics in the Asset editor without repetitive canvas/toast UI - [x] Reuse parser/diagnostic/revision contracts in Builder, build, publication, and MCP - [x] Keep MCP frontmatter editing file-based and return structured diagnostics for complete-source writes - [x] Emit build warnings; published sites skip invalid/unsupported content without diagnostic UI #### G. Verification and documentation - [x] Add fail/pass regressions for source preservation, independent validation, stale raw saves, lifecycle, dynamic Open, shared Assets, and cleanup - [x] Run affected tests, typechecks, lint, formatting, package boundaries, fixtures, and diff checks - [x] Open Markdown and MDX editors at twice the default dialog size with the right-side preview visible initially - [x] Document canvas body editing, file-owned frontmatter, Content-mode Asset access, and cloning an MDX Asset for a new post - [x] Perform a final correctness, data-loss, DRY, simplification, accessibility, and performance review - [x] Keep this checklist synchronized with implementation status ### 20. Content-mode component properties - [x] Show a connected Content Block Source read-only in Content mode with an active **Open** action - [x] Keep source switching, binding, reset, and disconnect available only in Design mode - [x] Expose HTML Embed code and preserve Code Text's code-editor metadata on authored Text Content - [x] Audit every registered component and expose authored payload while keeping design, structure, and behavior controls in Design mode - [x] Keep media dimensions and Code Text theme Design-only while preserving adapter-owned intrinsic Image metadata during MDX round trips - [x] Add exact component-permission and regression coverage - [x] Update authoritative component documentation - [x] Run affected full suites, typechecks, formatting, lint, package boundaries, and generated contract/documentation checks ### 21. Designed document shell and Body outlet - [x] Add one structural **Content Block Body** outlet and keep legacy Content Blocks without it compatible - [x] Materialize and persist only `document.body` children through the Body outlet while preserving the surrounding designed shell and Templates - [x] Expose the connected document frontmatter through a Content Block `document` parameter for shell text and property bindings - [x] Write direct frontmatter-bound text and properties back through the same ordered per-Asset session without blocking canvas mutations - [x] Resolve document `$ref` dependencies and Asset references consistently in Builder and published output - [x] Keep referenced-document values read-only on canvas and direct editors to the referenced file - [x] Isolate repeated Collection occurrences while retaining occurrence-specific source, frontmatter, shell, and Body content - [x] Render the same designed shell, frontmatter values, and Body substitution in generated/published output - [x] Upgrade the local blog article layout to bind title, excerpt, dates, author data, and images from frontmatter and render MDX through Body - [x] Add fail/pass regressions for nested Body discovery, lifecycle, permissions, repeated values, document references, and generated shell rendering - [x] Update authoritative Content Block documentation - [x] Run affected tests and typechecks, formatting, lint, package boundaries, generated API/docs checks, and fixture builds ### 22. Final nested references and frontmatter editing - [x] Keep direct frontmatter-bound text and property controls editable in Content mode without exposing or replacing their bindings - [x] Keep referenced-document values read-only and preserve legacy instance-property name normalization - [x] Render direct frontmatter text bindings through the plain-text editing path - [x] Resolve nested legacy and structured Asset references from referenced Markdown/MDX frontmatter - [x] Preserve missing Asset references, emit structured `ASSET_NOT_FOUND` diagnostics, and keep them out of the document graph - [x] Add fail/pass regressions for the real Settings control and root/nested missing Asset-reference cases - [x] Fix exact Content Block selection after the Body node appears in Navigator - [x] Run full affected suites/typechecks, all six MDX Content Block E2E scenarios, deterministic fixture builds, generated checks, lint, formatting, package boundaries, and diff checks ### Before merge - [x] Resolve #6261: Nested Asset references in referenced Markdown frontmatter work consistently in Assets queries, previews, publication, and static export ### 23. Explicit writable expression bindings - [x] Add an explicit `readwrite` mode to expression bindings; existing expressions remain read-only by default - [x] Accept `readwrite` only for direct static member paths and centralize their parsing and validation - [x] Require the expression root to provide a writable data source; initially support the connected Content Block document frontmatter - [x] Update Content Block permissions, canvas text editing, and property controls to require a valid writable binding - [x] Persist direct frontmatter edits through the existing ordered per-Asset session - [x] Create new direct frontmatter bindings with `readwrite` mode and migrate existing compatible bindings - [x] Preserve writable mode through schemas, serialization, loading, copy/paste, transfer, and export - [x] Add fail/pass regressions for valid paths, derived expressions, unsupported sources, permissions, persistence, and compatibility - [x] Review authoritative `docs/` content and update it if the persisted binding contract affects user-facing documentation - [x] Reject unsafe frontmatter paths and reject `readwrite` bindings outside a connected Content Block document - [x] Preserve expression binding mode through the public HTTP client and document the MCP authoring contract - [x] Limit frontmatter value write-back to Content mode so Design mode retains normal binding replacement/reset behavior - [x] Keep bindings inside Templates and across nested Content Block ownership boundaries read-only - [x] Upgrade compatible legacy bindings even when the Content Block is disconnected during migration - [x] Run affected tests, typechecks, formatting, lint, package boundaries, fixture checks, and `git diff --check` Verification: full SDK (567), Project Build (2,277), and Builder (2,695) suites pass, along with affected Expression, Migration, Template, HTTP Client, and MCP suites; typechecks, lint, formatting, package boundaries, and diff checks pass. Generated contracts are refreshed. Fixture synchronization remains blocked before generation because the development API rejects this local CLI version as incompatible. The checked-in bundles pass the current Protocol suite and every fixture build with the current bundle version. |
||
|
|
db2175f333 |
fix: transfer pasted assets across deployments (#6228)
Closes #6227 ## What changed Cross-deployment copy/paste now transfers referenced assets into the destination project before inserting instances, pages, templates, or folders. - Add optional source deployment metadata to the existing v0.1 clipboard formats. - Keep old v0.1 payloads readable and keep new payloads readable by older builders, which ignore the additive field. - Reuse the existing asset upload-by-URL flow for images, fonts, videos, and files. - Preserve source video dimensions through the existing upload route. - Replace source asset IDs and font-family references with destination values before runtime insertion. - Include and remap page social images and marketplace thumbnails. - Reject current clipboard payloads whose page metadata references omitted assets. - Leave the public `pageTransfer.insert` runtime input unchanged. - Abort insertion with a paste error when an asset cannot be imported. No clipboard version increase, custom network fetcher, address blocklist, rollback framework, or global paste wrapper is introduced. If a multi-asset import partially uploads before a later upload fails, the completed uploads may remain unused. ## Checklist - [x] Rebased onto `main` - [x] Preserve the existing v0.1 clipboard format - [x] Transfer and remap instance assets - [x] Transfer and remap page, template, and folder assets - [x] Preserve video metadata - [x] Keep runtime page-transfer API compatible - [x] Add regression coverage - [x] Review documentation impact and update page-copy documentation - [x] Run Builder tests and typecheck - [x] Run project-build tests and typecheck - [x] Run lint, package-boundary, and generated-API checks ## Verification - Builder full suite: 219 files, 2,552 tests passed - Project-build full suite: 78 files, 2,129 tests passed - Final affected suite: 84 tests passed - New regressions were confirmed failing before the fixes and passing afterward - Builder and project-build typechecks passed - `pnpm lint` passed - `pnpm check:package-boundaries` passed - `pnpm check:generated-api` passed - Fixture inputs and generated fixture bundles are unaffected |
||
|
|
130d371759 |
perf: Skip resources in statically hidden subtrees (#6233)
## Summary - Skip read-time resources scoped to an instance with literal `show=false` or any descendant of that instance. - Use the same static reachability rule in the Builder client preloader and CLI-generated published resource map. - Keep dynamic `show` expressions on the existing loading path because their result may depend on resource data. ## Data path and performance In the Builder, the client-side resource request set now excludes statically hidden subtrees before requests enter the preloader. For generated sites, the CLI still scans project instances and props during build, but removes statically hidden subtrees from the page build data. Their read resources therefore never enter the generated server-side resource map and no corresponding resource network request runs during page rendering. This removes request and server/network cost for literal-`false` subtrees. It does not remove build-time project scanning, and resources under dynamic conditions still load. ## Compatibility - No project schema or persisted data changes. - No migrations. - Action-bound resources retain their existing explicit-trigger behavior. - Dynamic `show` expressions retain current behavior. ## Checklist - [x] Add shared static-subtree reachability logic. - [x] Apply it to Builder preview resource preloading. - [x] Apply it to generated/published resource collection. - [x] Add Builder and generated-output regressions for direct, descendant, visible, and dynamic cases. - [x] Demonstrate the new regressions fail against the previous behavior and pass with the implementation. - [x] Review authoritative `docs/` content; no documentation update is required because the UI and authoring workflow are unchanged. - [x] Review fixture inputs and outputs; no fixture contains this resource/show combination, so fixture regeneration is not required. ## Verification - `pnpm --filter @webstudio-is/sdk test` - `pnpm --filter @webstudio-is/react-sdk test` - `pnpm --filter @webstudio-is/builder exec vitest run app/shared/nano-states/props.test.tsx` - `pnpm --filter webstudio build:content-runtime` - `pnpm --filter webstudio exec vitest run src/prebuild.test.ts` - Typechecks for SDK, React SDK, Builder, and CLI - Targeted `oxlint --deny-warnings` - `git diff --check` Agent evaluations were not run because they require separate explicit approval. Closes #6232 |
||
|
|
0760d6cfec |
fix: upload videos from Builder and MCP (#6189)
## Summary Video uploads failed because file detection returned `video` while the canonical Asset schema accepted only `font | image | file`. The mismatch was introduced when video detection was added in #5539; the recent JSON work only exposed the adjacent contract gap and did not cause it. This makes `video` a first-class `Asset["type"]` across Builder, SDK, storage formatting, project bundles, HTTP uploads, and MCP. There is no separate upload-only type and no conversion from `video` to `file`. ## Implementation - Add the canonical `video` Asset variant with width/height metadata. - Make Builder UI reservations and optimistic assets preserve `video`. - Make MCP and HTTP upload schemas derive their enum from the canonical SDK `assetType`. - Preserve video metadata through S3 and filesystem upload paths. - Include canonical video assets in project/canvas bundles. - Keep legacy video records without dimension metadata readable as generic `file` assets. - Fall back to a labeled file preview when the browser cannot decode a video format such as AVI; browser-supported formats continue to render video thumbnails. - Regenerate runtime operation contracts and sync fixtures from the PR deployment. ## Contract protection - Assert every detected upload category parses as a canonical Asset type. - Exercise Builder reservations for every value in `assetType.options`. - Assert MCP's upload enum exactly equals `assetType.options`. - Exercise direct HTTP video upload metadata and URL construction. - Cover canonical video formatting, legacy fallback, preview assets, and canvas serialization. - Verify a video decode error replaces the blank media element with a visible format-labeled fallback. - Store canonical WebM and MP4 records with dimensions in the `webstudio-features` fixture and verify generated runtime metadata. ## Checklist - [x] Make video a canonical Asset type - [x] Verify Builder UI upload flow - [x] Verify MCP and direct HTTP upload contracts - [x] Add fail/pass regression coverage for the broken paths - [x] Add an Assets panel fallback for browser-unsupported video formats - [x] Regenerate runtime contracts - [x] Sync and build fixtures against the PR deployment - [x] Run affected package tests, typechecks, lint, package-boundary, generated API, and generated docs checks - [x] Review documentation impact — existing Assets documentation already lists supported video formats and upload behavior; no user-facing documentation change is required ## Verification - Builder: 2,543 tests passed - Builder asset thumbnail regression: 14 tests passed - Project build/MCP runtime: 2,111 tests passed - CLI: 999 tests passed - SDK: 516 tests passed - Asset uploader: 199 tests passed - Protocol: 92 tests passed - HTTP client: 38 tests passed - Typechecks passed for Builder, SDK, asset uploader, project build, protocol, HTTP client, and CLI - Lint and package-boundary checks passed - Generated API and documentation checks passed - Fixture sync completed against `fix-video-up-be4d8a0.development.webstudio.is` - All fixture builds passed after synchronization Closes #6186 |
||
|
|
5a57234c75 |
revert: Store Content Block content in MDX assets
Revert the accidental merge of #6125 so the implementation can continue in draft PR #6181. |
||
|
|
86b6310fff | feat: Store Content Block content in MDX assets | ||
|
|
cb51d477e1 |
fix: Refresh resource diagnostics metadata (#6113)
## Summary Follow up on #6095 so explicit Diagnostics refreshes update both diagnostic and performance metadata, stale refreshes are cancelled during invalidation, and Assets batch measurements are labeled consistently in the Builder. Ref #6092 ## Technical choices - Cache response diagnostics and performance metadata through one shared path. - Attach an AbortController to each explicit diagnostics request and reject stale results after invalidation. - Accept additive performance fields from newer servers. - Clarify that Assets timings and counters cover the combined batch. - Reuse the measured view-authorization path across repository entry points. ## Implementation checklist - [x] Refresh performance metadata with explicit diagnostics. - [x] Cancel and ignore stale diagnostics requests. - [x] Keep performance parsing forward-compatible with additive fields. - [x] Clarify Assets batch labels and descriptions. - [x] Add regression coverage for refresh, invalidation, and parsing behavior. - [x] Review documentation impact — authoritative `docs/` does not cover this internal Diagnostics tooling, so no update is required. - [ ] Run affected Builder and asset-uploader typechecks. - [ ] Confirm GitHub checks pass. ## Verification - Builder diagnostics/resource tests: 27 passed. - Asset repository tests: 39 passed. - `git diff --check origin/main...HEAD` — passed. |
||
|
|
11d76eb43d |
perf: Diagnose and reduce Assets resource latency (#6095)
## Summary Adds Builder-only resource performance metrics to grouped Diagnostics tables, removes duplicate object-store reads from the Assets query path, scopes compiled-artifact reuse to one repository request, and compiles unpinned Builder previews from their concrete query values instead of hydrating the complete saved dynamic deployment plan. Nothing is precomputed or retained in a warm serverless process after the request finishes. Ref #6092 ## Technical choices - Keep `__performance__` as internal response metadata and strip it before resource values enter expression-visible caches. - Record Builder round-trip, server duration, response size, Assets phases, document work, completed storage reads, and cumulative compiler/document-graph storage-read durations. - Present Diagnostics as collapsible sections ordered Database and sizes, Timing, Query work, then detailed database/result inspectors. Open only the first available section by default. Every metric keeps an explanatory tooltip, with info icons revealed only when the label cell is hovered or keyboard-focused. - Add a copy action to every section header. It copies that section's raw structured data as formatted JSON without opening a collapsed section. - Keep Index preparation as an inclusive parent duration and explain that relationship in Diagnostics. - Compile ordinary unpinned Builder preview requests from a union of their concrete query values. A detail route therefore hydrates only documents that can satisfy that request. - Continue using the saved artifact-producing plan for revision-pinned requests. - Keep publication and prepublication preparation unchanged. - Load only `props`, `dataSources`, and `resources` when constructing the single-query Builder preview plan, matching the already-narrow batch path instead of downloading and parsing the complete dev Build row. - Build the deployment-wide saved plan only when detailed Diagnostics requests its Published database statistics. Report that work as the separate inclusive `Published diagnostics` timing and exclude its child phases and fetch counters from the normal query measurements. - Reuse successfully read content bytes across compiler preparation, document graph construction, and runtime resolution within one query or batch. Graph-fetched bytes are size-validated before entering the revision-keyed request map, and graph additions stop when the request cache reaches the 2 MiB hydrated-content limit. - Before repairing canonical metadata, load candidate metadata once and prove its revision, base document, and prepared metadata tier match the open source snapshot. Skip synchronization when it is already current; retain recovery and repair for stale, missing, insufficient-tier, or inconsistent rows. - Key request-local bytes by content reference and revision so source-change retries cannot reuse stale data. - Instantiate compiled-artifact reuse per `PostgresAssetRepository`; repeated preparations inside one request can reuse or coalesce work, but later requests and other serverless instances cannot. - Remove the module-level 32-entry compiled-artifact cache introduced in #5946. - Introduce no precomputed, persisted, process-local, or cross-request content cache. ## Findings and measurements The supplied production post resource was bimodal before removing the process cache: - Warm process: approximately 1.0–1.5 seconds. - Cold process: approximately 5.7–6.7 seconds. - Ordinary response: approximately 4.6 kB. The earlier development capture was a process-cache hit: 542.6 ms server time and 932.5 ms Builder round trip. A later hit measured 491.5 ms server time and 739.1 ms round trip. Those results demonstrated the old module cache's nondeterministic serverless behavior rather than cold-path performance. After request-scoped reuse replaced the process cache, the supplied cold capture measured 3,848.5 ms server time. Index preparation was 3,259.7 ms. The saved dynamic plan caused 25 compiler content fetches and 301 kB of compiler input even though the concrete post query resolved only two documents. The published database contained 28 documents and used 45.4 kB bounded / 512 kB unbounded. Tracing compilation also exposed duplicate request-time I/O: selected Markdown was fetched for compiler input, document graph construction, and runtime document resolution. Request-local byte reuse changes that representative path from three object-store reads to one. The remaining dominant issue was therefore query breadth, not response serialization or runtime result resolution. The Builder was compiling the deployment-wide dynamic plan to answer a concrete post request. The new preview path compiles the concrete values; regression coverage demonstrates that a saved two-post dynamic plan reads only `post-a` when previewing `post-a`. The next cold capture confirmed that change: Builder round trip fell from 4,391.4 ms to 2,245.2 ms, server duration from 3,848.5 ms to 1,601.8 ms, and index preparation from 3,259.7 ms to 1,044.9 ms. Compiler content fetches fell from 25 to 1 and document-graph content fetches from 3 to 1. Canonical metadata remained 239.2 ms because an unchanged request still performed a repair synchronization round and then loaded the same stored metadata again. The new fast path reduces that normal case to one recovery-safe candidate metadata read. A subsequent capture confirmed the canonical metadata fast path: server duration fell from 1,601.8 ms to 1,401.0 ms, index preparation from 1,044.9 ms to 684.8 ms, and canonical metadata from 239.2 ms to 74.7 ms. The post query remained narrow at one compiler fetch and one document-graph fetch. Builder round trip rose from 2,245.2 ms to 2,411.1 ms while the server improved, so that difference is outside the measured server work and is consistent with browser/network/development-environment variance. At that point `buildPlan` was the largest remaining isolated phase at 242.8 ms. Single-query preview was still loading every dev Build column and parsing pages, styles, instances, deployment data, and other fields that are not inputs to the Assets compilation plan. The narrow loader now requests and parses only the three content-engine fields used by plan construction. The next capture confirmed that optimization: build plan fell from 242.8 ms to 99.0 ms, server duration from 1,401.0 ms to 1,056.8 ms, and the concrete post query remained at one compiler fetch and one document-graph fetch. Index preparation stayed effectively flat at 679.7 ms, making compiler entries (142.3 ms), source snapshot (119.2 ms), source validation (119.1 ms), and document graph (114.0 ms) the next candidates. The new child timings separate cumulative storage waiting from the remaining compiler/document-graph work before choosing another behavioral optimization. The storage timing capture then showed that I/O dominated the remaining compiler and graph phases: compiler storage was 114.6 ms of 125.9 ms, while document-graph storage was 114.0 ms of 147.6 ms. The graph path consulted compiler-cached bytes but did not retain newly fetched graph documents, so runtime reference resolution fetched the same two-document chain again. Retaining validated graph bytes in the same request reduces the regression fixture from four physical object-store reads to two without changing its resolved result. Final three-run measurements after same-request graph-byte reuse were stable: median Builder round trip was 926.2 ms, median server duration was 681.9 ms, median index preparation was 407.9 ms, and median document resolution was 3.7 ms. Compared with the uncached 3,848.5 ms server / 3,259.7 ms index baseline, that is approximately 82% less server time and 87% less index-preparation time for this post query. A final DRY/correctness review centralized request-local byte-cache keying and its 2 MiB accounting, removed repeated Diagnostics size rows and SDK metadata assembly, typed phase keys, and corrected response-size measurement so internal performance metadata is excluded as the UI promises. The module cache was also only bounded for completed entries. Many simultaneous unique-project compilations in one process could exceed 32 pending entries until they settled. Scoping reuse to one repository request removes cross-user retention and tenant cache competition while preserving duplicate-work elimination inside the request. ## Todo - [x] Add generic resource timing and response-size metrics. - [x] Add Assets phase, document graph, source validation, storage-read count/byte metrics, and compiler/document-graph storage-read durations. - [x] Render ordered collapsible Diagnostics sections with only the first open by default, per-metric explanations, interaction-only info icons, and per-section JSON copy actions. - [x] Reuse compiler and document-graph content bytes through runtime resolution within the current request, without precomputation. - [x] Remove cross-request process caching and retain request-scoped compilation reuse. - [x] Compile unpinned previews from concrete query values. - [x] Separate deployment-wide diagnostic preparation from normal query phase counters. - [x] Skip redundant canonical metadata synchronization when stored candidates already match the source snapshot and requested tier. - [x] Narrow single-query Build reads to the three fields required by Assets plan construction. - [x] Expose cumulative compiler and document-graph storage-read timing as explained child rows. - [x] Add regression coverage, including red/green verification. - [x] Review the final diff for correctness, duplication, and unnecessary complexity. - [x] Verify concrete-query compilation against the supplied post resource: compiler fetches fell from 25 to 1. - [x] Remeasure the canonical metadata fast path: canonical metadata fell from 239.2 ms to 74.7 ms and index preparation from 1,044.9 ms to 684.8 ms. - [x] Review documentation impact: no authoritative `docs/` page covers this internal Diagnostics tooling, so no documentation update is required. - [x] Run focused tests, affected package typechecks, lint, formatting, and the fixture pipeline. ## Verification - Content engine tests: 23 passed. - Asset uploader tests: 195 passed, including repository, canonical synchronization, request-cache, and storage-timing coverage. - Builder focused Diagnostics tests: 10 passed in the latest storage-timing pass; 15 passed in the preceding final pass and 35 across the earlier instrumentation scope. The collapsible view test verifies section order, default state, and deferred content interaction. - Project tests: 84 passed, including 5 focused preview tests. The focused regression failed against the full-Build loader and passed after switching the single-query path to the narrow content-engine loader. - SDK resource loader tests: 24 passed. - Current canonical metadata regressions failed under the old always-synchronize expectations and pass with one metadata load and no synchronization; stale metadata still exercises and passes the repair fallback. - The Timing clipboard test failed with an empty payload and passed with the complete raw duration/phase JSON; copying a collapsed section is also verified not to open it. - The collapsible default-state test failed when Timing was temporarily opened alongside Database and sizes, then passed after restoring the first-section-only behavior. - The concrete-preview regression failed before the optimization by reading both `post-a` and `post-b`, then passed by reading only `post-a`. - A repository-reuse regression failed without the request cache and passed when reuse became the repository default. - The content-read regression failed before the original compiler reuse optimization with three storage calls and passed afterward with one. - The two-document graph regression failed with four physical storage calls and passed with two after graph reads populated the request-local runtime cache. - The response-size regression failed at 127 bytes when internal performance metadata was included and passed at the actual 66-byte payload after separating that metadata. - Builder, content engine, asset uploader, project, and SDK typechecks passed. - Oxfmt, oxlint, and `git diff --check` passed. - The full fixture link/sync/build pipeline passed with no tracked fixture changes after each runtime change. ## Known limitations - Detailed Diagnostics intentionally prepares both the concrete query database and the deployment-wide Published database. `Published diagnostics` reports the latter separately; normal resource loads do not perform it. - Builder round-trip and server duration cover the complete resource batch and therefore include Published diagnostics when the detailed view requests it. - Phase durations are aggregated when a batch contains multiple Assets queries. - Compiler and document-graph storage-read timings sum completed fetch durations. They exclude request-local byte-cache hits and can exceed wall-clock parent phases when reads run concurrently. - Content-read counters report completed normal-query reads; deployment-wide diagnostic reads are excluded, and failed storage reads are represented by the enclosing phase timing and error. - Request-local byte reuse retains selected content only until the current query or batch finishes. Document-graph additions are capped by the existing 2 MiB hydrated-content limit; content beyond the cap is still streamed and can be fetched again at runtime. - Source snapshot and final source validation remain separate reads. The metadata fast path removes redundant repair work without weakening that concurrent-change check. ## Measurement gate PR deployments use the development backend. The supplied production project returns 500 on both the branch deployment and `main.development.webstudio.is`, while the production host returns 200. Final phase timings for the exact project require its corresponding development project/session or an explicitly authorized production-backed deployment. An unrelated fixture will not be used to claim the production improvement. |
||
|
|
db3b11970f |
fix: Optimize images in Markdown Embed (#6089)
## Summary Markdown Embed now renders image tags with the same optimized attributes and canvas behavior as the SDK Image component. The implementation shares the complete attribute-resolution path without rendering React inside the Markdown pipeline: - `@webstudio-is/image` exposes one framework-independent `getImageAttributes` function for optimization, placeholder fallback, loading, decoding, dimensions, alternative text, and responsive sources. - React-only element props such as `className`, `style`, handlers, context, and refs stay in React callers. - The SDK Image component and Markdown renderer both use `getSdkImageProps` for canvas-specific loading, decoding, asset identity, and incomplete-upload handling. - Markdown retains only the necessary HTML attribute-name conversion, sanitization, and Safari-safe `sizes` / `srcset` / `src` ordering. - The redundant low-level React Image primitive was removed. Its callers now render native `img` elements, and the image package no longer has React runtime or peer dependencies. ## Compatibility and tradeoffs - The SDK Image component remains the public React component because it owns React context and ref forwarding. - The removed low-level `@webstudio-is/image` Image export is replaced by the pure attribute function for internal callers. - No data migration or schema change is required. - Loader output is sanitized after transformation, while unsafe input URLs are sanitized before reaching the loader. ## Todo - [x] Share image attribute resolution between native, SDK, and Markdown images. - [x] Preserve SDK canvas behavior for Markdown images. - [x] Preserve safe URL sanitization and Safari-safe responsive attribute ordering. - [x] Remove the redundant low-level React image primitive and migrate its callers. - [x] Remove the overlapping `getImageProps` abstraction. - [x] Add regression coverage comparing Markdown, SDK Image, and native attribute output. - [x] Review documentation impact and update the Markdown Embed documentation. - [x] Run targeted tests, typechecks, builds, lint, formatting, package-boundary checks, and fixture generation. ## Verification - `pnpm --filter @webstudio-is/image test` - `pnpm --filter @webstudio-is/image typecheck` - `pnpm --filter @webstudio-is/image build` - `pnpm --filter @webstudio-is/image dts` - `pnpm --filter @webstudio-is/sdk-components-react test` - `pnpm --filter @webstudio-is/sdk-components-react typecheck` - `pnpm --filter @webstudio-is/sdk-components-react build` - `pnpm --filter @webstudio-is/sdk-components-react dts` - `pnpm --filter @webstudio-is/builder typecheck` - `pnpm --filter @webstudio-is/design-system typecheck` - Targeted Oxlint and Oxfmt checks - `pnpm check:package-boundaries` - `pnpm fixtures` (no fixture diff) Ref #6014 |
||
|
|
c44ef705cf |
perf: Speed up Builder resource loading (#6011)
## Problem Assets-backed Builder pages can take 6–7 seconds to become usable even when their Assets resources only request metadata. On the production-derived Blog project, a single Assets query measured 8,703 ms and an immediate repeat measured 8,110 ms. Four sibling Assets resources repeated the same authorization, development-build load/parse, compilation, canonical-asset preparation, runtime metadata load, and document work. ## Architecture The loader now treats remote resources holistically while preserving provider boundaries: - The client scheduler is resource-provider neutral. It versions logical requests, frees capacity when obsolete requests are replaced, ignores stale results, and aborts physical batches only when every member is obsolete. - Ordinary Assets requests in one server resource-loader call use a deferred Assets provider and execute as one repository batch. Independent HTTP, GraphQL, and other resources start immediately instead of waiting for Assets preparation. - Arbitrary remote responses are **not** coalesced or persistently cached. Their auth headers, cookies, upstream freshness, and `Vary` semantics remain isolated. - The Assets provider authorizes and creates its API context once per batch, then loads only `props`, `dataSources`, and `resources` from the development Build. - Queries covered by the saved Build compilation plan reuse that exact plan/artifact, preserving published byte limits, truncation, and pinned-revision behavior. Uncovered unpinned queries use a deterministic literal union and safely fall back to their existing individual path if shared preparation cannot preserve behavior. - Request-scoped document loading uses npm's byte-bounded `lru-cache` so sibling queries do not reread the same referenced author/content files. - Sibling dynamic queries with the same filter, sort, output, and content shape stitch into one document-graph resolution while keeping filter values, pagination windows, results, and failures independent. Aggregate document/byte-limit failures fall back to isolated execution. - Provider-neutral batching lives in the SDK runtime, and project asset-query preview orchestration lives in the shared project package so Builder and MCP paths can reuse the same behavior. - Cancellation flows from the client batch through the SDK/server/build/query path, with checkpoints between expensive phases. - Runtime asset metadata remains conservatively artifact-wide because URL/size filters, structured references, markdown hydration, materialized rows, and saved plans have different reachability rules. To make 1,000-asset artifacts safe, PostgREST `.in(...)` lookups are deduplicated and split into 100-ID chunks with bounded concurrency. ## Implementation checklist - [x] Trace the production Blog resource path and identify repeated work - [x] Copy the production Blog project into an ignored disposable local project - [x] Record production and local baselines - [x] Add provider-neutral client scheduling, stale-result protection, and cancellation - [x] Batch ordinary Assets queries without coupling or caching HTTP/GraphQL responses - [x] Load only Build fields required to create the saved content compilation plan - [x] Preserve saved-plan, truncation, fallback, diagnostics, and revision semantics - [x] Share Assets artifacts, runtime metadata, and revision-keyed document reads per request - [x] Stitch compatible query shapes into one graph resolution with isolated fallback - [x] Extract reusable SDK batching and project preview orchestration for MCP/runtime consumers - [x] Bound large runtime asset metadata lookups for the 1,000-asset project limit - [x] Add regression coverage and demonstrate new tests failing before their fixes - [x] Run focused and broad tests, type checks, lint, format, package-boundary, and diff checks - [x] Review documentation impact; no authoritative `docs/` content changes are required - [x] Regenerate and verify the affected asset-query runtime fixture; no production data is changed - [x] Address all actionable PR review comments - [x] Rerun the final parity-preserving implementation against the copied Blog project ## Measurements Production trace before the change: - Updates Assets query: 8,703 ms - Immediate identical repeat: 8,110 ms - Query artifact: 9,344 bytes / 7 documents - Saved source database: 42,086 bytes / 28 documents Copied-project local baseline: - Builder navigation until resource loading: 5,111 ms - Exact four-resource batches: 1,448 / 797 / 1,377 / 774 / 3,018 ms During implementation, before the final saved-plan parity tightening: - Exact four-resource batch: 174 ms first run, then 51–80 ms - Actual Builder resource-loader requests: 234–246 ms Final parity-preserving copied-project rerun: - Preflight: 10 ms - Exact four-resource batch: 435 ms cold, then 119 / 97 / 90 / 79 ms warm - Every response returned HTTP 200 with the same 16,884-byte payload The final cold batch is 70% faster than the best copied-project cold baseline, and the fastest warm batch is 90% faster than the best warm baseline. The production trace remains the authoritative production baseline; these before/after comparisons use the same disposable local project and captured request payload. ## Verification Passed: - Full package suites: Builder (212 files / 2,496 tests), asset-uploader (22 / 193), content-engine (40 / 274), project (7 / 84), SDK (29 / 508), and project-build (82 / 2,070) - Final focused regressions: Builder preview/API/resource integration (3 files / 42 tests), SDK resource loader (23 tests), and document-graph resolution (6 tests) - Builder, asset-uploader, content-engine, project, SDK, and project-build type checks - Fixture regeneration is deterministic; the generated vendor exactly matches the source runtime bundle - Fixture typecheck and production build - Changed-file Oxfmt, repository Oxlint, and package-boundary checks - `git diff --check` - `git diff --name-only HEAD --exit-code` New behavioral tests were demonstrated to fail before implementation and pass afterward, including Assets batch coordination, saved-plan wiring, truncation and pinned revisions, shared document reads, cancellation checkpoints, stale/replaced client requests, and large PostgREST ID filters. Agent evaluations were not run because they require separate explicit approval. ## Compatibility and remaining work The public resource result/error shape and on-demand diagnostics contract stay compatible. Generic HTTP/GraphQL caching semantics are unchanged. Compilation already in progress is not force-cancelled because safely interrupting shared compilation requires ownership/reference tracking; cancellation prevents later phases from starting. Closes #6003 |
||
|
|
6385e290c6 |
feat: Add syntax highlighting to Code Text (#6007)
Closes #4119 Supersedes #5999 after GitHub closed it during the head-branch rename. ## Approach Code Text uses one optional public highlighting boundary: - `@webstudio-is/sdk-components-react/code-text` → `src/code-text.tsx` The ordinary `components` entry remains Shiki-free. Canvas loading, metadata options, theme variables, and the plain semantic `<code>` component remain private implementation details. **Code** uses the code editor. **Language** and **Theme** use normal bindable selects. Fixed selections generate direct imports for only the per-page language/theme union; plaintext imports no grammar. Bound selections generate Shiki's lazy language/theme registries. SSR and client rendering share the same factory. Missing bound assets suspend the first render while SSR resolves the selected modules. SSG now waits for Suspense before serializing HTML. During hydration the completed server markup stays in place while the browser fetches only the selected language and theme. Later client-side binding changes load only the new selections and keep the same `<code>` node and focus. Highlighted output preserves the semantic root, ref, author classes/styles, whitespace, escaped source, and legacy children/native `lang` behavior. Shiki's implicit tab stop is disabled. A color-only transformer strips theme typography so asynchronous highlighting cannot change text geometry. ## Bundle impact Measured from same-commit production React Router + Cloudflare builds containing one JavaScript / GitHub Light Code Text instance. Sizes are the sum of emitted JavaScript files; gzip is the sum of each file compressed independently. | Worker output | Files | Raw | Sum gzip | | --- | ---: | ---: | ---: | | Fixed selections | 3 | 1,341,851 B | 288,342 B | | Bound selections | 310 | 10,372,112 B | 1,872,273 B | | Increase | +307 | +9,030,261 B (8.61 MiB) | +1,583,931 B (1.51 MiB) | The bound browser build also emits the catalog as lazy chunks: +9,012,594 raw bytes / +1,578,779 sum-gzip bytes across the complete deployed output. Those chunks are not all delivered on page load. The production SSG regression verifies 100+ catalog chunks are emitted while the HTML directly module-preloads only one shared runtime chunk; the browser fetches the runtime-selected language/theme modules on demand. ## Tradeoffs and compatibility - Instances with both highlighting properties unset keep the legacy plain renderer. - Static highlighting still requires both **Language** and **Theme**. - Binding either selection includes that catalog in deploy output because its runtime value is not known at publish time. - The syntax-highlighting property is `language`; legacy native HTML `lang` remains preserved. - Invalid or unavailable runtime selections render as plain code. - Fixed selections retain the smallest publish output; bound selections trade deploy/worker size for runtime flexibility. ## Todo - [x] Restore the specialized Code Text component and semantic plain fallback - [x] Use a code editor for Code and bindable selects for Language and Theme - [x] Persist JavaScript / GitHub Light template defaults - [x] Collapse highlighting to one public `./code-text` entry - [x] Keep canvas loaders and option/theme helpers private - [x] Generate direct imports for fixed selections and omit plaintext grammar - [x] Generate lazy catalogs when Language or Theme is bound - [x] Preserve highlighted SSR markup during hydration without node/focus movement - [x] Support Suspense-aware highlighted SSG output - [x] Preserve escaping, accessibility, legacy children, and native `lang` - [x] Measure fixed versus bound Cloudflare worker output - [x] Deep-review correctness, DRYness, package surface, and obsolete binding infrastructure - [x] Review documentation impact and update Code Text documentation - [x] Run focused tests, typechecks, production builds, formatting, and staged diff review - [ ] Fixture pipeline — blocked during sync because the current API rejects this local CLI version as incompatible; no fixture output changed ## Verification - [x] New/changed regressions were demonstrated failing without their behavior and passing after restoration - [x] SDK Code Text metadata, rendering, hydration, and bundle tests: 17 passed - [x] Component registry tests: 3 passed - [x] CLI asset collection and full prebuild suite: 59 passed - [x] SDK components, component registry, SDK, and CLI typechecks - [x] SDK components declaration and production builds - [x] Component registry production build - [x] Production Vite + Vike prerender emits highlighted bound SSG HTML with one doctype - [x] Dynamic catalog chunks remain outside the SSG page's direct preload set - [x] Documentation reviewed and updated - [x] Formatting and staged diff checks |
||
|
|
cf0e76f96a |
fix: Validate authored fragments against HTML content models (#5988)
## Summary Prevent authored fragment insertion from creating invalid HTML structures while preserving the Builder's warning-only behavior for legacy clipboard content. Also show an explicit loading state while Assets resource diagnostics are fetched lazily, instead of briefly reporting that no diagnostics are available. Allow established text assets to be edited without their preserved creation timestamp being mistaken for an expired upload reservation. ## Root cause The shared insert-fragment runtime mutation did not validate the final HTML content model. Builder copy and paste uses the same mutation for legacy project content. The compatibility path must tolerate only violations already present in the copied fragment while still rejecting new violations caused by its destination. The diagnostics inspector had no pending state, and an unkeyed pending state could be cleared by an older request after the inspected resource changed. Asset revisions preserve the original file's `createdAt` value, but upload finalization used `createdAt` to enforce the 30-minute upload-ticket lifetime. ## Implementation - [x] Enforce HTML content-model validation at the shared fragment-insertion mutation boundary. - [x] Centralize fragment placement warning collection with the existing matcher logic. - [x] Keep the legacy compatibility switch out of the public runtime/MCP contract. - [x] Pass compatibility through trusted Builder runtime context only. - [x] Ignore only violations already present in legacy clipboard content and reject new destination violations. - [x] Preserve warning-only behavior across the shared Builder paste helper and instance-copy path. - [x] Key diagnostics loading state to the active resource request. - [x] Validate upload-ticket freshness with `updatedAt` while preserving logical asset `createdAt`. - [x] Regenerate the runtime operation contract. - [x] Add regression coverage for strict insertion, legacy compatibility, destination validation, paste context propagation, diagnostics request races, and established text assets. ## Impact MCP and other public authored-fragment callers remain strictly validated and cannot opt out. Legacy Builder clipboard content remains pasteable with warnings, but compatibility cannot introduce a new violation at the selected destination. Assets diagnostics retain their loading state when an older request settles, and established text assets can be updated after 30 minutes without changing their displayed creation date. ## Verification - [x] Project-build targeted tests: 80 passed. - [x] Builder targeted tests: 86 passed. - [x] Asset-uploader tests: 184 passed. - [x] Builder typecheck passed. - [x] Project-build typecheck passed. - [x] Asset-uploader typecheck passed. - [x] Generated runtime operation contracts are current. - [x] New regression tests were demonstrated failing without their fixes and passing after restoration. - [x] Fresh GitHub Main workflow passed, including both test/typecheck matrices and all six Builder E2E shards. Shard 3 was rerun successfully after Docker Hub rate-limited its initial database-image pull. ## External CI status - Lost Pixel failed on an external TLS certificate handshake after Storybook built successfully. - The staging deployment workflow failed before executing job steps, independently of code checks. |
||
|
|
5cefbc0320 |
perf: Speed up Builder resource loading (#5980)
## Summary Remove avoidable latency from Builder resource loading and make Assets resources use the content database efficiently. ## Why Builder currently applies a one-second debounce to every uncached resource request. Assets queries also perform server-side preparation work that should be reused or deferred, making pagination and route changes noticeably slower than expected. ## Implementation plan - [x] Dispatch the complete set of currently computable page resources without the one-second debounce. - [x] Preserve batching, deduplication, exact-request caching, invalidation, and bounded queue draining. - [x] Add regression tests for immediate dispatch, batching, deduplication, invalidation, and queue draining. - [x] Cache compiled Assets content databases by the relevant project/build and asset revision. - [x] Run pagination, filtering, sorting, and metadata selection against the cached database without rebuilding it for each request. - [x] Fetch only referenced document content that is required by the query and keep graph reads parallel. - [x] Avoid generating and returning full diagnostics artifacts for ordinary canvas resource loads; load detailed diagnostics on demand. - [x] Preserve distinct loading and confirmed-empty states for article and author resources. - [x] Add coverage for paginated overview and article navigation behavior. - [x] Deep-review the complete change and remove redundant plan construction, duplicated JSON comparison, path-based diagnostics inference, and synthetic canvas diagnostics. - [x] Make resource and diagnostics invalidation race-safe, including invalidation during in-flight requests. - [x] Run formatting, tests, lint, type checking, package-boundary checks, and affected fixtures. ## Verification - `pnpm oxfmt --check` - `pnpm lint` - `pnpm check:package-boundaries` - `pnpm --filter @webstudio-is/content-engine test` (262 tests) - `pnpm --filter @webstudio-is/asset-uploader test` (184 tests) - `pnpm --filter @webstudio-is/builder test` (2,483 tests) - Typechecks for Content Engine, Project Build, Asset Uploader, and Builder - `pnpm fixtures` (passed; no fixture diff) The new invalidation regression tests were verified to fail against the previous behavior and pass with the fix. Agent evaluations were not run because they require separate approval. ## Known limitations None identified. |
||
|
|
aefe532a73 |
fix: Preserve instance deep links on Builder reload (#5977)
## Summary Preserve exact Builder instance selections during initial load, authentication, and hard reload, including shared content rendered through multiple Slot instances. ## Implementation - [x] Store the complete selector in one comma-separated `instance` URL parameter. - [x] Reuse `canResolveInstanceSelector` to validate every selector path before restoring it. - [x] Resolve the selected page from the selector root. - [x] Wait for the authoritative Builder data-loaded state before applying initial URL state. - [x] Keep selection-to-URL synchronization paused until initial URL state is resolved. - [x] Let the existing auth loader derive `returnTo` directly from Builder requests. - [x] Update generated Builder links, including pre-publish audit links, to use full selectors. - [x] Add regression coverage for incomplete sync data and duplicate shared-slot child IDs. ## Verification - [x] A fresh browser session authenticates and opens a direct selector deep link. - [x] Hard reload retains the exact selector URL and selection. - [x] Clicking the first shared child creates an `instance` URL for the first Slot path; reload restores the first occurrence. - [x] Clicking the second shared child creates a distinct `instance` URL for the second Slot path; reload restores the second occurrence. - [x] Focused Vitest suite: 8 tests passed. - [x] Builder TypeScript typecheck. - [x] Focused oxlint. - [x] Focused oxfmt check. - [x] `git diff --check`. ## Compatibility and limitations No schema, migration, or public API changes. This change uses one canonical full-selector URL format; ID-only deep links are not retained as a compatibility path. |
||
|
|
9fe63c7501 |
fix: Submit webhook forms only once (#5979)
## Summary Webhook Form action resources were being persisted both as prop-bound actions and render-time data sources. Generated sites could therefore execute the POST while rendering and again when the visitor submitted the form. This change keeps prop-bound resources action-only and hardens generation for existing builds that still contain a stale data-source binding. Ref #5806 ## Implementation - Keep `resources.upsertProp` resources out of render-time data and remove stale exposure when an action is edited. - Make the mutation's `dataSourceId` result optional and regenerate the runtime contract. - Exclude action-bound resources from the generated data-resource map, preserving them only in the action map. - Extend the Webhook Form E2E with a local request-counting endpoint. ## Todo - [x] Prevent new Form actions from creating render-time data sources. - [x] Protect existing persisted builds from loading action resources during rendering. - [x] Regenerate the runtime operation contract. - [x] Add mutation, generation, Builder-store, and generated-site regression coverage. - [x] Verify zero calls on page load and one populated POST on submit. ## Verification - [x] Focused Builder E2E: `Webhook Form action submits once and persists after reload` - [x] Project Build test suite: 2,062 tests - [x] SDK test suite: 505 tests - [x] Builder resource mutation test file: 25 tests - [x] SDK, Project Build, and Builder typechecks - [x] Changed-file lint, formatting, and diff checks - [x] Generated API cleanliness check - [ ] Agent evaluation suite (not run; requires explicit approval) ## Notes No fixture inputs or generated fixture bundles are affected. |
||
|
|
ca08919b13 |
fix: Confirm conflicting root styles on page paste (#5971)
## Summary Prevent page, template, folder, and marketplace page imports from silently replacing local declarations on the shared global root. When incoming root styles differ at the same property, breakpoint, and state, Builder now asks whether to preserve the project values or use the incoming values. Cancellation happens before the runtime mutation. ## Technical choices - Compare CSS values by their serialized meaning so equivalent keyword and unparsed values do not create false conflicts. - Plan breakpoint remapping with the same insertion path used by fragment paste, including ID collisions and breakpoint limits. - Keep named-token conflict resolution separate because token conflicts support suffixing and merging, while a shared root-local declaration has only an existing or incoming value. - Default the root-style dialog to **Keep existing** because global root styles affect every page, and show the breakpoint plus current and incoming values for each conflict. - Re-read current project data after the asynchronous token dialog before detecting root conflicts. - Leave no-conflict resolution undefined so the runtime performs a final conflict check instead of treating it as an explicit **Use incoming** choice. - Resolve root conflicts at the page-copy boundary before using the generic fragment/style insertion path. - Reuse one root-conflict UI resolver for clipboard paste and marketplace imports, one page-transfer traversal helper in the UI and runtime, and one presentational shell for token and root-style conflict dialogs. - Require an explicit `rootStyleConflictResolution` for conflicting `pageTransfer.insert` and `pages.copy` runtime operations so non-UI callers cannot overwrite global values accidentally. - Document that `ours` keeps target-project values and `theirs` uses incoming values in both generated runtime contracts. ## Todo - [x] Detect conflicting root-local declarations by property, breakpoint, and state - [x] Add keep-existing, use-incoming, and cancel behavior - [x] Cover page, template, nested-folder, marketplace, and direct runtime transfer paths - [x] Preserve non-conflicting incoming root declarations with either resolution - [x] Avoid false conflicts for equivalent CSS values and colliding breakpoint IDs - [x] Revalidate root styles after asynchronous token resolution - [x] Preserve existing named-token conflict behavior - [x] Remove duplicated transfer traversal, conflict resolution mapping, and dialog layout - [x] Regenerate the runtime operation contract - [x] Verify regression tests fail when their corresponding behavior is disabled - [x] Run targeted tests, typechecks, formatting, and lint ## Verification - `pnpm --filter @webstudio-is/project-build test -- src/runtime/style-copy.test.ts src/runtime/fragment.test.tsx src/runtime/page-copy.test.tsx` — 80 passed - `pnpm --filter @webstudio-is/builder test -- app/shared/copy-paste/plugin-page.test.ts app/shared/root-style-conflict-dialog.test.tsx app/shared/token-conflict-dialog.test.tsx` — 17 passed - Retrospective fail/pass audit covered semantic CSS equality, breakpoint ID collisions, both runtime guards, both resolution directions, folder/template paths, conflict detail rendering, and stale target data after token resolution - `pnpm --filter @webstudio-is/project-build typecheck` — passed - `pnpm --filter @webstudio-is/builder typecheck` — passed - `pnpm lint` — passed - Runtime contract regeneration was repeated and produced identical output - Fixture pipeline not run locally: no fixture inputs or outputs are affected; GitHub fixture checks passed on the previous revision - Agent evaluations not run: repository guidance requires separate explicit approval before running them ## Compatibility and limitations The new runtime inputs are optional for non-conflicting imports. Existing imports continue unchanged; only imports that would overwrite a conflicting shared root-local declaration require an explicit resolution. No migrations or persistence changes are required. Closes #5970 |
||
|
|
56a22fde8a |
feat: Add storage-backed document graph resolution (#5966)
## Summary Introduce a document graph architecture for Assets resources so structured documents can reference one another while keeping full document payloads on CDN-backed storage. The content database will retain the searchable index and dependency graph. At runtime, the server will select a root document, determine its complete dependency set before performing I/O, fetch each unique document concurrently, and assemble the final result server-side. ## Implementation todos ### Document graph foundation - [x] Add normalized internal reference contracts for whole documents, JSON paths, Markdown body, and Markdown frontmatter. - [x] Add an immutable graph abstraction with deterministic ordering, dependency traversal, revision validation, and cycle detection. - [x] Add a storage-independent resolver with dependency closure, bounded parallel loading, shared-content deduplication, representation selection, assembly hooks, cancellation, and structured errors. - [x] Add focused unit coverage for the isolated document-graph abstraction. - [x] Define and validate the external remote-document reference syntax. - [x] Add a pure graph compiler that resolves canonical document URLs into IDs and revisions and validates missing, duplicate, and cyclic dependencies. ### Build-time graph - [x] Add a storage-independent source-catalog compiler for bounded parallel reference discovery and deterministic graph compilation. - [x] Add a versioned deterministic document graph artifact with canonical serialization and integrity verification. - [x] Discover and normalize statically declared references while Assets resources are processed. - [x] Resolve document identities and revisions and report missing, stale, or cyclic dependencies with document context. - [x] Persist document nodes and dependency edges alongside searchable resource metadata. - [x] Include persisted graph data in artifact integrity and size validation. ### Document representations - [x] Add a standalone JSON document adapter for exact reference markers, compiler occurrences, JSON path selection, and immutable assembly. - [x] Add a standalone Markdown document adapter for bounded decoding, frontmatter references, immutable assembly, and source, body, and frontmatter selection. - [x] Parse fetched JSON and Markdown documents through format-specific adapters. - [x] Select complete documents, JSON fragments, Markdown body, and Markdown frontmatter through the shared reference model. - [x] Assemble nested and shared references into the final server-side value. ### Runtime resolution - [x] Enforce reachable-document, per-document byte, and aggregate-byte limits before parsing and assembly. - [x] Add a revision-keyed bounded source-cache wrapper with replayable bytes, stale-entry fallback, and in-flight deduplication. - [x] Add a storage-neutral loaded-source contract that rejects revision mismatches before parsing. - [x] Resolve graph-backed property paths before filtering, sorting, pagination, and projection; skip graph loading for unrelated queries. - [x] Add an injected HTTP/CDN document loader with revision metadata validation. - [x] Apply runtime payload limits, bounded concurrency, caching, and structured failure mapping. - [x] Ensure shared documents are fetched and parsed once per resolution. ### Product integration - [x] Integrate equivalent behavior in local preview, SSR, SSG, and published runtime paths. - [x] Preserve existing embedded Assets resource behavior without requiring migration. - [x] Add observability for graph loading, document fetching, caching, and resolution failures. - [x] Document the public reference and resolution behavior. ### Review hardening - [x] Enforce graph, artifact, cache, and published-storage identity consistency. - [x] Compile only graph sources reachable from selected build entries. - [x] Resolve graph-backed fields before query evaluation and avoid unnecessary graph loading. - [x] Consolidate duplicated graph schemas, utilities, and document content-type detection. - [x] Correct runtime documentation and use structural evaluation validation. ### Verification - [x] Keep a deterministic high-impact agent evaluation for Markdown-to-JSON references across the dynamic blog overview and article routes. - [x] Add a separate discoverability evaluation that must learn the reference workflow from MCP guidance without prompt-supplied syntax, queries, bindings, or call order. - [ ] Re-run the complete local agent evaluation suite after the discoverability fixture change and compare every fixture with its accepted baseline. - [x] Add integration coverage for CDN-backed hydration, parallel fetching, caching, and shared dependencies. - [x] Add integration coverage for missing references, cycles, revision changes, limits, and cancellation. - [x] Verify equivalent behavior across preview, SSR, SSG, and published deployments. - [x] Verify that existing embedded Assets resources retain their current behavior. ## Intended outcome - Storage-backed documents do not require their complete payloads to be embedded in the content database. - The server knows the complete statically discoverable dependency set before fetching document content. - Shared dependencies are fetched once per resolution and independent resources can be fetched in parallel. - JSON fragments and Markdown body/frontmatter representations use the same document-resolution system. - Reference and payload revisions cannot be mixed silently. - Existing Assets resources continue to work without migration. ## Verification results - `@webstudio-is/content-engine`: 37 files / 244 tests passed; typecheck passed. - `@webstudio-is/asset-uploader`: 22 files / 182 tests passed; typecheck passed. - CLI: 57 files / 925 tests passed, including generated SSR and SSG build/prerender coverage; typecheck passed. - Repository-wide `pnpm oxfmt --check` passed across 2,757 files. - Package-boundary validation passed. - Previous `pnpm evaluations` run: all 4 original high-impact fixtures passed. The complete 5-fixture rerun is pending explicit approval after adding the discoverability fixture. |
||
|
|
3ccb46cec7 |
fix: restore Marketplace settings and optimize Assets publishing (#5964)
## Outcome Restore the Marketplace section for projects without existing marketplace metadata, make the complete review-submission workflow available over MCP, clarify shared Assets database diagnostics, and reduce published database size by materializing fully static Assets queries. ## Implementation ### Marketplace and MCP - Initialize the Marketplace form from a safe partial default instead of rendering nothing when metadata is unconfigured. - Keep review submission disabled until the complete metadata schema is valid. - Allow `get-marketplace-product` to return `null` and `update-marketplace-product` to create the first marketplace product through ProjectSession/MCP. - Add a confirmed `submit-marketplace-product` server operation that validates configured metadata before setting review status to `PENDING`. - Document the MCP workflow: upload the thumbnail, update metadata, publish production, then submit. - Correct the Marketplace thumbnail upload example to use the current MCP input field. ### Assets database - Show query size and merged published-database size separately in resource diagnostics. - Clarify that only the merged database counts toward the publish limit. - Allow the Assets query editor to scroll long query source. - Materialize fully static metadata-only Assets queries at publish time. - Store materialized results as compact field columns and rows keyed by a deterministic normalized-query hash. - Serve materialized rows through the existing Assets response contract without candidate documents or a runtime field catalog. - Keep route- or variable-dependent queries on the existing document-backed runtime path. - Exclude overview-only fields from dynamic detail documents so the merged database does not duplicate them. - Preserve byte-limit selection, truncation diagnostics, integrity verification, caching, and backward compatibility with existing artifacts. ### Release - Keep the release lockfile stable when preparing the CLI package. - Regenerate runtime contracts, server-only API metadata, and CLI documentation where required. ## Todo - [x] Restore the empty Marketplace settings state - [x] Prevent empty review submissions - [x] Support first-time Marketplace metadata creation over MCP - [x] Add MCP review submission - [x] Document the end-to-end MCP workflow - [x] Correct query and merged-database diagnostics - [x] Make long Assets query source scrollable - [x] Materialize static overview queries - [x] Preserve dynamic detail-query behavior - [x] Add regression coverage with fail/pass proof - [x] Run relevant package and integration checks ## Verification - [x] Content engine tests: 158 passed - [x] Asset uploader tests: 179 passed - [x] SDK tests: 505 passed - [x] CLI tests: 921 passed - [x] CLI prebuild integration tests: 46 passed - [x] Builder Assets resource and diagnostics tests: 14 passed - [x] Project runtime and ProjectSession tests: 133 passed - [x] Builder API router tests: 30 passed - [x] Protocol operation tests: 18 passed - [x] Content engine build and declaration generation - [x] Content engine, asset uploader, SDK, Builder, and CLI typechecks - [x] Repository lint and formatting checks - [x] Generated API and documentation checks - [x] New materialization tests demonstrated failing with the implementation disabled and passing after restoration ## Notes - The representative five-post overview fixture shrinks from 2,122 bytes to 1,197 bytes while returning the same sorted and paginated result. - Materialization is intentionally limited to static, metadata-only field queries without runtime asset fields. Unsupported or data-incompatible queries safely use the existing runtime database. - The complete `pnpm evaluations` agent suite was not run because repository policy requires separate explicit approval. - No database schema or migration changes are required. |
||
|
|
efbc3098c4 |
feat: add queryable Assets resources (#5946)
Closes #5919 Ref #5920 ## Outcome Extend the existing Assets system resource with optional queries for Markdown, JSON, and standard file metadata. Unconfigured Assets resources retain the existing fetch-all behavior and activate none of the content-query infrastructure. Files are the authoring model. A standalone content engine converts an immutable snapshot of only the files required by configured queries into one bounded, deterministic runtime database. Builder preview, SSR, and SSG use the same query semantics without requiring a database service for a published site. ## Final architecture ```text Markdown / JSON files │ ▼ ContentSource adapter (FS or Webstudio Assets) │ immutable snapshot ▼ ContentCompilationPlan (union of configured query requirements) │ ▼ ContentCompiler (parse, normalize, discover fields, enforce budget) │ ▼ ContentArtifact (bounded, deterministic, immutable JSON) │ ▼ ContentDatabase (filter, OR groups, sort, paginate, project, read content) │ ├── Builder preview ├── SSR runtime └── SSG build only ``` The authoring/storage layer owns mutable files. The content engine consumes immutable snapshots and produces immutable, disposable artifacts. Query authoring is a separate OpenAPI-driven path: ```text Assets REST OpenAPI operation + standard request JSON Schema + dynamic field paths │ ▼ Generic OpenAPI adapter │ resolved schemas, controls, paths, and transport mapping ▼ Generic query-builder core and React renderer │ arbitrary provider-defined value ▼ Assets REST request adapter │ ▼ { query: value } ``` The selected standard OpenAPI operation is the only source of query UI semantics. Request parameters and JSON Schema describe the editable data; the generic renderer derives controls and owns layout. ### Activation and compilation - The compiler does not index every asset. It combines all reachable configured Assets-resource queries into one `ContentCompilationPlan`. - Static constraints such as folder, path, and file type select candidate files. Dynamic values such as a route-bound slug cannot reduce the build-time candidate set. - The plan contains the union of fields used by filtering, sorting, routing, projection, excerpts, and requested full content. - Documents shared by multiple resources are stored once in one deployment-wide runtime database. - Markdown/frontmatter, JSON properties, excerpts, and full bodies are prepared only when required. - No configured content query means no parsing, artifact, or shipped runtime dependency. Fresh generated apps need no content-runtime cleanup; incremental generation removes stale integration if a previously configured query is deleted. ### Runtime database limit - `CONTENT_DATABASE_MAX_BYTES` limits the complete serialized runtime database, not source files, images, or unrelated assets. - The default is `500 * 1024` bytes and is presented as 500 KiB. - Counted data includes indexed metadata, frontmatter/JSON properties, excerpts, the field catalog, serialization overhead, and full Markdown bodies only when requested. - Prepublish validation builds and serializes the exact database that will be attached to the immutable build, so it knows the unbounded size, bounded size, included documents, omitted documents, and affected resources. - Overflow never stores partial documents. Candidates are ordered by `createdAt` descending, canonical path ascending, and stable document ID ascending. Missing dates follow dated documents. A document that does not fit is skipped while smaller later documents are still considered. - Exceeding the limit warns but does not block publishing. - Builder preview can report exact bounded versus unbounded results when dynamic expressions have concrete preview values. Prepublish warnings remain conservative for future route values. ### Snapshot consistency The source contract guarantees a consistent compilation, while adapters provide the mechanism: - Webstudio Assets captures asset metadata and immutable content references. - Filesystem sources capture file identity and verify files did not change before accepting the artifact. - A changed source retries once, then fails clearly instead of producing a mixed-revision database. - This requires no universal snapshot service, source-tree copy, or new versioned-asset infrastructure. ### Package ownership - `@webstudio-is/content-engine`: runtime artifact schema, query execution, field catalog, compact runtime statistics. - `@webstudio-is/content-engine/compiler`: source snapshots, compilation plans, Markdown/JSON parsing, deterministic compilation, byte accounting, and compiler diagnostics. This entry exists to keep Remark/YAML/future MDX dependencies out of runtime bundles. - `@webstudio-is/query-builder`: resolves standard OpenAPI operation schemas into an internal provider-neutral editor model, validation, mutations, and source serialization. It owns no Assets, provider-authored UI contract, or fixed REST-query semantics. - `@webstudio-is/query-builder/react`: container-neutral schema renderer whose layout is renderer policy. It remains separate from the content engine. - SDK: Webstudio Assets/Resource domain models and mapping Webstudio expressions into generic content requirements. - Protocol: external Assets REST/OpenAPI transport contracts. - Asset uploader/repository: asset and folder mutations, generic metadata, immutable byte references, and storage access. Its hosted adapter supplies Webstudio Assets snapshots and parsed-metadata cache persistence to the content engine; parsing, artifact creation, and query semantics remain engine-owned. - Builder and generated-app integrations decide when to compile, cache, embed, load, or discard a database. ### Runtime lifecycle - Builder caches immutable `ContentDatabase` instances by source revision plus compilation-plan hash and coalesces concurrent compilation. Matching operations reuse the same instance; field discovery uses a dedicated minimal all-properties plan that excludes excerpts and bodies. - SSR attaches the validated bounded database to the build and includes the small runtime only when required. - SSG uses the database only while generating pages and omits it and the runtime from final output unless a future runtime-query feature requires them. - Local development compiles from the filesystem and uses identical query semantics. - Published pages query their local artifact; they do not call Builder REST endpoints. - Published artifacts need no external index store, invalidation, garbage collection, SaaS content proxy, new bucket, or new environment variable. ## Implemented foundation These existing parts remain useful and should be moved behind the final ownership boundaries rather than reimplemented. - [x] Upgrade the existing Assets system resource in place and preserve unconfigured fetch-all behavior. - [x] Implement typed filtering, nested boolean groups, deterministic sorting, pagination, projections, dynamic values, and content hydration. - [x] Add Markdown frontmatter, excerpt, JSON-property, and field-catalog extraction. - [x] Add reusable query-control implementations and a separately imported React UI as a foundation for the final OpenAPI-driven contract. - [x] Add Assets query authoring and dynamic field discovery as existing behavior to migrate into the Assets OpenAPI operation. - [x] Add synchronized visual and editable expression authoring. - [x] Unify Assets REST operations under `/rest/assets/*`. - [x] Define authenticated collection, item, content, upload, folder, query, field-catalog, and OpenAPI operations. - [x] Support Builder cookie authentication and public API token authentication. - [x] Add Builder preview, MCP/CLI operations, SSR execution, SSG path generation, and conditional activation. - [x] Add revision-keyed parsed metadata persistence for the hosted Builder. - [x] Add focused query, API, authorization, Builder, CLI, SSR, SSG, migration, and fixture coverage. ## Remaining implementation plan ### 1. Establish the content engine - [x] Rename the generic `@webstudio-is/asset-resource` implementation to `@webstudio-is/content-engine`. - [x] Move existing behavior instead of creating a parallel implementation. - [x] Keep the primary entry runtime-only and add only the compiler entry needed for bundle separation. - [x] Remove the old package after all consumers migrate. - [x] Ensure the engine has no Builder, SDK, asset-uploader, PostgreSQL, R2, or deployment-target dependency. ### 2. Define source snapshots and compilation plans - [x] Introduce `ContentSource.openSnapshot()` and a revision-bearing `ContentSourceSnapshot`. - [x] Include canonical path, stable document ID, content type, immutable content reference, size, and optional `createdAt`. - [x] Implement Webstudio Assets and filesystem snapshot guarantees using their adapter-specific mechanisms. - [x] Introduce `ContentCompilationPlan` for the union of reachable configured resource requirements. - [x] Separate statically selectable candidates from dynamic runtime predicates. - [x] Add mutation-during-compilation tests for replacement, deletion, folder movement, and filesystem changes. ### 3. Separate storage from content processing - [x] Keep upload, replacement, folders, byte storage, revisions, and generic metadata in the Assets repository/uploader. - [x] Move parsing, excerpts, field discovery, query execution, and artifact creation into the content engine. - [x] Remove content-index coordination from asset mutation business logic. - [x] Preserve original source `createdAt` across content replacement and expose it as optional queryable metadata. - [x] Keep hosted parsed-document persistence behind the content compiler's optional cache interface. ### 4. Build deterministic compilation - [x] Move the existing Markdown, frontmatter, JSON, canonicalization, hashing, document-building, and tests into the compiler. - [x] Keep frontmatter behind a dedicated module and continue using the existing Remark/Micromark pipeline. - [x] Parse only candidate files and only the tiers required by the compilation plan. - [x] Deduplicate documents and fields required by multiple resources. - [x] Define a versioned, deterministic `ContentArtifact` with integrity information. - [x] Keep verbose parse and omission diagnostics outside the runtime artifact. - [x] Reject incompatible artifact versions cleanly. ### 5. Enforce the deployment-wide database budget - [x] Replace the old asset-index limit with `CONTENT_DATABASE_MAX_BYTES`, defaulting to 500 KiB. - [x] Have applications read configuration and pass the byte limit into the compiler. - [x] Measure the exact serialized database, including selected bodies and overhead. - [x] Implement deterministic skip-and-continue packing of complete documents. - [x] Return compiler diagnostics with unbounded/bounded bytes, included/omitted counts, omitted identities, largest contributors, and affected resources. - [x] Expose only compact artifact statistics at runtime through `ContentDatabase.getStats()`. - [x] Add exact boundary, deterministic ordering, deduplication, and oversized-document tests. ### 6. Finalize the runtime database - [x] Define `ContentDatabase` over an immutable artifact. - [x] Preserve filtering, nested AND/OR groups, sorting, offset/limit, projection, field references, and content retrieval. - [x] Expose `getFieldCatalog()` and compact `getStats()`. - [x] Keep Markdown/YAML/compiler dependencies out of the runtime entry. - [x] Add execution guards and maximum-size runtime benchmarks. ### 7. Make query authoring OpenAPI-driven The selected standard OpenAPI operation is the complete query-authoring contract. The query builder resolves parameters and request-body JSON Schema, derives its internal editor model, and applies renderer-owned layout policy. It must not embed or fetch a second capabilities/UI language through `x-webstudio-query` or another side channel. - [x] Remove `x-webstudio-query` and all vendor query-definition metadata from the Assets OpenAPI operation. - [x] Resolve local OpenAPI references and derive the editable request value, defaults, validation, labels, choices, arrays, objects, and unions solely from standard operation parameters and request-body JSON Schema. - [x] Represent dynamic Assets field paths with standard JSON Schema vocabulary in the `queryAssets` request schema. - [x] Keep the normalized editor representation internal to query-builder; do not expose another provider-authored capabilities contract. - [x] Remove provider-controlled layout metadata such as `group`; make compact scalar packing and all other layout renderer policy. - [x] Keep Webstudio expression binding as a consumer/editor concern layered over schema values, not part of the OpenAPI contract. - [x] Preserve the existing Assets filter, sort, output selection, full-content/range selection, limit/offset, editable source, and configured-field behavior. - [x] Keep the Assets resource adapter responsible for serializing the edited query into its request body. - [x] Remove the obsolete Assets query-capabilities endpoint, URL, and UI-side capability parsing. - [x] Add a non-Assets OpenAPI proof with differently named parameters and no vendor metadata. - [x] Verify the generic packages contain no Assets imports, fixed Assets property contract, or provider-authored layout model. - [x] Update focused core, React, SDK, protocol/OpenAPI, Builder, and source round-trip tests. ### 8. Manage ContentDatabase lifecycle in Builder - [x] Cache databases by source revision plus normalized compilation-plan hash. - [x] Coalesce concurrent compilation requests. - [x] Reuse cached database instances across field discovery, autocomplete, query preview, and response preview when their normalized compilation plans match. - [x] Replace it when source revision or query requirements change. - [x] Preserve the optional revision-keyed hosted parsed-document cache without making it a source of truth. - [x] Show exact preview warnings when concrete values reveal missing bounded results. ### 9. Materialize and verify immutable builds - [x] Compile and serialize the exact deployment-wide database during prepublish validation. - [x] Attach that validated bounded database to the immutable build without rebuilding it from later asset state. - [x] Show non-blocking prepublish warnings with database bytes, omissions, and potentially affected resources. - [x] Avoid claiming exact future route matches when dynamic values are unknown. - [x] Include the database/runtime only when a reachable configured query requires them. - [x] Ensure detail resources requesting Markdown content include all statically eligible bodies needed by dynamic routes. ### 10. Integrate all targets - [x] SSR: load the build's bounded database and conditionally include the small runtime. - [x] SSG: query the same database during generation, then omit database/runtime output when no runtime query is needed. - [x] Local/CLI: compile from the filesystem source with the same semantics. - [x] Preserve overview pagination, alternative route fields, draft-as-user-data behavior, and full article rendering. - [x] Verify ordinary Assets and fresh query-free sites perform no parser, artifact, content-runtime dependency install, or cleanup work. ### 11. Correct API and package ownership - [x] Move external Assets REST/OpenAPI contracts to `@webstudio-is/protocol`. - [x] Keep Webstudio domain models and Assets system-resource configuration in the SDK. - [x] Keep both cookie and token authentication behavior on the unified Assets REST surface. - [x] Remove pass-through route wrappers/reexports that add no transport behavior. - [x] Keep published runtime queries local to the artifact rather than routing them through Builder REST. ### 12. Remove superseded deployment infrastructure - [x] Remove external deployment-index upload, fetch, invalidation, cleanup, and garbage-collection code. - [x] Remove the SaaS asset-origin adapter requirement; close webstudio-is/webstudio-saas#652 as superseded because it has no independent purpose. - [x] Remove index-bucket compatibility configuration and avoid adding Infisical variables. - [x] Remove obsolete external-index paths, package dependencies, routes, scripts, and tests. - [x] Consolidate unreleased intermediate migrations where safe and retain only schema required by the final hosted cache/API design. ### 13. Verification and release readiness - [x] Move and preserve existing tests while changing ownership; add new tests only for changed contracts and behavior. - [x] Run content-engine compiler/runtime tests, query-builder tests, SDK/protocol/API tests, Builder tests, CLI tests, and affected typechecks. - [x] Run formatting, generated-file idempotence, fixtures, clean Supabase-compatible migrations, E2E shards, and complete agent evaluations. - [x] Verify SSR and SSG conditional inclusion and query-free generated package manifests. - [x] Run the five-article MCP blog workflow: authoring, field discovery, overview, dynamic detail routes, full Markdown rendering, publishing, and published-site verification. - [x] Run a maximum-budget database test and a mutation-during-compilation test. - [x] Perform a final architecture, security, API compatibility, migration, bundle, correctness, and DRY review. - [x] Reconcile every checkbox and replace the verification section with results from the final architecture before release. ## Future MDX work MDX is intentionally separate from this release. - The compiler contract should allow a future build-only MDX parser. - MDX compilation must not enter the published runtime. - Embedded JSX must use an explicit compiled representation rather than runtime source evaluation. - Webstudio components, tokens, styles, and component allowlists remain Webstudio integration concerns. - The future MDX work should reuse `ContentArtifact` and `ContentDatabase` where practical. ## Superseded design The following earlier design choices are no longer the target architecture: - GROQ or GraphQL as the Assets query language. - One external or long-lived index object per resource/query. - A deployment index fetched by the Worker from SaaS/CDN infrastructure. - Lazy production reads of source Markdown bodies. - A separate index bucket, asset origin, invalidation service, or garbage collector. - A 4 MiB published index limit. - Rebuilding an old build's database from the current mutable asset set. - Bundling query infrastructure into projects without a configured content query. ## Verification baseline The embedded runtime-database architecture previously passed the checks below. The OpenAPI-driven query-authoring change additionally passed: - Query-builder core: 4 files / 19 tests. - Query-builder React: 1 file / 7 tests. - SDK Assets source/config: 1 file / 7 tests. - Protocol Assets OpenAPI: 1 file / 9 tests, including independent OpenAPI validation. - Builder Assets OpenAPI description: 1 file / 5 tests. - Query-builder, React, SDK, protocol, and Builder typechecks. - Package-boundary validation and formatting of every affected file. Local verification baseline: - Content engine: 15 files / 104 tests. - Asset uploader: 24 files / 175 tests. - Builder: 204 files / 2,423 tests. - CLI: 54 files / 866 tests. - Complete workspace typecheck, formatting, lint, package boundaries, generated fixtures, and fixture builds. - The complete migration history applied successfully to a fresh Supabase-compatible database, including every migration after the consolidated Assets migration. - Builder production build and full local E2E suite passed. - Complete agent evaluations passed: authenticated page (170,859 ms), design input (207,101 ms), and font assets (67,050 ms). - Final review additionally verified correct REST edit/view/build permits, cookie and public-token read paths, immutable snapshot retries, exact database budgeting, query-free dependency omission, and SSR/SSG conditional runtime inclusion. Final staging verification passed against the embedded runtime-database architecture: MCP uploaded and updated five Markdown articles, discovered dynamic frontmatter fields, previewed a five-item sorted overview and one full-body slug query, updated both Assets resources, found zero binding issues, rendered `/blog` and `/blog/dynamic-routes` at HTTP 200, published build version 266, and verified both published routes at HTTP 200 with the expected article metadata and Markdown body. The workflow also found and fixed offset-form asset timestamps failing canonical datetime validation and made MCP `preview.start({ port: 0 })` correctly select an available local port. ## Release constraints - No new SaaS content service, bucket, or environment variable. - No published-site database dependency. - No parser/compiler dependency in query-free sites or runtime-only bundles. - The validated content database is immutable build output. - Publishing warnings do not block deployment. - The selected standard OpenAPI operation and its request JSON Schema are the only source of query UI semantics. |
||
|
|
54aa7be145 |
fix: Make MCP authoring reliable across assets, styles, and embeds (#5932)
Closes #5924 Closes #5925 Closes #5926 Closes #5927 Closes #5928 Closes #5929 ## Outcome Makes MCP and CLI authoring predictable across project-wide CSS variables, font uploads, retried asset uploads, destructive asset selection, design-token collisions, and SVG HtmlEmbed edits. ## Delivered ### Project-global CSS variables - [x] `define-css-variable` writes to `ROOT_INSTANCE_ID` / `:root` instead of the home-page Body. - [x] Breakpoint and overwrite behavior is preserved, and compact results identify the root scope. - [x] Existing body-local declarations remain untouched. - [x] Runtime and Builder integration tests cover root selection and generated project behavior. ### Font metadata and upload overrides - [x] Static fonts prefer authoritative `OS/2.usWeightClass`. - [x] Fallback matching normalizes spaces, hyphens, and underscores and checks specific aliases before shorter ones. - [x] Explicit format and partial metadata overrides travel through HTTP, filesystem, and S3 upload paths and are validated after merging with detected metadata. - [x] Regression tests cover ExtraBold, Extra-Bold, Semi Bold Italic, Ultra Light, valid weight classes, invalid weight classes, partial overrides, and invalid overrides. ### Idempotent asset uploads - [x] API uploads compute a project-scoped SHA-256 content hash. - [x] Sequential and concurrent duplicate uploads reuse the existing asset and return `deduplicated: true`. - [x] `force: true` bypasses deduplication for intentional duplicates. - [x] A partial unique index enforces one non-null content hash per uploader project. - [x] Stale reservations are cleaned up and active concurrent uploads are bounded by polling and an actionable retry error. - [x] HTTP, protocol, uploader, and CLI tests cover hash reuse, uniqueness races, force propagation, and result markers. The migration does not backfill historical files. Existing assets begin participating in deduplication when uploaded through the new path; matching content keeps the existing asset's metadata rather than silently rewriting it. ### Consistent asset inputs and permissions output - [x] `delete-asset` exposes exact `assetIds` and explicit `assetIdPrefixes`. - [x] The legacy `assetIdsOrPrefixes` input remains accepted and is marked deprecated. - [x] Exact ids no longer accidentally select longer prefixed ids. - [x] `webstudio permissions` prints a readable role/capability summary by default while `--json` remains unchanged for machine callers. - [x] Help and discovery describe JSON output as optional. - [x] Runtime, CLI, metadata, and generated-contract tests cover canonical and compatibility behavior. ### Deterministic fragment design tokens - [x] Fragment insertion reconciles incoming tokens against project tokens and earlier tokens in the same fragment. - [x] Equal name/style tokens are reused instead of creating suffixed twins. - [x] Same-name/different-style fragments require an explicit `ours`, `theirs`, or `merge` policy at the public runtime boundary. - [x] Builder's established internal paste behavior retains its explicit incoming-value default. - [x] `create-design-token` accepts documented CSS strings through the property-aware CSS parser as well as typed style values. - [x] Tests cover same-fragment reuse/conflicts, persisted tokens, explicit policies, and CSS string parsing. ### Editable SVG HtmlEmbed content - [x] HtmlEmbed validation uses parse5 parser errors and source structure instead of serialization-string equality. - [x] Valid inline SVG is accepted without silently normalizing authored markup. - [x] Fragment insertion now uses the same validator as prop update and text replacement paths. - [x] Malformed markup is rejected before mutation. - [x] Regression tests cover realistic SVG, parser failures, and no-partial-mutation behavior. ## Verification - [x] `pnpm checks`: all workspace tests, typechecks, lint, package boundaries, generated API/docs checks, and fixtures passed on the final committed tree. - [x] `pnpm --filter webstudio test:release-smoke`: packaged install, compact comparison, preview, agent workflow, stdio MCP, and structured commands passed. - [x] Prisma schema validation passed with test connection variables. - [x] Two minimally prompted weak-model subagents independently exercised the changed CLI/MCP discovery and focused workflows. - [x] The subagent-found stale `permissions --help` requirement was fixed, regression-tested, and retested. - [x] Final diff and worktree are clean. |
||
|
|
4ea8928880 |
fix: preserve legacy content through validation (#5933)
## Summary - Keep the shared content-model validator strict during normal Builder authoring. - Report HTML and component nesting violations as publish warnings instead of blocking legacy projects. - Allow paste operations to retain violations already contained inside legacy fragments and surface them as warnings. - Continue rejecting new violations introduced at the paste destination boundary. - Remove obsolete empty content models from CodeText and Video so their runtime-supported, HTML-valid children pass validation. ## Root cause The new pre-publish audit applied current authoring constraints to historical project trees. Some legacy structures, including old Accordion triggers containing an h3 inside a button, were created by earlier Webstudio templates and cannot satisfy the current HTML model without migration. Blocking publish or paste would make untouched legacy content unusable. CodeText and Video also had metadata that rejected children their renderers intentionally support. Those verified metadata mismatches are corrected independently of the compatibility policy. ## Behavior - New invalid nesting remains prevented by normal authoring operations. - Existing invalid nesting produces a Publish warning and does not abort publishing. - Webstudio instance, HTML, JSX, and Webflow paste retain fragment-internal violations and show a paste warning. - Paste still validates the destination and cannot introduce a new invalid parent-child relationship. - Resource integrity and missing audit inputs still prevent publishing. ## Verification - [x] Legacy button containing h3 reports a publish warning - [x] Legacy invalid subtree pastes successfully with a warning - [x] New paste destination violations remain rejected or use the closest valid target - [x] Valid legacy CodeText children pass validation - [x] Valid Video source and track children pass validation - [x] Project-build matcher and insert-target tests - [x] Builder instance, HTML, JSX, and Webflow paste tests - [x] Project-build and Builder typechecks - [x] Focused oxlint and formatting checks |