@thunderclap-engine/engine 0.0.0-stage → 1.1.0

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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,216 @@
1
+ /**
2
+ * The web host: the game in a browser tab, or in a frame such as itch.io's. It wraps the D-036
3
+ * link service (host/web-links.ts), the fullscreen helpers (platform/device.ts) and
4
+ * `localStorage` behind a guarded probe (save/storage.ts). Web storage is `localStorage` only.
5
+ *
6
+ * The mobile facets (ws/host-mobile) come from the page too: `lifecycle` from
7
+ * `visibilitychange` and `pagehide`/`pageshow`, `haptics` from `navigator.vibrate` when the
8
+ * browser has it, `safeArea` from `env(safe-area-inset-*)` read through a probe element and
9
+ * re-read on resize. The factories are exported so another host (a store app's) can reuse them
10
+ * for what its shell does not answer itself.
11
+ */
12
+ import { fullscreenAvailable, isFullscreen, toggleFullscreen, watchFullscreen, } from '../platform/device.js';
13
+ import { embedContext } from '../platform/links.js';
14
+ import { probeStorage } from '../save/storage.js';
15
+ import { clipboardWriteAllowed, copyText, openExternal } from './web-links.js';
16
+ export function createWebHost(opts = {}) {
17
+ const win = opts.win ?? window;
18
+ let embed = null;
19
+ return {
20
+ kind: 'web',
21
+ requestFrame: (cb) => win.requestAnimationFrame(cb),
22
+ cancelFrame: (handle) => win.cancelAnimationFrame(handle),
23
+ get embed() {
24
+ embed ??= embedContext(win);
25
+ return embed;
26
+ },
27
+ openExternal: (url, o) => openExternal(url, o, win),
28
+ copyText: (text) => copyText(text, win),
29
+ clipboardWriteAllowed: () => clipboardWriteAllowed(win.document),
30
+ fullscreen: {
31
+ available: () => fullscreenAvailable(win.document),
32
+ get active() {
33
+ return isFullscreen(win.document);
34
+ },
35
+ toggle: (o) => toggleFullscreen(win.document.documentElement, o),
36
+ watch: (fn) => watchFullscreen(fn, win.document),
37
+ },
38
+ storage: {
39
+ kind: 'local',
40
+ // Merely reading `localStorage` throws in some sandboxed or third-party frames, so it is
41
+ // read inside the probe's guard.
42
+ open: () => probeStorage(() => win.localStorage),
43
+ },
44
+ lifecycle: createWebLifecycle(win),
45
+ haptics: createWebHaptics(win),
46
+ safeArea: createWebSafeArea(win),
47
+ };
48
+ }
49
+ /**
50
+ * The page's lifecycle as a `HostLifecycle`: `background` once the document is hidden or paged
51
+ * out (`pagehide`, `freeze`), `active` when it is visible again (`visibilitychange`, `pageshow`,
52
+ * `resume`). Each watcher sees a change only when the state differs from what it last saw.
53
+ */
54
+ export function createWebLifecycle(win) {
55
+ const doc = win.document;
56
+ let pagedOut = false;
57
+ const state = () => pagedOut || doc.visibilityState === 'hidden' ? 'background' : 'active';
58
+ return {
59
+ get state() {
60
+ return state();
61
+ },
62
+ watch(fn) {
63
+ let last = state();
64
+ const emit = () => {
65
+ const s = state();
66
+ if (s === last)
67
+ return;
68
+ last = s;
69
+ fn(s);
70
+ };
71
+ const onVis = () => {
72
+ if (doc.visibilityState !== 'hidden')
73
+ pagedOut = false;
74
+ emit();
75
+ };
76
+ const onPageHide = () => {
77
+ pagedOut = true;
78
+ emit();
79
+ };
80
+ const onPageShow = () => {
81
+ pagedOut = false;
82
+ emit();
83
+ };
84
+ doc.addEventListener('visibilitychange', onVis);
85
+ doc.addEventListener('freeze', onPageHide);
86
+ doc.addEventListener('resume', onPageShow);
87
+ win.addEventListener('pagehide', onPageHide);
88
+ win.addEventListener('pageshow', onPageShow);
89
+ return () => {
90
+ doc.removeEventListener('visibilitychange', onVis);
91
+ doc.removeEventListener('freeze', onPageHide);
92
+ doc.removeEventListener('resume', onPageShow);
93
+ win.removeEventListener('pagehide', onPageHide);
94
+ win.removeEventListener('pageshow', onPageShow);
95
+ };
96
+ },
97
+ };
98
+ }
99
+ /** `navigator.vibrate` durations, ms. */
100
+ const IMPACT_MS = { light: 10, medium: 20, heavy: 35 };
101
+ const NOTIFICATION_PATTERN = {
102
+ success: [10, 40, 10],
103
+ warning: [25, 50, 25],
104
+ error: [40, 50, 40, 50, 40],
105
+ };
106
+ /**
107
+ * Haptics over `navigator.vibrate`, when the browser has it (Android Chrome does; iOS Safari
108
+ * does not, so `available()` is false there and every call is a no-op). A vibrate call outside a
109
+ * user gesture may be refused; that is ignored.
110
+ */
111
+ export function createWebHaptics(win) {
112
+ const nav = win.navigator;
113
+ const vibrate = (pattern) => {
114
+ try {
115
+ nav?.vibrate?.(pattern);
116
+ }
117
+ catch {
118
+ // Not allowed here (no gesture yet, or a frame policy): silence is the fallback.
119
+ }
120
+ };
121
+ const available = () => typeof nav?.vibrate === 'function';
122
+ return {
123
+ available,
124
+ impact: (style) => {
125
+ if (available())
126
+ vibrate(IMPACT_MS[style] ?? IMPACT_MS.medium);
127
+ },
128
+ notification: (kind) => {
129
+ if (available())
130
+ vibrate(NOTIFICATION_PATTERN[kind] ?? NOTIFICATION_PATTERN.warning);
131
+ },
132
+ vibrate: (ms) => {
133
+ if (available() && ms > 0)
134
+ vibrate(Math.round(ms));
135
+ },
136
+ };
137
+ }
138
+ /**
139
+ * CSS px safe-area insets of `win`'s viewport, read through a probe element padded with
140
+ * `env(safe-area-inset-*)` (the `--tc-safe-*` custom properties override them, for emulation).
141
+ * Zero when the document has no body yet or the window cannot compute styles.
142
+ */
143
+ export function readSafeAreaInsets(win, out) {
144
+ const doc = win.document;
145
+ out.top = out.right = out.bottom = out.left = 0;
146
+ if (!doc?.body || typeof win.getComputedStyle !== 'function')
147
+ return out;
148
+ const probe = doc.createElement('div');
149
+ probe.style.cssText =
150
+ 'position:fixed;left:0;top:0;width:0;height:0;visibility:hidden;pointer-events:none;' +
151
+ 'padding:var(--tc-safe-top,env(safe-area-inset-top,0px)) var(--tc-safe-right,env(safe-area-inset-right,0px)) ' +
152
+ 'var(--tc-safe-bottom,env(safe-area-inset-bottom,0px)) var(--tc-safe-left,env(safe-area-inset-left,0px));';
153
+ doc.body.append(probe);
154
+ const cs = win.getComputedStyle(probe);
155
+ const px = (v) => Number.parseFloat(v ?? '') || 0;
156
+ out.top = px(cs.paddingTop);
157
+ out.right = px(cs.paddingRight);
158
+ out.bottom = px(cs.paddingBottom);
159
+ out.left = px(cs.paddingLeft);
160
+ probe.remove();
161
+ return out;
162
+ }
163
+ /**
164
+ * The viewport's safe area as a `HostSafeArea`. `insets()` reads the page once and then serves
165
+ * the cached values until a `resize` (or `orientationchange`) invalidates them; watchers are
166
+ * called after a resize only when a value changed.
167
+ */
168
+ export function createWebSafeArea(win) {
169
+ const current = { top: 0, right: 0, bottom: 0, left: 0 };
170
+ let fresh = false;
171
+ const watchers = new Set();
172
+ const insets = () => {
173
+ if (!fresh) {
174
+ readSafeAreaInsets(win, current);
175
+ fresh = true;
176
+ }
177
+ return current;
178
+ };
179
+ const same = (a, b) => a.top === b.top && a.right === b.right && a.bottom === b.bottom && a.left === b.left;
180
+ const previous = { top: 0, right: 0, bottom: 0, left: 0 };
181
+ const onResize = () => {
182
+ if (watchers.size === 0) {
183
+ fresh = false;
184
+ return;
185
+ }
186
+ Object.assign(previous, current);
187
+ fresh = false;
188
+ insets();
189
+ if (same(previous, current))
190
+ return;
191
+ for (const fn of [...watchers])
192
+ fn(current);
193
+ };
194
+ let listening = false;
195
+ const listen = () => {
196
+ if (listening)
197
+ return;
198
+ listening = true;
199
+ win.addEventListener?.('resize', onResize);
200
+ win.addEventListener?.('orientationchange', onResize);
201
+ };
202
+ return {
203
+ insets: () => {
204
+ listen();
205
+ return insets();
206
+ },
207
+ watch(fn) {
208
+ listen();
209
+ insets();
210
+ watchers.add(fn);
211
+ return () => {
212
+ watchers.delete(fn);
213
+ };
214
+ },
215
+ };
216
+ }
@@ -0,0 +1,24 @@
1
+ /// <reference path="../platform/xr-types.d.ts" preserve="true" />
2
+ /**
3
+ * The XR host (ws/xr): a `Host` whose frames come from `XRSession.requestAnimationFrame`, so
4
+ * `GameLoop.setFrames(createXrHost(session, host))` moves a running game into the headset and
5
+ * every other host service (links, clipboard, fullscreen, storage, window, lifecycle, haptics,
6
+ * safe area) keeps coming from the host it wraps. The session hands each frame callback an `XRFrame`; the host keeps it in `frame`
7
+ * for the length of that tick (poses, input sources and the layer's textures are only valid
8
+ * then), so `XrSurface.views(host.frame)` and `XrPointerSource` read it from here.
9
+ */
10
+ import type { Host } from './types.js';
11
+ export interface XrHost extends Host {
12
+ readonly kind: 'xr';
13
+ readonly session: XRSession;
14
+ /** The host this one delegates everything but frames to. */
15
+ readonly base: Host;
16
+ /** The `XRFrame` of the frame callback that is running now; null between frames. */
17
+ readonly frame: XRFrame | null;
18
+ }
19
+ /**
20
+ * Wraps `session.requestAnimationFrame` / `cancelAnimationFrame` as a `FrameSource` and
21
+ * delegates every other `Host` member to `base`. Allocates nothing per frame once the loop's
22
+ * callback is stable (`GameLoop` passes the same function every time).
23
+ */
24
+ export declare function createXrHost(session: XRSession, base: Host): XrHost;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Wraps `session.requestAnimationFrame` / `cancelAnimationFrame` as a `FrameSource` and
3
+ * delegates every other `Host` member to `base`. Allocates nothing per frame once the loop's
4
+ * callback is stable (`GameLoop` passes the same function every time).
5
+ */
6
+ export function createXrHost(session, base) {
7
+ let current = null;
8
+ let lastCb = null;
9
+ let lastWrapped = null;
10
+ const wrap = (cb) => {
11
+ if (cb === lastCb && lastWrapped)
12
+ return lastWrapped;
13
+ lastCb = cb;
14
+ lastWrapped = (time, xrFrame) => {
15
+ current = xrFrame;
16
+ try {
17
+ cb(time);
18
+ }
19
+ finally {
20
+ current = null;
21
+ }
22
+ };
23
+ return lastWrapped;
24
+ };
25
+ return {
26
+ kind: 'xr',
27
+ session,
28
+ base,
29
+ get frame() {
30
+ return current;
31
+ },
32
+ requestFrame: (cb) => session.requestAnimationFrame(wrap(cb)),
33
+ cancelFrame: (handle) => session.cancelAnimationFrame(handle),
34
+ get embed() {
35
+ return base.embed;
36
+ },
37
+ openExternal: (url, opts) => base.openExternal(url, opts),
38
+ copyText: (text) => base.copyText(text),
39
+ clipboardWriteAllowed: () => base.clipboardWriteAllowed(),
40
+ get fullscreen() {
41
+ return base.fullscreen;
42
+ },
43
+ get storage() {
44
+ return base.storage;
45
+ },
46
+ get window() {
47
+ return base.window;
48
+ },
49
+ get lifecycle() {
50
+ return base.lifecycle;
51
+ },
52
+ get haptics() {
53
+ return base.haptics;
54
+ },
55
+ get safeArea() {
56
+ return base.safeArea;
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The string store: one table per locale (inline or loaded on demand), parsed once into an AST
3
+ * per key, looked up along a fallback chain, swapped at runtime with a subscription so the UI
4
+ * re-renders. Numbers, dates, durations and lists go through `Intl` for the current locale.
5
+ */
6
+ import { type MessageArgs, type MessageAst } from './message.js';
7
+ import { type TextDirection } from './negotiate.js';
8
+ /** Nested string table: `{ menu: { start: 'Start' } }` is the key `menu.start`. */
9
+ export type Table = {
10
+ readonly [key: string]: string | Table;
11
+ };
12
+ /** A table, or a loader for one (`() => import('./ru.json')` works: a lone `default` is unwrapped). */
13
+ export type TableSource = Table | (() => Promise<Table | {
14
+ default: Table;
15
+ }>);
16
+ /** What `t` returns for a key no table in the chain has. Default `'key'`: the key itself. */
17
+ export type MissingPolicy = 'key' | 'throw' | ((key: string, locale: string) => string);
18
+ export interface I18nDesc {
19
+ /** The starting locale: a key of `tables`, or a tag whose language one of them shares. */
20
+ locale: string;
21
+ /** Looked up after the locale and its language, in order. */
22
+ fallback?: string | readonly string[];
23
+ tables: Record<string, TableSource>;
24
+ missing?: MissingPolicy;
25
+ /** Called once per missing key and locale, whatever `missing` does. */
26
+ onMissing?: (key: string, locale: string) => void;
27
+ }
28
+ export interface DurationOptions {
29
+ /** `digital` (default): `m:ss` or `h:mm:ss` in the locale's digits. Others: words per unit. */
30
+ style?: 'digital' | 'long' | 'short' | 'narrow';
31
+ /** Show the hours field even when it is zero (digital style). */
32
+ hours?: 'auto' | 'always';
33
+ }
34
+ export interface I18n {
35
+ /** The active locale, as named in `tables`. */
36
+ readonly locale: string;
37
+ /** Every locale that has a table, in table order. */
38
+ readonly locales: readonly string[];
39
+ readonly direction: TextDirection;
40
+ /** Resolves when the starting locale and its fallback chain are loaded. */
41
+ readonly ready: Promise<void>;
42
+ /** The message for `key` formatted with `args`, from the first table in the chain that has it. */
43
+ t(key: string, args?: MessageArgs): string;
44
+ /** Whether any table in the chain has `key`. */
45
+ has(key: string): boolean;
46
+ /** Loads `locale` and its chain if needed, then swaps and notifies. Rejects for an unknown locale. */
47
+ setLocale(locale: string): Promise<void>;
48
+ /** Called after every locale swap. Returns unsubscribe. */
49
+ subscribe(fn: (i18n: I18n) => void): () => void;
50
+ formatNumber(value: number, opts?: Intl.NumberFormatOptions): string;
51
+ formatDate(value: Date | number, opts?: Intl.DateTimeFormatOptions): string;
52
+ formatDuration(ms: number, opts?: DurationOptions): string;
53
+ formatList(items: readonly string[], opts?: Intl.ListFormatOptions): string;
54
+ }
55
+ type Compiled = ReadonlyMap<string, MessageAst | string>;
56
+ /** Flattens a table to dotted keys and parses every string. Throws `MessageSyntaxError`. */
57
+ export declare function compileTable(table: Table, locale: string): Compiled;
58
+ export declare function createI18n(desc: I18nDesc): I18n;
59
+ export {};
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The string store: one table per locale (inline or loaded on demand), parsed once into an AST
3
+ * per key, looked up along a fallback chain, swapped at runtime with a subscription so the UI
4
+ * re-renders. Numbers, dates, durations and lists go through `Intl` for the current locale.
5
+ */
6
+ import { createIntlCache, formatMessage, parseMessage, } from './message.js';
7
+ import { directionOf, languageOf } from './negotiate.js';
8
+ /** Flattens a table to dotted keys and parses every string. Throws `MessageSyntaxError`. */
9
+ export function compileTable(table, locale) {
10
+ const out = new Map();
11
+ const visit = (node, prefix) => {
12
+ for (const k of Object.keys(node)) {
13
+ const v = node[k];
14
+ const key = prefix ? `${prefix}.${k}` : k;
15
+ if (typeof v === 'string') {
16
+ const ast = parseMessage(v, key);
17
+ out.set(key, ast.length === 1 && typeof ast[0] === 'string' ? ast[0] : ast);
18
+ }
19
+ else if (v && typeof v === 'object') {
20
+ visit(v, key);
21
+ }
22
+ else {
23
+ throw new TypeError(`i18n: table "${locale}" key "${key}" is not a string or object`);
24
+ }
25
+ }
26
+ };
27
+ visit(table, '');
28
+ return out;
29
+ }
30
+ function unwrap(loaded) {
31
+ const keys = Object.keys(loaded);
32
+ if (keys.length === 1 && keys[0] === 'default' && typeof loaded.default === 'object') {
33
+ return loaded.default;
34
+ }
35
+ return loaded;
36
+ }
37
+ export function createI18n(desc) {
38
+ const sources = desc.tables;
39
+ const locales = Object.keys(sources);
40
+ if (locales.length === 0)
41
+ throw new Error('i18n: tables is empty');
42
+ const fallbacks = desc.fallback === undefined
43
+ ? []
44
+ : typeof desc.fallback === 'string'
45
+ ? [desc.fallback]
46
+ : desc.fallback;
47
+ const missing = desc.missing ?? 'key';
48
+ const intl = createIntlCache();
49
+ const compiled = new Map();
50
+ const pending = new Map();
51
+ const subscribers = new Set();
52
+ const reported = new Set();
53
+ const formats = new Map();
54
+ let locale = desc.locale;
55
+ let ready = Promise.resolve();
56
+ let direction = directionOf(locale);
57
+ let chain = [];
58
+ let generation = 0;
59
+ /** The table key for `tag`: exact, else one sharing its language. Null when none. */
60
+ const resolve = (tag) => {
61
+ if (tag in sources)
62
+ return tag;
63
+ const lower = tag.toLowerCase();
64
+ const exact = locales.find((l) => l.toLowerCase() === lower);
65
+ if (exact)
66
+ return exact;
67
+ const lang = languageOf(tag);
68
+ return locales.find((l) => languageOf(l) === lang) ?? null;
69
+ };
70
+ /** Table keys to consult for `key`, most specific first, each once. */
71
+ const chainFor = (resolved) => {
72
+ const out = [];
73
+ const push = (tag) => {
74
+ const r = resolve(tag);
75
+ if (r && !out.includes(r))
76
+ out.push(r);
77
+ };
78
+ push(resolved);
79
+ push(languageOf(resolved));
80
+ for (const f of fallbacks) {
81
+ push(f);
82
+ push(languageOf(f));
83
+ }
84
+ return out;
85
+ };
86
+ const load = (key) => {
87
+ if (compiled.has(key))
88
+ return null;
89
+ const source = sources[key];
90
+ if (typeof source !== 'function') {
91
+ compiled.set(key, compileTable(source, key));
92
+ return null;
93
+ }
94
+ let p = pending.get(key);
95
+ if (!p) {
96
+ p = source().then((loaded) => {
97
+ compiled.set(key, compileTable(unwrap(loaded), key));
98
+ pending.delete(key);
99
+ }, (err) => {
100
+ pending.delete(key);
101
+ throw err;
102
+ });
103
+ pending.set(key, p);
104
+ }
105
+ return p;
106
+ };
107
+ const apply = (resolved, keys) => {
108
+ locale = resolved;
109
+ direction = directionOf(resolved);
110
+ chain = keys.map((k) => ({ locale: k, table: compiled.get(k) }));
111
+ for (const fn of subscribers)
112
+ fn(api);
113
+ };
114
+ const find = (key) => {
115
+ for (let i = 0; i < chain.length; i++)
116
+ if (chain[i].table.has(key))
117
+ return chain[i];
118
+ return null;
119
+ };
120
+ const cached = (kind, opts, make) => {
121
+ const k = `${kind}|${locale}|${opts ? JSON.stringify(opts) : ''}`;
122
+ let f = formats.get(k);
123
+ if (!f) {
124
+ f = make();
125
+ formats.set(k, f);
126
+ }
127
+ return f;
128
+ };
129
+ const setLocale = (tag) => {
130
+ const resolved = resolve(tag);
131
+ if (!resolved)
132
+ return Promise.reject(new Error(`i18n: no table for locale "${tag}"`));
133
+ const keys = chainFor(resolved);
134
+ const gen = ++generation;
135
+ const loads = [];
136
+ for (const k of keys) {
137
+ const p = load(k);
138
+ if (p)
139
+ loads.push(p);
140
+ }
141
+ if (loads.length === 0) {
142
+ apply(resolved, keys);
143
+ return Promise.resolve();
144
+ }
145
+ return Promise.all(loads).then(() => {
146
+ // A later setLocale won; leave its result in place.
147
+ if (gen !== generation)
148
+ return;
149
+ apply(resolved, keys);
150
+ });
151
+ };
152
+ const api = {
153
+ get locale() {
154
+ return locale;
155
+ },
156
+ locales,
157
+ get direction() {
158
+ return direction;
159
+ },
160
+ get ready() {
161
+ return ready;
162
+ },
163
+ t(key, args) {
164
+ const layer = find(key);
165
+ if (!layer) {
166
+ const tag = `${locale}\u0000${key}`;
167
+ if (desc.onMissing && !reported.has(tag)) {
168
+ reported.add(tag);
169
+ desc.onMissing(key, locale);
170
+ }
171
+ if (missing === 'key')
172
+ return key;
173
+ if (missing === 'throw')
174
+ throw new Error(`i18n: missing "${key}" in "${locale}"`);
175
+ return missing(key, locale);
176
+ }
177
+ const entry = layer.table.get(key);
178
+ return typeof entry === 'string' ? entry : formatMessage(entry, args, layer.locale, intl);
179
+ },
180
+ has(key) {
181
+ return find(key) !== null;
182
+ },
183
+ setLocale,
184
+ subscribe(fn) {
185
+ subscribers.add(fn);
186
+ return () => {
187
+ subscribers.delete(fn);
188
+ };
189
+ },
190
+ formatNumber(value, opts) {
191
+ return cached('n', opts, () => new Intl.NumberFormat(locale, opts)).format(value);
192
+ },
193
+ formatDate(value, opts) {
194
+ return cached('d', opts, () => new Intl.DateTimeFormat(locale, opts)).format(value);
195
+ },
196
+ formatDuration(ms, opts) {
197
+ const total = Math.max(0, Math.floor(ms / 1000));
198
+ const hours = Math.floor(total / 3600);
199
+ const minutes = Math.floor((total % 3600) / 60);
200
+ const seconds = total % 60;
201
+ const style = opts?.style ?? 'digital';
202
+ if (style === 'digital') {
203
+ const two = cached('n', { minimumIntegerDigits: 2, useGrouping: false }, () => new Intl.NumberFormat(locale, { minimumIntegerDigits: 2, useGrouping: false }));
204
+ const one = cached('n', { useGrouping: false }, () => new Intl.NumberFormat(locale, { useGrouping: false }));
205
+ if (hours > 0 || opts?.hours === 'always') {
206
+ return `${one.format(hours)}:${two.format(minutes)}:${two.format(seconds)}`;
207
+ }
208
+ return `${one.format(minutes)}:${two.format(seconds)}`;
209
+ }
210
+ const DF = Intl.DurationFormat;
211
+ const parts = {};
212
+ if (hours > 0)
213
+ parts.hours = hours;
214
+ if (minutes > 0)
215
+ parts.minutes = minutes;
216
+ if (seconds > 0 || total === 0)
217
+ parts.seconds = seconds;
218
+ // `Intl.DurationFormat` prints nothing for an all-zero duration; the unit path says "0 seconds".
219
+ if (DF && total > 0) {
220
+ return cached('dur', { style }, () => new DF(locale, { style })).format(parts);
221
+ }
222
+ // Older engines: one unit per field, joined as a list.
223
+ const items = [];
224
+ const unitDisplay = style === 'long' ? 'long' : style;
225
+ for (const unit of ['hour', 'minute', 'second']) {
226
+ const v = parts[`${unit}s`];
227
+ if (v === undefined)
228
+ continue;
229
+ items.push(cached('n', { style: 'unit', unit, unitDisplay }, () => new Intl.NumberFormat(locale, { style: 'unit', unit, unitDisplay })).format(v));
230
+ }
231
+ return api.formatList(items, { style: style === 'long' ? 'long' : 'narrow', type: 'unit' });
232
+ },
233
+ formatList(items, opts) {
234
+ if (typeof Intl.ListFormat !== 'function')
235
+ return items.join(', ');
236
+ return cached('l', opts, () => new Intl.ListFormat(locale, opts)).format(items);
237
+ },
238
+ };
239
+ ready = setLocale(desc.locale);
240
+ return api;
241
+ }
@@ -0,0 +1,3 @@
1
+ export { compileTable, createI18n, type DurationOptions, type I18n, type I18nDesc, type MissingPolicy, type Table, type TableSource, } from './i18n.js';
2
+ export { type ArgNode, createIntlCache, type FormatNode, formatMessage, type HashNode, type IntlCache, type MessageArgs, type MessageArgValue, type MessageAst, type MessageNode, MessageSyntaxError, type PluralNode, parseMessage, type SelectNode, } from './message.js';
3
+ export { directionOf, languageOf, negotiateLocale, RTL_LANGUAGES, requestedLocales, type TextDirection, } from './negotiate.js';
@@ -0,0 +1,3 @@
1
+ export { compileTable, createI18n, } from './i18n.js';
2
+ export { createIntlCache, formatMessage, MessageSyntaxError, parseMessage, } from './message.js';
3
+ export { directionOf, languageOf, negotiateLocale, RTL_LANGUAGES, requestedLocales, } from './negotiate.js';
@@ -0,0 +1,72 @@
1
+ /**
2
+ * ICU-style message syntax: a parser that runs once per string when a table loads, and a
3
+ * formatter that walks the resulting AST and builds only the output string.
4
+ *
5
+ * Supported forms:
6
+ * - `{name}`: an argument. Numbers go through `Intl.NumberFormat`, dates through
7
+ * `Intl.DateTimeFormat`, everything else through `String`.
8
+ * - `{n, number}`, `{n, number, integer | percent}`; `{d, date}`, `{d, date, short | medium |
9
+ * long | full}`; `{d, time}`, `{d, time, short | medium | long | full}`.
10
+ * - `{count, plural, offset:1 =0 {none} one {# item} other {# items}}`: `Intl.PluralRules`
11
+ * cardinal categories (`zero one two few many other`), exact `=N` forms checked first, `#` the
12
+ * number minus the offset, formatted for the locale. `selectordinal` uses ordinal rules.
13
+ * - `{g, select, male {he} female {she} other {they}}`.
14
+ * - `'{'` quotes a special character (`{ } #`), `''` is one apostrophe; any other apostrophe is
15
+ * literal.
16
+ *
17
+ * Every error names the key and the 1-based column where parsing stopped.
18
+ */
19
+ export type MessageArgValue = string | number | boolean | Date | null | undefined;
20
+ export type MessageArgs = Readonly<Record<string, MessageArgValue>>;
21
+ export interface ArgNode {
22
+ kind: 'arg';
23
+ name: string;
24
+ }
25
+ export interface FormatNode {
26
+ kind: 'number' | 'date' | 'time';
27
+ name: string;
28
+ style: string | null;
29
+ }
30
+ export interface HashNode {
31
+ kind: 'hash';
32
+ }
33
+ export interface PluralNode {
34
+ kind: 'plural';
35
+ name: string;
36
+ ordinal: boolean;
37
+ offset: number;
38
+ /** `=N` forms, keyed by the exact value. */
39
+ exact: ReadonlyMap<number, MessageAst>;
40
+ /** `zero one two few many`, when present. */
41
+ categories: ReadonlyMap<string, MessageAst>;
42
+ other: MessageAst;
43
+ }
44
+ export interface SelectNode {
45
+ kind: 'select';
46
+ name: string;
47
+ cases: ReadonlyMap<string, MessageAst>;
48
+ other: MessageAst;
49
+ }
50
+ /** Literal text is a plain string. */
51
+ export type MessageNode = string | ArgNode | FormatNode | HashNode | PluralNode | SelectNode;
52
+ export type MessageAst = readonly MessageNode[];
53
+ export declare class MessageSyntaxError extends Error {
54
+ readonly key: string;
55
+ /** 1-based column in the message where parsing stopped. */
56
+ readonly column: number;
57
+ constructor(key: string, column: number, detail: string);
58
+ }
59
+ /** Parses one message. Throws `MessageSyntaxError` naming `key` and the column. */
60
+ export declare function parseMessage(source: string, key?: string): MessageAst;
61
+ /** Per-locale `Intl` objects, built once and reused by every format call. */
62
+ export interface IntlCache {
63
+ plural(locale: string, ordinal: boolean): Intl.PluralRules;
64
+ number(locale: string, style: string | null): Intl.NumberFormat;
65
+ date(locale: string, kind: 'date' | 'time', style: string | null): Intl.DateTimeFormat;
66
+ }
67
+ export declare function createIntlCache(): IntlCache;
68
+ /**
69
+ * Formats a parsed message for `locale`. A missing argument renders as `{name}` so the UI never
70
+ * shows an empty string. Allocates only the output string.
71
+ */
72
+ export declare function formatMessage(ast: MessageAst, args: MessageArgs | undefined, locale: string, intl: IntlCache): string;