unity-open-mcp 0.1.2 → 0.3.1
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/dist/agent-identity.d.ts +52 -0
- package/dist/agent-identity.js +115 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/batch-spawn.d.ts +7 -0
- package/dist/batch-spawn.js +155 -29
- package/dist/batch-spawn.js.map +1 -1
- package/dist/capabilities/build-capabilities.d.ts +45 -0
- package/dist/capabilities/build-capabilities.js +262 -27
- package/dist/capabilities/build-capabilities.js.map +1 -1
- package/dist/capabilities/cost-hints.d.ts +82 -0
- package/dist/capabilities/cost-hints.js +185 -0
- package/dist/capabilities/cost-hints.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +40 -0
- package/dist/capabilities/lifecycle.js +353 -0
- package/dist/capabilities/lifecycle.js.map +1 -0
- package/dist/capabilities/rule-catalog.d.ts +13 -1
- package/dist/capabilities/rule-catalog.js +620 -75
- package/dist/capabilities/rule-catalog.js.map +1 -1
- package/dist/capabilities/tool-groups.d.ts +21 -0
- package/dist/capabilities/tool-groups.js +411 -14
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/cli/args.d.ts +32 -1
- package/dist/cli/args.js +201 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/cli.d.ts +21 -0
- package/dist/cli/cli.js +68 -5
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands.d.ts +33 -0
- package/dist/cli/commands.js +400 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/exit-codes.d.ts +43 -0
- package/dist/cli/exit-codes.js +78 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/compile-verify.d.ts +65 -0
- package/dist/compile-verify.js +94 -0
- package/dist/compile-verify.js.map +1 -0
- package/dist/compressible-router.js +64 -11
- package/dist/compressible-router.js.map +1 -1
- package/dist/compression/asset-model.d.ts +15 -0
- package/dist/compression/compact.d.ts +5 -4
- package/dist/compression/compact.js +13 -10
- package/dist/compression/compact.js.map +1 -1
- package/dist/dialog-dismiss.d.ts +127 -74
- package/dist/dialog-dismiss.js +427 -170
- package/dist/dialog-dismiss.js.map +1 -1
- package/dist/dialog-policy.d.ts +141 -0
- package/dist/dialog-policy.js +345 -0
- package/dist/dialog-policy.js.map +1 -0
- package/dist/gate-error.d.ts +6 -0
- package/dist/gate-error.js +13 -2
- package/dist/gate-error.js.map +1 -1
- package/dist/hub-control.d.ts +247 -0
- package/dist/hub-control.js +703 -0
- package/dist/hub-control.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/dist/live-client.d.ts +123 -3
- package/dist/live-client.js +555 -69
- package/dist/live-client.js.map +1 -1
- package/dist/offline.d.ts +105 -0
- package/dist/offline.js +816 -23
- package/dist/offline.js.map +1 -1
- package/dist/output-profile.d.ts +110 -0
- package/dist/output-profile.js +255 -0
- package/dist/output-profile.js.map +1 -0
- package/dist/retry-policy.d.ts +72 -0
- package/dist/retry-policy.js +146 -0
- package/dist/retry-policy.js.map +1 -0
- package/dist/routers.d.ts +4 -0
- package/dist/routers.js +2 -2
- package/dist/routers.js.map +1 -1
- package/dist/server-instructions.d.ts +18 -0
- package/dist/server-instructions.js +145 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/tool-router.d.ts +38 -1
- package/dist/tool-router.js +690 -37
- package/dist/tool-router.js.map +1 -1
- package/dist/tool-session-state.d.ts +37 -0
- package/dist/tool-session-state.js +78 -1
- package/dist/tool-session-state.js.map +1 -1
- package/dist/tools/apply-fix.js +16 -2
- package/dist/tools/apply-fix.js.map +1 -1
- package/dist/tools/asmdef-create.d.ts +2 -0
- package/dist/tools/asmdef-create.js +114 -0
- package/dist/tools/asmdef-create.js.map +1 -0
- package/dist/tools/asmdef-get.d.ts +2 -0
- package/dist/tools/asmdef-get.js +31 -0
- package/dist/tools/asmdef-get.js.map +1 -0
- package/dist/tools/asmdef-list.d.ts +2 -0
- package/dist/tools/asmdef-list.js +41 -0
- package/dist/tools/asmdef-list.js.map +1 -0
- package/dist/tools/asmdef-modify.d.ts +2 -0
- package/dist/tools/asmdef-modify.js +108 -0
- package/dist/tools/asmdef-modify.js.map +1 -0
- package/dist/tools/audio-listener-get.d.ts +2 -0
- package/dist/tools/audio-listener-get.js +16 -0
- package/dist/tools/audio-listener-get.js.map +1 -0
- package/dist/tools/audio-mixer-get-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-get-parameter.js +25 -0
- package/dist/tools/audio-mixer-get-parameter.js.map +1 -0
- package/dist/tools/audio-mixer-set-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-set-parameter.js +44 -0
- package/dist/tools/audio-mixer-set-parameter.js.map +1 -0
- package/dist/tools/audio-source-add.d.ts +2 -0
- package/dist/tools/audio-source-add.js +94 -0
- package/dist/tools/audio-source-add.js.map +1 -0
- package/dist/tools/audio-source-modify.d.ts +2 -0
- package/dist/tools/audio-source-modify.js +52 -0
- package/dist/tools/audio-source-modify.js.map +1 -0
- package/dist/tools/bridge-status.js +16 -6
- package/dist/tools/bridge-status.js.map +1 -1
- package/dist/tools/build-get-active-target.js +1 -2
- package/dist/tools/build-get-active-target.js.map +1 -1
- package/dist/tools/build-get-defines.js +1 -1
- package/dist/tools/build-get-defines.js.map +1 -1
- package/dist/tools/build-get-scenes.js +1 -2
- package/dist/tools/build-get-scenes.js.map +1 -1
- package/dist/tools/build-get-targets.js +1 -1
- package/dist/tools/build-get-targets.js.map +1 -1
- package/dist/tools/build-set-defines.js +1 -2
- package/dist/tools/build-set-defines.js.map +1 -1
- package/dist/tools/build-set-scenes.js +1 -1
- package/dist/tools/build-set-scenes.js.map +1 -1
- package/dist/tools/build-set-target.js +1 -1
- package/dist/tools/build-set-target.js.map +1 -1
- package/dist/tools/build-start.js +0 -1
- package/dist/tools/build-start.js.map +1 -1
- package/dist/tools/cinemachine-brain-ensure.d.ts +2 -0
- package/dist/tools/cinemachine-brain-ensure.js +27 -0
- package/dist/tools/cinemachine-brain-ensure.js.map +1 -0
- package/dist/tools/cinemachine-camera-list.d.ts +2 -0
- package/dist/tools/cinemachine-camera-list.js +15 -0
- package/dist/tools/cinemachine-camera-list.js.map +1 -0
- package/dist/tools/cinemachine-create-camera.d.ts +2 -0
- package/dist/tools/cinemachine-create-camera.js +55 -0
- package/dist/tools/cinemachine-create-camera.js.map +1 -0
- package/dist/tools/cinemachine-set-body.d.ts +2 -0
- package/dist/tools/cinemachine-set-body.js +33 -0
- package/dist/tools/cinemachine-set-body.js.map +1 -0
- package/dist/tools/cinemachine-set-lens.d.ts +2 -0
- package/dist/tools/cinemachine-set-lens.js +41 -0
- package/dist/tools/cinemachine-set-lens.js.map +1 -0
- package/dist/tools/cinemachine-set-noise.d.ts +2 -0
- package/dist/tools/cinemachine-set-noise.js +31 -0
- package/dist/tools/cinemachine-set-noise.js.map +1 -0
- package/dist/tools/cinemachine-set-targets.d.ts +2 -0
- package/dist/tools/cinemachine-set-targets.js +30 -0
- package/dist/tools/cinemachine-set-targets.js.map +1 -0
- package/dist/tools/console-log.js +2 -2
- package/dist/tools/console-log.js.map +1 -1
- package/dist/tools/constraint-add.d.ts +2 -0
- package/dist/tools/constraint-add.js +85 -0
- package/dist/tools/constraint-add.js.map +1 -0
- package/dist/tools/dependencies.d.ts +2 -0
- package/dist/tools/dependencies.js +76 -0
- package/dist/tools/dependencies.js.map +1 -0
- package/dist/tools/editor-add-tag.js +2 -3
- package/dist/tools/editor-add-tag.js.map +1 -1
- package/dist/tools/editor-clear-history.d.ts +2 -0
- package/dist/tools/editor-clear-history.js +25 -0
- package/dist/tools/editor-clear-history.js.map +1 -0
- package/dist/tools/editor-get-layers.js +3 -3
- package/dist/tools/editor-get-layers.js.map +1 -1
- package/dist/tools/editor-get-tags.js +2 -2
- package/dist/tools/editor-get-tags.js.map +1 -1
- package/dist/tools/editor-redo.js +2 -3
- package/dist/tools/editor-redo.js.map +1 -1
- package/dist/tools/editor-undo-history.d.ts +2 -0
- package/dist/tools/editor-undo-history.js +21 -0
- package/dist/tools/editor-undo-history.js.map +1 -0
- package/dist/tools/editor-undo.js +3 -3
- package/dist/tools/editor-undo.js.map +1 -1
- package/dist/tools/editorprefs-delete.d.ts +2 -0
- package/dist/tools/editorprefs-delete.js +23 -0
- package/dist/tools/editorprefs-delete.js.map +1 -0
- package/dist/tools/editorprefs-get.d.ts +2 -0
- package/dist/tools/editorprefs-get.js +28 -0
- package/dist/tools/editorprefs-get.js.map +1 -0
- package/dist/tools/editorprefs-set.d.ts +2 -0
- package/dist/tools/editorprefs-set.js +33 -0
- package/dist/tools/editorprefs-set.js.map +1 -0
- package/dist/tools/find-references.js +24 -4
- package/dist/tools/find-references.js.map +1 -1
- package/dist/tools/frame-debugger.d.ts +2 -0
- package/dist/tools/frame-debugger.js +48 -0
- package/dist/tools/frame-debugger.js.map +1 -0
- package/dist/tools/gameobject-modify.js +69 -2
- package/dist/tools/gameobject-modify.js.map +1 -1
- package/dist/tools/gate-budget-estimate.js +2 -1
- package/dist/tools/gate-budget-estimate.js.map +1 -1
- package/dist/tools/hub-available-releases.d.ts +2 -0
- package/dist/tools/hub-available-releases.js +23 -0
- package/dist/tools/hub-available-releases.js.map +1 -0
- package/dist/tools/hub-get-install-path.d.ts +2 -0
- package/dist/tools/hub-get-install-path.js +20 -0
- package/dist/tools/hub-get-install-path.js.map +1 -0
- package/dist/tools/hub-install-editor.d.ts +2 -0
- package/dist/tools/hub-install-editor.js +47 -0
- package/dist/tools/hub-install-editor.js.map +1 -0
- package/dist/tools/hub-install-modules.d.ts +2 -0
- package/dist/tools/hub-install-modules.js +44 -0
- package/dist/tools/hub-install-modules.js.map +1 -0
- package/dist/tools/hub-list-editors.d.ts +2 -0
- package/dist/tools/hub-list-editors.js +25 -0
- package/dist/tools/hub-list-editors.js.map +1 -0
- package/dist/tools/hub-set-install-path.d.ts +2 -0
- package/dist/tools/hub-set-install-path.js +30 -0
- package/dist/tools/hub-set-install-path.js.map +1 -0
- package/dist/tools/impact-preview.js +2 -2
- package/dist/tools/impact-preview.js.map +1 -1
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.js +534 -24
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/light-add.d.ts +2 -0
- package/dist/tools/light-add.js +73 -0
- package/dist/tools/light-add.js.map +1 -0
- package/dist/tools/light-modify.d.ts +2 -0
- package/dist/tools/light-modify.js +36 -0
- package/dist/tools/light-modify.js.map +1 -0
- package/dist/tools/light-set.d.ts +2 -0
- package/dist/tools/light-set.js +54 -0
- package/dist/tools/light-set.js.map +1 -0
- package/dist/tools/list-assets-of-type.d.ts +2 -0
- package/dist/tools/list-assets-of-type.js +54 -0
- package/dist/tools/list-assets-of-type.js.map +1 -0
- package/dist/tools/lod-add-level.d.ts +2 -0
- package/dist/tools/lod-add-level.js +66 -0
- package/dist/tools/lod-add-level.js.map +1 -0
- package/dist/tools/lod-group-configure.d.ts +2 -0
- package/dist/tools/lod-group-configure.js +64 -0
- package/dist/tools/lod-group-configure.js.map +1 -0
- package/dist/tools/manage-tools.js +7 -5
- package/dist/tools/manage-tools.js.map +1 -1
- package/dist/tools/material-set-property.js +5 -1
- package/dist/tools/material-set-property.js.map +1 -1
- package/dist/tools/memory-snapshot-capture.d.ts +2 -0
- package/dist/tools/memory-snapshot-capture.js +61 -0
- package/dist/tools/memory-snapshot-capture.js.map +1 -0
- package/dist/tools/mutation-explain.js +2 -2
- package/dist/tools/mutation-explain.js.map +1 -1
- package/dist/tools/playerprefs-delete.d.ts +2 -0
- package/dist/tools/playerprefs-delete.js +24 -0
- package/dist/tools/playerprefs-delete.js.map +1 -0
- package/dist/tools/playerprefs-get.d.ts +2 -0
- package/dist/tools/playerprefs-get.js +28 -0
- package/dist/tools/playerprefs-get.js.map +1 -0
- package/dist/tools/playerprefs-set.d.ts +2 -0
- package/dist/tools/playerprefs-set.js +37 -0
- package/dist/tools/playerprefs-set.js.map +1 -0
- package/dist/tools/profiler-capture-frame.d.ts +2 -0
- package/dist/tools/profiler-capture-frame.js +75 -0
- package/dist/tools/profiler-capture-frame.js.map +1 -0
- package/dist/tools/profiler-clear-data.js +1 -2
- package/dist/tools/profiler-clear-data.js.map +1 -1
- package/dist/tools/profiler-enable-module.js +4 -5
- package/dist/tools/profiler-enable-module.js.map +1 -1
- package/dist/tools/profiler-get-status.js +5 -6
- package/dist/tools/profiler-get-status.js.map +1 -1
- package/dist/tools/profiler-load-data.js +4 -5
- package/dist/tools/profiler-load-data.js.map +1 -1
- package/dist/tools/profiler-save-data.js +3 -3
- package/dist/tools/profiler-save-data.js.map +1 -1
- package/dist/tools/profiler-set-config.js +5 -5
- package/dist/tools/profiler-set-config.js.map +1 -1
- package/dist/tools/profiler-start.js +4 -4
- package/dist/tools/profiler-start.js.map +1 -1
- package/dist/tools/profiler-stop.js +2 -3
- package/dist/tools/profiler-stop.js.map +1 -1
- package/dist/tools/read-asset.js +21 -3
- package/dist/tools/read-asset.js.map +1 -1
- package/dist/tools/reflection-probe-bake.d.ts +2 -0
- package/dist/tools/reflection-probe-bake.js +47 -0
- package/dist/tools/reflection-probe-bake.js.map +1 -0
- package/dist/tools/reflection-probe-get.d.ts +2 -0
- package/dist/tools/reflection-probe-get.js +19 -0
- package/dist/tools/reflection-probe-get.js.map +1 -0
- package/dist/tools/reimport-package.d.ts +2 -0
- package/dist/tools/reimport-package.js +53 -0
- package/dist/tools/reimport-package.js.map +1 -0
- package/dist/tools/scan-paths.js +19 -0
- package/dist/tools/scan-paths.js.map +1 -1
- package/dist/tools/scene-focus.js +1 -1
- package/dist/tools/scene-focus.js.map +1 -1
- package/dist/tools/scene-get-data.js +31 -13
- package/dist/tools/scene-get-data.js.map +1 -1
- package/dist/tools/scene-get-dirty-summary.js +1 -1
- package/dist/tools/scene-get-dirty-summary.js.map +1 -1
- package/dist/tools/sceneview-get-camera.d.ts +2 -0
- package/dist/tools/sceneview-get-camera.js +16 -0
- package/dist/tools/sceneview-get-camera.js.map +1 -0
- package/dist/tools/sceneview-set-camera.d.ts +2 -0
- package/dist/tools/sceneview-set-camera.js +58 -0
- package/dist/tools/sceneview-set-camera.js.map +1 -0
- package/dist/tools/script-write.js +2 -2
- package/dist/tools/script-write.js.map +1 -1
- package/dist/tools/scriptableobject-create.d.ts +2 -0
- package/dist/tools/scriptableobject-create.js +86 -0
- package/dist/tools/scriptableobject-create.js.map +1 -0
- package/dist/tools/search-assets.js +20 -3
- package/dist/tools/search-assets.js.map +1 -1
- package/dist/tools/settings-get-lighting.js +3 -4
- package/dist/tools/settings-get-lighting.js.map +1 -1
- package/dist/tools/settings-get-player.js +2 -2
- package/dist/tools/settings-get-player.js.map +1 -1
- package/dist/tools/settings-get-quality.js +1 -2
- package/dist/tools/settings-get-quality.js.map +1 -1
- package/dist/tools/settings-get-render-pipeline.d.ts +2 -0
- package/dist/tools/settings-get-render-pipeline.js +19 -0
- package/dist/tools/settings-get-render-pipeline.js.map +1 -0
- package/dist/tools/settings-get-time.d.ts +2 -0
- package/dist/tools/settings-get-time.js +18 -0
- package/dist/tools/settings-get-time.js.map +1 -0
- package/dist/tools/settings-set-lighting.js +5 -5
- package/dist/tools/settings-set-lighting.js.map +1 -1
- package/dist/tools/settings-set-physics.js +2 -2
- package/dist/tools/settings-set-physics.js.map +1 -1
- package/dist/tools/settings-set-player.js +2 -2
- package/dist/tools/settings-set-player.js.map +1 -1
- package/dist/tools/settings-set-quality-level.d.ts +2 -0
- package/dist/tools/settings-set-quality-level.js +44 -0
- package/dist/tools/settings-set-quality-level.js.map +1 -0
- package/dist/tools/settings-set-quality.js +2 -2
- package/dist/tools/settings-set-quality.js.map +1 -1
- package/dist/tools/settings-set-time.d.ts +2 -0
- package/dist/tools/settings-set-time.js +53 -0
- package/dist/tools/settings-set-time.js.map +1 -0
- package/dist/tools/shader-get-data.js +3 -3
- package/dist/tools/shader-get-data.js.map +1 -1
- package/dist/tools/shader-graph-create.d.ts +2 -0
- package/dist/tools/shader-graph-create.js +40 -0
- package/dist/tools/shader-graph-create.js.map +1 -0
- package/dist/tools/shader-graph-node-add.d.ts +2 -0
- package/dist/tools/shader-graph-node-add.js +50 -0
- package/dist/tools/shader-graph-node-add.js.map +1 -0
- package/dist/tools/shader-graph-node-connect.d.ts +2 -0
- package/dist/tools/shader-graph-node-connect.js +60 -0
- package/dist/tools/shader-graph-node-connect.js.map +1 -0
- package/dist/tools/shader-graph-open.d.ts +2 -0
- package/dist/tools/shader-graph-open.js +29 -0
- package/dist/tools/shader-graph-open.js.map +1 -0
- package/dist/tools/skybox-get.d.ts +2 -0
- package/dist/tools/skybox-get.js +13 -0
- package/dist/tools/skybox-get.js.map +1 -0
- package/dist/tools/skybox-set.d.ts +2 -0
- package/dist/tools/skybox-set.js +34 -0
- package/dist/tools/skybox-set.js.map +1 -0
- package/dist/tools/spriteatlas-add-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-add-packable.js +40 -0
- package/dist/tools/spriteatlas-add-packable.js.map +1 -0
- package/dist/tools/spriteatlas-create.d.ts +2 -0
- package/dist/tools/spriteatlas-create.js +41 -0
- package/dist/tools/spriteatlas-create.js.map +1 -0
- package/dist/tools/spriteatlas-delete.d.ts +2 -0
- package/dist/tools/spriteatlas-delete.js +32 -0
- package/dist/tools/spriteatlas-delete.js.map +1 -0
- package/dist/tools/spriteatlas-get.d.ts +2 -0
- package/dist/tools/spriteatlas-get.js +23 -0
- package/dist/tools/spriteatlas-get.js.map +1 -0
- package/dist/tools/spriteatlas-list.d.ts +2 -0
- package/dist/tools/spriteatlas-list.js +21 -0
- package/dist/tools/spriteatlas-list.js.map +1 -0
- package/dist/tools/spriteatlas-modify.d.ts +2 -0
- package/dist/tools/spriteatlas-modify.js +55 -0
- package/dist/tools/spriteatlas-modify.js.map +1 -0
- package/dist/tools/spriteatlas-remove-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-remove-packable.js +39 -0
- package/dist/tools/spriteatlas-remove-packable.js.map +1 -0
- package/dist/tools/terrain-create.d.ts +2 -0
- package/dist/tools/terrain-create.js +73 -0
- package/dist/tools/terrain-create.js.map +1 -0
- package/dist/tools/terrain-paint-layer.d.ts +2 -0
- package/dist/tools/terrain-paint-layer.js +85 -0
- package/dist/tools/terrain-paint-layer.js.map +1 -0
- package/dist/tools/terrain-place-trees.d.ts +2 -0
- package/dist/tools/terrain-place-trees.js +92 -0
- package/dist/tools/terrain-place-trees.js.map +1 -0
- package/dist/tools/terrain-set-heights.d.ts +2 -0
- package/dist/tools/terrain-set-heights.js +68 -0
- package/dist/tools/terrain-set-heights.js.map +1 -0
- package/dist/tools/terrain-set-neighbors.d.ts +2 -0
- package/dist/tools/terrain-set-neighbors.js +65 -0
- package/dist/tools/terrain-set-neighbors.js.map +1 -0
- package/dist/tools/texture-get-importer.d.ts +2 -0
- package/dist/tools/texture-get-importer.js +25 -0
- package/dist/tools/texture-get-importer.js.map +1 -0
- package/dist/tools/texture-get.d.ts +2 -0
- package/dist/tools/texture-get.js +23 -0
- package/dist/tools/texture-get.js.map +1 -0
- package/dist/tools/texture-reimport.d.ts +2 -0
- package/dist/tools/texture-reimport.js +33 -0
- package/dist/tools/texture-reimport.js.map +1 -0
- package/dist/tools/texture-set-import.d.ts +2 -0
- package/dist/tools/texture-set-import.js +52 -0
- package/dist/tools/texture-set-import.js.map +1 -0
- package/dist/tools/tilemap-box-fill.d.ts +2 -0
- package/dist/tools/tilemap-box-fill.js +32 -0
- package/dist/tools/tilemap-box-fill.js.map +1 -0
- package/dist/tools/tilemap-create-rule-tile.d.ts +2 -0
- package/dist/tools/tilemap-create-rule-tile.js +39 -0
- package/dist/tools/tilemap-create-rule-tile.js.map +1 -0
- package/dist/tools/tilemap-create-tile-asset.d.ts +2 -0
- package/dist/tools/tilemap-create-tile-asset.js +35 -0
- package/dist/tools/tilemap-create-tile-asset.js.map +1 -0
- package/dist/tools/tilemap-create.d.ts +2 -0
- package/dist/tools/tilemap-create.js +32 -0
- package/dist/tools/tilemap-create.js.map +1 -0
- package/dist/tools/tilemap-set-tile.d.ts +2 -0
- package/dist/tools/tilemap-set-tile.js +33 -0
- package/dist/tools/tilemap-set-tile.js.map +1 -0
- package/dist/tools/timeline-clip-add.d.ts +2 -0
- package/dist/tools/timeline-clip-add.js +43 -0
- package/dist/tools/timeline-clip-add.js.map +1 -0
- package/dist/tools/timeline-create.d.ts +2 -0
- package/dist/tools/timeline-create.js +32 -0
- package/dist/tools/timeline-create.js.map +1 -0
- package/dist/tools/timeline-director-bind.d.ts +2 -0
- package/dist/tools/timeline-director-bind.js +34 -0
- package/dist/tools/timeline-director-bind.js.map +1 -0
- package/dist/tools/timeline-modify.d.ts +2 -0
- package/dist/tools/timeline-modify.js +44 -0
- package/dist/tools/timeline-modify.js.map +1 -0
- package/dist/tools/timeline-track-add.d.ts +2 -0
- package/dist/tools/timeline-track-add.js +37 -0
- package/dist/tools/timeline-track-add.js.map +1 -0
- package/dist/tools/ui-canvas-add.d.ts +2 -0
- package/dist/tools/ui-canvas-add.js +66 -0
- package/dist/tools/ui-canvas-add.js.map +1 -0
- package/dist/tools/ui-element-add.d.ts +2 -0
- package/dist/tools/ui-element-add.js +65 -0
- package/dist/tools/ui-element-add.js.map +1 -0
- package/dist/tools/ui-element-modify.d.ts +2 -0
- package/dist/tools/ui-element-modify.js +59 -0
- package/dist/tools/ui-element-modify.js.map +1 -0
- package/dist/tools/ui-layout-group-add.d.ts +2 -0
- package/dist/tools/ui-layout-group-add.js +80 -0
- package/dist/tools/ui-layout-group-add.js.map +1 -0
- package/dist/tools/validate-edit.js +22 -1
- package/dist/tools/validate-edit.js.map +1 -1
- package/dist/tools/vfx-block-edit.d.ts +2 -0
- package/dist/tools/vfx-block-edit.js +51 -0
- package/dist/tools/vfx-block-edit.js.map +1 -0
- package/dist/tools/vfx-list.d.ts +2 -0
- package/dist/tools/vfx-list.js +31 -0
- package/dist/tools/vfx-list.js.map +1 -0
- package/dist/tools/vfx-open.d.ts +2 -0
- package/dist/tools/vfx-open.js +29 -0
- package/dist/tools/vfx-open.js.map +1 -0
- package/package.json +1 -1
package/dist/offline.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Offline asset reader — parses text-serialized Unity assets (.prefab/.unity/
|
|
2
|
-
// .asset) from disk without a running Editor.
|
|
3
|
-
// internal/unityasset (yaml.go, scan.go, kind.go, project.go).
|
|
2
|
+
// .asset) from disk without a running Editor.
|
|
4
3
|
//
|
|
5
4
|
// Single runtime module (so it loads cleanly under node --experimental-strip-
|
|
6
5
|
// types in tests: no cross-file runtime imports). Type-only contract lives in
|
|
@@ -13,7 +12,7 @@
|
|
|
13
12
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
14
13
|
import { join, basename, extname, relative, sep } from "node:path";
|
|
15
14
|
// ===========================================================================
|
|
16
|
-
// Native Unity class IDs → names
|
|
15
|
+
// Native Unity class IDs → names.
|
|
17
16
|
// ===========================================================================
|
|
18
17
|
const nativeClassNames = {
|
|
19
18
|
1: "GameObject",
|
|
@@ -45,10 +44,11 @@ function nativeClassName(classID) {
|
|
|
45
44
|
return nativeClassNames[classID] ?? "";
|
|
46
45
|
}
|
|
47
46
|
// ===========================================================================
|
|
48
|
-
// Kind / extension utilities
|
|
47
|
+
// Kind / extension utilities.
|
|
49
48
|
// ===========================================================================
|
|
50
49
|
const extToKind = {
|
|
51
50
|
".anim": "anim",
|
|
51
|
+
".asmdef": "asmdef",
|
|
52
52
|
".asset": "asset",
|
|
53
53
|
".controller": "controller",
|
|
54
54
|
".cs": "cs",
|
|
@@ -58,13 +58,47 @@ const extToKind = {
|
|
|
58
58
|
".physicMaterial": "physics",
|
|
59
59
|
".playable": "playable",
|
|
60
60
|
".prefab": "prefab",
|
|
61
|
+
".preset": "preset",
|
|
61
62
|
".shader": "shader",
|
|
63
|
+
".shadergraph": "shadergraph",
|
|
64
|
+
".shadersubgraph": "shadergraph",
|
|
62
65
|
".spriteatlas": "atlas",
|
|
66
|
+
".terrainlayer": "terrainlayer",
|
|
63
67
|
".unity": "scene",
|
|
64
68
|
".uss": "uss",
|
|
65
69
|
".uxml": "uxml",
|
|
70
|
+
".vfx": "vfx",
|
|
66
71
|
".meta": "meta",
|
|
67
72
|
};
|
|
73
|
+
// ===========================================================================
|
|
74
|
+
// M24 — offline-parseable extension sets.
|
|
75
|
+
// YAML_PARSEABLE covers text-serialized Unity YAML; JSON_PARSEABLE covers the
|
|
76
|
+
// JSON asset kinds unity-scanner does not handle (our differentiator). Both are
|
|
77
|
+
// merged into OFFLINE_PARSEABLE for the read_asset / search / find_references
|
|
78
|
+
// gates. LISTABLE stays broader (it never parses, only enumerates files).
|
|
79
|
+
// ===========================================================================
|
|
80
|
+
const YAML_PARSEABLE_EXTENSIONS = new Set([
|
|
81
|
+
".prefab", ".unity", ".asset", ".mat", ".controller", ".anim", ".playable",
|
|
82
|
+
".preset", ".spriteatlas", ".terrainlayer", ".vfx",
|
|
83
|
+
]);
|
|
84
|
+
const JSON_PARSEABLE_EXTENSIONS = new Set([
|
|
85
|
+
".asmdef", ".shadergraph", ".shadersubgraph",
|
|
86
|
+
]);
|
|
87
|
+
const OFFLINE_PARSEABLE_EXTENSIONS = new Set([
|
|
88
|
+
...YAML_PARSEABLE_EXTENSIONS,
|
|
89
|
+
...JSON_PARSEABLE_EXTENSIONS,
|
|
90
|
+
]);
|
|
91
|
+
function offlineParseKind(assetPath) {
|
|
92
|
+
const dot = assetPath.lastIndexOf(".");
|
|
93
|
+
if (dot < 0)
|
|
94
|
+
return null;
|
|
95
|
+
const ext = assetPath.slice(dot).toLowerCase();
|
|
96
|
+
if (YAML_PARSEABLE_EXTENSIONS.has(ext))
|
|
97
|
+
return "yaml";
|
|
98
|
+
if (JSON_PARSEABLE_EXTENSIONS.has(ext))
|
|
99
|
+
return "json";
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
68
102
|
const kindAliases = {
|
|
69
103
|
prefabs: "prefab",
|
|
70
104
|
scenes: "scene",
|
|
@@ -106,7 +140,6 @@ function shouldSkipDir(name) {
|
|
|
106
140
|
}
|
|
107
141
|
// ===========================================================================
|
|
108
142
|
// YAML parser — split YAML into documents, extract key fields per object.
|
|
109
|
-
// (Ported from unity-scanner yaml.go ParseAssetWithOptions.)
|
|
110
143
|
// ===========================================================================
|
|
111
144
|
function parseAsset(data) {
|
|
112
145
|
const objects = [];
|
|
@@ -848,7 +881,7 @@ function shortGUID(guid) {
|
|
|
848
881
|
return guid.slice(0, 8);
|
|
849
882
|
}
|
|
850
883
|
// ===========================================================================
|
|
851
|
-
// GUID index builder — reads .meta files
|
|
884
|
+
// GUID index builder — reads .meta files.
|
|
852
885
|
// ===========================================================================
|
|
853
886
|
function readMetaGUID(data) {
|
|
854
887
|
let start;
|
|
@@ -953,29 +986,65 @@ async function walkMeta(dir, fn) {
|
|
|
953
986
|
// ===========================================================================
|
|
954
987
|
// readAssetOffline — parse a text-serialized asset → AssetModel.
|
|
955
988
|
// ===========================================================================
|
|
956
|
-
const OFFLINE_EXTENSIONS = new Set([
|
|
957
|
-
".prefab", ".unity", ".asset", ".mat", ".controller", ".anim", ".playable",
|
|
958
|
-
]);
|
|
959
989
|
export function isOfflineAsset(assetPath) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
990
|
+
return offlineParseKind(assetPath) !== null;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* M24 — full-hierarchy parity helper. The offline `objectCount` counts every
|
|
994
|
+
* YAML object in the file; the live `read_asset` bridge counts only GameObjects
|
|
995
|
+
* + the components attached to them (it walks the loaded Transform tree, not
|
|
996
|
+
* the raw YAML). This helper returns the live-comparable counts so an
|
|
997
|
+
* acceptance test can verify the offline-reconstructed tree matches a live read
|
|
998
|
+
* node-for-node and component-for-component, without depending on the bridge.
|
|
999
|
+
*
|
|
1000
|
+
* Returns `{ nodes, components }` where `nodes` is the GameObject count and
|
|
1001
|
+
* `components` is the sum of components across the tree — the two numbers the
|
|
1002
|
+
* live `ReadAssetTool` emits as `objectCount` (nodes+components) and
|
|
1003
|
+
* `componentCount`.
|
|
1004
|
+
*/
|
|
1005
|
+
export function countHierarchy(model) {
|
|
1006
|
+
let nodes = 0;
|
|
1007
|
+
let components = 0;
|
|
1008
|
+
const walk = (list) => {
|
|
1009
|
+
for (const node of list) {
|
|
1010
|
+
nodes++;
|
|
1011
|
+
components += node.components.length;
|
|
1012
|
+
walk(node.children);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
walk(model.roots);
|
|
1016
|
+
return { nodes, components };
|
|
964
1017
|
}
|
|
965
1018
|
export async function readAssetOffline(assetPath, opts) {
|
|
966
1019
|
const absPath = join(opts.projectRoot, assetPath);
|
|
967
1020
|
const data = await readFile(absPath, "utf-8");
|
|
1021
|
+
const kind = kindForPath(assetPath);
|
|
1022
|
+
const guid = await safeReadMetaGUID(absPath + ".meta");
|
|
1023
|
+
const parseKind = offlineParseKind(assetPath);
|
|
1024
|
+
// M24 — JSON asset kinds (.asmdef/.shadergraph/.shadersubgraph/.vfx) parse
|
|
1025
|
+
// through a separate path; unity-scanner handles YAML only, so this is the
|
|
1026
|
+
// coverage differentiator. vfx is YAML+binary, but its object headers + GUID
|
|
1027
|
+
// refs still parse via the YAML path (binary blobs are skipped).
|
|
1028
|
+
if (parseKind === "json") {
|
|
1029
|
+
const guidIndex = await buildGUIDIndex(opts.projectRoot, new Set(findGUIDs(data)));
|
|
1030
|
+
const model = buildJsonAssetModel(data, assetPath, kind, guid, guidIndex, opts.fieldLimit);
|
|
1031
|
+
return { model, source: "offline" };
|
|
1032
|
+
}
|
|
968
1033
|
const parsed = parseAsset(data);
|
|
969
1034
|
parsed.path = assetPath;
|
|
970
|
-
parsed.kind =
|
|
971
|
-
parsed.guid =
|
|
1035
|
+
parsed.kind = kind;
|
|
1036
|
+
parsed.guid = guid;
|
|
972
1037
|
const wantedScripts = scriptGUIDs(parsed);
|
|
973
1038
|
const scriptIndex = await buildScriptIndex(opts.projectRoot, wantedScripts);
|
|
974
1039
|
const wantedGUIDs = collectFieldGUIDs(parsed);
|
|
975
1040
|
const guidIndex = await buildGUIDIndex(opts.projectRoot, wantedGUIDs);
|
|
976
1041
|
for (const [g, p] of scriptIndex)
|
|
977
1042
|
guidIndex.set(g, p);
|
|
978
|
-
|
|
1043
|
+
const integrity = runYamlIntegrityChecks(parsed, guidIndex);
|
|
1044
|
+
return {
|
|
1045
|
+
model: { ...buildAssetModel(parsed, scriptIndex, guidIndex, opts.fieldLimit), ...(integrity ? { integrity } : {}) },
|
|
1046
|
+
source: "offline",
|
|
1047
|
+
};
|
|
979
1048
|
}
|
|
980
1049
|
async function safeReadMetaGUID(metaPath) {
|
|
981
1050
|
try {
|
|
@@ -1041,6 +1110,261 @@ function buildAssetModel(parsed, scriptIndex, guidIndex, fieldLimit) {
|
|
|
1041
1110
|
...(overrides.length > 0 ? { overrides } : {}),
|
|
1042
1111
|
};
|
|
1043
1112
|
}
|
|
1113
|
+
// ===========================================================================
|
|
1114
|
+
// M24 — JSON asset parser (.asmdef / .shadergraph / .shadersubgraph).
|
|
1115
|
+
//
|
|
1116
|
+
// unity-scanner handles YAML only; this path is the coverage differentiator.
|
|
1117
|
+
// Each kind has a distinct on-disk shape:
|
|
1118
|
+
// - .asmdef → a single JSON object (assembly-definition manifest).
|
|
1119
|
+
// - .shadergraph / .shadersubgraph → a JSON-document stream: multiple pretty-
|
|
1120
|
+
// printed JSON objects concatenated on blank-line boundaries, each carrying
|
|
1121
|
+
// an "m_Type" discriminator. The first is always the graph root.
|
|
1122
|
+
// GUID references inside the JSON are resolved against the meta index the same
|
|
1123
|
+
// way YAML field refs are.
|
|
1124
|
+
// ===========================================================================
|
|
1125
|
+
function buildJsonAssetModel(data, assetPath, kind, guid, guidIndex, fieldLimit) {
|
|
1126
|
+
const issues = [];
|
|
1127
|
+
const objects = parseJsonAsset(data, issues);
|
|
1128
|
+
const flatObjects = [];
|
|
1129
|
+
for (const obj of objects) {
|
|
1130
|
+
const fields = jsonFields(obj.value, fieldLimit, guidIndex);
|
|
1131
|
+
flatObjects.push({
|
|
1132
|
+
name: obj.name,
|
|
1133
|
+
type: obj.type,
|
|
1134
|
+
fields: fields.length > 0 ? fields : undefined,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
const integrity = runJsonIntegrityChecks(kind, objects, issues, assetPath);
|
|
1138
|
+
return {
|
|
1139
|
+
kind,
|
|
1140
|
+
path: assetPath,
|
|
1141
|
+
guid: guid || undefined,
|
|
1142
|
+
objectCount: objects.length,
|
|
1143
|
+
componentCount: 0,
|
|
1144
|
+
roots: [],
|
|
1145
|
+
flatObjects,
|
|
1146
|
+
...(integrity.length > 0 ? { integrity } : {}),
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Parse a JSON asset into one or more JsonParsedObjects. `.asmdef` is a single
|
|
1151
|
+
* object; `.shadergraph` is a stream of objects. Malformed JSON records an
|
|
1152
|
+
* integrity issue instead of throwing — a partial read is more useful to an
|
|
1153
|
+
* agent than a hard failure when the bridge is down.
|
|
1154
|
+
*/
|
|
1155
|
+
function parseJsonAsset(data, issues) {
|
|
1156
|
+
const ext = "json";
|
|
1157
|
+
const chunks = splitJsonStream(data);
|
|
1158
|
+
if (chunks.length === 0) {
|
|
1159
|
+
issues.push({ code: "malformed_json", detail: "no JSON objects found", severity: "error" });
|
|
1160
|
+
return [];
|
|
1161
|
+
}
|
|
1162
|
+
const out = [];
|
|
1163
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1164
|
+
let value;
|
|
1165
|
+
try {
|
|
1166
|
+
value = JSON.parse(chunks[i]);
|
|
1167
|
+
}
|
|
1168
|
+
catch (e) {
|
|
1169
|
+
issues.push({
|
|
1170
|
+
code: "malformed_json",
|
|
1171
|
+
detail: `object ${i + 1}: ${e.message}`,
|
|
1172
|
+
severity: "error",
|
|
1173
|
+
});
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
out.push(jsonParsedObject(value, i));
|
|
1177
|
+
}
|
|
1178
|
+
return out;
|
|
1179
|
+
function jsonParsedObject(value, index) {
|
|
1180
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
1181
|
+
const obj = value;
|
|
1182
|
+
const type = typeof obj.m_Type === "string" && obj.m_Type !== ""
|
|
1183
|
+
? jsonShortType(obj.m_Type)
|
|
1184
|
+
: "json";
|
|
1185
|
+
const name = pickString(obj.m_Name) ??
|
|
1186
|
+
pickString(obj.m_ObjectId) ??
|
|
1187
|
+
pickString(obj.name) ??
|
|
1188
|
+
(type !== "json" ? `${type}#${index}` : `object#${index}`);
|
|
1189
|
+
return { name, type, value };
|
|
1190
|
+
}
|
|
1191
|
+
return { name: `value#${index}`, type: ext, value };
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function pickString(v) {
|
|
1195
|
+
return typeof v === "string" && v !== "" ? v : undefined;
|
|
1196
|
+
}
|
|
1197
|
+
/** Shorten a ShaderGraph m_Type like "UnityEditor.ShaderGraph.MultiplyNode" → "MultiplyNode". */
|
|
1198
|
+
function jsonShortType(full) {
|
|
1199
|
+
const dot = full.lastIndexOf(".");
|
|
1200
|
+
return dot >= 0 ? full.slice(dot + 1) : full;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* A `.shadergraph` is a stream of pretty-printed JSON objects. They are
|
|
1204
|
+
* separated by blank lines, but a single object's pretty-printed body also
|
|
1205
|
+
* contains newlines — so the split must track brace depth, not line gaps.
|
|
1206
|
+
* `.asmdef` is a single object; this still returns a one-element array.
|
|
1207
|
+
*/
|
|
1208
|
+
function splitJsonStream(data) {
|
|
1209
|
+
const trimmed = data.trim();
|
|
1210
|
+
if (trimmed === "")
|
|
1211
|
+
return [];
|
|
1212
|
+
const chunks = [];
|
|
1213
|
+
let depth = 0;
|
|
1214
|
+
let inString = false;
|
|
1215
|
+
let escape = false;
|
|
1216
|
+
let start = -1;
|
|
1217
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
1218
|
+
const ch = trimmed[i];
|
|
1219
|
+
if (inString) {
|
|
1220
|
+
if (escape)
|
|
1221
|
+
escape = false;
|
|
1222
|
+
else if (ch === "\\")
|
|
1223
|
+
escape = true;
|
|
1224
|
+
else if (ch === '"')
|
|
1225
|
+
inString = false;
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (ch === '"') {
|
|
1229
|
+
inString = true;
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
if (ch === "{") {
|
|
1233
|
+
if (depth === 0)
|
|
1234
|
+
start = i;
|
|
1235
|
+
depth++;
|
|
1236
|
+
}
|
|
1237
|
+
else if (ch === "}") {
|
|
1238
|
+
depth--;
|
|
1239
|
+
if (depth === 0 && start >= 0) {
|
|
1240
|
+
chunks.push(trimmed.slice(start, i + 1));
|
|
1241
|
+
start = -1;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// Fallback: if brace tracking found nothing (e.g. a top-level array or a
|
|
1246
|
+
// malformed file), try a single JSON.parse on the whole content so the
|
|
1247
|
+
// malformed_json issue path can report it.
|
|
1248
|
+
if (chunks.length === 0)
|
|
1249
|
+
chunks.push(trimmed);
|
|
1250
|
+
return chunks;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Flatten a parsed JSON value into display fields. Scalars, arrays, and nested
|
|
1254
|
+
* objects are summarized into short `name: value` rows, mirroring the YAML
|
|
1255
|
+
* field renderer. GUID refs are resolved against the meta index.
|
|
1256
|
+
*/
|
|
1257
|
+
function jsonFields(value, limit, guidIndex) {
|
|
1258
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
1259
|
+
return [];
|
|
1260
|
+
const obj = value;
|
|
1261
|
+
const out = [];
|
|
1262
|
+
for (const [key, raw] of Object.entries(obj)) {
|
|
1263
|
+
if (out.length >= limit && limit > 0)
|
|
1264
|
+
break;
|
|
1265
|
+
out.push({ name: key, value: jsonScalar(raw, guidIndex) });
|
|
1266
|
+
}
|
|
1267
|
+
return out;
|
|
1268
|
+
}
|
|
1269
|
+
function jsonScalar(value, guidIndex) {
|
|
1270
|
+
if (value === null)
|
|
1271
|
+
return "null";
|
|
1272
|
+
switch (typeof value) {
|
|
1273
|
+
case "string":
|
|
1274
|
+
return resolveReferences(value, guidIndex, EMPTY_PARSED);
|
|
1275
|
+
case "number":
|
|
1276
|
+
case "boolean":
|
|
1277
|
+
return String(value);
|
|
1278
|
+
default: {
|
|
1279
|
+
if (Array.isArray(value))
|
|
1280
|
+
return `[${value.length}]`;
|
|
1281
|
+
const obj = value;
|
|
1282
|
+
// Inline {fileID, guid, type} PPtr → resolve to asset name.
|
|
1283
|
+
if (typeof obj.guid === "string" && obj.guid.length === 32) {
|
|
1284
|
+
return resolveReferences(`{fileID: ${obj.fileID ?? 0}, guid: ${obj.guid}, type: ${obj.type ?? 0}}`, guidIndex, EMPTY_PARSED);
|
|
1285
|
+
}
|
|
1286
|
+
const keys = Object.keys(obj);
|
|
1287
|
+
if (keys.length === 0)
|
|
1288
|
+
return "{}";
|
|
1289
|
+
return `{${keys.slice(0, 4).join(", ")}${keys.length > 4 ? ", …" : ""}}`;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
// A ParsedAsset stand-in used only by resolveReferences' local-reference path.
|
|
1294
|
+
// JSON assets have no in-file fileIDs, so local ref resolution is a no-op.
|
|
1295
|
+
const EMPTY_PARSED = { path: "", kind: "", guid: "", objects: [], byID: new Map() };
|
|
1296
|
+
// ===========================================================================
|
|
1297
|
+
// M24 — offline integrity checks. The verify rule suite (M25) consumes these
|
|
1298
|
+
// signals; here we only emit asset-local findings derivable from raw bytes.
|
|
1299
|
+
// ===========================================================================
|
|
1300
|
+
function runYamlIntegrityChecks(parsed, guidIndex) {
|
|
1301
|
+
const issues = [];
|
|
1302
|
+
// Orphaned PrefabInstance: a PrefabInstance whose m_SourcePrefab GUID is
|
|
1303
|
+
// missing from the project (the base prefab was deleted or never imported).
|
|
1304
|
+
for (const obj of parsed.objects) {
|
|
1305
|
+
if (obj.type !== "PrefabInstance")
|
|
1306
|
+
continue;
|
|
1307
|
+
const sourceGuid = readGUIDField(obj.lines, "m_SourcePrefab");
|
|
1308
|
+
if (sourceGuid !== "" && !guidIndex.has(sourceGuid)) {
|
|
1309
|
+
issues.push({
|
|
1310
|
+
code: "orphaned_prefab_instance",
|
|
1311
|
+
detail: `m_SourcePrefab guid ${sourceGuid} not found in project`,
|
|
1312
|
+
severity: "warning",
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
// Unresolved field GUIDs (missing references) — every guid: in a field that
|
|
1317
|
+
// the meta index cannot resolve. Component m_Script guids get their own code
|
|
1318
|
+
// so a missing-script is distinguishable from a missing asset ref.
|
|
1319
|
+
const missingScripts = new Set();
|
|
1320
|
+
const missingRefs = new Set();
|
|
1321
|
+
for (const obj of parsed.objects) {
|
|
1322
|
+
for (const line of obj.lines) {
|
|
1323
|
+
if (!line.includes("guid:"))
|
|
1324
|
+
continue;
|
|
1325
|
+
const isScript = line.includes("m_Script");
|
|
1326
|
+
for (const g of findGUIDs(line)) {
|
|
1327
|
+
if (guidIndex.has(g))
|
|
1328
|
+
continue;
|
|
1329
|
+
if (isScript)
|
|
1330
|
+
missingScripts.add(g);
|
|
1331
|
+
else
|
|
1332
|
+
missingRefs.add(g);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
for (const g of missingScripts) {
|
|
1337
|
+
issues.push({ code: "missing_script_reference", detail: `script guid ${g} unresolved`, severity: "error" });
|
|
1338
|
+
}
|
|
1339
|
+
for (const g of missingRefs) {
|
|
1340
|
+
issues.push({ code: "missing_reference", detail: `asset guid ${g} unresolved`, severity: "warning" });
|
|
1341
|
+
}
|
|
1342
|
+
return issues;
|
|
1343
|
+
}
|
|
1344
|
+
function runJsonIntegrityChecks(kind, objects, parseIssues, _assetPath) {
|
|
1345
|
+
const issues = [...parseIssues];
|
|
1346
|
+
if (kind === "asmdef") {
|
|
1347
|
+
const first = objects[0]?.value;
|
|
1348
|
+
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
1349
|
+
const obj = first;
|
|
1350
|
+
if (pickString(obj.name) === undefined) {
|
|
1351
|
+
issues.push({ code: "asmdef_missing_name", detail: "asmdef has no 'name' field", severity: "error" });
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (kind === "shadergraph") {
|
|
1356
|
+
// A valid graph stream must start with the GraphData root.
|
|
1357
|
+
const rootType = objects[0]?.type;
|
|
1358
|
+
if (rootType !== undefined && !rootType.includes("GraphData")) {
|
|
1359
|
+
issues.push({
|
|
1360
|
+
code: "shadergraph_root_missing",
|
|
1361
|
+
detail: `first object is ${rootType}, expected a GraphData root`,
|
|
1362
|
+
severity: "warning",
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return issues;
|
|
1367
|
+
}
|
|
1044
1368
|
function convertNode(node, parsed, scriptIndex, guidIndex, fieldLimit) {
|
|
1045
1369
|
const comps = componentsFor(parsed, node.gameObject.id, scriptIndex);
|
|
1046
1370
|
const components = comps.map((c) => {
|
|
@@ -1069,9 +1393,7 @@ function toComponentField(f) {
|
|
|
1069
1393
|
// ===========================================================================
|
|
1070
1394
|
// searchAssetsOffline — scan Assets/ → SearchModel.
|
|
1071
1395
|
// ===========================================================================
|
|
1072
|
-
const SEARCHABLE_EXTENSIONS =
|
|
1073
|
-
".prefab", ".unity", ".asset", ".mat", ".controller", ".anim", ".playable",
|
|
1074
|
-
]);
|
|
1396
|
+
const SEARCHABLE_EXTENSIONS = OFFLINE_PARSEABLE_EXTENSIONS;
|
|
1075
1397
|
export async function searchAssetsOffline(opts) {
|
|
1076
1398
|
const nameQuery = (opts.name ?? "").trim().toLowerCase();
|
|
1077
1399
|
const componentQuery = (opts.component ?? "").trim().toLowerCase();
|
|
@@ -1098,6 +1420,24 @@ export async function searchAssetsOffline(opts) {
|
|
|
1098
1420
|
const kind = kindForPath(filePath);
|
|
1099
1421
|
if (typeFilter && typeFilter.size > 0 && !typeFilter.has(kind))
|
|
1100
1422
|
continue;
|
|
1423
|
+
const guid = await safeReadMetaGUID(filePath + ".meta");
|
|
1424
|
+
// M24 — JSON asset kinds route through a content-based match path (they
|
|
1425
|
+
// have no GameObject/component tree for the YAML matcher to walk).
|
|
1426
|
+
if (offlineParseKind(filePath) === "json") {
|
|
1427
|
+
let content;
|
|
1428
|
+
try {
|
|
1429
|
+
content = await readFile(filePath, "utf-8");
|
|
1430
|
+
}
|
|
1431
|
+
catch {
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
const match = checkJsonMatch(content, guidIndex, assetPath, kind, guid, {
|
|
1435
|
+
nameQuery, componentQuery, guidQuery,
|
|
1436
|
+
});
|
|
1437
|
+
if (match)
|
|
1438
|
+
matches.push(match);
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1101
1441
|
let parsed;
|
|
1102
1442
|
try {
|
|
1103
1443
|
parsed = parseAsset(await readFile(filePath, "utf-8"));
|
|
@@ -1107,7 +1447,6 @@ export async function searchAssetsOffline(opts) {
|
|
|
1107
1447
|
}
|
|
1108
1448
|
parsed.path = assetPath;
|
|
1109
1449
|
parsed.kind = kind;
|
|
1110
|
-
const guid = await safeReadMetaGUID(filePath + ".meta");
|
|
1111
1450
|
const match = checkMatch(parsed, scriptIndex, guidIndex, assetPath, kind, guid, { nameQuery, componentQuery, guidQuery });
|
|
1112
1451
|
if (match)
|
|
1113
1452
|
matches.push(match);
|
|
@@ -1172,6 +1511,39 @@ function checkMatch(parsed, scriptIndex, guidIndex, assetPath, kind, guid, crite
|
|
|
1172
1511
|
return null;
|
|
1173
1512
|
return { path: assetPath, guid: guid || undefined, kind, reasons, objects: objects.length > 0 ? objects : undefined };
|
|
1174
1513
|
}
|
|
1514
|
+
/**
|
|
1515
|
+
* M24 — content-based matcher for JSON asset kinds (.asmdef/.shadergraph).
|
|
1516
|
+
* They have no GameObject tree, so matching is by file name, raw-text GUID
|
|
1517
|
+
* presence, and content substring (name/component queries search the raw text
|
|
1518
|
+
* since JSON is human-readable). This keeps `search_assets` offline-capable for
|
|
1519
|
+
* the expanded type set.
|
|
1520
|
+
*/
|
|
1521
|
+
function checkJsonMatch(content, guidIndex, assetPath, kind, guid, criteria) {
|
|
1522
|
+
const reasons = [];
|
|
1523
|
+
const { nameQuery, componentQuery, guidQuery } = criteria;
|
|
1524
|
+
const fileName = basename(assetPath);
|
|
1525
|
+
const lower = content.toLowerCase();
|
|
1526
|
+
if (nameQuery && fileName.toLowerCase().includes(nameQuery))
|
|
1527
|
+
reasons.push("file-name");
|
|
1528
|
+
if (guidQuery) {
|
|
1529
|
+
if (guid.toLowerCase() === guidQuery)
|
|
1530
|
+
reasons.push("guid");
|
|
1531
|
+
else if (lower.includes(guidQuery))
|
|
1532
|
+
reasons.push("guid");
|
|
1533
|
+
}
|
|
1534
|
+
if (nameQuery && !reasons.includes("file-name") && lower.includes(nameQuery)) {
|
|
1535
|
+
reasons.push("gameobject");
|
|
1536
|
+
}
|
|
1537
|
+
if (componentQuery && lower.includes(componentQuery)) {
|
|
1538
|
+
reasons.push("component");
|
|
1539
|
+
}
|
|
1540
|
+
if (!componentQuery && !nameQuery && !guidQuery)
|
|
1541
|
+
reasons.push("file-name");
|
|
1542
|
+
if (reasons.length === 0)
|
|
1543
|
+
return null;
|
|
1544
|
+
void guidIndex;
|
|
1545
|
+
return { path: assetPath, guid: guid || undefined, kind, reasons };
|
|
1546
|
+
}
|
|
1175
1547
|
function flattenHierarchy(nodes) {
|
|
1176
1548
|
const out = [];
|
|
1177
1549
|
const walk = (ns) => {
|
|
@@ -1245,6 +1617,9 @@ const LISTABLE_EXTENSIONS = new Set([
|
|
|
1245
1617
|
".prefab", ".unity", ".asset", ".mat", ".controller", ".anim", ".playable",
|
|
1246
1618
|
".cs", ".shader", ".spriteatlas", ".physicMaterial", ".physicsMaterial2D",
|
|
1247
1619
|
".overrideController", ".uxml", ".uss",
|
|
1620
|
+
// M24 — expanded offline-parseable kinds (also listable so they show up in
|
|
1621
|
+
// directory listings; listing never parses, so they cost nothing to include).
|
|
1622
|
+
".asmdef", ".shadergraph", ".shadersubgraph", ".vfx", ".preset", ".terrainlayer",
|
|
1248
1623
|
]);
|
|
1249
1624
|
export async function listAssetsOffline(opts) {
|
|
1250
1625
|
const folder = opts.folder ?? "Assets";
|
|
@@ -1336,9 +1711,7 @@ async function walkFiles(dir, projectRoot, fn) {
|
|
|
1336
1711
|
// fast-filter (content.includes(guid)) → optional YAML parse for verbose
|
|
1337
1712
|
// locations → group + cap + collapse.
|
|
1338
1713
|
// ===========================================================================
|
|
1339
|
-
const REFERENCEABLE_EXTENSIONS =
|
|
1340
|
-
".prefab", ".unity", ".asset", ".mat", ".controller", ".anim", ".playable",
|
|
1341
|
-
]);
|
|
1714
|
+
const REFERENCEABLE_EXTENSIONS = OFFLINE_PARSEABLE_EXTENSIONS;
|
|
1342
1715
|
export async function findReferencesOffline(opts) {
|
|
1343
1716
|
const detail = (opts.detail ?? "normal");
|
|
1344
1717
|
const maxResults = opts.maxResults ?? 100;
|
|
@@ -1492,4 +1865,424 @@ function extractReferenceLocations(content, targetGuid, maxPerFile) {
|
|
|
1492
1865
|
}
|
|
1493
1866
|
return locations;
|
|
1494
1867
|
}
|
|
1868
|
+
/** Collect the forward edges declared by a single asset: every external GUID
|
|
1869
|
+
* reference (field PPtrs + m_Script + PrefabInstance m_SourcePrefab). This is
|
|
1870
|
+
* the offline forward-edge primitive — unity-scanner has no full forward-edge
|
|
1871
|
+
* builder, so this is authored net-new on the existing GUID-extraction
|
|
1872
|
+
* primitives (findGUIDs / readGUIDField / parseAsset). */
|
|
1873
|
+
function collectForwardEdges(parsed) {
|
|
1874
|
+
const edges = [];
|
|
1875
|
+
const seen = new Set();
|
|
1876
|
+
// 1. PrefabInstance.m_SourcePrefab — the base-prefab edge of a variant.
|
|
1877
|
+
for (const obj of parsed.objects) {
|
|
1878
|
+
if (obj.type !== "PrefabInstance")
|
|
1879
|
+
continue;
|
|
1880
|
+
const sourceGuid = readGUIDField(obj.lines, "m_SourcePrefab");
|
|
1881
|
+
if (sourceGuid !== "" && seen.add(`prefab:${sourceGuid}`)) {
|
|
1882
|
+
edges.push({ guid: sourceGuid, assetPath: "", kind: "prefab_source", resolved: false });
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
// 2. m_Script GUIDs on MonoBehaviours — the script-class edge.
|
|
1886
|
+
for (const obj of parsed.objects) {
|
|
1887
|
+
if (obj.type !== "MonoBehaviour")
|
|
1888
|
+
continue;
|
|
1889
|
+
if (obj.scriptGUID !== "" && seen.add(`script:${obj.scriptGUID}`)) {
|
|
1890
|
+
edges.push({ guid: obj.scriptGUID, assetPath: "", kind: "script", resolved: false });
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
// 3. Every other guid: field — material refs, asset refs, animation clips,
|
|
1894
|
+
// etc. m_Script is handled above; m_SourcePrefab target: GUIDs inside
|
|
1895
|
+
// PrefabInstance.m_Modifications are included here so variant overrides
|
|
1896
|
+
// count as forward edges too.
|
|
1897
|
+
for (const obj of parsed.objects) {
|
|
1898
|
+
for (const line of obj.lines) {
|
|
1899
|
+
if (!line.includes("guid:"))
|
|
1900
|
+
continue;
|
|
1901
|
+
if (line.includes("m_Script:"))
|
|
1902
|
+
continue; // handled as a script edge above
|
|
1903
|
+
for (const g of findGUIDs(line)) {
|
|
1904
|
+
if (!seen.add(`pptr:${g}`))
|
|
1905
|
+
continue;
|
|
1906
|
+
edges.push({ guid: g, assetPath: "", kind: "pptr", resolved: false });
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return edges;
|
|
1911
|
+
}
|
|
1912
|
+
/** Resolve forward edges against the GUID→path index. Mutates each edge in
|
|
1913
|
+
* place: sets assetPath + resolved. Returns the broken (unresolved) GUIDs. */
|
|
1914
|
+
function resolveForwardEdges(edges, guidIndex) {
|
|
1915
|
+
const broken = [];
|
|
1916
|
+
const brokenSeen = new Set();
|
|
1917
|
+
for (const edge of edges) {
|
|
1918
|
+
const path = guidIndex.get(edge.guid);
|
|
1919
|
+
if (path !== undefined) {
|
|
1920
|
+
edge.assetPath = path;
|
|
1921
|
+
edge.resolved = true;
|
|
1922
|
+
}
|
|
1923
|
+
else if (brokenSeen.add(edge.guid)) {
|
|
1924
|
+
broken.push(edge.guid);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
return broken;
|
|
1928
|
+
}
|
|
1929
|
+
export async function dependenciesOffline(opts) {
|
|
1930
|
+
const detail = (opts.detail ?? "normal");
|
|
1931
|
+
const maxResults = opts.maxResults ?? 100;
|
|
1932
|
+
const includeImpact = opts.includeImpact ?? false;
|
|
1933
|
+
const maxImpactDepth = opts.maxImpactDepth ?? 5;
|
|
1934
|
+
const maxCycleDepth = opts.maxCycleDepth ?? 8;
|
|
1935
|
+
// Resolve target GUID + path (mirrors findReferencesOffline's resolution).
|
|
1936
|
+
let targetGuid = "";
|
|
1937
|
+
let targetPath = "";
|
|
1938
|
+
if (opts.guid) {
|
|
1939
|
+
targetGuid = opts.guid.toLowerCase();
|
|
1940
|
+
}
|
|
1941
|
+
else if (opts.assetPath) {
|
|
1942
|
+
targetPath = opts.assetPath;
|
|
1943
|
+
targetGuid = (await safeReadMetaGUID(join(opts.projectRoot, opts.assetPath + ".meta"))).toLowerCase();
|
|
1944
|
+
}
|
|
1945
|
+
if (targetGuid === "") {
|
|
1946
|
+
return emptyDependencies(targetPath, "", detail);
|
|
1947
|
+
}
|
|
1948
|
+
if (targetPath === "") {
|
|
1949
|
+
const idx = await buildGUIDIndex(opts.projectRoot, new Set([targetGuid]));
|
|
1950
|
+
targetPath = idx.get(targetGuid) ?? "";
|
|
1951
|
+
}
|
|
1952
|
+
// ---- Forward edges ----
|
|
1953
|
+
// Parse the queried asset + collect its declared edges, then resolve each
|
|
1954
|
+
// against a GUID→path index scoped to exactly the GUIDs this asset names.
|
|
1955
|
+
let forwardEdges = [];
|
|
1956
|
+
let brokenGuids = [];
|
|
1957
|
+
let cycles = [];
|
|
1958
|
+
let forwardSkipped;
|
|
1959
|
+
const absPath = join(opts.projectRoot, targetPath);
|
|
1960
|
+
let data;
|
|
1961
|
+
try {
|
|
1962
|
+
data = await readFile(absPath, "utf-8");
|
|
1963
|
+
}
|
|
1964
|
+
catch {
|
|
1965
|
+
// The queried asset isn't on disk (GUID resolve but path is stale). No
|
|
1966
|
+
// forward edges can be extracted; reverse edges still computed below.
|
|
1967
|
+
forwardSkipped = "queried asset not readable on disk";
|
|
1968
|
+
}
|
|
1969
|
+
if (data !== undefined) {
|
|
1970
|
+
try {
|
|
1971
|
+
const parsed = parseAsset(data);
|
|
1972
|
+
parsed.path = targetPath;
|
|
1973
|
+
forwardEdges = collectForwardEdges(parsed);
|
|
1974
|
+
const wanted = new Set(forwardEdges.map((e) => e.guid));
|
|
1975
|
+
const guidIndex = await buildGUIDIndex(opts.projectRoot, wanted);
|
|
1976
|
+
brokenGuids = resolveForwardEdges(forwardEdges, guidIndex);
|
|
1977
|
+
if (forwardEdges.length > 0) {
|
|
1978
|
+
cycles = await detectCyclesOffline(targetPath, forwardEdges, opts.projectRoot, maxCycleDepth);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
catch (err) {
|
|
1982
|
+
// Not parseable as Unity YAML (JSON-only kind, binary, malformed). The
|
|
1983
|
+
// forward arrays stay empty; reverse edges are unaffected. This is the
|
|
1984
|
+
// honest signal — the offline forward parser covers text-serialized YAML
|
|
1985
|
+
// only, matching the documented offline coverage.
|
|
1986
|
+
forwardSkipped = err instanceof Error ? err.message : String(err);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
// ---- Reverse edges ---- (reuse the findReferencesOffline machinery).
|
|
1990
|
+
const refResult = await findReferencesOffline({
|
|
1991
|
+
assetPath: targetPath || undefined,
|
|
1992
|
+
guid: targetGuid,
|
|
1993
|
+
detail: "normal",
|
|
1994
|
+
projectRoot: opts.projectRoot,
|
|
1995
|
+
});
|
|
1996
|
+
const reverseEdges = refResult.referencedBy.map((e) => ({
|
|
1997
|
+
assetPath: e.assetPath,
|
|
1998
|
+
guid: e.guid ?? "",
|
|
1999
|
+
kind: e.kind,
|
|
2000
|
+
}));
|
|
2001
|
+
// ---- Transitive impact (optional) ----
|
|
2002
|
+
let impact;
|
|
2003
|
+
if (includeImpact) {
|
|
2004
|
+
impact = await computeTransitiveImpact(targetGuid, targetPath, opts.projectRoot, maxImpactDepth);
|
|
2005
|
+
}
|
|
2006
|
+
// ---- Apply detail + caps ----
|
|
2007
|
+
const displayForward = detail === "summary" ? [] : forwardEdges;
|
|
2008
|
+
const totalCount = reverseEdges.length;
|
|
2009
|
+
let displayReverse;
|
|
2010
|
+
let truncated = 0;
|
|
2011
|
+
if (detail === "summary") {
|
|
2012
|
+
displayReverse = [];
|
|
2013
|
+
}
|
|
2014
|
+
else if (maxResults > 0 && totalCount > maxResults) {
|
|
2015
|
+
displayReverse = reverseEdges.slice(0, maxResults);
|
|
2016
|
+
truncated = totalCount - maxResults;
|
|
2017
|
+
}
|
|
2018
|
+
else {
|
|
2019
|
+
displayReverse = reverseEdges;
|
|
2020
|
+
}
|
|
2021
|
+
return {
|
|
2022
|
+
queriedAssetPath: targetPath,
|
|
2023
|
+
queriedAssetGuid: targetGuid,
|
|
2024
|
+
forwardDependencies: displayForward,
|
|
2025
|
+
forwardCount: forwardEdges.length,
|
|
2026
|
+
brokenForwardGuids: brokenGuids,
|
|
2027
|
+
cycles,
|
|
2028
|
+
reverseDependencies: displayReverse,
|
|
2029
|
+
reverseCount: totalCount,
|
|
2030
|
+
impact,
|
|
2031
|
+
detail,
|
|
2032
|
+
truncated,
|
|
2033
|
+
...(forwardSkipped ? { forwardSkipped } : {}),
|
|
2034
|
+
_source: "offline",
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
function emptyDependencies(path, guid, detail) {
|
|
2038
|
+
return {
|
|
2039
|
+
queriedAssetPath: path,
|
|
2040
|
+
queriedAssetGuid: guid,
|
|
2041
|
+
forwardDependencies: [],
|
|
2042
|
+
forwardCount: 0,
|
|
2043
|
+
brokenForwardGuids: [],
|
|
2044
|
+
cycles: [],
|
|
2045
|
+
reverseDependencies: [],
|
|
2046
|
+
reverseCount: 0,
|
|
2047
|
+
detail,
|
|
2048
|
+
truncated: 0,
|
|
2049
|
+
_source: "offline",
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
/** Forward-cycle detection via DFS over the transitive forward closure of the
|
|
2053
|
+
* queried asset. Bounded by maxDepth to stay cheap on large graphs; a cycle
|
|
2054
|
+
* is reported when the DFS revisits the queried asset's path. This mirrors
|
|
2055
|
+
* the C# Dependencies.Scanner.DetectCycles shape (each cycle is a path list).
|
|
2056
|
+
*
|
|
2057
|
+
* unity-scanner has no cycle detector — this is authored net-new. */
|
|
2058
|
+
async function detectCyclesOffline(startPath, startEdges, projectRoot, maxDepth) {
|
|
2059
|
+
const cycles = [];
|
|
2060
|
+
// Build a small forward-edge cache so repeated visits of the same asset
|
|
2061
|
+
// parse once. Keyed by asset path.
|
|
2062
|
+
const edgeCache = new Map();
|
|
2063
|
+
edgeCache.set(startPath, startEdges);
|
|
2064
|
+
async function edgesOf(path) {
|
|
2065
|
+
const cached = edgeCache.get(path);
|
|
2066
|
+
if (cached)
|
|
2067
|
+
return cached;
|
|
2068
|
+
try {
|
|
2069
|
+
const data = await readFile(join(projectRoot, path), "utf-8");
|
|
2070
|
+
const parsed = parseAsset(data);
|
|
2071
|
+
const edges = collectForwardEdges(parsed);
|
|
2072
|
+
// Best-effort resolve so the DFS walks resolved paths only.
|
|
2073
|
+
const idx = await buildGUIDIndex(projectRoot, new Set(edges.map((e) => e.guid)));
|
|
2074
|
+
resolveForwardEdges(edges, idx);
|
|
2075
|
+
edgeCache.set(path, edges);
|
|
2076
|
+
return edges;
|
|
2077
|
+
}
|
|
2078
|
+
catch {
|
|
2079
|
+
edgeCache.set(path, []);
|
|
2080
|
+
return [];
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
// DFS from each resolved forward neighbor of the start asset, looking for a
|
|
2084
|
+
// path back to startPath. visited guards against re-descending the same node
|
|
2085
|
+
// within one root-neighbor walk (the cycle itself is the back-edge to start).
|
|
2086
|
+
const visiting = new Set();
|
|
2087
|
+
async function dfs(current, trail) {
|
|
2088
|
+
if (trail.length > maxDepth)
|
|
2089
|
+
return;
|
|
2090
|
+
const edges = await edgesOf(current);
|
|
2091
|
+
for (const edge of edges) {
|
|
2092
|
+
if (!edge.resolved || edge.assetPath === "")
|
|
2093
|
+
continue;
|
|
2094
|
+
if (edge.assetPath === startPath) {
|
|
2095
|
+
cycles.push([...trail, startPath]);
|
|
2096
|
+
continue;
|
|
2097
|
+
}
|
|
2098
|
+
if (visiting.has(edge.assetPath))
|
|
2099
|
+
continue;
|
|
2100
|
+
visiting.add(edge.assetPath);
|
|
2101
|
+
await dfs(edge.assetPath, [...trail, edge.assetPath]);
|
|
2102
|
+
visiting.delete(edge.assetPath);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
for (const edge of startEdges) {
|
|
2106
|
+
if (!edge.resolved || edge.assetPath === "" || edge.assetPath === startPath)
|
|
2107
|
+
continue;
|
|
2108
|
+
visiting.clear();
|
|
2109
|
+
visiting.add(edge.assetPath);
|
|
2110
|
+
await dfs(edge.assetPath, [startPath, edge.assetPath]);
|
|
2111
|
+
}
|
|
2112
|
+
return cycles;
|
|
2113
|
+
}
|
|
2114
|
+
/** Transitive impact closure via BFS over reverse edges. Starting from the
|
|
2115
|
+
* queried asset's direct referencers, walk one hop out at a time, accumulating
|
|
2116
|
+
* every asset that (transitively) depends on the queried asset. Bounded by
|
|
2117
|
+
* maxDepth. This is the "what breaks if I delete/move this?" answer — neither
|
|
2118
|
+
* the offline nor the live path did multi-hop reverse before T24.2. */
|
|
2119
|
+
async function computeTransitiveImpact(startGuid, startPath, projectRoot, maxDepth) {
|
|
2120
|
+
const affected = [];
|
|
2121
|
+
const seen = new Set([startPath]);
|
|
2122
|
+
let frontier = [];
|
|
2123
|
+
let truncated = false;
|
|
2124
|
+
// Seed: direct reverse edges of the queried asset.
|
|
2125
|
+
const seed = await findReferencesOffline({
|
|
2126
|
+
assetPath: startPath || undefined,
|
|
2127
|
+
guid: startGuid,
|
|
2128
|
+
detail: "normal",
|
|
2129
|
+
projectRoot,
|
|
2130
|
+
});
|
|
2131
|
+
for (const e of seed.referencedBy) {
|
|
2132
|
+
if (seen.has(e.assetPath))
|
|
2133
|
+
continue;
|
|
2134
|
+
seen.add(e.assetPath);
|
|
2135
|
+
frontier.push(e.assetPath);
|
|
2136
|
+
affected.push({ assetPath: e.assetPath, depth: 1 });
|
|
2137
|
+
}
|
|
2138
|
+
// BFS: for each level, resolve the reverse edges of every frontier asset.
|
|
2139
|
+
for (let depth = 2; depth <= maxDepth; depth++) {
|
|
2140
|
+
if (frontier.length === 0)
|
|
2141
|
+
break;
|
|
2142
|
+
const next = [];
|
|
2143
|
+
for (const nodePath of frontier) {
|
|
2144
|
+
const nodeGuid = await safeReadMetaGUID(join(projectRoot, nodePath + ".meta"));
|
|
2145
|
+
if (nodeGuid === "")
|
|
2146
|
+
continue;
|
|
2147
|
+
const res = await findReferencesOffline({
|
|
2148
|
+
guid: nodeGuid,
|
|
2149
|
+
detail: "normal",
|
|
2150
|
+
projectRoot,
|
|
2151
|
+
});
|
|
2152
|
+
for (const e of res.referencedBy) {
|
|
2153
|
+
if (seen.has(e.assetPath))
|
|
2154
|
+
continue;
|
|
2155
|
+
seen.add(e.assetPath);
|
|
2156
|
+
next.push(e.assetPath);
|
|
2157
|
+
affected.push({ assetPath: e.assetPath, depth });
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
frontier = next;
|
|
2161
|
+
if (depth === maxDepth && frontier.length > 0) {
|
|
2162
|
+
// The next level exists but we stop here — the closure is a prefix.
|
|
2163
|
+
truncated = true;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return {
|
|
2167
|
+
affected,
|
|
2168
|
+
affectedCount: affected.length,
|
|
2169
|
+
maxDepth,
|
|
2170
|
+
truncated,
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
/** Scan the whole Assets/ tree for project-wide integrity issues that the
|
|
2174
|
+
* per-read checks cannot surface (orphaned .meta, duplicate GUIDs) plus the
|
|
2175
|
+
* aggregated missing-reference set. Offline counterpart of a full verify
|
|
2176
|
+
* scan_paths run for these three rule families. */
|
|
2177
|
+
export async function scanIntegrityOffline(opts) {
|
|
2178
|
+
const issues = [];
|
|
2179
|
+
const assetsDir = join(opts.projectRoot, "Assets");
|
|
2180
|
+
// ---- Walk every .meta + every asset file in one pass ----
|
|
2181
|
+
// We collect: guidByPath (path -> guid), pathByGuid (guid -> paths[]), and
|
|
2182
|
+
// the set of asset files (companions for the orphan-meta check). Skipping a
|
|
2183
|
+
// dir uses the same skipDirs policy as every other offline walk.
|
|
2184
|
+
const guidByPath = new Map();
|
|
2185
|
+
const allAssetPaths = new Set();
|
|
2186
|
+
const assetFiles = await collectFiles(assetsDir);
|
|
2187
|
+
for (const absPath of assetFiles) {
|
|
2188
|
+
const assetPath = toAssetPath(opts.projectRoot, absPath);
|
|
2189
|
+
allAssetPaths.add(assetPath);
|
|
2190
|
+
const metaGuid = await safeReadMetaGUID(absPath + ".meta");
|
|
2191
|
+
if (metaGuid !== "") {
|
|
2192
|
+
guidByPath.set(assetPath, metaGuid);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
// Also index .meta-only paths (files whose companion was deleted show up
|
|
2196
|
+
// here but not in allAssetPaths — those are the orphans). walkMeta gives us
|
|
2197
|
+
// every .meta under Assets/.
|
|
2198
|
+
const metaOnlyPaths = new Set();
|
|
2199
|
+
await walkMeta(assetsDir, async (metaPath) => {
|
|
2200
|
+
const assetPath = toAssetPath(opts.projectRoot, metaPath.slice(0, -5));
|
|
2201
|
+
if (!allAssetPaths.has(assetPath)) {
|
|
2202
|
+
metaOnlyPaths.add(assetPath);
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2205
|
+
// ---- orphan_meta: .meta whose companion asset is gone ----
|
|
2206
|
+
for (const path of metaOnlyPaths) {
|
|
2207
|
+
issues.push({
|
|
2208
|
+
path: path + ".meta",
|
|
2209
|
+
code: "orphan_meta",
|
|
2210
|
+
severity: "warning",
|
|
2211
|
+
detail: `meta file has no companion asset at ${path}`,
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
// ---- duplicate_guid: GUID shared by 2+ assets ----
|
|
2215
|
+
const pathsByGuid = new Map();
|
|
2216
|
+
for (const [assetPath, guid] of guidByPath) {
|
|
2217
|
+
const list = pathsByGuid.get(guid);
|
|
2218
|
+
if (list)
|
|
2219
|
+
list.push(assetPath);
|
|
2220
|
+
else
|
|
2221
|
+
pathsByGuid.set(guid, [assetPath]);
|
|
2222
|
+
}
|
|
2223
|
+
for (const [guid, paths] of pathsByGuid) {
|
|
2224
|
+
if (paths.length < 2)
|
|
2225
|
+
continue;
|
|
2226
|
+
for (const p of paths) {
|
|
2227
|
+
issues.push({
|
|
2228
|
+
path: p,
|
|
2229
|
+
code: "duplicate_guid",
|
|
2230
|
+
severity: "error",
|
|
2231
|
+
detail: `guid ${guid} shared by ${paths.length} assets`,
|
|
2232
|
+
relatedPaths: paths.filter((other) => other !== p),
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
// ---- aggregated missing references (the per-read check, project-wide) ----
|
|
2237
|
+
// Re-uses the full GUID index so unresolved field GUIDs are detected once
|
|
2238
|
+
// per asset across the whole tree. This is the offline seed for the
|
|
2239
|
+
// missing_references verify rule (M25 consumes these primitives).
|
|
2240
|
+
const fullGuidIndex = await buildGUIDIndex(opts.projectRoot);
|
|
2241
|
+
for (const absPath of assetFiles) {
|
|
2242
|
+
const ext = extname(absPath).toLowerCase();
|
|
2243
|
+
if (!OFFLINE_PARSEABLE_EXTENSIONS.has(ext))
|
|
2244
|
+
continue;
|
|
2245
|
+
const assetPath = toAssetPath(opts.projectRoot, absPath);
|
|
2246
|
+
let data;
|
|
2247
|
+
try {
|
|
2248
|
+
data = await readFile(absPath, "utf-8");
|
|
2249
|
+
}
|
|
2250
|
+
catch {
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
if (!data.includes("guid:"))
|
|
2254
|
+
continue; // fast filter: no refs at all
|
|
2255
|
+
try {
|
|
2256
|
+
const parsed = parseAsset(data);
|
|
2257
|
+
parsed.path = assetPath;
|
|
2258
|
+
// Run the per-asset YAML integrity check against the FULL index (not a
|
|
2259
|
+
// scoped one) so a ref resolves even if its target lives far away.
|
|
2260
|
+
const perAsset = runYamlIntegrityChecks(parsed, fullGuidIndex);
|
|
2261
|
+
for (const issue of perAsset) {
|
|
2262
|
+
issues.push({
|
|
2263
|
+
path: assetPath,
|
|
2264
|
+
code: issue.code,
|
|
2265
|
+
severity: issue.severity,
|
|
2266
|
+
detail: issue.detail,
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
catch {
|
|
2271
|
+
// Not parseable as YAML — skip. JSON integrity is per-read only; a
|
|
2272
|
+
// project-wide JSON scan is out of scope for the verify-engine seed.
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
// ---- group by code ----
|
|
2276
|
+
const byCode = {};
|
|
2277
|
+
for (const issue of issues) {
|
|
2278
|
+
byCode[issue.code] = (byCode[issue.code] ?? 0) + 1;
|
|
2279
|
+
}
|
|
2280
|
+
return {
|
|
2281
|
+
issues,
|
|
2282
|
+
byCode,
|
|
2283
|
+
totalIssues: issues.length,
|
|
2284
|
+
assetsScanned: allAssetPaths.size,
|
|
2285
|
+
_source: "offline",
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
1495
2288
|
//# sourceMappingURL=offline.js.map
|