Skip to content

fix(editor): install a structuredClone fallback where lingo is imported (Sentry MONOREPO-EDITOR-FB) - #577

Merged
Aymericr merged 1 commit into
mainfrom
fix/structured-clone-lingo
Aug 4, 2026
Merged

fix(editor): install a structuredClone fallback where lingo is imported (Sentry MONOREPO-EDITOR-FB)#577
Aymericr merged 1 commit into
mainfrom
fix/structured-clone-lingo

Conversation

@Aymericr

@Aymericr Aymericr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Supersedes #524, which correctly diagnosed the root cause but installed the polyfill in the wrong place.

Root cause (verified)

@pascal-app/lingo@0.2.0 builds defaultRegistry = createRegistry(allKinds) at module scope. Registry's constructor calls registerKind for each kind, and registerKind does const copy = structuredClone(def). So on a browser without the global, the throw happens while lingo's module body is still evaluating — no component-level guard can run in time.

packages/editor/src/lib/measurement-parser.ts:1 imports lingo, which is what makes it load-reachable from the editor bundle. Confirmed by test: importing @pascal-app/lingo with globalThis.structuredClone = undefined throws; with the shim installed it resolves and parses correctly.

Sentry MONOREPO-EDITOR-FB (ReferenceError: structuredClone is not defined, Honor Browser 9.8 / Chromium <98).

Why here and not in an app entry

#524 put the polyfill in apps/editor/instrumentation-client.ts. apps/editor has no Sentry SDK and no instrumentation-client.ts on main — the app that reports MONOREPO-EDITOR-FB is the hosted app, which has its own instrumentation entry and would never load that file. So the crash would have stayed unfixed for the reporting app, for every npm consumer of @pascal-app/editor, and the Sentry issue would have looked addressed.

packages/editor is the layer all three load. Importing the shim for its side effect immediately above the lingo import puts it exactly where the ordering constraint is, and keeps it visible to anyone who later moves that import.

Scope

Deliberately a JSON round-trip rather than @ungap/structured-clone: lingo's kind definitions are plain JSON (strings, numbers, arrays of those), so no dependency is warranted. The file documents that it is not spec-compliant — no Map/Set/Date/ArrayBuffer/cycles, and it throws TypeError rather than DataCloneError — so nothing else starts leaning on it. packages/core/src/utils/clone-scene-graph.ts already avoids structuredClone for live runtime nodes for related reasons.

Not claimed: that this makes Chromium <98 able to run the editor. Next 16 compiles for a chrome 111 baseline, globals.css uses oklch() 62 times, and the viewer needs WebGL2/WebGPU. This removes one hard ReferenceError at module load; it is not a browser-support expansion.

Verification

  • bun run check clean (1589 files)
  • bun run check-types 9/9 tasks
  • bun run test 12/12 tasks green, including 3 new tests covering native-passthrough, the JSON clone, and lingo importing successfully without the global

Note

Low Risk
Small, guarded polyfill with tests; only affects environments without native structuredClone and is scoped to JSON-serializable data lingo needs.

Overview
Fixes MONOREPO-EDITOR-FB by ensuring globalThis.structuredClone exists before @pascal-app/lingo evaluates. Lingo deep-copies its kind registry at import time; without the global, the editor bundle throws during module load.

Adds structured-clone-fallback.ts, a JSON round-trip shim only when the native API is missing, and imports it for side effect immediately above the lingo import in measurement-parser.ts so every @pascal-app/editor consumer gets the fix—not an app-only entry. The shim is documented as non–spec-compliant and intentionally narrow.

Three tests cover native passthrough, JSON cloning behavior, and successful lingo import/parse when structuredClone is undefined.

Reviewed by Cursor Bugbot for commit 35fc449. Bugbot is set up for automated code reviews on this repo. Configure here.

`@pascal-app/lingo` builds its unit registry at module-eval time and
`registerKind` deep-copies each kind definition with `structuredClone`, so
a browser without that global (Chromium <98, reported from Honor Browser
9.8 as `ReferenceError: structuredClone is not defined`) fails while the
module graph is still evaluating. Sentry MONOREPO-EDITOR-FB.

The shim belongs next to the import that makes lingo load-reachable, not
in an app entry: `packages/editor` is what the OSS app, the hosted app and
every npm consumer all load. An app-level polyfill covers only the app
that declares it.

Scoped narrowly and documented as such — lingo's kind table is plain JSON,
so a JSON round-trip suffices and needs no new dependency. It is not
spec-compliant and must not be relied on for real structured-clone
semantics.
@Aymericr
Aymericr merged commit de07c8a into main Aug 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant