@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,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server half of creation-site write-back — ownership, path
|
|
3
|
+
* containment, and the read/plan/apply handlers behind `/__ingest-source/*`.
|
|
4
|
+
*
|
|
5
|
+
* The client never decides any of this. It sends an anchor
|
|
6
|
+
* (`{file, line, col}`, project-root-RELATIVE — the string the creation-site
|
|
7
|
+
* registry hands out) plus the value in force and the value the user produced; this
|
|
8
|
+
* module resolves that to a real path inside the project, reads it, runs the
|
|
9
|
+
* pure planner, and hands back exact bytes or a named refusal. Three properties
|
|
10
|
+
* are deliberately server-side and unreachable from the browser:
|
|
11
|
+
*
|
|
12
|
+
* - OWNERSHIP. Whether the base may be written at all, and WHO RECORDS the
|
|
13
|
+
* resulting diff, are facts about the filesystem and about which folder this
|
|
14
|
+
* editor was pointed at. A client cannot be trusted to answer either and is
|
|
15
|
+
* never asked to; it renders whatever {@link ingestSourceOwnership} says,
|
|
16
|
+
* including the reason and the recorder.
|
|
17
|
+
* - CONTAINMENT. {@link resolveIngestSourceFile} refuses any path that escapes
|
|
18
|
+
* the roots an anchor may name, including through `..`, an absolute path, or
|
|
19
|
+
* a symlink (the resolution is `realpath`-based, so a link planted inside a
|
|
20
|
+
* root cannot be used to write outside it). There are TWO such roots and
|
|
21
|
+
* that is not a widening: the opened project, and — only for a file some
|
|
22
|
+
* `vendor/games/<id>.UPSTREAM.lock` actually claims — the repo-vendored
|
|
23
|
+
* home, which is the set this route can write WITH its lock in one gesture.
|
|
24
|
+
* This is the only door, so it is the only place the check has to be right.
|
|
25
|
+
* - THE CHECKSUM GUARD. `apply` refuses a write whose `ifMatchSha` no longer
|
|
26
|
+
* matches the file on disk, which is what makes undo/redo safe against a
|
|
27
|
+
* file the user edited in their own editor meanwhile.
|
|
28
|
+
*
|
|
29
|
+
* WHY A SEPARATE ROUTE FAMILY FROM `/__ui-source/*`. That family's scope guard
|
|
30
|
+
* (`resolveEditableSourceFile`) admits `.tsx` under the project's `src/` and
|
|
31
|
+
* project TypeScript — it is the JSX/R3F authoring door, and its shape encodes
|
|
32
|
+
* that. An ingested game is somebody else's JavaScript, laid out however they
|
|
33
|
+
* laid it out (`src/scripts/sim/city.js`), and it is reachable ONLY through an
|
|
34
|
+
* anchor this editor itself stamped. Widening the JSX door to admit arbitrary
|
|
35
|
+
* `.js` would hand every existing oid-driven endpoint a scope it was never
|
|
36
|
+
* designed for; a second door with its own, narrower key does not.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { createHash } from 'node:crypto';
|
|
40
|
+
import {
|
|
41
|
+
accessSync,
|
|
42
|
+
constants,
|
|
43
|
+
readFileSync,
|
|
44
|
+
realpathSync,
|
|
45
|
+
statSync,
|
|
46
|
+
writeFileSync,
|
|
47
|
+
} from 'node:fs';
|
|
48
|
+
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
49
|
+
import {
|
|
50
|
+
type CreationSiteEditRequest,
|
|
51
|
+
type CreationSiteLiteralReport,
|
|
52
|
+
type CreationSiteLiteralRequest,
|
|
53
|
+
type CreationSiteSurface,
|
|
54
|
+
type CreationSiteWriteScope,
|
|
55
|
+
planCreationSiteEdit,
|
|
56
|
+
planCreationSiteRemoval,
|
|
57
|
+
readCreationSiteLiteral,
|
|
58
|
+
} from '../src/creation-site-edit';
|
|
59
|
+
import {
|
|
60
|
+
type HmrInvalidationGraph,
|
|
61
|
+
staleModuleWarning,
|
|
62
|
+
stampHmrInvalidation,
|
|
63
|
+
} from './project-script-hmr';
|
|
64
|
+
import { isPathInside } from './server-utils';
|
|
65
|
+
import {
|
|
66
|
+
findVendoredTarget,
|
|
67
|
+
settleVendoredWrite,
|
|
68
|
+
LOCKS_DIR as VENDORED_HOME,
|
|
69
|
+
writeRecordedVendoredFile,
|
|
70
|
+
} from './vendored-lock-recorder';
|
|
71
|
+
|
|
72
|
+
/** The JSON response a handler produces (status defaults to 200). */
|
|
73
|
+
export interface HandlerResult {
|
|
74
|
+
readonly status?: number;
|
|
75
|
+
readonly body: unknown;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface IngestSourceOwnership {
|
|
79
|
+
/** May this session write the game's own source at all? */
|
|
80
|
+
readonly writable: boolean;
|
|
81
|
+
/** Always present when `writable` is false — never a blank (honest-floor). */
|
|
82
|
+
readonly reason?: string;
|
|
83
|
+
/** Echoed back so the surface can name the folder it is talking about. */
|
|
84
|
+
readonly projectRoot: string | null;
|
|
85
|
+
/**
|
|
86
|
+
* WHO RECORDS the diff this session's edits produce, in one phrase a surface
|
|
87
|
+
* can show. Present whenever `writable` — the two answers are the user's own
|
|
88
|
+
* version control, and (for a repo-vendored copy) that game's UPSTREAM lock,
|
|
89
|
+
* updated in the same gesture as the write.
|
|
90
|
+
*/
|
|
91
|
+
readonly recorder?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Hashes BYTES. Every digest this route quotes — `ifMatchSha`, the one it
|
|
95
|
+
* returns — has to mean the same thing as the vendored lock's and the
|
|
96
|
+
* verifier's, and those hash the file's bytes. */
|
|
97
|
+
function sha256(bytes: Buffer): string {
|
|
98
|
+
return createHash('sha256').update(bytes).digest('hex');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The wire form of a file's content.
|
|
103
|
+
*
|
|
104
|
+
* Source is text and stays text. But a vendored game's authorable truth is not
|
|
105
|
+
* always source: a level-based game's placed objects live in a binary level
|
|
106
|
+
* file, so the same route has to carry megabytes of binary data, and a JSON
|
|
107
|
+
* string cannot hold it — UTF-8 round-tripping those bytes silently produces a
|
|
108
|
+
* different, longer file. `encoding` is explicit on the wire rather than sniffed, so a
|
|
109
|
+
* client that forgets it gets a wrong SHA and a refusal, not a corrupted game.
|
|
110
|
+
*/
|
|
111
|
+
function decodeSource(source: string, encoding: unknown): Buffer | null {
|
|
112
|
+
if (encoding === 'base64') return Buffer.from(source, 'base64');
|
|
113
|
+
if (encoding === undefined || encoding === 'utf8') return Buffer.from(source, 'utf8');
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Git's own rule: a NUL byte near the front means this is not text. */
|
|
118
|
+
function looksBinary(bytes: Buffer): boolean {
|
|
119
|
+
return bytes.subarray(0, 8000).includes(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function realOrSelf(path: string): string {
|
|
123
|
+
try {
|
|
124
|
+
return realpathSync(path);
|
|
125
|
+
} catch {
|
|
126
|
+
return path;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether an ingest session may write the game's own source, WHO RECORDS the
|
|
132
|
+
* diff, and — when it may not — the sentence the UI shows verbatim.
|
|
133
|
+
*
|
|
134
|
+
* OWNERSHIP DECIDES THE RECORDER, NOT WHETHER TO WRITE. Persistence works the
|
|
135
|
+
* same way it does in the native lane: an authored edit edits the game's own
|
|
136
|
+
* code, and the only thing that varies is where the resulting divergence is
|
|
137
|
+
* accounted for. A folder the user owns records in their version control. A
|
|
138
|
+
* repo-vendored copy records in its own `vendor/games/<id>.UPSTREAM.lock`, in
|
|
139
|
+
* the same server gesture as the write, so the estate's bar — zero UNRECORDED
|
|
140
|
+
* diff, never zero diff — holds at every instant (`vendored-lock-recorder.ts`).
|
|
141
|
+
*
|
|
142
|
+
* What is left here is the filesystem's own answer: a folder has to exist and
|
|
143
|
+
* be writable. Everything else this function used to decide was a policy about
|
|
144
|
+
* whose files these are, and that policy now lives one layer down, in which
|
|
145
|
+
* recorder runs.
|
|
146
|
+
*/
|
|
147
|
+
export function ingestSourceOwnership(
|
|
148
|
+
projectRoot: string | undefined | null,
|
|
149
|
+
engineRoot: string,
|
|
150
|
+
): IngestSourceOwnership {
|
|
151
|
+
if (!projectRoot) {
|
|
152
|
+
return {
|
|
153
|
+
writable: false,
|
|
154
|
+
reason: 'no project folder is open — this session has no source to write',
|
|
155
|
+
projectRoot: null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const root = realOrSelf(resolve(projectRoot));
|
|
159
|
+
try {
|
|
160
|
+
if (!statSync(root).isDirectory()) {
|
|
161
|
+
return { writable: false, reason: 'the project path is not a folder', projectRoot: root };
|
|
162
|
+
}
|
|
163
|
+
accessSync(root, constants.W_OK);
|
|
164
|
+
} catch {
|
|
165
|
+
return { writable: false, reason: 'the project folder is not writable', projectRoot: root };
|
|
166
|
+
}
|
|
167
|
+
return { writable: true, projectRoot: root, recorder: recorderFor(root, engineRoot) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** The one phrase naming who accounts for this session's edits. */
|
|
171
|
+
function recorderFor(projectRoot: string, engineRoot: string): string {
|
|
172
|
+
const vendored = findVendoredTarget(projectRoot, engineRoot);
|
|
173
|
+
return vendored
|
|
174
|
+
? `the vendored game’s own lock (vendor/games/${vendored.id}.UPSTREAM.lock), updated in the same gesture as the write`
|
|
175
|
+
: 'your own version control — these are your project’s files';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Resolve `clean` under `root`, contained on both sides of symlink resolution. */
|
|
179
|
+
function resolveInsideRoot(clean: string, rootDir: string): string | null {
|
|
180
|
+
const root = realOrSelf(resolve(rootDir));
|
|
181
|
+
const candidate = resolve(root, clean);
|
|
182
|
+
if (!isPathInside(root, candidate)) return null;
|
|
183
|
+
let real: string;
|
|
184
|
+
try {
|
|
185
|
+
real = realpathSync(candidate);
|
|
186
|
+
if (!statSync(real).isFile()) return null;
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
// Re-checked AFTER symlink resolution: a link inside the root pointing out of
|
|
191
|
+
// it passes the first check and fails this one.
|
|
192
|
+
return isPathInside(root, real) ? real : null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The absolute path an anchor's root-relative `file` names, or `null` when it
|
|
197
|
+
* escapes every root an anchor may be written against (or does not exist).
|
|
198
|
+
*
|
|
199
|
+
* TWO ROOTS, because an anchor is relative to whichever root OWNS the module,
|
|
200
|
+
* and `vite-plugin-creation-site`'s `creationSiteScopeFor` picks that root from
|
|
201
|
+
* the same set the game-globals shadow uses (`ingestGameShadowRoots`): an
|
|
202
|
+
* opened project, AND the repo-vendored home. So a repo-vendored game's
|
|
203
|
+
* creation site arrives as `<id>/src/...`, relative to `vendor/games` — a path
|
|
204
|
+
* that cannot resolve inside the opened project and, until this, was refused as
|
|
205
|
+
* "not a file inside this project". MEASURED on the bubbo-bubbo ingest: every
|
|
206
|
+
* edit to that game stayed live-only with that reason, which was TRUE and
|
|
207
|
+
* beside the point — the file is not the project's, it is the vendored game's,
|
|
208
|
+
* and `vendored-lock-recorder.ts` exists precisely to write it.
|
|
209
|
+
*
|
|
210
|
+
* The vendored leg's containment is not a path prefix but the RECORDER's own
|
|
211
|
+
* answer: `findVendoredTarget` must claim the resolved file, i.e. some
|
|
212
|
+
* `vendor/games/<id>.UPSTREAM.lock` covers it. That is strictly stronger than
|
|
213
|
+
* "under vendor/games", because it is exactly the set of files this route can
|
|
214
|
+
* write WITH their lock in the same gesture (`handleIngestApply`) — a file the
|
|
215
|
+
* recorder does not claim can never be written here, so it is never resolved
|
|
216
|
+
* here either.
|
|
217
|
+
*
|
|
218
|
+
* Every rejection is the same `null` on purpose: a client that can distinguish
|
|
219
|
+
* "outside the project" from "does not exist" can probe the host's filesystem
|
|
220
|
+
* one guess at a time.
|
|
221
|
+
*/
|
|
222
|
+
export function resolveIngestSourceFile(
|
|
223
|
+
file: unknown,
|
|
224
|
+
projectRoot: string,
|
|
225
|
+
engineRoot?: string,
|
|
226
|
+
): string | null {
|
|
227
|
+
if (typeof file !== 'string' || file.length === 0) return null;
|
|
228
|
+
const clean = file.split('?')[0] ?? file;
|
|
229
|
+
// An ANCHOR is always root-relative — an absolute path did not come from the
|
|
230
|
+
// registry, so it is refused rather than resolved.
|
|
231
|
+
if (isAbsolute(clean)) return null;
|
|
232
|
+
const inProject = resolveInsideRoot(clean, projectRoot);
|
|
233
|
+
if (inProject) return inProject;
|
|
234
|
+
if (engineRoot === undefined) return null;
|
|
235
|
+
const inVendored = resolveInsideRoot(clean, join(engineRoot, ...VENDORED_HOME));
|
|
236
|
+
return inVendored && findVendoredTarget(inVendored, engineRoot) ? inVendored : null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Complete any vendored write that died between its own artifacts, BEFORE this
|
|
241
|
+
* request reads the file. A journalled write is finished by the next request
|
|
242
|
+
* that touches the game, which is why every route calls this and not just the
|
|
243
|
+
* writing one — planning an edit against half-written bytes would produce a
|
|
244
|
+
* plan that is wrong in a way no later check could catch.
|
|
245
|
+
*
|
|
246
|
+
* When it does roll bytes forward it stamps the same invalidation a write of
|
|
247
|
+
* our own stamps: settling REPLACES the file's content, so a dev server that
|
|
248
|
+
* kept serving the pre-settle module would hand the next mount source that no
|
|
249
|
+
* longer exists on disk — the defect `invalidateWrittenSource` above was added
|
|
250
|
+
* for, reaching the graph through the settle instead of through the write.
|
|
251
|
+
*/
|
|
252
|
+
function settlePendingRecord(resolvedFile: string, engineRoot: string): void {
|
|
253
|
+
const vendored = findVendoredTarget(resolvedFile, engineRoot);
|
|
254
|
+
if (vendored && settleVendoredWrite(vendored)) invalidateWrittenSource(resolvedFile);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Stable identity for the history registry (mirrors the `/__ui-source` family's
|
|
259
|
+
* `sourceResourcePath`).
|
|
260
|
+
*
|
|
261
|
+
* A vendored game's file is not under the project root, so a plain `relative()`
|
|
262
|
+
* would key it by a ladder of `..` segments that describes the host's directory
|
|
263
|
+
* layout rather than the file. It is keyed by its position in the vendored home
|
|
264
|
+
* instead — the same `<id>/…` string the creation-site anchor already uses, so
|
|
265
|
+
* one file has one identity whichever route reached it.
|
|
266
|
+
*/
|
|
267
|
+
function resourcePathOf(file: string, projectRoot: string, engineRoot: string): string {
|
|
268
|
+
const vendorHome = realOrSelf(resolve(join(engineRoot, ...VENDORED_HOME)));
|
|
269
|
+
const root = isPathInside(vendorHome, file) ? vendorHome : realOrSelf(resolve(projectRoot));
|
|
270
|
+
return relative(root, file).split(sep).join('/');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** The wire shape of a prepare request — the anchor plus the two values.
|
|
274
|
+
* `remove: true` plans a REMOVAL instead (`planCreationSiteRemoval`): `next`
|
|
275
|
+
* is absent because absence is not a value, and the same route carries it so
|
|
276
|
+
* ownership, path containment and the checksum-guarded apply are literally
|
|
277
|
+
* the same code for either direction. */
|
|
278
|
+
interface PrepareBody {
|
|
279
|
+
readonly site?: { file?: unknown; line?: unknown; col?: unknown };
|
|
280
|
+
readonly instances?: unknown;
|
|
281
|
+
readonly writeScope?: unknown;
|
|
282
|
+
readonly property?: unknown;
|
|
283
|
+
readonly surface?: unknown;
|
|
284
|
+
readonly baseline?: unknown;
|
|
285
|
+
readonly next?: unknown;
|
|
286
|
+
readonly remove?: unknown;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function writeScopeOf(value: unknown): CreationSiteWriteScope | 'invalid' {
|
|
290
|
+
if (value === undefined || value === 'instance') return 'instance';
|
|
291
|
+
if (value === 'creation-site') return 'creation-site';
|
|
292
|
+
return 'invalid';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Which surface vocabulary the client's `property` is in — validated here rather
|
|
297
|
+
* than cast, because it selects the CHANNEL TABLE and an unrecognized value must
|
|
298
|
+
* not silently fall through to the other surface's meaning of the same word
|
|
299
|
+
* (`position` is a 3-vector on three and a 2-vector on canvas).
|
|
300
|
+
*/
|
|
301
|
+
function surfaceOf(value: unknown): CreationSiteSurface | 'invalid' {
|
|
302
|
+
if (value === undefined || value === 'three') return 'three';
|
|
303
|
+
if (value === 'pixi' || value === 'babylon') return value;
|
|
304
|
+
return 'invalid';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface PreparedCreationSiteEdit {
|
|
308
|
+
readonly changed: boolean;
|
|
309
|
+
readonly file?: string;
|
|
310
|
+
readonly resourcePath?: string;
|
|
311
|
+
readonly prevSource?: string;
|
|
312
|
+
readonly newSource?: string;
|
|
313
|
+
readonly prevSha?: string;
|
|
314
|
+
readonly newSha?: string;
|
|
315
|
+
/** Present iff `!changed` — the named refusal. */
|
|
316
|
+
readonly reason?: string;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* POST `/__ingest-source/prepare` — plan one property edit against the game's
|
|
321
|
+
* own source. Reads, never writes.
|
|
322
|
+
*
|
|
323
|
+
* The planning itself is `planCreationSiteEdit`, which is pure and shared with
|
|
324
|
+
* the unit tests, so what a test proves is what the server does.
|
|
325
|
+
*/
|
|
326
|
+
export function handleIngestPrepare(
|
|
327
|
+
body: PrepareBody,
|
|
328
|
+
projectRoot: string | undefined | null,
|
|
329
|
+
engineRoot: string,
|
|
330
|
+
): HandlerResult {
|
|
331
|
+
const ownership = ingestSourceOwnership(projectRoot, engineRoot);
|
|
332
|
+
if (!ownership.writable || !ownership.projectRoot) {
|
|
333
|
+
return { status: 403, body: { changed: false, reason: ownership.reason } };
|
|
334
|
+
}
|
|
335
|
+
const site = body.site ?? {};
|
|
336
|
+
const surface = surfaceOf(body.surface);
|
|
337
|
+
const writeScope = writeScopeOf(body.writeScope);
|
|
338
|
+
if (
|
|
339
|
+
typeof site.file !== 'string' ||
|
|
340
|
+
typeof site.line !== 'number' ||
|
|
341
|
+
typeof site.col !== 'number' ||
|
|
342
|
+
typeof body.property !== 'string' ||
|
|
343
|
+
typeof body.instances !== 'number' ||
|
|
344
|
+
surface === 'invalid' ||
|
|
345
|
+
writeScope === 'invalid'
|
|
346
|
+
) {
|
|
347
|
+
return {
|
|
348
|
+
status: 400,
|
|
349
|
+
body: { changed: false, reason: 'malformed creation-site edit request' },
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const resolved = resolveIngestSourceFile(site.file, ownership.projectRoot, engineRoot);
|
|
353
|
+
if (!resolved) {
|
|
354
|
+
return {
|
|
355
|
+
status: 403,
|
|
356
|
+
body: { changed: false, reason: 'the creation site is not a file inside this project' },
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
settlePendingRecord(resolved, engineRoot);
|
|
360
|
+
const source = readFileSync(resolved, 'utf8');
|
|
361
|
+
const plan =
|
|
362
|
+
body.remove === true
|
|
363
|
+
? planCreationSiteRemoval(source, {
|
|
364
|
+
site: { file: site.file, line: site.line, col: site.col },
|
|
365
|
+
instances: body.instances,
|
|
366
|
+
writeScope,
|
|
367
|
+
property: body.property,
|
|
368
|
+
surface,
|
|
369
|
+
})
|
|
370
|
+
: planCreationSiteEdit(source, {
|
|
371
|
+
site: { file: site.file, line: site.line, col: site.col },
|
|
372
|
+
instances: body.instances,
|
|
373
|
+
writeScope,
|
|
374
|
+
property: body.property,
|
|
375
|
+
surface,
|
|
376
|
+
baseline: body.baseline as CreationSiteEditRequest['baseline'],
|
|
377
|
+
next: body.next as CreationSiteEditRequest['next'],
|
|
378
|
+
});
|
|
379
|
+
if (!plan.changed) {
|
|
380
|
+
return { body: { changed: false, reason: plan.reason ?? 'the edit was refused' } };
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
body: {
|
|
384
|
+
changed: true,
|
|
385
|
+
file: site.file,
|
|
386
|
+
resourcePath: resourcePathOf(resolved, ownership.projectRoot, engineRoot),
|
|
387
|
+
prevSource: plan.prevSource,
|
|
388
|
+
newSource: plan.newSource,
|
|
389
|
+
prevSha: sha256(Buffer.from(plan.prevSource, 'utf8')),
|
|
390
|
+
newSha: sha256(Buffer.from(plan.newSource, 'utf8')),
|
|
391
|
+
} satisfies PreparedCreationSiteEdit,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** The wire shape of an inspect request — one anchor, many properties, each
|
|
396
|
+
* with the value in force on the live object. */
|
|
397
|
+
interface InspectBody {
|
|
398
|
+
readonly site?: { file?: unknown; line?: unknown; col?: unknown };
|
|
399
|
+
readonly instances?: unknown;
|
|
400
|
+
readonly writeScope?: unknown;
|
|
401
|
+
readonly surface?: unknown;
|
|
402
|
+
readonly properties?: unknown;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* POST `/__ingest-source/inspect` — what the construction statement SAYS each of
|
|
407
|
+
* these properties is, so the inspector can show the live object's value as an
|
|
408
|
+
* override of the site's own literal. Reads, never writes, never plans.
|
|
409
|
+
*
|
|
410
|
+
* ONE ROUND TRIP FOR THE WHOLE SUBJECT, not one per property: the file is read
|
|
411
|
+
* and parsed once per request, and a per-property route would re-parse the
|
|
412
|
+
* game's source for every row the inspector draws.
|
|
413
|
+
*
|
|
414
|
+
* It is the same `readCreationSiteLiteral` the unit tests drive, for the same
|
|
415
|
+
* reason `prepare` is `planCreationSiteEdit`: what a test proves is what the
|
|
416
|
+
* server does. The route is deliberately NOT gated on the session's "persist to
|
|
417
|
+
* game source" consent — reading what a line of the game says is a read, and the
|
|
418
|
+
* write consent is checked where a byte would actually move.
|
|
419
|
+
*/
|
|
420
|
+
export function handleIngestInspect(
|
|
421
|
+
body: InspectBody,
|
|
422
|
+
projectRoot: string | undefined | null,
|
|
423
|
+
engineRoot: string,
|
|
424
|
+
): HandlerResult {
|
|
425
|
+
const ownership = ingestSourceOwnership(projectRoot, engineRoot);
|
|
426
|
+
if (!ownership.writable || !ownership.projectRoot) {
|
|
427
|
+
return { status: 403, body: { error: ownership.reason } };
|
|
428
|
+
}
|
|
429
|
+
const site = body.site ?? {};
|
|
430
|
+
const surface = surfaceOf(body.surface);
|
|
431
|
+
const writeScope = writeScopeOf(body.writeScope);
|
|
432
|
+
if (
|
|
433
|
+
typeof site.file !== 'string' ||
|
|
434
|
+
typeof site.line !== 'number' ||
|
|
435
|
+
typeof site.col !== 'number' ||
|
|
436
|
+
typeof body.instances !== 'number' ||
|
|
437
|
+
!Array.isArray(body.properties) ||
|
|
438
|
+
surface === 'invalid' ||
|
|
439
|
+
writeScope === 'invalid'
|
|
440
|
+
) {
|
|
441
|
+
return { status: 400, body: { error: 'malformed creation-site inspect request' } };
|
|
442
|
+
}
|
|
443
|
+
const resolved = resolveIngestSourceFile(site.file, ownership.projectRoot, engineRoot);
|
|
444
|
+
if (!resolved) {
|
|
445
|
+
return { status: 403, body: { error: 'the creation site is not a file inside this project' } };
|
|
446
|
+
}
|
|
447
|
+
settlePendingRecord(resolved, engineRoot);
|
|
448
|
+
const source = readFileSync(resolved, 'utf8');
|
|
449
|
+
const properties: Record<string, CreationSiteLiteralReport> = {};
|
|
450
|
+
for (const entry of body.properties as ReadonlyArray<{ property?: unknown; live?: unknown }>) {
|
|
451
|
+
if (typeof entry?.property !== 'string') continue;
|
|
452
|
+
properties[entry.property] = readCreationSiteLiteral(source, {
|
|
453
|
+
site: { file: site.file, line: site.line, col: site.col },
|
|
454
|
+
instances: body.instances,
|
|
455
|
+
writeScope,
|
|
456
|
+
property: entry.property,
|
|
457
|
+
surface,
|
|
458
|
+
live: entry.live as CreationSiteLiteralRequest['live'],
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
return { body: { file: site.file, properties } };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** POST `/__ingest-source/read` — one scope-checked file, for history capture
|
|
465
|
+
* and the undo/redo preflight. */
|
|
466
|
+
export function handleIngestRead(
|
|
467
|
+
body: { file?: unknown },
|
|
468
|
+
projectRoot: string | undefined | null,
|
|
469
|
+
engineRoot: string,
|
|
470
|
+
): HandlerResult {
|
|
471
|
+
const ownership = ingestSourceOwnership(projectRoot, engineRoot);
|
|
472
|
+
if (!ownership.writable || !ownership.projectRoot) {
|
|
473
|
+
return { status: 403, body: { error: ownership.reason } };
|
|
474
|
+
}
|
|
475
|
+
const resolved = resolveIngestSourceFile(body.file, ownership.projectRoot, engineRoot);
|
|
476
|
+
if (!resolved) return { status: 403, body: { error: 'file out of project scope' } };
|
|
477
|
+
settlePendingRecord(resolved, engineRoot);
|
|
478
|
+
const bytes = readFileSync(resolved);
|
|
479
|
+
// Binary comes back base64-tagged so the caller can hand the SAME bytes back
|
|
480
|
+
// through `apply` for undo — which is the only thing that makes undo of a
|
|
481
|
+
// level-data edit byte-exact.
|
|
482
|
+
const binary = looksBinary(bytes);
|
|
483
|
+
return {
|
|
484
|
+
body: {
|
|
485
|
+
source: binary ? bytes.toString('base64') : bytes.toString('utf8'),
|
|
486
|
+
encoding: binary ? 'base64' : 'utf8',
|
|
487
|
+
sha: sha256(bytes),
|
|
488
|
+
resourcePath: resourcePathOf(resolved, ownership.projectRoot, engineRoot),
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* THE MODULE GRAPH `handleIngestApply` MUST INVALIDATE — and why the editor
|
|
495
|
+
* cannot leave that to the file watcher.
|
|
496
|
+
*
|
|
497
|
+
* The editor writes the ingested game's own bytes itself, so making that write
|
|
498
|
+
* visible to the next mount is its own job. Vite serves a source module from
|
|
499
|
+
* `ModuleGraph.transformResult` until something invalidates it, and the only
|
|
500
|
+
* thing that ever did here was the filesystem watcher's change event. Where the
|
|
501
|
+
* watcher is not watching, the write lands on disk, the ack is honest, the diff
|
|
502
|
+
* is real — and the next mount re-derives the game from the PRE-EDIT bytes.
|
|
503
|
+
*
|
|
504
|
+
* That is not a hypothetical environment: the root config's
|
|
505
|
+
* `server.watch.ignored` carries a `.claude` glob (it stops one agent
|
|
506
|
+
* worktree's churn reloading every OTHER live session's tab), so every project
|
|
507
|
+
* opened from inside a worktree gets no watcher event for its own source at
|
|
508
|
+
* all. Measured on the `/__ui-source/*` family first — an authored
|
|
509
|
+
* `position={[0,1.5,0]}` read back as `[0,0,0]` after a reload — and closed
|
|
510
|
+
* there by `vite-plugin-ui-oid.ts`'s `invalidateWrittenSource`. This is the
|
|
511
|
+
* same defect on the ingest lane's own write door, closed the same way. A write
|
|
512
|
+
* path may not depend on an ignore list it does not own.
|
|
513
|
+
*
|
|
514
|
+
* Graph-only, deliberately: this stamps invalidation exactly the way
|
|
515
|
+
* `handleProjectScriptHotUpdate` does when it swallows Vite's HMR, and sends no
|
|
516
|
+
* client event, so hot-update behaviour on a watched project is untouched (its
|
|
517
|
+
* watcher event still arrives and still drives the custom events) and stamping
|
|
518
|
+
* twice is idempotent.
|
|
519
|
+
*/
|
|
520
|
+
let writtenSourceInvalidationGraph: HmrInvalidationGraph | undefined;
|
|
521
|
+
let writtenSourceStampBoundary: ((moduleFile: string) => boolean) | undefined;
|
|
522
|
+
|
|
523
|
+
/** Called once by `creationSiteWritePlugin`'s `configureServer` — these routes
|
|
524
|
+
* are mounted on exactly one dev server. `stampBoundary` bounds the stamp's
|
|
525
|
+
* importer propagation at editor-chrome modules (see
|
|
526
|
+
* `project-script-hmr.ts`'s `stampHmrInvalidation` doc for the
|
|
527
|
+
* split-singleton failure an unbounded walk shipped): a written GAME module
|
|
528
|
+
* must re-evaluate on the next mount, and the editor modules that imported
|
|
529
|
+
* it must not be re-instanced mid-session. */
|
|
530
|
+
export function bindIngestWriteInvalidation(
|
|
531
|
+
graph: HmrInvalidationGraph,
|
|
532
|
+
stampBoundary?: (moduleFile: string) => boolean,
|
|
533
|
+
): void {
|
|
534
|
+
writtenSourceInvalidationGraph = graph;
|
|
535
|
+
writtenSourceStampBoundary = stampBoundary;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function invalidateWrittenSource(file: string): void {
|
|
539
|
+
if (!writtenSourceInvalidationGraph) return;
|
|
540
|
+
const outcome = stampHmrInvalidation(
|
|
541
|
+
writtenSourceInvalidationGraph,
|
|
542
|
+
file,
|
|
543
|
+
undefined,
|
|
544
|
+
writtenSourceStampBoundary,
|
|
545
|
+
);
|
|
546
|
+
const warning = staleModuleWarning(file, outcome);
|
|
547
|
+
if (warning) console.warn(warning);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* POST `/__ingest-source/apply` — the guarded whole-file write used by commit,
|
|
552
|
+
* undo and redo.
|
|
553
|
+
*
|
|
554
|
+
* `ifMatchSha` is not optional and there is no force: a stale digest means the
|
|
555
|
+
* file changed under us, and the only honest response is to refuse and say so.
|
|
556
|
+
*
|
|
557
|
+
* THE RECORDER RUNS INSIDE THIS ROUTE, not beside it. When the target is a
|
|
558
|
+
* repo-vendored game, the bytes and that game's lock move in ONE operation
|
|
559
|
+
* (`writeRecordedVendoredFile`), so there is no instant at which the file has
|
|
560
|
+
* changed and its lock has not. Undo needs no counterpart: it comes back
|
|
561
|
+
* through this same route with the previous bytes, and the recorder reconciles
|
|
562
|
+
* to whatever those bytes are — including all the way back to upstream's own,
|
|
563
|
+
* which removes the record entirely.
|
|
564
|
+
*
|
|
565
|
+
* `note` is an optional hint from the client naming the edit (the property and
|
|
566
|
+
* the creation site) for the generated record. It is a HINT: the record's
|
|
567
|
+
* truth — the diff and both shas — is derived from the bytes on either side, so
|
|
568
|
+
* an absent or stale note can make the prose less specific and can never make
|
|
569
|
+
* it wrong.
|
|
570
|
+
*/
|
|
571
|
+
export function handleIngestApply(
|
|
572
|
+
body: {
|
|
573
|
+
file?: unknown;
|
|
574
|
+
source?: unknown;
|
|
575
|
+
encoding?: unknown;
|
|
576
|
+
ifMatchSha?: unknown;
|
|
577
|
+
note?: unknown;
|
|
578
|
+
},
|
|
579
|
+
projectRoot: string | undefined | null,
|
|
580
|
+
engineRoot: string,
|
|
581
|
+
): HandlerResult {
|
|
582
|
+
const ownership = ingestSourceOwnership(projectRoot, engineRoot);
|
|
583
|
+
if (!ownership.writable || !ownership.projectRoot) {
|
|
584
|
+
return { status: 403, body: { applied: false, error: ownership.reason } };
|
|
585
|
+
}
|
|
586
|
+
const resolved = resolveIngestSourceFile(body.file, ownership.projectRoot, engineRoot);
|
|
587
|
+
if (!resolved)
|
|
588
|
+
return { status: 403, body: { applied: false, error: 'file out of project scope' } };
|
|
589
|
+
if (typeof body.source !== 'string' || typeof body.ifMatchSha !== 'string') {
|
|
590
|
+
return { status: 400, body: { applied: false, error: 'malformed apply request' } };
|
|
591
|
+
}
|
|
592
|
+
const next = decodeSource(body.source, body.encoding);
|
|
593
|
+
if (next === null) {
|
|
594
|
+
return {
|
|
595
|
+
status: 400,
|
|
596
|
+
body: {
|
|
597
|
+
applied: false,
|
|
598
|
+
error: `unknown source encoding ${String(body.encoding)} — expected "utf8" or "base64"`,
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
settlePendingRecord(resolved, engineRoot);
|
|
603
|
+
const vendored = findVendoredTarget(resolved, engineRoot);
|
|
604
|
+
const current = readFileSync(resolved);
|
|
605
|
+
// Every path below this line reads `current`, which is now settled.
|
|
606
|
+
const currentSha = sha256(current);
|
|
607
|
+
if (currentSha !== body.ifMatchSha) {
|
|
608
|
+
return {
|
|
609
|
+
status: 409,
|
|
610
|
+
body: {
|
|
611
|
+
applied: false,
|
|
612
|
+
error: 'the file changed on disk since this edit was planned',
|
|
613
|
+
sha: currentSha,
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
if (vendored) {
|
|
618
|
+
const outcome = writeRecordedVendoredFile(
|
|
619
|
+
vendored,
|
|
620
|
+
next,
|
|
621
|
+
typeof body.note === 'string' ? body.note : undefined,
|
|
622
|
+
);
|
|
623
|
+
if (!outcome.ok) {
|
|
624
|
+
// The lock could not be brought along, so the bytes do not move either:
|
|
625
|
+
// an unrecordable edit to a vendored game is refused, never written.
|
|
626
|
+
return {
|
|
627
|
+
status: 409,
|
|
628
|
+
body: {
|
|
629
|
+
applied: false,
|
|
630
|
+
error: `the edit was not written — its divergence could not be recorded in ${vendored.id}'s lock: ${outcome.error}`,
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
invalidateWrittenSource(resolved);
|
|
635
|
+
return { body: { applied: true, sha: sha256(next), recorded: outcome.recorded } };
|
|
636
|
+
}
|
|
637
|
+
writeFileSync(resolved, next);
|
|
638
|
+
invalidateWrittenSource(resolved);
|
|
639
|
+
return { body: { applied: true, sha: sha256(next) } };
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** GET `/__ingest-source/ownership`. */
|
|
643
|
+
export function handleIngestOwnership(
|
|
644
|
+
projectRoot: string | undefined | null,
|
|
645
|
+
engineRoot: string,
|
|
646
|
+
): HandlerResult {
|
|
647
|
+
return { body: ingestSourceOwnership(projectRoot, engineRoot) };
|
|
648
|
+
}
|