@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,47 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { AdapterRegionIncludes } from '../src/ui-source/adapter-region-includes';
|
|
4
|
+
import {
|
|
5
|
+
ADAPTER_MODULE_FILENAME,
|
|
6
|
+
EMPTY_REGION_INCLUDES,
|
|
7
|
+
parseAdapterRegionIncludes,
|
|
8
|
+
} from '../src/ui-source/adapter-region-includes';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The dev server's filesystem half of the adapter's region `include`
|
|
12
|
+
* globs — the pure parse lives in `../src/ui-source/adapter-region-includes.ts`
|
|
13
|
+
* (browser-safe, because Content's component index reads the same declaration
|
|
14
|
+
* through a storage backend), and this adds only "read the file beside the
|
|
15
|
+
* manifest, once, and cache it".
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Cache: project directory -> its adapter's region includes. Same
|
|
19
|
+
* no-invalidation bar as the manifest-roots cache beside it — editing
|
|
20
|
+
* `vgai.adapter.ts` already restarts the dev session. */
|
|
21
|
+
const cache = new Map<string, AdapterRegionIncludes>();
|
|
22
|
+
|
|
23
|
+
/** Region includes declared by the `vgai.adapter.ts` beside `manifestDir`. A
|
|
24
|
+
* project with no adapter module gets the native default: no includes. */
|
|
25
|
+
export function adapterRegionIncludes(manifestDir: string): AdapterRegionIncludes {
|
|
26
|
+
const cached = cache.get(manifestDir);
|
|
27
|
+
if (cached !== undefined) return cached;
|
|
28
|
+
let result = EMPTY_REGION_INCLUDES;
|
|
29
|
+
try {
|
|
30
|
+
result = parseAdapterRegionIncludes(
|
|
31
|
+
readFileSync(join(manifestDir, ADAPTER_MODULE_FILENAME), 'utf8'),
|
|
32
|
+
);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
// No adapter module is the DECLARED native default. Any other fs error is a
|
|
35
|
+
// real environment problem and is not swallowed into "declares nothing".
|
|
36
|
+
if ((err as NodeJS.ErrnoException)?.code !== 'ENOENT') throw err;
|
|
37
|
+
}
|
|
38
|
+
cache.set(manifestDir, result);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Test-only: forget what has been read. */
|
|
43
|
+
export function resetAdapterRegionIncludesCacheForTest(): void {
|
|
44
|
+
cache.clear();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { ADAPTER_MODULE_FILENAME };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
type AssetCatalog,
|
|
5
|
+
type AssetFamily,
|
|
6
|
+
AssetFileSchema,
|
|
7
|
+
type AssetVariant,
|
|
8
|
+
parseAssetCatalog,
|
|
9
|
+
} from '../src/asset-workflow/asset-types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ONE catalog record — the generator's intermediate representation, produced
|
|
13
|
+
* by `scripts/sync-asset-library.ts` while it walks the SSD library and
|
|
14
|
+
* folded into families by `buildAssetCatalogV2`. It is NOT a persisted format:
|
|
15
|
+
* the only on-disk catalog is v2 (`AssetCatalogEnvelopeSchema`).
|
|
16
|
+
*/
|
|
17
|
+
export const CatalogRecordSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
id: z.string().regex(/^[a-f0-9]{24}$/),
|
|
20
|
+
source: z.string().min(1),
|
|
21
|
+
name: z.string().min(1),
|
|
22
|
+
type: z.enum(['model', 'animation', 'source']),
|
|
23
|
+
categories: z.array(z.string()),
|
|
24
|
+
tags: z.array(z.string()),
|
|
25
|
+
license: z.string().min(1),
|
|
26
|
+
author: z.string().min(1),
|
|
27
|
+
sourceUrl: z.string().url(),
|
|
28
|
+
thumbnail: z.string().min(1).optional(),
|
|
29
|
+
files: z.array(AssetFileSchema).min(1),
|
|
30
|
+
animationCount: z.number().int().nonnegative().optional(),
|
|
31
|
+
clipNames: z.array(z.string()).optional(),
|
|
32
|
+
})
|
|
33
|
+
.strict();
|
|
34
|
+
|
|
35
|
+
export type CatalogRecord = z.infer<typeof CatalogRecordSchema>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* REMOVED-FORMAT guard. `catalog.json` v1 — `{ version: 1, generatedAt,
|
|
39
|
+
* assets: [...] }`, a flat variant list with no families — was accepted on
|
|
40
|
+
* read and migrated in memory by `migrateLegacyAssetCatalog`. Both are
|
|
41
|
+
* deleted: v2 is the only catalog format, and `scripts/sync-asset-library.ts`
|
|
42
|
+
* writes v2 directly.
|
|
43
|
+
*
|
|
44
|
+
* Zod's envelope would reject a v1 file anyway, but with `version: expected 2,
|
|
45
|
+
* received 1` and no hint about what happened — so this runs FIRST and names
|
|
46
|
+
* the removal, the same way every other retired on-disk format in this repo
|
|
47
|
+
* does.
|
|
48
|
+
*/
|
|
49
|
+
export function assertNoRemovedAssetCatalogV1(input: unknown, path: string): void {
|
|
50
|
+
if (
|
|
51
|
+
input &&
|
|
52
|
+
typeof input === 'object' &&
|
|
53
|
+
'version' in input &&
|
|
54
|
+
(input as { version: unknown }).version !== 2
|
|
55
|
+
) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Asset catalog "${path}" declares version ` +
|
|
58
|
+
`${JSON.stringify((input as { version: unknown }).version)}. The v1 catalog format ` +
|
|
59
|
+
'(`{ version: 1, assets: [...] }`) and its read-time migration ' +
|
|
60
|
+
'(`migrateLegacyAssetCatalog`) were REMOVED — v2 (`{ version: 2, families: [...] }`) ' +
|
|
61
|
+
'is the only format. Re-run `scripts/sync-asset-library.ts`, which writes v2 directly.',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizeFamilyName(name: string): string {
|
|
67
|
+
return name
|
|
68
|
+
.trim()
|
|
69
|
+
.toLocaleLowerCase('en-US')
|
|
70
|
+
.replace(/\s+/g, ' ')
|
|
71
|
+
.replace(/\s+\(final\)$/, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generator-only semantic key. It is persisted as an explicit family id in v2;
|
|
76
|
+
* search/preview code never reconstructs it. Collection segments prevent two
|
|
77
|
+
* unrelated packs with the same display name from aliasing.
|
|
78
|
+
*/
|
|
79
|
+
export function catalogFamilyKey(asset: CatalogRecord): string {
|
|
80
|
+
const segments = asset.files[0]!.path.replaceAll('\\', '/').split('/');
|
|
81
|
+
return JSON.stringify([
|
|
82
|
+
asset.source,
|
|
83
|
+
segments[1] ?? '',
|
|
84
|
+
segments[2] ?? '',
|
|
85
|
+
normalizeFamilyName(asset.name),
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function makeCatalogFamilyId(key: string): string {
|
|
90
|
+
return createHash('sha256').update(`family\0${key}`).digest('hex').slice(0, 24);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function uniqueSorted(values: Iterable<string>): string[] {
|
|
94
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function familyType(records: readonly CatalogRecord[]): AssetFamily['type'] {
|
|
98
|
+
if (records.some((record) => record.type === 'model')) return 'model';
|
|
99
|
+
if (records.some((record) => record.type === 'animation')) return 'animation';
|
|
100
|
+
return 'source';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function variantFromRecord(record: CatalogRecord): AssetVariant {
|
|
104
|
+
return {
|
|
105
|
+
id: record.id,
|
|
106
|
+
label: record.files.map((file) => file.label).join(' + '),
|
|
107
|
+
type: record.type,
|
|
108
|
+
files: record.files,
|
|
109
|
+
deliveries: [
|
|
110
|
+
{
|
|
111
|
+
id: `local:${record.id}`,
|
|
112
|
+
kind: 'local-ssd',
|
|
113
|
+
available: true,
|
|
114
|
+
fileIndexes: record.files.map((_, index) => index),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
thumbnail: {
|
|
118
|
+
path: record.thumbnail ?? `thumbnails/${record.id}.webp`,
|
|
119
|
+
state: 'generated',
|
|
120
|
+
profileVersion: 'thumbnail-v1',
|
|
121
|
+
},
|
|
122
|
+
...(record.animationCount !== undefined ? { animationCount: record.animationCount } : {}),
|
|
123
|
+
...(record.clipNames !== undefined ? { clipNames: record.clipNames } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function buildAssetCatalogV2(
|
|
128
|
+
records: readonly CatalogRecord[],
|
|
129
|
+
generatedAt: string,
|
|
130
|
+
): AssetCatalog {
|
|
131
|
+
const grouped = new Map<string, CatalogRecord[]>();
|
|
132
|
+
for (const record of records) {
|
|
133
|
+
const key = catalogFamilyKey(record);
|
|
134
|
+
const siblings = grouped.get(key);
|
|
135
|
+
if (siblings) siblings.push(record);
|
|
136
|
+
else grouped.set(key, [record]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const seenFamilyIds = new Map<string, string>();
|
|
140
|
+
const seenVariantIds = new Set<string>();
|
|
141
|
+
const families: AssetFamily[] = [];
|
|
142
|
+
for (const [key, siblings] of grouped) {
|
|
143
|
+
siblings.sort((left, right) => left.id.localeCompare(right.id));
|
|
144
|
+
const first = siblings[0]!;
|
|
145
|
+
const id = makeCatalogFamilyId(key);
|
|
146
|
+
const priorKey = seenFamilyIds.get(id);
|
|
147
|
+
if (priorKey && priorKey !== key) throw new Error(`Catalog family id collision: ${id}`);
|
|
148
|
+
seenFamilyIds.set(id, key);
|
|
149
|
+
for (const sibling of siblings) {
|
|
150
|
+
if (seenVariantIds.has(sibling.id))
|
|
151
|
+
throw new Error(`Duplicate catalog variant id: ${sibling.id}`);
|
|
152
|
+
seenVariantIds.add(sibling.id);
|
|
153
|
+
}
|
|
154
|
+
families.push({
|
|
155
|
+
id,
|
|
156
|
+
source: first.source,
|
|
157
|
+
name: first.name.replace(/\s+\(final\)$/, ''),
|
|
158
|
+
type: familyType(siblings),
|
|
159
|
+
categories: uniqueSorted(siblings.flatMap((record) => record.categories)),
|
|
160
|
+
tags: uniqueSorted(siblings.flatMap((record) => record.tags)),
|
|
161
|
+
license: first.license,
|
|
162
|
+
author: first.author,
|
|
163
|
+
sourceUrl: first.sourceUrl,
|
|
164
|
+
variants: siblings.map(variantFromRecord),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
families.sort(
|
|
168
|
+
(left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id),
|
|
169
|
+
);
|
|
170
|
+
return parseAssetCatalog({ version: 2, generatedAt, families });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function serializableAssetCatalog(catalog: AssetCatalog): object {
|
|
174
|
+
return { version: 2, generatedAt: catalog.generatedAt, families: catalog.families };
|
|
175
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import {
|
|
4
|
+
cp,
|
|
5
|
+
mkdir,
|
|
6
|
+
mkdtemp,
|
|
7
|
+
readdir,
|
|
8
|
+
readFile,
|
|
9
|
+
rename,
|
|
10
|
+
rm,
|
|
11
|
+
stat,
|
|
12
|
+
writeFile,
|
|
13
|
+
} from 'node:fs/promises';
|
|
14
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
15
|
+
import { PROJECT_PROVENANCE_PATH } from './support/project/provenance';
|
|
16
|
+
import { isPathInside } from './server-utils';
|
|
17
|
+
|
|
18
|
+
interface AssetHistorySnapshotMetadata {
|
|
19
|
+
version: 1;
|
|
20
|
+
assetPath: string;
|
|
21
|
+
destination: string;
|
|
22
|
+
assetPresent: boolean;
|
|
23
|
+
provenancePresent: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
27
|
+
try {
|
|
28
|
+
await stat(path);
|
|
29
|
+
return true;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return false;
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizedAssetPath(assetPath: string): string {
|
|
37
|
+
const normalized = assetPath.replaceAll('\\', '/').replace(/^\/+/, '');
|
|
38
|
+
if (
|
|
39
|
+
!normalized.startsWith('asset-library/') ||
|
|
40
|
+
normalized.includes('\0') ||
|
|
41
|
+
normalized.split('/').includes('..') ||
|
|
42
|
+
basename(normalized) === ''
|
|
43
|
+
) {
|
|
44
|
+
throw new Error('Asset history paths must name a file under public/asset-library/.');
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function hashFile(hash: ReturnType<typeof createHash>, path: string): Promise<void> {
|
|
50
|
+
const info = await stat(path);
|
|
51
|
+
hash.update(`file:${info.size}:`);
|
|
52
|
+
for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function hashDirectory(
|
|
56
|
+
hash: ReturnType<typeof createHash>,
|
|
57
|
+
root: string,
|
|
58
|
+
directory: string,
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
const entries = (await readdir(directory, { withFileTypes: true })).sort((left, right) =>
|
|
61
|
+
left.name.localeCompare(right.name),
|
|
62
|
+
);
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const absolute = join(directory, entry.name);
|
|
65
|
+
const path = relative(root, absolute).split(sep).join('/');
|
|
66
|
+
if (entry.isDirectory()) {
|
|
67
|
+
hash.update(`directory:${path}\0`);
|
|
68
|
+
await hashDirectory(hash, root, absolute);
|
|
69
|
+
} else if (entry.isFile()) {
|
|
70
|
+
hash.update(`path:${path}\0`);
|
|
71
|
+
await hashFile(hash, absolute);
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(`Asset history cannot snapshot special filesystem entry ${absolute}.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Content-addressed, server-resident snapshots for one imported asset directory.
|
|
80
|
+
* History stores only the stable digest token in browser memory; exact model,
|
|
81
|
+
* dependency, recipe, thumbnail, and provenance bytes stay under `.vgai/tmp`.
|
|
82
|
+
*/
|
|
83
|
+
export class AssetHistorySnapshots {
|
|
84
|
+
private readonly projectRoot: string;
|
|
85
|
+
private readonly snapshotRoot: string;
|
|
86
|
+
|
|
87
|
+
constructor(private readonly publicRoot: string) {
|
|
88
|
+
this.projectRoot = dirname(publicRoot);
|
|
89
|
+
this.snapshotRoot = resolve(this.projectRoot, '.vgai', 'tmp', 'asset-history-snapshots');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async capture(assetPath: string): Promise<string> {
|
|
93
|
+
const normalized = normalizedAssetPath(assetPath);
|
|
94
|
+
const destination = resolve(this.publicRoot, dirname(normalized));
|
|
95
|
+
if (!isPathInside(this.publicRoot, destination)) {
|
|
96
|
+
throw new Error('Asset history destination escapes public/.');
|
|
97
|
+
}
|
|
98
|
+
const provenance = resolve(this.projectRoot, PROJECT_PROVENANCE_PATH);
|
|
99
|
+
const metadata: AssetHistorySnapshotMetadata = {
|
|
100
|
+
version: 1,
|
|
101
|
+
assetPath: normalized,
|
|
102
|
+
destination: relative(this.projectRoot, destination).split(sep).join('/'),
|
|
103
|
+
assetPresent: await pathExists(destination),
|
|
104
|
+
provenancePresent: await pathExists(provenance),
|
|
105
|
+
};
|
|
106
|
+
const hash = createHash('sha256');
|
|
107
|
+
hash.update(JSON.stringify(metadata));
|
|
108
|
+
if (metadata.assetPresent) await hashDirectory(hash, destination, destination);
|
|
109
|
+
if (metadata.provenancePresent) {
|
|
110
|
+
hash.update('provenance\0');
|
|
111
|
+
await hashFile(hash, provenance);
|
|
112
|
+
}
|
|
113
|
+
const token = hash.digest('hex');
|
|
114
|
+
const snapshot = resolve(this.snapshotRoot, token);
|
|
115
|
+
if (await pathExists(snapshot)) return token;
|
|
116
|
+
|
|
117
|
+
await mkdir(this.snapshotRoot, { recursive: true });
|
|
118
|
+
const staging = await mkdtemp(resolve(this.snapshotRoot, `.capture-${randomUUID()}-`));
|
|
119
|
+
try {
|
|
120
|
+
if (metadata.assetPresent)
|
|
121
|
+
await cp(destination, resolve(staging, 'asset'), { recursive: true });
|
|
122
|
+
if (metadata.provenancePresent) await cp(provenance, resolve(staging, 'provenance.json'));
|
|
123
|
+
await writeFile(resolve(staging, 'metadata.json'), `${JSON.stringify(metadata, null, 2)}\n`);
|
|
124
|
+
try {
|
|
125
|
+
await rename(staging, snapshot);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (!(await pathExists(snapshot))) throw error;
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
await rm(staging, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
return token;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async restore(assetPath: string, token: string): Promise<void> {
|
|
136
|
+
const normalized = normalizedAssetPath(assetPath);
|
|
137
|
+
if (!/^[a-f0-9]{64}$/.test(token)) throw new Error('Invalid asset history snapshot token.');
|
|
138
|
+
const snapshot = resolve(this.snapshotRoot, token);
|
|
139
|
+
const metadata = JSON.parse(
|
|
140
|
+
await readFile(resolve(snapshot, 'metadata.json'), 'utf8'),
|
|
141
|
+
) as AssetHistorySnapshotMetadata;
|
|
142
|
+
if (metadata.version !== 1 || metadata.assetPath !== normalized) {
|
|
143
|
+
throw new Error('Asset history snapshot identity does not match the selected asset.');
|
|
144
|
+
}
|
|
145
|
+
const destination = resolve(this.projectRoot, metadata.destination);
|
|
146
|
+
const expectedDestination = resolve(this.publicRoot, dirname(normalized));
|
|
147
|
+
if (destination !== expectedDestination || !isPathInside(this.publicRoot, destination)) {
|
|
148
|
+
throw new Error('Asset history snapshot destination is invalid.');
|
|
149
|
+
}
|
|
150
|
+
const provenance = resolve(this.projectRoot, PROJECT_PROVENANCE_PATH);
|
|
151
|
+
const transaction = await mkdtemp(
|
|
152
|
+
resolve(this.projectRoot, '.vgai', 'tmp', `.asset-history-restore-${randomUUID()}-`),
|
|
153
|
+
);
|
|
154
|
+
const stagedAsset = resolve(transaction, 'staged-asset');
|
|
155
|
+
const backupAsset = resolve(transaction, 'backup-asset');
|
|
156
|
+
const stagedProvenance = resolve(transaction, 'staged-provenance.json');
|
|
157
|
+
const backupProvenance = resolve(transaction, 'backup-provenance.json');
|
|
158
|
+
let assetBackedUp = false;
|
|
159
|
+
let assetInstalled = false;
|
|
160
|
+
let provenanceBackedUp = false;
|
|
161
|
+
let provenanceInstalled = false;
|
|
162
|
+
try {
|
|
163
|
+
if (metadata.assetPresent)
|
|
164
|
+
await cp(resolve(snapshot, 'asset'), stagedAsset, { recursive: true });
|
|
165
|
+
if (metadata.provenancePresent) {
|
|
166
|
+
await cp(resolve(snapshot, 'provenance.json'), stagedProvenance);
|
|
167
|
+
}
|
|
168
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
169
|
+
if (await pathExists(destination)) {
|
|
170
|
+
await rename(destination, backupAsset);
|
|
171
|
+
assetBackedUp = true;
|
|
172
|
+
}
|
|
173
|
+
if (metadata.assetPresent) {
|
|
174
|
+
await rename(stagedAsset, destination);
|
|
175
|
+
assetInstalled = true;
|
|
176
|
+
}
|
|
177
|
+
await mkdir(dirname(provenance), { recursive: true });
|
|
178
|
+
if (await pathExists(provenance)) {
|
|
179
|
+
await rename(provenance, backupProvenance);
|
|
180
|
+
provenanceBackedUp = true;
|
|
181
|
+
}
|
|
182
|
+
if (metadata.provenancePresent) {
|
|
183
|
+
await rename(stagedProvenance, provenance);
|
|
184
|
+
provenanceInstalled = true;
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (provenanceInstalled) await rm(provenance, { force: true });
|
|
188
|
+
if (provenanceBackedUp) await rename(backupProvenance, provenance);
|
|
189
|
+
if (assetInstalled) await rm(destination, { recursive: true, force: true });
|
|
190
|
+
if (assetBackedUp) await rename(backupAsset, destination);
|
|
191
|
+
throw error;
|
|
192
|
+
} finally {
|
|
193
|
+
await rm(transaction, { recursive: true, force: true });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem side of the asset provenance ledger (D-AP3).
|
|
3
|
+
*
|
|
4
|
+
* The shape, validation and merge rules live in the pure module
|
|
5
|
+
* `../src/asset-workflow/asset-ledger.ts`; this file is only read-modify-write
|
|
6
|
+
* of `<projectRoot>/.vgai/assets.json`.
|
|
7
|
+
*
|
|
8
|
+
* That read-modify-write is CONCURRENT-SAFE, and both halves are load-bearing
|
|
9
|
+
* (see `../src/asset-workflow/ledger-write-lock.ts` for why neither alone is
|
|
10
|
+
* enough):
|
|
11
|
+
*
|
|
12
|
+
* - Same process: a promise chain per ledger path, so two overlapping
|
|
13
|
+
* `recordAssetMaterialization` calls do not both read the pre-write object
|
|
14
|
+
* and lose the earlier record.
|
|
15
|
+
* - Other processes: an exclusive `assets.json.lock` beside the ledger.
|
|
16
|
+
* Two `vgai edit` sessions, or `asset-packs:sync` racing an editor, are
|
|
17
|
+
* separate processes and the in-process chain cannot see them.
|
|
18
|
+
*
|
|
19
|
+
* The write itself is ATOMIC — staging file then `rename` — so a crash or a
|
|
20
|
+
* concurrent READER never observes a half-serialized ledger. A truncated
|
|
21
|
+
* provenance record reads as "these assets were never materialized", which is
|
|
22
|
+
* exactly the silent-drop failure D-AP3 exists to prevent.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
26
|
+
import { dirname, join } from 'node:path';
|
|
27
|
+
import {
|
|
28
|
+
ASSET_LEDGER_PATH,
|
|
29
|
+
type AssetLedger,
|
|
30
|
+
type AssetLedgerEntry,
|
|
31
|
+
normalizeLedgerPath,
|
|
32
|
+
parseAssetLedgerJson,
|
|
33
|
+
serializeAssetLedger,
|
|
34
|
+
sourceHashFromSha256,
|
|
35
|
+
upsertAssetLedgerEntry,
|
|
36
|
+
} from '../src/asset-workflow/asset-ledger';
|
|
37
|
+
import {
|
|
38
|
+
createSerializer,
|
|
39
|
+
type ExclusiveLockIO,
|
|
40
|
+
type ExclusiveLockOptions,
|
|
41
|
+
removeIfHeldByTakeover,
|
|
42
|
+
withExclusiveLock,
|
|
43
|
+
} from '../src/asset-workflow/ledger-write-lock';
|
|
44
|
+
|
|
45
|
+
export function assetLedgerFile(projectRoot: string): string {
|
|
46
|
+
return join(projectRoot, ...ASSET_LEDGER_PATH.split('/'));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Read the ledger. Absent → `{}`. Corrupt → throws, never silently reset. */
|
|
50
|
+
export async function readAssetLedger(projectRoot: string): Promise<AssetLedger> {
|
|
51
|
+
let text: string;
|
|
52
|
+
try {
|
|
53
|
+
text = await readFile(assetLedgerFile(projectRoot), 'utf8');
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return {};
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
return parseAssetLedgerJson(text);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function writeAssetLedger(projectRoot: string, ledger: AssetLedger): Promise<void> {
|
|
62
|
+
const file = assetLedgerFile(projectRoot);
|
|
63
|
+
await mkdir(dirname(file), { recursive: true });
|
|
64
|
+
// Staging + rename: `rename` within one directory is atomic, so a reader sees
|
|
65
|
+
// either the old ledger or the new one, never a partial write.
|
|
66
|
+
const staging = `${file}.writing-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
67
|
+
await writeFile(staging, serializeAssetLedger(ledger), 'utf8');
|
|
68
|
+
try {
|
|
69
|
+
await rename(staging, file);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
await rm(staging, { force: true });
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run `work` while holding the ledger's CROSS-PROCESS lock.
|
|
78
|
+
*
|
|
79
|
+
* Exported for the concurrency test, which needs the on-disk half without the
|
|
80
|
+
* in-process chain in front of it — a file lock cannot exclude its own holder,
|
|
81
|
+
* so the two halves can only be proven separately.
|
|
82
|
+
*/
|
|
83
|
+
export async function withAssetLedgerFileLock<T>(
|
|
84
|
+
projectRoot: string,
|
|
85
|
+
work: () => Promise<T>,
|
|
86
|
+
options: ExclusiveLockOptions = {},
|
|
87
|
+
): Promise<T> {
|
|
88
|
+
const lockFile = `${assetLedgerFile(projectRoot)}.lock`;
|
|
89
|
+
await mkdir(dirname(lockFile), { recursive: true });
|
|
90
|
+
return withExclusiveLock(ledgerLockIo(lockFile), work, {
|
|
91
|
+
owner: `pid ${process.pid}`,
|
|
92
|
+
...options,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The `node:fs` lock I/O for ANY ledger file, exported for the lock's own tests
|
|
98
|
+
* — the interesting behavior here is per-operation (who may break a corpse,
|
|
99
|
+
* what a release leaves behind) and is not reachable through
|
|
100
|
+
* `withAssetLedgerFileLock`.
|
|
101
|
+
*
|
|
102
|
+
* Nothing in it is asset-specific: it takes the lock PATH. The project's
|
|
103
|
+
* central provenance ledger (`.vgai/provenance.json`, another whole-document
|
|
104
|
+
* read-modify-write, now written by the editor server AND by `vgai blender-mcp`)
|
|
105
|
+
* takes the same lock through `server/project-output-writer.ts`.
|
|
106
|
+
*/
|
|
107
|
+
export function ledgerLockIo(lockFile: string): ExclusiveLockIO {
|
|
108
|
+
/** `wx` = create-or-fail: the one primitive here that is a true atomic CAS. */
|
|
109
|
+
const createExclusive = async (path: string, content: string): Promise<boolean> => {
|
|
110
|
+
try {
|
|
111
|
+
await writeFile(path, content, { encoding: 'utf8', flag: 'wx' });
|
|
112
|
+
return true;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if ((error as NodeJS.ErrnoException).code === 'EEXIST') return false;
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const readOrNull = async (path: string): Promise<string | null> => {
|
|
119
|
+
try {
|
|
120
|
+
return await readFile(path, 'utf8');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
createIfAbsent: (content) => createExclusive(lockFile, content),
|
|
129
|
+
read: () => readOrNull(lockFile),
|
|
130
|
+
// A filesystem has no atomic compare-and-delete, so this is a TAKEOVER:
|
|
131
|
+
// `rename` (one syscall) decides which racer may break a given lock, and
|
|
132
|
+
// the comparison happens after the seize rather than before it. See
|
|
133
|
+
// `removeIfHeldByTakeover` for why read-then-unlink is not good enough and
|
|
134
|
+
// for the residual window this still leaves.
|
|
135
|
+
removeIfHeldBy: (expected) => {
|
|
136
|
+
// Private to this attempt, so concurrent attempts never collide here.
|
|
137
|
+
const aside = `${lockFile}.breaking-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
138
|
+
return removeIfHeldByTakeover(
|
|
139
|
+
{
|
|
140
|
+
moveAside: async () => {
|
|
141
|
+
try {
|
|
142
|
+
await rename(lockFile, aside);
|
|
143
|
+
return true;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// ENOENT — another racer seized it first, or it is simply gone.
|
|
146
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
readAside: () => readOrNull(aside),
|
|
151
|
+
deleteAside: () => rm(aside, { force: true }),
|
|
152
|
+
restoreIfAbsent: (content) => createExclusive(lockFile, content),
|
|
153
|
+
},
|
|
154
|
+
expected,
|
|
155
|
+
);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface MaterializationRecord {
|
|
161
|
+
/** Project root — the directory that owns `.vgai/`. */
|
|
162
|
+
readonly projectRoot: string;
|
|
163
|
+
/** Project-relative destination, e.g. `public/asset-library/local/x/y.glb`. */
|
|
164
|
+
readonly destPath: string;
|
|
165
|
+
/** `source:id` — the library's identity for this asset. */
|
|
166
|
+
readonly key: string;
|
|
167
|
+
/** License string as the library reported it, if it reported one. */
|
|
168
|
+
readonly license?: string | undefined;
|
|
169
|
+
/** The LIBRARY's sha256 of the source bytes. Omit when the library has none —
|
|
170
|
+
* never substitute a hash of the bytes we just wrote (anti-shim). */
|
|
171
|
+
readonly sourceSha256?: string | undefined;
|
|
172
|
+
readonly materializedAt?: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** In-process serialization, keyed by the ledger file each record targets. */
|
|
176
|
+
const serializeLedgerWrite = createSerializer();
|
|
177
|
+
|
|
178
|
+
/** Record one materialization in the project's ledger. */
|
|
179
|
+
export async function recordAssetMaterialization(record: MaterializationRecord): Promise<void> {
|
|
180
|
+
const entry: AssetLedgerEntry = {
|
|
181
|
+
key: record.key,
|
|
182
|
+
...(record.license ? { license: record.license } : {}),
|
|
183
|
+
...(record.sourceSha256 ? { sourceHash: sourceHashFromSha256(record.sourceSha256) } : {}),
|
|
184
|
+
materializedAt: record.materializedAt ?? new Date().toISOString(),
|
|
185
|
+
};
|
|
186
|
+
await serializeLedgerWrite(assetLedgerFile(record.projectRoot), () =>
|
|
187
|
+
withAssetLedgerFileLock(record.projectRoot, async () => {
|
|
188
|
+
const ledger = await readAssetLedger(record.projectRoot);
|
|
189
|
+
await writeAssetLedger(
|
|
190
|
+
record.projectRoot,
|
|
191
|
+
upsertAssetLedgerEntry(ledger, normalizeLedgerPath(record.destPath), entry),
|
|
192
|
+
);
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
}
|