Skip to content

UVE: editor silently accepts legacy params-only CLIENT_READY, leaving migrated headless clients half-connected (no content events, no warning) #36713

Description

@oidacra

Problem

The UVE editor silently accepts the legacy, beta-era params-only CLIENT_READY shapeinitUVE({ params: { depth: 3 } }) — and leaves the headless client in a half-connected state:

  • The client is marked ready (setIsClientReady(true)), params are honored on the editor's page fetch (depth appears on the iframe URL)
  • Non-gated events (uve-request-bounds / uve-flush-bounds / uve-scroll-inside-iframe / selection) flow normally
  • But uve-set-page-data / uve-reload-page are never emitted on contentlet add/remove/save, so CONTENT_CHANGES / PAGE_RELOAD subscriptions never fire and the canvas never re-renders until a manual browser refresh

There is no warning, no error, and no documentation stating that registering a GraphQL query via CLIENT_READY is required for content-event delivery. The runtime accepts a shape the SDK types already reject (DotCMSPageResponse has no params field), and the degradation is completely silent.

Why this matters: every integration built on the 0.0.1-beta.x line that migrates to 26.7.x and keeps its hand-wired bridge (initUVE({params}) + manual createUVESubscription) will hit exactly this half-working state. Diagnosing it requires instrumenting the postMessage channel and reading editor source — this cost a real integration team days of instrumentation and a multi-day support investigation.

Code path (verified on main)

  • Gate: edit-ema-editor.component.ts$handleReloadContentEffectif (pageType === PageType.HEADLESS && !hasClientQuery) return; (deliberate: "Never push a REST-sourced pageAsset… skip until a GraphQL-backed update is available")
  • requestMetadata is only set from CLIENT_READY when devConfig.graphql.query is present: dot-uve-actions-handler.service.tsCLIENT_READY handler (setCustomClient only if (query))
  • params-only configs still get pageReload(pageParams) + setIsClientReady(true) → the half-connected state, with no feedback
  • uve-reload-page only fires on narrow paths (e.g. SAVE_PAGE with clientAction === EDIT_CONTENTLET), so add/remove emits nothing to these clients
  • SDK type contract (DotCMSPageResponse, sdk/types/.../public.ts) doesn't include params — the runtime accepts a shape the types reject

Repro

  1. Headless site in UVE; the app initializes with initUVE({ params: { depth: 3 } }) and subscribes to UVEEventType.CONTENT_CHANGES.
  2. In the editor, add or remove a contentlet.
  3. Observe (raw message listener): bounds/scroll/selection events arrive; uve-set-page-data / uve-reload-page never do. Canvas doesn't update until manual refresh. No console warning anywhere.

Verified against @dotcms/uve / @dotcms/types 26.7.21-1 with a 26.07.06-3 backend, with a custom rendering layer (no @dotcms/react/@dotcms/angular).

Expected

  1. Warn loudly when a headless client sends CLIENT_READY without graphql.query — editor console (and ideally an editor UI hint): "client registered without a page query; content-change events will not be delivered. Pass the full client.page.get() response to initUVE()."
  2. Document and formally deprecate the params-only shape: state in the @dotcms/uve docs/migration guide that initUVE() expects the full DotCMSPageResponse (whose graphql field registers the client for CONTENT_CHANGES/PAGE_RELOAD delivery), and that the beta-era { params } shape is legacy/unsupported.

(Optional, for triage discussion: a lightweight invalidation signal for query-less clients was considered, but since the supported path — passing the full client.page.get() response — registers the query automatically, warning + documentation likely covers the real-world cases.)

Workaround / correct usage

Fetch via @dotcms/client's client.page.get() and pass its entire response to initUVE(pageResponse) — the response carries the graphql metadata, so registration happens automatically. Framework packages (useEditableDotCMSPage in React/Vue, DotCMSEditablePageService in Angular) do all of this internally, as shown in the official examples (examples/nextjs, examples/angular).

Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type : DefectUPLItem sourced from the Unified Priority ListdotCMS : UVEUniversal Visual Editor

    Type

    No type

    Projects

    Status
    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions