@needle-tools/engine 5.1.0-canary.fbdfce3 → 5.1.0-experimental.03e8105

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 (311) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/SKILL.md +4 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-BFSj2Fz8.js → needle-engine.bundle-BNqUjnSQ.js} +19180 -18386
  5. package/dist/needle-engine.bundle-Bt8ULD7E.umd.cjs +1733 -0
  6. package/dist/needle-engine.bundle-DF6ovbwD.min.js +1733 -0
  7. package/dist/needle-engine.d.ts +1487 -356
  8. package/dist/needle-engine.js +544 -542
  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 +32 -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 +183 -57
  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_scenedata.js +2 -2
  70. package/lib/engine/engine_scenedata.js.map +1 -1
  71. package/lib/engine/engine_serialization_builtin_serializer.js +28 -5
  72. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  73. package/lib/engine/engine_serialization_core.d.ts +1 -0
  74. package/lib/engine/engine_serialization_core.js +7 -0
  75. package/lib/engine/engine_serialization_core.js.map +1 -1
  76. package/lib/engine/engine_types.d.ts +29 -11
  77. package/lib/engine/engine_types.js +1 -1
  78. package/lib/engine/engine_types.js.map +1 -1
  79. package/lib/engine/engine_util_decorator.js +7 -2
  80. package/lib/engine/engine_util_decorator.js.map +1 -1
  81. package/lib/engine/engine_utils.d.ts +1 -1
  82. package/lib/engine/engine_utils.js +19 -5
  83. package/lib/engine/engine_utils.js.map +1 -1
  84. package/lib/engine/engine_utils_qrcode.js +2 -2
  85. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  86. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  87. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  88. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  89. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  90. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -1
  91. package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
  92. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  93. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  94. package/lib/engine/webcomponents/needle-engine.js +3 -3
  95. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  96. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  97. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  98. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  99. package/lib/engine/xr/NeedleXRSession.js +50 -14
  100. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  101. package/lib/engine/xr/TempXRContext.js +2 -2
  102. package/lib/engine/xr/TempXRContext.js.map +1 -1
  103. package/lib/engine/xr/events.d.ts +1 -1
  104. package/lib/engine/xr/events.js.map +1 -1
  105. package/lib/engine-components/Animation.js +17 -16
  106. package/lib/engine-components/Animation.js.map +1 -1
  107. package/lib/engine-components/AnimationBuilder.d.ts +158 -0
  108. package/lib/engine-components/AnimationBuilder.js +305 -0
  109. package/lib/engine-components/AnimationBuilder.js.map +1 -0
  110. package/lib/engine-components/Animator.d.ts +6 -0
  111. package/lib/engine-components/Animator.js +23 -13
  112. package/lib/engine-components/Animator.js.map +1 -1
  113. package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
  114. package/lib/engine-components/AnimatorController.builder.js +263 -0
  115. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  116. package/lib/engine-components/AnimatorController.d.ts +2 -119
  117. package/lib/engine-components/AnimatorController.js +33 -232
  118. package/lib/engine-components/AnimatorController.js.map +1 -1
  119. package/lib/engine-components/AudioSource.d.ts +19 -3
  120. package/lib/engine-components/AudioSource.js +121 -68
  121. package/lib/engine-components/AudioSource.js.map +1 -1
  122. package/lib/engine-components/Collider.d.ts +18 -9
  123. package/lib/engine-components/Collider.js +61 -14
  124. package/lib/engine-components/Collider.js.map +1 -1
  125. package/lib/engine-components/Component.d.ts +72 -9
  126. package/lib/engine-components/Component.js +114 -10
  127. package/lib/engine-components/Component.js.map +1 -1
  128. package/lib/engine-components/ContactShadows.d.ts +1 -0
  129. package/lib/engine-components/ContactShadows.js +14 -1
  130. package/lib/engine-components/ContactShadows.js.map +1 -1
  131. package/lib/engine-components/DragControls.d.ts +7 -0
  132. package/lib/engine-components/DragControls.js +19 -7
  133. package/lib/engine-components/DragControls.js.map +1 -1
  134. package/lib/engine-components/DropListener.js +3 -0
  135. package/lib/engine-components/DropListener.js.map +1 -1
  136. package/lib/engine-components/EventList.d.ts +31 -9
  137. package/lib/engine-components/EventList.js +37 -76
  138. package/lib/engine-components/EventList.js.map +1 -1
  139. package/lib/engine-components/Joints.d.ts +4 -2
  140. package/lib/engine-components/Joints.js +19 -3
  141. package/lib/engine-components/Joints.js.map +1 -1
  142. package/lib/engine-components/Light.js +9 -1
  143. package/lib/engine-components/Light.js.map +1 -1
  144. package/lib/engine-components/Networking.d.ts +1 -1
  145. package/lib/engine-components/Networking.js +1 -1
  146. package/lib/engine-components/OrbitControls.d.ts +0 -2
  147. package/lib/engine-components/OrbitControls.js +30 -12
  148. package/lib/engine-components/OrbitControls.js.map +1 -1
  149. package/lib/engine-components/RigidBody.d.ts +12 -4
  150. package/lib/engine-components/RigidBody.js +18 -4
  151. package/lib/engine-components/RigidBody.js.map +1 -1
  152. package/lib/engine-components/SceneSwitcher.js +3 -0
  153. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  154. package/lib/engine-components/SeeThrough.js +2 -2
  155. package/lib/engine-components/SeeThrough.js.map +1 -1
  156. package/lib/engine-components/api.d.ts +2 -1
  157. package/lib/engine-components/api.js +2 -1
  158. package/lib/engine-components/api.js.map +1 -1
  159. package/lib/engine-components/codegen/components.d.ts +7 -13
  160. package/lib/engine-components/codegen/components.js +7 -13
  161. package/lib/engine-components/codegen/components.js.map +1 -1
  162. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  163. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  164. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  165. package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
  166. package/lib/engine-components/timeline/PlayableDirector.js +75 -67
  167. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  168. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  169. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  170. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  171. package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
  172. package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
  173. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  174. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  175. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  176. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  177. package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
  178. package/lib/engine-components/timeline/TimelineTracks.js +92 -26
  179. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  180. package/lib/engine-components/timeline/index.d.ts +2 -1
  181. package/lib/engine-components/timeline/index.js +2 -0
  182. package/lib/engine-components/timeline/index.js.map +1 -1
  183. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  184. package/lib/engine-components/ui/Canvas.js +2 -8
  185. package/lib/engine-components/ui/Canvas.js.map +1 -1
  186. package/lib/engine-components/ui/Text.d.ts +1 -0
  187. package/lib/engine-components/ui/Text.js +10 -7
  188. package/lib/engine-components/ui/Text.js.map +1 -1
  189. package/lib/engine-components/web/CursorFollow.d.ts +0 -1
  190. package/lib/engine-components/web/CursorFollow.js +21 -13
  191. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  192. package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
  193. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  194. package/package.json +2 -83
  195. package/plugins/common/cloud.js +6 -1
  196. package/plugins/common/license.js +31 -10
  197. package/plugins/common/worker.js +9 -4
  198. package/plugins/vite/asap.js +17 -8
  199. package/plugins/vite/dependencies.js +29 -10
  200. package/plugins/vite/dependency-watcher.js +2 -2
  201. package/plugins/vite/editor-connection.js +3 -3
  202. package/plugins/vite/license.js +46 -7
  203. package/plugins/vite/local-files-core.js +3 -3
  204. package/plugins/vite/local-files-utils.d.ts +3 -1
  205. package/plugins/vite/local-files-utils.js +29 -5
  206. package/plugins/vite/reload.js +1 -1
  207. package/plugins/vite/server.js +2 -1
  208. package/src/engine/api.ts +11 -1
  209. package/src/engine/codegen/register_types.ts +10 -18
  210. package/src/engine/engine_audio.ts +184 -0
  211. package/src/engine/engine_camera.fit.ts +15 -4
  212. package/src/engine/engine_components.ts +1 -1
  213. package/src/engine/engine_context.ts +34 -18
  214. package/src/engine/engine_context_eventbus.ts +73 -0
  215. package/src/engine/engine_disposable.ts +214 -0
  216. package/src/engine/engine_gameobject.ts +54 -159
  217. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  218. package/src/engine/engine_init.ts +7 -7
  219. package/src/engine/engine_input.ts +28 -7
  220. package/src/engine/engine_instantiate_resolve.ts +407 -0
  221. package/src/engine/engine_license.ts +197 -55
  222. package/src/engine/engine_mainloop_utils.ts +7 -4
  223. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  224. package/src/engine/engine_networking.ts +161 -137
  225. package/src/engine/engine_networking_blob.ts +4 -4
  226. package/src/engine/engine_networking_instantiate.ts +2 -2
  227. package/src/engine/engine_networking_types.ts +41 -1
  228. package/src/engine/engine_physics_rapier.ts +102 -33
  229. package/src/engine/engine_scenedata.ts +3 -3
  230. package/src/engine/engine_serialization_builtin_serializer.ts +32 -9
  231. package/src/engine/engine_serialization_core.ts +9 -0
  232. package/src/engine/engine_types.ts +46 -27
  233. package/src/engine/engine_util_decorator.ts +7 -2
  234. package/src/engine/engine_utils.ts +16 -5
  235. package/src/engine/engine_utils_qrcode.ts +2 -2
  236. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  237. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  238. package/src/engine/webcomponents/needle menu/needle-menu.ts +6 -6
  239. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  240. package/src/engine/webcomponents/needle-engine.ts +12 -6
  241. package/src/engine/xr/NeedleXRSession.ts +48 -13
  242. package/src/engine/xr/TempXRContext.ts +2 -2
  243. package/src/engine/xr/events.ts +1 -1
  244. package/src/engine-components/Animation.ts +19 -16
  245. package/src/engine-components/AnimationBuilder.ts +472 -0
  246. package/src/engine-components/Animator.ts +24 -12
  247. package/src/engine-components/AnimatorController.builder.ts +387 -0
  248. package/src/engine-components/AnimatorController.ts +20 -291
  249. package/src/engine-components/AudioSource.ts +130 -79
  250. package/src/engine-components/Collider.ts +66 -18
  251. package/src/engine-components/Component.ts +118 -20
  252. package/src/engine-components/ContactShadows.ts +15 -1
  253. package/src/engine-components/DragControls.ts +18 -11
  254. package/src/engine-components/DropListener.ts +3 -0
  255. package/src/engine-components/EventList.ts +45 -83
  256. package/src/engine-components/Joints.ts +20 -4
  257. package/src/engine-components/Light.ts +10 -2
  258. package/src/engine-components/Networking.ts +1 -1
  259. package/src/engine-components/OrbitControls.ts +34 -14
  260. package/src/engine-components/RigidBody.ts +18 -4
  261. package/src/engine-components/SceneSwitcher.ts +3 -0
  262. package/src/engine-components/SeeThrough.ts +2 -2
  263. package/src/engine-components/api.ts +2 -1
  264. package/src/engine-components/codegen/components.ts +7 -13
  265. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  266. package/src/engine-components/timeline/PlayableDirector.ts +83 -81
  267. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  268. package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
  269. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  270. package/src/engine-components/timeline/TimelineTracks.ts +96 -27
  271. package/src/engine-components/timeline/index.ts +2 -1
  272. package/src/engine-components/ui/Canvas.ts +2 -8
  273. package/src/engine-components/ui/Text.ts +12 -8
  274. package/src/engine-components/web/CursorFollow.ts +21 -14
  275. package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
  276. package/dist/needle-engine.bundle-CmxIO5uH.min.js +0 -1732
  277. package/dist/needle-engine.bundle-tJIZukCz.umd.cjs +0 -1732
  278. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  279. package/lib/engine-components/AvatarLoader.js +0 -232
  280. package/lib/engine-components/AvatarLoader.js.map +0 -1
  281. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  282. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  283. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  284. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  285. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  286. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  287. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  288. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  289. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  290. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  291. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  292. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  293. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  294. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  295. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  296. package/plugins/dts-generator/dts.codegen.js +0 -334
  297. package/plugins/dts-generator/dts.scan.js +0 -99
  298. package/plugins/dts-generator/dts.writer.js +0 -59
  299. package/plugins/dts-generator/glb.discovery.js +0 -279
  300. package/plugins/dts-generator/glb.extractor.js +0 -215
  301. package/plugins/dts-generator/glb.reader.js +0 -167
  302. package/plugins/dts-generator/index.js +0 -36
  303. package/plugins/dts-generator/manifest.types.js +0 -174
  304. package/plugins/gltf-packer.mjs +0 -1
  305. package/src/engine-components/AvatarLoader.ts +0 -264
  306. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  307. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  308. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  309. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  310. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
  311. package/src/vite-env.d.ts +0 -16
@@ -2,7 +2,6 @@ const defaultNetworkingBackendUrlProvider = "https://urls.needle.tools/default-n
2
2
  let networkingServerUrl: string | undefined = "wss://networking-2.needle.tools/socket";
3
3
 
4
4
  import * as flatbuffers from 'flatbuffers';
5
- import { type Websocket } from 'websocket-ts';
6
5
 
7
6
  import type { Networking } from "../engine-components/Networking.js";
8
7
  import type { SyncedRoom } from "../engine-components/SyncedRoom.js";
@@ -10,11 +9,16 @@ import * as schemes from "../engine-schemes/schemes.js";
10
9
  import { isDevEnvironment } from './debug/index.js';
11
10
  import { Telemetry } from './engine_license.js';
12
11
  import { PeerNetworking } from './engine_networking_peer.js';
13
- import { type IModel, type INetworkConnection, SendQueue } from './engine_networking_types.js';
12
+ import { type IModel, type INetworkConnection, type INetworkTransport, SendQueue } from './engine_networking_types.js';
13
+ import { WebsocketTransport } from './engine_networking.transport.websocket.js';
14
14
  import { isHostedOnGlitch } from './engine_networking_utils.js';
15
15
  import { Context } from './engine_setup.js';
16
+ import type { DisposeFn } from "./engine_disposable.js";
16
17
  import * as utils from "./engine_utils.js";
17
18
 
19
+ /** @internal Symbol used to attach the original callback to unsubscribe functions returned by beginListen/beginListenBinary */
20
+ const $originalCallback = Symbol("originalCallback");
21
+
18
22
  export const debugNet = utils.getParam("debugnet") ? true : false;
19
23
  export const debugOwner = debugNet || utils.getParam("debugowner") ? true : false;
20
24
  const debugnetBin = utils.getParam("debugnetbin");
@@ -58,16 +62,6 @@ export enum RoomEvents {
58
62
  /** When a user joins a room, the server sends the entire room state. Afterwards, the server sends the room-state-sent event. */
59
63
  RoomStateSent = "room-state-sent",
60
64
  }
61
- /**
62
- * See {@link RoomEvents} for all event names and docs.
63
- * - `joined-room`: When the local user has joined a room
64
- * - `left-room`: When the local user has left a room
65
- * - `user-joined-room`: When a other user has joined the room
66
- * - `user-left-room`: When a other user has left the room
67
- * - `room-state-sent`: When the server has sent the room state to the client
68
- */
69
- type RoomEventsIncoming = Exclude<`${RoomEvents}`, "join-room" | "leave-room">;
70
-
71
65
  /** Received when listening to `RoomEvents.JoinedRoom` event */
72
66
  export class JoinedRoomResponse {
73
67
  room!: string; // room name
@@ -100,8 +94,6 @@ export enum OwnershipEvent {
100
94
  GainedOwnershipBroadcast = 'gained-ownership-broadcast',
101
95
  LostOwnershipBroadcast = 'lost-ownership-broadcast',
102
96
  }
103
- type OwnershipEventNamesIncoming = Exclude<`${OwnershipEvent}`, "request-has-owner" | "request-is-owner" | "request-ownership" | "remove-ownership">;
104
-
105
97
  declare type GainedOwnershipBroadcastResponse = {
106
98
  guid: string;
107
99
  owner: string;
@@ -116,7 +108,7 @@ declare type OwnershipResponse = {
116
108
  value: boolean;
117
109
  }
118
110
 
119
- declare type WebsocketSendType = IModel | object | boolean | null | string | number;
111
+ declare type WebsocketSendType = IModel | object | boolean | string | number;
120
112
 
121
113
  /** Maps known networking event keys to their callback signatures.
122
114
  * Used by {@link NetworkConnection.beginListen} and {@link NetworkConnection.stopListen} for type-safe event handling.
@@ -559,8 +551,7 @@ export class NetworkConnection implements INetworkConnection {
559
551
  * The current server url that the networking backend is connected to (e.g. the url of the websocket server)
560
552
  */
561
553
  public get currentServerUrl(): string | null {
562
- // @ts-ignore (in ts-websocket 2.x this property is exposed)
563
- return this._ws?.url ?? null;
554
+ return this._transport?.url ?? null;
564
555
  }
565
556
 
566
557
  /** A ping is sent to the server at a regular interval while the browser tab is active. This method can be used to send additional ping messages when needed so that the user doesn't get disconnected from the networking backend */
@@ -620,25 +611,16 @@ export class NetworkConnection implements INetworkConnection {
620
611
  /** Send a message to the networking backend - it will be broadcasted to all connected users (except yourself) in the same room by default */
621
612
  public send<K extends NetworkEventKey>(
622
613
  key: K,
623
- data?: (K extends keyof NetworkEventMap ? NetworkEventData<K> : WebsocketSendType) | null,
614
+ data?: (K extends keyof NetworkEventMap ? NetworkEventData<K> : WebsocketSendType),
624
615
  queue?: SendQueue,
625
616
  ): void
626
- public send(key: string, data: WebsocketSendType | null = null, queue: SendQueue = SendQueue.Queued) {
627
-
628
- //@ts-ignore
629
- if (data === null) data = {};
617
+ public send(key: string, data?: WebsocketSendType, queue: SendQueue = SendQueue.Queued) {
630
618
 
631
619
  if (queue === SendQueue.Queued) {
632
620
  this._defaultMessagesBuffer.push({ key: key, value: data });
633
621
  return;
634
622
  }
635
623
 
636
- // if (!this.connected) return;
637
- // if (this.channelId)
638
- // data["__id"] = this.channelId;
639
- // else if (this.connectionId)
640
- // data["__id"] = this.connectionId;
641
- // this.sendGeckosIo(key, data);
642
624
  return this.sendWithWebsocket(key, data, queue);
643
625
  }
644
626
 
@@ -668,13 +650,13 @@ export class NetworkConnection implements INetworkConnection {
668
650
  /** Send a binary message to the server (broadcasted to all connected users) */
669
651
  public sendBinary(bin: Uint8Array) {
670
652
  if (debugnetBin) console.log("<< send binary", this.context.time.frame, (bin.length / 1024) + " KB");
671
- this._ws?.send(bin);
653
+ this._transport?.send(bin);
672
654
  }
673
655
 
674
656
  private _defaultMessagesBuffer: Array<{ key: string, value: any }> = [];
675
657
  private _defaultMessagesBufferArray: Array<{ key: string, data: any }> = [];
676
658
  public sendBufferedMessagesNow() {
677
- if (!this._ws) return;
659
+ if (!this._transport) return;
678
660
  this._defaultMessagesBufferArray.length = 0;
679
661
  const count = Object.keys(this._defaultMessagesBuffer).length;
680
662
  for (const key in this._defaultMessagesBuffer) {
@@ -692,47 +674,59 @@ export class NetworkConnection implements INetworkConnection {
692
674
  console.log("SEND BUFFERED", this._defaultMessagesBufferArray.length);
693
675
  if (this._defaultMessagesBufferArray.length <= 0) return;
694
676
  const message = JSON.stringify(this._defaultMessagesBufferArray);
695
- this._ws?.send(message);
677
+ this._transport?.send(message);
696
678
  }
697
679
 
698
- /** Use to start listening to networking events.
699
- * To unsubscribe from events use the `{@link stopListen}` method.
700
- *
701
- * @example Custom event example
680
+ /** Use to start listening to networking events.
681
+ * Returns an unsubscribe function that removes the listener when called.
682
+ * The returned function can also be passed to {@link stopListen} or {@link Component.autoCleanup} for automatic lifecycle management.
683
+ *
684
+ * @example With autoCleanup (recommended)
685
+ * ```ts
686
+ * export class MyComponent extends Behaviour {
687
+ * onEnable() {
688
+ * this.autoCleanup(this.context.connection.beginListen("joined-room", () => {
689
+ * console.log("I joined a networked room");
690
+ * }));
691
+ * }
692
+ * // Automatically unsubscribed on disable — no manual cleanup needed!
693
+ * }
694
+ * ```
695
+ *
696
+ * @example Manual unsubscribe
702
697
  * ```ts
703
- * // Listen to a custom event sent by the server
704
- * this.context.connection.beginListen<MyDataType>("my-custom-event", (data) => {
705
- * console.log("Received custom event:", data);
706
- * });
698
+ * const unsub = this.context.connection.beginListen("joined-room", () => { });
699
+ * // Later:
700
+ * unsub(); // removes the listener
707
701
  * ```
708
702
  *
709
- * @example Listening to room events
703
+ * @example With stopListen (legacy pattern, still supported)
710
704
  * ```ts
711
- * // Make sure to unsubscribe from events when the component is disabled
712
705
  * export class MyComponent extends Behaviour {
713
706
  * onEnable() {
714
- * this.connection.beginListen("joined-room", this.onJoinedRoom)
707
+ * this.context.connection.beginListen("joined-room", this.onJoinedRoom);
715
708
  * }
716
709
  * onDisable() {
717
- * this.connection.stopListen("joined-room", this.onJoinedRoom)
710
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom);
718
711
  * }
719
712
  * onJoinedRoom = () => {
720
- * console.log("I joined a networked room")
713
+ * console.log("I joined a networked room");
721
714
  * }
722
715
  * }
723
716
  * ```
724
717
  * @link https://engine.needle.tools/docs/networking.html
725
- *
718
+ *
726
719
  */
727
720
  public beginListen<K extends NetworkEventKey>(
728
721
  key: K,
729
722
  callback: K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void,
730
- ): K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void {
723
+ ): DisposeFn {
731
724
  if (!this._listeners[key])
732
725
  this._listeners[key] = [];
733
726
  this._listeners[key].push(callback);
734
- // TODO: for a breaking change it would be easier to return a function to unsubscribe directly
735
- return callback as any;
727
+ const unsub = () => this.stopListen(key, callback as any);
728
+ (unsub as any)[$originalCallback] = callback;
729
+ return unsub;
736
730
  }
737
731
 
738
732
  /**@deprecated please use stopListen instead (2.65.2-pre) */
@@ -741,51 +735,59 @@ export class NetworkConnection implements INetworkConnection {
741
735
  callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | null,
742
736
  ) { return this.stopListen(key, callback as any); }
743
737
 
744
- /** Use to stop listening to networking events
745
- * To subscribe to events use the `{@link beginListen}` method.
746
- * See the example below for typical usage:
747
- *
748
- * ### Component Example
738
+ /** Use to stop listening to networking events.
739
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListen}.
740
+ * To subscribe to events use the `{@link beginListen}` method.
741
+ *
742
+ * @example
749
743
  * ```ts
750
- * // Make sure to unsubscribe from events when the component is disabled
751
- * export class MyComponent extends Behaviour {
752
- * onEnable() {
753
- * this.connection.beginListen("joined-room", this.onJoinedRoom)
754
- * }
755
- * onDisable() {
756
- * this.connection.stopListen("joined-room", this.onJoinedRoom)
757
- * }
758
- * onJoinedRoom = () => {
759
- * console.log("I joined a networked room")
760
- * }
761
- * }
744
+ * // Both patterns work:
745
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom); // original callback
746
+ * this.context.connection.stopListen("joined-room", unsub); // unsubscribe fn from beginListen
762
747
  * ```
763
748
  */
749
+ private static _didLogStopListenHint = false;
764
750
  public stopListen<K extends NetworkEventKey>(
765
751
  key: K,
766
- callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | null,
752
+ callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | Function | null,
767
753
  ): void
768
754
  public stopListen(key: string, callback: Function | null) {
769
755
  if (!callback) return;
770
756
  if (!this._listeners[key]) return;
771
- const index = this._listeners[key].indexOf(callback);
757
+ // Backwards compat: if an unsubscribe function returned by beginListen was passed to stopListen,
758
+ // resolve it to the original callback via symbol so the listener can be found and removed.
759
+ const original = (callback as any)[$originalCallback] ?? callback;
760
+ if (original !== callback && isDevEnvironment() && !NetworkConnection._didLogStopListenHint) {
761
+ NetworkConnection._didLogStopListenHint = true;
762
+ console.warn("[Needle Engine] Tip: beginListen() returns an unsubscribe function — you can call it directly instead of using stopListen().");
763
+ }
764
+ const index = this._listeners[key].indexOf(original);
772
765
  if (index >= 0) {
773
766
  this._listeners[key].splice(index, 1);
774
767
  }
775
768
  }
776
769
 
777
- /** Use to start listening to networking binary events */
778
- public beginListenBinary(identifier: string, callback: BinaryCallback): BinaryCallback {
770
+ /** Use to start listening to networking binary events.
771
+ * Returns an unsubscribe function that removes the listener when called.
772
+ * The returned function can also be passed to {@link stopListenBinary} or {@link Component.autoCleanup}.
773
+ */
774
+ public beginListenBinary(identifier: string, callback: BinaryCallback): DisposeFn {
779
775
  if (!this._listenersBinary[identifier])
780
776
  this._listenersBinary[identifier] = [];
781
777
  this._listenersBinary[identifier].push(callback);
782
- return callback;
778
+ const unsub = () => this.stopListenBinary(identifier, callback);
779
+ (unsub as any)[$originalCallback] = callback;
780
+ return unsub;
783
781
  }
784
782
 
785
- /** Use to stop listening to networking binary events */
783
+ /** Use to stop listening to networking binary events.
784
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListenBinary}.
785
+ */
786
786
  public stopListenBinary(identifier: string, callback: any) {
787
787
  if (!this._listenersBinary[identifier]) return;
788
- const index = this._listenersBinary[identifier].indexOf(callback);
788
+ // Backwards compat: resolve unsubscribe function to original callback via symbol
789
+ const original = callback?.[$originalCallback] ?? callback;
790
+ const index = this._listenersBinary[identifier].indexOf(original);
789
791
  if (index >= 0) {
790
792
  this._listenersBinary[identifier].splice(index, 1);
791
793
  }
@@ -802,14 +804,18 @@ export class NetworkConnection implements INetworkConnection {
802
804
 
803
805
  /** Used to connect to the networking server
804
806
  * @param url Optional url to connect to. If not provided, it will use the url from the registered `INetworkingWebsocketUrlProvider` or the default backend networking url. If you want to change the url after connecting, you need to disconnect first and then connect again with the new url.
807
+ * @param transport Optional custom transport to use instead of the default websocket. Useful for testing or alternative transports.
805
808
  */
806
- public async connect(url?: string) {
809
+ public async connect(url?: string, transport?: INetworkTransport) {
807
810
  if (this.connected && url && url !== networkingServerUrl) {
808
811
  return Promise.reject("Can not connect to different server url. Please disconnect first.");
809
812
  }
810
813
  if (this.connected) {
811
814
  return Promise.resolve(true);
812
815
  }
816
+ if (transport) {
817
+ return this.connectTransport(transport);
818
+ }
813
819
  if (url)
814
820
  console.debug("Connecting to user provided url " + url);
815
821
  const overrideUrl = url || this.netWebSocketUrlProvider?.getWebsocketUrl();
@@ -824,11 +830,20 @@ export class NetworkConnection implements INetworkConnection {
824
830
 
825
831
  /** Disconnect from the networking backend + reset internal state */
826
832
  public disconnect() {
827
- this._ws?.close();
828
- this._ws = undefined;
833
+ if (this._transport) {
834
+ // Clear callbacks before closing so the onClose handler doesn't fire
835
+ this._transport.onOpen = null;
836
+ this._transport.onClose = null;
837
+ this._transport.onError = null;
838
+ this._transport.onMessage = null;
839
+ this._transport.close();
840
+ this._transport = undefined;
841
+ }
829
842
  networkingServerUrl = undefined;
830
843
 
831
- // Reset all state
844
+ // Reset all state synchronously so callers can rely on isConnected/isInRoom immediately
845
+ this.connected = false;
846
+ this._connectionId = null;
832
847
  this._currentRoomAllowEditing = true;
833
848
  this._currentRoomName = null;
834
849
  this._currentRoomViewId = null;
@@ -836,16 +851,26 @@ export class NetworkConnection implements INetworkConnection {
836
851
  this._currentInRoom.length = 0;
837
852
  this._state = {};
838
853
  this._currentDelay = -1;
854
+ this._connectingToWebsocketPromise = null;
855
+ }
856
+
857
+ /** Full teardown: disconnect, clear all listeners, and release all resources.
858
+ * Called when the owning Context is destroyed. After dispose(), this instance should not be reused. */
859
+ public dispose() {
860
+ this.disconnect();
861
+ this._listeners = {};
862
+ this._listenersBinary = {};
863
+ this._waitingForSocket = {};
864
+ this._peer = null;
839
865
  }
840
866
 
841
867
  private _listeners: { [key: string]: Function[] } = {};
842
868
  private _listenersBinary: { [key: string]: BinaryCallback[] } = {};
843
869
  private connected: boolean = false;
844
- private channelId: string | undefined;
845
870
  private _connectionId: string | null = null;
846
871
 
847
- // Websocket ------------------------------------------------------------
848
- private _ws: Websocket | undefined;
872
+ // Transport ------------------------------------------------------------
873
+ private _transport: INetworkTransport | undefined;
849
874
  private _waitingForSocket: { [key: string]: Array<Function> } = {};
850
875
  private _isInRoom: boolean = false;
851
876
  private _currentRoomName: string | null = null;
@@ -857,71 +882,70 @@ export class NetworkConnection implements INetworkConnection {
857
882
 
858
883
  private _connectingToWebsocketPromise: Promise<boolean> | null = null;
859
884
 
860
- private connectWebsocket() {
885
+ /** Wire up a transport's event callbacks and start it */
886
+ private connectTransport(transport: INetworkTransport): Promise<boolean> {
861
887
  if (this._connectingToWebsocketPromise) return this._connectingToWebsocketPromise;
862
- return this._connectingToWebsocketPromise = new Promise(async (res, _) => {
888
+ return this._connectingToWebsocketPromise = new Promise((res) => {
863
889
  let didResolve = false;
864
890
  const resolve = (val: boolean) => {
865
891
  if (didResolve) return;
866
892
  didResolve = true;
867
893
  res(val);
868
- }
869
- if (networkingServerUrl === undefined) {
870
- console.log("Fetch default backend url: " + defaultNetworkingBackendUrlProvider);
871
- const failed = false;
872
- const defaultUrlResponse = await fetch(defaultNetworkingBackendUrlProvider);
873
- networkingServerUrl = await defaultUrlResponse.text();
874
- if (failed) return;
875
- }
876
-
877
- if (networkingServerUrl === undefined) {
894
+ };
895
+
896
+ transport.onOpen = () => {
897
+ this._connectingToWebsocketPromise = null;
898
+ this._transport = transport;
899
+ this.connected = true;
900
+ if (isDevEnvironment() || debugNet) console.log("Connected to networking backend\n" + (transport.url ?? ""));
901
+ else console.debug("Connected to networking backend", transport.url ?? "");
902
+ resolve(true);
903
+ this.onSendQueued(SendQueue.OnConnection);
904
+ };
905
+ transport.onClose = () => {
906
+ this._connectingToWebsocketPromise = null;
907
+ this.connected = false;
908
+ this._isInRoom = false;
878
909
  resolve(false);
879
- return;
880
- }
881
-
882
- console.debug("Connecting to networking backend on\n" + networkingServerUrl)
883
- const pkg = await import('websocket-ts');
884
- // @ts-ignore
885
- const WebsocketBuilder = pkg.default?.WebsocketBuilder ?? pkg.WebsocketBuilder;
886
- const ExponentialBackoff = pkg.default?.ExponentialBackoff ?? pkg.ExponentialBackoff;
887
- const ws = new WebsocketBuilder(networkingServerUrl)
888
- .withMaxRetries(10)
889
- .withBackoff(new ExponentialBackoff(2000, 4))
890
- .onOpen(() => {
891
- this._connectingToWebsocketPromise = null;
892
- this._ws = ws;
893
- this.connected = true;
894
- if (isDevEnvironment() || debugNet) console.log("Connected to networking backend\n" + networkingServerUrl);
895
- else console.debug("Connected to networking backend", networkingServerUrl);
896
- resolve(true);
897
- this.onSendQueued(SendQueue.OnConnection);
898
- })
899
- .onClose((_evt) => {
900
- this._connectingToWebsocketPromise = null;
901
- this.connected = false;
902
- this._isInRoom = false;
903
- resolve(false);
904
- let msg = "Websocket connection closed...";
905
- if (!networkingServerUrl?.includes("/socket")) msg += ` Do you perhaps mean to connect to \"/socket\"?`;
906
- console.error(msg);
907
- })
908
- .onError((_e) => {
909
- console.error("Websocket connection failed...");
910
- resolve(false);
911
- Telemetry.sendEvent(this.context, "networking", {
912
- event: "connection_error",
913
- });
914
- })
915
- .onRetry(() => { console.log("Retry connecting to networking websocket") })
916
- .build();
917
- ws.addEventListener(pkg.WebsocketEvent.message, (socket, msg) => {
918
- this.onMessage(socket, msg);
919
- });
910
+ let msg = "Websocket connection closed...";
911
+ if (!transport.url?.includes("/socket")) msg += ` Do you perhaps mean to connect to "/socket"?`;
912
+ console.error(msg);
913
+ };
914
+ transport.onError = () => {
915
+ console.error("Websocket connection failed...");
916
+ resolve(false);
917
+ Telemetry.sendEvent(this.context, "networking", {
918
+ event: "connection_error",
919
+ });
920
+ };
921
+ transport.onMessage = (data) => {
922
+ this.onMessage(data);
923
+ };
924
+
925
+ transport.start();
920
926
  });
921
927
  }
922
928
 
923
- private onMessage(_, ev) {
924
- const msg = ev.data;
929
+ private async connectWebsocket() {
930
+ if (this._connectingToWebsocketPromise) return this._connectingToWebsocketPromise;
931
+
932
+ if (networkingServerUrl === undefined) {
933
+ console.log("Fetch default backend url: " + defaultNetworkingBackendUrlProvider);
934
+ const defaultUrlResponse = await fetch(defaultNetworkingBackendUrlProvider);
935
+ networkingServerUrl = await defaultUrlResponse.text();
936
+ }
937
+
938
+ if (networkingServerUrl === undefined) {
939
+ return false;
940
+ }
941
+
942
+ console.debug("Connecting to networking backend on\n" + networkingServerUrl);
943
+ const transport = new WebsocketTransport(networkingServerUrl);
944
+ return this.connectTransport(transport);
945
+ }
946
+
947
+ private onMessage(data: string | Blob) {
948
+ const msg = data;
925
949
  try {
926
950
  if (typeof msg !== "string") {
927
951
  if (msg.size) {
@@ -1098,9 +1122,9 @@ export class NetworkConnection implements INetworkConnection {
1098
1122
  };
1099
1123
  }
1100
1124
 
1101
- private sendWithWebsocket(key: string, data: IModel | object | boolean | string | number, queue: SendQueue = SendQueue.OnRoomJoin) {
1125
+ private sendWithWebsocket(key: string, data: WebsocketSendType | undefined, queue: SendQueue = SendQueue.OnRoomJoin) {
1102
1126
  // console.log(key);
1103
- if (!this._ws) {
1127
+ if (!this._transport) {
1104
1128
  const arr = this._waitingForSocket[queue] || [];
1105
1129
  arr.push(() => this.sendWithWebsocket(key, data, queue));
1106
1130
  this._waitingForSocket[queue] = arr;
@@ -1109,7 +1133,7 @@ export class NetworkConnection implements INetworkConnection {
1109
1133
  }
1110
1134
  const str = JSON.stringify(this.toMessage(key, data));
1111
1135
  if (debugNet) console.log(">>", key);
1112
- this._ws.send(str);
1136
+ this._transport.send(str);
1113
1137
  }
1114
1138
 
1115
1139
  private onSendQueued(queue: SendQueue) {
@@ -1,9 +1,9 @@
1
1
  import { FileLoader } from "three";
2
2
 
3
3
  import { showBalloonWarning } from "./debug/index.js";
4
- import { hasCommercialLicense } from "./engine_license.js";
4
+ import { _BGpnKD } from "./engine_license.js";
5
5
  import { delay } from "./engine_utils.js";
6
- import { md5Hex, md5AsBytes, sha256Base64 } from "./engine_utils_hash.js";
6
+ import { md5AsBytes, md5Hex, sha256Base64 } from "./engine_utils_hash.js";
7
7
 
8
8
 
9
9
  export namespace BlobStorage {
@@ -45,7 +45,7 @@ export namespace BlobStorage {
45
45
  */
46
46
  export function canUpload(info: { filesize: number }) {
47
47
  const sizeInMB = info.filesize / 1024 / 1024;
48
- if (hasCommercialLicense()) {
48
+ if (_BGpnKD()) {
49
49
  return sizeInMB < maxSizeInMB;
50
50
  }
51
51
  return sizeInMB < maxFreeSizeInMB;
@@ -100,7 +100,7 @@ export namespace BlobStorage {
100
100
  console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max allowed size is ${maxSizeInMB}MB`);
101
101
  return null;
102
102
  }
103
- else if (!hasCommercialLicense() && filesizeInMB > maxFreeSizeInMB) {
103
+ else if (!_BGpnKD() && filesizeInMB > maxFreeSizeInMB) {
104
104
  if (opts?.silent !== true) showBalloonWarning(`File is too large for uploading. Please get a <a href=\"https://needle.tools/pricing\" target=\"_blank\">commercial license</a> to upload files larger than 5MB`);
105
105
  console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max size is 5MB for non-commercial users. Please get a commercial license at https://needle.tools/pricing for larger files (up to 50MB)`);
106
106
  return null;
@@ -498,8 +498,8 @@ export function beginListenInstantiate(context: Context) {
498
498
  });
499
499
 
500
500
  return () => {
501
- context.connection.stopListen(InstantiateEvent.NewInstanceCreated, cb1);
502
- context.connection.stopListen("left-room", cb2);
501
+ cb1();
502
+ cb2();
503
503
  }
504
504
  }
505
505
 
@@ -11,15 +11,55 @@ export declare interface IModel {
11
11
  }
12
12
 
13
13
 
14
+ /** Controls when a network message is actually sent to the server.
15
+ * @see {@link NetworkConnection.send}
16
+ */
14
17
  export enum SendQueue {
18
+ /** Hold the message until the transport connection opens, then send it. Use for messages that must arrive as soon as the socket is ready (e.g. join-room). */
15
19
  OnConnection,
20
+ /** Hold the message until the client has joined a room, then send it. Use for messages that require room context. */
16
21
  OnRoomJoin,
22
+ /** Buffer the message and send it on the next `sendBufferedMessagesNow()` call (typically once per frame). This is the default for `send()`. */
17
23
  Queued,
24
+ /** Send the message to the server immediately without buffering. */
18
25
  Immediate,
19
26
  }
20
27
 
21
28
  export declare interface INetworkConnection {
22
29
  get isConnected(): boolean;
23
30
  get isInRoom(): boolean;
24
- send(key: string, data: IModel | object | boolean | null | string | number, queue: SendQueue): unknown;
31
+ send(key: string, data?: IModel | object | boolean | string | number, queue?: SendQueue): unknown;
32
+ }
33
+
34
+ /**
35
+ * @experimental
36
+ * Interface for a network transport layer used by {@link NetworkConnection}.
37
+ * The default implementation wraps a websocket via `websocket-ts`.
38
+ * Custom implementations can be injected into {@link NetworkConnection.connect}
39
+ * for testing or alternative transports.
40
+ *
41
+ * **Lifecycle:** After creating a transport and passing it to `connect()`,
42
+ * `NetworkConnection` sets the four event callbacks (`onOpen`, `onClose`,
43
+ * `onError`, `onMessage`) and then calls {@link start}. The transport
44
+ * should call `onOpen` when the connection is ready.
45
+ */
46
+ export interface INetworkTransport {
47
+ /** Start the connection. Called by NetworkConnection after event callbacks are set.
48
+ * May return a promise if setup is async (e.g. dynamic imports). */
49
+ start(): void | Promise<void>;
50
+ /** Send data (string for JSON messages, Uint8Array for binary) */
51
+ send(data: string | Uint8Array): void;
52
+ /** Close the transport */
53
+ close(): void | Promise<void>;
54
+ /** The URL this transport is connected to, if applicable */
55
+ readonly url: string | undefined;
56
+
57
+ /** Called when the transport connection opens */
58
+ onOpen: (() => void) | null;
59
+ /** Called when the transport connection closes */
60
+ onClose: (() => void) | null;
61
+ /** Called when an error occurs */
62
+ onError: ((err: any) => void) | null;
63
+ /** Called when a message is received. Data is either a string (JSON) or Blob (binary). */
64
+ onMessage: ((data: string | Blob) => void) | null;
25
65
  }