mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-28 23:57:17 +09:00
docs
27
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3c7dac7eb0 |
fix: resolve MCP preview and content graph reports (#6511)
## Summary - Raise the document-reference graph budget independently of the 20-file content hydration limit, and document the 2,000-document graph cap. This lets the reported 29-post referenced query complete. - Make iterative preview startup verify the generated project identity endpoint instead of compiling the application route; exact route validation remains with screenshot/capture. - Keep production preview readiness tied to the application route even when no CSS assets are present. - Include a stable structured diagnostic when the browser DevTools connection closes, while preserving the existing reconnect behavior. - Document and test a safe leaves-first `convert-instance` migration for required-part composite trees. Closes #6508, #6515 Ref #6509, #6510 ## Verification - Content Engine suite: 656 tests passed. - Vision suite: 85 tests passed. - CLI preview/readiness/controller tests: 32 passed; project-build instance conversion tests: 89 passed. - CLI typecheck, targeted lint, formatting, and diff checks passed. - The full CLI suite timed out in unrelated launcher/fixture tests under parallel load. The preview-script and launcher tests passed with fewer workers, and the MDX fixture test passed alone in 29 seconds. No full local agent evaluation command was run. - All required GitHub checks passed on latest commit `7f7b981` (29 successful; visual regression skipped). ## Remaining scope - #6509 remains open: this PR improves diagnostics but does not establish a stable Edge DevTools capture. - #6510 remains open: this PR removes the initial route-compilation dependency, but does not yet provide route-specific preparing/retry status. ## Checklist - [x] Review the change and preserve production preview route checks. - [x] Add regression coverage for the review fix and required-part conversion recipe. - [x] Update MCP manual docs with the supported conversion order. - [x] Run focused tests, typecheck, lint, and formatting. - [ ] Run the full local agent evaluation suite (not run). - [x] Confirm required CI checks pass on the latest commit. |
||
|
|
ac75dcdd73 |
fix: retain MDX identity in runtime asset queries (#6495)
## Summary - Preserve `revision` and `contentRef` for MDX documents in runtime asset queries, including when `content.mode` is `none`. - Add a regression test for dynamic MDX queries that omit hydrated content. - Verified against the reported shared project: synced preview builds successfully and the affected blog route returns HTTP 200. Fixes #6490 ## Checks - `pnpm --filter @webstudio-is/content-engine test` (654 tests) - `pnpm --filter @webstudio-is/content-engine typecheck` - `pnpm exec oxfmt --check packages/content-engine/src/compilation-plan.ts packages/content-engine/src/asset-index.test.ts` - `git diff --check` |
||
|
|
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. |
||
|
|
9845517029 |
fix: hydrate published Markdown assets (#6422)
Migrated from #6421 to the short .staging branch name. |
||
|
|
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. |
||
|
|
2a389b878c |
fix: load embedded MDX documents in previews (#6415)
## What changed Published MDX Content Blocks could build with document bindings but fail at runtime when the content index did not include the MDX source text. Dynamic article routes then returned an asset-query 500 or crashed during SSR. This change: - hydrates missing local MDX source text during CLI prebuild; - resolves document-graph references from embedded content before making an HTTP request; - adds a regression test for embedded document loading. ## Verification - published runtime tests passed (17 tests) - prebuild tests passed (69 tests) - content-engine and CLI typechecks passed - exact imported marketplace project reproduced locally and verified: SSG generated 10 pages, `/posts/designing-with-intent` returned HTTP 200 with article content, and no `ReferenceError` or asset-query 500 occurred |
||
|
|
e7181fffdc |
feat: Add GitHub-style Alert component (#6279)
## Summary - Add one official `Alert` component with a content-editable `variant` property for note, tip, important, warning, and caution. - Register Alert in the component catalog with a generated title descendant and Storybook story. - Render GitHub-style alerts in Markdown Embed using the same DOM, class, role, and variant contract. - Materialize connected Content Block MDX alerts as actual Alert instances and serialize content or variant edits back to `> [!TYPE]`. - Document the component and both Markdown workflows. ## Implementation Alert labels and variants live in Content Engine and are shared across the component, Markdown renderer, and MDX adapters. 1. The Alert React component owns the wrapper, generated title, stable classes, `role="note"`, and `data-variant`. 2. Markdown Embed transforms candidate micromark HTML before the existing sanitizer and emits the same markup contract. It remains sanitized HTML rather than a Builder instance tree. 3. Content Engine retains alert metadata on parsed Markdown blockquotes. Project Build maps those nodes to Alert instances, reconciles their authored children, and restores canonical GitHub syntax during MDX serialization. 4. The component registry exports one Alert insertion template with a styleable title descendant. Unsupported, lowercase, misplaced, and malformed markers remain ordinary blockquotes. There are no schema, migration, persisted-data, or sanitizer-policy changes. ## Checklist - [x] Add Alert as an official component with one Variant property. - [x] Support all five GitHub alert types. - [x] Share alert metadata and marker recognition. - [x] Preserve nested Markdown and ordinary blockquote behavior. - [x] Keep URL and HTML sanitization in the existing security boundary. - [x] Add shared, title, and type-specific Markdown Embed descendants. - [x] Materialize Content Block alerts as Alert instances. - [x] Preserve alert content and variant changes through MDX serialization. - [x] Regenerate component props and Storybook artifacts from source. - [x] Review documentation impact and update Alert, Markdown Embed, Content Block, and navigation docs. - [x] Verify fixture impact; no fixture inputs or generated fixture outputs are changed. ## Verification - [x] New tests demonstrated failure before implementation and pass afterward. - [x] `pnpm --filter @webstudio-is/content-engine test` — 417 passed. - [x] `pnpm --filter @webstudio-is/project-build test` — 2,282 passed. - [x] `pnpm --filter @webstudio-is/sdk-components-react test` — 144 passed, 1 skipped. - [x] SDK component registry tests. - [x] Relevant package typechecks, declaration builds, and production builds. - [x] Root lint, formatting, package-boundary, generated-doc, and diff checks. - [x] GitHub Actions main, E2E, lint, submodule, deployment, and approved visual-regression checks. ## Known limitations Alert identifiers must be uppercase and appear at the start of the blockquote's first paragraph, matching the documented syntax. Markdown Embed emits the component's markup contract but cannot create Builder instances inside sanitized HTML. Closes #6278 |
||
|
|
f23007457d |
fix(content-engine): normalize asset path filters (#6389)
## Description Fixes Assets path filters so authors can use visible folder and file names while canonical stored paths remain URL-encoded. - normalizes path filter operands segment by segment without double encoding - preserves already encoded filters and literal string matching behavior - covers equality, inequality, membership, contains, starts-with, and ends-with operators with spaces and URL-significant characters - verifies local/Preview database execution and published runtime return the same result - documents visible path input in the generated Content Engine reference Closes #6388 ## Steps for reproduction 1. Create an Assets folder named `Tutorial posts`. 2. Add an MDX file inside it. 3. Create an Assets system resource with `path starts with "Tutorial posts/"`. 4. Preview the query and expect the MDX file to match. 5. Change the value to `Tutorial%20posts/` and expect the same result for backward compatibility. ## Regression proof Before the implementation, the focused reproduction failed because the matcher returned no items: expected `["alpha"]`, received `[]`. With the fix, the expanded path-operator regression suite passes. ## Verification - [x] Content Engine test suite: 47 files, 577 tests passed - [x] Content Engine typecheck - [x] CLI typecheck - [x] Repository lint with warnings denied - [x] Formatting check for every changed source/test file - [x] Content Engine documentation test - [x] Content Engine and MCP documentation generation is idempotent - [x] Webstudio Features fixture generation is idempotent and typechecks - [x] No agent evaluations were run ## Documentation impact Updated the authoritative CLI Content Engine reference source and regenerated the Builder Content Engine and MCP documentation. The new guidance tells users to enter visible path names and notes that existing encoded filters remain supported. ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [x] made a self-review - [x] added inline comments where behavior is not obvious ## Before merging - [ ] tested on preview environment (preview dev login: 0000) - [ ] updated test cases document (not needed; automated regression and authoritative user docs were updated) - [x] added tests - [x] no new environment variables |
||
|
|
5d34b77fac |
feat: Improve collection entry authoring (#6361)
Collection authors and editors get clearer field help, direct entry navigation, and actionable validation instead of warnings that point at the wrong place. Closes #6360 ## Implementation - [x] Keep resettable help open after a click while the label remains hovered - [x] Stop repeating the property label inside resettable help - [x] Store JSON Schema field descriptions and show them in entry forms - [x] Add an optional dynamic collection entry page for explicit canvas navigation - [x] Rename entry file actions to **Edit file** - [x] Add **Open on canvas** to entry menus and Entry settings - [x] Put the entry filename in the Entry settings title - [x] Make collection validation banners explain the problem and open the first affected entry - [x] Remove false output-only `UNOBSERVED_FIELD` warnings while keeping filter and sort diagnostics - [x] Encourage useful field descriptions in general MCP collection guidance - [x] Update Content Engine documentation - [x] Confirm no database migration is required - [x] Regenerate the affected asset-query runtime fixture - [ ] Visually review the affected Storybook states in light and dark themes - [ ] Run the complete local agent evaluation suite - [x] Run broader package and Builder checks - [x] Complete final diff review ## Technical notes - `entryPageId` lives in `collection.json` under `x-webstudio`; no database schema changes are needed. - Entry creation still leaves the current canvas unchanged. Canvas navigation only happens after the user selects **Open on canvas**. - An entry page must contain exactly one URL parameter. The entry filename supplies that parameter, so the page can identify the selected entry. - Output projection fields are schemaless and may be produced after reference resolution, so catalog absence is no longer reported there. Filter and sort fields still report catalog diagnostics. - The structured-query runtime change affects the generated `webstudio-features` fixture, which is regenerated in this PR. ## Verification - 561 Content Engine tests - 2,413 Project Build tests - 253 Design System tests - 1,794 Builder tests - 67 focused Builder regression tests after the final navigation change - 11 ResettableLabel browser tests - 128 focused MCP tests - Builder, Content Engine, Design System, and Project Build typechecks - Repository lint and package-boundary checks - Generated documentation checks - Full workspace fixture build with `pnpm -r fixtures:build` - Formatting and diff checks - New regression tests were demonstrated failing before their fixes and passing afterward ## Documentation impact - [x] Updated the Content Engine guide for field descriptions, dynamic entry pages, actionable validation, **Edit file**, and **Open on canvas** - [x] Updated MCP guidance so collection fields include editor-facing descriptions |
||
|
|
e69a3d2f8e |
feat: Create structured MDX entries from collection folders (#6317)
## Paragraph editing follow-up — |
||
|
|
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. |
||
|
|
1b48716ff4 |
docs: Document MDX-backed Content Blocks (#6273)
## Summary - Document how people create, connect, edit, validate, and publish MDX-backed Content Blocks. - Document the complete MCP workflow, including scoped expression sources, frontmatter updates, diagnostics, conflicts, and disconnect behavior. - Add two focused screenshots for the human workflow. - Fix issues found by a low-reasoning MCP agent while executing every documented workflow. Ref #6182 ## Implementation notes - Connection confirmation uses the public `confirmReplacement` field because `confirm` is reserved as a hidden transport field. - MCP edits frontmatter with `update-content-block-frontmatter`; materialized MDX elements are not persistent targets for generic instance mutation tools. - Reload now intentionally discards a conflicted local source and accepts the Asset's latest storage revision before an edit is reapplied. - Generated MCP documentation and runtime contracts are updated from their source files. ## Checklist - [x] Document the human MDX Content Block workflow. - [x] Document MCP discovery, connection, editing, diagnostics, frontmatter, dynamic sources, conflicts, and disconnection. - [x] Add only screenshots that clarify otherwise hard-to-describe UI steps. - [x] Test every documented MDX capability through the local MCP CLI with a low-reasoning agent. - [x] Reproduce and fix the replacement-confirmation schema mismatch. - [x] Reproduce and fix persistent-session conflict reload and reapply. - [x] Add regression tests that failed before each product fix and pass afterward. - [x] Regenerate runtime contracts and generated MCP documentation. - [x] Review authoritative `docs/` content and update affected pages. - [x] Run affected tests, typechecks, formatting, and diff checks. ## Verification - Project Build: 208 affected tests passed. - Content Engine Asset session: 8 tests passed. - CLI documentation tests: 14 tests passed. - MCP docs generator: 3 tests passed. - Content Engine, Project Build, CLI, and MCP docs typechecks passed. - `pnpm oxfmt --check` passed. - `git diff --check` passed. - Low-reasoning MCP evaluation passed all documented workflows, including a real two-client revision conflict, reload, reapply, and final saved inspection. |
||
|
|
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. |
||
|
|
2df130171f |
feat: Resolve asset references as structured metadata (#6184)
## Summary - add exact `$ref` asset values that resolve to Asset Manager metadata - keep plain local asset paths returning URL strings for backward compatibility - derive reference metadata from the canonical asset object so newly added asset fields flow through automatically - let Assets query output fields select only the metadata returned, including nested arrays - carry the same resolved shape through preview, generated preview, published runtime, and static export - document the syntax and accessible image binding in Content Engine and MCP docs ## Design The source document declares only the relationship: ```yaml featureImage: $ref: ./assets/hero.png ``` The Assets query controls projection with fields such as `properties.featureImage.src` and `properties.featureImage.description`. Selecting `properties.featureImage` returns the canonical Asset Manager asset metadata together with the resolved `src`. The Content Engine runtime contract accepts the canonical asset object instead of maintaining a second metadata field list; `url` becomes `src`, and internal `contentRef` is not exposed. Plain strings retain their existing URL-string result. Existing document `$ref` behavior is preserved by resolving structured asset metadata only when the target is a known non-document asset. ## Verification - [x] Content Engine full test suite: 298 tests - [x] SDK full test suite: 517 tests - [x] Asset repository tests: 39 tests - [x] CLI prebuild tests: 58 tests - [x] Content Engine, SDK, Asset Uploader, CLI, and Project Build typechecks - [x] Repository lint - [x] Content Engine and MCP documentation generation and tests - [x] Generated API check - [x] Local fixture build and generated fixture update - [x] New behavior tests demonstrated fail before/pass after the implementation - [ ] Complete fixture sync (development API currently rejects this branch's newer CLI protocol; local fixture build passes) - [ ] Agent evaluations (not run; require explicit approval) ## Checklist - [x] Local structured references return field-selected metadata - [x] Newly added canonical asset fields flow through without a Content Engine whitelist change - [x] Plain-path queries remain backward compatible - [x] Nested object and array projection - [x] External URL strings remain usable - [x] Missing or null descriptions allow `??` fallback to the title - [x] Expanded metadata counts toward query result-size limits - [x] Documentation impact reviewed and Content Engine and MCP docs updated - [ ] Missing asset targets return a non-failing unresolved value and dedicated diagnostic - [ ] Apply the resource and Image binding changes to the separate Webstudio blog project ## Known limitation A `$ref` whose asset target no longer exists is not yet converted into an unresolved asset value with a dedicated diagnostic. This PR intentionally does not claim that acceptance criterion yet. Ref #6179 |
||
|
|
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 | ||
|
|
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. |
||
|
|
43a9f824ad |
fix: Resolve published asset paths in structured content (#6096)
## Summary Resolve Markdown frontmatter and JSON values written as published paths such as `./assets/hero.png` or `/assets/hero.png`, even when the content document and asset live in sibling Asset folders. This staging PR replaces #6094 and supersedes #6016. #6016's raw-format changes were unrelated to the reported asset-path problem. ## Technical approach - Keep format-neutral path indexing and resolution in `asset-path-resolution.ts`. - Keep `markdown-assets.ts` limited to Markdown destination parsing. - Normalize both Markdown frontmatter and JSON objects through the shared structured-value reference pipeline. - Keep canonical source-relative resolution as the first choice. - Recognize only the published `assets/<file-name>` path shape as a fallback. - Match the final canonical asset file name only when it uniquely identifies one project asset. - Leave external URLs, missing assets, ambiguous names, and unrelated root-relative paths unchanged. - Resolve recorded asset references to runtime URLs when queries render their results. ## Compatibility This extends resolution for imported/published structured content without changing ordinary source-relative paths or direct asset ID references. Duplicate final file names intentionally remain unresolved to avoid choosing the wrong asset. ## Checklist - [x] Add regression coverage for relative and root-relative published asset paths. - [x] Cover discovery and runtime structured-value resolution. - [x] Add explicit JSON document coverage with the asset in a sibling folder. - [x] Verify ambiguous asset names remain unresolved. - [x] Separate format-neutral resolution from Markdown parsing. - [x] Run the full content-engine test suite. - [x] Run content-engine typecheck and targeted lint/format checks. - [x] Rebuild content runtime and repository fixtures. - [x] Review documentation impact and update the Assets guide. - [ ] Manually verify the published site from `md-assets.staging`. - [ ] Confirm CI checks pass. ## Verification - Regression tests were run with the published-path fallback disabled and failed for both generic structured values and the explicit JSON integration case. - `pnpm --filter @webstudio-is/content-engine test` — 294 tests passed. - `pnpm --filter @webstudio-is/content-engine typecheck` — passed. - `pnpm exec oxlint --deny-warnings ...` — passed. - `pnpm exec oxfmt --check ...` — passed. - `pnpm --filter webstudio build:content-runtime` — passed. - `pnpm -r fixtures:build` — passed; no generated fixture diff. - `git diff --check` — passed. - Agent evaluations were not run because they require separate explicit approval. Closes #6093 |
||
|
|
2ec78c183d |
feat: Add single-result modes to Assets queries (#6040)
## Summary - Add `many`, `one`, `first`, and `last` result modes to Assets queries while keeping `many` as the backward-compatible default. - Return direct items for single-result modes, validate deterministic ordering, and report duplicate `one` matches as a structured `MULTIPLE_RESULTS` error. - Update Builder controls, preview/published runtime handling, SDK resource encoding, MCP/CLI guidance, generated contracts, and fixtures. ## Implementation TODOs - [x] Add result-mode schemas and core execution behavior. - [x] Select single results before content hydration and preserve deterministic ID tie-breaking. - [x] Add structured duplicate-match transport errors. - [x] Update Builder controls and hide/reset pagination for single-result modes. - [x] Keep existing settings first, then Output and Result, without a duplicate Return label. - [x] Preserve legacy saved resources with an implicit `many` default. - [x] Update preview, published runtime, SDK, protocol, MCP, and CLI behavior. - [x] Regenerate runtime contracts, server metadata, CLI docs, and fixtures. - [x] Review documentation impact and update the authoritative MCP/CLI guidance. - [x] Add regression coverage and verify the new tests fail without the implementation. - [ ] Run the agent evaluation suite (not run; requires explicit user approval). ## Compatibility and behavior - Existing queries without `result` continue to behave as `many` and retain the ID-keyed collection shape. - `one` returns an item or `null`, but fails when more than one asset matches. - `first` and `last` require explicit sorting; the existing asset-ID tie-breaker makes equal sort values deterministic. - Single-result modes ignore and do not persist pagination controls. Materialized collection queries remain collection-only; single-result queries execute dynamically. ## Verification - Content engine typecheck and full test suite (291 tests). - Query builder, query-builder React, SDK, protocol, asset uploader, project build, Builder, and CLI targeted typechecks/tests. - Root lint. - MCP documentation tests, typecheck, sync check, and generated-doc checks. - Generated API contract checks. - Fixture generation pipeline. - Final focused content-engine regression run (53 tests). - Final DRY review: affected typechecks, focused tests, lint, package boundaries, generated checks, and fixtures. Closes #6039 |
||
|
|
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 |
||
|
|
74d88a3fb2 |
fix: Resolve structured Asset paths in queries (#5995)
## Summary Resolve portable relative Asset paths stored in Markdown frontmatter and structured JSON properties to canonical runtime Asset URLs inside the content engine. The authored source remains unchanged. Asset resource consumers now receive canonical URLs without Image, CSS, JSON-LD, or other binding-specific transformations. ## Root cause Markdown body links already recorded relative Asset references for runtime rewriting, but structured values bypassed that pipeline. Browsers therefore resolved values such as `./assets/hero.png` against the page URL instead of the source document folder. ## Implementation - Recursively discover unique structured Asset references using the existing canonical path resolver and the platform URL parser. - Store optional `assetValueReferences` in content artifacts and runtime projections. - Include referenced Assets in publication/runtime dependency collection and validation. - Resolve values before filtering, sorting, and projection in normal and document-graph queries. - Rewrite materialized query items at runtime without mutating canonical documents or compiled rows. - Merge authored query parameters and fragments with canonical Asset URLs. - Keep queries whose filter/sort semantics depend on runtime Asset URLs out of compile-time materialization. - Add an explicit content-compilation cache generation so pre-feature artifacts are recompiled. - Refresh fixture bundle versions and the generated asset-query runtime. - Document relative references in Markdown frontmatter and JSON content. ## Checklist - [x] Add structured reference discovery for nested objects and arrays - [x] Support Markdown frontmatter and JSON document properties - [x] Preserve URL query strings and fragments - [x] Thread references through build/runtime artifacts and publication dependencies - [x] Resolve normal, materialized, and document-graph query results - [x] Apply resolved values to filtering, sorting, and projection - [x] Preserve compatibility with artifacts that omit the optional field - [x] Add fail/pass regression coverage - [x] Run package tests, typechecks, lint, and generated API checks - [x] Regenerate and build all fixtures - [x] Review documentation impact and update the Assets guide ## Verification - `pnpm --filter @webstudio-is/content-engine test` — 269 passed - `pnpm --filter @webstudio-is/content-engine typecheck` — passed - `pnpm --filter @webstudio-is/asset-uploader test` — 184 passed - `pnpm --filter @webstudio-is/asset-uploader typecheck` — passed - `pnpm --filter @webstudio-is/protocol test` — 88 passed - `pnpm lint` — passed - `pnpm check:generated-api` — passed; generated files clean - `pnpm --filter webstudio build:content-runtime && pnpm -r fixtures:build` — passed for all fixtures - Assets documentation — internal links resolve and `git diff --check` passes - Regression proof: neutralizing structured discovery/replacement caused 8 expected failures across source compilation, standard queries, document-graph queries, and materialized queries; restoring the implementation passed all tests. - `pnpm evaluations` — not run because explicit approval is required. ## Compatibility The artifact field is optional, so older artifacts continue to parse and execute. Existing absolute, protocol-relative, root-relative, fragment-only, query-only, external-scheme, malformed, missing, and ambiguous values remain unchanged. |
||
|
|
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. |
||
|
|
f335340a54 |
fix: Correct content database sizing and Markdown references (#5972)
## Summary - Report only serialized content-database bytes that are actually included in the published bundle. - Replace the ambiguous `markdown-body` query mode with the strict `markdown-body-ref` mode. - Require every selected Markdown body to have a document-graph node; compilation now fails instead of embedding source bytes as a fallback. - Apply the same Markdown eligibility rule during compilation and runtime query execution, excluding non-Markdown files from reference-only databases and results. - Highlight database sizes in red only when documents were omitted because of the size limit. - Refresh Assets diagnostics after an Assets resource edit is confirmed by the server. - Update CLI/MCP guidance, examples, schemas, generated contracts, and fixtures to use the reference-only mode. ## Root cause Dynamic Markdown-body queries conservatively treated unrelated files, including remote images, as possible hydration candidates. The old mode could also fall back to embedding a Markdown body when graph resolution was unavailable, despite its intended storage-backed behavior. Compilation and runtime selection did not previously share the same content-mode eligibility check. Diagnostics displayed the unbounded pre-limit estimate instead of the serialized bundled size, did not carry the omission reason into query-preview diagnostics, and an early cached preview could leave the merged measurement stale until reload. ## Impact Content database diagnostics and publish warnings now reflect bundled database bytes rather than remote source-file sizes. Saved query filters trigger a post-sync diagnostics refresh. Markdown detail queries must use `content.mode: "markdown-body-ref"`; the old `markdown-body` value is rejected, missing graph support is an explicit compilation error, and non-Markdown assets cannot be selected or counted by a reference-only query. Ref #5966 ## Verification - Content engine: 39 files / 261 tests passed. - Asset uploader: 22 files / 183 tests passed. - SDK: 29 files / 505 tests passed. - Protocol: 9 files / 86 tests passed. - Project build: 82 files / 2,055 tests passed. - CLI: full suite previously passed (57 files / 933 tests); final prebuild verification passed 48 tests. - Focused Builder diagnostics tests: 4 files / 10 tests passed. - Typechecks passed for Builder, content engine, asset uploader, SDK, protocol, project build, and CLI. - Generated API contracts and docs are current. - Fixture build passed and is reproducible after the final review changes. - Formatting, lint, package-boundary checks, and diff checks passed. - New strict-reference, runtime-selection, compilation-selection, and size-highlight regression tests were demonstrated failing without their implementations and passing with them. - Agent evaluations were not run because this follow-up did not receive separate evaluation approval. |
||
|
|
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. |