@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,686 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project's OWN FILES — reading and writing them, and serving them to the
|
|
3
|
+
* running game.
|
|
4
|
+
*
|
|
5
|
+
* `/__editor/save-file`, `/__editor/vgai-file`, `/__editor/project-resource`,
|
|
6
|
+
* `/__editor/data-file(s)`, `/__editor/story-files`,
|
|
7
|
+
* `/__editor/scoped-game-css`, `/__editor/source-conflict` and the `/@fs`
|
|
8
|
+
* module server.
|
|
9
|
+
*
|
|
10
|
+
* Every WRITE here goes through `ctx.commitProjectMutation` — the one
|
|
11
|
+
* attributed, conflict-checked transaction — and answers failures through
|
|
12
|
+
* `ctx.projectMutationError`, including the structured revision-conflict
|
|
13
|
+
* shape. There is deliberately no second write path.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type Dirent, existsSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
18
|
+
import { join, relative, resolve, sep } from 'node:path';
|
|
19
|
+
import { isContainedRelativePath } from '@volter/editor-sdk/session/relative-path-guard';
|
|
20
|
+
import { globToRegExp } from '@volter/editor-sdk/session/source-glob';
|
|
21
|
+
import type { Request, Response } from 'express';
|
|
22
|
+
import { resolveManifestPath } from '@volter/editor-project/manifest/locate';
|
|
23
|
+
import { projectRegionEntriesFromSources } from '../../src/asset-workflow/project-source-index';
|
|
24
|
+
import { ADAPTER_MODULE_FILENAME } from '../../src/ui-source/adapter-region-includes';
|
|
25
|
+
import { foldDataFileText } from '../data-file-serialize';
|
|
26
|
+
import type { EditorServerRouter } from '../editor-server';
|
|
27
|
+
import { buildScopedGameStyles } from '../scoped-game-css';
|
|
28
|
+
import {
|
|
29
|
+
isCanonicalPathInside,
|
|
30
|
+
isPathInside,
|
|
31
|
+
isReadableVgaiPath,
|
|
32
|
+
isServableFsExtension,
|
|
33
|
+
isServableFsPath,
|
|
34
|
+
isViteDepCachePath,
|
|
35
|
+
isWritableProjectResourcePath,
|
|
36
|
+
isWritableVgaiEditorPath,
|
|
37
|
+
projectStorySourceDirs,
|
|
38
|
+
} from '../server-utils';
|
|
39
|
+
import type { RouteContext } from './context';
|
|
40
|
+
import { shareControlError } from './route-helpers';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Recursive file scanning
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
async function findFiles(dir: string, ext: string): Promise<string[]> {
|
|
47
|
+
const results: string[] = [];
|
|
48
|
+
let entries: Dirent[];
|
|
49
|
+
try {
|
|
50
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
51
|
+
} catch {
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.name.startsWith('.')) continue;
|
|
56
|
+
const full = join(dir, entry.name);
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
results.push(...(await findFiles(full, ext)));
|
|
59
|
+
} else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
60
|
+
results.push(full);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function registerProjectSourceRoutes(router: EditorServerRouter, ctx: RouteContext): void {
|
|
67
|
+
const {
|
|
68
|
+
commitProjectMutation,
|
|
69
|
+
engineRoot,
|
|
70
|
+
projectMutationError,
|
|
71
|
+
readProjectManifest,
|
|
72
|
+
sourceConflictTickets,
|
|
73
|
+
trustedShareIdentity,
|
|
74
|
+
} = ctx;
|
|
75
|
+
|
|
76
|
+
router.get('/__editor/source-conflict', async (req: Request, res: Response) => {
|
|
77
|
+
const path =
|
|
78
|
+
typeof req.query['path'] === 'string' ? req.query['path'].split('\\').join('/') : '';
|
|
79
|
+
const absolute = resolve(ctx.projectRoot, path);
|
|
80
|
+
if (!isContainedRelativePath(path) || !isPathInside(ctx.projectRoot, absolute)) {
|
|
81
|
+
res.status(400).json({ error: 'Conflict path is outside the project.' });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
if (trustedShareIdentity(req) && !(await isCanonicalPathInside(ctx.projectRoot, absolute))) {
|
|
86
|
+
res.status(403).json({ error: 'Shared conflict path leaves the project root.' });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const info = await stat(absolute);
|
|
90
|
+
if (!info.isFile() || info.size > 2 * 1024 * 1024) {
|
|
91
|
+
res.status(413).json({ error: 'Conflict inspection is limited to 2 MiB text files.' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
res.json({ path, content: await readFile(absolute, 'utf8') });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
97
|
+
res.json({ path, content: null });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
shareControlError(res, error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
router.post('/__editor/source-conflict/resolve', async (req: Request, res: Response) => {
|
|
105
|
+
const body = (req.body ?? {}) as Record<string, unknown>;
|
|
106
|
+
const ticket =
|
|
107
|
+
typeof body['conflictId'] === 'string'
|
|
108
|
+
? sourceConflictTickets.get(body['conflictId'])
|
|
109
|
+
: undefined;
|
|
110
|
+
if (!ticket || ticket.expiresAt < Date.now()) {
|
|
111
|
+
if (typeof body['conflictId'] === 'string') sourceConflictTickets.delete(body['conflictId']);
|
|
112
|
+
res.status(409).json({ error: 'This source-conflict resolution expired. Compare again.' });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(body['resources']) || body['resources'].length === 0) {
|
|
116
|
+
res.status(400).json({ error: 'Expected at least one resolved source resource.' });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const resources: Array<{ path: string; content: string | null }> = [];
|
|
120
|
+
for (const value of body['resources']) {
|
|
121
|
+
if (!value || typeof value !== 'object') {
|
|
122
|
+
res.status(400).json({ error: 'Invalid resolved source resource.' });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const resource = value as Record<string, unknown>;
|
|
126
|
+
if (
|
|
127
|
+
typeof resource['path'] !== 'string' ||
|
|
128
|
+
!ticket.paths.has(resource['path']) ||
|
|
129
|
+
(resource['content'] !== null && typeof resource['content'] !== 'string') ||
|
|
130
|
+
(typeof resource['content'] === 'string' && resource['content'].length > 2 * 1024 * 1024)
|
|
131
|
+
) {
|
|
132
|
+
res.status(400).json({ error: 'Resolved content must match this conflict ticket.' });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
resources.push({ path: resource['path'], content: resource['content'] });
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const revision = await commitProjectMutation(req, resources);
|
|
139
|
+
sourceConflictTickets.delete(body['conflictId'] as string);
|
|
140
|
+
res.json({ ok: true, revision: revision?.revision ?? null });
|
|
141
|
+
} catch (error) {
|
|
142
|
+
projectMutationError(res, error);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
// ---- Save file ----
|
|
146
|
+
router.post('/__editor/save-file', async (req: Request, res: Response) => {
|
|
147
|
+
const body = req.body as {
|
|
148
|
+
path: string;
|
|
149
|
+
content?: string;
|
|
150
|
+
encoding?: 'base64';
|
|
151
|
+
delete?: boolean;
|
|
152
|
+
};
|
|
153
|
+
const filePath = body.path ?? '';
|
|
154
|
+
|
|
155
|
+
// Both halves are load-bearing, and the pair used to be
|
|
156
|
+
// `includes('..')` + `startsWith(publicRoot)`, which let an ABSOLUTE
|
|
157
|
+
// `body.path` through: `resolve(root, 'public', '/root/publicX/x.js')`
|
|
158
|
+
// returns its last argument verbatim, so no `..` ever appeared and the
|
|
159
|
+
// separator-less prefix test accepted the sibling directory. The write
|
|
160
|
+
// then landed outside `public/`.
|
|
161
|
+
if (!isContainedRelativePath(filePath)) {
|
|
162
|
+
res.status(400).json({ error: 'Invalid path.' });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const absPath = resolve(ctx.projectRoot, 'public', filePath);
|
|
167
|
+
if (!isPathInside(ctx.publicRoot, absPath)) {
|
|
168
|
+
res.status(400).json({ error: 'Path outside public directory.' });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
if (body.delete) {
|
|
174
|
+
const revision = await commitProjectMutation(req, [
|
|
175
|
+
{ path: `public/${filePath}`, content: null },
|
|
176
|
+
]);
|
|
177
|
+
res.json({ ok: true, path: filePath, revision: revision?.revision ?? null });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (typeof body.content !== 'string') {
|
|
181
|
+
res.status(400).json({ error: 'Missing content.' });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const data = body.encoding === 'base64' ? Buffer.from(body.content, 'base64') : body.content;
|
|
185
|
+
const revision = await commitProjectMutation(req, [
|
|
186
|
+
{ path: `public/${filePath}`, content: data },
|
|
187
|
+
]);
|
|
188
|
+
res.json({ ok: true, path: filePath, revision: revision?.revision ?? null });
|
|
189
|
+
} catch (error) {
|
|
190
|
+
projectMutationError(res, error);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
// ---- Project-root `.vgai/` file access ----
|
|
194
|
+
//
|
|
195
|
+
// The editor-state/thumbnail routes above already read/write
|
|
196
|
+
// `<projectRoot>/.vgai/...` directly via `node:fs` — but `StorageBackend`
|
|
197
|
+
// (`HttpStorage`/`/__editor/save-file` above) is hard-rooted at
|
|
198
|
+
// `<projectRoot>/public/`, so anything a client writes through that seam
|
|
199
|
+
// under a `.vgai/` path actually lands at `<projectRoot>/public/.vgai/...`
|
|
200
|
+
// (the two-`.vgai`-dirs finding recorded in T3.3's close-out).
|
|
201
|
+
// This route pair is the fix: project-root-relative, scoped to `.vgai/` only
|
|
202
|
+
// (defense in depth — it is not a general project-file API), mirroring the
|
|
203
|
+
// editor-state route's mkdir-on-write / read-or-empty shape. Today's readers
|
|
204
|
+
// are the provenance ledger (`src/project-provenance.ts`) and the
|
|
205
|
+
// asset-budget optimize pass (`@vgai/game/src/asset-budget/`).
|
|
206
|
+
router.get('/__editor/vgai-file', async (req: Request, res: Response) => {
|
|
207
|
+
const relPath = (req.query['path'] as string) ?? '';
|
|
208
|
+
if (!isReadableVgaiPath(relPath)) {
|
|
209
|
+
res.status(400).json({ error: 'Invalid path.' });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (ctx.projectRoot === engineRoot) {
|
|
213
|
+
res.status(404).json({ error: 'No project open.' });
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const absolute = join(ctx.projectRoot, relPath);
|
|
218
|
+
if (trustedShareIdentity(req) && !(await isCanonicalPathInside(ctx.projectRoot, absolute))) {
|
|
219
|
+
res.status(403).json({ error: 'Shared path leaves the project root.' });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const raw = await readFile(absolute);
|
|
223
|
+
res.type(relPath.endsWith('.png') ? 'image/png' : 'text/plain').send(raw);
|
|
224
|
+
} catch {
|
|
225
|
+
res.status(404).json({ error: 'Not found.' });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
router.post('/__editor/vgai-file', async (req: Request, res: Response) => {
|
|
230
|
+
const body = req.body as { path?: string; content?: string; encoding?: string };
|
|
231
|
+
const relPath = body.path ?? '';
|
|
232
|
+
if (!isWritableVgaiEditorPath(relPath)) {
|
|
233
|
+
res.status(400).json({ error: 'Invalid path.' });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (ctx.projectRoot === engineRoot) {
|
|
237
|
+
res.status(400).json({ error: 'No project open.' });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const content =
|
|
242
|
+
body.encoding === 'base64'
|
|
243
|
+
? Buffer.from(body.content ?? '', 'base64')
|
|
244
|
+
: (body.content ?? '');
|
|
245
|
+
const revision = await commitProjectMutation(req, [{ path: relPath, content }]);
|
|
246
|
+
res.json({ ok: true, revision: revision?.revision ?? null });
|
|
247
|
+
} catch (error) {
|
|
248
|
+
projectMutationError(res, error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ---- Project-owned authoring resources ----
|
|
253
|
+
//
|
|
254
|
+
// Asset Lab documents can serialize several native resources as one logical
|
|
255
|
+
// history step. The browser-side history driver reaches each exact byte
|
|
256
|
+
// snapshot through this project-root route; executable source remains owned
|
|
257
|
+
// by `/__ui-source/*`, and project control files remain unreachable.
|
|
258
|
+
// Source files matching `include` globs — what a finder's `include` reads
|
|
259
|
+
// through (ARCHITECTURE-CORE §The project model). Bounded to `src/`.
|
|
260
|
+
router.get('/__editor/source-files', async (req: Request, res: Response) => {
|
|
261
|
+
if (ctx.projectRoot === engineRoot) {
|
|
262
|
+
res.json({ files: [] });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const raw = req.query['include'];
|
|
266
|
+
const globs = (Array.isArray(raw) ? raw : [raw]).filter(
|
|
267
|
+
(g): g is string => typeof g === 'string' && g.length > 0 && !g.includes('..'),
|
|
268
|
+
);
|
|
269
|
+
const matchers = globs.map(globToRegExp);
|
|
270
|
+
// `order=mtime` answers newest first: the adapter table takes this order,
|
|
271
|
+
// and a fresh checkout with several documents and no declared default
|
|
272
|
+
// opens on the one most recently written — the one being worked on.
|
|
273
|
+
const byMtime = req.query['order'] === 'mtime';
|
|
274
|
+
const files: { path: string; mtimeMs: number }[] = [];
|
|
275
|
+
const walk = async (absolute: string, rel: string): Promise<void> => {
|
|
276
|
+
let entries: Dirent[];
|
|
277
|
+
try {
|
|
278
|
+
entries = await readdir(absolute, { withFileTypes: true });
|
|
279
|
+
} catch {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
for (const entry of entries) {
|
|
283
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
284
|
+
const path = rel ? `${rel}/${entry.name}` : entry.name;
|
|
285
|
+
if (entry.isDirectory()) await walk(join(absolute, entry.name), path);
|
|
286
|
+
else if (matchers.some((m) => m.test(path))) {
|
|
287
|
+
let mtimeMs = 0;
|
|
288
|
+
if (byMtime) {
|
|
289
|
+
try {
|
|
290
|
+
mtimeMs = (await stat(join(absolute, entry.name))).mtimeMs;
|
|
291
|
+
} catch {
|
|
292
|
+
/* vanished between readdir and stat: sorts last */
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
files.push({ path, mtimeMs });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
await walk(join(ctx.projectRoot, 'src'), 'src');
|
|
300
|
+
files.sort((a, b) =>
|
|
301
|
+
byMtime && a.mtimeMs !== b.mtimeMs ? b.mtimeMs - a.mtimeMs : a.path.localeCompare(b.path),
|
|
302
|
+
);
|
|
303
|
+
res.json({ files: files.map((file) => file.path) });
|
|
304
|
+
});
|
|
305
|
+
router.get('/__editor/project-resource', async (req: Request, res: Response) => {
|
|
306
|
+
const relPath = (req.query['path'] as string) ?? '';
|
|
307
|
+
if (!isWritableProjectResourcePath(relPath)) {
|
|
308
|
+
res.status(400).json({ error: 'Invalid project resource path.' });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (ctx.projectRoot === engineRoot) {
|
|
312
|
+
res.status(404).json({ error: 'No project open.' });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const absolute = join(ctx.projectRoot, relPath);
|
|
317
|
+
if (trustedShareIdentity(req) && !(await isCanonicalPathInside(ctx.projectRoot, absolute))) {
|
|
318
|
+
res.status(403).json({ error: 'Shared path leaves the project root.' });
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
res.type('application/octet-stream').send(await readFile(absolute));
|
|
322
|
+
} catch {
|
|
323
|
+
res.status(404).json({ error: 'Not found.' });
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
router.post('/__editor/project-resource', async (req: Request, res: Response) => {
|
|
328
|
+
const body = req.body as { path?: string; content?: string; encoding?: string };
|
|
329
|
+
const relPath = body.path ?? '';
|
|
330
|
+
if (!isWritableProjectResourcePath(relPath)) {
|
|
331
|
+
res.status(400).json({ error: 'Invalid project resource path.' });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (ctx.projectRoot === engineRoot) {
|
|
335
|
+
res.status(400).json({ error: 'No project open.' });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (body.encoding !== 'base64' || typeof body.content !== 'string') {
|
|
339
|
+
res.status(400).json({ error: 'Project resource content must be base64.' });
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const content = Buffer.from(body.content, 'base64');
|
|
343
|
+
if (content.toString('base64') !== body.content) {
|
|
344
|
+
res.status(400).json({ error: 'Project resource content is not valid base64.' });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
const revision = await commitProjectMutation(req, [{ path: relPath, content }]);
|
|
349
|
+
res.json({ ok: true, revision: revision?.revision ?? null });
|
|
350
|
+
} catch (error) {
|
|
351
|
+
projectMutationError(res, error);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
router.delete('/__editor/project-resource', async (req: Request, res: Response) => {
|
|
356
|
+
const relPath = (req.body as { path?: string }).path ?? '';
|
|
357
|
+
if (!isWritableProjectResourcePath(relPath)) {
|
|
358
|
+
res.status(400).json({ error: 'Invalid project resource path.' });
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (ctx.projectRoot === engineRoot) {
|
|
362
|
+
res.status(400).json({ error: 'No project open.' });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const revision = await commitProjectMutation(req, [{ path: relPath, content: null }]);
|
|
367
|
+
res.json({ ok: true, revision: revision?.revision ?? null });
|
|
368
|
+
} catch (error) {
|
|
369
|
+
projectMutationError(res, error);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// ---- Data assets (W2) ----
|
|
373
|
+
//
|
|
374
|
+
// The Data dock tab's server half. Discovery is the folder scan, per D3 (no
|
|
375
|
+
// asset database): every `src/data/*.data.json` of the open project, plus
|
|
376
|
+
// the sibling EMITTED `*.schema.json` when present — the editor consumes the
|
|
377
|
+
// JSON Schema interchange artifact and never executes the project's
|
|
378
|
+
// `.schema.ts` (§2.1). Raw file text is returned (not parsed JSON) so the
|
|
379
|
+
// client owns parse errors and can report a malformed file instead of this
|
|
380
|
+
// route 500ing on it.
|
|
381
|
+
//
|
|
382
|
+
// Why not `/__editor/save-file` + `/@fs/` reads: that seam is HARD-ROOTED at
|
|
383
|
+
// `<projectRoot>/public/` (see `HttpStorage`'s doc comment and the
|
|
384
|
+
// two-`.vgai`-dirs finding above) — data assets live under the project
|
|
385
|
+
// root's `src/data/`, outside its reach. Same fix as the `vgai-file` pair:
|
|
386
|
+
// a purpose-scoped route rooted at the project root, deliberately NOT a
|
|
387
|
+
// general project-file API (defense in depth — the write half accepts only
|
|
388
|
+
// `src/data/**/*.data.json`).
|
|
389
|
+
router.get('/__editor/data-files', async (_req: Request, res: Response) => {
|
|
390
|
+
if (ctx.projectRoot === engineRoot) {
|
|
391
|
+
res.json({ files: [] });
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const dataDir = join(ctx.projectRoot, 'src', 'data');
|
|
395
|
+
const found = await findFiles(dataDir, '.data.json');
|
|
396
|
+
const files: Array<{ path: string; content: string; schema: string | null }> = [];
|
|
397
|
+
for (const abs of found.sort()) {
|
|
398
|
+
let content: string;
|
|
399
|
+
try {
|
|
400
|
+
content = await readFile(abs, 'utf-8');
|
|
401
|
+
} catch {
|
|
402
|
+
continue; // deleted between scan and read — just omit it
|
|
403
|
+
}
|
|
404
|
+
// Sibling emitted schema (D4: generated + committed by default). Absent
|
|
405
|
+
// (project hasn't run `npm run emit-schemas`) → null; the client shows
|
|
406
|
+
// a teaching message rather than guessing widget types.
|
|
407
|
+
let schema: string | null = null;
|
|
408
|
+
try {
|
|
409
|
+
schema = await readFile(abs.replace(/\.data\.json$/, '.schema.json'), 'utf-8');
|
|
410
|
+
} catch {
|
|
411
|
+
/* no emitted schema */
|
|
412
|
+
}
|
|
413
|
+
files.push({ path: relative(ctx.projectRoot, abs).split('\\').join('/'), content, schema });
|
|
414
|
+
}
|
|
415
|
+
res.json({ files });
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// D1 (spec §2.5): a Data-panel edit writes the `.data.json` IMMEDIATELY. A
|
|
419
|
+
// running game picks the write up
|
|
420
|
+
// through the already-shipped HMR path (`dev.ts`'s `.data.json` exemption →
|
|
421
|
+
// the owning module's `import.meta.hot.accept` → `DataHandle.hotSwap`).
|
|
422
|
+
// Undo for a data write is git, so there is no byte-exact replay path and
|
|
423
|
+
// therefore no bytes-in body variant — see the fold note in the handler.
|
|
424
|
+
router.post('/__editor/data-file', async (req: Request, res: Response) => {
|
|
425
|
+
const body = req.body as { path?: string; content?: unknown; values?: unknown };
|
|
426
|
+
const relPath = body.path ?? '';
|
|
427
|
+
if (
|
|
428
|
+
!isContainedRelativePath(relPath) ||
|
|
429
|
+
!relPath.startsWith('src/data/') ||
|
|
430
|
+
!relPath.endsWith('.data.json')
|
|
431
|
+
) {
|
|
432
|
+
res.status(400).json({ error: 'Invalid path — expected src/data/**/*.data.json.' });
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (ctx.projectRoot === engineRoot) {
|
|
436
|
+
res.status(400).json({ error: 'No project open.' });
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
// ONE accepted body: `values`, a parsed next-state object. THIS route folds
|
|
440
|
+
// it against the file's current key order and serializes it
|
|
441
|
+
// (`data-file-serialize.ts`). The data-asset UI ships as a copied
|
|
442
|
+
// capability, so the fold cannot live there: a project that reorganizes its
|
|
443
|
+
// own table UI must not be able to reorganize the file's bytes with it.
|
|
444
|
+
// Raw bytes are deliberately NOT accepted — a caller that could hand over
|
|
445
|
+
// exact text would be a second serializer, and the fold would no longer be
|
|
446
|
+
// the one place the diff contract lives.
|
|
447
|
+
if (body.content !== undefined) {
|
|
448
|
+
res.status(400).json({
|
|
449
|
+
error:
|
|
450
|
+
'This route writes PARSED VALUES, not bytes — post `values` (a JSON object) and the ' +
|
|
451
|
+
"dev server's fold owns key order and the $schema-first serialization.",
|
|
452
|
+
});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (body.values === undefined) {
|
|
456
|
+
res
|
|
457
|
+
.status(400)
|
|
458
|
+
.json({ error: "Missing values — post the file's parsed next state as `values`." });
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (!body.values || typeof body.values !== 'object' || Array.isArray(body.values)) {
|
|
462
|
+
res.status(400).json({ error: 'values must be a JSON object.' });
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
let currentText: string | null = null;
|
|
466
|
+
try {
|
|
467
|
+
currentText = await readFile(join(ctx.projectRoot, relPath), 'utf-8');
|
|
468
|
+
} catch {
|
|
469
|
+
/* new file — the posted values define the order */
|
|
470
|
+
}
|
|
471
|
+
const content = foldDataFileText(currentText, body.values as Record<string, unknown>);
|
|
472
|
+
try {
|
|
473
|
+
const revision = await commitProjectMutation(req, [{ path: relPath, content }]);
|
|
474
|
+
res.json({ ok: true, path: relPath, revision: revision?.revision ?? null });
|
|
475
|
+
} catch (error) {
|
|
476
|
+
projectMutationError(res, error);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// There is deliberately no GET/DELETE on `/__editor/data-file`: reading a
|
|
481
|
+
// data asset is the folder scan (`GET /__editor/data-files`, which returns
|
|
482
|
+
// every file's text), and nothing in the product deletes one through the
|
|
483
|
+
// server. This route is the WRITE seam only.
|
|
484
|
+
// ---- Story discovery (C3, spec §9 — "Add CSF Discovery and Portable
|
|
485
|
+
// Story Mounting") ----
|
|
486
|
+
//
|
|
487
|
+
// Conventional `*.stories.tsx` / `*.stories.ts` files, colocated anywhere
|
|
488
|
+
// under the project's `src/` (Storybook's own convention — unlike
|
|
489
|
+
// `src/contributions/`/`src/data/`, stories are not confined to one folder). Same
|
|
490
|
+
// D3 physics as every other editor list here: a live folder scan, never a
|
|
491
|
+
// cached manifest — add/remove a `*.stories.tsx` and the next refresh sees
|
|
492
|
+
// it. Paths only: the CSF MODULE itself is loaded client-side through
|
|
493
|
+
// Vite's `/@fs/` dynamic-import path (`story-discovery.ts`, mirroring
|
|
494
|
+
// `tool-loader.ts`), so it arrives transpiled with HMR, and the editor
|
|
495
|
+
// never stores a duplicate story definition — the CSF module stays the one
|
|
496
|
+
// source of truth (spec §9 C3 "Story source remains the CSF module").
|
|
497
|
+
//
|
|
498
|
+
// `previewModulePath` rides along in the same response: the project's
|
|
499
|
+
// OPTIONAL `.storybook/preview.tsx`/`.ts` (checked with `existsSync`, same
|
|
500
|
+
// "server tells the truth about disk, client never guesses" contract as
|
|
501
|
+
// the rest of this route), or `null` when the project has no `.storybook/`
|
|
502
|
+
// folder — most projects don't. `story-discovery.ts`'s
|
|
503
|
+
// `loadProjectPreviewAnnotations` used to blind-probe both candidate paths
|
|
504
|
+
// client-side via `/@fs/` and swallow the import failure; that still
|
|
505
|
+
// logged a real failed network request for every project without a
|
|
506
|
+
// preview config (the common case), which is indistinguishable from a
|
|
507
|
+
// genuine broken-editor error to anything watching the page's network/
|
|
508
|
+
// console (e.g. `check-vgai-generated-project-p2p.ts`'s browser-proof).
|
|
509
|
+
// Reporting existence here lets the client only ever import a path that is
|
|
510
|
+
// actually there.
|
|
511
|
+
router.get('/__editor/story-files', async (_req: Request, res: Response) => {
|
|
512
|
+
if (ctx.projectRoot === engineRoot) {
|
|
513
|
+
res.json({ files: [], previewModulePath: null, regions: [] });
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
// NOT `<projectRoot>/src` — the project's own manifest says where its
|
|
517
|
+
// authored source lives (`projectStorySourceDirs`, server-utils.ts). For
|
|
518
|
+
// every scaffolded project and every `examples/<id>` that IS
|
|
519
|
+
// `<projectRoot>/src`; for a project whose root `entry` resolves outside
|
|
520
|
+
// its folder (a repo-vendored game: manifest + host shim under
|
|
521
|
+
// `packages/editor/src/ingest/games/<id>/`, source under
|
|
522
|
+
// `vendor/games/<id>/src/`) it is that game's source tree as well, so a
|
|
523
|
+
// story colocated with the component it declares is DISCOVERED there.
|
|
524
|
+
const scanDirs = projectStorySourceDirs(ctx.projectRoot, readProjectManifest());
|
|
525
|
+
const scans = await Promise.all(
|
|
526
|
+
scanDirs.flatMap((dir) => [findFiles(dir, '.stories.tsx'), findFiles(dir, '.stories.ts')]),
|
|
527
|
+
);
|
|
528
|
+
const found = scans.flat().filter((abs) => !abs.includes(`${sep}node_modules${sep}`));
|
|
529
|
+
const previewCandidates = ['.storybook/preview.tsx', '.storybook/preview.ts'];
|
|
530
|
+
const previewModulePath =
|
|
531
|
+
previewCandidates.find((candidate) => existsSync(join(ctx.projectRoot, candidate))) ?? null;
|
|
532
|
+
let manifestSource = '';
|
|
533
|
+
let adapterSource = '';
|
|
534
|
+
try {
|
|
535
|
+
manifestSource = readFileSync(resolveManifestPath(ctx.projectRoot), 'utf8');
|
|
536
|
+
} catch {
|
|
537
|
+
/* no manifest — regions stay empty */
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
adapterSource = readFileSync(join(ctx.projectRoot, ADAPTER_MODULE_FILENAME), 'utf8');
|
|
541
|
+
} catch {
|
|
542
|
+
/* no adapter — native default, regions still derive from roots */
|
|
543
|
+
}
|
|
544
|
+
const { regions } = projectRegionEntriesFromSources(manifestSource, adapterSource);
|
|
545
|
+
res.json({
|
|
546
|
+
files: found.sort().map((abs) => ({
|
|
547
|
+
path: relative(ctx.projectRoot, abs).split('\\').join('/'),
|
|
548
|
+
})),
|
|
549
|
+
previewModulePath,
|
|
550
|
+
regions,
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// ---- Scoped game CSS (the game's page-level stylesheet, contained) ----
|
|
555
|
+
// A foreign game keeps its HUD layout in a page stylesheet whose `html`/
|
|
556
|
+
// `body`/`*` rules would restyle the EDITOR, so no surface could load it and
|
|
557
|
+
// every surface showing that game's DOM rendered unstyled. Serve it rewritten
|
|
558
|
+
// into `@scope ([data-vgai-game-styles])` instead: the client attaches it once
|
|
559
|
+
// and marks each container that owns game DOM (a story card's content, the
|
|
560
|
+
// ingest surface). The transform is SERVE-TIME — the bytes under
|
|
561
|
+
// `vendor/games/` are read and never written, which is what keeps
|
|
562
|
+
// `vendor/games/verify-unaltered.mjs` green.
|
|
563
|
+
router.get('/__editor/scoped-game-css', (_req: Request, res: Response) => {
|
|
564
|
+
const result = buildScopedGameStyles({
|
|
565
|
+
manifest: readProjectManifest(),
|
|
566
|
+
readStyleSheet: (relPath) => {
|
|
567
|
+
// Same containment every project read here has: a declared path is
|
|
568
|
+
// resolved against the project root and must stay on disk. A vendored
|
|
569
|
+
// game's sheet legitimately sits OUTSIDE the project folder (the
|
|
570
|
+
// manifest lives under `packages/editor/src/ingest/games/<id>/`, the
|
|
571
|
+
// game's source under `vendor/games/<id>/src/`), which is exactly the
|
|
572
|
+
// shape `projectStorySourceDirs` already serves stories from — so the
|
|
573
|
+
// bound is "readable file", not "inside ctx.projectRoot".
|
|
574
|
+
const absolute = resolve(ctx.projectRoot, relPath);
|
|
575
|
+
try {
|
|
576
|
+
return {
|
|
577
|
+
css: readFileSync(absolute, 'utf8'),
|
|
578
|
+
// Vite serves an out-of-root file at `/@fs/<absolute>`; relative
|
|
579
|
+
// `url()`s in the sheet rebase onto that, so a game asset request
|
|
580
|
+
// goes to the game's own folder instead of an editor route.
|
|
581
|
+
servedPath: `/@fs/${absolute.split(sep).join('/')}`,
|
|
582
|
+
};
|
|
583
|
+
} catch {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
res.json(result);
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* The module transport's missing-module answer, registered SEPARATELY because
|
|
594
|
+
* its POSITION is load-bearing: it must sit after the packaged host's
|
|
595
|
+
* transpiling `/@fs` route so that route's extension resolution runs first.
|
|
596
|
+
* The server calls this immediately after that block, exactly where the
|
|
597
|
+
* middleware used to be written inline.
|
|
598
|
+
*/
|
|
599
|
+
export function registerModuleTransportNotFound(
|
|
600
|
+
router: EditorServerRouter,
|
|
601
|
+
ctx: RouteContext,
|
|
602
|
+
): void {
|
|
603
|
+
const { engineRoot } = ctx;
|
|
604
|
+
|
|
605
|
+
// ---- A DELETED project module must 404, not become the editor's HTML ----
|
|
606
|
+
//
|
|
607
|
+
// Measured live (2026-08-19): delete a file the mounted world imports, and
|
|
608
|
+
// `GET /@fs/<abs>/src/scenes/MainScene.tsx` answered `200 text/html` with the
|
|
609
|
+
// editor's own `index.html` (27 KB) — the SPA fallback, which Vite's
|
|
610
|
+
// `htmlFallbackMiddleware` applies to any unmatched GET whose `Accept`
|
|
611
|
+
// includes `*/*`, and a browser's ES-module fetch sends exactly that. The
|
|
612
|
+
// browser then refuses to execute a module served as HTML and reports
|
|
613
|
+
//
|
|
614
|
+
// TypeError: Failed to fetch dynamically imported module:
|
|
615
|
+
// http://…/src/world.tsx?vgai-mount=19
|
|
616
|
+
//
|
|
617
|
+
// which names the ENTRY (a file that exists), never the deleted file, and
|
|
618
|
+
// carries a `?vgai-mount=N` that grows per attempt. `vgai play` then refuses
|
|
619
|
+
// four times in a row with a message that reads like the server serving a
|
|
620
|
+
// stale mount graph — which is where a probe went looking, and there is no
|
|
621
|
+
// such bug: the unlink watcher fires, `project-script-hmr.ts` stamps the
|
|
622
|
+
// graph, and restoring the file recovers without a restart. The whole defect
|
|
623
|
+
// was this response.
|
|
624
|
+
//
|
|
625
|
+
// Line 3121's `hasUiRegistry` comment already recorded the behavior ("a dev
|
|
626
|
+
// server answers 200 (SPA fallback) for missing files") and worked around it
|
|
627
|
+
// at one call site. This closes the class instead: `/@fs/` is a MODULE
|
|
628
|
+
// TRANSPORT, and a module that does not exist has exactly one honest answer.
|
|
629
|
+
//
|
|
630
|
+
// Deliberately narrow — anything not provably a missing module falls through
|
|
631
|
+
// untouched: only GET/HEAD, only paths contained in the project or engine
|
|
632
|
+
// root, only the `/@fs/` extensions this server already agrees to serve
|
|
633
|
+
// (`isServableFsExtension`), never the private estates (`isServableFsPath`),
|
|
634
|
+
// never Vite's own dependency-optimizer cache (`isViteDepCachePath` — those
|
|
635
|
+
// files are written asynchronously and absence there means "not bundled
|
|
636
|
+
// YET", which is Vite's middleware to answer, not ours), and only when the
|
|
637
|
+
// path genuinely is not on disk. Registered after the production transpiling
|
|
638
|
+
// route above so its extension-resolution runs first.
|
|
639
|
+
router.use('/@fs', async (req: Request, res: Response, next) => {
|
|
640
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
641
|
+
next();
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const absPath = resolve(req.path);
|
|
645
|
+
if (!isPathInside(ctx.projectRoot, absPath) && !isPathInside(engineRoot, absPath)) {
|
|
646
|
+
next();
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const ext = absPath.split('.').pop() ?? '';
|
|
650
|
+
// An extensionless path may still resolve (index files, appended
|
|
651
|
+
// extensions), and a private path must not have its existence confirmed
|
|
652
|
+
// or denied here — both keep their existing behavior. The `stat` is async
|
|
653
|
+
// (like the transpiling route's above) because this sits on the module
|
|
654
|
+
// transport a cold boot drives thousands of requests through.
|
|
655
|
+
if (!isServableFsExtension(ext) || !isServableFsPath(absPath)) {
|
|
656
|
+
next();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
// The optimizer's own output is never "a module the game imports is
|
|
660
|
+
// missing" — mid-run it is simply not written yet. See `isViteDepCachePath`.
|
|
661
|
+
if (isViteDepCachePath(absPath)) {
|
|
662
|
+
next();
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const present = await stat(absPath).then(
|
|
666
|
+
() => true,
|
|
667
|
+
() => false,
|
|
668
|
+
);
|
|
669
|
+
if (present) {
|
|
670
|
+
next();
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
res
|
|
674
|
+
.status(404)
|
|
675
|
+
.type('text/plain')
|
|
676
|
+
.send(
|
|
677
|
+
`vgai: ${absPath} does not exist.\n\n` +
|
|
678
|
+
'A module the running game imports is missing from disk — deleted or renamed, ' +
|
|
679
|
+
'and the import that names it was not updated. This is a 404 rather than the ' +
|
|
680
|
+
"editor's index.html so the failure names the missing file instead of surfacing " +
|
|
681
|
+
'as an opaque "Failed to fetch dynamically imported module" against the entry.\n\n' +
|
|
682
|
+
'Restore the file, or fix the import that still names it; the editor picks the ' +
|
|
683
|
+
'change up without a restart.\n',
|
|
684
|
+
);
|
|
685
|
+
});
|
|
686
|
+
}
|