@needle-tools/engine 5.1.0-canary.deec6e4 → 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.
Files changed (326) hide show
  1. package/CHANGELOG.md +99 -1
  2. package/SKILL.md +4 -1
  3. package/components.needle.json +1 -1
  4. package/dist/needle-engine.bundle-Bl_hyH5G.umd.cjs +1734 -0
  5. package/dist/needle-engine.bundle-Cduc1gj6.min.js +1734 -0
  6. package/dist/{needle-engine.bundle-CvtELXh0.js → needle-engine.bundle-DNcqT8nJ.js} +19415 -18452
  7. package/dist/needle-engine.d.ts +1588 -374
  8. package/dist/needle-engine.js +572 -569
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/dist/three.js +1 -0
  12. package/dist/three.min.js +21 -21
  13. package/dist/three.umd.cjs +16 -16
  14. package/lib/engine/api.d.ts +8 -1
  15. package/lib/engine/api.js +7 -1
  16. package/lib/engine/api.js.map +1 -1
  17. package/lib/engine/codegen/register_types.js +10 -18
  18. package/lib/engine/codegen/register_types.js.map +1 -1
  19. package/lib/engine/engine_audio.d.ts +68 -0
  20. package/lib/engine/engine_audio.js +172 -0
  21. package/lib/engine/engine_audio.js.map +1 -1
  22. package/lib/engine/engine_camera.fit.js +16 -4
  23. package/lib/engine/engine_camera.fit.js.map +1 -1
  24. package/lib/engine/engine_components.js +1 -1
  25. package/lib/engine/engine_components.js.map +1 -1
  26. package/lib/engine/engine_context.d.ts +21 -8
  27. package/lib/engine/engine_context.js +46 -16
  28. package/lib/engine/engine_context.js.map +1 -1
  29. package/lib/engine/engine_context_eventbus.d.ts +47 -0
  30. package/lib/engine/engine_context_eventbus.js +47 -0
  31. package/lib/engine/engine_context_eventbus.js.map +1 -0
  32. package/lib/engine/engine_disposable.d.ts +172 -0
  33. package/lib/engine/engine_disposable.js +136 -0
  34. package/lib/engine/engine_disposable.js.map +1 -0
  35. package/lib/engine/engine_gameobject.d.ts +1 -10
  36. package/lib/engine/engine_gameobject.js +22 -120
  37. package/lib/engine/engine_gameobject.js.map +1 -1
  38. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  39. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  40. package/lib/engine/engine_init.js +7 -7
  41. package/lib/engine/engine_init.js.map +1 -1
  42. package/lib/engine/engine_input.d.ts +24 -5
  43. package/lib/engine/engine_input.js +3 -2
  44. package/lib/engine/engine_input.js.map +1 -1
  45. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  46. package/lib/engine/engine_instantiate_resolve.js +372 -0
  47. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  48. package/lib/engine/engine_license.d.ts +7 -7
  49. package/lib/engine/engine_license.js +186 -58
  50. package/lib/engine/engine_license.js.map +1 -1
  51. package/lib/engine/engine_mainloop_utils.js +7 -4
  52. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  53. package/lib/engine/engine_networking.d.ts +51 -37
  54. package/lib/engine/engine_networking.js +132 -82
  55. package/lib/engine/engine_networking.js.map +1 -1
  56. package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
  57. package/lib/engine/engine_networking.transport.websocket.js +38 -0
  58. package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
  59. package/lib/engine/engine_networking_blob.js +4 -4
  60. package/lib/engine/engine_networking_blob.js.map +1 -1
  61. package/lib/engine/engine_networking_instantiate.js +2 -2
  62. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  63. package/lib/engine/engine_networking_types.d.ts +39 -1
  64. package/lib/engine/engine_networking_types.js +7 -0
  65. package/lib/engine/engine_networking_types.js.map +1 -1
  66. package/lib/engine/engine_physics_rapier.d.ts +21 -3
  67. package/lib/engine/engine_physics_rapier.js +94 -25
  68. package/lib/engine/engine_physics_rapier.js.map +1 -1
  69. package/lib/engine/engine_pmrem.js +51 -3
  70. package/lib/engine/engine_pmrem.js.map +1 -1
  71. package/lib/engine/engine_scenedata.js +2 -2
  72. package/lib/engine/engine_scenedata.js.map +1 -1
  73. package/lib/engine/engine_serialization_builtin_serializer.js +28 -5
  74. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  75. package/lib/engine/engine_serialization_core.d.ts +1 -0
  76. package/lib/engine/engine_serialization_core.js +7 -0
  77. package/lib/engine/engine_serialization_core.js.map +1 -1
  78. package/lib/engine/engine_types.d.ts +29 -11
  79. package/lib/engine/engine_types.js +1 -1
  80. package/lib/engine/engine_types.js.map +1 -1
  81. package/lib/engine/engine_util_decorator.js +7 -2
  82. package/lib/engine/engine_util_decorator.js.map +1 -1
  83. package/lib/engine/engine_utils.d.ts +1 -1
  84. package/lib/engine/engine_utils.js +19 -5
  85. package/lib/engine/engine_utils.js.map +1 -1
  86. package/lib/engine/engine_utils_format.js +20 -14
  87. package/lib/engine/engine_utils_format.js.map +1 -1
  88. package/lib/engine/engine_utils_qrcode.js +2 -2
  89. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  90. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  91. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  92. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  93. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  94. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -1
  95. package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
  96. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  97. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  98. package/lib/engine/webcomponents/needle-engine.js +3 -3
  99. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  100. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  101. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  102. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  103. package/lib/engine/xr/NeedleXRSession.js +50 -14
  104. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  105. package/lib/engine/xr/TempXRContext.js +2 -2
  106. package/lib/engine/xr/TempXRContext.js.map +1 -1
  107. package/lib/engine/xr/events.d.ts +1 -1
  108. package/lib/engine/xr/events.js.map +1 -1
  109. package/lib/engine-components/Animation.js +17 -16
  110. package/lib/engine-components/Animation.js.map +1 -1
  111. package/lib/engine-components/AnimationBuilder.d.ts +158 -0
  112. package/lib/engine-components/AnimationBuilder.js +305 -0
  113. package/lib/engine-components/AnimationBuilder.js.map +1 -0
  114. package/lib/engine-components/Animator.d.ts +6 -0
  115. package/lib/engine-components/Animator.js +23 -13
  116. package/lib/engine-components/Animator.js.map +1 -1
  117. package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
  118. package/lib/engine-components/AnimatorController.builder.js +263 -0
  119. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  120. package/lib/engine-components/AnimatorController.d.ts +2 -119
  121. package/lib/engine-components/AnimatorController.js +33 -232
  122. package/lib/engine-components/AnimatorController.js.map +1 -1
  123. package/lib/engine-components/AudioSource.d.ts +19 -3
  124. package/lib/engine-components/AudioSource.js +121 -68
  125. package/lib/engine-components/AudioSource.js.map +1 -1
  126. package/lib/engine-components/Camera.d.ts +6 -1
  127. package/lib/engine-components/Camera.js +16 -3
  128. package/lib/engine-components/Camera.js.map +1 -1
  129. package/lib/engine-components/CameraUtils.js +14 -6
  130. package/lib/engine-components/CameraUtils.js.map +1 -1
  131. package/lib/engine-components/Collider.d.ts +18 -9
  132. package/lib/engine-components/Collider.js +61 -14
  133. package/lib/engine-components/Collider.js.map +1 -1
  134. package/lib/engine-components/Component.d.ts +72 -9
  135. package/lib/engine-components/Component.js +114 -10
  136. package/lib/engine-components/Component.js.map +1 -1
  137. package/lib/engine-components/ContactShadows.d.ts +1 -0
  138. package/lib/engine-components/ContactShadows.js +14 -1
  139. package/lib/engine-components/ContactShadows.js.map +1 -1
  140. package/lib/engine-components/DragControls.d.ts +7 -0
  141. package/lib/engine-components/DragControls.js +19 -7
  142. package/lib/engine-components/DragControls.js.map +1 -1
  143. package/lib/engine-components/DropListener.js +4 -0
  144. package/lib/engine-components/DropListener.js.map +1 -1
  145. package/lib/engine-components/EventList.d.ts +31 -9
  146. package/lib/engine-components/EventList.js +37 -76
  147. package/lib/engine-components/EventList.js.map +1 -1
  148. package/lib/engine-components/Joints.d.ts +4 -2
  149. package/lib/engine-components/Joints.js +19 -3
  150. package/lib/engine-components/Joints.js.map +1 -1
  151. package/lib/engine-components/Light.js +9 -1
  152. package/lib/engine-components/Light.js.map +1 -1
  153. package/lib/engine-components/Networking.d.ts +1 -1
  154. package/lib/engine-components/Networking.js +1 -1
  155. package/lib/engine-components/OrbitControls.d.ts +1 -2
  156. package/lib/engine-components/OrbitControls.js +37 -14
  157. package/lib/engine-components/OrbitControls.js.map +1 -1
  158. package/lib/engine-components/RigidBody.d.ts +12 -4
  159. package/lib/engine-components/RigidBody.js +18 -4
  160. package/lib/engine-components/RigidBody.js.map +1 -1
  161. package/lib/engine-components/SceneSwitcher.js +3 -0
  162. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  163. package/lib/engine-components/SeeThrough.js +2 -2
  164. package/lib/engine-components/SeeThrough.js.map +1 -1
  165. package/lib/engine-components/VideoPlayer.d.ts +8 -2
  166. package/lib/engine-components/VideoPlayer.js +42 -19
  167. package/lib/engine-components/VideoPlayer.js.map +1 -1
  168. package/lib/engine-components/Voip.d.ts +16 -7
  169. package/lib/engine-components/Voip.js +90 -53
  170. package/lib/engine-components/Voip.js.map +1 -1
  171. package/lib/engine-components/api.d.ts +3 -1
  172. package/lib/engine-components/api.js +3 -1
  173. package/lib/engine-components/api.js.map +1 -1
  174. package/lib/engine-components/codegen/components.d.ts +7 -13
  175. package/lib/engine-components/codegen/components.js +7 -13
  176. package/lib/engine-components/codegen/components.js.map +1 -1
  177. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  178. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  179. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  180. package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
  181. package/lib/engine-components/timeline/PlayableDirector.js +75 -67
  182. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  183. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  184. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  185. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  186. package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
  187. package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
  188. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  189. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  190. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  191. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  192. package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
  193. package/lib/engine-components/timeline/TimelineTracks.js +92 -26
  194. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  195. package/lib/engine-components/timeline/index.d.ts +2 -1
  196. package/lib/engine-components/timeline/index.js +2 -0
  197. package/lib/engine-components/timeline/index.js.map +1 -1
  198. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  199. package/lib/engine-components/ui/Canvas.js +2 -8
  200. package/lib/engine-components/ui/Canvas.js.map +1 -1
  201. package/lib/engine-components/ui/Text.d.ts +1 -0
  202. package/lib/engine-components/ui/Text.js +10 -7
  203. package/lib/engine-components/ui/Text.js.map +1 -1
  204. package/lib/engine-components/web/CursorFollow.d.ts +0 -1
  205. package/lib/engine-components/web/CursorFollow.js +21 -13
  206. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  207. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +62 -1
  208. package/lib/engine-components/webxr/WebXRImageTracking.js +59 -2
  209. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  210. package/package.json +2 -83
  211. package/plugins/common/cloud.js +6 -1
  212. package/plugins/common/license.js +55 -12
  213. package/plugins/common/worker.js +9 -4
  214. package/plugins/types/userconfig.d.ts +4 -1
  215. package/plugins/vite/asap.js +17 -8
  216. package/plugins/vite/build-pipeline.js +57 -20
  217. package/plugins/vite/dependencies.js +29 -10
  218. package/plugins/vite/dependency-watcher.js +2 -2
  219. package/plugins/vite/editor-connection.js +3 -3
  220. package/plugins/vite/license.js +42 -7
  221. package/plugins/vite/local-files-core.js +3 -3
  222. package/plugins/vite/local-files-utils.d.ts +3 -1
  223. package/plugins/vite/local-files-utils.js +29 -5
  224. package/plugins/vite/reload.js +1 -1
  225. package/plugins/vite/server.js +2 -1
  226. package/src/engine/api.ts +11 -1
  227. package/src/engine/codegen/register_types.ts +10 -18
  228. package/src/engine/engine_audio.ts +184 -0
  229. package/src/engine/engine_camera.fit.ts +15 -4
  230. package/src/engine/engine_components.ts +1 -1
  231. package/src/engine/engine_context.ts +52 -19
  232. package/src/engine/engine_context_eventbus.ts +73 -0
  233. package/src/engine/engine_disposable.ts +214 -0
  234. package/src/engine/engine_gameobject.ts +54 -159
  235. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  236. package/src/engine/engine_init.ts +7 -7
  237. package/src/engine/engine_input.ts +28 -7
  238. package/src/engine/engine_instantiate_resolve.ts +407 -0
  239. package/src/engine/engine_license.ts +202 -56
  240. package/src/engine/engine_mainloop_utils.ts +7 -4
  241. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  242. package/src/engine/engine_networking.ts +161 -137
  243. package/src/engine/engine_networking_blob.ts +4 -4
  244. package/src/engine/engine_networking_instantiate.ts +2 -2
  245. package/src/engine/engine_networking_types.ts +41 -1
  246. package/src/engine/engine_physics_rapier.ts +102 -33
  247. package/src/engine/engine_pmrem.ts +53 -3
  248. package/src/engine/engine_scenedata.ts +3 -3
  249. package/src/engine/engine_serialization_builtin_serializer.ts +32 -9
  250. package/src/engine/engine_serialization_core.ts +9 -0
  251. package/src/engine/engine_types.ts +46 -27
  252. package/src/engine/engine_util_decorator.ts +7 -2
  253. package/src/engine/engine_utils.ts +16 -5
  254. package/src/engine/engine_utils_format.ts +20 -14
  255. package/src/engine/engine_utils_qrcode.ts +2 -2
  256. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  257. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  258. package/src/engine/webcomponents/needle menu/needle-menu.ts +6 -6
  259. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  260. package/src/engine/webcomponents/needle-engine.ts +12 -6
  261. package/src/engine/xr/NeedleXRSession.ts +48 -13
  262. package/src/engine/xr/TempXRContext.ts +2 -2
  263. package/src/engine/xr/events.ts +1 -1
  264. package/src/engine-components/Animation.ts +19 -16
  265. package/src/engine-components/AnimationBuilder.ts +472 -0
  266. package/src/engine-components/Animator.ts +24 -12
  267. package/src/engine-components/AnimatorController.builder.ts +387 -0
  268. package/src/engine-components/AnimatorController.ts +20 -291
  269. package/src/engine-components/AudioSource.ts +130 -79
  270. package/src/engine-components/Camera.ts +16 -3
  271. package/src/engine-components/CameraUtils.ts +12 -5
  272. package/src/engine-components/Collider.ts +66 -18
  273. package/src/engine-components/Component.ts +118 -20
  274. package/src/engine-components/ContactShadows.ts +15 -1
  275. package/src/engine-components/DragControls.ts +18 -11
  276. package/src/engine-components/DropListener.ts +4 -0
  277. package/src/engine-components/EventList.ts +45 -83
  278. package/src/engine-components/Joints.ts +20 -4
  279. package/src/engine-components/Light.ts +10 -2
  280. package/src/engine-components/Networking.ts +1 -1
  281. package/src/engine-components/OrbitControls.ts +42 -16
  282. package/src/engine-components/RigidBody.ts +18 -4
  283. package/src/engine-components/SceneSwitcher.ts +3 -0
  284. package/src/engine-components/SeeThrough.ts +2 -2
  285. package/src/engine-components/VideoPlayer.ts +40 -17
  286. package/src/engine-components/Voip.ts +88 -53
  287. package/src/engine-components/api.ts +3 -1
  288. package/src/engine-components/codegen/components.ts +7 -13
  289. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  290. package/src/engine-components/timeline/PlayableDirector.ts +83 -81
  291. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  292. package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
  293. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  294. package/src/engine-components/timeline/TimelineTracks.ts +96 -27
  295. package/src/engine-components/timeline/index.ts +2 -1
  296. package/src/engine-components/ui/Canvas.ts +2 -8
  297. package/src/engine-components/ui/Text.ts +12 -8
  298. package/src/engine-components/web/CursorFollow.ts +21 -14
  299. package/src/engine-components/webxr/WebXRImageTracking.ts +79 -7
  300. package/dist/needle-engine.bundle-1s2gOoKZ.min.js +0 -1732
  301. package/dist/needle-engine.bundle-j4nGJXCs.umd.cjs +0 -1732
  302. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  303. package/lib/engine-components/AvatarLoader.js +0 -232
  304. package/lib/engine-components/AvatarLoader.js.map +0 -1
  305. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  306. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  307. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  308. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  309. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  310. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  311. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  312. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  313. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  314. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  315. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  316. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  317. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  318. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  319. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  320. package/src/engine-components/AvatarLoader.ts +0 -264
  321. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  322. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  323. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  324. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  325. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
  326. package/src/vite-env.d.ts +0 -16
@@ -86,35 +86,71 @@ async function requestNeedleCloud(path, options) {
86
86
 
87
87
  /** @typedef {{ ok: boolean, status: number, statusText: string, text: string }} NeedleCloudHttpResponse */
88
88
 
89
+ /** @typedef {{ type: string, jwt: string | null }} LicenseResult */
90
+
89
91
  /**
90
92
  * Replace license string - used for webpack
91
93
  * @param {string} code
92
94
  * @param {DefaultOptions & {accessToken?:string, team:string|undefined}} opts
93
95
  */
94
96
  export async function replaceLicense(code, opts) {
95
- const index = code?.indexOf("NEEDLE_ENGINE_LICENSE_TYPE");
97
+ const index = code?.indexOf("_Ktp");
96
98
  if (index >= 0) {
97
- const licenseType = await resolveLicense(opts);
98
- if (!licenseType) {
99
+ const licenseResult = await resolveLicense(opts);
100
+ if (!licenseResult) {
99
101
  return code;
100
102
  }
101
103
  const end = code.indexOf(";", index);
102
104
  if (end >= 0) {
103
105
  const line = code.substring(index, end);
104
- const replaced = "NEEDLE_ENGINE_LICENSE_TYPE = \"" + licenseType + "\"";
106
+ const replaced = "_Ktp = \"" + licenseResult.type + "\"";
105
107
  code = code.replace(line, replaced);
106
- return code;
108
+ }
109
+
110
+ // Also inject JWT if available
111
+ if (licenseResult.jwt) {
112
+ const jwtIndex = code.indexOf("_HXKeIG");
113
+ if (jwtIndex >= 0) {
114
+ const jwtEnd = code.indexOf(";", jwtIndex);
115
+ if (jwtEnd >= 0) {
116
+ const jwtLine = code.substring(jwtIndex, jwtEnd);
117
+ const jwtReplaced = "_HXKeIG = \"" + licenseResult.jwt + "\"";
118
+ code = code.replace(jwtLine, jwtReplaced);
119
+ }
120
+ }
107
121
  }
108
122
  }
109
123
  return code;
110
124
  }
111
125
 
112
126
  /**
113
- * Resolve the license using the needle engine licensing server
127
+ * Module-level cache keyed by `team:accessToken`. The license is stable for
128
+ * the lifetime of the process, so multiple callers (license plugin, build
129
+ * pipeline) share one fetch.
130
+ * @type {Map<string, Promise<LicenseResult | null>>}
131
+ */
132
+ const licenseCache = new Map();
133
+
134
+ /**
135
+ * Resolve the license using the needle engine licensing server.
136
+ * Memoized per (team, accessToken).
114
137
  * @param {DefaultOptions & {accessToken?:string, team?:string} | null} args
115
- * @returns {Promise<string | null>}
138
+ * @returns {Promise<LicenseResult | null>}
116
139
  */
117
- export async function resolveLicense(args = null) {
140
+ export function resolveLicense(args = null) {
141
+ const cacheKey = `${args?.team || ""}:${args?.accessToken || ""}`;
142
+ const cached = licenseCache.get(cacheKey);
143
+ if (cached) return cached;
144
+ const promise = resolveLicenseUncached(args);
145
+ licenseCache.set(cacheKey, promise);
146
+ return promise;
147
+ }
148
+
149
+ /**
150
+ * @param {DefaultOptions & {accessToken?:string, team?:string} | null} args
151
+ * @returns {Promise<LicenseResult | null>}
152
+ */
153
+ async function resolveLicenseUncached(args = null) {
118
154
  let accessToken = args?.accessToken;
119
155
 
120
156
  // If a process.env.NEEDLE_CLOUD_TOKEN is set we want to use this (e.g. via nextjs)
@@ -210,7 +246,7 @@ export async function resolveLicense(args = null) {
210
246
  return null;
211
247
  }
212
248
  else if (!licenseResponse.ok) {
213
- if (licenseResponse.status === 500)
249
+ if (licenseResponse.status >= 500)
214
250
  logLicense(`ERROR: Failed to fetch license (${licenseResponse.status})`, "error");
215
251
  else
216
252
  logLicense(`No license found (${licenseResponse.status})`);
@@ -226,10 +262,11 @@ export async function resolveLicense(args = null) {
226
262
  /**
227
263
  * @param {string} str License string
228
264
  * @param {{includeFetchLine?: boolean}} [options]
265
+ * @returns {LicenseResult | null}
229
266
  */
230
267
  function tryParseLicense(str, options = undefined) {
231
268
  try {
232
- /** @type {{needle_engine_license:string}} */
269
+ /** @type {{needle_engine_license:string, needle_license_jwt?:string}} */
233
270
  const licenseJson = JSON.parse(str);
234
271
  if (licenseJson.needle_engine_license) {
235
272
  const success = `INFO: Successfully received \"${licenseJson.needle_engine_license?.toUpperCase()}\" license`;
@@ -239,7 +276,10 @@ function tryParseLicense(str, options = undefined) {
239
276
  else {
240
277
  logLicense(success);
241
278
  }
242
- return licenseJson.needle_engine_license;
279
+ return {
280
+ type: licenseJson.needle_engine_license,
281
+ jwt: licenseJson.needle_license_jwt || null,
282
+ };
243
283
  }
244
284
  if ("error" in licenseJson) {
245
285
  logLicense(`ERROR in license check: \"${licenseJson.error}\"`, "error");
@@ -361,7 +401,10 @@ export async function getPublicIdentifier(project_id, opts = undefined) {
361
401
  return null;
362
402
  }
363
403
  else if (!res.ok) {
364
- logIdentifier("ERROR: Failed to fetch project identifier", "error");
404
+ if (res.status >= 500)
405
+ logIdentifier(`Failed to fetch project identifier (${res.status})`, "error");
406
+ else
407
+ logIdentifier(`No project identifier found (${res.status})`);
365
408
  await releaseResponse(res);
366
409
  return null;
367
410
  }
@@ -1,3 +1,4 @@
1
+ import path from 'path';
1
2
  import { needleLog } from '../vite/logging.js';
2
3
 
3
4
 
@@ -53,13 +54,17 @@ export function rollupFixWorkerImport(opts = { logFail: true }) {
53
54
  // console.log("WORKER?", url)
54
55
  if (url?.startsWith("/")) {
55
56
  needleLog("rollup", `Rewrite worker import in ${chunk.fileName}`, "log", { leadingNewline: true, dimBody: false });
56
- // Make url file-relative
57
- const newUrl = url.replace(/^\//, "");
57
+ // Compute the relative path from the chunk's directory to the worker file.
58
+ // url is absolute from build root (e.g. "/assets/worker.js") and the chunk
59
+ // may itself be inside assets/ — using path.posix.relative avoids producing
60
+ // a double "assets/assets/" path.
61
+ const workerPath = url.replace(/^\//, "");
62
+ const chunkDir = path.posix.dirname(chunk.fileName);
63
+ const relativeUrl = path.posix.relative(chunkDir, workerPath) || workerPath;
58
64
  // For CORS issues we need to use importScripts: https://linear.app/needle/issue/NE-6572#comment-ea5dc65e
59
- const output = `/* new-worker */ new Worker(URL.createObjectURL(new Blob(["import '" + \`\${new URL('./${newUrl}', import.meta.url).toString()}\` + "';"], { type: 'text/javascript' }))`;
65
+ const output = `/* new-worker */ new Worker(URL.createObjectURL(new Blob(["import '" + \`\${new URL('./${relativeUrl}', import.meta.url).toString()}\` + "';"], { type: 'text/javascript' }))`;
60
66
  needleLog("rollup", "Did rewrite worker output to: " + output, "log", { leadingNewline: true });
61
67
  return output;
62
- // return `new Worker(new URL("./${newUrl}", import.meta.url)`;
63
68
  }
64
69
  return match;
65
70
  });
@@ -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
  /**
@@ -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(_config, tags) {
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: chunk,
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: {
@@ -1,5 +1,6 @@
1
1
  import { ChildProcess, exec } from 'child_process';
2
2
  import { NEEDLE_CLOUD_CLI_NAME } from '../common/cloud.js';
3
+ import { resolveLicense } from '../common/license.js';
3
4
  import { getOutputDirectory, loadConfig } from './config.js';
4
5
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
5
6
  import { relative } from 'path';
@@ -10,6 +11,8 @@ import { needleBlue, needleDim, needleLog, needleSupportsColor, setTransientLogL
10
11
  const PIPELINE_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
11
12
  const PIPELINE_STRUCTURED_LOG_PREFIX = "__needle_pipeline_log__:";
12
13
 
14
+ // #region Validation
15
+
13
16
  /**
14
17
  * @param {import('../types').userSettings} config
15
18
  * @returns {boolean}
@@ -37,8 +40,11 @@ env:
37
40
  return true;
38
41
  }
39
42
 
43
+ // #endregion
44
+
40
45
  // see https://linear.app/needle/issue/NE-3798
41
46
 
47
+ // #region State
42
48
 
43
49
  /** @type {Promise<void>|null} */
44
50
  let buildPipelineTask;
@@ -87,6 +93,10 @@ function increaseMaxWaitTime(debugLog) {
87
93
  }
88
94
  }
89
95
 
96
+ // #endregion
97
+
98
+ // #region Plugin
99
+
90
100
  /** Runs the needle build pipeline as part of the vite build process.
91
101
  * @param {"build" | "serve"} command
92
102
  * @param {import('../types/needleConfig').needleMeta | null | undefined} config
@@ -237,9 +247,12 @@ export async function needleBuildPipeline(command, config, userSettings) {
237
247
  }
238
248
  }
239
249
 
250
+ // #endregion
251
+
252
+ // #region Migration
240
253
 
241
254
  /**
242
- * Previously we did always install the build pipeline and run an extra command to invoke the build pipeline.
255
+ * Previously we did always install the build pipeline and run an extra command to invoke the build pipeline.
243
256
  * This is now done automatically by the needle build pipeline plugin - so we update all legacy projects to use the new method.
244
257
  * @param {string} packageJsonPath
245
258
  */
@@ -258,6 +271,10 @@ async function fixPackageJson(packageJsonPath) {
258
271
  writeFileSync(packageJsonPath, fixed);
259
272
  }
260
273
 
274
+ // #endregion
275
+
276
+ // #region Logging
277
+
261
278
  /** @param {...unknown} args */
262
279
  function log(...args) {
263
280
  needleLog("needle-buildpipeline", args.join(" "));
@@ -267,11 +284,15 @@ function warn(...args) {
267
284
  needleLog("needle-buildpipeline", args.join(" "), "warn");
268
285
  }
269
286
 
287
+ // #endregion
288
+
289
+ // #region Execution
290
+
270
291
  /**
271
292
  * @typedef {{ event?: string, phase?: string, target?: string, message?: string, level?: string }} BuildPipelinePayload
272
293
  */
273
294
  /**
274
- * @param {import('../types').userSettings} opts
295
+ * @param {import('../types').userSettings} opts
275
296
  * @param {{verbose?:boolean}} [options]
276
297
  * @returns {Promise<boolean>}
277
298
  */
@@ -283,12 +304,7 @@ async function invokeBuildPipeline(opts, options = {}) {
283
304
  const supportsColor = needleSupportsColor();
284
305
  const key = (/** @type {string} */ text) => supportsColor ? needleBlue(text) : text;
285
306
 
286
- const installPath = "node_modules/@needle-tools/gltf-build-pipeline";
287
- const fullInstallPath = process.cwd() + "/" + installPath;
288
- const existsLocally = existsSync(fullInstallPath);
289
- if (existsLocally) {
290
- log("Found local installation at " + fullInstallPath);
291
- }
307
+ // #region Wait for output
292
308
  await delay(500);
293
309
  const outputDirectory = getOutputDirectory() + "/assets";
294
310
  const startWaitTime = Date.now();
@@ -332,17 +348,18 @@ async function invokeBuildPipeline(opts, options = {}) {
332
348
  `${key("Files to process")}: ${files.length} in ${rel(outputDirectory)}, ${formatBytes(filesBytes)}`,
333
349
  existsSync(process.cwd() + "/node_modules/.needle/build-pipeline/output") ? needleDim("Removing temporary output directory") : undefined,
334
350
  ].filter(Boolean), "log", { dimBody: false });
351
+ // #endregion
352
+
353
+ // #region Setup
335
354
 
336
355
  /** @type {null | ChildProcess} */
337
356
  let proc = null;
338
357
 
339
- let cloudAccessToken = opts.buildPipeline?.accessToken || opts.license?.accessToken;
340
- if (!cloudAccessToken) {
341
- cloudAccessToken = process.env.NEEDLE_CLOUD_TOKEN;
342
- }
358
+ // Cloud token used by `${NEEDLE_CLOUD_CLI_NAME} optimize --token <token>`.
359
+ // This is a Needle Cloud access token (created in the Needle Cloud UI), NOT a JWT.
360
+ const cloudAccessToken = opts.buildPipeline?.accessToken;
343
361
  const runInCloud = typeof cloudAccessToken === "string" && cloudAccessToken.length > 0;
344
- // if a user has defined the build pipeline settings object but not passed in a token we should print out some information
345
- // or perhaps log an error / prevent the build from running completely
362
+
346
363
  if (opts.buildPipeline && !runInCloud && process.env.CI) {
347
364
  warn(`No cloud access token found. Please set it via process.env.NEEDLE_CLOUD_TOKEN`);
348
365
  return false;
@@ -367,6 +384,10 @@ async function invokeBuildPipeline(opts, options = {}) {
367
384
  }
368
385
  }
369
386
 
387
+ // #endregion
388
+
389
+ // #region Run
390
+
370
391
  // allow running the build pipeline in the cloud. It requires and access token to be set in the vite.config.js
371
392
  // this can be set via e.g. process.env.NEEDLE_CLOUD_TOKEN
372
393
  const commandEnv = { ...process.env, NEEDLE_PIPELINE_STRUCTURED_LOGS: "1" };
@@ -393,11 +414,6 @@ async function invokeBuildPipeline(opts, options = {}) {
393
414
  log(`Running compression in cloud ⛅ using access token: ${obfuscatedToken}`);
394
415
  proc = exec(cmd, { env: commandEnv });
395
416
  }
396
- else if (existsLocally) {
397
- const cmd = `needle-gltf transform "${outputDirectory}" \"${tempOutputPath}\"`;
398
- log("Running command \"" + cmd + "\" at " + process.cwd() + "...");
399
- proc = exec(cmd, { cwd: installPath, env: commandEnv });
400
- }
401
417
  else {
402
418
  // First check if the user passed in a specific version to use via the vite config
403
419
  let version = opts.buildPipeline?.version;
@@ -424,10 +440,21 @@ async function invokeBuildPipeline(opts, options = {}) {
424
440
  if (!version) version = "stable";
425
441
 
426
442
  const versionInfo = versionSource ? `'${version}' (${versionSource})` : `'${version}'`;
427
- const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}" \"${tempOutputPath}\"`;
443
+ // needle-gltf 3.x requires a JWT via `--auth-token <jwt>` on every CLI invocation.
444
+ // The JWT comes from the needle license server — it is NOT the Needle Cloud access token.
445
+ const licenseResult = await resolveLicense({
446
+ team: opts.license?.team,
447
+ accessToken: opts.license?.accessToken,
448
+ loglevel: opts.debugLicense === true ? "verbose" : undefined,
449
+ });
450
+ const authTokenArg = licenseResult?.jwt ? ` --auth-token ${licenseResult.jwt}` : "";
451
+ const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}" \"${tempOutputPath}\"${authTokenArg}`;
428
452
  log(`Running compression locally using version ${versionInfo}`);
429
453
  proc = exec(cmd, { env: commandEnv });
430
454
  }
455
+ // #endregion
456
+
457
+ // #region Output
431
458
  let pipelineSpinnerIndex = 0;
432
459
  let pipelineSpinnerActive = false;
433
460
  let transformStepCount = 0;
@@ -550,6 +577,9 @@ async function invokeBuildPipeline(opts, options = {}) {
550
577
  }
551
578
  proc.stdout?.on('data', onLog);
552
579
  proc.stderr?.on('data', onLog);
580
+ // #endregion
581
+
582
+ // #region Exit
553
583
  return new Promise((resolve, reject) => {
554
584
  proc.on('exit', (code) => {
555
585
  clearPipelineProgress();
@@ -570,8 +600,13 @@ async function invokeBuildPipeline(opts, options = {}) {
570
600
  resolve(success);
571
601
  });
572
602
  });
603
+ // #endregion
573
604
  }
574
605
 
606
+ // #endregion
607
+
608
+ // #region Helpers
609
+
575
610
  /** @param {string | null | undefined} directory */
576
611
  function getDirectoryStats(directory) {
577
612
  if (!directory || !existsSync(directory)) return { fileCount: 0, totalBytes: 0 };
@@ -597,3 +632,5 @@ function getDirectoryStats(directory) {
597
632
  }
598
633
  return { fileCount, totalBytes };
599
634
  }
635
+
636
+ // #endregion
@@ -61,6 +61,35 @@ export function needleDependencies(command, config, userSettings) {
61
61
  }
62
62
  },
63
63
  },
64
+ // Vite 8's optimizer rebases `new URL(specifier, import.meta.url)` paths
65
+ // inside pre-bundled dependencies (PR #21434), but only handles truly relative
66
+ // paths (./ ../). Bare-specifier paths like `three-mesh-bvh/src/workers/...`
67
+ // are treated as relative to the *source file* that contained the `new URL()`
68
+ // call, producing a wrong URL such as:
69
+ // /node_modules/@needle-tools/engine/.../three-mesh-bvh/src/workers/generateMeshBVH.worker.js
70
+ // This middleware rewrites those broken URLs so the dev server finds the file at
71
+ // its real location in node_modules.
72
+ {
73
+ name: 'needle:worker-url-rewrite',
74
+ configureServer(server) {
75
+ const rewritePackages = ['three-mesh-bvh'];
76
+ server.middlewares.use((req, _res, next) => {
77
+ if (req.url) {
78
+ for (const pkg of rewritePackages) {
79
+ const marker = `/${pkg}/`;
80
+ if (req.url.includes(marker) && !req.url.startsWith(`/node_modules/${pkg}/`) && !req.url.startsWith('/@fs/')) {
81
+ const idx = req.url.indexOf(marker);
82
+ const rewritten = '/node_modules' + req.url.slice(idx);
83
+ needleLog('needle-dependencies', `Rewriting worker URL → ${rewritten}`);
84
+ req.url = rewritten;
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ next();
90
+ });
91
+ },
92
+ },
64
93
  ]
65
94
  }
66
95
 
@@ -158,16 +187,6 @@ function handleOptimizeDeps(config) {
158
187
  config.optimizeDeps.exclude.push('@needle-tools/engine');
159
188
  needleLog("needle-dependencies", 'Detected local @needle-tools/engine package → will exclude it from optimization');
160
189
  }
161
- // When engine is excluded from optimizeDeps, three-mesh-bvh must also be excluded.
162
- // The BVH worker (generateMeshBVH.worker.js) uses bare imports like `import 'three'`
163
- // which only resolve correctly when served through Vite's dev server module system.
164
- // If three-mesh-bvh is pre-bundled, the worker URL points into the cache and bare
165
- // imports fail at runtime → "Unknown error. Please check the server console."
166
- if (!config.optimizeDeps.include?.includes('three-mesh-bvh') &&
167
- !config.optimizeDeps.exclude.includes('three-mesh-bvh')) {
168
- config.optimizeDeps.exclude.push('three-mesh-bvh');
169
- needleLog("needle-dependencies", 'Detected local @needle-tools/engine package → will also exclude three-mesh-bvh from optimization');
170
- }
171
190
  }
172
191
  }
173
192
 
@@ -98,7 +98,7 @@ function watchPackageJson(server, projectDir, packageJsonPath, cachePath) {
98
98
 
99
99
  setTimeout(() => {
100
100
  requireInstall = testIfInstallIsRequired(projectDir, packageJson);
101
- }, 1000);
101
+ }, 1000).unref();
102
102
 
103
103
  setInterval(() => {
104
104
  if (!packageJson || lastEditTime === undefined) return;
@@ -149,7 +149,7 @@ function watchPackageJson(server, projectDir, packageJsonPath, cachePath) {
149
149
  restart(server, projectDir, cachePath);
150
150
  }
151
151
  }
152
- }, 2000);
152
+ }, 2000).unref();
153
153
  }
154
154
 
155
155
  /** @param {string} projectDir @param {PackageJson | undefined} packageJson @returns {boolean} */
@@ -27,7 +27,7 @@ export async function editorConnection(command, config, userSettings, pluginsArr
27
27
  if (typeof config.generator === "string" && !config.generator.includes("Unity")) return;
28
28
 
29
29
  if (!config) {
30
- setTimeout(() => needleLog("needle-editor-sync", "Needle Editor Sync can not be installed automatically to vite: missing config", "warn"), 1000);
30
+ setTimeout(() => needleLog("needle-editor-sync", "Needle Editor Sync can not be installed automatically to vite: missing config", "warn"), 1000).unref();
31
31
  return createPlugin(false);
32
32
  }
33
33
 
@@ -42,14 +42,14 @@ export async function editorConnection(command, config, userSettings, pluginsArr
42
42
  // }
43
43
  // }
44
44
  if (needleEditorSettings && needleEditorSettings.enabled === false) {
45
- setTimeout(() => needleLog("needle-editor-sync", "Needle Editor Sync is not enabled. Add a 'Needle Editor Sync' component to your scene to enable", "warn"), 1000);
45
+ setTimeout(() => needleLog("needle-editor-sync", "Needle Editor Sync is not enabled. Add a 'Needle Editor Sync' component to your scene to enable", "warn"), 1000).unref();
46
46
  return createPlugin(false);
47
47
  }
48
48
 
49
49
  // Check if the editor package is installed
50
50
  let path = root + `/node_modules/${editorSyncPackageName}/plugins/index.js`;
51
51
  if (existsSync(path) === false) {
52
- setTimeout(() => needleLog("needle-editor-sync", `${editorSyncPackageName} is not installed: Add the "Needle Editor Sync" component to your scene if you want to send changes directly from the Unity Editor to web app`, "warn"), 1000);
52
+ setTimeout(() => needleLog("needle-editor-sync", `${editorSyncPackageName} is not installed: Add the "Needle Editor Sync" component to your scene if you want to send changes directly from the Unity Editor to web app`, "warn"), 1000).unref();
53
53
  return createPlugin(false);
54
54
  }
55
55
 
@@ -8,7 +8,9 @@ import { loadConfig } from './config.js';
8
8
  * @returns {import('vite').Plugin}
9
9
  */
10
10
  export function needleLicense(command, config, userSettings) {
11
- let license = undefined;
11
+ /** @type {import('../common/license.js').LicenseResult | null | undefined} */
12
+ let licenseResult = undefined;
13
+ let appliedLicense = false;
12
14
 
13
15
  return {
14
16
  name: "needle:license",
@@ -23,7 +25,7 @@ export function needleLicense(command, config, userSettings) {
23
25
  }
24
26
  }
25
27
 
26
- license = await resolveLicense({
28
+ licenseResult = await resolveLicense({
27
29
  team: team,
28
30
  accessToken: userSettings?.license?.accessToken,
29
31
  loglevel: userSettings?.debugLicense === true ? "verbose" : undefined
@@ -31,25 +33,58 @@ export function needleLicense(command, config, userSettings) {
31
33
 
32
34
  },
33
35
  async transform(src, id) {
34
- const isNeedleEngineFile = id.includes("engine/engine_license") || id.includes("needle-tools_engine");
36
+ // Vite 4 and 8 handling:
37
+ const isNeedleEngineFile = id.includes("engine/engine_license")
38
+ || id.includes("needle-tools_engine")
39
+ || id.includes("@needle-tools")
40
+ || id.includes("needle-engine");
35
41
  // sometimes the actual license parameter is in a unnamed chunk file
36
42
  const isViteChunkFile = id.includes("chunk") && id.includes(".vite");
37
43
  if (isNeedleEngineFile || isViteChunkFile) {
38
44
 
39
- if (!license) {
45
+ if (!licenseResult) {
40
46
  return;
41
47
  }
42
48
 
43
- const index = src.indexOf("NEEDLE_ENGINE_LICENSE_TYPE");
49
+ let modified = false;
50
+
51
+ // Replace license type
52
+ const index = src.indexOf("_Ktp");
44
53
  if (index >= 0) {
45
54
  const end = src.indexOf(";", index);
46
55
  if (end >= 0) {
47
56
  const line = src.substring(index, end);
48
- const replaced = "NEEDLE_ENGINE_LICENSE_TYPE = \"" + license + "\"";
57
+ const replaced = "_Ktp = \"" + licenseResult.type + "\"";
49
58
  src = src.replace(line, replaced);
50
- return { code: src, map: null }
59
+ modified = true;
51
60
  }
52
61
  }
62
+
63
+ // Replace license JWT (same pattern)
64
+ if (licenseResult.jwt) {
65
+ const jwtIndex = src.indexOf("_HXKeIG");
66
+ if (jwtIndex >= 0) {
67
+ const jwtEnd = src.indexOf(";", jwtIndex);
68
+ if (jwtEnd >= 0) {
69
+ const jwtLine = src.substring(jwtIndex, jwtEnd);
70
+ const jwtReplaced = "_HXKeIG = \"" + licenseResult.jwt + "\"";
71
+ src = src.replace(jwtLine, jwtReplaced);
72
+ modified = true;
73
+ }
74
+ }
75
+ }
76
+
77
+ if (modified) {
78
+ appliedLicense = true;
79
+ return { code: src, map: null }
80
+ }
81
+ }
82
+ },
83
+ buildEnd() {
84
+ if (!appliedLicense) {
85
+ if (process.env.NEEDLE_TEST_ENV) {
86
+ console.error("ERR: License was not applied!");
87
+ }
53
88
  }
54
89
  }
55
90
  }
@@ -291,7 +291,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
291
291
  failedDownloads,
292
292
  localizationStats,
293
293
  }, activeHandlers);
294
- src = fixRelativeNewURL(src);
294
+ src = fixRelativeNewURL(src, viteConfig?.base);
295
295
  }
296
296
  catch (err) {
297
297
  needleLog("needle:local-files", "Error in transform: " + getErrMessage(err), "error");
@@ -303,7 +303,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
303
303
  },
304
304
  renderChunk(code, chunk) {
305
305
  if (!chunk.fileName?.endsWith(".js")) return null;
306
- const fixed = fixRelativeNewURL(code);
306
+ const fixed = fixRelativeNewURL(code, viteConfig?.base);
307
307
  if (fixed === code) return null;
308
308
  return {
309
309
  code: fixed,
@@ -314,7 +314,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
314
314
  for (const output of Object.values(bundle)) {
315
315
  if (output.type !== "chunk") continue;
316
316
  if (!output.fileName?.endsWith(".js")) continue;
317
- const fixed = fixRelativeNewURL(output.code);
317
+ const fixed = fixRelativeNewURL(output.code, viteConfig?.base);
318
318
  if (fixed !== output.code) output.code = fixed;
319
319
  }
320
320
  },
@@ -24,9 +24,11 @@ export function normalizeWebPath(path: string): string;
24
24
  export function ensureTrailingSlash(path: string): string;
25
25
  /**
26
26
  * @param {string} src
27
+ * @param {string} [base] - Vite base path (e.g. "/" or "/app/"). When provided,
28
+ * ext/ paths are made absolute so they resolve correctly under SPA routing.
27
29
  * @returns {string}
28
30
  */
29
- export function fixRelativeNewURL(src: string): string;
31
+ export function fixRelativeNewURL(src: string, base?: string): string;
30
32
  /**
31
33
  * @param {string} src
32
34
  * @returns {string}