@volter/editor-core 0.5.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { ProductIdentity } from '@volter/editor-sdk/session/product-locator';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* WHERE THE EDITOR'S FRAME ENTRY POINT IS — and it is the PRODUCT's, not the
|
|
7
|
+
* kit's.
|
|
8
|
+
*
|
|
9
|
+
* The module the Code-OSS contribution imports (`/__editor/served-modules`,
|
|
10
|
+
* routes/served-modules.ts) is `@vgai/game-editor`'s or `@vgai/model-editor`'s
|
|
11
|
+
* ONE source entry, which composes the product and re-exports `mountVgai`
|
|
12
|
+
* (`packages/editor/src/frame/product.ts`). The kit's own mount is
|
|
13
|
+
* `frame/bridge.tsx`'s `mountEditor`, which names no product and is not served
|
|
14
|
+
* to anybody.
|
|
15
|
+
*
|
|
16
|
+
* There is no constant here to spell it, and that is the point: which file it
|
|
17
|
+
* is depends on WHICH PRODUCT the project resolved, so the one source is the
|
|
18
|
+
* product's own `package.json#vgai.product.entry`
|
|
19
|
+
* (`@volter/editor-sdk/session/product-locator`). Three readers agree through it:
|
|
20
|
+
* the dev host builds a Vite URL from it, the packaged host finds it in the
|
|
21
|
+
* product's production build manifest by it, and
|
|
22
|
+
* `packages/editor/vite-product-build.ts` makes it that build's rollup input.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** The entry as a path relative to `fromDir`, POSIX — which is what a Vite
|
|
26
|
+
* manifest key is, and what a dev-server URL is built from. */
|
|
27
|
+
export { productEntryPath } from '@volter/editor-sdk/session/product-locator';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The URL the packaged host serves the built product entry's CSS-loading
|
|
31
|
+
* wrapper at.
|
|
32
|
+
*
|
|
33
|
+
* A production build emits the entry's stylesheet as its own asset and nothing
|
|
34
|
+
* injects the `<link>`: under the frame there is no HTML of ours for Vite's
|
|
35
|
+
* HTML plugin to write into — the page is the workbench's. So the packaged host
|
|
36
|
+
* serves a three-line ES module that adds the entry's own stylesheets and
|
|
37
|
+
* re-exports the built chunk, and the door hands back THIS url. The
|
|
38
|
+
* contribution still imports one module and still reads `mountVgai` off it.
|
|
39
|
+
*/
|
|
40
|
+
export const FRAME_BRIDGE_PACKAGED_PATH = '/__editor/frame-bridge.js';
|
|
41
|
+
|
|
42
|
+
/** What a production build emitted for the product entry, read off its manifest. */
|
|
43
|
+
export interface BuiltProductEntry {
|
|
44
|
+
/** The entry chunk's url under the served `dist/`, e.g. `/assets/product-a1b2.js`. */
|
|
45
|
+
readonly js: string;
|
|
46
|
+
/** Its own stylesheets, in the manifest's order. */
|
|
47
|
+
readonly css: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The product entry inside a product's `dist/`, or a throw naming the build.
|
|
52
|
+
*
|
|
53
|
+
* `build.manifest` is on for exactly this read (repo-root `vite.config.ts`) —
|
|
54
|
+
* the entry's file name is content-hashed, so the only honest way to find it is
|
|
55
|
+
* the manifest the build writes. A `dist/` cut before this product existed
|
|
56
|
+
* answers the door with this sentence rather than a 404 two layers down.
|
|
57
|
+
*
|
|
58
|
+
* THE KEY IS MATCHED BY SUFFIX, not spelled. A Vite manifest key is the entry
|
|
59
|
+
* module's path relative to the build's ROOT, and the root is the repo the
|
|
60
|
+
* build ran in — a fact this process, reading an INSTALLED product out of
|
|
61
|
+
* `node_modules`, has no way to reconstruct. What it does know is the
|
|
62
|
+
* product's own declaration (`./src/index.ts`), and that is the tail of the
|
|
63
|
+
* key whatever the build's root was. `isEntry` is what keeps the match honest:
|
|
64
|
+
* only a rollup entry may answer this door.
|
|
65
|
+
*/
|
|
66
|
+
export function readBuiltProductEntry(
|
|
67
|
+
distPath: string,
|
|
68
|
+
product: ProductIdentity,
|
|
69
|
+
): BuiltProductEntry {
|
|
70
|
+
const manifestPath = join(distPath, '.vite', 'manifest.json');
|
|
71
|
+
const rebuild = `npm run build -w ${product.name}`;
|
|
72
|
+
let manifest: Record<string, { file?: string; css?: string[]; isEntry?: boolean }>;
|
|
73
|
+
try {
|
|
74
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as typeof manifest;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`${manifestPath} is not readable (${error instanceof Error ? error.message : String(error)}), so this ` +
|
|
78
|
+
`session cannot find ${product.name}'s entry in its own production build. Rebuild it:\n ${rebuild}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const tail = product.entry.replace(/^\.\//, '');
|
|
82
|
+
const found = Object.entries(manifest).find(
|
|
83
|
+
([key, value]) => value.isEntry === true && (key === tail || key.endsWith(`/${tail}`)),
|
|
84
|
+
);
|
|
85
|
+
const entry = found?.[1];
|
|
86
|
+
if (!entry || typeof entry.file !== 'string') {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`${manifestPath} names no entry ending in ${tail}, so this ${product.name} build predates ` +
|
|
89
|
+
`its frame entry and cannot be framed. Rebuild it:\n ${rebuild}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return { js: `/${entry.file}`, css: (entry.css ?? []).map((file) => `/${file}`) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The wrapper module the packaged host serves at
|
|
97
|
+
* {@link FRAME_BRIDGE_PACKAGED_PATH}: the built entry's stylesheets, then the
|
|
98
|
+
* built entry itself, re-exported whole so `mountVgai` is read off this module
|
|
99
|
+
* exactly as it is read off the dev one.
|
|
100
|
+
*/
|
|
101
|
+
export function builtFrameBridgeModule(built: BuiltProductEntry): string {
|
|
102
|
+
const sheets = JSON.stringify(built.css);
|
|
103
|
+
return (
|
|
104
|
+
'// Generated per request by packages/editor/server/frame-bridge.ts — the\n' +
|
|
105
|
+
"// product's production build entry, with the stylesheets no HTML of ours injects.\n" +
|
|
106
|
+
`for (const href of ${sheets}) {\n` +
|
|
107
|
+
' if (document.head.querySelector(`link[href="${href}"]`)) continue;\n' +
|
|
108
|
+
" const link = document.createElement('link');\n" +
|
|
109
|
+
" link.rel = 'stylesheet';\n" +
|
|
110
|
+
' link.href = href;\n' +
|
|
111
|
+
' document.head.appendChild(link);\n' +
|
|
112
|
+
'}\n' +
|
|
113
|
+
`export * from ${JSON.stringify(built.js)};\n`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ONE ORIGIN in front of two servers: the Code-OSS workbench and this
|
|
3
|
+
* session. It listens IN THE SESSION PROCESS — no spawn, no second program —
|
|
4
|
+
* exactly as the loopback Colyseus server does.
|
|
5
|
+
*
|
|
6
|
+
* /$product-segment/*, /static, /vscode-remote, … the workbench (the REH)
|
|
7
|
+
* everything else this session
|
|
8
|
+
*
|
|
9
|
+
* WHY A PROXY AND NOT TWO ORIGINS (measured 2026-09-19 against the REH). The
|
|
10
|
+
* REH stamps its own CSP on the workbench page
|
|
11
|
+
* (`createWorkbenchContentSecurityPolicy`, src/vs/server/node/webClientServer.ts):
|
|
12
|
+
*
|
|
13
|
+
* script-src 'self' 'unsafe-eval' blob: 'nonce-…' <sha> http://<the REH's own authority>
|
|
14
|
+
* connect-src 'self' ws: wss: https:
|
|
15
|
+
*
|
|
16
|
+
* The ONLY http origin on either list is the server's own. The editor's own
|
|
17
|
+
* entry module and every project module are imported as module scripts from
|
|
18
|
+
* this session, and every `/__editor/*` call is a fetch to it — so a session on
|
|
19
|
+
* a second port is refused by `script-src` before CORS is ever consulted, and
|
|
20
|
+
* its fetches by `connect-src` (which carries no `http:` at all). Relaxing that
|
|
21
|
+
* means editing a core service; the proxy costs none.
|
|
22
|
+
*
|
|
23
|
+
* CROSS-ORIGIN ISOLATION. Every response carries COOP `same-origin` and COEP
|
|
24
|
+
* `credentialless`, because the Blender build is `-sPROXY_TO_PTHREAD` and needs
|
|
25
|
+
* `SharedArrayBuffer`. The REH has no cross-origin-isolation flag or header
|
|
26
|
+
* hook of its own — `--enable-coi` is an ELECTRON-MAIN argument — so these
|
|
27
|
+
* headers are this proxy's. `credentialless` rather than `require-corp` so the
|
|
28
|
+
* workbench's own cross-origin loads keep working, the same choice `dev.ts`
|
|
29
|
+
* makes.
|
|
30
|
+
*
|
|
31
|
+
* THE WEBVIEW'S SECOND ORIGIN. VS Code loads every webview from
|
|
32
|
+
* `webviewExternalEndpoint`, which defaults to `https://{{uuid}}.vscode-cdn.net/…`
|
|
33
|
+
* — a host that does not exist for this fork and that a cross-origin-isolated
|
|
34
|
+
* page could not embed anyway. `IWorkbenchConstructionOptions.webviewEndpoint`
|
|
35
|
+
* is the supported door and the REH leaves it unset, so this rewrites the page
|
|
36
|
+
* config to `http://{{uuid}}.localhost:<port>`. It has to be a SUBDOMAIN and
|
|
37
|
+
* not this origin itself: `pre/index.html` refuses to start unless
|
|
38
|
+
* `location.hostname` IS the hash of `{parentOrigin, salt}` or begins with it,
|
|
39
|
+
* and `*.localhost` is the one wildcard host a browser resolves to loopback
|
|
40
|
+
* with no DNS. Two consequences handled here: the webview is genuinely
|
|
41
|
+
* cross-origin, so its responses need CORP `cross-origin` (without it the frame
|
|
42
|
+
* dies as `corp-not-same-origin-after-defaulted-to-same-origin-by-coep`, which
|
|
43
|
+
* Chrome renders as "refused to connect" and which cost an hour); and the REH's
|
|
44
|
+
* own `frame-src 'self' https://*.vscode-cdn.net` is widened on the way through.
|
|
45
|
+
*
|
|
46
|
+
* COMPRESSION IS THIS PROXY'S JOB, and it is the reason the frame's page weight
|
|
47
|
+
* is not the workbench's disk size. U3 measured `transferSize ===
|
|
48
|
+
* decodedBodySize` on every entry of a 41.75 MB page: the REH writes no
|
|
49
|
+
* `content-encoding` at all, and neither did this proxy. It negotiates
|
|
50
|
+
* `accept-encoding` now — brotli, then gzip — over text, JavaScript, CSS, JSON
|
|
51
|
+
* and wasm only; everything else is byte-for-byte.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
import {
|
|
55
|
+
createServer,
|
|
56
|
+
request as httpRequest,
|
|
57
|
+
type IncomingMessage,
|
|
58
|
+
type Server,
|
|
59
|
+
type ServerResponse,
|
|
60
|
+
} from 'node:http';
|
|
61
|
+
import { connect as netConnect, type Socket } from 'node:net';
|
|
62
|
+
import { basename } from 'node:path';
|
|
63
|
+
import type { Transform } from 'node:stream';
|
|
64
|
+
import { pipeline } from 'node:stream/promises';
|
|
65
|
+
import { createBrotliCompress, createGzip, constants as zlibConstants } from 'node:zlib';
|
|
66
|
+
|
|
67
|
+
export interface FrameProxyOptions {
|
|
68
|
+
/** The one port a person opens: this proxy's. */
|
|
69
|
+
readonly port: number;
|
|
70
|
+
/** The Code-OSS workbench (REH) behind it. */
|
|
71
|
+
readonly upstreamPort: number;
|
|
72
|
+
/** This session's own port. */
|
|
73
|
+
readonly sessionPort: number;
|
|
74
|
+
/** The project the workbench opens; its folder name is the `?project=` id. */
|
|
75
|
+
readonly projectRoot: string;
|
|
76
|
+
/**
|
|
77
|
+
* Which way round the PRODUCT paints, from its own declaration
|
|
78
|
+
* (`package.json#vgai.product.colorTheme`, `@volter/editor-sdk/session/
|
|
79
|
+
* product-locator`), or `null` when this session could not resolve a product
|
|
80
|
+
* — in which case the page keeps the workbench's own web default and the
|
|
81
|
+
* `served-modules` door is what says why there is no product.
|
|
82
|
+
*/
|
|
83
|
+
readonly colorTheme: 'dark' | 'light' | null;
|
|
84
|
+
readonly log: (line: string) => void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface FrameProxy {
|
|
88
|
+
/** The url to open: this origin, on this project. */
|
|
89
|
+
readonly url: string;
|
|
90
|
+
close(): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The workbench's own paths. `<product segment>` (`/oss-dev` out of a source
|
|
95
|
+
* checkout, the quality segment out of a build — `getServerProductSegment`) is
|
|
96
|
+
* DISCOVERED at boot rather than spelled here, because the page states it.
|
|
97
|
+
*/
|
|
98
|
+
const VSCODE_PATH_BASE =
|
|
99
|
+
'^\\/$|^\\/(static|favicon|manifest\\.json|code-192|code-512|callback|oauth|out|extensions|builtin|remote|vscode-remote|_static|web-extension|version|delay-shutdown)';
|
|
100
|
+
|
|
101
|
+
/** What is worth compressing. Everything else goes through byte for byte. */
|
|
102
|
+
const COMPRESSIBLE =
|
|
103
|
+
/^(?:text\/|application\/(?:javascript|json|wasm|manifest\+json)|image\/svg\+xml)/;
|
|
104
|
+
|
|
105
|
+
/** `"`-encoded HTML attribute value ↔ JSON, the way webClientServer writes it. */
|
|
106
|
+
const decodeAttr = (value: string): string =>
|
|
107
|
+
value
|
|
108
|
+
.replaceAll('"', '"')
|
|
109
|
+
.replaceAll(''', "'")
|
|
110
|
+
.replaceAll('<', '<')
|
|
111
|
+
.replaceAll('>', '>')
|
|
112
|
+
.replaceAll('&', '&');
|
|
113
|
+
const encodeAttr = (value: string): string =>
|
|
114
|
+
value
|
|
115
|
+
.replaceAll('&', '&')
|
|
116
|
+
.replaceAll('"', '"')
|
|
117
|
+
.replaceAll("'", ''')
|
|
118
|
+
.replaceAll('<', '<')
|
|
119
|
+
.replaceAll('>', '>');
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Which encoding the browser asked for, in this proxy's order of preference.
|
|
123
|
+
* `null` means "send it as it is" — no `accept-encoding`, or none we write.
|
|
124
|
+
*/
|
|
125
|
+
function negotiateEncoding(header: string | string[] | undefined): 'br' | 'gzip' | null {
|
|
126
|
+
const accepted = (Array.isArray(header) ? header.join(',') : (header ?? '')).toLowerCase();
|
|
127
|
+
if (accepted.includes('br')) return 'br';
|
|
128
|
+
if (accepted.includes('gzip')) return 'gzip';
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Quality 5 rather than brotli's default 11. The default spends seconds on a
|
|
134
|
+
* multi-megabyte workbench chunk and this proxy compresses on the request path,
|
|
135
|
+
* with the person waiting; 5 is the knee where the ratio is nearly the same and
|
|
136
|
+
* the CPU is not.
|
|
137
|
+
*/
|
|
138
|
+
function compressor(encoding: 'br' | 'gzip'): Transform {
|
|
139
|
+
return encoding === 'br'
|
|
140
|
+
? createBrotliCompress({ params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 5 } })
|
|
141
|
+
: createGzip({ level: 6 });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function startFrameProxy(options: FrameProxyOptions): Promise<FrameProxy> {
|
|
145
|
+
const { port, upstreamPort, sessionPort, projectRoot, colorTheme, log } = options;
|
|
146
|
+
const upstream = { host: '127.0.0.1', port: upstreamPort };
|
|
147
|
+
const session = { host: '127.0.0.1', port: sessionPort };
|
|
148
|
+
const upstreamOrigin = `http://${upstream.host}:${upstream.port}`;
|
|
149
|
+
const selfOrigin = `http://127.0.0.1:${port}`;
|
|
150
|
+
const webviewOriginTemplate = `http://{{uuid}}.localhost:${port}`;
|
|
151
|
+
const webviewOriginPattern = `http://*.localhost:${port}`;
|
|
152
|
+
const projectId = basename(projectRoot);
|
|
153
|
+
|
|
154
|
+
const isolation: Record<string, string> = {
|
|
155
|
+
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
156
|
+
'Cross-Origin-Embedder-Policy': 'credentialless',
|
|
157
|
+
'Cross-Origin-Resource-Policy': 'cross-origin',
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
let vscodePath = new RegExp(VSCODE_PATH_BASE);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* THE DOCUMENT'S OWN COLOUR, before it has any styles at all.
|
|
164
|
+
*
|
|
165
|
+
* `initialColorTheme` below fixes the WORKBENCH's first paint, and that is not the page's
|
|
166
|
+
* first paint: measured 2026-09-21 on the live release, `html` and `body` compute
|
|
167
|
+
* `rgba(0, 0, 0, 0)` — upstream's `workbench.css` styles `.monaco-workbench` and nothing
|
|
168
|
+
* above it — so from the document's first frame until `Workbench.renderWorkbench` builds that
|
|
169
|
+
* element, the canvas is the BROWSER's default, which is white. One frame of it survived F1's
|
|
170
|
+
* first reading (frame 0 at 0.0 s white, frame 1 at 0.5 s already the product's splash).
|
|
171
|
+
*
|
|
172
|
+
* `color-scheme` is the platform's own answer to exactly that question, and it is why this is
|
|
173
|
+
* a meta tag and not a background rule: it hands the browser the product's `dark`/`light`
|
|
174
|
+
* declaration UNCHANGED and lets it paint its own dark canvas, so nothing here becomes a
|
|
175
|
+
* second author of a palette value. The workbench sets `color-scheme` itself once its theme
|
|
176
|
+
* loads; this only fills the window before that.
|
|
177
|
+
*/
|
|
178
|
+
const colorSchemeMeta = (html: string): string =>
|
|
179
|
+
colorTheme === null
|
|
180
|
+
? html
|
|
181
|
+
: html.replace('<head>', `<head>\n\t\t<meta name="color-scheme" content="${colorTheme}">`);
|
|
182
|
+
|
|
183
|
+
/** Rewrite the workbench page's own configuration for THIS origin.
|
|
184
|
+
*
|
|
185
|
+
* - `remoteAuthority` needs NOTHING here, and that is worth stating: the REH
|
|
186
|
+
* derives it from the request's HOST header (measured — a direct fetch to
|
|
187
|
+
* the REH reports its own authority, the same fetch through this proxy
|
|
188
|
+
* reports this one), so forwarding the original Host is already what makes
|
|
189
|
+
* the management websocket, the `vscode-remote://` file system, every
|
|
190
|
+
* `vscode-remote-resource` url AND the page's own CSP address this origin.
|
|
191
|
+
* - `webviewEndpoint` is set (see the header).
|
|
192
|
+
* - `initialColorTheme` is set (see below). */
|
|
193
|
+
const patchWorkbenchConfig = (html: string): string => {
|
|
194
|
+
const webBaseUrl =
|
|
195
|
+
html.match(/id="vscode-workbench-web-base-url" data-settings="([^"]*)"/)?.[1] ?? '/static';
|
|
196
|
+
return html.replace(
|
|
197
|
+
/(id="vscode-workbench-web-configuration" data-settings=")([^"]*)(")/,
|
|
198
|
+
(_all, head: string, value: string, tail: string) => {
|
|
199
|
+
const config = JSON.parse(decodeAttr(value)) as Record<string, unknown>;
|
|
200
|
+
config['webviewEndpoint'] =
|
|
201
|
+
`${webviewOriginTemplate}${decodeAttr(webBaseUrl)}/out/vs/workbench/contrib/webview/browser/pre/`;
|
|
202
|
+
// THE FIRST PAINT IS THE PRODUCT'S, NOT THE WEB DEFAULT'S. Measured
|
|
203
|
+
// 2026-09-21 against both releases: with nothing here the page's first
|
|
204
|
+
// frame is LIGHT — `workbenchThemeService` falls back to
|
|
205
|
+
// `isWeb ? ColorScheme.LIGHT : ColorScheme.DARK` when it has no stored
|
|
206
|
+
// theme — so the product's opening cover, every colour of which is a
|
|
207
|
+
// `--vscode-*` variable by design, was WHITE from the first frame until
|
|
208
|
+
// the real theme loaded seconds later, and then flipped.
|
|
209
|
+
//
|
|
210
|
+
// `IWorkbenchConstructionOptions.initialColorTheme` is the supported
|
|
211
|
+
// door and the REH leaves it unset, the same shape as `webviewEndpoint`
|
|
212
|
+
// above. It has to be HERE and not in a setting or an extension: the
|
|
213
|
+
// theme service reads it in its constructor, before any extension is
|
|
214
|
+
// registered, which is exactly the window the cover lives in. The
|
|
215
|
+
// themeType strings ARE `ColorScheme`'s values (`dark`, `light`).
|
|
216
|
+
//
|
|
217
|
+
// The colour comes from the PRODUCT's own declaration and nothing else
|
|
218
|
+
// (`package.json#vgai.product.colorTheme`), and it is a FALLBACK in the
|
|
219
|
+
// workbench's own order: a person who has already picked a theme has it
|
|
220
|
+
// in storage, and `ColorThemeData.fromStorageData` is preferred over
|
|
221
|
+
// this on every open after the first.
|
|
222
|
+
if (colorTheme !== null) config['initialColorTheme'] = { themeType: colorTheme };
|
|
223
|
+
return head + encodeAttr(JSON.stringify(config)) + tail;
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const sendText = (res: ServerResponse, status: number, body: string, type: string): void => {
|
|
229
|
+
res.writeHead(status, { ...isolation, 'content-type': type, 'cache-control': 'no-store' });
|
|
230
|
+
res.end(body);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/** An unknown `?project=` says so by name rather than opening an empty workbench. */
|
|
234
|
+
const refuseProject = (res: ServerResponse, id: string): void => {
|
|
235
|
+
sendText(
|
|
236
|
+
res,
|
|
237
|
+
404,
|
|
238
|
+
`<!doctype html><meta charset="utf-8"><title>no such project</title>` +
|
|
239
|
+
`<body style="font:14px system-ui;padding:2rem"><h1>No project "${encodeAttr(id)}" is open here.</h1>` +
|
|
240
|
+
`<p>This workbench is the session for <code>${encodeAttr(projectRoot)}</code>, whose id is ` +
|
|
241
|
+
`<code>${encodeAttr(projectId)}</code>.</p>`,
|
|
242
|
+
'text/html; charset=utf-8',
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const handleRequest = (req: IncomingMessage, res: ServerResponse): void => {
|
|
247
|
+
const url = new URL(req.url ?? '/', selfOrigin);
|
|
248
|
+
const pathname = url.pathname;
|
|
249
|
+
// THE ONE HOSTED BOOT PARAM, ONTO THE WORKBENCH'S OWN. `?project=<id>` is
|
|
250
|
+
// what a link to the vgai editor carries; the workbench's own boot contract
|
|
251
|
+
// is `?folder=<path>` (`WorkspaceProvider.create`, which reads a bare path
|
|
252
|
+
// against `remoteAuthority`). This is a REDIRECT and not an injected
|
|
253
|
+
// `folderUri`, and the reason was measured (2026-09-19): under this shape
|
|
254
|
+
// the workbench page is ALSO the vgai editor's page, and the editor's boot
|
|
255
|
+
// refuses `?project=` on a session-backed surface by name — "project
|
|
256
|
+
// identity does NOT live in the URL on a local editor"
|
|
257
|
+
// (`assertNoRemovedBootParams`, packages/editor/src/boot-routing.ts). The
|
|
258
|
+
// redirect hands the page a url both halves accept.
|
|
259
|
+
if (pathname === '/') {
|
|
260
|
+
const id = url.searchParams.get('project');
|
|
261
|
+
if (id && !url.searchParams.has('folder') && !url.searchParams.has('workspace')) {
|
|
262
|
+
if (id !== projectId) {
|
|
263
|
+
refuseProject(res, id);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const next = new URL(url);
|
|
267
|
+
next.searchParams.delete('project');
|
|
268
|
+
next.searchParams.set('folder', projectRoot);
|
|
269
|
+
res.writeHead(302, {
|
|
270
|
+
...isolation,
|
|
271
|
+
location: `${next.pathname}${next.search}`,
|
|
272
|
+
'cache-control': 'no-store',
|
|
273
|
+
});
|
|
274
|
+
res.end();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const target = vscodePath.test(pathname) ? upstream : session;
|
|
280
|
+
const encoding = negotiateEncoding(req.headers['accept-encoding']);
|
|
281
|
+
const forward = httpRequest(
|
|
282
|
+
{ ...target, method: req.method, path: req.url, headers: req.headers },
|
|
283
|
+
(answer) => {
|
|
284
|
+
const headers: Record<string, string | string[]> = {};
|
|
285
|
+
for (const [key, value] of Object.entries(answer.headers)) {
|
|
286
|
+
if (value !== undefined) headers[key] = value;
|
|
287
|
+
}
|
|
288
|
+
delete headers['cross-origin-opener-policy'];
|
|
289
|
+
delete headers['cross-origin-embedder-policy'];
|
|
290
|
+
delete headers['cross-origin-resource-policy'];
|
|
291
|
+
// The webview's second origin, onto the REH's own `frame-src`.
|
|
292
|
+
const csp = headers['content-security-policy'];
|
|
293
|
+
if (typeof csp === 'string' && csp.includes('frame-src')) {
|
|
294
|
+
// GLTFLoader fetches local blob textures; img-src alone cannot
|
|
295
|
+
// authorize that fetch. Media previews use local blobs as well.
|
|
296
|
+
headers['content-security-policy'] = csp
|
|
297
|
+
.replace(/frame-src([^;]*)/, `frame-src$1 ${webviewOriginPattern}`)
|
|
298
|
+
.replace(/connect-src([^;]*)/, 'connect-src$1 blob:')
|
|
299
|
+
.replace(/media-src([^;]*)/, 'media-src$1 blob:');
|
|
300
|
+
}
|
|
301
|
+
const type = String(headers['content-type'] ?? '');
|
|
302
|
+
const alreadyEncoded = headers['content-encoding'] !== undefined;
|
|
303
|
+
// The workbench HTML and its config spell the upstream's OWN origin
|
|
304
|
+
// into absolute script and endpoint URLs; through this origin those are
|
|
305
|
+
// cross-origin and CORS refuses them. Text bodies are re-spelled here,
|
|
306
|
+
// which needs the whole body — so this branch buffers and the streaming
|
|
307
|
+
// one below does not.
|
|
308
|
+
if (/text\/html|javascript|json/.test(type) && !alreadyEncoded) {
|
|
309
|
+
const chunks: Buffer[] = [];
|
|
310
|
+
answer.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
311
|
+
answer.on('end', () => {
|
|
312
|
+
let body = Buffer.concat(chunks)
|
|
313
|
+
.toString('utf8')
|
|
314
|
+
.replaceAll(upstreamOrigin, selfOrigin);
|
|
315
|
+
if (pathname === '/' && target === upstream && /text\/html/.test(type)) {
|
|
316
|
+
body = colorSchemeMeta(patchWorkbenchConfig(body));
|
|
317
|
+
}
|
|
318
|
+
delete headers['content-length'];
|
|
319
|
+
const raw = Buffer.from(body, 'utf8');
|
|
320
|
+
if (encoding === null) {
|
|
321
|
+
res.writeHead(answer.statusCode ?? 502, {
|
|
322
|
+
...headers,
|
|
323
|
+
...isolation,
|
|
324
|
+
'content-length': raw.byteLength,
|
|
325
|
+
});
|
|
326
|
+
res.end(raw);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
res.writeHead(answer.statusCode ?? 502, {
|
|
330
|
+
...headers,
|
|
331
|
+
...isolation,
|
|
332
|
+
'content-encoding': encoding,
|
|
333
|
+
vary: 'accept-encoding',
|
|
334
|
+
});
|
|
335
|
+
const stream = compressor(encoding);
|
|
336
|
+
stream.pipe(res);
|
|
337
|
+
stream.end(raw);
|
|
338
|
+
});
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (encoding !== null && !alreadyEncoded && COMPRESSIBLE.test(type)) {
|
|
342
|
+
delete headers['content-length'];
|
|
343
|
+
res.writeHead(answer.statusCode ?? 502, {
|
|
344
|
+
...headers,
|
|
345
|
+
...isolation,
|
|
346
|
+
'content-encoding': encoding,
|
|
347
|
+
vary: 'accept-encoding',
|
|
348
|
+
});
|
|
349
|
+
void pipeline(answer, compressor(encoding), res).catch(() => res.destroy());
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
res.writeHead(answer.statusCode ?? 502, { ...headers, ...isolation });
|
|
353
|
+
answer.pipe(res);
|
|
354
|
+
},
|
|
355
|
+
);
|
|
356
|
+
forward.on('error', (error) => {
|
|
357
|
+
if (!res.headersSent) {
|
|
358
|
+
sendText(
|
|
359
|
+
res,
|
|
360
|
+
502,
|
|
361
|
+
`upstream ${target.port}: ${error.message}`,
|
|
362
|
+
'text/plain; charset=utf-8',
|
|
363
|
+
);
|
|
364
|
+
} else res.destroy();
|
|
365
|
+
});
|
|
366
|
+
// A browser that goes away takes its upstream request with it — otherwise a
|
|
367
|
+
// dead tab's event stream stays open, this session keeps that tab blessed,
|
|
368
|
+
// and EVERY later tab lands on `/__editor/tab-yielded` (measured
|
|
369
|
+
// 2026-09-19 in the session journal).
|
|
370
|
+
res.on('close', () => forward.destroy());
|
|
371
|
+
req.pipe(forward);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const handleUpgrade = (req: IncomingMessage, socket: Socket, head: Buffer): void => {
|
|
375
|
+
const target = vscodePath.test(new URL(req.url ?? '/', selfOrigin).pathname)
|
|
376
|
+
? upstream
|
|
377
|
+
: session;
|
|
378
|
+
const proxied = netConnect(target.port, target.host, () => {
|
|
379
|
+
proxied.write(
|
|
380
|
+
`${req.method} ${req.url} HTTP/${req.httpVersion}\r\n` +
|
|
381
|
+
Object.entries(req.headers)
|
|
382
|
+
.map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(', ') : value}`)
|
|
383
|
+
.join('\r\n') +
|
|
384
|
+
'\r\n\r\n',
|
|
385
|
+
);
|
|
386
|
+
if (head.length) proxied.write(head);
|
|
387
|
+
socket.pipe(proxied).pipe(socket);
|
|
388
|
+
});
|
|
389
|
+
proxied.on('error', () => socket.destroy());
|
|
390
|
+
socket.on('error', () => proxied.destroy());
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Wait for the upstream to report its shape before accepting requests. The REH serves its static
|
|
395
|
+
* assets under a PRODUCT SEGMENT (`/oss-dev/static` out of a source checkout,
|
|
396
|
+
* `/<quality>/static` out of a build), and this cannot route without knowing
|
|
397
|
+
* it. The page itself states it in `vscode-workbench-web-base-url`, so it is
|
|
398
|
+
* read rather than spelled.
|
|
399
|
+
*/
|
|
400
|
+
const discoverUpstream = (): Promise<string | null> =>
|
|
401
|
+
new Promise((done) => {
|
|
402
|
+
const probe = httpRequest({ ...upstream, path: '/' }, (answer) => {
|
|
403
|
+
const chunks: Buffer[] = [];
|
|
404
|
+
answer.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
405
|
+
answer.on('end', () => {
|
|
406
|
+
const html = Buffer.concat(chunks).toString('utf8');
|
|
407
|
+
const base = html.match(
|
|
408
|
+
/id="vscode-workbench-web-base-url" data-settings="([^"]*)"/,
|
|
409
|
+
)?.[1];
|
|
410
|
+
done(answer.statusCode === 200 && base ? decodeAttr(base).replace(/\/static\/?$/, '') : null);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
probe.on('error', () => done(null));
|
|
414
|
+
probe.setTimeout(3_000, () => {
|
|
415
|
+
probe.destroy();
|
|
416
|
+
done(null);
|
|
417
|
+
});
|
|
418
|
+
probe.end();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// A listening socket is not proof that Code-OSS can serve its HTML yet.
|
|
422
|
+
// Never expose a half-configured proxy: unknown prefixes route native JS to
|
|
423
|
+
// the session server and produce a blank workbench with script 404s.
|
|
424
|
+
const discoveryDeadline = Date.now() + 30_000;
|
|
425
|
+
let segment: string | null = null;
|
|
426
|
+
do {
|
|
427
|
+
segment = await discoverUpstream();
|
|
428
|
+
if (segment !== null) break;
|
|
429
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
430
|
+
} while (Date.now() < discoveryDeadline);
|
|
431
|
+
if (segment === null) throw new Error(
|
|
432
|
+
`Code-OSS on port ${upstream.port} did not report its workbench asset base within 30s; refusing to start an incorrectly routed proxy.`,
|
|
433
|
+
);
|
|
434
|
+
if (segment) {
|
|
435
|
+
vscodePath = new RegExp(
|
|
436
|
+
`${VSCODE_PATH_BASE}|^${segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\/|$)`,
|
|
437
|
+
);
|
|
438
|
+
log(`frame: workbench product segment ${segment}`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ONE handler, TWO loopback listeners — 127.0.0.1 and ::1. The webview's
|
|
442
|
+
// origin is a `*.localhost` subdomain (see the header), and macOS answers
|
|
443
|
+
// `<anything>.localhost` with the IPv6 `::1` as well as 127.0.0.1
|
|
444
|
+
// (`dscacheutil -q host -a name x.localhost`); a browser that picks the IPv6
|
|
445
|
+
// address finds nothing listening and the webview iframe fails as
|
|
446
|
+
// `chrome-error://chromewebdata` with no message at all — measured
|
|
447
|
+
// 2026-09-19 against a proxy bound to 127.0.0.1 alone.
|
|
448
|
+
const servers: Server[] = [];
|
|
449
|
+
await new Promise<void>((ready, failed) => {
|
|
450
|
+
let settled = false;
|
|
451
|
+
for (const host of ['127.0.0.1', '::1']) {
|
|
452
|
+
const one = createServer(handleRequest);
|
|
453
|
+
one.on('upgrade', handleUpgrade);
|
|
454
|
+
one.on('error', (error) => {
|
|
455
|
+
if (host === '::1') {
|
|
456
|
+
log(
|
|
457
|
+
`frame: no IPv6 loopback listener (${error.message}); webviews need one if the browser resolves *.localhost to ::1`,
|
|
458
|
+
);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (!settled) {
|
|
462
|
+
settled = true;
|
|
463
|
+
failed(error);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
servers.push(one);
|
|
467
|
+
one.listen(port, host, () => {
|
|
468
|
+
if (!settled) {
|
|
469
|
+
settled = true;
|
|
470
|
+
ready();
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
url: `${selfOrigin}/?project=${encodeURIComponent(projectId)}`,
|
|
478
|
+
close: async () => {
|
|
479
|
+
await Promise.all(
|
|
480
|
+
servers.map(
|
|
481
|
+
(one) =>
|
|
482
|
+
new Promise<void>((done) => {
|
|
483
|
+
one.close(() => done());
|
|
484
|
+
one.closeAllConnections?.();
|
|
485
|
+
}),
|
|
486
|
+
),
|
|
487
|
+
);
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|