@unrulysystems/native-motion-core 0.1.0-alpha.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 (326) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +48 -0
  4. package/dist/clock.cjs +71 -0
  5. package/dist/clock.d.cts +23 -0
  6. package/dist/clock.d.ts +23 -0
  7. package/dist/clock.js +66 -0
  8. package/dist/collect-reads.cjs +68 -0
  9. package/dist/collect-reads.d.cts +23 -0
  10. package/dist/collect-reads.d.ts +23 -0
  11. package/dist/collect-reads.js +62 -0
  12. package/dist/component/boundedArray.cjs +74 -0
  13. package/dist/component/boundedArray.d.cts +25 -0
  14. package/dist/component/boundedArray.d.ts +25 -0
  15. package/dist/component/boundedArray.js +68 -0
  16. package/dist/component/index.cjs +57 -0
  17. package/dist/component/index.d.cts +12 -0
  18. package/dist/component/index.d.ts +12 -0
  19. package/dist/component/index.js +22 -0
  20. package/dist/component/orchestration.cjs +129 -0
  21. package/dist/component/orchestration.d.cts +66 -0
  22. package/dist/component/orchestration.d.ts +66 -0
  23. package/dist/component/orchestration.js +124 -0
  24. package/dist/component/resolve.cjs +104 -0
  25. package/dist/component/resolve.d.cts +11 -0
  26. package/dist/component/resolve.d.ts +11 -0
  27. package/dist/component/resolve.js +97 -0
  28. package/dist/component/transition.cjs +352 -0
  29. package/dist/component/transition.d.cts +33 -0
  30. package/dist/component/transition.d.ts +33 -0
  31. package/dist/component/transition.js +339 -0
  32. package/dist/component/types.cjs +114 -0
  33. package/dist/component/types.d.cts +60 -0
  34. package/dist/component/types.d.ts +60 -0
  35. package/dist/component/types.js +111 -0
  36. package/dist/component/validate.cjs +1015 -0
  37. package/dist/component/validate.d.cts +37 -0
  38. package/dist/component/validate.d.ts +37 -0
  39. package/dist/component/validate.js +1002 -0
  40. package/dist/component/variants.cjs +333 -0
  41. package/dist/component/variants.d.cts +106 -0
  42. package/dist/component/variants.d.ts +106 -0
  43. package/dist/component/variants.js +321 -0
  44. package/dist/config/constants.cjs +41 -0
  45. package/dist/config/constants.d.cts +29 -0
  46. package/dist/config/constants.d.ts +29 -0
  47. package/dist/config/constants.js +38 -0
  48. package/dist/delay.cjs +72 -0
  49. package/dist/delay.d.cts +26 -0
  50. package/dist/delay.d.ts +26 -0
  51. package/dist/delay.js +70 -0
  52. package/dist/derived.cjs +143 -0
  53. package/dist/derived.d.cts +41 -0
  54. package/dist/derived.d.ts +41 -0
  55. package/dist/derived.js +139 -0
  56. package/dist/driver/index.cjs +22 -0
  57. package/dist/driver/index.d.cts +7 -0
  58. package/dist/driver/index.d.ts +7 -0
  59. package/dist/driver/index.js +14 -0
  60. package/dist/driver/keyframeTiming.cjs +130 -0
  61. package/dist/driver/keyframeTiming.d.cts +18 -0
  62. package/dist/driver/keyframeTiming.d.ts +18 -0
  63. package/dist/driver/keyframeTiming.js +124 -0
  64. package/dist/driver/keyframeTimingConfig.cjs +24 -0
  65. package/dist/driver/keyframeTimingConfig.d.cts +5 -0
  66. package/dist/driver/keyframeTimingConfig.d.ts +5 -0
  67. package/dist/driver/keyframeTimingConfig.js +22 -0
  68. package/dist/driver/prepare.cjs +459 -0
  69. package/dist/driver/prepare.d.cts +4 -0
  70. package/dist/driver/prepare.d.ts +4 -0
  71. package/dist/driver/prepare.js +454 -0
  72. package/dist/driver/reference.cjs +762 -0
  73. package/dist/driver/reference.d.cts +53 -0
  74. package/dist/driver/reference.d.ts +53 -0
  75. package/dist/driver/reference.js +757 -0
  76. package/dist/driver/step.cjs +55 -0
  77. package/dist/driver/step.d.cts +9 -0
  78. package/dist/driver/step.d.ts +9 -0
  79. package/dist/driver/step.js +52 -0
  80. package/dist/driver/tiers.cjs +40 -0
  81. package/dist/driver/tiers.d.cts +2 -0
  82. package/dist/driver/tiers.d.ts +2 -0
  83. package/dist/driver/tiers.js +37 -0
  84. package/dist/driver/types.cjs +9 -0
  85. package/dist/driver/types.d.cts +68 -0
  86. package/dist/driver/types.d.ts +68 -0
  87. package/dist/driver/types.js +8 -0
  88. package/dist/external-animation-ledger.cjs +1250 -0
  89. package/dist/external-animation-ledger.d.cts +416 -0
  90. package/dist/external-animation-ledger.d.ts +416 -0
  91. package/dist/external-animation-ledger.js +1243 -0
  92. package/dist/gesture/directionLock.cjs +25 -0
  93. package/dist/gesture/directionLock.d.cts +8 -0
  94. package/dist/gesture/directionLock.d.ts +8 -0
  95. package/dist/gesture/directionLock.js +21 -0
  96. package/dist/gesture/dragConfig.cjs +634 -0
  97. package/dist/gesture/dragConfig.d.cts +298 -0
  98. package/dist/gesture/dragConfig.d.ts +298 -0
  99. package/dist/gesture/dragConfig.js +624 -0
  100. package/dist/gesture/elastic.cjs +44 -0
  101. package/dist/gesture/elastic.d.cts +4 -0
  102. package/dist/gesture/elastic.d.ts +4 -0
  103. package/dist/gesture/elastic.js +39 -0
  104. package/dist/gesture/handoffSession.cjs +161 -0
  105. package/dist/gesture/handoffSession.d.cts +35 -0
  106. package/dist/gesture/handoffSession.d.ts +35 -0
  107. package/dist/gesture/handoffSession.js +158 -0
  108. package/dist/gesture/index.cjs +37 -0
  109. package/dist/gesture/index.d.cts +12 -0
  110. package/dist/gesture/index.d.ts +12 -0
  111. package/dist/gesture/index.js +11 -0
  112. package/dist/gesture/projection.cjs +95 -0
  113. package/dist/gesture/projection.d.cts +7 -0
  114. package/dist/gesture/projection.d.ts +7 -0
  115. package/dist/gesture/projection.js +87 -0
  116. package/dist/gesture/session.cjs +162 -0
  117. package/dist/gesture/session.d.cts +29 -0
  118. package/dist/gesture/session.d.ts +29 -0
  119. package/dist/gesture/session.js +158 -0
  120. package/dist/gesture/types.cjs +5 -0
  121. package/dist/gesture/types.d.cts +13 -0
  122. package/dist/gesture/types.d.ts +13 -0
  123. package/dist/gesture/types.js +4 -0
  124. package/dist/gesture/viewportConstraints.cjs +38 -0
  125. package/dist/gesture/viewportConstraints.d.cts +6 -0
  126. package/dist/gesture/viewportConstraints.d.ts +6 -0
  127. package/dist/gesture/viewportConstraints.js +34 -0
  128. package/dist/graph.cjs +226 -0
  129. package/dist/graph.d.cts +2 -0
  130. package/dist/graph.d.ts +2 -0
  131. package/dist/graph.js +223 -0
  132. package/dist/index.cjs +140 -0
  133. package/dist/index.d.cts +33 -0
  134. package/dist/index.d.ts +33 -0
  135. package/dist/index.js +70 -0
  136. package/dist/inertia.cjs +214 -0
  137. package/dist/inertia.d.cts +53 -0
  138. package/dist/inertia.d.ts +53 -0
  139. package/dist/inertia.js +212 -0
  140. package/dist/instant.cjs +66 -0
  141. package/dist/instant.d.cts +16 -0
  142. package/dist/instant.d.ts +16 -0
  143. package/dist/instant.js +63 -0
  144. package/dist/internal-driver.cjs +81 -0
  145. package/dist/internal-driver.d.cts +19 -0
  146. package/dist/internal-driver.d.ts +19 -0
  147. package/dist/internal-driver.js +35 -0
  148. package/dist/keyframes.cjs +191 -0
  149. package/dist/keyframes.d.cts +13 -0
  150. package/dist/keyframes.d.ts +13 -0
  151. package/dist/keyframes.js +188 -0
  152. package/dist/layout/commitDetector.cjs +67 -0
  153. package/dist/layout/commitDetector.d.cts +21 -0
  154. package/dist/layout/commitDetector.d.ts +21 -0
  155. package/dist/layout/commitDetector.js +64 -0
  156. package/dist/layout/compose.cjs +67 -0
  157. package/dist/layout/compose.d.cts +30 -0
  158. package/dist/layout/compose.d.ts +30 -0
  159. package/dist/layout/compose.js +65 -0
  160. package/dist/layout/constants.cjs +13 -0
  161. package/dist/layout/constants.d.cts +6 -0
  162. package/dist/layout/constants.d.ts +6 -0
  163. package/dist/layout/constants.js +10 -0
  164. package/dist/layout/identity.cjs +700 -0
  165. package/dist/layout/identity.d.cts +67 -0
  166. package/dist/layout/identity.d.ts +67 -0
  167. package/dist/layout/identity.js +698 -0
  168. package/dist/layout/index.cjs +36 -0
  169. package/dist/layout/index.d.cts +18 -0
  170. package/dist/layout/index.d.ts +18 -0
  171. package/dist/layout/index.js +13 -0
  172. package/dist/layout/measure.cjs +81 -0
  173. package/dist/layout/measure.d.cts +43 -0
  174. package/dist/layout/measure.d.ts +43 -0
  175. package/dist/layout/measure.js +78 -0
  176. package/dist/layout/projection.cjs +99 -0
  177. package/dist/layout/projection.d.cts +23 -0
  178. package/dist/layout/projection.d.ts +23 -0
  179. package/dist/layout/projection.js +97 -0
  180. package/dist/layout/scroll.cjs +27 -0
  181. package/dist/layout/scroll.d.cts +13 -0
  182. package/dist/layout/scroll.d.ts +13 -0
  183. package/dist/layout/scroll.js +24 -0
  184. package/dist/layout/session.cjs +207 -0
  185. package/dist/layout/session.d.cts +73 -0
  186. package/dist/layout/session.d.ts +73 -0
  187. package/dist/layout/session.js +205 -0
  188. package/dist/layout/tree.cjs +826 -0
  189. package/dist/layout/tree.d.cts +70 -0
  190. package/dist/layout/tree.d.ts +70 -0
  191. package/dist/layout/tree.js +823 -0
  192. package/dist/layout/types.cjs +36 -0
  193. package/dist/layout/types.d.cts +31 -0
  194. package/dist/layout/types.d.ts +31 -0
  195. package/dist/layout/types.js +35 -0
  196. package/dist/motion-arc.cjs +184 -0
  197. package/dist/motion-arc.d.cts +78 -0
  198. package/dist/motion-arc.d.ts +78 -0
  199. package/dist/motion-arc.js +183 -0
  200. package/dist/motion-mix.cjs +205 -0
  201. package/dist/motion-mix.d.cts +3 -0
  202. package/dist/motion-mix.d.ts +3 -0
  203. package/dist/motion-mix.js +202 -0
  204. package/dist/motion-value-driver-port.cjs +636 -0
  205. package/dist/motion-value-driver-port.d.cts +406 -0
  206. package/dist/motion-value-driver-port.d.ts +406 -0
  207. package/dist/motion-value-driver-port.js +627 -0
  208. package/dist/motion-value.cjs +189 -0
  209. package/dist/motion-value.d.cts +51 -0
  210. package/dist/motion-value.d.ts +51 -0
  211. package/dist/motion-value.js +185 -0
  212. package/dist/presence/controller.cjs +657 -0
  213. package/dist/presence/controller.d.cts +6 -0
  214. package/dist/presence/controller.d.ts +6 -0
  215. package/dist/presence/controller.js +652 -0
  216. package/dist/presence/index.cjs +19 -0
  217. package/dist/presence/index.d.cts +4 -0
  218. package/dist/presence/index.d.ts +4 -0
  219. package/dist/presence/index.js +12 -0
  220. package/dist/presence/machine.cjs +50 -0
  221. package/dist/presence/machine.d.cts +10 -0
  222. package/dist/presence/machine.d.ts +10 -0
  223. package/dist/presence/machine.js +46 -0
  224. package/dist/presence/types.cjs +6 -0
  225. package/dist/presence/types.d.cts +32 -0
  226. package/dist/presence/types.d.ts +32 -0
  227. package/dist/presence/types.js +5 -0
  228. package/dist/repeat.cjs +311 -0
  229. package/dist/repeat.d.cts +174 -0
  230. package/dist/repeat.d.ts +174 -0
  231. package/dist/repeat.js +300 -0
  232. package/dist/spring.cjs +128 -0
  233. package/dist/spring.d.cts +18 -0
  234. package/dist/spring.d.ts +18 -0
  235. package/dist/spring.js +125 -0
  236. package/dist/subscriptions.cjs +74 -0
  237. package/dist/subscriptions.d.cts +19 -0
  238. package/dist/subscriptions.d.ts +19 -0
  239. package/dist/subscriptions.js +69 -0
  240. package/dist/subset/index.cjs +23 -0
  241. package/dist/subset/index.d.cts +6 -0
  242. package/dist/subset/index.d.ts +6 -0
  243. package/dist/subset/index.js +13 -0
  244. package/dist/subset/normalize.cjs +90 -0
  245. package/dist/subset/normalize.d.cts +10 -0
  246. package/dist/subset/normalize.d.ts +10 -0
  247. package/dist/subset/normalize.js +85 -0
  248. package/dist/subset/registry.cjs +259 -0
  249. package/dist/subset/registry.d.cts +25 -0
  250. package/dist/subset/registry.d.ts +25 -0
  251. package/dist/subset/registry.js +256 -0
  252. package/dist/subset/resolve.cjs +98 -0
  253. package/dist/subset/resolve.d.cts +24 -0
  254. package/dist/subset/resolve.d.ts +24 -0
  255. package/dist/subset/resolve.js +90 -0
  256. package/dist/timing.cjs +206 -0
  257. package/dist/timing.d.cts +17 -0
  258. package/dist/timing.d.ts +17 -0
  259. package/dist/timing.js +201 -0
  260. package/dist/transformTemplate.cjs +219 -0
  261. package/dist/transformTemplate.d.cts +29 -0
  262. package/dist/transformTemplate.d.ts +29 -0
  263. package/dist/transformTemplate.js +215 -0
  264. package/dist/transition.cjs +231 -0
  265. package/dist/transition.d.cts +89 -0
  266. package/dist/transition.d.ts +89 -0
  267. package/dist/transition.js +223 -0
  268. package/dist/types.cjs +4 -0
  269. package/dist/types.d.cts +175 -0
  270. package/dist/types.d.ts +175 -0
  271. package/dist/types.js +3 -0
  272. package/dist/value-types/color.cjs +220 -0
  273. package/dist/value-types/color.d.cts +19 -0
  274. package/dist/value-types/color.d.ts +19 -0
  275. package/dist/value-types/color.js +218 -0
  276. package/dist/value-types/complex.cjs +160 -0
  277. package/dist/value-types/complex.d.cts +19 -0
  278. package/dist/value-types/complex.d.ts +19 -0
  279. package/dist/value-types/complex.js +155 -0
  280. package/dist/value-types/constants.cjs +15 -0
  281. package/dist/value-types/constants.d.cts +6 -0
  282. package/dist/value-types/constants.d.ts +6 -0
  283. package/dist/value-types/constants.js +12 -0
  284. package/dist/value-types/discrete.cjs +62 -0
  285. package/dist/value-types/discrete.d.cts +4 -0
  286. package/dist/value-types/discrete.d.ts +4 -0
  287. package/dist/value-types/discrete.js +56 -0
  288. package/dist/value-types/index.cjs +59 -0
  289. package/dist/value-types/index.d.cts +14 -0
  290. package/dist/value-types/index.d.ts +14 -0
  291. package/dist/value-types/index.js +24 -0
  292. package/dist/value-types/measure-resolve.cjs +325 -0
  293. package/dist/value-types/measure-resolve.d.cts +91 -0
  294. package/dist/value-types/measure-resolve.d.ts +91 -0
  295. package/dist/value-types/measure-resolve.js +313 -0
  296. package/dist/value-types/mix.cjs +90 -0
  297. package/dist/value-types/mix.d.cts +27 -0
  298. package/dist/value-types/mix.d.ts +27 -0
  299. package/dist/value-types/mix.js +85 -0
  300. package/dist/value-types/named-colors.cjs +61 -0
  301. package/dist/value-types/named-colors.d.cts +2 -0
  302. package/dist/value-types/named-colors.d.ts +2 -0
  303. package/dist/value-types/named-colors.js +58 -0
  304. package/dist/value-types/numeric.cjs +86 -0
  305. package/dist/value-types/numeric.d.cts +16 -0
  306. package/dist/value-types/numeric.d.ts +16 -0
  307. package/dist/value-types/numeric.js +79 -0
  308. package/dist/worklet-layout/config/constants.js +39 -0
  309. package/dist/worklet-layout/layout/constants.js +11 -0
  310. package/dist/worklet-layout/layout/identity.js +699 -0
  311. package/dist/worklet-layout/layout/projection.js +98 -0
  312. package/dist/worklet-layout/layout/session.js +206 -0
  313. package/dist/worklet-layout/layout/tree.js +824 -0
  314. package/dist/worklet-layout/layout/types.js +36 -0
  315. package/dist/worklet-layout/spring.js +125 -0
  316. package/dist/worklet-layout/timing.js +201 -0
  317. package/dist/worklet-layout/transition.js +223 -0
  318. package/dist/worklet-layout.cjs +47 -0
  319. package/dist/worklet-layout.d.cts +15 -0
  320. package/dist/worklet-layout.d.ts +15 -0
  321. package/dist/worklet-layout.js +28 -0
  322. package/dist/wrap.cjs +7 -0
  323. package/dist/wrap.d.cts +1 -0
  324. package/dist/wrap.d.ts +1 -0
  325. package/dist/wrap.js +4 -0
  326. package/package.json +45 -0
@@ -0,0 +1,223 @@
1
+ 'worklet';
2
+ // @frame-path — executes inside the UI-runtime frame step; allocation-gated (REQ-DRIVER-021).
3
+ // Transition-config resolution (SPEC-SPRING REQ-SPRING-004/005/006/007/012). Turns a user transition
4
+ // config into concrete spring physics + the flags the generator/settle layers need. Host-agnostic
5
+ // (REQ-CORE-003): imports nothing from motion/react. Numerics are pinned to motion@12.42.2 and proven
6
+ // by resolution parity in transition.test.ts.
7
+ //
8
+ // findSpring (REQ-SPRING-004) uses the DETERMINISTIC closed form, not motion's 12-iteration
9
+ // Newton-Raphson: for velocity-free duration springs the closed form is the exact analytic root of
10
+ // motion's envelope, so it reproduces motion's resolution (proven to ~5e-5 relative in the tests) with
11
+ // no iteration.
12
+ import { springGenerator } from "./spring.js";
13
+ // Pinned defaults (REQ-SPRING-003 springDefaults).
14
+ const DEFAULT_PHYSICS = { stiffness: 100, damping: 10, mass: 1 };
15
+ const DEFAULT_DURATION_MS = 800;
16
+ const DEFAULT_BOUNCE = 0.3;
17
+ const SAFE_MIN = 0.001;
18
+ const clamp = (lo, hi, v) => Math.max(lo, Math.min(hi, v));
19
+ // REQ-SPRING-004 critical-root constant: the u solving e^(−u)·(u+1) = SAFE_MIN. Derived from SAFE_MIN
20
+ // by Newton (NOT hard-copied 9.2334…) so it stays consistent if SAFE_MIN changes.
21
+ const CRITICAL_ROOT = (() => {
22
+ let u = 9;
23
+ for (let i = 0; i < 40; i++)
24
+ u -= (Math.exp(-u) * (u + 1) - SAFE_MIN) / (-u * Math.exp(-u));
25
+ return u;
26
+ })();
27
+ // REQ-SPRING-004: duration+bounce → physics via the closed-form undamped frequency (velocity = 0).
28
+ // alloc-ok: lifecycle-edge — duration→physics resolution at command time.
29
+ function findSpring(durationMs, bounce, mass) {
30
+ const z = clamp(0.05, 1, 1 - bounce);
31
+ const tSec = clamp(0.01, 10, durationMs / 1000);
32
+ const omega = z < 1 ? Math.log(z / (SAFE_MIN * Math.sqrt(1 - z * z))) / (z * tSec) : CRITICAL_ROOT / tSec;
33
+ if (Number.isNaN(omega)) {
34
+ return { ...DEFAULT_PHYSICS, mass, durationMs: tSec * 1000 };
35
+ }
36
+ const stiffness = omega * omega * mass;
37
+ return { stiffness, damping: z * 2 * Math.sqrt(mass * stiffness), mass, durationMs: tSec * 1000 };
38
+ }
39
+ // REQ-SPRING-005: visualDuration+bounce → the DISTINCT path (no findSpring). bounce defaults to 0 here
40
+ // (intentional asymmetry with REQ-SPRING-004's 0.3). The 1.2 factor is load-bearing.
41
+ // alloc-ok: lifecycle-edge — visualDuration→physics resolution at command time.
42
+ function visualDurationSpring(visualDuration, bounce, mass) {
43
+ const root = (2 * Math.PI) / (visualDuration * 1.2);
44
+ const stiffness = root * root;
45
+ const damping = 2 * clamp(0.05, 1, 1 - (bounce ?? 0)) * Math.sqrt(stiffness);
46
+ return { stiffness, damping, mass };
47
+ }
48
+ const hasPhysicsKey = (c) => c.stiffness !== undefined || c.damping !== undefined || c.mass !== undefined;
49
+ /**
50
+ * Resolve a spring transition config to concrete physics + carry/settle flags (REQ-SPRING-006
51
+ * precedence). Presence is tested on `!== undefined`.
52
+ */
53
+ // alloc-ok: lifecycle-edge — config resolution at command time (REQ-SPRING-006 precedence).
54
+ export function resolveSpring(config) {
55
+ const mass = config.mass ?? DEFAULT_PHYSICS.mass;
56
+ // Any physics key ⇒ physics used directly; the entire duration family is ignored; velocity carries.
57
+ if (hasPhysicsKey(config)) {
58
+ return {
59
+ physics: {
60
+ stiffness: config.stiffness ?? DEFAULT_PHYSICS.stiffness,
61
+ damping: config.damping ?? DEFAULT_PHYSICS.damping,
62
+ mass,
63
+ },
64
+ carryVelocity: true,
65
+ isResolvedFromDuration: false,
66
+ calculatedDuration: null,
67
+ };
68
+ }
69
+ // Duration family — velocity is ZEROED (REQ-SPRING-007). visualDuration wins over duration, and a
70
+ // visualDuration-only config still takes the vd path (REQ-SPRING-006, post-plan-030); it MUST NOT
71
+ // fall back to default physics.
72
+ if (config.visualDuration !== undefined) {
73
+ return {
74
+ physics: visualDurationSpring(config.visualDuration, config.bounce, mass),
75
+ carryVelocity: false,
76
+ isResolvedFromDuration: false, // vd does NOT set the duration flag (REQ-SPRING-011)
77
+ calculatedDuration: null,
78
+ };
79
+ }
80
+ if (config.duration !== undefined || config.bounce !== undefined) {
81
+ const fs = findSpring(config.duration ?? DEFAULT_DURATION_MS, config.bounce ?? DEFAULT_BOUNCE, mass);
82
+ return {
83
+ physics: { stiffness: fs.stiffness, damping: fs.damping, mass: fs.mass },
84
+ carryVelocity: false,
85
+ isResolvedFromDuration: true,
86
+ calculatedDuration: fs.durationMs,
87
+ };
88
+ }
89
+ // Bare `{type:'spring'}` / empty ⇒ REQ-SPRING-003 defaults (100/10/1); a physics spring (carries).
90
+ return {
91
+ physics: { ...DEFAULT_PHYSICS },
92
+ carryVelocity: true,
93
+ isResolvedFromDuration: false,
94
+ calculatedDuration: null,
95
+ };
96
+ }
97
+ /**
98
+ * Build a `GeneratorFactory` from a target + config, honoring velocity carry/zero (REQ-SPRING-007).
99
+ * Duration/visualDuration springs drop the seed's incoming velocity; physics springs keep it —
100
+ * unless the config carries an AUTHORED `velocity`, which replaces it (REQ-API-053, T21): the pin's
101
+ * animateMotionValue spreads the authored transition over the live-velocity default
102
+ * (motion-dom@12.42.2 motion-value.ts:45-50), so the authored number wins on every start.
103
+ */
104
+ // alloc-ok: lifecycle-edge — factory construction at command time; sampling stays allocation-free.
105
+ export function resolveSpringGenerator(target, config) {
106
+ const resolved = resolveSpring(config);
107
+ // findSpring (duration) springs settle at t ≥ calculatedDuration; physics/visualDuration settle by
108
+ // threshold (settleDuration null). (REQ-SPRING-011)
109
+ const factory = springGenerator(target, resolved.physics, {
110
+ settleDuration: resolved.calculatedDuration,
111
+ ...(config.restSpeed !== undefined ? { restSpeed: config.restSpeed } : {}),
112
+ ...(config.restDelta !== undefined ? { restDelta: config.restDelta } : {}),
113
+ });
114
+ if (resolved.carryVelocity) {
115
+ const authoredVelocity = config.velocity;
116
+ if (authoredVelocity === undefined)
117
+ return factory;
118
+ // alloc-ok: lifecycle-edge — the authored-velocity reseed wrapper runs at generator construction.
119
+ return (seed) => factory({ from: seed.from, velocity: authoredVelocity });
120
+ }
121
+ // alloc-ok: lifecycle-edge — the zero-velocity reseed wrapper runs at generator construction.
122
+ return (seed) => factory({ from: seed.from, velocity: 0 });
123
+ }
124
+ /**
125
+ * Interrupt/retarget (REQ-SPRING-009): build a new generator for `newTarget` from the OUTGOING
126
+ * generator's analytic state at `atMs` — its exact value and analytic velocity (preferred over any
127
+ * finite-diff, which avoids systematic velocity loss at high frame rates). Whether that incoming
128
+ * velocity carries (physics), is replaced by an authored config `velocity` (REQ-API-053), or is
129
+ * zeroed (duration/visualDuration) is decided by
130
+ * `resolveSpringGenerator` (REQ-SPRING-007). The result's `value(0)` equals the outgoing `value(atMs)`
131
+ * exactly — no teleport, no velocity reset. The graph's own interruption seam does the equivalent via
132
+ * `motionValue.getVelocity()`; this is the explicit spring-layer helper for consumers driving
133
+ * generators directly (e.g. the Reanimated driver).
134
+ */
135
+ // alloc-ok: lifecycle-edge — interruption/retarget construction; samples the outgoing generator
136
+ // once, then builds the successor.
137
+ export function retargetSpring(outgoing, atMs, newTarget, config) {
138
+ const at = outgoing.sample(atMs);
139
+ return resolveSpringGenerator(newTarget, config)({ from: at.value, velocity: at.velocity });
140
+ }
141
+ // REQ-SPRING-012 getDefaultTransition selection table. transformProps mirrors motion's set.
142
+ const TRANSFORM_PROPS = new Set([
143
+ 'transformPerspective',
144
+ 'x',
145
+ 'y',
146
+ 'z',
147
+ 'translateX',
148
+ 'translateY',
149
+ 'translateZ',
150
+ 'scale',
151
+ 'scaleX',
152
+ 'scaleY',
153
+ 'rotate',
154
+ 'rotateX',
155
+ 'rotateY',
156
+ 'rotateZ',
157
+ 'skew',
158
+ 'skewX',
159
+ 'skewY',
160
+ 'pathRotation',
161
+ ]);
162
+ // R8-F1: a spring interpolates EXACTLY two keyframes (origin → target); the pinned Motion throws
163
+ // `spring-two-frames` for any other count. The ONE predicate the reference driver's
164
+ // springKeyframeGenerator throws from AND the native controller PRE-VALIDATES with before issuing any
165
+ // driver command (so a rejected multi-command mount/update mutates nothing — atomicity, review major
166
+ // 36). The worklet driver mirrors the SAME message inline on its 'worklet' path. Returns the loud
167
+ // refusal for a non-two count, else null.
168
+ // alloc-ok: lifecycle-edge — a refusal built at command/pre-validation time, never per frame.
169
+ export function springKeyframeCountRefusal(count) {
170
+ if (count === 2)
171
+ return null;
172
+ return new Error(`a spring transition cannot interpolate through ${count} keyframes — a spring animates exactly ` +
173
+ 'two (origin → target). Keyframe index 2 (the successor) is unreachable; use a tween for a ' +
174
+ 'multi-keyframe array (REQ-API-033, R8-F1).');
175
+ }
176
+ /**
177
+ * Default transition per property when none is specified (REQ-SPRING-012), keyframe-count-aware to
178
+ * match the pinned `motion-dom` `getDefaultTransition(valueKey, { keyframes })`
179
+ * (default-transitions.ts): MORE than two keyframes → an 800ms keyframes transition (regardless of
180
+ * property); otherwise a transform prop gets an underdamped spring (scale: critically-damped 550,
181
+ * damping 30 for a non-zero target and 2√550 at exactly 0; other transforms: 500/25), and every
182
+ * other property gets the 300ms shallow-browser tween. A scalar animation is the two-keyframe case
183
+ * (`[from, target]`); the last keyframe drives the scale-at-zero branch (Motion reads `keyframes[1]`).
184
+ */
185
+ // alloc-ok: lifecycle-edge — per-property default selection at command time.
186
+ export function getDefaultTransition(valueKey, keyframes) {
187
+ if (keyframes.length > 2) {
188
+ return { type: 'keyframes', duration: 0.8 };
189
+ }
190
+ if (TRANSFORM_PROPS.has(valueKey)) {
191
+ if (valueKey.startsWith('scale')) {
192
+ const last = keyframes[keyframes.length - 1];
193
+ const target = typeof last === 'number' ? last : 0;
194
+ return {
195
+ type: 'spring',
196
+ stiffness: 550,
197
+ damping: target === 0 ? 2 * Math.sqrt(550) : 30,
198
+ restSpeed: 10,
199
+ };
200
+ }
201
+ return { type: 'spring', stiffness: 500, damping: 25, restSpeed: 10 };
202
+ }
203
+ return { type: 'keyframes', ease: [0.25, 0.1, 0.35, 1], duration: 0.3 };
204
+ }
205
+ // Pinned drag-layer physics (startAnimation spread: timeConstant 750, restDelta 1, restSpeed 10;
206
+ // motion-dom inertia `power` default 0.8 — single-sourced with PROJECTION_POWER's pin note).
207
+ export const DRAG_INERTIA_DEFAULTS = {
208
+ power: 0.8,
209
+ timeConstant: 750,
210
+ restDelta: 1,
211
+ restSpeed: 10,
212
+ };
213
+ // The pin's elastic-conditional boundary-spring pairs (VisualElementDragControls: dragElastic
214
+ // truthy → 200/40, falsy → 1e6/1e7 "overdamp the boundary spring"). dragTransition overrides.
215
+ // Shared frozen constants (never a per-call literal — the frame-path allocation law, REQ-DRIVER-021).
216
+ const ELASTIC_BOUNDARY_BOUNCE = Object.freeze({ bounceStiffness: 200, bounceDamping: 40 });
217
+ const RIGID_BOUNDARY_BOUNCE = Object.freeze({
218
+ bounceStiffness: 1000000,
219
+ bounceDamping: 10000000,
220
+ });
221
+ export function dragInertiaBounce(elastic) {
222
+ return elastic !== undefined && elastic > 0 ? ELASTIC_BOUNDARY_BOUNCE : RIGID_BOUNDARY_BOUNCE;
223
+ }
package/dist/types.cjs ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Host-agnostic motion-graph contracts (SPEC-CORE §3). Zero host dependencies (REQ-CORE-003):
3
+ // this file imports nothing from react-native / reanimated / gesture-handler / motion / react.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,175 @@
1
+ /** Injectable time source. Core reads no ambient time (`Date.now`/`performance.now`) — REQ-CLOCK-001. */
2
+ export interface Clock {
3
+ now(): number;
4
+ }
5
+ /**
6
+ * Per-frame step seam (REQ-CLOCK-001). Production drives this from vsync; tests pump frames
7
+ * explicitly. A step receives the elapsed time since the previous frame (already dt-clamped by the
8
+ * graph, REQ-CLOCK-003).
9
+ */
10
+ export interface Scheduler {
11
+ onFrame(step: (dtMs: number) => void): () => void;
12
+ }
13
+ export type ValueChangeListener<T> = (value: T) => void;
14
+ /**
15
+ * A derived value with a re-attachable input lifecycle (review r4): consumers that bind and
16
+ * unbind across React effect replays detach/re-attach the SAME value; refresh() resyncs
17
+ * synchronously when the compute's captured parameters change. Detaching also CANCELS any
18
+ * already-queued recompute (review r5) — a detached node never applies or notifies.
19
+ */
20
+ export interface DerivedMotionValue<T> extends MotionValue<T> {
21
+ attachInputs(): void;
22
+ detachInputs(): void;
23
+ refresh(): void;
24
+ }
25
+ /**
26
+ * The structural input contract of a derivation (REQ-VALUE-002: "one or more motion values" —
27
+ * ANY value type; REQ-VALUE-013 glitch-freeness holds regardless of T). Every MotionValue<T>
28
+ * satisfies this shape, so string/color values compose into derivations like numeric ones.
29
+ */
30
+ export interface DerivedSource<V = unknown> {
31
+ get(): V;
32
+ subscribe(listener: () => void): () => void;
33
+ }
34
+ /**
35
+ * Per-position value types of a derivation's input tuple (review r6): the compute receives the
36
+ * REAL value type of each input — never-erasure would reject valid string code while admitting
37
+ * mistyped callbacks.
38
+ */
39
+ export type DerivedSourceValues<Inputs extends readonly DerivedSource[]> = {
40
+ [Index in keyof Inputs]: Inputs[Index] extends DerivedSource<infer Value> ? Value : never;
41
+ };
42
+ export type MotionValueEvent = 'change' | 'animationStart' | 'animationComplete' | 'animationCancel' | 'destroy';
43
+ /**
44
+ * The live animated signal (REQ-VALUE-001/010/011/012). Generic over `T` (default `number`);
45
+ * non-numeric values are opaque and report velocity `0`. `subscribe` never triggers a host render.
46
+ */
47
+ export interface MotionValue<T = number> {
48
+ get(): T;
49
+ /** Set the value; velocity is computed from clock-timestamped samples. */
50
+ set(value: T): void;
51
+ /**
52
+ * Teleport: set the value, reset velocity to `0`, and END any active animation through
53
+ * `stop()` (a cancellation) — the pinned `jump(v, endAnimation = true)` shape (r15): a dead
54
+ * flight must never overwrite the teleported value on the next frame.
55
+ */
56
+ jump(value: T): void;
57
+ subscribe(listener: ValueChangeListener<T>): () => void;
58
+ /** Velocity in units/sec; `0` for non-numeric `T`; `0` if the last sample is stale. */
59
+ getVelocity(): number;
60
+ /**
61
+ * `change` delivers the latest value (pinned payload, r15); lifecycle events carry NONE —
62
+ * no broad union overload exists (r16 major 5: it made a payload-taking lifecycle listener
63
+ * representable while runtime supplies undefined). Union-event call sites narrow first.
64
+ */
65
+ on(event: 'change', listener: (latest: T) => void): () => void;
66
+ on(event: Exclude<MotionValueEvent, 'change'>, listener: () => void): () => void;
67
+ /** Stop any animation currently driving this value, leaving it at its current value. */
68
+ stop(): void;
69
+ /**
70
+ * Whether an animation is currently driving this value (pinned motion-dom member, r12): the
71
+ * style-binding lane refuses an ALREADY-animating value at attach, not just one that starts
72
+ * while bound. Derived values are never animated directly and always report false.
73
+ */
74
+ isAnimating(): boolean;
75
+ destroy(): void;
76
+ }
77
+ /** Seed handed to a generator factory when an animation (re)starts on a value (REQ-CORE-002). */
78
+ export interface GeneratorSeed {
79
+ from: number;
80
+ velocity: number;
81
+ }
82
+ /**
83
+ * The record is OWNED and REUSED by its generator — sample() mutates and returns the same object
84
+ * every call (motion's next() contract; allocation-free per-frame sampling, REQ-DRIVER-015).
85
+ * Read the scalars immediately; never retain the record across sample() calls.
86
+ */
87
+ export interface GeneratorSample {
88
+ value: number;
89
+ velocity: number;
90
+ done: boolean;
91
+ }
92
+ /**
93
+ * An animation source sampled purely by elapsed time (REQ-CORE-001). Core hosts generators through
94
+ * this interface but does not parameterize their numerics — spring/timing math is SPEC-SPRING's.
95
+ */
96
+ export interface Generator {
97
+ sample(elapsedMs: number): GeneratorSample;
98
+ /**
99
+ * The value to COMMIT when the trajectory finishes, when that differs from what `sample` reports
100
+ * at the terminal instant — the pin's `getFinalKeyframe` position.
101
+ *
102
+ * The pinned Motion applies `getFinalKeyframe` INSIDE `tick`, gated on
103
+ * `holdTime === null && (state === 'finished' || (state === 'running' && done))`, and only then
104
+ * calls `finish()`, which never touches the value (motion-dom `JSAnimation`). A FRESH,
105
+ * non-autoplaying instance is `paused`, so `holdTime` is non-null and neither disjunct holds: its
106
+ * `sample()` returns the raw fold, which is how this repo's goldens are generated. An
107
+ * already-FINISHED instance takes the override at ANY sampled time, mid-play included; a RUNNING
108
+ * one takes it only at or past `totalDuration`, because that disjunct also requires `done`
109
+ * (recomputed in `tick` as `currentTime >= totalDuration` at forward speed). Measured on
110
+ * `repeat: 1.5 reverse`, whose raw fold is 80 mid-play and 50 at the terminal — paused 80/50,
111
+ * running 80/100, finished 100/100.
112
+ *
113
+ * Absent on every generator whose terminal sample already IS its resting value — only a fractional
114
+ * `repeat` fold sets it (REQ-TIMING-003). `stepProp` is the one consumer, so both drivers get the
115
+ * rule from one place.
116
+ *
117
+ * **A generator that WRAPS another and rebuilds the object owes this forward.** It is optional, so
118
+ * a wrapper that omits it silently drops the wrapped trajectory's resting value — exactly what
119
+ * `delayGenerator` did until the H2 round-1 review caught it. Known wrappers that do NOT forward,
120
+ * and why that is currently safe: `presence/controller.ts`'s min/max aggregators and its
121
+ * `normalizedFactory` (which would have to RESCALE a finish value, not forward it). No fold can
122
+ * reach them — `shippedSurface.ts` lists `repeat`/`repeatType`/`repeatDelay`/`delay` in
123
+ * `EXIT_LANE_INEXECUTABLE_OPTIONS`, and the presence controller never builds a repeat fold. Named
124
+ * here so a change that makes them reachable cannot reopen the defect silently (H2 round-2
125
+ * review MINOR 2). A new wrapper needs its own COMPOSED floor: a defect in composition is
126
+ * invisible to every floor that tests the parts.
127
+ */
128
+ readonly finalValue?: number | undefined;
129
+ }
130
+ /**
131
+ * Constructs a generator from a seed. The seam that carries velocity across interruption: on start
132
+ * the seed is the value's current value+velocity; on retarget the new factory is re-seeded with the
133
+ * current value+velocity so motion continues from the present visual state (REQ-CORE-002).
134
+ */
135
+ export type GeneratorFactory = (seed: GeneratorSeed) => Generator;
136
+ /** Handle to an in-flight animation. */
137
+ export interface Animation {
138
+ stop(): void;
139
+ readonly done: boolean;
140
+ }
141
+ /**
142
+ * Unsettled-work handle (REQ-SETTLE-001). Gesture and layout (non-core) call `registerActivity`
143
+ * and hold this handle open while their work is in flight; `release` marks that source done.
144
+ */
145
+ export interface ActivityHandle {
146
+ release(): void;
147
+ }
148
+ /**
149
+ * The motion graph: value factory, derived graph, animation registry, and settle ledger. Owns the
150
+ * injected clock/scheduler and the definition of graph quiescence (REQ-SETTLE-001).
151
+ */
152
+ export interface MotionGraph {
153
+ motionValue<T = number>(initial: T): MotionValue<T>;
154
+ /**
155
+ * Read-only value computed from any motion values; recomputes off the host path, glitch-free.
156
+ * Two call shapes (review r6 + r7): with NO explicit type arguments the input tuple infers
157
+ * per position, so the compute sees each input's REAL value type and mistyped callbacks are
158
+ * compile errors; with an explicit `derived<T>(...)` output type (the original public shape)
159
+ * values arrive as `unknown` and the caller narrows deliberately — sound in both directions.
160
+ */
161
+ derived<const Inputs extends readonly DerivedSource[], T>(inputs: Inputs, compute: (...values: DerivedSourceValues<Inputs>) => T): DerivedMotionValue<T>;
162
+ derived<T>(inputs: readonly DerivedSource[], compute: (...values: unknown[]) => T): DerivedMotionValue<T>;
163
+ /** Start (or interrupt-and-restart) an animation driving `value`. */
164
+ animate(value: MotionValue<number>, factory: GeneratorFactory): Animation;
165
+ activeAnimationCount(): number;
166
+ registerActivity(): ActivityHandle;
167
+ /** True iff `activeAnimationCount() === 0` and no activity handle is open. */
168
+ isSettled(): boolean;
169
+ /** Resolves exactly on the frame the graph transitions to settled; re-armable; fail-closed. */
170
+ whenSettled(): Promise<void>;
171
+ }
172
+ export interface MotionGraphOptions {
173
+ clock: Clock;
174
+ scheduler: Scheduler;
175
+ }
@@ -0,0 +1,175 @@
1
+ /** Injectable time source. Core reads no ambient time (`Date.now`/`performance.now`) — REQ-CLOCK-001. */
2
+ export interface Clock {
3
+ now(): number;
4
+ }
5
+ /**
6
+ * Per-frame step seam (REQ-CLOCK-001). Production drives this from vsync; tests pump frames
7
+ * explicitly. A step receives the elapsed time since the previous frame (already dt-clamped by the
8
+ * graph, REQ-CLOCK-003).
9
+ */
10
+ export interface Scheduler {
11
+ onFrame(step: (dtMs: number) => void): () => void;
12
+ }
13
+ export type ValueChangeListener<T> = (value: T) => void;
14
+ /**
15
+ * A derived value with a re-attachable input lifecycle (review r4): consumers that bind and
16
+ * unbind across React effect replays detach/re-attach the SAME value; refresh() resyncs
17
+ * synchronously when the compute's captured parameters change. Detaching also CANCELS any
18
+ * already-queued recompute (review r5) — a detached node never applies or notifies.
19
+ */
20
+ export interface DerivedMotionValue<T> extends MotionValue<T> {
21
+ attachInputs(): void;
22
+ detachInputs(): void;
23
+ refresh(): void;
24
+ }
25
+ /**
26
+ * The structural input contract of a derivation (REQ-VALUE-002: "one or more motion values" —
27
+ * ANY value type; REQ-VALUE-013 glitch-freeness holds regardless of T). Every MotionValue<T>
28
+ * satisfies this shape, so string/color values compose into derivations like numeric ones.
29
+ */
30
+ export interface DerivedSource<V = unknown> {
31
+ get(): V;
32
+ subscribe(listener: () => void): () => void;
33
+ }
34
+ /**
35
+ * Per-position value types of a derivation's input tuple (review r6): the compute receives the
36
+ * REAL value type of each input — never-erasure would reject valid string code while admitting
37
+ * mistyped callbacks.
38
+ */
39
+ export type DerivedSourceValues<Inputs extends readonly DerivedSource[]> = {
40
+ [Index in keyof Inputs]: Inputs[Index] extends DerivedSource<infer Value> ? Value : never;
41
+ };
42
+ export type MotionValueEvent = 'change' | 'animationStart' | 'animationComplete' | 'animationCancel' | 'destroy';
43
+ /**
44
+ * The live animated signal (REQ-VALUE-001/010/011/012). Generic over `T` (default `number`);
45
+ * non-numeric values are opaque and report velocity `0`. `subscribe` never triggers a host render.
46
+ */
47
+ export interface MotionValue<T = number> {
48
+ get(): T;
49
+ /** Set the value; velocity is computed from clock-timestamped samples. */
50
+ set(value: T): void;
51
+ /**
52
+ * Teleport: set the value, reset velocity to `0`, and END any active animation through
53
+ * `stop()` (a cancellation) — the pinned `jump(v, endAnimation = true)` shape (r15): a dead
54
+ * flight must never overwrite the teleported value on the next frame.
55
+ */
56
+ jump(value: T): void;
57
+ subscribe(listener: ValueChangeListener<T>): () => void;
58
+ /** Velocity in units/sec; `0` for non-numeric `T`; `0` if the last sample is stale. */
59
+ getVelocity(): number;
60
+ /**
61
+ * `change` delivers the latest value (pinned payload, r15); lifecycle events carry NONE —
62
+ * no broad union overload exists (r16 major 5: it made a payload-taking lifecycle listener
63
+ * representable while runtime supplies undefined). Union-event call sites narrow first.
64
+ */
65
+ on(event: 'change', listener: (latest: T) => void): () => void;
66
+ on(event: Exclude<MotionValueEvent, 'change'>, listener: () => void): () => void;
67
+ /** Stop any animation currently driving this value, leaving it at its current value. */
68
+ stop(): void;
69
+ /**
70
+ * Whether an animation is currently driving this value (pinned motion-dom member, r12): the
71
+ * style-binding lane refuses an ALREADY-animating value at attach, not just one that starts
72
+ * while bound. Derived values are never animated directly and always report false.
73
+ */
74
+ isAnimating(): boolean;
75
+ destroy(): void;
76
+ }
77
+ /** Seed handed to a generator factory when an animation (re)starts on a value (REQ-CORE-002). */
78
+ export interface GeneratorSeed {
79
+ from: number;
80
+ velocity: number;
81
+ }
82
+ /**
83
+ * The record is OWNED and REUSED by its generator — sample() mutates and returns the same object
84
+ * every call (motion's next() contract; allocation-free per-frame sampling, REQ-DRIVER-015).
85
+ * Read the scalars immediately; never retain the record across sample() calls.
86
+ */
87
+ export interface GeneratorSample {
88
+ value: number;
89
+ velocity: number;
90
+ done: boolean;
91
+ }
92
+ /**
93
+ * An animation source sampled purely by elapsed time (REQ-CORE-001). Core hosts generators through
94
+ * this interface but does not parameterize their numerics — spring/timing math is SPEC-SPRING's.
95
+ */
96
+ export interface Generator {
97
+ sample(elapsedMs: number): GeneratorSample;
98
+ /**
99
+ * The value to COMMIT when the trajectory finishes, when that differs from what `sample` reports
100
+ * at the terminal instant — the pin's `getFinalKeyframe` position.
101
+ *
102
+ * The pinned Motion applies `getFinalKeyframe` INSIDE `tick`, gated on
103
+ * `holdTime === null && (state === 'finished' || (state === 'running' && done))`, and only then
104
+ * calls `finish()`, which never touches the value (motion-dom `JSAnimation`). A FRESH,
105
+ * non-autoplaying instance is `paused`, so `holdTime` is non-null and neither disjunct holds: its
106
+ * `sample()` returns the raw fold, which is how this repo's goldens are generated. An
107
+ * already-FINISHED instance takes the override at ANY sampled time, mid-play included; a RUNNING
108
+ * one takes it only at or past `totalDuration`, because that disjunct also requires `done`
109
+ * (recomputed in `tick` as `currentTime >= totalDuration` at forward speed). Measured on
110
+ * `repeat: 1.5 reverse`, whose raw fold is 80 mid-play and 50 at the terminal — paused 80/50,
111
+ * running 80/100, finished 100/100.
112
+ *
113
+ * Absent on every generator whose terminal sample already IS its resting value — only a fractional
114
+ * `repeat` fold sets it (REQ-TIMING-003). `stepProp` is the one consumer, so both drivers get the
115
+ * rule from one place.
116
+ *
117
+ * **A generator that WRAPS another and rebuilds the object owes this forward.** It is optional, so
118
+ * a wrapper that omits it silently drops the wrapped trajectory's resting value — exactly what
119
+ * `delayGenerator` did until the H2 round-1 review caught it. Known wrappers that do NOT forward,
120
+ * and why that is currently safe: `presence/controller.ts`'s min/max aggregators and its
121
+ * `normalizedFactory` (which would have to RESCALE a finish value, not forward it). No fold can
122
+ * reach them — `shippedSurface.ts` lists `repeat`/`repeatType`/`repeatDelay`/`delay` in
123
+ * `EXIT_LANE_INEXECUTABLE_OPTIONS`, and the presence controller never builds a repeat fold. Named
124
+ * here so a change that makes them reachable cannot reopen the defect silently (H2 round-2
125
+ * review MINOR 2). A new wrapper needs its own COMPOSED floor: a defect in composition is
126
+ * invisible to every floor that tests the parts.
127
+ */
128
+ readonly finalValue?: number | undefined;
129
+ }
130
+ /**
131
+ * Constructs a generator from a seed. The seam that carries velocity across interruption: on start
132
+ * the seed is the value's current value+velocity; on retarget the new factory is re-seeded with the
133
+ * current value+velocity so motion continues from the present visual state (REQ-CORE-002).
134
+ */
135
+ export type GeneratorFactory = (seed: GeneratorSeed) => Generator;
136
+ /** Handle to an in-flight animation. */
137
+ export interface Animation {
138
+ stop(): void;
139
+ readonly done: boolean;
140
+ }
141
+ /**
142
+ * Unsettled-work handle (REQ-SETTLE-001). Gesture and layout (non-core) call `registerActivity`
143
+ * and hold this handle open while their work is in flight; `release` marks that source done.
144
+ */
145
+ export interface ActivityHandle {
146
+ release(): void;
147
+ }
148
+ /**
149
+ * The motion graph: value factory, derived graph, animation registry, and settle ledger. Owns the
150
+ * injected clock/scheduler and the definition of graph quiescence (REQ-SETTLE-001).
151
+ */
152
+ export interface MotionGraph {
153
+ motionValue<T = number>(initial: T): MotionValue<T>;
154
+ /**
155
+ * Read-only value computed from any motion values; recomputes off the host path, glitch-free.
156
+ * Two call shapes (review r6 + r7): with NO explicit type arguments the input tuple infers
157
+ * per position, so the compute sees each input's REAL value type and mistyped callbacks are
158
+ * compile errors; with an explicit `derived<T>(...)` output type (the original public shape)
159
+ * values arrive as `unknown` and the caller narrows deliberately — sound in both directions.
160
+ */
161
+ derived<const Inputs extends readonly DerivedSource[], T>(inputs: Inputs, compute: (...values: DerivedSourceValues<Inputs>) => T): DerivedMotionValue<T>;
162
+ derived<T>(inputs: readonly DerivedSource[], compute: (...values: unknown[]) => T): DerivedMotionValue<T>;
163
+ /** Start (or interrupt-and-restart) an animation driving `value`. */
164
+ animate(value: MotionValue<number>, factory: GeneratorFactory): Animation;
165
+ activeAnimationCount(): number;
166
+ registerActivity(): ActivityHandle;
167
+ /** True iff `activeAnimationCount() === 0` and no activity handle is open. */
168
+ isSettled(): boolean;
169
+ /** Resolves exactly on the frame the graph transitions to settled; re-armable; fail-closed. */
170
+ whenSettled(): Promise<void>;
171
+ }
172
+ export interface MotionGraphOptions {
173
+ clock: Clock;
174
+ scheduler: Scheduler;
175
+ }
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // Host-agnostic motion-graph contracts (SPEC-CORE §3). Zero host dependencies (REQ-CORE-003):
2
+ // this file imports nothing from react-native / reanimated / gesture-handler / motion / react.
3
+ export {};