lecodes-sdk 1.2.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.md +5 -2
  2. package/dist/editor.d.ts +12 -0
  3. package/dist/global.d.ts +4 -8
  4. package/dist/host.d.ts +2 -3
  5. package/dist/types/animate/tween/Animation.d.ts +0 -3
  6. package/dist/types/animate/tween/animateValue.d.ts +4 -2
  7. package/dist/types/animate/tween/easing.d.ts +8 -0
  8. package/dist/types/animate/tween/spec.d.ts +15 -7
  9. package/dist/types/audio/audio.d.ts +2 -1
  10. package/dist/types/canvas/Canvas.d.ts +40 -108
  11. package/dist/types/canvas/gen/cssColor.d.ts +17 -0
  12. package/dist/types/canvas/gen/recorder.d.ts +118 -0
  13. package/dist/types/canvas/gen/spec.d.ts +144 -0
  14. package/dist/types/core/color.d.ts +3 -1
  15. package/dist/types/core/pins.d.ts +18 -0
  16. package/dist/types/g2/Node2D.d.ts +5 -8
  17. package/dist/types/g2/Scene2D.d.ts +6 -2
  18. package/dist/types/gl/Foliage.d.ts +30 -7
  19. package/dist/types/gl/Light.d.ts +8 -0
  20. package/dist/types/gl/Lightmap.d.ts +13 -2
  21. package/dist/types/gl/Material.d.ts +17 -3
  22. package/dist/types/gl/Model.d.ts +6 -2
  23. package/dist/types/gl/Node.d.ts +3 -6
  24. package/dist/types/gl/Scene.d.ts +41 -13
  25. package/dist/types/gl/Texture.d.ts +1 -1
  26. package/dist/types/gl/animation/Locomotion.d.ts +8 -1
  27. package/dist/types/inject.d.ts +11 -11
  28. package/dist/types/inject.editor.d.ts +1 -0
  29. package/dist/types/net/core.d.ts +7 -0
  30. package/dist/types/plugin.d.ts +76 -0
  31. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  32. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  33. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  34. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  35. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  36. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  37. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  38. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  39. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  40. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  41. package/dist/types/runtime/app.d.ts +9 -2
  42. package/dist/types/runtime/fetch.d.ts +2 -0
  43. package/dist/types/runtime/input.d.ts +1 -1
  44. package/dist/types/runtime/media.d.ts +6 -10
  45. package/dist/types/runtime/misc.d.ts +4 -1
  46. package/dist/types/runtime/net.d.ts +3 -2
  47. package/dist/types/runtime/touch.d.ts +32 -0
  48. package/dist/types/scene/defineScene.d.ts +43 -2
  49. package/dist/types/scene/editor.d.ts +52 -0
  50. package/dist/types/scene/gizmos.d.ts +7 -4
  51. package/dist/types/ui/NativeView.d.ts +6 -4
  52. package/dist/types/ui/UI.d.ts +1 -1
  53. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  54. package/dist/types/ui/UIButton.d.ts +12 -14
  55. package/dist/types/ui/UIContainer.d.ts +0 -6
  56. package/dist/types/ui/UIImage.d.ts +1 -4
  57. package/dist/types/ui/UIInput.d.ts +8 -24
  58. package/dist/types/ui/UIModal.d.ts +0 -2
  59. package/dist/types/ui/UINode.d.ts +70 -56
  60. package/dist/types/ui/UIPager.d.ts +28 -27
  61. package/dist/types/ui/UIPopover.d.ts +0 -2
  62. package/dist/types/ui/UIScreen.d.ts +12 -17
  63. package/dist/types/ui/UIScrollable.d.ts +1 -4
  64. package/dist/types/ui/UIText.d.ts +0 -2
  65. package/dist/types/ui/UIVideo.d.ts +3 -5
  66. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  67. package/dist/types/ui/UIWidget.d.ts +10 -9
  68. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  69. package/dist/types/ui/presentable.d.ts +46 -32
  70. package/dist/types/ui/router.d.ts +18 -7
  71. package/dist/types/ui/styleColor.d.ts +1 -0
  72. package/dist/types/ui/transitions.d.ts +18 -0
  73. package/dist/types/ui/tree.d.ts +75 -0
  74. package/dist/types/version.d.ts +10 -0
  75. package/dist/types.json +1 -1
  76. package/package.json +12 -3
  77. package/prompts/2d.md +2 -6
  78. package/prompts/3d.md +1 -5
  79. package/prompts/README.md +1 -1
  80. package/prompts/canvas.md +9 -8
  81. package/prompts/compose.ts +1 -1
  82. package/prompts/core.md +3 -3
  83. package/prompts/design.md +1 -1
  84. package/prompts/dist/2d-game.md +473 -239
  85. package/prompts/dist/3d-app.md +553 -205
  86. package/prompts/dist/ar-app.md +435 -202
  87. package/prompts/dist/design.md +113 -95
  88. package/prompts/dist/ui-app.md +386 -170
  89. package/prompts/ui-design.md +2 -3
  90. package/prompts/ui.md +45 -37
  91. package/src/animate/tween/Animation.ts +34 -150
  92. package/src/animate/tween/Timeline.ts +175 -175
  93. package/src/animate/tween/animateValue.ts +6 -3
  94. package/src/animate/tween/easing.ts +10 -3
  95. package/src/animate/tween/spec.ts +41 -15
  96. package/src/audio/Sound.ts +3 -3
  97. package/src/audio/audio.ts +2 -1
  98. package/src/bridges/2d.d.ts +317 -0
  99. package/src/bridges/app.d.ts +91 -0
  100. package/src/bridges/audio.d.ts +97 -0
  101. package/src/bridges/canvas.d.ts +79 -0
  102. package/src/bridges/device.d.ts +72 -0
  103. package/src/bridges/fetch.d.ts +80 -0
  104. package/src/bridges/files.d.ts +70 -0
  105. package/src/bridges/gl.d.ts +1133 -0
  106. package/src/bridges/input.d.ts +72 -0
  107. package/src/bridges/media.d.ts +55 -0
  108. package/src/bridges/nav.d.ts +71 -0
  109. package/src/bridges/net.d.ts +52 -0
  110. package/src/bridges/service.d.ts +52 -0
  111. package/src/bridges/socket.d.ts +31 -0
  112. package/src/bridges/storage.d.ts +33 -0
  113. package/src/bridges/tree.d.ts +301 -0
  114. package/src/bridges/types.d.ts +49 -0
  115. package/src/canvas/Canvas.ts +114 -159
  116. package/src/canvas/gen/cssColor.ts +224 -0
  117. package/src/canvas/gen/recorder.ts +212 -0
  118. package/src/canvas/gen/spec.ts +201 -0
  119. package/src/chisel.ts +193 -0
  120. package/src/compile/assetMacro.ts +1 -1
  121. package/src/compile/bundler.ts +11 -2
  122. package/src/compile/compileProject.ts +43 -4
  123. package/src/compile/fontMacro.ts +3 -4
  124. package/src/compile/header.ts +26 -5
  125. package/src/compile/index.ts +3 -1
  126. package/src/compile/liteMaterial.ts +1 -1
  127. package/src/compile/sceneEditor.ts +11 -26
  128. package/src/core/color.ts +73 -30
  129. package/src/core/pins.ts +51 -0
  130. package/src/core/signals.ts +8 -1
  131. package/src/g2/CharacterController2D.ts +3 -3
  132. package/src/g2/Node2D.ts +57 -39
  133. package/src/g2/Physics2D.ts +2 -2
  134. package/src/g2/Scene2D.ts +35 -23
  135. package/src/g2/Texture2D.ts +1 -1
  136. package/src/g2/loop.ts +4 -4
  137. package/src/gl/CameraPlace.ts +52 -52
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +10 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Mesh.ts +120 -120
  144. package/src/gl/Model.ts +21 -16
  145. package/src/gl/Node.ts +91 -24
  146. package/src/gl/Particles.ts +1 -1
  147. package/src/gl/Scene.ts +100 -44
  148. package/src/gl/Texture.ts +8 -7
  149. package/src/gl/animation/AnimationClip.ts +1 -1
  150. package/src/gl/animation/DynamicBone.ts +482 -482
  151. package/src/gl/animation/Locomotion.ts +8 -3
  152. package/src/gl/nav/NavMesh.ts +3 -4
  153. package/src/gl/physics/Physics.ts +2 -2
  154. package/src/gl/physics/physicsEvents.ts +3 -3
  155. package/src/gl/scenarios.ts +291 -291
  156. package/src/gl/terrain/Terrain.ts +4 -5
  157. package/src/gl/touch.ts +14 -15
  158. package/src/host.d.ts +2 -3
  159. package/src/inject.editor.ts +7 -0
  160. package/src/inject.ts +13 -16
  161. package/src/net/core.ts +6 -5
  162. package/src/net/index.ts +1 -1
  163. package/src/net/replication.ts +1 -1
  164. package/src/plugin.ts +191 -0
  165. package/src/plugins/gen/camera/contract.d.ts +27 -0
  166. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  167. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  168. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  169. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  170. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  171. package/src/plugins/gen/map/contract.d.ts +144 -0
  172. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  173. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  174. package/src/plugins/gen/push/contract.d.ts +61 -0
  175. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  176. package/src/plugins/gen/push/sdk/push.ts +105 -0
  177. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  178. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  179. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  180. package/src/plugins/permission.ts +5 -4
  181. package/src/runtime/app.ts +20 -9
  182. package/src/runtime/appEvents.ts +5 -4
  183. package/src/runtime/channel.ts +18 -15
  184. package/src/runtime/clipboard.ts +4 -3
  185. package/src/runtime/datetime.ts +2 -1
  186. package/src/runtime/device.ts +17 -15
  187. package/src/runtime/fetch.ts +30 -20
  188. package/src/runtime/files.ts +16 -15
  189. package/src/runtime/input.ts +12 -10
  190. package/src/runtime/media.ts +50 -46
  191. package/src/runtime/misc.ts +7 -3
  192. package/src/runtime/net.ts +8 -7
  193. package/src/runtime/rpc.ts +1 -3
  194. package/src/runtime/service.ts +19 -14
  195. package/src/runtime/share.ts +4 -3
  196. package/src/runtime/storage.ts +6 -4
  197. package/src/runtime/touch.ts +32 -0
  198. package/src/scene/defineScene.ts +61 -365
  199. package/src/scene/editor.ts +408 -0
  200. package/src/scene/editorPlugins.ts +3 -3
  201. package/src/scene/gizmos.ts +15 -9
  202. package/src/server/db/marci/query.ts +1 -1
  203. package/src/server/host.ts +1 -1
  204. package/src/server/runtime.ts +1 -1
  205. package/src/ui/NativeView.ts +72 -25
  206. package/src/ui/UI.ts +3 -3
  207. package/src/ui/UIBottomSheet.ts +16 -17
  208. package/src/ui/UIButton.ts +54 -16
  209. package/src/ui/UIContainer.ts +0 -6
  210. package/src/ui/UIImage.ts +34 -30
  211. package/src/ui/UIInput.ts +29 -37
  212. package/src/ui/UIModal.ts +1 -3
  213. package/src/ui/UINode.ts +347 -297
  214. package/src/ui/UIPager.ts +93 -78
  215. package/src/ui/UIPopover.ts +0 -2
  216. package/src/ui/UIScreen.ts +41 -36
  217. package/src/ui/UIScrollable.ts +19 -13
  218. package/src/ui/UISpacer.ts +1 -1
  219. package/src/ui/UITabs.ts +8 -6
  220. package/src/ui/UIText.ts +7 -17
  221. package/src/ui/UIVideo.ts +30 -27
  222. package/src/ui/UIVirtualizedList.ts +58 -59
  223. package/src/ui/UIWidget.ts +38 -18
  224. package/src/ui/colorKeys.gen.ts +37 -0
  225. package/src/ui/fonts.ts +2 -2
  226. package/src/ui/presentable.ts +59 -42
  227. package/src/ui/router.ts +52 -35
  228. package/src/ui/styleColor.ts +56 -0
  229. package/src/ui/theme.ts +7 -6
  230. package/src/ui/transitions.ts +249 -0
  231. package/src/ui/tree.ts +346 -0
  232. package/src/version.ts +24 -0
  233. package/tests/helpers/engineWorld.ts +11 -0
  234. package/tests/helpers/fakeTree.ts +353 -0
  235. package/tests/helpers/hostStubs.ts +31 -0
  236. package/tests/helpers/index.ts +14 -0
  237. package/tests/helpers/memoryMarci.ts +124 -0
  238. package/tests/helpers/phases.ts +23 -0
  239. package/tests/helpers/preload.ts +18 -0
  240. package/tests/helpers/stubApp.ts +2 -0
  241. package/tests/helpers/stubDevice.ts +2 -0
  242. package/tests/helpers/stubFetch.ts +2 -0
  243. package/tests/helpers/stubInput.ts +2 -0
  244. package/dist/inject.js +0 -4629
  245. package/dist/types/core/registry.d.ts +0 -7
  246. package/dist/types/plugins/camera.d.ts +0 -25
  247. package/dist/types/plugins/push.d.ts +0 -46
  248. package/src/bridges.d.ts +0 -1769
  249. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  250. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  251. package/src/compile/__tests__/assetName.test.ts +0 -55
  252. package/src/compile/__tests__/compile.test.ts +0 -310
  253. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  254. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  255. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  256. package/src/core/__tests__/stateMachine.test.ts +0 -132
  257. package/src/core/registry.ts +0 -23
  258. package/src/plugins/camera.ts +0 -81
  259. package/src/plugins/push.ts +0 -132
  260. package/src/plugins/qr.ts +0 -73
package/src/gl/touch.ts CHANGED
@@ -2,17 +2,18 @@
2
2
  // the struck Node and to the active scene's listeners. Ported from worker/src/TouchEventSystem.ts.
3
3
 
4
4
  import { ClickEvent, TouchStartEvent, type TrackHandler } from "../runtime/touch"
5
- import { nodeRegistry } from "./Node"
5
+ import { ensureEmitter, setSceneTouchHandlers } from "../ui/tree"
6
+ import { nodes } from "./Node"
6
7
  import { glState } from "./state"
7
8
 
8
9
  // Hits inside a loaded model (GLB) land on sub-entities the user never created a Node for — walk up
9
10
  // the transform hierarchy to the nearest entity someone actually holds (the model's Node).
10
11
  const resolveHitNode = (entityId: number) => {
11
- let node = nodeRegistry.get(entityId)
12
+ let node = nodes.get(entityId)
12
13
  for (let id = entityId, i = 0; !node && i < 32; i++) {
13
14
  id = _creator.getParent(id)
14
15
  if (id === 0) break
15
- node = nodeRegistry.get(id)
16
+ node = nodes.get(id)
16
17
  }
17
18
  return node ?? null
18
19
  }
@@ -53,16 +54,14 @@ const onTouchStart = (pointerId: number, clientX: number, clientY: number, entit
53
54
  return null
54
55
  }
55
56
 
56
- let touchStartRegistered = false
57
- export const registerTouchStartEvent = (): void => {
58
- if (touchStartRegistered) return
59
- touchStartRegistered = true
60
- _creatorUI.registerTouchStartEvent(onTouchStart)
61
- }
62
-
63
- let touchEndRegistered = false
64
- export const registerTouchEndEvent = (): void => {
65
- if (touchEndRegistered) return
66
- touchEndRegistered = true
67
- _creatorUI.registerTouchEndEvent(onTouchEnd)
57
+ // The scene-level pointer dispatch rides the tree emitter (TREE_EVENT_SCENE_TOUCH_START / SCENE_CLICK):
58
+ // installed once, on the first listener.
59
+ let registered = false
60
+ const register = (): void => {
61
+ if (registered) return
62
+ registered = true
63
+ ensureEmitter()
64
+ setSceneTouchHandlers(onTouchStart, onTouchEnd)
68
65
  }
66
+ export const registerTouchStartEvent = register
67
+ export const registerTouchEndEvent = register
package/src/host.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  // JS-runtime globals provided by the host engine (web viewer / desktop / iOS), NOT by the SDK.
2
- // Declared only so SDK and user code type-check — never bundled or injected. Mirrors
3
- // packages/projects/src/global.d.ts.
2
+ // Declared only so SDK and user code type-check — never bundled or injected.
4
3
 
5
4
  declare global {
6
5
  function setTimeout(handler: (...args: any[]) => void, timeout?: number): number
@@ -57,7 +56,7 @@ declare global {
57
56
  * `Style<typeof myButton>`) is what you'd pass to `el.style(...)` — for typing reusable style
58
57
  * presets. Deliberately no `Partial<>` wrapper: every style prop is already optional, and
59
58
  * Partial over ClassStyles' `$`-index signature adds `| undefined`, breaking assignability back
60
- * into `.style(...)`. Round-trip guarded by sdk/tests/ui-types.test.ts. */
59
+ * into `.style(...)`. Round-trip guarded by sdk/tests/ui/types.test.ts. */
61
60
  type Style<T> = T extends { style: (s: infer S) => any } ? S : never
62
61
  }
63
62
 
@@ -0,0 +1,7 @@
1
+ // The SECOND inject module of an editor bundle (a compile with `editor: true` — bundler.ts adds it
2
+ // after inject.ts): what a scene editor's in-world half may name and an app may not. A shipped
3
+ // bundle is compiled without it, so nothing here can reach one.
4
+ //
5
+ // Same rule as inject.ts: the only file that exports these names.
6
+
7
+ export { sceneEditor as __sceneEditor, type SceneEditorApi } from "./scene/editor"
package/src/inject.ts CHANGED
@@ -52,7 +52,7 @@ export { Bus, REVERB_PRESETS, type ReverbPreset, type ReverbParams, type EchoPar
52
52
  export { audio, type PlaySoundOptions, type SpatialOptions, type Rolloff, type AudioStats } from "./audio/audio"
53
53
  export { openFilePicker, files, type FileData } from "./runtime/files"
54
54
  export { share } from "./runtime/share"
55
- export { ClickEvent, TouchStartEvent, LongPressEvent } from "./runtime/touch"
55
+ export { ClickEvent, TouchStartEvent, LongPressEvent, MouseEnterEvent } from "./runtime/touch"
56
56
 
57
57
  // ---- 2D engine (creator-2d) ----
58
58
  export { Scene2D } from "./g2/Scene2D"
@@ -114,7 +114,7 @@ export {
114
114
  // The Presentable navigation contract (docs/navigation-presentable-plan.md). `Presentable` is
115
115
  // interface + runtime companion (declaration merging): `Presentable.current` = what's on screen.
116
116
  export { Presentable } from "./ui/UI"
117
- export type { PresentOptions, Transition, TransitionName, TransitionSpec, TransitionTransform } from "./ui/UI"
117
+ export type { DismissOptions, PresentOptions, Transition, TransitionName, TransitionPose, TransitionSpec } from "./ui/UI"
118
118
  // The base node type + the union a container accepts as children — documented as the signature of
119
119
  // UIColumn/UIRow/append/etc., so they must be global (type-only; erased at runtime).
120
120
  export type { UINode, UINodeChild } from "./ui/UINode"
@@ -135,26 +135,23 @@ export { type TweenMeta } from "./animate/tween/spec"
135
135
  export { type EasingInput } from "./animate/tween/easing"
136
136
  export { easeIn, easeOut, easeInOut } from "./animate/easings"
137
137
 
138
- // ---- plugins (optional, host-provided capabilities — typed NativeView/service wrappers, see src/plugins/) ----
139
- export { QRScanner } from "./plugins/qr"
140
- export { CameraView, type CameraFacing } from "./plugins/camera"
141
- export { Geolocation, type GeoPosition, type GeoOptions, type GeoWatch } from "./plugins/geolocation"
138
+ // ---- plugins (optional, host-provided capabilities — typed NativeView/service wrappers). The first-party
139
+ // ones are VENDORED from the plugin repo (src/plugins/gen, `bun run vendor:plugins`); oauth / service are the SDK's. ----
140
+ export { QRScanner } from "./plugins/gen/qr-scanner/sdk/qr-scanner"
141
+ export { CameraView, type CameraFacing } from "./plugins/gen/camera/sdk/camera"
142
+ export { Geolocation, type GeoPosition, type GeoOptions, type GeoWatch } from "./plugins/gen/geolocation/sdk/geolocation"
142
143
  export {
143
144
  MapView, type MapOptions, type MapStyle, type MapStyleName, type MapCamera, type MapTap, type LngLat, type CameraMove, type FitOptions,
144
145
  type MapPaddingValues, type MarkerItem, type MarkerTap, type MarkerLayer, type MarkerLayerOptions,
145
146
  type LineLayer, type LineLayerOptions, type UserLocationOptions,
146
- } from "./plugins/map"
147
- export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/push"
147
+ } from "./plugins/gen/map/sdk/map"
148
+ export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/gen/push/sdk/push"
148
149
  export { OAuth, type OAuthCredential, type OAuthProviderName } from "./plugins/oauth"
149
150
  export { Service } from "./plugins/service"
150
151
 
151
152
  // ---- scenes as data (.scene.ts files — see src/scene/defineScene.ts) ----
152
- export { defineScene, use, ref, make, SceneHandle, applyEditorPose as __applyEditorPose } from "./scene/defineScene"
153
- export {
154
- defineMaterial, MaterialHandle,
155
- // editor-harness helpers (the scene editor's live material paths — not app API)
156
- assignMaterialDef as __assignMaterialDef, applyMaterialParam as __applyMaterialParam,
157
- } from "./scene/material"
153
+ export { defineScene, use, ref, make, SceneHandle } from "./scene/defineScene"
154
+ export { defineMaterial, MaterialHandle } from "./scene/material"
158
155
  export type { MaterialData, MaterialParamValue, ShaderMaterialDef, LitMaterialDef, UnlitMaterialDef } from "./scene/material"
159
156
  export type { SceneDef, SceneNodeDef, MeshDef, LightDef, MaterialDef, CameraNodeDef, AspectEntry, MakeEntry, LoadedScene, SceneInstance } from "./scene/defineScene"
160
157
  export { CameraPlace } from "./gl/CameraPlace"
@@ -164,14 +161,14 @@ export type { LevelSpot, LevelBuild, LevelCollider, LevelLineOptions, LevelGridO
164
161
  export type { FieldMeta } from "./core/fields"
165
162
  // Editor-only line drawing from generator rebuild() / make() factories / tool hooks — drawn by the
166
163
  // scene editor's viewport overlay, a no-op everywhere else (see src/scene/gizmos.ts).
167
- export { Gizmos, GizmoBuffer as __GizmoBuffer, withGizmoScope as __withGizmoScope } from "./scene/gizmos"
164
+ export { Gizmos } from "./scene/gizmos"
168
165
  export type { GizmoStyle, GizmoAnchor } from "./scene/gizmos"
169
166
 
170
167
  // ---- no-code scenario aspects (attachable in the scene editor — see src/gl/scenarios.ts) ----
171
168
  export { MoveTo, FollowPath, Spin, LookAt, PlayAnimation } from "./gl/scenarios"
172
169
 
173
170
  // ---- editor plugins (called from `*.editor.ts` files — editor bundles only; DCE'd from apps) ----
174
- export { registerEditorWindow, registerEditorTool, __editorPlugins } from "./scene/editorPlugins"
171
+ export { registerEditorWindow, registerEditorTool } from "./scene/editorPlugins"
175
172
  export type { EditorApi, EditorRayHit, EditorToolHooks, EditorWindowFn } from "./scene/editorPlugins"
176
173
 
177
174
  // ---- 3D engine (creator-gl) ----
package/src/net/core.ts CHANGED
@@ -43,6 +43,8 @@ export type NetEvents = {
43
43
  }
44
44
 
45
45
  export type NetLaunch = { role: "server" | "client"; address: string; port: number; maxClients: number }
46
+ /** Link statistics (`player.rtt` / `player.loss` read them): the bridge's `NetStats`. */
47
+ export type NetStats = { rtt: number; loss: number; sentKbps: number; receivedKbps: number }
46
48
 
47
49
  export const CH_RELIABLE = 0
48
50
  export const CH_UNRELIABLE = 1
@@ -80,8 +82,8 @@ export class NetPlayer {
80
82
 
81
83
  get isMe(): boolean { return state.me === this }
82
84
  /** Round trip to this player in ms (server side) or to the server (our own player on a client). */
83
- get rtt(): number { return infoOf(this)[1] }
84
- get loss(): number { return infoOf(this)[2] }
85
+ get rtt(): number { return infoOf(this)?.rtt ?? 0 }
86
+ get loss(): number { return infoOf(this)?.loss ?? 0 }
85
87
 
86
88
  /** Server → this player. */
87
89
  send<T>(msg: NetMessage<T>, data: T): void {
@@ -162,10 +164,9 @@ export const emit = (event: keyof NetEvents, ...args: any[]): void => {
162
164
  for (const fn of list.slice()) fn(...args)
163
165
  }
164
166
 
165
- const EMPTY_INFO = new Float32Array(5)
166
- const infoOf = (p: NetPlayer): Float32Array => {
167
+ const infoOf = (p: NetPlayer): NetStats | null => {
167
168
  const b = bridge()
168
- if (!b || p._slot === LOCAL_SLOT && state.role !== "client") return EMPTY_INFO
169
+ if (!b || p._slot === LOCAL_SLOT && state.role !== "client") return null
169
170
  return b.clientInfo(p._slot)
170
171
  }
171
172
 
package/src/net/index.ts CHANGED
@@ -30,7 +30,7 @@ class NetImpl {
30
30
  get players(): readonly NetPlayer[] { if (state.role !== "server") ensureMe(); return state.players }
31
31
  player(id: number): NetPlayer | undefined { return state.players.find((p) => p.id === id) }
32
32
  /** Our round trip to the server (client), 0 otherwise. */
33
- get rtt(): number { return state.role === "client" ? (bridge()?.clientInfo(0)[1] ?? 0) : 0 }
33
+ get rtt(): number { return state.role === "client" ? (bridge()?.clientInfo(0)?.rtt ?? 0) : 0 }
34
34
  /** The simulation tick — one per fixed step (`updateFixed`). The server's is THE tick; a client
35
35
  * runs a few ticks ahead so its inputs arrive in time. */
36
36
  get tick(): number { return currentTick() }
@@ -407,7 +407,7 @@ const sendSnapshots = (): void => {
407
407
 
408
408
  // ---- client: snapshots in, inputs out, the clock, interpolation, correction ----------------------------
409
409
 
410
- const rttTicks = (): number => (bridge()?.clientInfo(0)[1] ?? 0) / TICK_MS
410
+ const rttTicks = (): number => (bridge()?.clientInfo(0)?.rtt ?? 0) / TICK_MS
411
411
 
412
412
  const onClientBinary = (channel: number, data: ArrayBuffer): void => {
413
413
  if (data.byteLength < 12) return
package/src/plugin.ts ADDED
@@ -0,0 +1,191 @@
1
+ // `lecodes-sdk/plugin` — the one entry a PLUGIN imports the SDK through (docs/plans/plugins-plan.md).
2
+ // Two readers:
3
+ // - a plugin's `contract.d.ts`: the channel markers and the precise value types the generator maps
4
+ // to Swift / Kotlin;
5
+ // - its `sdk/` wrapper, generated (`<id>.gen.ts`) and hand-written (`<id>.ts`): the two channel
6
+ // bases and the few SDK internals a wrapper needs.
7
+ //
8
+ // A contract describes the WIRE of one channel, never the app-facing API: three interfaces per
9
+ // channel — the methods (it extends a marker), the params, the events.
10
+
11
+ import { File as HostFile } from "./runtime/fetch"
12
+ import { ServiceClient } from "./runtime/service"
13
+ import { NativeView, NativeViewElement } from "./ui/NativeView"
14
+ import { _navEpoch } from "./ui/presentable"
15
+ import { _requestCameraPermission } from "./plugins/permission"
16
+
17
+ export type { PresentOptions } from "./ui/presentable"
18
+ export type { NativeView } from "./ui/NativeView"
19
+ export type { FetchResponse } from "./runtime/fetch"
20
+
21
+ // ---- the contract's vocabulary ---------------------------------------------------------------------
22
+
23
+ /** Signed 32-bit integer on the wire. */
24
+ export type i32 = number
25
+ /** Double on the wire — also every integer that can leave 32 bits (a millisecond time stamp). */
26
+ export type f64 = number
27
+
28
+ /** Bytes that STAY in the host's buffer table: the native half hands over its bytes + a name, the
29
+ * app gets the SDK's `File` handle (a texture source, a `FormData` value, a share) and the bytes
30
+ * never enter the JS heap. For bytes the app must read itself, a contract says `Uint8Array`. */
31
+ export type File = HostFile
32
+
33
+ /** Any JSON value, opaque to the generator (GeoJSON, a developer's own payload): it crosses as is
34
+ * and the native half receives the platform's untyped value. */
35
+ export type Json = null | boolean | number | string | Json[] | { [key: string]: Json }
36
+
37
+ declare const channel: unique symbol
38
+
39
+ /** Marks a contract interface as a NativeView channel. `Name` is what the halves register under
40
+ * (`registerView(name)`); `Params` is what a factory receives; `Events` maps an event's name to
41
+ * its payload (`void` = no payload). */
42
+ export interface View<Name extends string, Params extends object = {}, Events extends object = {}> {
43
+ readonly [channel]?: { kind: "view", name: Name, params: Params, events: Events }
44
+ }
45
+
46
+ /** Marks a contract interface as a headless Service channel — see {@link View}. */
47
+ export interface Service<Name extends string, Params extends object = {}, Events extends object = {}> {
48
+ readonly [channel]?: { kind: "service", name: Name, params: Params, events: Events }
49
+ }
50
+
51
+ // ---- the wrapper's bases ---------------------------------------------------------------------------
52
+
53
+ /** A listener of the event `K`: no argument for a `void` payload. */
54
+ export type Listener<Events, K extends keyof Events> = Events[K] extends void ? () => void : (payload: Events[K]) => void
55
+
56
+ /** A method the host's half is too old for (`@since`): refused HERE, by what it needs, before the
57
+ * half is asked. `version` = the half's contract version (0 = the host has none: the channel's own
58
+ * "not supported" speaks then). */
59
+ const tooOld = (iface: string, name: string, method: string, since: number, version: number): Error | null =>
60
+ since > 1 && version > 0 && version < since
61
+ ? new Error(`${iface}.${method} needs the "${name}" plugin at contract version ${since} — this host's is ${version}`)
62
+ : null
63
+
64
+ /** An optional argument the caller left out is ABSENT on the wire, not a null in its place. */
65
+ const present = (args: any[]): any[] => {
66
+ let n = args.length
67
+ while (n > 0 && args[n - 1] === undefined) n--
68
+ return n === args.length ? args : args.slice(0, n)
69
+ }
70
+
71
+ /** What an event's payload is read with when it is not on the wire what it is for the app (it
72
+ * holds a File): by the event's name. */
73
+ export type EventReaders = { [event: string]: (wire: any) => any }
74
+
75
+ /** The listeners of the events that have a reader: the app's callback → the one that reads first,
76
+ * so `off` finds what `on` registered. */
77
+ class Readers {
78
+ private readonly wrapped = new Map<string, Map<Function, (data?: any) => void>>()
79
+ private readonly readers: EventReaders | undefined
80
+ constructor(readers: EventReaders | undefined) { this.readers = readers }
81
+
82
+ on(event: string, callback: (data?: any) => void): (data?: any) => void {
83
+ const read = this.readers?.[event]
84
+ if (!read) return callback
85
+ let byCallback = this.wrapped.get(event)
86
+ if (!byCallback) this.wrapped.set(event, byCallback = new Map())
87
+ let listener = byCallback.get(callback)
88
+ if (!listener) byCallback.set(callback, listener = (wire?: any) => callback(read(wire)))
89
+ return listener
90
+ }
91
+ off(event: string, callback: (data?: any) => void): (data?: any) => void {
92
+ const byCallback = this.wrapped.get(event)
93
+ const listener = byCallback?.get(callback)
94
+ if (!listener) return callback
95
+ byCallback!.delete(callback)
96
+ return listener
97
+ }
98
+ }
99
+
100
+ /** The element of a view plugin: a `NativeView` whose events are the contract's. Generated wrappers
101
+ * extend it (`<Name>Base`); a hand-written one extends that, or this when its API is not the wire's. */
102
+ export class ViewChannel<Events extends object = {}> extends NativeViewElement {
103
+ /** @internal The contract interface's name, for messages. */
104
+ readonly _iface: string
105
+ private readonly _readers: Readers
106
+
107
+ constructor(iface: string, name: string, params: object, readers?: EventReaders) {
108
+ super(name, params)
109
+ this._iface = iface
110
+ this._readers = new Readers(readers)
111
+ }
112
+
113
+ /** Whether this host registered a view under `name` — at contract version `since` or later, when
114
+ * one is given (a method's `@since`). */
115
+ static supports(name: string, since = 1): boolean { return since <= 1 ? NativeView.isSupported(name) : NativeView.version(name) >= since }
116
+
117
+ override on<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
118
+ return super.on(event, this._readers.on(event, callback as (data?: any) => void))
119
+ }
120
+ override off<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
121
+ return super.off(event, this._readers.off(event, callback as (data?: any) => void))
122
+ }
123
+
124
+ /** @internal One call over the wire — what the generated functions are made of. */
125
+ _call(method: string, args: any[], since = 1): Promise<any> {
126
+ const old = since > 1 ? tooOld(this._iface, this.viewName, method, since, NativeView.version(this.viewName)) : null
127
+ return old ? Promise.reject(old) : this._channelSend(method, present(args))
128
+ }
129
+ }
130
+
131
+ /** The client of a service plugin: a session that opens on the first call, with the contract's
132
+ * events. The service twin of {@link ViewChannel}. */
133
+ export class ServiceChannel<Events extends object = {}> extends ServiceClient {
134
+ /** @internal */
135
+ readonly _iface: string
136
+ private readonly _readers: Readers
137
+
138
+ constructor(iface: string, name: string, params: object = {}, readers?: EventReaders) {
139
+ super(name, params)
140
+ this._iface = iface
141
+ this._readers = new Readers(readers)
142
+ }
143
+
144
+ /** Whether this host registered a service under `name` — at contract version `since` or later,
145
+ * when one is given (a method's `@since`). */
146
+ static supports(name: string, since = 1): boolean { return since <= 1 ? ServiceClient.isSupported(name) : ServiceClient.version(name) >= since }
147
+
148
+ override on<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
149
+ return super.on(event, this._readers.on(event, callback as (data?: any) => void))
150
+ }
151
+ override off<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
152
+ return super.off(event, this._readers.off(event, callback as (data?: any) => void))
153
+ }
154
+
155
+ /** @internal */
156
+ _call(method: string, args: any[], since = 1): Promise<any> {
157
+ const old = since > 1 ? tooOld(this._iface, this.serviceName, method, since, ServiceClient.version(this.serviceName)) : null
158
+ return old ? Promise.reject(old) : this._channelSend(method, present(args))
159
+ }
160
+ }
161
+
162
+ // ---- what a wrapper reads off the wire -------------------------------------------------------------
163
+
164
+ /** @internal A `File` of the contract as it crosses: the host buffer's id, the name, the size. */
165
+ export const _toFile = (wire: { systemId: number, name: string, size: number }): File =>
166
+ new HostFile(wire.systemId, wire.name, wire.size)
167
+
168
+ /** @internal …and the other way: a File the app hands a half. The bytes stay where they are — the
169
+ * half reads them out of the host's buffer table. */
170
+ export const _fromFile = (file: File): { systemId: number, name: string, size: number } =>
171
+ ({ systemId: file._h.id, name: file.name, size: file.size })
172
+
173
+ // ---- for the hand-written part ---------------------------------------------------------------------
174
+
175
+ /** The text of a host buffer — what a bundled `asset()` is in a shell compile (`"id:N"`): the host
176
+ * already holds the bytes. Empty when there is no such buffer. */
177
+ export const bufferText = (systemId: number): string => _creatorFetch.toText(systemId)
178
+
179
+ /** Prepare-then-present for a view that needs the camera: fail before any prompt on a host without
180
+ * the view, ask for the permission while the current destination stays visible, give up when
181
+ * another navigation superseded this one. Resolves when the view may open. */
182
+ export const prepareCamera = async (view: ViewChannel<any>): Promise<void> => {
183
+ if (!NativeView.isSupported(view.viewName)) {
184
+ throw new Error(`${view._iface} isn't available on this host — it needs a registered "${view.viewName}" view.`)
185
+ }
186
+ const epoch = _navEpoch()
187
+ await _requestCameraPermission()
188
+ if (_navEpoch() !== epoch) {
189
+ throw new Error(`${view._iface}.open() superseded: another destination was opened while preparing`)
190
+ }
191
+ }
@@ -0,0 +1,27 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/contract.d.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // camera — a live camera preview with still capture.
4
+ // The contract of the WIRE: what the app-facing `CameraView` (sdk/) and the halves (ios/, android/,
5
+ // web/) agree on. The generator reads this file; nothing else describes the channel.
6
+
7
+ import type { File, View } from "../../../plugin"
8
+
9
+ export type Facing = "front" | "back"
10
+
11
+ export interface CameraParams {
12
+ /** Default "back". */
13
+ facingMode?: Facing
14
+ }
15
+
16
+ export interface CameraEvents {
17
+ /** The preview could not start or stopped (no camera, the stream was refused). */
18
+ error: { message: string }
19
+ }
20
+
21
+ export interface CameraView extends View<"camera", CameraParams, CameraEvents> {
22
+ /** Capture a still frame as a JPEG.
23
+ * @rejects failed */
24
+ takePhoto(): Promise<File>
25
+ /** Switch the camera while the preview is live. */
26
+ setFacingMode(mode: Facing): Promise<void>
27
+ }
@@ -0,0 +1,46 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/sdk/camera.gen.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // GENERATED by `lecodes plugin gen` from contract.d.ts — do not edit.
4
+ // camera: the view "camera", contract version 1.
5
+
6
+ import { ViewChannel, _toFile, type File } from "../../../../plugin"
7
+ import type { CameraEvents, CameraParams, Facing } from "../contract"
8
+
9
+ /** What the halves register under. */
10
+ export const NAME = "camera"
11
+ /** The contract's version: the highest `@since` of its members. */
12
+ export const VERSION = 1
13
+
14
+ // ---- the wire, call by call ----------------------------------------------------------------------
15
+
16
+ /** Capture a still frame as a JPEG.
17
+ * Rejects with "failed". */
18
+ export const takePhoto = (view: ViewChannel<CameraEvents>): Promise<File> =>
19
+ view._call("takePhoto", []).then((w: any) => _toFile(w))
20
+
21
+ /** Switch the camera while the preview is live. */
22
+ export const setFacingMode = (view: ViewChannel<CameraEvents>, mode: Facing): Promise<void> =>
23
+ view._call("setFacingMode", [mode])
24
+
25
+ // ---- what the host's half has --------------------------------------------------------------------
26
+
27
+ /** A method of the contract. */
28
+ export type Method = "takePhoto" | "setFacingMode"
29
+
30
+ /** Whether this host has the view "camera" — and, given a method, a half new enough for it
31
+ * (a method added after the first version of the contract is tagged `@since`). */
32
+ export const supports = (method?: Method): boolean =>
33
+ ViewChannel.supports(NAME) // every method is of version 1
34
+
35
+ // ---- the element ----------------------------------------------------------------------------------
36
+
37
+ /** The view "camera" with the wire's methods as its own. */
38
+ export class CameraViewBase extends ViewChannel<CameraEvents> {
39
+ constructor(params: CameraParams = {}) { super("CameraView", NAME, params) }
40
+
41
+ /** Capture a still frame as a JPEG.
42
+ * Rejects with "failed". */
43
+ takePhoto(): Promise<File> { return takePhoto(this) }
44
+ /** Switch the camera while the preview is live. */
45
+ setFacingMode(mode: Facing): Promise<void> { return setFacingMode(this, mode) }
46
+ }
@@ -0,0 +1,57 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/sdk/camera.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // CameraView — the device camera (a live preview + still capture) as a view the app opens
4
+ // fullscreen (`await camera.open()`), pushes (`Router.push(camera)`) or embeds among a screen's
5
+ // children. `takePhoto()` returns a `File`: a texture source, a `FormData` value, a share.
6
+ //
7
+ // Hand-written over the generated wire for ONE reason: the permission is asked BEFORE the view is
8
+ // presented, while the current destination stays visible.
9
+
10
+ import { prepareCamera, type File, type NativeView, type PresentOptions } from "../../../../plugin"
11
+ import { CameraViewBase, NAME } from "./camera.gen"
12
+ import type { Facing } from "../contract"
13
+
14
+ export type CameraFacing = Facing
15
+
16
+ export interface CameraView extends NativeView {
17
+ /** Request the camera permission, then present the preview. The current destination stays
18
+ * visible until the camera is ready; rejects if the permission is denied, the host has no
19
+ * camera, or another navigation superseded this one. */
20
+ open(options?: PresentOptions): Promise<void>
21
+ /** Capture a still frame as an image `File` (host-encoded). */
22
+ takePhoto(): Promise<File>
23
+ /** Switch between the front and the back camera while the preview is live. */
24
+ setFacingMode(facingMode: CameraFacing): Promise<void>
25
+ }
26
+
27
+ class CameraViewElement extends CameraViewBase {
28
+ constructor(options: { facingMode?: CameraFacing } = {}) {
29
+ super({ facingMode: options.facingMode ?? "back" })
30
+ }
31
+
32
+ /** @internal prepare-then-present (see Presentable._prepare). */
33
+ _prepare(): Promise<void> { return prepareCamera(this) }
34
+
35
+ override async open(options?: PresentOptions): Promise<void> {
36
+ await this._prepare()
37
+ super.open(options)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Create a camera view (default: the back camera). `CameraView.isSupported` reports whether this
43
+ * host has the capability — check it before offering the feature.
44
+ */
45
+ // PURE IIFE so an app that never uses the camera tree-shakes the whole plugin away.
46
+ export const CameraView: {
47
+ (options?: { facingMode?: CameraFacing }): CameraView
48
+ /** Whether this host registered a "camera" view. */
49
+ readonly isSupported: boolean
50
+ } = /*#__PURE__*/ (() => {
51
+ const factory = (options?: { facingMode?: CameraFacing }): CameraView =>
52
+ new CameraViewElement(options) as unknown as CameraView
53
+ Object.defineProperty(factory, "isSupported", {
54
+ get: (): boolean => CameraViewBase.supports(NAME),
55
+ })
56
+ return factory as any
57
+ })()
@@ -0,0 +1,50 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/geolocation/contract.d.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // geolocation — the device position.
4
+ // The wire has ONE watch per session (startWatch / stopWatch + the `position` event); the
5
+ // app-facing `Geolocation.watch(callback)` fans it out to its subscribers in sdk/.
6
+ //
7
+ // A session opens without a prompt. The OS permission prompt belongs to the first call that needs
8
+ // the position.
9
+
10
+ import type { Service, f64 } from "../../../plugin"
11
+
12
+ export interface GeoOptions {
13
+ /** Prefer the precise (GPS) provider — slower first fix, more battery. Default false. */
14
+ highAccuracy?: boolean
15
+ /** getCurrent only: reject with "timeout" when no fix arrives in this many ms. */
16
+ timeout?: f64
17
+ }
18
+
19
+ export interface GeoPosition {
20
+ latitude: f64
21
+ longitude: f64
22
+ /** Horizontal accuracy radius, meters. */
23
+ accuracy: f64
24
+ /** Meters above sea level; null when the provider does not know. */
25
+ altitude: f64 | null
26
+ /** Direction of travel, degrees clockwise from north; null when stationary or unknown. */
27
+ heading: f64 | null
28
+ /** Ground speed, m/s; null when unknown. */
29
+ speed: f64 | null
30
+ /** When the fix was taken, ms since the epoch. */
31
+ timestamp: f64
32
+ }
33
+
34
+ export interface GeolocationParams {}
35
+
36
+ export interface GeolocationEvents {
37
+ /** A fix of the live watch. */
38
+ position: GeoPosition
39
+ }
40
+
41
+ export interface Geolocation extends Service<"geolocation", GeolocationParams, GeolocationEvents> {
42
+ /** One fix.
43
+ * @rejects denied unavailable timeout */
44
+ getCurrent(options?: GeoOptions): Promise<GeoPosition>
45
+ /** Start the session's watch; resolves once it is live (permission granted, provider started).
46
+ * A second call while watching resolves at once and keeps the first call's options.
47
+ * @rejects denied unavailable */
48
+ startWatch(options?: GeoOptions): Promise<void>
49
+ stopWatch(): Promise<void>
50
+ }
@@ -0,0 +1,54 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/geolocation/sdk/geolocation.gen.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // GENERATED by `lecodes plugin gen` from contract.d.ts — do not edit.
4
+ // geolocation: the service "geolocation", contract version 1.
5
+
6
+ import { ServiceChannel } from "../../../../plugin"
7
+ import type { GeoOptions, GeoPosition, GeolocationEvents, GeolocationParams } from "../contract"
8
+
9
+ /** What the halves register under. */
10
+ export const NAME = "geolocation"
11
+ /** The contract's version: the highest `@since` of its members. */
12
+ export const VERSION = 1
13
+
14
+ // ---- the wire, call by call ----------------------------------------------------------------------
15
+
16
+ /** One fix.
17
+ * Rejects with "denied" / "unavailable" / "timeout". */
18
+ export const getCurrent = (service: ServiceChannel<GeolocationEvents>, options?: GeoOptions): Promise<GeoPosition> =>
19
+ service._call("getCurrent", [options])
20
+
21
+ /** Start the session's watch; resolves once it is live (permission granted, provider started).
22
+ * A second call while watching resolves at once and keeps the first call's options.
23
+ * Rejects with "denied" / "unavailable". */
24
+ export const startWatch = (service: ServiceChannel<GeolocationEvents>, options?: GeoOptions): Promise<void> =>
25
+ service._call("startWatch", [options])
26
+
27
+ export const stopWatch = (service: ServiceChannel<GeolocationEvents>): Promise<void> =>
28
+ service._call("stopWatch", [])
29
+
30
+ // ---- what the host's half has --------------------------------------------------------------------
31
+
32
+ /** A method of the contract. */
33
+ export type Method = "getCurrent" | "startWatch" | "stopWatch"
34
+
35
+ /** Whether this host has the service "geolocation" — and, given a method, a half new enough for it
36
+ * (a method added after the first version of the contract is tagged `@since`). */
37
+ export const supports = (method?: Method): boolean =>
38
+ ServiceChannel.supports(NAME) // every method is of version 1
39
+
40
+ // ---- the client -----------------------------------------------------------------------------------
41
+
42
+ /** The service "geolocation" with the wire's methods as its own. */
43
+ export class GeolocationBase extends ServiceChannel<GeolocationEvents> {
44
+ constructor(params: GeolocationParams = {}) { super("Geolocation", NAME, params) }
45
+
46
+ /** One fix.
47
+ * Rejects with "denied" / "unavailable" / "timeout". */
48
+ getCurrent(options?: GeoOptions): Promise<GeoPosition> { return getCurrent(this, options) }
49
+ /** Start the session's watch; resolves once it is live (permission granted, provider started).
50
+ * A second call while watching resolves at once and keeps the first call's options.
51
+ * Rejects with "denied" / "unavailable". */
52
+ startWatch(options?: GeoOptions): Promise<void> { return startWatch(this, options) }
53
+ stopWatch(): Promise<void> { return stopWatch(this) }
54
+ }