@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,679 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic persistence boundary for files produced by project tools.
|
|
3
|
+
*
|
|
4
|
+
* Generators remain ordinary project/package code. They hand their completed
|
|
5
|
+
* bytes to this writer only when they are ready to commit; the editor host
|
|
6
|
+
* validates every path, stages the whole batch, and rolls the batch back if
|
|
7
|
+
* any rename fails. A half-generated output set must never become
|
|
8
|
+
* visible to the asset watcher.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
12
|
+
import { createReadStream } from 'node:fs';
|
|
13
|
+
import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
14
|
+
import { dirname, extname, posix, relative, resolve, sep } from 'node:path';
|
|
15
|
+
import { isContainedRelativePath } from '@volter/editor-sdk/session/relative-path-guard';
|
|
16
|
+
import type { GenerationJob } from '@volter/editor-sdk/generations';
|
|
17
|
+
import { normalizeProjectOutputPath } from '@volter/editor-sdk/project/output-roots';
|
|
18
|
+
import {
|
|
19
|
+
emptyProjectProvenanceDocument,
|
|
20
|
+
PROJECT_ATTEST_OPERATION,
|
|
21
|
+
PROJECT_PROVENANCE_PATH,
|
|
22
|
+
ProjectProvenanceDocumentSchema,
|
|
23
|
+
type ProjectProvenanceExecution,
|
|
24
|
+
ProjectProvenanceOperationSchema,
|
|
25
|
+
} from './support/project/provenance';
|
|
26
|
+
import type {
|
|
27
|
+
ProjectGeneratedOutput,
|
|
28
|
+
ProjectGeneratedOutputFile,
|
|
29
|
+
ProjectOutputFile,
|
|
30
|
+
ProjectOutputProvenanceContext,
|
|
31
|
+
ProjectOutputWriter,
|
|
32
|
+
} from '@volter/editor-sdk/tools/types';
|
|
33
|
+
import { withExclusiveLock } from '../src/asset-workflow/ledger-write-lock';
|
|
34
|
+
import { ledgerLockIo } from './asset-ledger-store';
|
|
35
|
+
import { readGenerationJobs } from './generation-jobs';
|
|
36
|
+
import { consumeGenerativeExecutions } from './generative-execution-context';
|
|
37
|
+
import { sanitizeRecordedValue } from './redact-secrets';
|
|
38
|
+
|
|
39
|
+
type NormalizedOutputFile = ProjectOutputFile & { path: string };
|
|
40
|
+
|
|
41
|
+
interface HashedProjectFile extends ProjectGeneratedOutputFile {
|
|
42
|
+
sha256: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const projectCommitQueues = new Map<string, Promise<unknown>>();
|
|
46
|
+
|
|
47
|
+
function withCapturedExecutions(
|
|
48
|
+
provenanceContext: ProjectOutputProvenanceContext,
|
|
49
|
+
): ProjectOutputProvenanceContext {
|
|
50
|
+
const captured = consumeGenerativeExecutions();
|
|
51
|
+
const explicit = provenanceContext.executions
|
|
52
|
+
? [...provenanceContext.executions]
|
|
53
|
+
: provenanceContext.execution
|
|
54
|
+
? [provenanceContext.execution]
|
|
55
|
+
: [];
|
|
56
|
+
if (
|
|
57
|
+
captured.length > 0 &&
|
|
58
|
+
explicit.length > 0 &&
|
|
59
|
+
JSON.stringify(captured) !== JSON.stringify(explicit)
|
|
60
|
+
) {
|
|
61
|
+
throw new Error('Explicit and automatically captured provider execution facts disagree.');
|
|
62
|
+
}
|
|
63
|
+
const executions = captured.length > 0 ? captured : explicit;
|
|
64
|
+
const effective = { ...provenanceContext };
|
|
65
|
+
delete effective.execution;
|
|
66
|
+
delete effective.executions;
|
|
67
|
+
if (executions.length === 1) effective.execution = executions[0]!;
|
|
68
|
+
if (executions.length > 1) effective.executions = executions;
|
|
69
|
+
return effective;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function withGenerationBilling(
|
|
73
|
+
projectRoot: string,
|
|
74
|
+
provenanceContext: ProjectOutputProvenanceContext,
|
|
75
|
+
): Promise<ProjectOutputProvenanceContext> {
|
|
76
|
+
const executions = provenanceContext.executions
|
|
77
|
+
? [...provenanceContext.executions]
|
|
78
|
+
: provenanceContext.execution
|
|
79
|
+
? [provenanceContext.execution]
|
|
80
|
+
: [];
|
|
81
|
+
if (executions.length === 0) return provenanceContext;
|
|
82
|
+
let jobs: GenerationJob[];
|
|
83
|
+
try {
|
|
84
|
+
jobs = (await readGenerationJobs(projectRoot)).jobs;
|
|
85
|
+
} catch {
|
|
86
|
+
// A direct callable may never have registered a job, and a damaged
|
|
87
|
+
// operational ledger must not strand otherwise valid accepted bytes.
|
|
88
|
+
return provenanceContext;
|
|
89
|
+
}
|
|
90
|
+
const enriched = executions.map((execution) => {
|
|
91
|
+
const externalId = execution.requestId ?? execution.taskId;
|
|
92
|
+
if (!externalId) return execution;
|
|
93
|
+
const job = jobs.find(
|
|
94
|
+
(candidate) =>
|
|
95
|
+
candidate.provider === execution.provider && candidate.externalId === externalId,
|
|
96
|
+
);
|
|
97
|
+
return job ? { ...execution, billing: job.billing } : execution;
|
|
98
|
+
});
|
|
99
|
+
const effective = { ...provenanceContext };
|
|
100
|
+
delete effective.execution;
|
|
101
|
+
delete effective.executions;
|
|
102
|
+
if (enriched.length === 1) effective.execution = enriched[0]!;
|
|
103
|
+
if (enriched.length > 1) effective.executions = enriched;
|
|
104
|
+
return effective;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Project-relative, contained, and under one of the writable output roots —
|
|
109
|
+
* `public/` (shipped game assets) or `references/` (reference material the
|
|
110
|
+
* editor's Content panel indexes but no export copies). The root rule itself
|
|
111
|
+
* lives in `@vgai/sdk/output-roots` because provider boundaries enforce the
|
|
112
|
+
* same one; `isContainedRelativePath` stays here as the host's own escape
|
|
113
|
+
* check, which knows about absolute Windows paths and NUL bytes.
|
|
114
|
+
*/
|
|
115
|
+
function normalizeOutputPath(path: string): string {
|
|
116
|
+
const slashPath = path.split(sep).join('/');
|
|
117
|
+
const normalized = posix.normalize(slashPath).replace(/^\.\//, '');
|
|
118
|
+
if (!isContainedRelativePath(normalized)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Generated output path ${JSON.stringify(path)} is invalid; outputs must be project-relative files.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return normalizeProjectOutputPath(normalized);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function byteLength(content: string | Uint8Array): number {
|
|
127
|
+
return typeof content === 'string' ? Buffer.byteLength(content) : content.byteLength;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function sha256(content: string | Uint8Array): string {
|
|
131
|
+
return createHash('sha256').update(content).digest('hex');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function sha256File(path: string): Promise<string> {
|
|
135
|
+
const hash = createHash('sha256');
|
|
136
|
+
for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);
|
|
137
|
+
return hash.digest('hex');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function existingFile(path: string): Promise<boolean> {
|
|
141
|
+
try {
|
|
142
|
+
const info = await stat(path);
|
|
143
|
+
if (!info.isFile()) throw new Error(`Generated output target is not a file: ${path}`);
|
|
144
|
+
return true;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return false;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function validateBatch(files: readonly ProjectOutputFile[]): {
|
|
152
|
+
normalized: NormalizedOutputFile[];
|
|
153
|
+
summaries: ProjectGeneratedOutputFile[];
|
|
154
|
+
totalBytes: number;
|
|
155
|
+
} {
|
|
156
|
+
if (files.length === 0) throw new Error('Generated output batch must contain at least one file.');
|
|
157
|
+
const normalized = files.map((file) => ({ ...file, path: normalizeOutputPath(file.path) }));
|
|
158
|
+
const paths = new Set<string>();
|
|
159
|
+
let totalBytes = 0;
|
|
160
|
+
for (const file of normalized) {
|
|
161
|
+
if (paths.has(file.path)) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Generated output batch contains duplicate path ${JSON.stringify(file.path)}.`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
paths.add(file.path);
|
|
167
|
+
totalBytes += byteLength(file.content);
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
normalized,
|
|
171
|
+
totalBytes,
|
|
172
|
+
summaries: normalized.map((file) => ({
|
|
173
|
+
path: file.path,
|
|
174
|
+
bytes: byteLength(file.content),
|
|
175
|
+
...(file.mediaType ? { mediaType: file.mediaType } : {}),
|
|
176
|
+
...(file.role ? { role: file.role } : {}),
|
|
177
|
+
})),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function stageBatch(stagedRoot: string, files: readonly NormalizedOutputFile[]) {
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
const staged = resolve(stagedRoot, file.path);
|
|
184
|
+
await mkdir(dirname(staged), { recursive: true });
|
|
185
|
+
await writeFile(staged, file.content);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function commitBatch(
|
|
190
|
+
projectRoot: string,
|
|
191
|
+
stagedRoot: string,
|
|
192
|
+
backupRoot: string,
|
|
193
|
+
files: readonly NormalizedOutputFile[],
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
const committed: Array<{ target: string; backup: string | null }> = [];
|
|
196
|
+
try {
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
const staged = resolve(stagedRoot, file.path);
|
|
199
|
+
const target = resolve(projectRoot, file.path);
|
|
200
|
+
const backup = resolve(backupRoot, file.path);
|
|
201
|
+
await mkdir(dirname(target), { recursive: true });
|
|
202
|
+
let prior: string | null = null;
|
|
203
|
+
if (await existingFile(target)) {
|
|
204
|
+
await mkdir(dirname(backup), { recursive: true });
|
|
205
|
+
await rename(target, backup);
|
|
206
|
+
prior = backup;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
await rename(staged, target);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (prior) await rename(prior, target);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
committed.push({ target, backup: prior });
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
for (const entry of committed.reverse()) {
|
|
218
|
+
await rm(entry.target, { force: true });
|
|
219
|
+
if (entry.backup) await rename(entry.backup, entry.target);
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function readProvenance(projectRoot: string) {
|
|
226
|
+
try {
|
|
227
|
+
const parsed: unknown = JSON.parse(
|
|
228
|
+
await readFile(resolve(projectRoot, PROJECT_PROVENANCE_PATH), 'utf8'),
|
|
229
|
+
);
|
|
230
|
+
return ProjectProvenanceDocumentSchema.parse(parsed);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (!(error instanceof Error) || !('code' in error) || error.code !== 'ENOENT') throw error;
|
|
233
|
+
return emptyProjectProvenanceDocument();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function jsonSafeInput(input: unknown): unknown {
|
|
238
|
+
if (input === undefined) return undefined;
|
|
239
|
+
return sanitizeRecordedValue(input, new WeakSet());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function provenanceOperation(
|
|
243
|
+
provenanceContext: ProjectOutputProvenanceContext,
|
|
244
|
+
outputs: readonly HashedProjectFile[],
|
|
245
|
+
) {
|
|
246
|
+
return ProjectProvenanceOperationSchema.parse({
|
|
247
|
+
createdAt: new Date().toISOString(),
|
|
248
|
+
operation: {
|
|
249
|
+
name: provenanceContext.operationName,
|
|
250
|
+
...(provenanceContext.operationSource ? { source: provenanceContext.operationSource } : {}),
|
|
251
|
+
},
|
|
252
|
+
...(provenanceContext.execution ? { execution: provenanceContext.execution } : {}),
|
|
253
|
+
...(provenanceContext.executions ? { executions: provenanceContext.executions } : {}),
|
|
254
|
+
...(provenanceContext.session ? { session: provenanceContext.session } : {}),
|
|
255
|
+
...(provenanceContext.input === undefined
|
|
256
|
+
? {}
|
|
257
|
+
: { input: jsonSafeInput(provenanceContext.input) }),
|
|
258
|
+
...(provenanceContext.inputs && provenanceContext.inputs.length > 0
|
|
259
|
+
? { inputs: [...provenanceContext.inputs] }
|
|
260
|
+
: {}),
|
|
261
|
+
outputs,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function nextProvenanceFile(
|
|
266
|
+
projectRoot: string,
|
|
267
|
+
provenanceOperationId: string,
|
|
268
|
+
provenanceContext: ProjectOutputProvenanceContext,
|
|
269
|
+
outputs: readonly HashedProjectFile[],
|
|
270
|
+
): Promise<NormalizedOutputFile> {
|
|
271
|
+
const provenance = await readProvenance(projectRoot);
|
|
272
|
+
const nextProvenance = ProjectProvenanceDocumentSchema.parse({
|
|
273
|
+
...provenance,
|
|
274
|
+
operations: {
|
|
275
|
+
...provenance.operations,
|
|
276
|
+
[provenanceOperationId]: provenanceOperation(provenanceContext, outputs),
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
path: PROJECT_PROVENANCE_PATH,
|
|
281
|
+
content: `${JSON.stringify(nextProvenance, null, 2)}\n`,
|
|
282
|
+
mediaType: 'application/json',
|
|
283
|
+
role: 'provenance',
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Both halves of "a read-modify-write of `.vgai/provenance.json` never loses a
|
|
289
|
+
* record", exactly as `asset-ledger-store.ts` holds them for `.vgai/assets.json`
|
|
290
|
+
* — the queue below is the in-process half, and it CANNOT see another process.
|
|
291
|
+
*
|
|
292
|
+
* The other process is ordinary now, not exotic: `vgai blender-mcp` mirrors a
|
|
293
|
+
* session's `public/` outputs in through this same writer while the editor
|
|
294
|
+
* server it is talking to may be committing a bake for the same project. Both
|
|
295
|
+
* read the whole document, add one operation, and write it back; interleaved,
|
|
296
|
+
* the later write wins and the earlier record is gone with no error anywhere.
|
|
297
|
+
*/
|
|
298
|
+
async function serializeProjectCommit<T>(projectRoot: string, work: () => Promise<T>): Promise<T> {
|
|
299
|
+
const guarded = async () => {
|
|
300
|
+
const lockFile = `${resolve(projectRoot, PROJECT_PROVENANCE_PATH)}.lock`;
|
|
301
|
+
await mkdir(dirname(lockFile), { recursive: true });
|
|
302
|
+
return withExclusiveLock(ledgerLockIo(lockFile), work, { owner: `pid ${process.pid}` });
|
|
303
|
+
};
|
|
304
|
+
const previous = projectCommitQueues.get(projectRoot) ?? Promise.resolve();
|
|
305
|
+
const current = previous.catch(() => undefined).then(guarded);
|
|
306
|
+
projectCommitQueues.set(projectRoot, current);
|
|
307
|
+
try {
|
|
308
|
+
return await current;
|
|
309
|
+
} finally {
|
|
310
|
+
if (projectCommitQueues.get(projectRoot) === current) projectCommitQueues.delete(projectRoot);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function createProjectOutputWriter(
|
|
315
|
+
projectRoot: string,
|
|
316
|
+
provenanceContext: ProjectOutputProvenanceContext = {
|
|
317
|
+
operationName: 'project.output.write',
|
|
318
|
+
},
|
|
319
|
+
): ProjectOutputWriter {
|
|
320
|
+
return {
|
|
321
|
+
async write(files, options = {}): Promise<ProjectGeneratedOutput> {
|
|
322
|
+
const { normalized, summaries, totalBytes } = validateBatch(files);
|
|
323
|
+
if (options.dryRun === true) {
|
|
324
|
+
return { files: summaries, totalBytes, dryRun: true };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Capture request-scoped execution facts synchronously, then enqueue
|
|
328
|
+
// before doing any filesystem-backed billing lookup. Otherwise two
|
|
329
|
+
// writes invoked in order can reach the commit queue in the opposite
|
|
330
|
+
// order when the first lookup yields longer than the second.
|
|
331
|
+
const capturedProvenanceContext = withCapturedExecutions(provenanceContext);
|
|
332
|
+
|
|
333
|
+
return serializeProjectCommit(projectRoot, async () => {
|
|
334
|
+
const effectiveProvenanceContext = await withGenerationBilling(
|
|
335
|
+
projectRoot,
|
|
336
|
+
capturedProvenanceContext,
|
|
337
|
+
);
|
|
338
|
+
const provenanceOperationId = randomUUID();
|
|
339
|
+
const hashedFiles: HashedProjectFile[] = normalized.map((file) => ({
|
|
340
|
+
path: file.path,
|
|
341
|
+
bytes: byteLength(file.content),
|
|
342
|
+
sha256: sha256(file.content),
|
|
343
|
+
...(file.mediaType ? { mediaType: file.mediaType } : {}),
|
|
344
|
+
...(file.role ? { role: file.role } : {}),
|
|
345
|
+
}));
|
|
346
|
+
const provenanceFile = await nextProvenanceFile(
|
|
347
|
+
projectRoot,
|
|
348
|
+
provenanceOperationId,
|
|
349
|
+
effectiveProvenanceContext,
|
|
350
|
+
hashedFiles,
|
|
351
|
+
);
|
|
352
|
+
const committedFiles = [...normalized, provenanceFile];
|
|
353
|
+
const transactionRoot = resolve(
|
|
354
|
+
projectRoot,
|
|
355
|
+
'.vgai',
|
|
356
|
+
'tmp',
|
|
357
|
+
`project-output-${provenanceOperationId}`,
|
|
358
|
+
);
|
|
359
|
+
const stagedRoot = resolve(transactionRoot, 'staged');
|
|
360
|
+
const backupRoot = resolve(transactionRoot, 'backup');
|
|
361
|
+
try {
|
|
362
|
+
await stageBatch(stagedRoot, committedFiles);
|
|
363
|
+
await commitBatch(projectRoot, stagedRoot, backupRoot, committedFiles);
|
|
364
|
+
} finally {
|
|
365
|
+
await rm(transactionRoot, { recursive: true, force: true });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
files: summaries,
|
|
370
|
+
totalBytes,
|
|
371
|
+
dryRun: false,
|
|
372
|
+
provenanceOperationId,
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Idempotently repairs the permanent billing snapshot after an accept tool
|
|
380
|
+
* returns its final settlement. Acceptance writes bytes before its provider
|
|
381
|
+
* mapper can update the operational job, so this second phase is also run by
|
|
382
|
+
* startup reconciliation to close the crash window between those commits. */
|
|
383
|
+
export async function reconcileAcceptedGenerationProvenance(
|
|
384
|
+
projectRoot: string,
|
|
385
|
+
job: GenerationJob,
|
|
386
|
+
): Promise<boolean> {
|
|
387
|
+
if (!job.acceptedAt || !job.provenanceOperationId) return false;
|
|
388
|
+
const provenanceOperationId = job.provenanceOperationId;
|
|
389
|
+
return serializeProjectCommit(projectRoot, async () => {
|
|
390
|
+
const provenance = await readProvenance(projectRoot);
|
|
391
|
+
const operation = provenance.operations[provenanceOperationId];
|
|
392
|
+
if (!operation) return false;
|
|
393
|
+
const matches = (execution: ProjectProvenanceExecution) =>
|
|
394
|
+
execution.provider === job.provider &&
|
|
395
|
+
(execution.requestId === job.externalId || execution.taskId === job.externalId);
|
|
396
|
+
let changed = false;
|
|
397
|
+
let nextOperation = operation;
|
|
398
|
+
if (operation.execution && matches(operation.execution)) {
|
|
399
|
+
changed = JSON.stringify(operation.execution.billing) !== JSON.stringify(job.billing);
|
|
400
|
+
nextOperation = { ...operation, execution: { ...operation.execution, billing: job.billing } };
|
|
401
|
+
} else if (operation.executions) {
|
|
402
|
+
const executions = operation.executions.map((execution) => {
|
|
403
|
+
if (!matches(execution)) return execution;
|
|
404
|
+
if (JSON.stringify(execution.billing) !== JSON.stringify(job.billing)) changed = true;
|
|
405
|
+
return { ...execution, billing: job.billing };
|
|
406
|
+
});
|
|
407
|
+
nextOperation = { ...operation, executions };
|
|
408
|
+
}
|
|
409
|
+
if (!changed) return false;
|
|
410
|
+
const next = ProjectProvenanceDocumentSchema.parse({
|
|
411
|
+
...provenance,
|
|
412
|
+
operations: { ...provenance.operations, [provenanceOperationId]: nextOperation },
|
|
413
|
+
});
|
|
414
|
+
const path = resolve(projectRoot, PROJECT_PROVENANCE_PATH);
|
|
415
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
416
|
+
await writeFile(temporary, `${JSON.stringify(next, null, 2)}\n`);
|
|
417
|
+
try {
|
|
418
|
+
await rename(temporary, path);
|
|
419
|
+
} finally {
|
|
420
|
+
await rm(temporary, { force: true });
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** The `mediaType` an output's extension implies. Exported because every
|
|
427
|
+
* writer that commits bytes it did not choose the type of needs the same
|
|
428
|
+
* table — the CLI's Blender write-back door among them — and two tables
|
|
429
|
+
* drift. */
|
|
430
|
+
export function projectOutputMediaType(path: string): string | undefined {
|
|
431
|
+
switch (extname(path).toLowerCase()) {
|
|
432
|
+
case '.glb':
|
|
433
|
+
return 'model/gltf-binary';
|
|
434
|
+
case '.gltf':
|
|
435
|
+
return 'model/gltf+json';
|
|
436
|
+
case '.png':
|
|
437
|
+
return 'image/png';
|
|
438
|
+
case '.mp4':
|
|
439
|
+
return 'video/mp4';
|
|
440
|
+
case '.webm':
|
|
441
|
+
return 'video/webm';
|
|
442
|
+
case '.webp':
|
|
443
|
+
return 'image/webp';
|
|
444
|
+
case '.jpg':
|
|
445
|
+
case '.jpeg':
|
|
446
|
+
return 'image/jpeg';
|
|
447
|
+
case '.json':
|
|
448
|
+
return 'application/json';
|
|
449
|
+
case '.hdr':
|
|
450
|
+
return 'image/vnd.radiance';
|
|
451
|
+
default:
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function stagedRole(path: string): ProjectOutputFile['role'] {
|
|
457
|
+
if (path.endsWith('/.vgai-thumbnail.webp')) return 'other';
|
|
458
|
+
return 'asset';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async function describeStagedDirectory(
|
|
462
|
+
stagedDirectory: string,
|
|
463
|
+
destinationProjectPath: string,
|
|
464
|
+
): Promise<HashedProjectFile[]> {
|
|
465
|
+
const outputs: HashedProjectFile[] = [];
|
|
466
|
+
async function walk(directory: string): Promise<void> {
|
|
467
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
468
|
+
for (const entry of entries) {
|
|
469
|
+
const absolute = resolve(directory, entry.name);
|
|
470
|
+
if (entry.isDirectory()) {
|
|
471
|
+
await walk(absolute);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (!entry.isFile()) {
|
|
475
|
+
throw new Error(
|
|
476
|
+
`Staged project output contains an unsupported filesystem entry: ${absolute}`,
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
const relativeFile = relative(stagedDirectory, absolute).split(sep).join('/');
|
|
480
|
+
const path = posix.join(destinationProjectPath, relativeFile);
|
|
481
|
+
const info = await stat(absolute);
|
|
482
|
+
const mediaType = projectOutputMediaType(path);
|
|
483
|
+
outputs.push({
|
|
484
|
+
path,
|
|
485
|
+
bytes: info.size,
|
|
486
|
+
sha256: await sha256File(absolute),
|
|
487
|
+
...(mediaType ? { mediaType } : {}),
|
|
488
|
+
role: stagedRole(path),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
await walk(stagedDirectory);
|
|
493
|
+
outputs.sort((left, right) => left.path.localeCompare(right.path));
|
|
494
|
+
if (outputs.length === 0) throw new Error('Staged project output directory is empty.');
|
|
495
|
+
return outputs;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
499
|
+
try {
|
|
500
|
+
await stat(path);
|
|
501
|
+
return true;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return false;
|
|
504
|
+
throw error;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* RE-RECORD BYTES THAT ARE ALREADY ON DISK, against the door that builds them
|
|
510
|
+
* today — without touching a single one of them.
|
|
511
|
+
*
|
|
512
|
+
* Every other path here WRITES: it stages bytes a generator just produced and
|
|
513
|
+
* commits them with the record of the operation that produced them. This one
|
|
514
|
+
* writes nothing but the ledger. It hashes the files where they lie and
|
|
515
|
+
* appends ONE `project.provenance.attest` operation naming them.
|
|
516
|
+
*
|
|
517
|
+
* WHEN IT IS THE RIGHT ANSWER, and it is a narrow case: a project ships a
|
|
518
|
+
* binary whose ledger record names a tool that no longer exists (renamed,
|
|
519
|
+
* folded into another capability, deleted), so nothing tells a reader which
|
|
520
|
+
* door rebuilds it — and re-running the live door WOULD change the bytes the
|
|
521
|
+
* project ships. Re-baking is the better answer whenever the bytes may move;
|
|
522
|
+
* an attest is for when they may not. It never edits or removes the old
|
|
523
|
+
* record: that record is what happened.
|
|
524
|
+
*
|
|
525
|
+
* The claim it makes is deliberately weak, and `input.reason` is REQUIRED by
|
|
526
|
+
* `scripts/validate-project-provenance.mjs` to keep it honest — see
|
|
527
|
+
* {@link PROJECT_ATTEST_OPERATION}. `attests` names the live operation.
|
|
528
|
+
*
|
|
529
|
+
* Driven by `scripts/attest-project-output.ts`.
|
|
530
|
+
*/
|
|
531
|
+
export async function attestExistingProjectOutputs(options: {
|
|
532
|
+
projectRoot: string;
|
|
533
|
+
/** Project-relative paths that must ALREADY exist. Nothing is written to them. */
|
|
534
|
+
paths: readonly string[];
|
|
535
|
+
/** Why the door was not simply re-run. Recorded verbatim as `input.reason`. */
|
|
536
|
+
reason: string;
|
|
537
|
+
/** The operation that produces these bytes today, and where it lives. */
|
|
538
|
+
attests: { operation: string; source?: string; input?: unknown };
|
|
539
|
+
}): Promise<{ provenanceOperationId: string; files: ProjectGeneratedOutputFile[] }> {
|
|
540
|
+
if (options.paths.length === 0) throw new Error('attest: no output paths given.');
|
|
541
|
+
if (options.reason.trim().length === 0) {
|
|
542
|
+
throw new Error(
|
|
543
|
+
'attest: `reason` is required and must be non-empty — an attest is the weakest ' +
|
|
544
|
+
'claim in the ledger and says in writing why the door was not re-run.',
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
const outputs: HashedProjectFile[] = [];
|
|
548
|
+
for (const raw of options.paths) {
|
|
549
|
+
const path = normalizeOutputPath(raw);
|
|
550
|
+
const absolute = resolve(options.projectRoot, path);
|
|
551
|
+
if (!(await existingFile(absolute))) {
|
|
552
|
+
throw new Error(
|
|
553
|
+
`attest: ${path} does not exist. An attest records bytes already on disk; ` +
|
|
554
|
+
'it never creates them.',
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
const info = await stat(absolute);
|
|
558
|
+
const mediaType = projectOutputMediaType(path);
|
|
559
|
+
outputs.push({
|
|
560
|
+
path,
|
|
561
|
+
bytes: info.size,
|
|
562
|
+
sha256: await sha256File(absolute),
|
|
563
|
+
...(mediaType ? { mediaType } : {}),
|
|
564
|
+
role: 'asset',
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
outputs.sort((left, right) => left.path.localeCompare(right.path));
|
|
568
|
+
|
|
569
|
+
return serializeProjectCommit(options.projectRoot, async () => {
|
|
570
|
+
const provenanceOperationId = randomUUID();
|
|
571
|
+
const provenanceFile = await nextProvenanceFile(
|
|
572
|
+
options.projectRoot,
|
|
573
|
+
provenanceOperationId,
|
|
574
|
+
{
|
|
575
|
+
operationName: PROJECT_ATTEST_OPERATION,
|
|
576
|
+
input: {
|
|
577
|
+
reason: options.reason,
|
|
578
|
+
attests: {
|
|
579
|
+
operation: options.attests.operation,
|
|
580
|
+
...(options.attests.source ? { source: options.attests.source } : {}),
|
|
581
|
+
...(options.attests.input === undefined ? {} : { input: options.attests.input }),
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
outputs,
|
|
586
|
+
);
|
|
587
|
+
const transactionRoot = resolve(
|
|
588
|
+
options.projectRoot,
|
|
589
|
+
'.vgai',
|
|
590
|
+
'tmp',
|
|
591
|
+
`project-attest-${provenanceOperationId}`,
|
|
592
|
+
);
|
|
593
|
+
try {
|
|
594
|
+
await stageBatch(resolve(transactionRoot, 'staged'), [provenanceFile]);
|
|
595
|
+
await commitBatch(
|
|
596
|
+
options.projectRoot,
|
|
597
|
+
resolve(transactionRoot, 'staged'),
|
|
598
|
+
resolve(transactionRoot, 'backup'),
|
|
599
|
+
[provenanceFile],
|
|
600
|
+
);
|
|
601
|
+
} finally {
|
|
602
|
+
await rm(transactionRoot, { recursive: true, force: true });
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
provenanceOperationId,
|
|
606
|
+
files: outputs.map(({ sha256: _sha256, ...file }) => file),
|
|
607
|
+
};
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Commits an already-streamed directory and the central provenance rewrite as
|
|
613
|
+
* one rollback-safe project transaction. Catalog acquisition uses this path so
|
|
614
|
+
* large assets never need to be buffered into ProjectOutputFile.content.
|
|
615
|
+
*/
|
|
616
|
+
export async function commitStagedProjectDirectory(options: {
|
|
617
|
+
projectRoot: string;
|
|
618
|
+
stagedDirectory: string;
|
|
619
|
+
destination: string;
|
|
620
|
+
provenance: ProjectOutputProvenanceContext;
|
|
621
|
+
}): Promise<ProjectGeneratedOutput> {
|
|
622
|
+
const destinationProjectPath = normalizeOutputPath(
|
|
623
|
+
relative(options.projectRoot, options.destination).split(sep).join('/'),
|
|
624
|
+
);
|
|
625
|
+
const outputs = await describeStagedDirectory(options.stagedDirectory, destinationProjectPath);
|
|
626
|
+
const summaries: ProjectGeneratedOutputFile[] = outputs.map(
|
|
627
|
+
({ sha256: _sha256, ...file }) => file,
|
|
628
|
+
);
|
|
629
|
+
const totalBytes = summaries.reduce((total, file) => total + file.bytes, 0);
|
|
630
|
+
|
|
631
|
+
return serializeProjectCommit(options.projectRoot, async () => {
|
|
632
|
+
if (await pathExists(options.destination)) {
|
|
633
|
+
throw new Error(`Destination already exists and was not overwritten: ${options.destination}`);
|
|
634
|
+
}
|
|
635
|
+
const provenanceOperationId = randomUUID();
|
|
636
|
+
const provenanceFile = await nextProvenanceFile(
|
|
637
|
+
options.projectRoot,
|
|
638
|
+
provenanceOperationId,
|
|
639
|
+
options.provenance,
|
|
640
|
+
outputs,
|
|
641
|
+
);
|
|
642
|
+
const transactionRoot = resolve(
|
|
643
|
+
options.projectRoot,
|
|
644
|
+
'.vgai',
|
|
645
|
+
'tmp',
|
|
646
|
+
`project-directory-${provenanceOperationId}`,
|
|
647
|
+
);
|
|
648
|
+
const stagedRoot = resolve(transactionRoot, 'staged');
|
|
649
|
+
const backupRoot = resolve(transactionRoot, 'backup');
|
|
650
|
+
let directoryCommitted = false;
|
|
651
|
+
try {
|
|
652
|
+
await stageBatch(stagedRoot, [provenanceFile]);
|
|
653
|
+
await mkdir(dirname(options.destination), { recursive: true });
|
|
654
|
+
await rename(options.stagedDirectory, options.destination);
|
|
655
|
+
directoryCommitted = true;
|
|
656
|
+
await commitBatch(options.projectRoot, stagedRoot, backupRoot, [provenanceFile]);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
if (directoryCommitted) {
|
|
659
|
+
try {
|
|
660
|
+
await rename(options.destination, options.stagedDirectory);
|
|
661
|
+
} catch (rollbackError) {
|
|
662
|
+
throw new AggregateError(
|
|
663
|
+
[error, rollbackError],
|
|
664
|
+
'Project directory transaction and its rollback both failed.',
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
throw error;
|
|
669
|
+
} finally {
|
|
670
|
+
await rm(transactionRoot, { recursive: true, force: true });
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
files: summaries,
|
|
674
|
+
totalBytes,
|
|
675
|
+
dryRun: false,
|
|
676
|
+
provenanceOperationId,
|
|
677
|
+
};
|
|
678
|
+
});
|
|
679
|
+
}
|