Skip to content

Console code editor: serve Monaco from the Console's own assets instead of a public CDN #25899

Closed

Description

@objectstack-fleet

Filing-gate category: ① user-visible defect with a named landing site (follow-up owed by landed work: objectstack#25569).

Reader who acts: the objectui card thread that takes this (app-shell + plugin-editor owner). The maintainer approved adding monaco-editor as a runtime dependency on 2026-10-25 (director thread, option A: self-host instead of a CDN allowance), so the human-floor call named in the objectstack-ai/objectui#11800 triage is already made; the PR still gets the maintainer's review as usual.

Dedupe: repo:objectstack-ai/objectui monaco in:title,body → 1 hit (#11800, closed; follow-ups objectstack-ai/objectui#11857 / objectstack-ai/objectui#11858 named there). jsdelivr → same hit. No open card covers self-hosting.

Problem

The Console's code editor (Monaco, the editor component of VS Code) fetches its core at run time from https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs through @monaco-editor/loader's default config. Any deployment that cannot or will not reach that CDN loses the editor:

  • air-gapped and offline installs today;
  • every install once the Console document's content-security-policy lands (objectstack#25569), which deliberately lists no CDN in script-src.

Where it is used:

Surface File Without the CDN
Studio JSON source tab packages/app-shell/src/views/metadata-admin/JsonSourceEditor.tsx plain-text fallback (useMonacoFallback)
html / react page source editor .../metadata-admin/previews/SourcePageEditor.tsx plain-text fallback
metadata form code fields .../metadata-admin/widgets.tsx (CodeWidget) plain-text fallback
plugin-editor:code-editor page component packages/plugin-editor/src/MonacoImpl.tsx no fallback: stays on "Loading..."

reach: measured once through the public UI — browser smoke on objectstack#25569's branch (showcase, @objectstack/console@18.0.0 dist), the CDN script request is refused and reported by the policy; the metadata-admin surfaces show the textarea.

Proposed change

  1. Bundle monaco-editor and point the loader at it: loader.config({ monaco }), with the language workers imported through Vite's ?worker so they ship as same-origin assets under /_console/assets/.
  2. Do it once, in a shared spot both app-shell and plugin-editor use, so the two copies of @monaco-editor/react don't drift.
  3. Keep useMonacoFallback for a failed chunk load, and give MonacoImpl the same fallback.
  4. Load the editor lazily (it already is behind LazyCodeEditor), so the cost is paid only on screens that open an editor.

Cost to state in the PR: monaco-editor adds a few MB of lazy chunks to the Console dist (measure before/after du -sh dist/assets); it does not change first-paint size if the lazy boundary holds.

Acceptance

  • With objectstack#25569's policy on, the four surfaces above open the full editor, and the browser reports no script-src / worker-src violation.
  • No request goes to cdn.jsdelivr.net from the Console.
  • MonacoImpl shows the plain-text fallback when the editor chunk fails to load (test).

Business rationale: authors edit page source, JSON metadata and code fields daily in Studio; a textarea loses syntax highlighting, validation markers and folding on exactly those screens. Offline customers lose it today; every customer loses it once the CSP ships.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Transferred 2026-10-26T03:05Z from objectstack-ai/objectui#13510, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the ui/ workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.

    Paths. Read every repository path in the body with a ui/ prefix: packages/app-shell/src/views/metadata-admin/JsonSourceEditor.tsx is now ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.tsx; packages/plugin-editor/src/MonacoImpl.tsx is now ui/packages/plugin-editor/src/MonacoImpl.tsx.

    References. The bare issue numbers in the body were written in objectui and now resolve against this repository by mistake; read them as objectstack-ai/objectui#11800, objectstack-ai/objectui#11857, objectstack-ai/objectui#11858.

    No stale pm:dispatched or assignee to strip.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: re-grade — bug · area:studio added · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T06:13Z

    ⛔ Not a claim, ⛔ not a dispatch. Re-grade after the transfer from objectui (#25854). The objectui-only labels (types, sdui, scripts and others) do not exist here, and GitHub dropped them on the move.

    • bug: a user-visible defect (the editor depends on a public CDN at run time). area:studio: the code editor surface. Grade otherwise unchanged (p2 · domain:ui). The dependency call was already made by the maintainer (2026-10-25, option A, self-host). Paths are now under ui/.

    Generated by Claude Code

  3. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    bugSomething isn't working
    and removed on Oct 26, 2026
  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_017iXjUm1rujwDk7f3KT1Pkk (seat); implementing card thread session_0144tqLAVSHy6hGKUSH4LhVL
    Account: os-steve
    Branch: claude/project-thread-d433a3 (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record. Wording amended by the seat: an issue-shaped placeholder here was read by the Closing-Target Claim Guard as a second claimed branch)
    Worktree: the card thread's own container
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/views/metadata-admin/ (JsonSourceEditor.tsx, previews/SourcePageEditor.tsx, useMonacoFallback.ts, widgets.tsx CodeWidget), ui/packages/plugin-editor/src/ (MonacoImpl.tsx, a shared loader module), ui/packages/app-shell/package.json, ui/packages/plugin-editor/package.json, pnpm-lock.yaml (via pnpm install only); worker output config in ui/apps/console/vite.config.ts if needed (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root .changeset/.
    Container & model: L, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: xhigh
    Clause-②: no (a new runtime dependency, approved by the maintainer 2026-10-25 option A per the card body; no accept set or export widened)
    Metadata-protocol: none
    Responsibility: n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public Console
    Thread-read: 6409510137
    Premise: holds (read-only premise check at origin/main 59d9181, 2026-10-26T06:40Z; spot-checked by the seat)
    Serial constraints cleared: none: no open PR touches these files or pnpm-lock.yaml (open-PR file scan 2026-10-26T06:33Z); #25903 edits metadata-admin/inspectors/ only, disjoint from this surface; no in-flight domain:ui claim

    Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25899,
      "status": "done",
      "branch": "claude/project-thread-d433a3",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25993",
      "head": "32c20dedd8e0e282e85f88b7c1722c7e58590f02",
      "session": "session_0144tqLAVSHy6hGKUSH4LhVL",
      "premise_still_valid": true,
      "summary": "Premise held: nothing in ui/ called loader.config, so @monaco-editor/loader 1.7.0 injected monaco-editor@0.55.1 from cdn.jsdelivr.net. plugin-editor now depends on monaco-editor and exports configureBundledMonaco(loader), which imports the ESM core as a lazy chunk, hands it to loader.config({ monaco }) and serves each language worker through Vite ?worker from same-origin assets; MonacoImpl mounts the editor only after that and falls back to a plain textarea when the chunk fails. app-shell useMonacoFallback awaits it before loader.init(). Console assets 54M to 73M (+13.5 MB lazy JS), first-paint closure +103 B raw, entry chunk unchanged.",
      "tests": "32c20ded: cd ui && pnpm exec vitest run packages/app-shell/ packages/plugin-editor/ under os-verify-lock: VERDICT command-exit 0, Test Files 1724 passed | 1 skipped (1725), Tests 15880 passed | 9 skipped. Monaco subset 21 files 907/907. plugin-editor build + type-check, app-shell type-check exit 0; eslint changed files 0 errors. Ablations via scripts/ablation-replace.mjs (blob moved, restored, git diff HEAD empty): drop loader.config in bundledMonaco.ts -> bundledMonaco.noCdn red (expected false to be true); disable the unavailable branch in MonacoImpl.tsx -> MonacoImpl.chunkFailure 2/2 red (Unable to find a label with the text of: javascript source). Browser smoke under the strict Console CSP (OS_PAGE_REACT_ENABLED=false): CodeWidget, JsonSourceEditor, SourcePageEditor all mounted Monaco; 0 securitypolicyviolation, 0 cdn.jsdelivr.net requests, 0 failed requests of 827; workers from /_console/assets/.",
      "gates": "dispatch-gates --commands: 53 commands, each run with its exit captured before any pipe. 51 exit 0 on first run; pnpm check:dts-closure exit 1 (missing ui/apps/console/plugin.d.ts: my measurement build ran vite build without build:plugin) and pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: 7 backend packages had no dist). After pnpm build:plugin and a turbo build of those 7, both re-ran exit 0. --ran: 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. PR check-runs: not yet reported at report time.",
      "deviations": [
        "Bounded in-place fix outside the claimed file surface: one-line configureBundledMonaco stub in 3 suites under ui/packages/app-shell/src/views/studio-design/ (InterfacesPillar.sourcePage-12522, StudioDesignSurface.interfacesCreatePage-11823, StudioDesignSurface.interfacesReadOnly-11136); same defect class, same gate families. Seat to amend the claim file surface.",
        "ui/apps/console/vite.config.ts not edited (Vite default ?worker output sufficed)."
      ],
      "files_changed": [
        ".changeset/25899-monaco-self-host.md",
        "pnpm-lock.yaml",
        "ui/packages/app-shell/src/views/metadata-admin/CodeWidget.expressionEnvelope-10963.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/CodeWidget.loaderFallback-11858.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.fallback.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.monaco-export.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.nameSurvivesLabel-12680.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.pageRequiresStamp-11357.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.nonRegistryLabelling.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.widgetLabelling.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/SourcePageEditor.lineNumbers-12522.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/SourcePageEditor.pageKind-11933.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/SourcePageEditor.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/dataPreviews.i18n-10862-s3.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/useMonacoFallback.fastfail.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/useMonacoFallback.loaderRejection-11800.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/useMonacoFallback.ts",
        "ui/packages/app-shell/src/views/metadata-admin/widgets.tsx",
        "ui/packages/app-shell/src/views/studio-design/InterfacesPillar.sourcePage-12522.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.interfacesCreatePage-11823.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.interfacesReadOnly-11136.test.tsx",
        "ui/packages/plugin-editor/package.json",
        "ui/packages/plugin-editor/src/MonacoImpl.chunkFailure.test.tsx",
        "ui/packages/plugin-editor/src/MonacoImpl.tsx",
        "ui/packages/plugin-editor/src/bundledMonaco.noCdn.test.tsx",
        "ui/packages/plugin-editor/src/bundledMonaco.ts",
        "ui/packages/plugin-editor/src/index.tsx"
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "4 — mcp__github__issue_read x4 (get x1, get_comments x3); no write tools",
      "api_writes": "2 — fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create (POST /pulls draft + POST /issues/25993/assignees os-steve); this os-dev-report comment via post-stamped.mjs (POST /issues/25899/comments)",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed · packages/cli/src/utils/console.ts CSP doc comment near consoleContentSecurityPolicy still says Monaco loads its core from jsdelivr and shows its plain-text fallback; stale after this PR",
        "carrier: none · noted, not filed · ui/apps/site playground (apps/site/app/playground/page.tsx) still loads Monaco from the CDN; out of scope per dispatch"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment (seat domain:ui#1, 2026-10-26T08:21Z): the file surface of the claim above adds three test files under ui/packages/app-shell/src/views/studio-design/: InterfacesPillar.sourcePage-12522, StudioDesignSurface.interfacesCreatePage-11823 and StudioDesignSurface.interfacesReadOnly-11136. Each gets the same one-line configureBundledMonaco stub, a bounded in-place fix named in PR #25993's body. Branch, Clause-② and everything else are unchanged.

  7. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat: PR #25993 ACCEPT (2026-10-26T08:40Z), checks green on head 32c20ded; ready + auto-merge requested. The out-of-scope notes (CLI comment, site playground) go to Acceptance notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions