@needle-tools/engine 5.1.0-canary.db0c38f → 5.1.0-canary.e6680fa
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/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/CHANGELOG.md +133 -1
- package/SKILL.md +4 -1
- package/components.needle.json +1 -1
- package/dist/needle-engine.bundle-Bl_hyH5G.umd.cjs +1734 -0
- package/dist/needle-engine.bundle-Cduc1gj6.min.js +1734 -0
- package/dist/{needle-engine.bundle-B29kieh0.js → needle-engine.bundle-DNcqT8nJ.js} +13770 -12741
- package/dist/needle-engine.d.ts +1628 -402
- package/dist/needle-engine.js +591 -586
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/three.js +1 -0
- package/dist/three.min.js +21 -21
- package/dist/three.umd.cjs +16 -16
- package/lib/engine/api.d.ts +9 -2
- package/lib/engine/api.js +7 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +10 -18
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
- package/lib/engine/debug/debug_spatial_console.js +10 -7
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +2 -0
- package/lib/engine/engine_addressables.js +6 -3
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_audio.d.ts +68 -0
- package/lib/engine/engine_audio.js +172 -0
- package/lib/engine/engine_audio.js.map +1 -1
- package/lib/engine/engine_camera.fit.js +16 -4
- package/lib/engine/engine_camera.fit.js.map +1 -1
- package/lib/engine/engine_components.js +1 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_context.d.ts +41 -27
- package/lib/engine/engine_context.js +71 -30
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_eventbus.d.ts +47 -0
- package/lib/engine/engine_context_eventbus.js +47 -0
- package/lib/engine/engine_context_eventbus.js.map +1 -0
- package/lib/engine/engine_disposable.d.ts +172 -0
- package/lib/engine/engine_disposable.js +136 -0
- package/lib/engine/engine_disposable.js.map +1 -0
- package/lib/engine/engine_gameobject.d.ts +1 -10
- package/lib/engine/engine_gameobject.js +22 -120
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_gltf_builtin_components.js +7 -69
- package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
- package/lib/engine/engine_init.js +16 -1
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.d.ts +24 -5
- package/lib/engine/engine_input.js +3 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
- package/lib/engine/engine_instantiate_resolve.js +372 -0
- package/lib/engine/engine_instantiate_resolve.js.map +1 -0
- package/lib/engine/engine_license.d.ts +8 -6
- package/lib/engine/engine_license.js +195 -59
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_mainloop_utils.js +7 -4
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +51 -37
- package/lib/engine/engine_networking.js +132 -82
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
- package/lib/engine/engine_networking.transport.websocket.js +38 -0
- package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
- package/lib/engine/engine_networking_blob.js +4 -4
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_networking_instantiate.js +2 -2
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_networking_types.d.ts +39 -1
- package/lib/engine/engine_networking_types.js +7 -0
- package/lib/engine/engine_networking_types.js.map +1 -1
- package/lib/engine/engine_physics_rapier.d.ts +21 -3
- package/lib/engine/engine_physics_rapier.js +94 -25
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_pmrem.js +53 -5
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +13 -17
- package/lib/engine/engine_scenedata.js +58 -31
- package/lib/engine/engine_scenedata.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
- package/lib/engine/engine_serialization_builtin_serializer.js +56 -46
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_serialization_core.d.ts +1 -0
- package/lib/engine/engine_serialization_core.js +7 -0
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_ssr.d.ts +2 -0
- package/lib/engine/engine_ssr.js +20 -0
- package/lib/engine/engine_ssr.js.map +1 -1
- package/lib/engine/engine_types.d.ts +31 -11
- package/lib/engine/engine_types.js +1 -1
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_util_decorator.js +7 -2
- package/lib/engine/engine_util_decorator.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +1 -1
- package/lib/engine/engine_utils.js +19 -5
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_format.js +20 -14
- package/lib/engine/engine_utils_format.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_qrcode.js.map +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/jsx.d.ts +51 -0
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +3 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
- package/lib/engine/webcomponents/needle-engine.js +3 -3
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
- package/lib/engine/xr/NeedleXRSession.js +50 -14
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine/xr/events.d.ts +1 -1
- package/lib/engine/xr/events.js.map +1 -1
- package/lib/engine-components/Animation.js +17 -16
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimationBuilder.d.ts +158 -0
- package/lib/engine-components/AnimationBuilder.js +305 -0
- package/lib/engine-components/AnimationBuilder.js.map +1 -0
- package/lib/engine-components/Animator.d.ts +6 -0
- package/lib/engine-components/Animator.js +23 -13
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
- package/lib/engine-components/AnimatorController.builder.js +263 -0
- package/lib/engine-components/AnimatorController.builder.js.map +1 -0
- package/lib/engine-components/AnimatorController.d.ts +4 -119
- package/lib/engine-components/AnimatorController.js +37 -233
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +19 -3
- package/lib/engine-components/AudioSource.js +121 -68
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +6 -1
- package/lib/engine-components/Camera.js +16 -3
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +14 -6
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +18 -9
- package/lib/engine-components/Collider.js +61 -14
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +72 -9
- package/lib/engine-components/Component.js +114 -10
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ContactShadows.d.ts +1 -0
- package/lib/engine-components/ContactShadows.js +14 -1
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +7 -0
- package/lib/engine-components/DragControls.js +19 -7
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.js +4 -0
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/EventList.d.ts +31 -9
- package/lib/engine-components/EventList.js +37 -76
- package/lib/engine-components/EventList.js.map +1 -1
- package/lib/engine-components/Joints.d.ts +4 -2
- package/lib/engine-components/Joints.js +19 -3
- package/lib/engine-components/Joints.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +48 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +1 -1
- package/lib/engine-components/Networking.js +1 -1
- package/lib/engine-components/OrbitControls.d.ts +1 -2
- package/lib/engine-components/OrbitControls.js +37 -14
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.js +2 -0
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/RigidBody.d.ts +12 -4
- package/lib/engine-components/RigidBody.js +18 -4
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +3 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SeeThrough.js +2 -2
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/VideoPlayer.d.ts +8 -2
- package/lib/engine-components/VideoPlayer.js +42 -19
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/Voip.d.ts +16 -7
- package/lib/engine-components/Voip.js +90 -53
- package/lib/engine-components/Voip.js.map +1 -1
- package/lib/engine-components/api.d.ts +3 -1
- package/lib/engine-components/api.js +3 -1
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +7 -13
- package/lib/engine-components/codegen/components.js +7 -13
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
- package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
- package/lib/engine-components/timeline/PlayableDirector.js +75 -67
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
- package/lib/engine-components/timeline/SignalAsset.js +1 -0
- package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
- package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
- package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
- package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
- package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
- package/lib/engine-components/timeline/TimelineModels.js +3 -0
- package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
- package/lib/engine-components/timeline/TimelineTracks.js +92 -26
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/timeline/index.d.ts +2 -1
- package/lib/engine-components/timeline/index.js +2 -0
- package/lib/engine-components/timeline/index.js.map +1 -1
- package/lib/engine-components/ui/Canvas.d.ts +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -8
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +10 -7
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +0 -1
- package/lib/engine-components/web/CursorFollow.js +21 -13
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.d.ts +62 -1
- package/lib/engine-components/webxr/WebXRImageTracking.js +59 -2
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +4 -84
- package/plugins/common/cloud.js +6 -1
- package/plugins/common/license.js +55 -12
- package/plugins/common/worker.js +9 -4
- package/plugins/dts-generator/dts.codegen.js +255 -50
- package/plugins/dts-generator/dts.scan.js +37 -9
- package/plugins/dts-generator/dts.writer.js +1 -1
- package/plugins/dts-generator/glb.discovery.js +140 -23
- package/plugins/dts-generator/glb.extractor.js +48 -8
- package/plugins/dts-generator/glb.reader.js +80 -27
- package/plugins/dts-generator/index.js +1 -1
- package/plugins/types/needle-bindings.d.ts +25 -14
- package/plugins/types/userconfig.d.ts +16 -1
- package/plugins/vite/asap.js +18 -9
- package/plugins/vite/build-pipeline.js +57 -20
- package/plugins/vite/dependencies.js +29 -10
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +5 -6
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +2 -2
- package/plugins/vite/dts-generator.js +43 -9
- package/plugins/vite/editor-connection.js +3 -3
- package/plugins/vite/index.d.ts +9 -3
- package/plugins/vite/index.js +23 -10
- package/plugins/vite/license.js +42 -7
- package/plugins/vite/local-files-core.js +3 -3
- package/plugins/vite/local-files-utils.d.ts +3 -1
- package/plugins/vite/local-files-utils.js +29 -5
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +23 -23
- package/plugins/vite/server.js +2 -1
- package/src/engine/api.ts +12 -2
- package/src/engine/codegen/register_types.ts +10 -18
- package/src/engine/debug/debug_spatial_console.ts +10 -7
- package/src/engine/engine_addressables.ts +6 -3
- package/src/engine/engine_audio.ts +184 -0
- package/src/engine/engine_camera.fit.ts +15 -4
- package/src/engine/engine_components.ts +1 -1
- package/src/engine/engine_context.ts +85 -38
- package/src/engine/engine_context_eventbus.ts +73 -0
- package/src/engine/engine_disposable.ts +214 -0
- package/src/engine/engine_gameobject.ts +54 -159
- package/src/engine/engine_gltf_builtin_components.ts +7 -76
- package/src/engine/engine_init.ts +15 -1
- package/src/engine/engine_input.ts +28 -7
- package/src/engine/engine_instantiate_resolve.ts +407 -0
- package/src/engine/engine_license.ts +209 -61
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_mainloop_utils.ts +7 -4
- package/src/engine/engine_networking.transport.websocket.ts +45 -0
- package/src/engine/engine_networking.ts +161 -137
- package/src/engine/engine_networking_blob.ts +4 -4
- package/src/engine/engine_networking_instantiate.ts +2 -2
- package/src/engine/engine_networking_types.ts +41 -1
- package/src/engine/engine_physics_rapier.ts +102 -33
- package/src/engine/engine_pmrem.ts +56 -6
- package/src/engine/engine_scenedata.ts +56 -30
- package/src/engine/engine_serialization_builtin_serializer.ts +64 -52
- package/src/engine/engine_serialization_core.ts +9 -0
- package/src/engine/engine_ssr.ts +29 -3
- package/src/engine/engine_types.ts +48 -27
- package/src/engine/engine_util_decorator.ts +7 -2
- package/src/engine/engine_utils.ts +16 -5
- package/src/engine/engine_utils_format.ts +20 -14
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/src/engine/webcomponents/jsx.d.ts +51 -0
- package/src/engine/webcomponents/logo-element.ts +1 -0
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +8 -7
- package/src/engine/webcomponents/needle-button.ts +1 -0
- package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
- package/src/engine/webcomponents/needle-engine.ts +13 -6
- package/src/engine/xr/NeedleXRSession.ts +48 -13
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine/xr/events.ts +1 -1
- package/src/engine-components/Animation.ts +19 -16
- package/src/engine-components/AnimationBuilder.ts +472 -0
- package/src/engine-components/Animator.ts +24 -12
- package/src/engine-components/AnimatorController.builder.ts +387 -0
- package/src/engine-components/AnimatorController.ts +24 -292
- package/src/engine-components/AudioSource.ts +130 -79
- package/src/engine-components/Camera.ts +16 -3
- package/src/engine-components/CameraUtils.ts +12 -5
- package/src/engine-components/Collider.ts +66 -18
- package/src/engine-components/Component.ts +118 -20
- package/src/engine-components/ContactShadows.ts +15 -1
- package/src/engine-components/DragControls.ts +18 -11
- package/src/engine-components/DropListener.ts +4 -0
- package/src/engine-components/EventList.ts +45 -83
- package/src/engine-components/Joints.ts +20 -4
- package/src/engine-components/Light.ts +49 -27
- package/src/engine-components/Networking.ts +1 -1
- package/src/engine-components/OrbitControls.ts +42 -16
- package/src/engine-components/ReflectionProbe.ts +2 -0
- package/src/engine-components/RigidBody.ts +18 -4
- package/src/engine-components/SceneSwitcher.ts +3 -0
- package/src/engine-components/SeeThrough.ts +2 -2
- package/src/engine-components/VideoPlayer.ts +40 -17
- package/src/engine-components/Voip.ts +88 -53
- package/src/engine-components/api.ts +3 -1
- package/src/engine-components/codegen/components.ts +7 -13
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
- package/src/engine-components/timeline/PlayableDirector.ts +83 -81
- package/src/engine-components/timeline/SignalAsset.ts +4 -1
- package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
- package/src/engine-components/timeline/TimelineModels.ts +5 -1
- package/src/engine-components/timeline/TimelineTracks.ts +96 -27
- package/src/engine-components/timeline/index.ts +2 -1
- package/src/engine-components/ui/Canvas.ts +2 -8
- package/src/engine-components/ui/Text.ts +12 -8
- package/src/engine-components/web/CursorFollow.ts +21 -14
- package/src/engine-components/webxr/WebXRImageTracking.ts +79 -7
- package/src/needle-engine.ts +3 -0
- package/dist/needle-engine.bundle-Dq0Ly8fW.umd.cjs +0 -1732
- package/dist/needle-engine.bundle-YnpzzOPL.min.js +0 -1732
- package/lib/engine-components/AvatarLoader.d.ts +0 -80
- package/lib/engine-components/AvatarLoader.js +0 -232
- package/lib/engine-components/AvatarLoader.js.map +0 -1
- package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
- package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
- package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
- package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
- package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
- package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
- package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
- package/src/engine-components/AvatarLoader.ts +0 -264
- package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
- package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
- package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
- package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
- package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
- package/src/vite-env.d.ts +0 -16
|
@@ -8,7 +8,63 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
11
|
-
import { join,
|
|
11
|
+
import { join, basename, extname } from 'path';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic URL path segments that carry no useful identity — fall back to
|
|
15
|
+
* the previous segment when the last segment matches one of these.
|
|
16
|
+
*/
|
|
17
|
+
const GENERIC_SEGMENTS = new Set(["file", "index", "scene", "assets", "glb", "gltf", "model", "download"]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Derive a short, human-friendly identifier from a GLB URL or local path.
|
|
21
|
+
*
|
|
22
|
+
* Rules (in priority order):
|
|
23
|
+
* 1. If `contentDispositionFilename` is provided → strip extension, use it.
|
|
24
|
+
* 2. Remote URL → walk path segments right-to-left, skip generic ones,
|
|
25
|
+
* strip extension from first useful segment.
|
|
26
|
+
* 3. Local path → basename without extension.
|
|
27
|
+
*
|
|
28
|
+
* Result is identifier-safe: non-alphanumeric chars replaced with `_`,
|
|
29
|
+
* leading digits prefixed with `_`.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} pathOrUrl
|
|
32
|
+
* @param {string | null} [contentDispositionFilename]
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function glbFriendlyName(pathOrUrl, contentDispositionFilename) {
|
|
36
|
+
let raw = "";
|
|
37
|
+
|
|
38
|
+
if (contentDispositionFilename) {
|
|
39
|
+
raw = basename(contentDispositionFilename, extname(contentDispositionFilename));
|
|
40
|
+
} else if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
|
|
41
|
+
try {
|
|
42
|
+
const segments = new URL(pathOrUrl).pathname.split("/").filter(Boolean);
|
|
43
|
+
// Walk right-to-left, skip generic segments
|
|
44
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
45
|
+
const seg = segments[i];
|
|
46
|
+
const withoutExt = seg.replace(/\.(glb|gltf)$/i, "");
|
|
47
|
+
if (!GENERIC_SEGMENTS.has(withoutExt.toLowerCase())) {
|
|
48
|
+
raw = withoutExt;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!raw) raw = segments[segments.length - 1] ?? "scene";
|
|
53
|
+
} catch (_e) {
|
|
54
|
+
raw = "scene";
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
raw = basename(pathOrUrl, extname(pathOrUrl));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Make identifier-safe
|
|
61
|
+
let id = raw.replace(/[^a-zA-Z0-9]/g, "_");
|
|
62
|
+
if (/^\d/.test(id)) id = "_" + id;
|
|
63
|
+
return id || "scene";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Source file extensions that may contain `<needle-engine src="...">` markup. */
|
|
67
|
+
const SOURCE_EXTENSIONS = /\.(html|svelte|tsx|jsx|vue)$/i;
|
|
12
68
|
|
|
13
69
|
/**
|
|
14
70
|
* Parse `<needle-engine src="...">` from HTML.
|
|
@@ -17,7 +73,7 @@ import { join, resolve } from 'path';
|
|
|
17
73
|
* @returns {string[]}
|
|
18
74
|
*/
|
|
19
75
|
function parseSrcAttribute(html) {
|
|
20
|
-
const re = /<needle-engine[
|
|
76
|
+
const re = /<needle-engine[\s\S]*?\ssrc=["']([^"']+)["']/gi;
|
|
21
77
|
/** @type {string[]} */
|
|
22
78
|
const out = [];
|
|
23
79
|
let m;
|
|
@@ -54,19 +110,26 @@ function parseGenJs(src) {
|
|
|
54
110
|
/**
|
|
55
111
|
* Resolve a list of (possibly relative) GLB path strings to absolute paths.
|
|
56
112
|
* Remote URLs (http/https) are passed through as-is.
|
|
113
|
+
* The `key` field preserves the original src value (relative path or URL)
|
|
114
|
+
* for use as the SceneMap key in the generated .d.ts.
|
|
57
115
|
*
|
|
58
|
-
* @param {string
|
|
116
|
+
* @param {Array<{glbPath: string, sourceFile: string | null}>} pathEntries
|
|
59
117
|
* @param {string} projectRoot
|
|
60
118
|
* @param {string} assetsDir
|
|
61
|
-
* @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean}>}
|
|
119
|
+
* @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}>}
|
|
62
120
|
*/
|
|
63
|
-
function resolveGlbPaths(
|
|
64
|
-
/** @type {
|
|
65
|
-
const
|
|
66
|
-
for (const p of
|
|
121
|
+
function resolveGlbPaths(pathEntries, projectRoot, assetsDir) {
|
|
122
|
+
/** @type {Map<string, {path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}>} */
|
|
123
|
+
const byKey = new Map();
|
|
124
|
+
for (const { glbPath: p, sourceFile } of pathEntries) {
|
|
67
125
|
if (p.startsWith("http://") || p.startsWith("https://")) {
|
|
68
126
|
const type = p.toLowerCase().endsWith(".gltf") ? "gltf" : "glb";
|
|
69
|
-
|
|
127
|
+
const existing = byKey.get(p);
|
|
128
|
+
if (existing) {
|
|
129
|
+
if (sourceFile) existing.sourceFiles.push(sourceFile);
|
|
130
|
+
} else {
|
|
131
|
+
byKey.set(p, { path: p, type, remote: true, key: p, sourceFiles: sourceFile ? [sourceFile] : [] });
|
|
132
|
+
}
|
|
70
133
|
continue;
|
|
71
134
|
}
|
|
72
135
|
const clean = p.replace(/^\.\//, "").replace(/^\//, "");
|
|
@@ -77,52 +140,106 @@ function resolveGlbPaths(paths, projectRoot, assetsDir) {
|
|
|
77
140
|
for (const candidate of candidates) {
|
|
78
141
|
if (existsSync(candidate)) {
|
|
79
142
|
const type = candidate.toLowerCase().endsWith(".gltf") ? "gltf" : "glb";
|
|
80
|
-
|
|
143
|
+
const existing = byKey.get(clean);
|
|
144
|
+
if (existing) {
|
|
145
|
+
if (sourceFile) existing.sourceFiles.push(sourceFile);
|
|
146
|
+
} else {
|
|
147
|
+
byKey.set(clean, { path: candidate, type, key: clean, sourceFiles: sourceFile ? [sourceFile] : [] });
|
|
148
|
+
}
|
|
81
149
|
break;
|
|
82
150
|
}
|
|
83
151
|
}
|
|
84
152
|
}
|
|
153
|
+
return Array.from(byKey.values());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Walk `src/` (or the project root) for source files that may contain
|
|
158
|
+
* `<needle-engine src="...">` and return GLB path + source file pairs.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} projectRoot
|
|
161
|
+
* @returns {Array<{glbPath: string, sourceFile: string}>}
|
|
162
|
+
*/
|
|
163
|
+
function scanSourceFilesForGlbs(projectRoot) {
|
|
164
|
+
/** @type {Array<{glbPath: string, sourceFile: string}>} */
|
|
165
|
+
const out = [];
|
|
166
|
+
const srcDir = join(projectRoot, "src");
|
|
167
|
+
const searchRoot = existsSync(srcDir) ? srcDir : projectRoot;
|
|
168
|
+
|
|
169
|
+
/** @param {string} dir */
|
|
170
|
+
function walk(dir) {
|
|
171
|
+
let entries;
|
|
172
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch (_e) { return; }
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
const fullPath = join(dir, entry.name);
|
|
175
|
+
if (entry.isDirectory()) {
|
|
176
|
+
// Skip node_modules and hidden dirs
|
|
177
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
178
|
+
walk(fullPath);
|
|
179
|
+
} else if (SOURCE_EXTENSIONS.test(entry.name)) {
|
|
180
|
+
try {
|
|
181
|
+
const content = readFileSync(fullPath, "utf8");
|
|
182
|
+
const relPath = fullPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
|
|
183
|
+
for (const glbPath of parseSrcAttribute(content)) {
|
|
184
|
+
out.push({ glbPath, sourceFile: relPath });
|
|
185
|
+
}
|
|
186
|
+
} catch (_e) { /* ignore unreadable files */ }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
walk(searchRoot);
|
|
85
192
|
return out;
|
|
86
193
|
}
|
|
87
194
|
|
|
88
195
|
/**
|
|
89
196
|
* Resolve the entrypoint GLB file paths for a project.
|
|
90
197
|
*
|
|
91
|
-
*
|
|
92
|
-
* 1. `<needle-engine src="...">`
|
|
198
|
+
* Sources (all merged, deduplicated by key):
|
|
199
|
+
* 1. `<needle-engine src="...">` in `index.html`
|
|
93
200
|
* 2. `needle_exported_files.push("...")` lines in `{codegenDir}/gen.js`
|
|
94
|
-
* 3.
|
|
201
|
+
* 3. `<needle-engine src="...">` in any `.svelte`, `.tsx`, `.jsx`, `.vue`, `.html` under `src/`
|
|
202
|
+
*
|
|
203
|
+
* Returns `null` if nothing found — caller should fall back to `collectSceneFiles`.
|
|
95
204
|
*
|
|
96
205
|
* @param {string} projectRoot Absolute path to the project root
|
|
97
206
|
* @param {string} assetsDir Absolute path to the assets directory
|
|
98
207
|
* @param {string} [codegenDir] Absolute path to the codegen directory
|
|
99
|
-
* @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean}> | null}
|
|
208
|
+
* @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}> | null}
|
|
100
209
|
*/
|
|
101
210
|
export function resolveEntrypointGlbs(projectRoot, assetsDir, codegenDir) {
|
|
211
|
+
/** @type {Array<{glbPath: string, sourceFile: string | null}>} */
|
|
212
|
+
const allEntries = [];
|
|
213
|
+
|
|
214
|
+
// 1. index.html
|
|
102
215
|
const htmlPath = join(projectRoot, "index.html");
|
|
103
216
|
if (existsSync(htmlPath)) {
|
|
104
217
|
try {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const resolved = resolveGlbPaths(srcPaths, projectRoot, assetsDir);
|
|
108
|
-
if (resolved.length > 0) return resolved;
|
|
218
|
+
for (const glbPath of parseSrcAttribute(readFileSync(htmlPath, "utf8"))) {
|
|
219
|
+
allEntries.push({ glbPath, sourceFile: "index.html" });
|
|
109
220
|
}
|
|
110
221
|
} catch (_e) { /* ignore */ }
|
|
111
222
|
}
|
|
112
223
|
|
|
224
|
+
// 2. gen.js
|
|
113
225
|
const genDir = codegenDir ?? join(projectRoot, "src", "generated");
|
|
114
226
|
const genPath = join(genDir, "gen.js");
|
|
115
227
|
if (existsSync(genPath)) {
|
|
116
228
|
try {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (resolved.length > 0) return resolved;
|
|
229
|
+
const relGenPath = genPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
|
|
230
|
+
for (const glbPath of parseGenJs(readFileSync(genPath, "utf8"))) {
|
|
231
|
+
allEntries.push({ glbPath, sourceFile: relGenPath });
|
|
121
232
|
}
|
|
122
233
|
} catch (_e) { /* ignore */ }
|
|
123
234
|
}
|
|
124
235
|
|
|
125
|
-
|
|
236
|
+
// 3. Source files (SvelteKit, React, Vue, etc.)
|
|
237
|
+
allEntries.push(...scanSourceFilesForGlbs(projectRoot));
|
|
238
|
+
|
|
239
|
+
if (allEntries.length === 0) return null;
|
|
240
|
+
|
|
241
|
+
const resolved = resolveGlbPaths(allEntries, projectRoot, assetsDir);
|
|
242
|
+
return resolved.length > 0 ? resolved : null;
|
|
126
243
|
}
|
|
127
244
|
|
|
128
245
|
/**
|
|
@@ -29,17 +29,44 @@ export function sanitizeNodeName(rawName, namesUsed) {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Infer the Three.js runtime type of a glTF node from its JSON properties.
|
|
32
|
-
* - `mesh` present
|
|
33
|
-
* - `
|
|
34
|
-
* -
|
|
35
|
-
* -
|
|
32
|
+
* - `mesh` present with skinning → `import("three").SkinnedMesh`
|
|
33
|
+
* - `mesh` present → `import("three").Mesh`
|
|
34
|
+
* - `camera` present, perspective → `import("three").PerspectiveCamera`
|
|
35
|
+
* - `camera` present, orthographic→ `import("three").OrthographicCamera`
|
|
36
|
+
* - KHR_lights_punctual → `import("three").Light`
|
|
37
|
+
* - otherwise → `import("three").Object3D`
|
|
36
38
|
*
|
|
37
39
|
* @param {Record<string, unknown>} node
|
|
40
|
+
* @param {Array<Record<string, unknown>>} cameras Raw glTF cameras array
|
|
41
|
+
* @param {Array<Record<string, unknown>>} meshes Raw glTF meshes array
|
|
38
42
|
* @returns {string}
|
|
39
43
|
*/
|
|
40
|
-
export function inferNodeThreeType(node) {
|
|
41
|
-
if ("mesh" in node)
|
|
42
|
-
|
|
44
|
+
export function inferNodeThreeType(node, cameras, meshes) {
|
|
45
|
+
if ("mesh" in node) {
|
|
46
|
+
const hasSkin = "skin" in node;
|
|
47
|
+
if (!hasSkin) {
|
|
48
|
+
const meshIdx = /** @type {number} */ (node.mesh);
|
|
49
|
+
const mesh = meshes[meshIdx];
|
|
50
|
+
const primitives = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(mesh?.primitives) ? mesh.primitives : []);
|
|
51
|
+
const isSkinned = primitives.some(p => {
|
|
52
|
+
const attrs = /** @type {Record<string, unknown>} */ (p?.attributes ?? {});
|
|
53
|
+
return "WEIGHTS_0" in attrs;
|
|
54
|
+
});
|
|
55
|
+
if (isSkinned) return `import("three").SkinnedMesh`;
|
|
56
|
+
} else {
|
|
57
|
+
return `import("three").SkinnedMesh`;
|
|
58
|
+
}
|
|
59
|
+
return `import("three").Mesh`;
|
|
60
|
+
}
|
|
61
|
+
if ("camera" in node) {
|
|
62
|
+
const camIdx = /** @type {number} */ (node.camera);
|
|
63
|
+
const cam = cameras[camIdx];
|
|
64
|
+
if (cam && typeof cam === "object") {
|
|
65
|
+
if (cam.type === "perspective") return `import("three").PerspectiveCamera`;
|
|
66
|
+
if (cam.type === "orthographic") return `import("three").OrthographicCamera`;
|
|
67
|
+
}
|
|
68
|
+
return `import("three").Camera`;
|
|
69
|
+
}
|
|
43
70
|
if (node.extensions && /** @type {any} */ (node.extensions)["KHR_lights_punctual"] != null) return `import("three").Light`;
|
|
44
71
|
return `import("three").Object3D`;
|
|
45
72
|
}
|
|
@@ -123,6 +150,17 @@ function buildNodePaths(nodes, namesUsed) {
|
|
|
123
150
|
*/
|
|
124
151
|
export function extractComponentBindings(json) {
|
|
125
152
|
const nodes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.nodes) ? json.nodes : []);
|
|
153
|
+
const cameras = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.cameras) ? json.cameras : []);
|
|
154
|
+
const meshes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.meshes) ? json.meshes : []);
|
|
155
|
+
|
|
156
|
+
// Collect scene root node indices — these map to THREE.Scene at runtime
|
|
157
|
+
const scenes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.scenes) ? json.scenes : []);
|
|
158
|
+
/** @type {Set<number>} */
|
|
159
|
+
const sceneRootNodeIndices = new Set();
|
|
160
|
+
for (const scene of scenes) {
|
|
161
|
+
const sceneNodes = Array.isArray(scene.nodes) ? /** @type {number[]} */ (scene.nodes) : [];
|
|
162
|
+
for (const idx of sceneNodes) sceneRootNodeIndices.add(idx);
|
|
163
|
+
}
|
|
126
164
|
/** @type {Array<{nodeName: string, nodePath: string, componentName: string, fields: Record<string, unknown>, nodeThreeType: string}>} */
|
|
127
165
|
const results = [];
|
|
128
166
|
/** @type {Record<string, number>} */
|
|
@@ -138,7 +176,9 @@ export function extractComponentBindings(json) {
|
|
|
138
176
|
if (!nodeName) continue;
|
|
139
177
|
|
|
140
178
|
const nodePath = pathMap.get(i) ?? nodeName;
|
|
141
|
-
const nodeThreeType =
|
|
179
|
+
const nodeThreeType = sceneRootNodeIndices.has(i)
|
|
180
|
+
? `import("three").Scene`
|
|
181
|
+
: inferNodeThreeType(/** @type {Record<string, unknown>} */ (node), cameras, meshes);
|
|
142
182
|
const ext = node.extensions?.[NEEDLE_COMPONENTS_EXTENSION];
|
|
143
183
|
|
|
144
184
|
if (!ext || typeof ext !== "object") {
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { openSync, readSync, closeSync, readFileSync } from 'fs';
|
|
10
|
+
import { needleLog } from '../vite/logging.js';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
const _remoteGlbCache = new Map();
|
|
12
|
+
const PLUGIN = "needle:dts-generator";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Read the JSON chunk from a binary GLB file without loading the binary blob.
|
|
@@ -56,12 +56,39 @@ export function readGltfJsonFile(filePath) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* Parse the filename from a `Content-Disposition` header value.
|
|
60
|
+
* Handles `filename="foo.glb"` and `filename*=UTF-8''foo.glb` forms.
|
|
61
|
+
* Returns null if not present or not parseable.
|
|
62
|
+
* @param {string | null} header
|
|
63
|
+
* @returns {string | null}
|
|
64
|
+
*/
|
|
65
|
+
export function parseContentDispositionFilename(header) {
|
|
66
|
+
if (!header) return null;
|
|
67
|
+
// RFC 5987 extended form: filename*=UTF-8''foo.glb
|
|
68
|
+
const extMatch = header.match(/filename\*\s*=\s*(?:[^']*'')?([^;]+)/i);
|
|
69
|
+
if (extMatch) {
|
|
70
|
+
try { return decodeURIComponent(extMatch[1].trim()); } catch (_e) { /* fall through */ }
|
|
71
|
+
}
|
|
72
|
+
// Plain form: filename="foo.glb" or filename=foo.glb
|
|
73
|
+
const plainMatch = header.match(/filename\s*=\s*"?([^";]+)"?/i);
|
|
74
|
+
if (plainMatch) return plainMatch[1].trim();
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @type {Map<string, { lastModified: string, json: Record<string, unknown>, filename: string | null }>} */
|
|
79
|
+
const _remoteGlbCache = new Map();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fetch the JSON chunk of a remote GLB.
|
|
83
|
+
* Tries an initial Range request for the header bytes; if the server already returned
|
|
84
|
+
* enough data (200 + full body, or 206 with sufficient bytes) the JSON chunk is
|
|
85
|
+
* extracted directly. Otherwise a second Range request fetches the JSON chunk.
|
|
60
86
|
* Uses `Last-Modified` header for caching — avoids re-parsing if unchanged.
|
|
61
|
-
*
|
|
87
|
+
* Also captures `Content-Disposition` filename for friendly key generation.
|
|
88
|
+
* Returns `null` on any network or parse error.
|
|
62
89
|
*
|
|
63
90
|
* @param {string} url
|
|
64
|
-
* @returns {Promise<Record<string, unknown
|
|
91
|
+
* @returns {Promise<{ json: Record<string, unknown>, filename: string | null } | null>}
|
|
65
92
|
*/
|
|
66
93
|
export async function readRemoteGlbJsonChunk(url) {
|
|
67
94
|
try {
|
|
@@ -69,46 +96,72 @@ export async function readRemoteGlbJsonChunk(url) {
|
|
|
69
96
|
|
|
70
97
|
const headerRes = await fetch(url, { headers: { Range: "bytes=0-19" } });
|
|
71
98
|
if (!headerRes.ok) {
|
|
72
|
-
|
|
99
|
+
needleLog(PLUGIN, `Remote GLB fetch failed (${headerRes.status}): ${url}`, "warn");
|
|
73
100
|
return null;
|
|
74
101
|
}
|
|
75
102
|
|
|
76
103
|
const lastModified = headerRes.headers.get("Last-Modified") ?? "";
|
|
104
|
+
const filename = parseContentDispositionFilename(headerRes.headers.get("Content-Disposition"));
|
|
105
|
+
|
|
77
106
|
if (cached && lastModified && cached.lastModified === lastModified) {
|
|
78
|
-
|
|
107
|
+
// Prefer the filename we resolved during the full fetch (may have come from the JSON chunk
|
|
108
|
+
// response). Only fall back to the fresh header value if the cache has nothing.
|
|
109
|
+
return { json: cached.json, filename: cached.filename ?? filename };
|
|
79
110
|
}
|
|
80
111
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
if (headerBytes.length < 20) return null;
|
|
112
|
+
const firstBytes = Buffer.from(await headerRes.arrayBuffer());
|
|
113
|
+
if (firstBytes.length < 20) return null;
|
|
84
114
|
|
|
85
|
-
const magic =
|
|
86
|
-
const version =
|
|
87
|
-
const chunkLength =
|
|
88
|
-
const chunkType =
|
|
115
|
+
const magic = firstBytes.readUInt32LE(0);
|
|
116
|
+
const version = firstBytes.readUInt32LE(4);
|
|
117
|
+
const chunkLength = firstBytes.readUInt32LE(12);
|
|
118
|
+
const chunkType = firstBytes.readUInt32LE(16);
|
|
89
119
|
|
|
90
120
|
if (magic !== 0x46546c67 || version !== 2) return null; // not a GLB
|
|
91
121
|
if (chunkType !== 0x4E4F534A) return null; // chunk0 not JSON
|
|
92
122
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
let jsonBytes;
|
|
124
|
+
let resolvedFilename = filename;
|
|
125
|
+
|
|
126
|
+
if (firstBytes.length >= 20 + chunkLength) {
|
|
127
|
+
// The first response already contained the full JSON chunk (server ignored Range or returned 200).
|
|
128
|
+
jsonBytes = firstBytes.slice(20, 20 + chunkLength);
|
|
129
|
+
} else {
|
|
130
|
+
// Try a second Range request for just the JSON chunk bytes.
|
|
131
|
+
const jsonEnd = 20 + chunkLength - 1;
|
|
132
|
+
const jsonRes = await fetch(url, { headers: { Range: `bytes=20-${jsonEnd}` } });
|
|
133
|
+
if (!jsonRes.ok) {
|
|
134
|
+
needleLog(PLUGIN, `Remote GLB JSON chunk fetch failed (${jsonRes.status}): ${url}`, "warn");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const jsonResBytes = Buffer.from(await jsonRes.arrayBuffer());
|
|
138
|
+
// Server may return 200 + full body even for a Range request — slice out our window.
|
|
139
|
+
if (jsonResBytes.length >= 20 + chunkLength) {
|
|
140
|
+
jsonBytes = jsonResBytes.slice(20, 20 + chunkLength);
|
|
141
|
+
} else if (jsonResBytes.length >= chunkLength) {
|
|
142
|
+
jsonBytes = jsonResBytes.slice(0, chunkLength);
|
|
143
|
+
} else {
|
|
144
|
+
needleLog(PLUGIN, `Remote GLB unexpected response (${jsonRes.status}, ${jsonResBytes.length} bytes, expected ${chunkLength}): ${url}`, "warn");
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const filenameFromJsonRes = parseContentDispositionFilename(jsonRes.headers.get("Content-Disposition"));
|
|
149
|
+
resolvedFilename = filename ?? filenameFromJsonRes;
|
|
96
150
|
}
|
|
97
151
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
152
|
+
if (resolvedFilename === null) {
|
|
153
|
+
try {
|
|
154
|
+
const headRes = await fetch(url, { method: "HEAD" });
|
|
155
|
+
resolvedFilename = parseContentDispositionFilename(headRes.headers.get("Content-Disposition"));
|
|
156
|
+
} catch (_e) { /* ignore — fall back to URL-based name */ }
|
|
103
157
|
}
|
|
104
|
-
|
|
105
158
|
const json = /** @type {Record<string, unknown>} */ (
|
|
106
|
-
JSON.parse(
|
|
159
|
+
JSON.parse(jsonBytes.toString("utf8").replace(/\u0000+$/g, ""))
|
|
107
160
|
);
|
|
108
|
-
_remoteGlbCache.set(url, { lastModified, json });
|
|
109
|
-
return json;
|
|
161
|
+
_remoteGlbCache.set(url, { lastModified, json, filename: resolvedFilename });
|
|
162
|
+
return { json, filename: resolvedFilename };
|
|
110
163
|
} catch (e) {
|
|
111
|
-
|
|
164
|
+
needleLog(PLUGIN, `Failed to fetch remote GLB: ${url} — ${/** @type {any} */ (e)?.message ?? e}`, "warn");
|
|
112
165
|
return null;
|
|
113
166
|
}
|
|
114
167
|
}
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ambient module declaration for `needle
|
|
2
|
+
* Ambient module declaration for `needle-bindings`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* `SceneData` is keyed by GLB friendly name, then by the node hierarchy:
|
|
5
|
+
* ctx.sceneData.Minimal.Camera.$object // → THREE.Camera
|
|
6
|
+
* ctx.sceneData.Minimal.Camera.$components.OrbitControls.autoRotate = true;
|
|
7
|
+
* ctx.sceneData.Minimal.UI.Button.$components.Button
|
|
8
|
+
*
|
|
9
|
+
* Each node entry has:
|
|
10
|
+
* $object — the Three.js Object3D (typed precisely, e.g. Mesh, Camera, Light)
|
|
11
|
+
* $components — Needle components attached to this node
|
|
12
|
+
* [childName] — child nodes, recursively typed
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* When the `needle:dts-generator` Vite plugin is active it writes
|
|
15
|
+
* `.needle/generated/needle-bindings.gen.d.ts` next to the installed package,
|
|
16
|
+
* which augments this interface with the actual bindings extracted from
|
|
17
|
+
* the project's GLB files at build time.
|
|
13
18
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
// Pull in the project-local generated augmentation (written by needle:dts-generator).
|
|
21
|
+
/// <reference path="../../.needle/generated/needle-bindings.gen.d.ts" />
|
|
22
|
+
|
|
23
|
+
declare module "needle-bindings" {
|
|
24
|
+
/**
|
|
25
|
+
* Scene data keyed by GLB friendly name, then by node hierarchy.
|
|
26
|
+
* Fallback index signature allows unknown names without crashing.
|
|
27
|
+
*/
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
29
|
+
interface SceneData {}
|
|
19
30
|
}
|
|
@@ -82,7 +82,10 @@ export type userSettings = {
|
|
|
82
82
|
*/
|
|
83
83
|
version?: string;
|
|
84
84
|
|
|
85
|
-
/** If defined the access token will be used to run compression on Needle Cloud
|
|
85
|
+
/** If defined the access token will be used to run compression on Needle Cloud.
|
|
86
|
+
*
|
|
87
|
+
* Expected to be a Needle Cloud access token (created in the Needle Cloud UI),
|
|
88
|
+
* NOT a JWT. Do not pass a licensing JWT here. */
|
|
86
89
|
accessToken?: string | undefined;
|
|
87
90
|
|
|
88
91
|
/**
|
|
@@ -176,4 +179,16 @@ export type userSettings = {
|
|
|
176
179
|
|
|
177
180
|
/** Set to true to disable the plugin that ensures VSCode workspace settings for custom-elements.json data */
|
|
178
181
|
noCustomElementData?: boolean;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate Typescript declaration files for references 3D assets in your project.
|
|
185
|
+
* These will be available via `context.sceneData` in your code.
|
|
186
|
+
* @default enabled
|
|
187
|
+
*/
|
|
188
|
+
dts?: {
|
|
189
|
+
/** When set to false, disables the generation of TypeScript declaration files.
|
|
190
|
+
* @default true
|
|
191
|
+
*/
|
|
192
|
+
enabled?: boolean;
|
|
193
|
+
}
|
|
179
194
|
}
|
package/plugins/vite/asap.js
CHANGED
|
@@ -22,7 +22,7 @@ export async function needleAsap(command, config, userSettings) {
|
|
|
22
22
|
|
|
23
23
|
fixMainTs();
|
|
24
24
|
|
|
25
|
-
if (command
|
|
25
|
+
if (command === "serve") {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -50,7 +50,7 @@ export async function needleAsap(command, config, userSettings) {
|
|
|
50
50
|
const tags = [];
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
generateGltfPreloadLinks(config, html, tags);
|
|
53
|
+
generateGltfPreloadLinks(config, html, tags, viteConfig?.base);
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
56
|
console.error("Error generating gltf preload links", err);
|
|
@@ -173,18 +173,21 @@ function fixMainTs() {
|
|
|
173
173
|
* @param {import('vite').ResolvedConfig} _config
|
|
174
174
|
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
175
175
|
*/
|
|
176
|
-
function generateScriptPreloadLinks(
|
|
176
|
+
function generateScriptPreloadLinks(config, tags) {
|
|
177
177
|
try {
|
|
178
|
+
const base = config.base || '/';
|
|
178
179
|
const chunks = preloadScriptPaths;
|
|
179
180
|
// console.log("ASAP", chunks)
|
|
180
181
|
if (chunks.length > 0) {
|
|
181
182
|
for (const chunk of chunks) {
|
|
183
|
+
// Apply base path so preload hrefs resolve correctly under SPA routing
|
|
184
|
+
const href = chunk.startsWith('./') ? base + chunk.slice(2) : chunk;
|
|
182
185
|
tags.push({
|
|
183
186
|
tag: 'link',
|
|
184
187
|
attrs: {
|
|
185
188
|
rel: "modulepreload",
|
|
186
189
|
as: "script",
|
|
187
|
-
href:
|
|
190
|
+
href: href,
|
|
188
191
|
}
|
|
189
192
|
});
|
|
190
193
|
}
|
|
@@ -203,15 +206,16 @@ const codegenRegex = /\"(?<gltf>.+(.glb|.gltf)(\?.*)?)\"/gm;
|
|
|
203
206
|
/**
|
|
204
207
|
* @param {import('../types').needleConfig} config
|
|
205
208
|
* @param {string} html
|
|
206
|
-
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
209
|
+
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
210
|
+
* @param {string} [base]
|
|
207
211
|
**/
|
|
208
|
-
function generateGltfPreloadLinks(config, html, tags) {
|
|
212
|
+
function generateGltfPreloadLinks(config, html, tags, base) {
|
|
209
213
|
|
|
210
214
|
// TODO: try to get the <needle-engine src> element src attribute and preload that
|
|
211
215
|
const needleEngineMatches = tryParseNeedleEngineSrcAttributeFromHtml(html);
|
|
212
216
|
if (needleEngineMatches?.length) {
|
|
213
217
|
for (const item of needleEngineMatches) {
|
|
214
|
-
insertPreloadLink(tags, item, "model/gltf+json");
|
|
218
|
+
insertPreloadLink(tags, item, "model/gltf+json", base);
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -246,7 +250,7 @@ function generateGltfPreloadLinks(config, html, tags) {
|
|
|
246
250
|
}
|
|
247
251
|
}
|
|
248
252
|
needleLog("needle:asap", `Insert glTF preload link: ${value}`);
|
|
249
|
-
insertPreloadLink(tags, value, "model/gltf+json");
|
|
253
|
+
insertPreloadLink(tags, value, "model/gltf+json", base);
|
|
250
254
|
}
|
|
251
255
|
}
|
|
252
256
|
}
|
|
@@ -258,9 +262,14 @@ function generateGltfPreloadLinks(config, html, tags) {
|
|
|
258
262
|
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
259
263
|
* @param {string} href
|
|
260
264
|
* @param {string} type
|
|
265
|
+
* @param {string} [base]
|
|
261
266
|
*/
|
|
262
|
-
function insertPreloadLink(tags, href, type) {
|
|
267
|
+
function insertPreloadLink(tags, href, type, base) {
|
|
263
268
|
if (!href) return;
|
|
269
|
+
// Apply base path so preload hrefs resolve correctly under SPA routing
|
|
270
|
+
if (base && !href.startsWith('http') && !href.startsWith('/')) {
|
|
271
|
+
href = base + (href.startsWith('./') ? href.slice(2) : href);
|
|
272
|
+
}
|
|
264
273
|
tags.push({
|
|
265
274
|
tag: 'link',
|
|
266
275
|
attrs: {
|