rn-gamekit 0.1.0 → 0.2.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 (601) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +95 -14
  3. package/lib/module/assets/defineAssets.js +28 -3
  4. package/lib/module/assets/defineAssets.js.map +1 -1
  5. package/lib/module/assets.js +11 -0
  6. package/lib/module/assets.js.map +1 -0
  7. package/lib/module/audio/createGameAudio.js +784 -0
  8. package/lib/module/audio/createGameAudio.js.map +1 -0
  9. package/lib/module/audio/errors.js +12 -0
  10. package/lib/module/audio/errors.js.map +1 -0
  11. package/lib/module/audio/index.js +5 -0
  12. package/lib/module/audio/index.js.map +1 -0
  13. package/lib/module/audio/resolver.js +37 -0
  14. package/lib/module/audio/resolver.js.map +1 -0
  15. package/lib/module/audio/types.js +4 -0
  16. package/lib/module/audio/types.js.map +1 -0
  17. package/lib/module/audio.js +9 -0
  18. package/lib/module/audio.js.map +1 -0
  19. package/lib/module/camera2d/bounds.js +59 -0
  20. package/lib/module/camera2d/bounds.js.map +1 -0
  21. package/lib/module/camera2d/extents.js +25 -0
  22. package/lib/module/camera2d/extents.js.map +1 -0
  23. package/lib/module/camera2d/follow.js +138 -0
  24. package/lib/module/camera2d/follow.js.map +1 -0
  25. package/lib/module/camera2d/index.js +20 -0
  26. package/lib/module/camera2d/index.js.map +1 -0
  27. package/lib/module/camera2d/interpolation.js +98 -0
  28. package/lib/module/camera2d/interpolation.js.map +1 -0
  29. package/lib/module/camera2d/shake.js +85 -0
  30. package/lib/module/camera2d/shake.js.map +1 -0
  31. package/lib/module/camera2d/transform.js +151 -0
  32. package/lib/module/camera2d/transform.js.map +1 -0
  33. package/lib/module/camera2d/types.js +4 -0
  34. package/lib/module/camera2d/types.js.map +1 -0
  35. package/lib/module/camera2d/validation.js +98 -0
  36. package/lib/module/camera2d/validation.js.map +1 -0
  37. package/lib/module/camera2d/visibility.js +138 -0
  38. package/lib/module/camera2d/visibility.js.map +1 -0
  39. package/lib/module/camera2d.js +9 -0
  40. package/lib/module/camera2d.js.map +1 -0
  41. package/lib/module/collision2d/colliders.js +266 -0
  42. package/lib/module/collision2d/colliders.js.map +1 -0
  43. package/lib/module/collision2d/debug.js +105 -0
  44. package/lib/module/collision2d/debug.js.map +1 -0
  45. package/lib/module/collision2d/filters.js +40 -0
  46. package/lib/module/collision2d/filters.js.map +1 -0
  47. package/lib/module/collision2d/index.js +12 -0
  48. package/lib/module/collision2d/index.js.map +1 -0
  49. package/lib/module/collision2d/intersections.js +67 -0
  50. package/lib/module/collision2d/intersections.js.map +1 -0
  51. package/lib/module/collision2d/manifolds.js +218 -0
  52. package/lib/module/collision2d/manifolds.js.map +1 -0
  53. package/lib/module/collision2d/segments.js +187 -0
  54. package/lib/module/collision2d/segments.js.map +1 -0
  55. package/lib/module/collision2d/spatialHash.js +167 -0
  56. package/lib/module/collision2d/spatialHash.js.map +1 -0
  57. package/lib/module/collision2d/sweeps.js +255 -0
  58. package/lib/module/collision2d/sweeps.js.map +1 -0
  59. package/lib/module/collision2d.js +9 -0
  60. package/lib/module/collision2d.js.map +1 -0
  61. package/lib/module/core/input/buttonPad.js +214 -0
  62. package/lib/module/core/input/buttonPad.js.map +1 -0
  63. package/lib/module/core/input/createInputBuffer.js +66 -14
  64. package/lib/module/core/input/createInputBuffer.js.map +1 -1
  65. package/lib/module/core/session/createGameSession.js +454 -50
  66. package/lib/module/core/session/createGameSession.js.map +1 -1
  67. package/lib/module/core/session/deepFreeze.js +92 -2
  68. package/lib/module/core/session/deepFreeze.js.map +1 -1
  69. package/lib/module/core/session/types.js +12 -0
  70. package/lib/module/core/session/types.js.map +1 -1
  71. package/lib/module/definition/defineGame.js +23 -0
  72. package/lib/module/definition/defineGame.js.map +1 -1
  73. package/lib/module/events/defineGameEvents.js +46 -0
  74. package/lib/module/events/defineGameEvents.js.map +1 -0
  75. package/lib/module/events/errors.js +16 -0
  76. package/lib/module/events/errors.js.map +1 -0
  77. package/lib/module/events/index.js +7 -0
  78. package/lib/module/events/index.js.map +1 -0
  79. package/lib/module/events/payload.js +219 -0
  80. package/lib/module/events/payload.js.map +1 -0
  81. package/lib/module/events/seed.js +33 -0
  82. package/lib/module/events/seed.js.map +1 -0
  83. package/lib/module/events/types.js +4 -0
  84. package/lib/module/events/types.js.map +1 -0
  85. package/lib/module/events.js +13 -0
  86. package/lib/module/events.js.map +1 -0
  87. package/lib/module/geometry/helpers.js +140 -0
  88. package/lib/module/geometry/helpers.js.map +1 -0
  89. package/lib/module/geometry/index.js +5 -0
  90. package/lib/module/geometry/index.js.map +1 -0
  91. package/lib/module/geometry/validation.js +104 -0
  92. package/lib/module/geometry/validation.js.map +1 -0
  93. package/lib/module/geometry.js +9 -0
  94. package/lib/module/geometry.js.map +1 -0
  95. package/lib/module/haptics/createGameHaptics.js +214 -0
  96. package/lib/module/haptics/createGameHaptics.js.map +1 -0
  97. package/lib/module/haptics/errors.js +12 -0
  98. package/lib/module/haptics/errors.js.map +1 -0
  99. package/lib/module/haptics/index.js +5 -0
  100. package/lib/module/haptics/index.js.map +1 -0
  101. package/lib/module/haptics/resolver.js +37 -0
  102. package/lib/module/haptics/resolver.js.map +1 -0
  103. package/lib/module/haptics/types.js +4 -0
  104. package/lib/module/haptics/types.js.map +1 -0
  105. package/lib/module/haptics.js +9 -0
  106. package/lib/module/haptics.js.map +1 -0
  107. package/lib/module/index.js +8 -1
  108. package/lib/module/index.js.map +1 -1
  109. package/lib/module/particles/createParticleSystem.js +481 -0
  110. package/lib/module/particles/createParticleSystem.js.map +1 -0
  111. package/lib/module/particles/defineParticleEffect.js +178 -0
  112. package/lib/module/particles/defineParticleEffect.js.map +1 -0
  113. package/lib/module/particles/errors.js +9 -0
  114. package/lib/module/particles/errors.js.map +1 -0
  115. package/lib/module/particles/index.js +8 -0
  116. package/lib/module/particles/index.js.map +1 -0
  117. package/lib/module/particles/sampling.js +121 -0
  118. package/lib/module/particles/sampling.js.map +1 -0
  119. package/lib/module/particles/types.js +4 -0
  120. package/lib/module/particles/types.js.map +1 -0
  121. package/lib/module/particles.js +4 -0
  122. package/lib/module/particles.js.map +1 -0
  123. package/lib/module/react/GameButtonPad.js +344 -0
  124. package/lib/module/react/GameButtonPad.js.map +1 -0
  125. package/lib/module/react/GamePointerInput.js +74 -16
  126. package/lib/module/react/GamePointerInput.js.map +1 -1
  127. package/lib/module/react/GameView.js +139 -60
  128. package/lib/module/react/GameView.js.map +1 -1
  129. package/lib/module/react/assets/createGameAssetStore.js +145 -95
  130. package/lib/module/react/assets/createGameAssetStore.js.map +1 -1
  131. package/lib/module/react/assets/useGameAssets.js +60 -35
  132. package/lib/module/react/assets/useGameAssets.js.map +1 -1
  133. package/lib/module/react/bindAppLifecycle.js +46 -17
  134. package/lib/module/react/bindAppLifecycle.js.map +1 -1
  135. package/lib/module/react/bindGameSession.js +13 -2
  136. package/lib/module/react/bindGameSession.js.map +1 -1
  137. package/lib/module/react/camera2d/GameLayer2D.js +50 -0
  138. package/lib/module/react/camera2d/GameLayer2D.js.map +1 -0
  139. package/lib/module/react/camera2d/defineGameCamera2D.js +30 -0
  140. package/lib/module/react/camera2d/defineGameCamera2D.js.map +1 -0
  141. package/lib/module/react/camera2d/usePresentedCameraBinding.js +177 -0
  142. package/lib/module/react/camera2d/usePresentedCameraBinding.js.map +1 -0
  143. package/lib/module/react/cleanupList.js +38 -0
  144. package/lib/module/react/cleanupList.js.map +1 -0
  145. package/lib/module/react/lifecycleSource.js +49 -0
  146. package/lib/module/react/lifecycleSource.js.map +1 -0
  147. package/lib/module/react/particles/ParticleView.js +308 -0
  148. package/lib/module/react/particles/ParticleView.js.map +1 -0
  149. package/lib/module/react/particles/culling.js +115 -0
  150. package/lib/module/react/particles/culling.js.map +1 -0
  151. package/lib/module/react/particles/index.js +7 -0
  152. package/lib/module/react/particles/index.js.map +1 -0
  153. package/lib/module/react/particles/spriteXForm.js +45 -0
  154. package/lib/module/react/particles/spriteXForm.js.map +1 -0
  155. package/lib/module/react/particles/useParticlePresentation.js +204 -0
  156. package/lib/module/react/particles/useParticlePresentation.js.map +1 -0
  157. package/lib/module/react/pointerBinding.js +55 -17
  158. package/lib/module/react/pointerBinding.js.map +1 -1
  159. package/lib/module/react/pointerCamera.js +28 -0
  160. package/lib/module/react/pointerCamera.js.map +1 -0
  161. package/lib/module/react/pointerCoalescer.js +16 -9
  162. package/lib/module/react/pointerCoalescer.js.map +1 -1
  163. package/lib/module/react/sprites/GameSprite.js +5 -17
  164. package/lib/module/react/sprites/GameSprite.js.map +1 -1
  165. package/lib/module/react/sprites/GameWorld2D.js +112 -9
  166. package/lib/module/react/sprites/GameWorld2D.js.map +1 -1
  167. package/lib/module/react/sprites/Sprite.js +48 -63
  168. package/lib/module/react/sprites/Sprite.js.map +1 -1
  169. package/lib/module/react/sprites/SpriteBatch.js +53 -28
  170. package/lib/module/react/sprites/SpriteBatch.js.map +1 -1
  171. package/lib/module/react/sprites/batchVisibility.js +58 -0
  172. package/lib/module/react/sprites/batchVisibility.js.map +1 -0
  173. package/lib/module/react/sprites/spriteSelection.js +103 -0
  174. package/lib/module/react/sprites/spriteSelection.js.map +1 -0
  175. package/lib/module/react/sprites/spriteTransform.js +46 -25
  176. package/lib/module/react/sprites/spriteTransform.js.map +1 -1
  177. package/lib/module/react/tilemap/TileMapLayer2D.js +217 -0
  178. package/lib/module/react/tilemap/TileMapLayer2D.js.map +1 -0
  179. package/lib/module/react/tilemap/tilePresentation.js +374 -0
  180. package/lib/module/react/tilemap/tilePresentation.js.map +1 -0
  181. package/lib/module/react/useGameSession.js +70 -0
  182. package/lib/module/react/useGameSession.js.map +1 -0
  183. package/lib/module/react/useGameSessionStatus.js +46 -0
  184. package/lib/module/react/useGameSessionStatus.js.map +1 -0
  185. package/lib/module/react/viewportBinding.js +5 -1
  186. package/lib/module/react/viewportBinding.js.map +1 -1
  187. package/lib/module/react.js +12 -1
  188. package/lib/module/react.js.map +1 -1
  189. package/lib/module/scene/defineScene.js +17 -0
  190. package/lib/module/scene/defineScene.js.map +1 -1
  191. package/lib/module/sprites/sampleSpriteClip.js +66 -6
  192. package/lib/module/sprites/sampleSpriteClip.js.map +1 -1
  193. package/lib/module/sprites/spriteAnimationState.js +10 -20
  194. package/lib/module/sprites/spriteAnimationState.js.map +1 -1
  195. package/lib/module/sprites.js +11 -0
  196. package/lib/module/sprites.js.map +1 -0
  197. package/lib/module/storage/adapters/asyncStorage.js +57 -0
  198. package/lib/module/storage/adapters/asyncStorage.js.map +1 -0
  199. package/lib/module/storage/adapters/memory.js +50 -0
  200. package/lib/module/storage/adapters/memory.js.map +1 -0
  201. package/lib/module/storage/errors.js +34 -0
  202. package/lib/module/storage/errors.js.map +1 -0
  203. package/lib/module/storage/index.js +11 -0
  204. package/lib/module/storage/index.js.map +1 -0
  205. package/lib/module/storage/schema.js +151 -0
  206. package/lib/module/storage/schema.js.map +1 -0
  207. package/lib/module/storage/serialization.js +249 -0
  208. package/lib/module/storage/serialization.js.map +1 -0
  209. package/lib/module/storage/store.js +387 -0
  210. package/lib/module/storage/store.js.map +1 -0
  211. package/lib/module/storage/types.js +16 -0
  212. package/lib/module/storage/types.js.map +1 -0
  213. package/lib/module/storage/validation.js +111 -0
  214. package/lib/module/storage/validation.js.map +1 -0
  215. package/lib/module/storage.js +4 -0
  216. package/lib/module/storage.js.map +1 -0
  217. package/lib/module/testing.js +4 -0
  218. package/lib/module/testing.js.map +1 -1
  219. package/lib/module/tilemap/chunkStats.js +27 -0
  220. package/lib/module/tilemap/chunkStats.js.map +1 -0
  221. package/lib/module/tilemap/definitions.js +308 -0
  222. package/lib/module/tilemap/definitions.js.map +1 -0
  223. package/lib/module/tilemap/errors.js +13 -0
  224. package/lib/module/tilemap/errors.js.map +1 -0
  225. package/lib/module/tilemap/index.js +10 -0
  226. package/lib/module/tilemap/index.js.map +1 -0
  227. package/lib/module/tilemap/movement.js +379 -0
  228. package/lib/module/tilemap/movement.js.map +1 -0
  229. package/lib/module/tilemap/queries.js +114 -0
  230. package/lib/module/tilemap/queries.js.map +1 -0
  231. package/lib/module/tilemap/tiledAdapter.js +141 -0
  232. package/lib/module/tilemap/tiledAdapter.js.map +1 -0
  233. package/lib/module/tilemap/types.js +4 -0
  234. package/lib/module/tilemap/types.js.map +1 -0
  235. package/lib/module/tilemap.js +4 -0
  236. package/lib/module/tilemap.js.map +1 -0
  237. package/lib/typescript/src/assets/defineAssets.d.ts +1 -1
  238. package/lib/typescript/src/assets/defineAssets.d.ts.map +1 -1
  239. package/lib/typescript/src/assets.d.ts +11 -0
  240. package/lib/typescript/src/assets.d.ts.map +1 -0
  241. package/lib/typescript/src/audio/createGameAudio.d.ts +5 -0
  242. package/lib/typescript/src/audio/createGameAudio.d.ts.map +1 -0
  243. package/lib/typescript/src/audio/errors.d.ts +6 -0
  244. package/lib/typescript/src/audio/errors.d.ts.map +1 -0
  245. package/lib/typescript/src/audio/index.d.ts +4 -0
  246. package/lib/typescript/src/audio/index.d.ts.map +1 -0
  247. package/lib/typescript/src/audio/resolver.d.ts +44 -0
  248. package/lib/typescript/src/audio/resolver.d.ts.map +1 -0
  249. package/lib/typescript/src/audio/types.d.ts +42 -0
  250. package/lib/typescript/src/audio/types.d.ts.map +1 -0
  251. package/lib/typescript/src/audio.d.ts +8 -0
  252. package/lib/typescript/src/audio.d.ts.map +1 -0
  253. package/lib/typescript/src/camera2d/bounds.d.ts +25 -0
  254. package/lib/typescript/src/camera2d/bounds.d.ts.map +1 -0
  255. package/lib/typescript/src/camera2d/extents.d.ts +17 -0
  256. package/lib/typescript/src/camera2d/extents.d.ts.map +1 -0
  257. package/lib/typescript/src/camera2d/follow.d.ts +24 -0
  258. package/lib/typescript/src/camera2d/follow.d.ts.map +1 -0
  259. package/lib/typescript/src/camera2d/index.d.ts +19 -0
  260. package/lib/typescript/src/camera2d/index.d.ts.map +1 -0
  261. package/lib/typescript/src/camera2d/interpolation.d.ts +17 -0
  262. package/lib/typescript/src/camera2d/interpolation.d.ts.map +1 -0
  263. package/lib/typescript/src/camera2d/shake.d.ts +20 -0
  264. package/lib/typescript/src/camera2d/shake.d.ts.map +1 -0
  265. package/lib/typescript/src/camera2d/transform.d.ts +44 -0
  266. package/lib/typescript/src/camera2d/transform.d.ts.map +1 -0
  267. package/lib/typescript/src/camera2d/types.d.ts +74 -0
  268. package/lib/typescript/src/camera2d/types.d.ts.map +1 -0
  269. package/lib/typescript/src/camera2d/validation.d.ts +30 -0
  270. package/lib/typescript/src/camera2d/validation.d.ts.map +1 -0
  271. package/lib/typescript/src/camera2d/visibility.d.ts +47 -0
  272. package/lib/typescript/src/camera2d/visibility.d.ts.map +1 -0
  273. package/lib/typescript/src/camera2d.d.ts +7 -0
  274. package/lib/typescript/src/camera2d.d.ts.map +1 -0
  275. package/lib/typescript/src/collision2d/colliders.d.ts +120 -0
  276. package/lib/typescript/src/collision2d/colliders.d.ts.map +1 -0
  277. package/lib/typescript/src/collision2d/debug.d.ts +67 -0
  278. package/lib/typescript/src/collision2d/debug.d.ts.map +1 -0
  279. package/lib/typescript/src/collision2d/filters.d.ts +28 -0
  280. package/lib/typescript/src/collision2d/filters.d.ts.map +1 -0
  281. package/lib/typescript/src/collision2d/index.d.ts +17 -0
  282. package/lib/typescript/src/collision2d/index.d.ts.map +1 -0
  283. package/lib/typescript/src/collision2d/intersections.d.ts +24 -0
  284. package/lib/typescript/src/collision2d/intersections.d.ts.map +1 -0
  285. package/lib/typescript/src/collision2d/manifolds.d.ts +39 -0
  286. package/lib/typescript/src/collision2d/manifolds.d.ts.map +1 -0
  287. package/lib/typescript/src/collision2d/segments.d.ts +24 -0
  288. package/lib/typescript/src/collision2d/segments.d.ts.map +1 -0
  289. package/lib/typescript/src/collision2d/spatialHash.d.ts +50 -0
  290. package/lib/typescript/src/collision2d/spatialHash.d.ts.map +1 -0
  291. package/lib/typescript/src/collision2d/sweeps.d.ts +60 -0
  292. package/lib/typescript/src/collision2d/sweeps.d.ts.map +1 -0
  293. package/lib/typescript/src/collision2d.d.ts +7 -0
  294. package/lib/typescript/src/collision2d.d.ts.map +1 -0
  295. package/lib/typescript/src/core/input/buttonPad.d.ts +59 -0
  296. package/lib/typescript/src/core/input/buttonPad.d.ts.map +1 -0
  297. package/lib/typescript/src/core/input/createInputBuffer.d.ts +1 -1
  298. package/lib/typescript/src/core/input/createInputBuffer.d.ts.map +1 -1
  299. package/lib/typescript/src/core/input/types.d.ts +7 -0
  300. package/lib/typescript/src/core/input/types.d.ts.map +1 -1
  301. package/lib/typescript/src/core/session/createGameSession.d.ts +4 -2
  302. package/lib/typescript/src/core/session/createGameSession.d.ts.map +1 -1
  303. package/lib/typescript/src/core/session/deepFreeze.d.ts +21 -1
  304. package/lib/typescript/src/core/session/deepFreeze.d.ts.map +1 -1
  305. package/lib/typescript/src/core/session/diagnostics.d.ts +2 -0
  306. package/lib/typescript/src/core/session/diagnostics.d.ts.map +1 -1
  307. package/lib/typescript/src/core/session/types.d.ts +69 -4
  308. package/lib/typescript/src/core/session/types.d.ts.map +1 -1
  309. package/lib/typescript/src/definition/defineGame.d.ts +12 -1
  310. package/lib/typescript/src/definition/defineGame.d.ts.map +1 -1
  311. package/lib/typescript/src/definition/types.d.ts +10 -1
  312. package/lib/typescript/src/definition/types.d.ts.map +1 -1
  313. package/lib/typescript/src/events/defineGameEvents.d.ts +26 -0
  314. package/lib/typescript/src/events/defineGameEvents.d.ts.map +1 -0
  315. package/lib/typescript/src/events/errors.d.ts +12 -0
  316. package/lib/typescript/src/events/errors.d.ts.map +1 -0
  317. package/lib/typescript/src/events/index.d.ts +6 -0
  318. package/lib/typescript/src/events/index.d.ts.map +1 -0
  319. package/lib/typescript/src/events/payload.d.ts +21 -0
  320. package/lib/typescript/src/events/payload.d.ts.map +1 -0
  321. package/lib/typescript/src/events/seed.d.ts +3 -0
  322. package/lib/typescript/src/events/seed.d.ts.map +1 -0
  323. package/lib/typescript/src/events/types.d.ts +42 -0
  324. package/lib/typescript/src/events/types.d.ts.map +1 -0
  325. package/lib/typescript/src/events.d.ts +12 -0
  326. package/lib/typescript/src/events.d.ts.map +1 -0
  327. package/lib/typescript/src/geometry/helpers.d.ts +35 -0
  328. package/lib/typescript/src/geometry/helpers.d.ts.map +1 -0
  329. package/lib/typescript/src/geometry/index.d.ts +4 -0
  330. package/lib/typescript/src/geometry/index.d.ts.map +1 -0
  331. package/lib/typescript/src/geometry/types.d.ts +49 -0
  332. package/lib/typescript/src/geometry/types.d.ts.map +1 -1
  333. package/lib/typescript/src/geometry/validation.d.ts +44 -0
  334. package/lib/typescript/src/geometry/validation.d.ts.map +1 -0
  335. package/lib/typescript/src/geometry.d.ts +7 -0
  336. package/lib/typescript/src/geometry.d.ts.map +1 -0
  337. package/lib/typescript/src/haptics/createGameHaptics.d.ts +3 -0
  338. package/lib/typescript/src/haptics/createGameHaptics.d.ts.map +1 -0
  339. package/lib/typescript/src/haptics/errors.d.ts +6 -0
  340. package/lib/typescript/src/haptics/errors.d.ts.map +1 -0
  341. package/lib/typescript/src/haptics/index.d.ts +4 -0
  342. package/lib/typescript/src/haptics/index.d.ts.map +1 -0
  343. package/lib/typescript/src/haptics/resolver.d.ts +22 -0
  344. package/lib/typescript/src/haptics/resolver.d.ts.map +1 -0
  345. package/lib/typescript/src/haptics/types.d.ts +37 -0
  346. package/lib/typescript/src/haptics/types.d.ts.map +1 -0
  347. package/lib/typescript/src/haptics.d.ts +8 -0
  348. package/lib/typescript/src/haptics.d.ts.map +1 -0
  349. package/lib/typescript/src/index.d.ts +10 -2
  350. package/lib/typescript/src/index.d.ts.map +1 -1
  351. package/lib/typescript/src/particles/createParticleSystem.d.ts +5 -0
  352. package/lib/typescript/src/particles/createParticleSystem.d.ts.map +1 -0
  353. package/lib/typescript/src/particles/defineParticleEffect.d.ts +3 -0
  354. package/lib/typescript/src/particles/defineParticleEffect.d.ts.map +1 -0
  355. package/lib/typescript/src/particles/errors.d.ts +5 -0
  356. package/lib/typescript/src/particles/errors.d.ts.map +1 -0
  357. package/lib/typescript/src/particles/index.d.ts +6 -0
  358. package/lib/typescript/src/particles/index.d.ts.map +1 -0
  359. package/lib/typescript/src/particles/sampling.d.ts +54 -0
  360. package/lib/typescript/src/particles/sampling.d.ts.map +1 -0
  361. package/lib/typescript/src/particles/types.d.ts +292 -0
  362. package/lib/typescript/src/particles/types.d.ts.map +1 -0
  363. package/lib/typescript/src/particles.d.ts +2 -0
  364. package/lib/typescript/src/particles.d.ts.map +1 -0
  365. package/lib/typescript/src/react/GameButtonPad.d.ts +84 -0
  366. package/lib/typescript/src/react/GameButtonPad.d.ts.map +1 -0
  367. package/lib/typescript/src/react/GamePointerInput.d.ts.map +1 -1
  368. package/lib/typescript/src/react/GameView.d.ts +27 -1
  369. package/lib/typescript/src/react/GameView.d.ts.map +1 -1
  370. package/lib/typescript/src/react/assets/createGameAssetStore.d.ts.map +1 -1
  371. package/lib/typescript/src/react/assets/useGameAssets.d.ts.map +1 -1
  372. package/lib/typescript/src/react/bindAppLifecycle.d.ts +7 -1
  373. package/lib/typescript/src/react/bindAppLifecycle.d.ts.map +1 -1
  374. package/lib/typescript/src/react/bindGameSession.d.ts.map +1 -1
  375. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts +24 -0
  376. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts.map +1 -0
  377. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts +44 -0
  378. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts.map +1 -0
  379. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts +22 -0
  380. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts.map +1 -0
  381. package/lib/typescript/src/react/cleanupList.d.ts +21 -0
  382. package/lib/typescript/src/react/cleanupList.d.ts.map +1 -0
  383. package/lib/typescript/src/react/instrumentation.d.ts +33 -3
  384. package/lib/typescript/src/react/instrumentation.d.ts.map +1 -1
  385. package/lib/typescript/src/react/lifecycleSource.d.ts +21 -0
  386. package/lib/typescript/src/react/lifecycleSource.d.ts.map +1 -0
  387. package/lib/typescript/src/react/particles/ParticleView.d.ts +65 -0
  388. package/lib/typescript/src/react/particles/ParticleView.d.ts.map +1 -0
  389. package/lib/typescript/src/react/particles/culling.d.ts +70 -0
  390. package/lib/typescript/src/react/particles/culling.d.ts.map +1 -0
  391. package/lib/typescript/src/react/particles/index.d.ts +7 -0
  392. package/lib/typescript/src/react/particles/index.d.ts.map +1 -0
  393. package/lib/typescript/src/react/particles/spriteXForm.d.ts +28 -0
  394. package/lib/typescript/src/react/particles/spriteXForm.d.ts.map +1 -0
  395. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts +48 -0
  396. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts.map +1 -0
  397. package/lib/typescript/src/react/pointerBinding.d.ts +38 -8
  398. package/lib/typescript/src/react/pointerBinding.d.ts.map +1 -1
  399. package/lib/typescript/src/react/pointerCamera.d.ts +20 -0
  400. package/lib/typescript/src/react/pointerCamera.d.ts.map +1 -0
  401. package/lib/typescript/src/react/pointerCoalescer.d.ts +17 -1
  402. package/lib/typescript/src/react/pointerCoalescer.d.ts.map +1 -1
  403. package/lib/typescript/src/react/sprites/GameSprite.d.ts +3 -10
  404. package/lib/typescript/src/react/sprites/GameSprite.d.ts.map +1 -1
  405. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts +51 -6
  406. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts.map +1 -1
  407. package/lib/typescript/src/react/sprites/Sprite.d.ts.map +1 -1
  408. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts +22 -1
  409. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts.map +1 -1
  410. package/lib/typescript/src/react/sprites/batchVisibility.d.ts +28 -0
  411. package/lib/typescript/src/react/sprites/batchVisibility.d.ts.map +1 -0
  412. package/lib/typescript/src/react/sprites/spriteSelection.d.ts +58 -0
  413. package/lib/typescript/src/react/sprites/spriteSelection.d.ts.map +1 -0
  414. package/lib/typescript/src/react/sprites/spriteTransform.d.ts +27 -12
  415. package/lib/typescript/src/react/sprites/spriteTransform.d.ts.map +1 -1
  416. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts +69 -0
  417. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts.map +1 -0
  418. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts +217 -0
  419. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts.map +1 -0
  420. package/lib/typescript/src/react/useGameSession.d.ts +48 -0
  421. package/lib/typescript/src/react/useGameSession.d.ts.map +1 -0
  422. package/lib/typescript/src/react/useGameSessionStatus.d.ts +23 -0
  423. package/lib/typescript/src/react/useGameSessionStatus.d.ts.map +1 -0
  424. package/lib/typescript/src/react/viewportBinding.d.ts +5 -1
  425. package/lib/typescript/src/react/viewportBinding.d.ts.map +1 -1
  426. package/lib/typescript/src/react.d.ts +18 -1
  427. package/lib/typescript/src/react.d.ts.map +1 -1
  428. package/lib/typescript/src/scene/defineScene.d.ts +22 -2
  429. package/lib/typescript/src/scene/defineScene.d.ts.map +1 -1
  430. package/lib/typescript/src/scene/types.d.ts +24 -4
  431. package/lib/typescript/src/scene/types.d.ts.map +1 -1
  432. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts +26 -2
  433. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts.map +1 -1
  434. package/lib/typescript/src/sprites/spriteAnimationState.d.ts +6 -7
  435. package/lib/typescript/src/sprites/spriteAnimationState.d.ts.map +1 -1
  436. package/lib/typescript/src/sprites.d.ts +10 -0
  437. package/lib/typescript/src/sprites.d.ts.map +1 -0
  438. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts +18 -0
  439. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts.map +1 -0
  440. package/lib/typescript/src/storage/adapters/memory.d.ts +16 -0
  441. package/lib/typescript/src/storage/adapters/memory.d.ts.map +1 -0
  442. package/lib/typescript/src/storage/errors.d.ts +37 -0
  443. package/lib/typescript/src/storage/errors.d.ts.map +1 -0
  444. package/lib/typescript/src/storage/index.d.ts +11 -0
  445. package/lib/typescript/src/storage/index.d.ts.map +1 -0
  446. package/lib/typescript/src/storage/schema.d.ts +22 -0
  447. package/lib/typescript/src/storage/schema.d.ts.map +1 -0
  448. package/lib/typescript/src/storage/serialization.d.ts +23 -0
  449. package/lib/typescript/src/storage/serialization.d.ts.map +1 -0
  450. package/lib/typescript/src/storage/store.d.ts +3 -0
  451. package/lib/typescript/src/storage/store.d.ts.map +1 -0
  452. package/lib/typescript/src/storage/types.d.ts +69 -0
  453. package/lib/typescript/src/storage/types.d.ts.map +1 -0
  454. package/lib/typescript/src/storage/validation.d.ts +24 -0
  455. package/lib/typescript/src/storage/validation.d.ts.map +1 -0
  456. package/lib/typescript/src/storage.d.ts +2 -0
  457. package/lib/typescript/src/storage.d.ts.map +1 -0
  458. package/lib/typescript/src/testing.d.ts +1 -0
  459. package/lib/typescript/src/testing.d.ts.map +1 -1
  460. package/lib/typescript/src/tilemap/chunkStats.d.ts +14 -0
  461. package/lib/typescript/src/tilemap/chunkStats.d.ts.map +1 -0
  462. package/lib/typescript/src/tilemap/definitions.d.ts +38 -0
  463. package/lib/typescript/src/tilemap/definitions.d.ts.map +1 -0
  464. package/lib/typescript/src/tilemap/errors.d.ts +7 -0
  465. package/lib/typescript/src/tilemap/errors.d.ts.map +1 -0
  466. package/lib/typescript/src/tilemap/index.d.ts +8 -0
  467. package/lib/typescript/src/tilemap/index.d.ts.map +1 -0
  468. package/lib/typescript/src/tilemap/movement.d.ts +52 -0
  469. package/lib/typescript/src/tilemap/movement.d.ts.map +1 -0
  470. package/lib/typescript/src/tilemap/queries.d.ts +21 -0
  471. package/lib/typescript/src/tilemap/queries.d.ts.map +1 -0
  472. package/lib/typescript/src/tilemap/tiledAdapter.d.ts +32 -0
  473. package/lib/typescript/src/tilemap/tiledAdapter.d.ts.map +1 -0
  474. package/lib/typescript/src/tilemap/types.d.ts +127 -0
  475. package/lib/typescript/src/tilemap/types.d.ts.map +1 -0
  476. package/lib/typescript/src/tilemap.d.ts +2 -0
  477. package/lib/typescript/src/tilemap.d.ts.map +1 -0
  478. package/package.json +94 -4
  479. package/src/assets/defineAssets.ts +31 -5
  480. package/src/assets.ts +27 -0
  481. package/src/audio/createGameAudio.ts +749 -0
  482. package/src/audio/errors.ts +12 -0
  483. package/src/audio/index.ts +9 -0
  484. package/src/audio/resolver.ts +74 -0
  485. package/src/audio/types.ts +47 -0
  486. package/src/audio.ts +13 -0
  487. package/src/camera2d/bounds.ts +69 -0
  488. package/src/camera2d/extents.ts +25 -0
  489. package/src/camera2d/follow.ts +165 -0
  490. package/src/camera2d/index.ts +29 -0
  491. package/src/camera2d/interpolation.ts +113 -0
  492. package/src/camera2d/shake.ts +90 -0
  493. package/src/camera2d/transform.ts +148 -0
  494. package/src/camera2d/types.ts +73 -0
  495. package/src/camera2d/validation.ts +126 -0
  496. package/src/camera2d/visibility.ts +181 -0
  497. package/src/camera2d.ts +6 -0
  498. package/src/collision2d/colliders.ts +292 -0
  499. package/src/collision2d/debug.ts +133 -0
  500. package/src/collision2d/filters.ts +48 -0
  501. package/src/collision2d/index.ts +74 -0
  502. package/src/collision2d/intersections.ts +82 -0
  503. package/src/collision2d/manifolds.ts +191 -0
  504. package/src/collision2d/segments.ts +176 -0
  505. package/src/collision2d/spatialHash.ts +211 -0
  506. package/src/collision2d/sweeps.ts +269 -0
  507. package/src/collision2d.ts +6 -0
  508. package/src/core/input/buttonPad.ts +235 -0
  509. package/src/core/input/createInputBuffer.ts +95 -14
  510. package/src/core/input/types.ts +7 -0
  511. package/src/core/session/createGameSession.ts +480 -53
  512. package/src/core/session/deepFreeze.ts +104 -3
  513. package/src/core/session/diagnostics.ts +2 -0
  514. package/src/core/session/types.ts +77 -4
  515. package/src/definition/defineGame.ts +61 -3
  516. package/src/definition/types.ts +10 -0
  517. package/src/events/defineGameEvents.ts +46 -0
  518. package/src/events/errors.ts +14 -0
  519. package/src/events/index.ts +14 -0
  520. package/src/events/payload.ts +234 -0
  521. package/src/events/seed.ts +33 -0
  522. package/src/events/types.ts +53 -0
  523. package/src/events.ts +20 -0
  524. package/src/geometry/helpers.ts +136 -0
  525. package/src/geometry/index.ts +14 -0
  526. package/src/geometry/types.ts +54 -0
  527. package/src/geometry/validation.ts +131 -0
  528. package/src/geometry.ts +6 -0
  529. package/src/haptics/createGameHaptics.ts +214 -0
  530. package/src/haptics/errors.ts +12 -0
  531. package/src/haptics/index.ts +3 -0
  532. package/src/haptics/resolver.ts +58 -0
  533. package/src/haptics/types.ts +48 -0
  534. package/src/haptics.ts +7 -0
  535. package/src/index.ts +37 -1
  536. package/src/particles/createParticleSystem.ts +505 -0
  537. package/src/particles/defineParticleEffect.ts +164 -0
  538. package/src/particles/errors.ts +6 -0
  539. package/src/particles/index.ts +5 -0
  540. package/src/particles/sampling.ts +141 -0
  541. package/src/particles/types.ts +327 -0
  542. package/src/particles.ts +1 -0
  543. package/src/react/GameButtonPad.tsx +436 -0
  544. package/src/react/GamePointerInput.tsx +73 -8
  545. package/src/react/GameView.tsx +160 -61
  546. package/src/react/assets/createGameAssetStore.ts +140 -107
  547. package/src/react/assets/useGameAssets.ts +63 -31
  548. package/src/react/bindAppLifecycle.ts +55 -19
  549. package/src/react/bindGameSession.ts +12 -2
  550. package/src/react/camera2d/GameLayer2D.tsx +43 -0
  551. package/src/react/camera2d/defineGameCamera2D.ts +59 -0
  552. package/src/react/camera2d/usePresentedCameraBinding.ts +185 -0
  553. package/src/react/cleanupList.ts +48 -0
  554. package/src/react/instrumentation.ts +31 -3
  555. package/src/react/lifecycleSource.ts +58 -0
  556. package/src/react/particles/ParticleView.tsx +358 -0
  557. package/src/react/particles/culling.ts +145 -0
  558. package/src/react/particles/index.ts +6 -0
  559. package/src/react/particles/spriteXForm.ts +72 -0
  560. package/src/react/particles/useParticlePresentation.ts +261 -0
  561. package/src/react/pointerBinding.ts +74 -16
  562. package/src/react/pointerCamera.ts +33 -0
  563. package/src/react/pointerCoalescer.ts +36 -10
  564. package/src/react/sprites/GameSprite.tsx +4 -33
  565. package/src/react/sprites/GameWorld2D.tsx +113 -7
  566. package/src/react/sprites/Sprite.tsx +51 -72
  567. package/src/react/sprites/SpriteBatch.tsx +74 -22
  568. package/src/react/sprites/batchVisibility.ts +76 -0
  569. package/src/react/sprites/spriteSelection.ts +133 -0
  570. package/src/react/sprites/spriteTransform.ts +46 -26
  571. package/src/react/tilemap/TileMapLayer2D.tsx +274 -0
  572. package/src/react/tilemap/tilePresentation.ts +491 -0
  573. package/src/react/useGameSession.ts +90 -0
  574. package/src/react/useGameSessionStatus.ts +53 -0
  575. package/src/react/viewportBinding.ts +5 -1
  576. package/src/react.ts +18 -1
  577. package/src/scene/defineScene.ts +47 -5
  578. package/src/scene/types.ts +37 -3
  579. package/src/sprites/sampleSpriteClip.ts +82 -14
  580. package/src/sprites/spriteAnimationState.ts +29 -25
  581. package/src/sprites.ts +22 -0
  582. package/src/storage/adapters/asyncStorage.ts +77 -0
  583. package/src/storage/adapters/memory.ts +58 -0
  584. package/src/storage/errors.ts +88 -0
  585. package/src/storage/index.ts +18 -0
  586. package/src/storage/schema.ts +147 -0
  587. package/src/storage/serialization.ts +248 -0
  588. package/src/storage/store.ts +380 -0
  589. package/src/storage/types.ts +76 -0
  590. package/src/storage/validation.ts +102 -0
  591. package/src/storage.ts +1 -0
  592. package/src/testing.ts +4 -0
  593. package/src/tilemap/chunkStats.ts +24 -0
  594. package/src/tilemap/definitions.ts +327 -0
  595. package/src/tilemap/errors.ts +11 -0
  596. package/src/tilemap/index.ts +10 -0
  597. package/src/tilemap/movement.ts +368 -0
  598. package/src/tilemap/queries.ts +136 -0
  599. package/src/tilemap/tiledAdapter.ts +165 -0
  600. package/src/tilemap/types.ts +119 -0
  601. package/src/tilemap.ts +1 -0
@@ -0,0 +1,380 @@
1
+ import { GameStorageError, storageError } from './errors';
2
+ import type { CreateGameSaveStoreOptions, GameSaveLoadResult, GameSaveStore } from './types';
3
+ import { STORAGE_LIMITS } from './types';
4
+ import { storageKey, legacyStorageKey, validateNamespace, validateSlot } from './validation';
5
+ import { createDefaultData, migratePayload, validateCurrentData } from './schema';
6
+ import { cloneAndValidatePlainData, parseEnvelope, serializeEnvelope, utf8ByteLength, STORAGE_ENVELOPE_FORMAT } from './serialization';
7
+ import type { StoredGameEnvelope } from './types';
8
+
9
+ export function createGameSaveStore<TData>(options: CreateGameSaveStoreOptions<TData>): GameSaveStore<TData> {
10
+ const schema = options.schema;
11
+ const adapter = options.adapter;
12
+ const namespace = options.namespace;
13
+
14
+ validateNamespace(namespace);
15
+
16
+ // Per-slot serialized queue: slot -> tail promise. Retained until every accepted operation settles.
17
+ const slotQueues = new Map<string, Promise<void>>();
18
+ // Active operation promises for flush() snapshotting. Each flush gets its own immutable snapshot.
19
+ const active = new Set<Promise<void>>();
20
+
21
+ let disposed = false;
22
+
23
+ function track<T>(p: Promise<T>): Promise<T> {
24
+ const wrapped = p.finally(() => {
25
+ active.delete(wrapped as unknown as Promise<void>);
26
+ });
27
+ active.add(wrapped as unknown as Promise<void>);
28
+ return wrapped;
29
+ }
30
+
31
+ function enqueue<T>(slot: string, op: () => Promise<T>): Promise<T> {
32
+ // Acceptance boundary is the successful public method call; already-accepted ops are never cancelled by later dispose.
33
+ const tail = slotQueues.get(slot) ?? Promise.resolve();
34
+ let resolveTail: () => void = () => {};
35
+ const nextTail = new Promise<void>((res) => {
36
+ resolveTail = res;
37
+ });
38
+ slotQueues.set(slot, nextTail);
39
+
40
+ const run = tail
41
+ .catch(() => {
42
+ // Previous failure does not block next; continue
43
+ })
44
+ .then(() => op())
45
+ .finally(() => {
46
+ // Clean only when this tail is still the current tail; otherwise a newer operation has already replaced it.
47
+ if (slotQueues.get(slot) === nextTail) {
48
+ slotQueues.delete(slot);
49
+ }
50
+ resolveTail();
51
+ });
52
+
53
+ // Ensure nextTail mirrors run settlement without creating an unhandled rejection.
54
+ run.then(() => {}, () => {});
55
+
56
+ return track(run);
57
+ }
58
+
59
+ async function loadInternal(slot: string): Promise<GameSaveLoadResult<TData>> {
60
+ validateSlot(slot);
61
+ const key = storageKey(namespace, slot);
62
+ let raw: string | undefined;
63
+ try {
64
+ raw = await adapter.read(key);
65
+ } catch (cause) {
66
+ throw storageError(`backend read failed for slot "${slot}"`, 'BACKEND_READ_FAILED', {
67
+ operation: 'load',
68
+ namespace,
69
+ slot,
70
+ cause,
71
+ });
72
+ }
73
+ // GS-STORAGE-01: read-only legacy fallback — pre-upgrade records load
74
+ // without any rewrite. A v2 miss costs one extra read for heritage.
75
+ if (raw === undefined) {
76
+ try {
77
+ raw = await adapter.read(legacyStorageKey(namespace, slot));
78
+ } catch (cause) {
79
+ throw storageError(`backend read failed for slot "${slot}"`, 'BACKEND_READ_FAILED', {
80
+ operation: 'load',
81
+ namespace,
82
+ slot,
83
+ cause,
84
+ });
85
+ }
86
+ }
87
+ if (raw === undefined) {
88
+ const data = createDefaultData(schema);
89
+ return { status: 'default', data };
90
+ }
91
+
92
+ // F3: measure raw UTF-8 byte length before JSON.parse and reject oversized records.
93
+ const rawBytes = utf8ByteLength(raw);
94
+ if (rawBytes > STORAGE_LIMITS.MAX_SERIALIZED_BYTES) {
95
+ throw storageError(`stored record for slot "${slot}" exceeds ${STORAGE_LIMITS.MAX_SERIALIZED_BYTES} bytes (got ${rawBytes})`, 'SIZE_EXCEEDED', {
96
+ operation: 'load',
97
+ namespace,
98
+ slot,
99
+ schemaId: schema.id,
100
+ schemaVersion: schema.version,
101
+ path: 'envelope',
102
+ });
103
+ }
104
+
105
+ // Validate loaded bytes as untrusted before migration
106
+ let envelope: StoredGameEnvelope;
107
+ try {
108
+ envelope = parseEnvelope(raw);
109
+ } catch (cause) {
110
+ if (cause instanceof GameStorageError) {
111
+ throw new GameStorageError((cause as GameStorageError).message, {
112
+ code: (cause as GameStorageError).code,
113
+ operation: 'load',
114
+ namespace,
115
+ slot,
116
+ schemaId: schema.id,
117
+ schemaVersion: schema.version,
118
+ path: (cause as GameStorageError).path,
119
+ cause: (cause as GameStorageError).cause ?? cause,
120
+ });
121
+ }
122
+ throw storageError(`corrupt envelope for slot "${slot}"`, 'CORRUPT_ENVELOPE', {
123
+ operation: 'load',
124
+ namespace,
125
+ slot,
126
+ cause,
127
+ });
128
+ }
129
+
130
+ if (envelope.schemaId !== schema.id) {
131
+ throw storageError(`schema id mismatch: expected "${schema.id}" got "${envelope.schemaId}"`, 'SCHEMA_ID_MISMATCH', {
132
+ operation: 'load',
133
+ namespace,
134
+ slot,
135
+ schemaId: schema.id,
136
+ schemaVersion: schema.version,
137
+ });
138
+ }
139
+ if (!Number.isSafeInteger(envelope.schemaVersion) || envelope.schemaVersion < 1) {
140
+ throw storageError(`corrupt envelope: invalid schemaVersion`, 'CORRUPT_ENVELOPE', {
141
+ operation: 'load',
142
+ namespace,
143
+ slot,
144
+ schemaId: schema.id,
145
+ });
146
+ }
147
+ if (envelope.schemaVersion > schema.version) {
148
+ throw storageError(`stored version ${envelope.schemaVersion} is newer than current ${schema.version}`, 'FUTURE_VERSION', {
149
+ operation: 'load',
150
+ namespace,
151
+ slot,
152
+ schemaId: schema.id,
153
+ schemaVersion: schema.version,
154
+ });
155
+ }
156
+
157
+ // F3: bounded plain-data validation of the loaded payload BEFORE any migration sees it.
158
+ try {
159
+ cloneAndValidatePlainData(envelope.payload, 'payload');
160
+ } catch (cause) {
161
+ if (cause instanceof GameStorageError) {
162
+ throw new GameStorageError((cause as GameStorageError).message, {
163
+ code: (cause as GameStorageError).code,
164
+ operation: 'load',
165
+ namespace,
166
+ slot,
167
+ schemaId: schema.id,
168
+ schemaVersion: schema.version,
169
+ path: (cause as GameStorageError).path,
170
+ cause: (cause as GameStorageError).cause ?? cause,
171
+ });
172
+ }
173
+ throw storageError(`payload validation failed`, 'VALIDATION_FAILED', {
174
+ operation: 'load',
175
+ namespace,
176
+ slot,
177
+ cause,
178
+ });
179
+ }
180
+
181
+ // Migrate if needed — each migration receives a deeply frozen engine-owned input and its output is bounded-cloned/frozen.
182
+ let payload: unknown = envelope.payload;
183
+ let fromVersion = envelope.schemaVersion;
184
+ let toVersion = schema.version;
185
+ let migrated = false;
186
+ if (fromVersion !== toVersion) {
187
+ try {
188
+ const result = migratePayload(schema, fromVersion, payload);
189
+ payload = result.data;
190
+ fromVersion = result.fromVersion;
191
+ toVersion = result.toVersion;
192
+ migrated = true;
193
+ } catch (cause) {
194
+ if (cause instanceof GameStorageError) {
195
+ throw new GameStorageError((cause as GameStorageError).message, {
196
+ code: (cause as GameStorageError).code,
197
+ operation: 'load',
198
+ namespace,
199
+ slot,
200
+ schemaId: schema.id,
201
+ schemaVersion: schema.version,
202
+ path: (cause as GameStorageError).path,
203
+ cause: (cause as GameStorageError).cause ?? cause,
204
+ });
205
+ }
206
+ throw storageError(`migration failed`, 'MIGRATION_FAILED', {
207
+ operation: 'load',
208
+ namespace,
209
+ slot,
210
+ cause,
211
+ });
212
+ }
213
+ }
214
+
215
+ // Final validation — never overwrites stored record on failure
216
+ let data: TData;
217
+ try {
218
+ data = validateCurrentData(schema, payload, 'payload');
219
+ } catch (cause) {
220
+ if (cause instanceof GameStorageError) {
221
+ throw new GameStorageError(cause.message, {
222
+ code: 'VALIDATION_FAILED',
223
+ operation: 'load',
224
+ namespace,
225
+ slot,
226
+ schemaId: schema.id,
227
+ schemaVersion: schema.version,
228
+ path: cause.path,
229
+ cause: cause.cause ?? cause,
230
+ });
231
+ }
232
+ throw storageError(`final validation failed`, 'VALIDATION_FAILED', {
233
+ operation: 'load',
234
+ namespace,
235
+ slot,
236
+ cause,
237
+ });
238
+ }
239
+
240
+ if (migrated) {
241
+ return { status: 'migrated', data, fromVersion: envelope.schemaVersion, toVersion };
242
+ }
243
+ return { status: 'stored', data };
244
+ }
245
+
246
+ async function saveInternal(slot: string, data: TData): Promise<void> {
247
+ validateSlot(slot);
248
+ // GS-STORAGE-02: save() already validated, normalized, cloned, and
249
+ // froze this snapshot at acceptance — re-running domain validation
250
+ // here would double-apply normalizing validators. Serialization still
251
+ // bounds-checks the bytes at its own boundary.
252
+ const envelope: StoredGameEnvelope = {
253
+ format: STORAGE_ENVELOPE_FORMAT,
254
+ schemaId: schema.id,
255
+ schemaVersion: schema.version,
256
+ savedAtMs: Date.now(),
257
+ payload: data,
258
+ };
259
+ let serialized: string;
260
+ try {
261
+ serialized = serializeEnvelope(envelope);
262
+ } catch (cause) {
263
+ if (cause instanceof GameStorageError) {
264
+ throw new GameStorageError(cause.message, {
265
+ code: cause.code === 'SIZE_EXCEEDED' || cause.code === 'DEPTH_EXCEEDED' ? cause.code : 'SERIALIZATION_FAILED',
266
+ operation: 'save',
267
+ namespace,
268
+ slot,
269
+ schemaId: schema.id,
270
+ schemaVersion: schema.version,
271
+ path: cause.path,
272
+ cause: cause.cause ?? cause,
273
+ });
274
+ }
275
+ throw storageError(`serialization failed for slot "${slot}"`, 'SERIALIZATION_FAILED', {
276
+ operation: 'save',
277
+ namespace,
278
+ slot,
279
+ cause,
280
+ });
281
+ }
282
+ const key = storageKey(namespace, slot);
283
+ try {
284
+ await adapter.write(key, serialized);
285
+ } catch (cause) {
286
+ throw storageError(`backend write failed for slot "${slot}"`, 'BACKEND_WRITE_FAILED', {
287
+ operation: 'save',
288
+ namespace,
289
+ slot,
290
+ cause,
291
+ });
292
+ }
293
+ }
294
+
295
+ async function removeInternal(slot: string): Promise<void> {
296
+ validateSlot(slot);
297
+ // GS-STORAGE-01: delete the versioned record and its legacy heritage.
298
+ // Heritage deletion matches v1 shared-fate semantics for colliding
299
+ // pairs and converges to independence as pairs save under v2.
300
+ const key = storageKey(namespace, slot);
301
+ const legacyKey = legacyStorageKey(namespace, slot);
302
+ try {
303
+ await adapter.remove(key);
304
+ if (legacyKey !== key) {
305
+ await adapter.remove(legacyKey);
306
+ }
307
+ } catch (cause) {
308
+ throw storageError(`backend remove failed for slot "${slot}"`, 'BACKEND_REMOVE_FAILED', {
309
+ operation: 'remove',
310
+ namespace,
311
+ slot,
312
+ cause,
313
+ });
314
+ }
315
+ }
316
+
317
+ const store: GameSaveStore<TData> = {
318
+ get disposed() {
319
+ return disposed;
320
+ },
321
+ load(slot: string): Promise<GameSaveLoadResult<TData>> {
322
+ if (disposed) {
323
+ return Promise.reject(storageError(`store is disposed`, 'DISPOSED', { operation: 'load', namespace, slot }));
324
+ }
325
+ try {
326
+ validateSlot(slot);
327
+ } catch (e) {
328
+ return Promise.reject(e);
329
+ }
330
+ return enqueue(slot, () => loadInternal(slot));
331
+ },
332
+ save(slot: string, data: TData): Promise<void> {
333
+ if (disposed) {
334
+ return Promise.reject(storageError(`store is disposed`, 'DISPOSED', { operation: 'save', namespace, slot }));
335
+ }
336
+ try {
337
+ validateSlot(slot);
338
+ } catch (e) {
339
+ return Promise.reject(e);
340
+ }
341
+ // Snapshot synchronously so caller mutation after save() does not affect stored bytes
342
+ let snapshot: TData;
343
+ try {
344
+ snapshot = validateCurrentData(schema, data, 'data');
345
+ } catch (e) {
346
+ return Promise.reject(e);
347
+ }
348
+ return enqueue(slot, () => saveInternal(slot, snapshot));
349
+ },
350
+ remove(slot: string): Promise<void> {
351
+ if (disposed) {
352
+ return Promise.reject(storageError(`store is disposed`, 'DISPOSED', { operation: 'remove', namespace, slot }));
353
+ }
354
+ try {
355
+ validateSlot(slot);
356
+ } catch (e) {
357
+ return Promise.reject(e);
358
+ }
359
+ return enqueue(slot, () => removeInternal(slot));
360
+ },
361
+ delete(slot: string): Promise<void> {
362
+ return (store as GameSaveStore<TData>).remove(slot);
363
+ },
364
+ async flush(): Promise<void> {
365
+ // Snapshot the currently tracked operation promises; do not let operations accepted after the call delay this flush.
366
+ const snapshot = Array.from(active);
367
+ if (snapshot.length === 0) return;
368
+ await Promise.allSettled(snapshot);
369
+ },
370
+ dispose(): void {
371
+ if (disposed) return;
372
+ disposed = true;
373
+ // Do not use generation to suppress already-accepted Promise results.
374
+ // Retain queue/tail ownership until every accepted operation settles; tails clean themselves when still current.
375
+ // New work is rejected at the public boundary above.
376
+ },
377
+ };
378
+
379
+ return store;
380
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * V1 storage contracts — game-owned projections, engine envelope, adapter.
3
+ */
4
+
5
+ export interface GameSaveSchema<TData> {
6
+ readonly id: string;
7
+ readonly version: number;
8
+ readonly createDefault: () => TData;
9
+ readonly validate: (value: unknown) => TData;
10
+ readonly migrations: Readonly<Record<number, (value: unknown) => unknown>>;
11
+ }
12
+
13
+ export interface GameStorageAdapter {
14
+ read(key: string): Promise<string | undefined>;
15
+ write(key: string, value: string): Promise<void>;
16
+ remove(key: string): Promise<void>;
17
+ }
18
+
19
+ export interface StoredGameEnvelope {
20
+ readonly format: 'rn-gamekit.save';
21
+ readonly schemaId: string;
22
+ readonly schemaVersion: number;
23
+ readonly savedAtMs: number;
24
+ readonly payload: unknown;
25
+ }
26
+
27
+ export type GameSaveLoadStatus = 'default' | 'stored' | 'migrated';
28
+
29
+ export interface GameSaveLoadResult<TData> {
30
+ readonly status: GameSaveLoadStatus;
31
+ readonly data: TData;
32
+ /** Present only when status === 'migrated' */
33
+ readonly fromVersion?: number;
34
+ /** Present only when status === 'migrated' */
35
+ readonly toVersion?: number;
36
+ }
37
+
38
+ export interface CreateGameSaveStoreOptions<TData> {
39
+ readonly schema: GameSaveSchema<TData>;
40
+ readonly adapter: GameStorageAdapter;
41
+ readonly namespace: string;
42
+ }
43
+
44
+ export interface GameSaveStore<TData> {
45
+ load(slot: string): Promise<GameSaveLoadResult<TData>>;
46
+ save(slot: string, data: TData): Promise<void>;
47
+ /**
48
+ * Delete a slot's record (GS-STORAGE-04): `remove` is the canonical
49
+ * verb; `delete` is a same-behavior alias kept for preview
50
+ * compatibility. Removes the versioned record and its legacy heritage.
51
+ */
52
+ remove(slot: string): Promise<void>;
53
+ /** Alias for remove — both names are accepted. */
54
+ delete(slot: string): Promise<void>;
55
+ /**
56
+ * Drain operations accepted before the call (GS-STORAGE-04): snapshots
57
+ * the in-flight set, waits for every snapshot member to settle —
58
+ * including failures — and never waits for later work. Always resolves
59
+ * (never rejects): each operation's own returned promise carries its
60
+ * success or failure. Per-slot ordering is per store instance, not a
61
+ * global cross-store lock; disposal rejects new work while accepted
62
+ * work still settles normally.
63
+ */
64
+ flush(): Promise<void>;
65
+ dispose(): void;
66
+ readonly disposed: boolean;
67
+ }
68
+
69
+ export const STORAGE_LIMITS = {
70
+ MAX_SERIALIZED_BYTES: 262_144, // 256 KiB per slot
71
+ MAX_DEPTH: 16,
72
+ MAX_NODES: 4096,
73
+ MAX_ARRAY_LENGTH: 1024,
74
+ MAX_OBJECT_FIELDS: 1024,
75
+ MAX_STRING_LENGTH: 16_384,
76
+ } as const;
@@ -0,0 +1,102 @@
1
+ import { GameStorageError, storageError } from './errors';
2
+
3
+ const SCHEMA_ID_RE = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/i;
4
+ const NAMESPACE_RE = /^[a-zA-Z0-9._-]+$/;
5
+ const SLOT_RE = /^[a-zA-Z0-9._-]+$/;
6
+
7
+ export function validateSchemaId(id: string): void {
8
+ if (typeof id !== 'string' || id.length === 0 || id.length > 128 || id.includes('\0')) {
9
+ throw storageError(`schema id must be a non-empty string 1..128 without null bytes`, 'INVALID_SCHEMA_ID', {
10
+ schemaId: String(id),
11
+ });
12
+ }
13
+ if (!SCHEMA_ID_RE.test(id)) {
14
+ throw storageError(`schema id "${id}" must be namespaced (e.g. com.example.game.save)`, 'INVALID_SCHEMA_ID', {
15
+ schemaId: id,
16
+ });
17
+ }
18
+ if (id.startsWith('.') || id.endsWith('.') || id.includes('..')) {
19
+ throw storageError(`schema id "${id}" has invalid dot placement`, 'INVALID_SCHEMA_ID', { schemaId: id });
20
+ }
21
+ }
22
+
23
+ export function validateVersion(version: number): void {
24
+ if (!Number.isSafeInteger(version) || version < 1) {
25
+ throw storageError(`schema version must be a positive safe integer`, 'INVALID_SCHEMA_VERSION', {
26
+ schemaVersion: version,
27
+ });
28
+ }
29
+ }
30
+
31
+ export function validateNamespace(namespace: string): void {
32
+ if (typeof namespace !== 'string' || namespace.length === 0 || namespace.length > 64 || namespace.includes('\0')) {
33
+ throw storageError(`namespace must be a non-empty string 1..64`, 'INVALID_NAMESPACE', { namespace });
34
+ }
35
+ if (!NAMESPACE_RE.test(namespace)) {
36
+ throw storageError(`namespace "${namespace}" contains invalid characters`, 'INVALID_NAMESPACE', { namespace });
37
+ }
38
+ }
39
+
40
+ export function validateSlot(slot: string): void {
41
+ if (typeof slot !== 'string' || slot.length === 0 || slot.length > 64 || slot.includes('\0')) {
42
+ throw storageError(`slot must be a non-empty string 1..64`, 'INVALID_SLOT', { slot });
43
+ }
44
+ if (!SLOT_RE.test(slot)) {
45
+ throw storageError(`slot "${slot}" contains invalid characters`, 'INVALID_SLOT', { slot });
46
+ }
47
+ }
48
+
49
+ export function validateMigrations(
50
+ migrations: Readonly<Record<number, (value: unknown) => unknown>>,
51
+ currentVersion: number,
52
+ ): void {
53
+ if (migrations === null || typeof migrations !== 'object' || Array.isArray(migrations)) {
54
+ throw new GameStorageError('migrations must be a plain record', { code: 'INVALID_MIGRATION' });
55
+ }
56
+ const proto = Object.getPrototypeOf(migrations);
57
+ if (proto !== Object.prototype && proto !== null) {
58
+ throw new GameStorageError('migrations must be a plain record', { code: 'INVALID_MIGRATION' });
59
+ }
60
+ for (const [rawKey, fn] of Object.entries(migrations)) {
61
+ const n = Number(rawKey);
62
+ // GS-STORAGE-03: keys must be canonical — '01' and '1e0' normalize to
63
+ // 1 but would never be found by the numeric lookup, and two spellings
64
+ // of one version must not both be accepted (the canonicality check
65
+ // rejects every non-canonical spelling, so each version appears once).
66
+ if (!Number.isSafeInteger(n) || n < 1 || n >= currentVersion || String(n) !== rawKey) {
67
+ throw storageError(`migration key "${rawKey}" must be a canonical integer 1..${currentVersion - 1}`, 'INVALID_MIGRATION', {
68
+ path: `migrations.${rawKey}`,
69
+ });
70
+ }
71
+ if (typeof fn !== 'function') {
72
+ throw storageError(`migration ${n} must be a function`, 'INVALID_MIGRATION', { path: `migrations.${n}` });
73
+ }
74
+ }
75
+ }
76
+
77
+ export function storageKey(namespace: string, slot: string): string {
78
+ // GS-STORAGE-01: length-prefixed tuple — dots, slashes, and colons
79
+ // inside names can never collide across pairs. Format-versioned so
80
+ // future schemes route without guessing.
81
+ return `rn-gamekit.storage/v2/${namespace.length}:${namespace}/${slot.length}:${slot}`;
82
+ }
83
+
84
+ /**
85
+ * Recovery/compatibility policy (GS-STORAGE-01, preview release):
86
+ *
87
+ * - Writes go to v2 keys only. Reads try v2, then fall back to the legacy
88
+ * dotted key, so pre-upgrade records load without any rewrite.
89
+ * - The first v2 save for a slot lazily migrates it; no blanket rewrite
90
+ * ever runs, and legacy records are never deleted after an ambiguous
91
+ * read (a legacy key cannot identify which pair wrote it).
92
+ * - `remove` deletes both the v2 and legacy keys. Under v1, colliding
93
+ * pairs already shared one record (shared fate on delete); v2 converges
94
+ * to independence as each pair saves. A pair that never saves under v2
95
+ * keeps reading its legacy record until it does.
96
+ * - A legacy collision (`('game.a','b')` vs `('game','a.b')`) cannot be
97
+ * disambiguated from the key alone: both pairs read the same heritage
98
+ * record, exactly as v1 behaved. New writes never collide.
99
+ */
100
+ export function legacyStorageKey(namespace: string, slot: string): string {
101
+ return `rn-gamekit.storage.${namespace}.${slot}`;
102
+ }
package/src/storage.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './storage/index';
package/src/testing.ts CHANGED
@@ -59,3 +59,7 @@ export class ManualFrameDriver implements FrameDriver {
59
59
  callback(timestampMs);
60
60
  }
61
61
  }
62
+
63
+ // Test-only tilemap chunk-read instrumentation (T16-RF4): intentionally NOT
64
+ // re-exported from rn-gamekit/tilemap.
65
+ export { __resetChunkReadStats, __chunkReadCount } from './tilemap/chunkStats';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Test-only chunk-read instrumentation (T16-F6/T16-RF4).
3
+ *
4
+ * Lives OUTSIDE the public `rn-gamekit/tilemap` entry: the counters are
5
+ * re-exported only through the `rn-gamekit/testing` subpath so published
6
+ * diagnostics never leak double-underscore seams into app code.
7
+ */
8
+
9
+ let chunkReads = 0;
10
+
11
+ /** Internal: record one chunk-region lookup. */
12
+ export function recordChunkRead(): void {
13
+ chunkReads += 1;
14
+ }
15
+
16
+ /** Test-only: reset the chunk-visit counter. */
17
+ export function __resetChunkReadStats(): void {
18
+ chunkReads = 0;
19
+ }
20
+
21
+ /** Test-only: number of chunk lookups since the last reset. */
22
+ export function __chunkReadCount(): number {
23
+ return chunkReads;
24
+ }