@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,454 @@
1
+ // REQ-DRIVER-030 — the one raw DriverCommand → immutable PreparedDriverCommand boundary. Concrete
2
+ // backends receive only the prepared protocol, so caller accessors, mutation, optional velocity, and
3
+ // per-property transition fallback semantics are settled exactly once before backend dispatch.
4
+ import { keyframeTimingRefusal } from "./keyframeTiming.js";
5
+ import { captureBoundedArray, captureBoundedCubicBezier, capturedArrayDescription, } from "../component/boundedArray.js";
6
+ import { InvalidTargetError, InvalidTransitionError, transitionShapeRefusal, validateTransitionRefusal, } from "../component/validate.js";
7
+ import { TARGET_PROPERTY_KEYS } from "../component/types.js";
8
+ // `PropTransition` additionally carries controller-resolved DefaultTransition springs. `restSpeed`
9
+ // is internal-only (never an authored public Transition option) but must survive preparation so both
10
+ // drivers retain the pinned default's settle threshold.
11
+ function captureTarget(to) {
12
+ if (typeof to === 'number')
13
+ return to;
14
+ // Read the caller's cardinality exactly once, then copy that many indexed values. Array iteration
15
+ // may re-read a stateful proxy's `length` between elements; this boundary must capture one target
16
+ // truth before validation and dispatch.
17
+ const capture = captureBoundedArray(to);
18
+ if (capture.kind !== 'captured') {
19
+ throw new InvalidTargetError(undefined, 'to', capturedArrayDescription(capture), 'driver keyframe targets must have a safe array length (REQ-API-033)');
20
+ }
21
+ return capture.values;
22
+ }
23
+ // Capture the one nested level the legal timing schema admits: an outer `ease` list may contain a
24
+ // 4-number cubic-bezier tuple. Generic recursive cloning was unsafe — a malformed cyclic/deeper array
25
+ // overflowed before it reached the typed validator. Sparse slots deliberately stay sparse; validation
26
+ // names the missing index rather than letting Array.prototype.every skip it. A deeper array remains an
27
+ // opaque malformed member and is rejected by the bounded timing predicate before any backend runs it.
28
+ function captureTimingArray(value, nested = false) {
29
+ const capture = nested ? captureBoundedCubicBezier(value) : captureBoundedArray(value);
30
+ if (capture.kind !== 'captured')
31
+ return capturedArrayDescription(capture);
32
+ const length = capture.values.length;
33
+ const captured = [];
34
+ captured.length = length;
35
+ for (let index = 0; index < length; index++) {
36
+ if (!Object.hasOwn(capture.values, index))
37
+ continue;
38
+ const element = capture.values[index];
39
+ captured[index] =
40
+ !nested && Array.isArray(element) ? captureTimingArray(element, true) : element;
41
+ }
42
+ return Object.freeze(captured);
43
+ }
44
+ const TRANSITION_MAP_KEYS = new Set(['default', 'layout', ...TARGET_PROPERTY_KEYS]);
45
+ function transitionMapRefusal(transition) {
46
+ if (typeof transition !== 'object' || transition === null || Array.isArray(transition))
47
+ return null;
48
+ for (const key of Reflect.ownKeys(transition)) {
49
+ if (typeof key === 'string' && TRANSITION_MAP_KEYS.has(key)) {
50
+ return new InvalidTransitionError(undefined, key, transition[key], 'a raw DriverCommand must carry a flat transition selected by the component boundary; ' +
51
+ 'transition maps are not executable at the backend protocol (REQ-DRIVER-033)');
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ function isTransitionMapKey(key) {
57
+ return TRANSITION_MAP_KEYS.has(key);
58
+ }
59
+ function targetTransitionsShapeRefusal(value, targets) {
60
+ if (value === undefined)
61
+ return null;
62
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
63
+ return new InvalidTransitionError(undefined, 'targetTransitions', value, 'retarget targetTransitions must be a plain object keyed by retarget targets (REQ-DRIVER-030)');
64
+ }
65
+ const prototype = Object.getPrototypeOf(value);
66
+ if (prototype !== Object.prototype && prototype !== null) {
67
+ return new InvalidTransitionError(undefined, 'targetTransitions', value, 'retarget targetTransitions must be a plain object keyed by retarget targets (REQ-DRIVER-030)');
68
+ }
69
+ for (const key of Reflect.ownKeys(value)) {
70
+ if (typeof key !== 'string') {
71
+ return new InvalidTransitionError(undefined, String(key), undefined, 'retarget targetTransitions may only contain string retarget target keys (REQ-DRIVER-030)');
72
+ }
73
+ if (!Object.hasOwn(targets, key)) {
74
+ return new InvalidTransitionError(undefined, key, undefined, 'retarget targetTransitions may only override a key present in targets (REQ-DRIVER-030)');
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+ // Capture exactly one legal nested transition-bag level. A nested map entry is retained as an opaque
80
+ // value so the validator can refuse recursive maps; recursively cloning malformed input here would
81
+ // turn a fail-closed schema error into an unbounded/cyclic capture walk.
82
+ function captureNestedTransitionBag(value) {
83
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
84
+ return value;
85
+ const proto = Object.getPrototypeOf(value);
86
+ if (proto !== Object.prototype && proto !== null)
87
+ return value;
88
+ const source = value;
89
+ const captured = Object.create(null);
90
+ for (const key of Reflect.ownKeys(source)) {
91
+ const member = source[key];
92
+ const capturedMember = typeof key === 'string' &&
93
+ (key === 'times' || key === 'ease' || key === 'easings' || key === 'easing') &&
94
+ Array.isArray(member)
95
+ ? captureTimingArray(member)
96
+ : member;
97
+ Object.defineProperty(captured, key, {
98
+ value: capturedMember,
99
+ enumerable: Object.getOwnPropertyDescriptor(source, key)?.enumerable ?? true,
100
+ configurable: true,
101
+ writable: true,
102
+ });
103
+ }
104
+ return Object.freeze(captured);
105
+ }
106
+ // Capture a transition independently of its caller. Every own field (including non-enumerable fields and
107
+ // symbols) is read once and preserved; capture is deliberately NOT the schema/severity authority. Its
108
+ // callers validate this immutable truth, so an unknown authored option is refused by the established
109
+ // typed boundary instead of escaping before production can report it. Arrays are DEEP-cloned and frozen because
110
+ // generator/contending state may retain them after command() returns (including a per-segment ease list's
111
+ // nested cubic-bezier arrays, R8 M2-B). A null-prototype accumulator preserves an own `__proto__` field
112
+ // instead of invoking the legacy setter.
113
+ export function captureTransition(transition) {
114
+ if (typeof transition !== 'object' || transition === null || Array.isArray(transition))
115
+ return transition;
116
+ const captured = Object.create(null);
117
+ for (const key of Reflect.ownKeys(transition)) {
118
+ const value = transition[key];
119
+ const capturedValue = typeof key === 'string' &&
120
+ (key === 'times' || key === 'ease' || key === 'easings' || key === 'easing') &&
121
+ Array.isArray(value)
122
+ ? captureTimingArray(value)
123
+ : typeof key === 'string' && isTransitionMapKey(key)
124
+ ? captureNestedTransitionBag(value)
125
+ : value;
126
+ Object.defineProperty(captured, key, {
127
+ value: capturedValue,
128
+ enumerable: Object.getOwnPropertyDescriptor(transition, key)?.enumerable ?? true,
129
+ configurable: true,
130
+ writable: true,
131
+ });
132
+ }
133
+ return Object.freeze(captured);
134
+ }
135
+ // A command-level transition is ALWAYS authored. Only a per-property transition can be the controller's
136
+ // count-aware DefaultTransition, because defaults differ by property/keyframe count. Keep those two raw
137
+ // slots distinct at this boundary instead of inferring provenance from a field such as `restSpeed`.
138
+ function internalPropTransitionRefusal(transition) {
139
+ if (typeof transition !== 'object' || transition === null || Array.isArray(transition)) {
140
+ return validateTransitionRefusal(transition);
141
+ }
142
+ const value = transition;
143
+ const type = value['type'];
144
+ const hasRestSpeed = Object.hasOwn(value, 'restSpeed');
145
+ // R16 (REQ-GESTURE-023): the internal inertia transition — the gesture free-drag release's
146
+ // two-phase generator descriptor. Command-layer only; validated field-by-field here, then
147
+ // passed through unchanged (both drivers construct the SAME generator physics, no skew).
148
+ if (type === 'inertia') {
149
+ const allowed = new Set([
150
+ 'type',
151
+ 'power',
152
+ 'timeConstant',
153
+ 'bounceStiffness',
154
+ 'bounceDamping',
155
+ 'restDelta',
156
+ 'restSpeed',
157
+ 'min',
158
+ 'max',
159
+ ]);
160
+ for (const key of Reflect.ownKeys(value)) {
161
+ if (!allowed.has(key)) {
162
+ return new InvalidTransitionError(undefined, typeof key === 'string' ? key : String(key), value[key], 'not a recognized internal inertia transition option (REQ-DRIVER-030, R16)');
163
+ }
164
+ }
165
+ for (const key of [
166
+ 'power',
167
+ 'timeConstant',
168
+ 'bounceStiffness',
169
+ 'bounceDamping',
170
+ 'restDelta',
171
+ 'restSpeed',
172
+ ]) {
173
+ const field = value[key];
174
+ if (!Object.hasOwn(value, key) ||
175
+ typeof field !== 'number' ||
176
+ !Number.isFinite(field) ||
177
+ field <= 0) {
178
+ return new InvalidTransitionError(undefined, key, field, `an internal inertia transition requires an own finite ${key} greater than zero (REQ-DRIVER-030, R16)`);
179
+ }
180
+ }
181
+ // Bounds follow the drag-constraints sentinel law: open sides are ±Infinity of the correct
182
+ // polarity; wrong-polarity infinities and min > max are refused (G-INV-8).
183
+ const min = value['min'];
184
+ const max = value['max'];
185
+ if (min !== undefined && (typeof min !== 'number' || Number.isNaN(min) || min === Infinity)) {
186
+ return new InvalidTransitionError(undefined, 'min', min, 'an internal inertia transition min must be a number or -Infinity (REQ-DRIVER-030, R16)');
187
+ }
188
+ if (max !== undefined && (typeof max !== 'number' || Number.isNaN(max) || max === -Infinity)) {
189
+ return new InvalidTransitionError(undefined, 'max', max, 'an internal inertia transition max must be a number or +Infinity (REQ-DRIVER-030, R16)');
190
+ }
191
+ if (min !== undefined && max !== undefined && min > max) {
192
+ return new InvalidTransitionError(undefined, 'min', min, 'an internal inertia transition requires min <= max (REQ-DRIVER-030, R16)');
193
+ }
194
+ return null;
195
+ }
196
+ if (type !== 'keyframes' && !hasRestSpeed)
197
+ return validateTransitionRefusal(transition);
198
+ if (type !== 'keyframes') {
199
+ // U7c (REQ-SPRING-014): restSpeed/restDelta are AUTHORED vocabulary now, so restSpeed
200
+ // presence can no longer discriminate the controller's internal spring default from an
201
+ // authored spring — the shapes genuinely overlap, and Motion fidelity gives the authored
202
+ // reading precedence (a legal authored per-prop spring must never refuse as a "malformed
203
+ // internal default"). Validate as authored first; only when that fails does the STRICT
204
+ // internal shape law below get its say, so a malformed controller default still fails loud
205
+ // instead of riding along.
206
+ const authoredRefusal = validateTransitionRefusal(transition);
207
+ if (authoredRefusal === null)
208
+ return null;
209
+ const internalRefusal = internalSpringDefaultRefusal(value);
210
+ return internalRefusal ?? authoredRefusal;
211
+ }
212
+ const allowed = new Set([
213
+ 'type',
214
+ 'duration',
215
+ 'ease',
216
+ 'delay',
217
+ 'repeat',
218
+ 'repeatType',
219
+ 'repeatDelay',
220
+ ]);
221
+ for (const key of Reflect.ownKeys(value)) {
222
+ if (!allowed.has(key)) {
223
+ return new InvalidTransitionError(undefined, typeof key === 'string' ? key : String(key), value[key], 'not a recognized internal driver transition option (REQ-DRIVER-030)');
224
+ }
225
+ }
226
+ const duration = value['duration'];
227
+ if (typeof duration !== 'number' || !Number.isFinite(duration) || duration <= 0) {
228
+ return new InvalidTransitionError(undefined, 'duration', duration, 'an internal keyframes default requires a finite duration greater than zero (REQ-DRIVER-030)');
229
+ }
230
+ if (value['ease'] !== undefined && !isInternalKeyframesEase(value['ease'])) {
231
+ return new InvalidTransitionError(undefined, 'ease', value['ease'], 'an internal keyframes default permits only one finite four-number cubic-bezier ease tuple (REQ-DRIVER-030)');
232
+ }
233
+ const authored = Object.create(null);
234
+ authored['type'] = 'tween';
235
+ authored['duration'] = duration;
236
+ if (value['ease'] !== undefined)
237
+ authored['ease'] = value['ease'];
238
+ for (const key of ['delay', 'repeat', 'repeatType', 'repeatDelay']) {
239
+ if (value[key] !== undefined)
240
+ authored[key] = value[key];
241
+ }
242
+ return validateTransitionRefusal(authored);
243
+ }
244
+ // The STRICT internal spring-default shape (r16): the controller's count-aware
245
+ // `getDefaultTransition` output carries exactly this vocabulary, every physics field present and
246
+ // positive. Reached only when the authored reading already failed (U7c — see above), so this is
247
+ // the fail-loud backstop for malformed controller output, never a gate on authored input.
248
+ function internalSpringDefaultRefusal(value) {
249
+ const allowed = new Set([
250
+ 'type',
251
+ 'stiffness',
252
+ 'damping',
253
+ 'restSpeed',
254
+ 'delay',
255
+ 'repeat',
256
+ 'repeatType',
257
+ 'repeatDelay',
258
+ ]);
259
+ for (const key of Reflect.ownKeys(value)) {
260
+ if (!allowed.has(key)) {
261
+ return new InvalidTransitionError(undefined, typeof key === 'string' ? key : String(key), value[key], 'not a recognized internal driver transition option (REQ-DRIVER-030)');
262
+ }
263
+ }
264
+ if (value['type'] !== 'spring') {
265
+ return new InvalidTransitionError(undefined, 'restSpeed', value['restSpeed'], 'restSpeed is internal-only and valid only on an internal spring transition (REQ-DRIVER-030)');
266
+ }
267
+ for (const key of ['stiffness', 'damping', 'restSpeed']) {
268
+ const field = value[key];
269
+ if (!Object.hasOwn(value, key) ||
270
+ typeof field !== 'number' ||
271
+ !Number.isFinite(field) ||
272
+ field <= 0) {
273
+ return new InvalidTransitionError(undefined, key, field, `an internal spring default requires an own finite ${key} greater than zero (REQ-DRIVER-030)`);
274
+ }
275
+ }
276
+ const authored = {
277
+ type: 'spring',
278
+ stiffness: value['stiffness'],
279
+ damping: value['damping'],
280
+ };
281
+ for (const key of ['delay', 'repeat', 'repeatType', 'repeatDelay']) {
282
+ if (value[key] !== undefined)
283
+ authored[key] = value[key];
284
+ }
285
+ return validateTransitionRefusal(authored);
286
+ }
287
+ function isInternalKeyframesEase(value) {
288
+ if (!Array.isArray(value) || value.length !== 4)
289
+ return false;
290
+ for (let index = 0; index < 4; index++) {
291
+ if (!Object.hasOwn(value, index) ||
292
+ typeof value[index] !== 'number' ||
293
+ !Number.isFinite(value[index])) {
294
+ return false;
295
+ }
296
+ }
297
+ return true;
298
+ }
299
+ function validatePreparedPropTransition(transition) {
300
+ // The raw/prepared seam admits controller defaults in addition to the closed authored Transition
301
+ // vocabulary ONLY in a PropTarget slot. Supplying and command boundaries call the authored validator.
302
+ const refusal = internalPropTransitionRefusal(transition);
303
+ if (refusal !== null)
304
+ throw refusal;
305
+ return transition;
306
+ }
307
+ function captureAndValidatePropTransition(transition) {
308
+ const shapeRefusal = transitionShapeRefusal(transition);
309
+ if (shapeRefusal !== null)
310
+ throw shapeRefusal;
311
+ const captured = captureTransition(transition);
312
+ const mapRefusal = transitionMapRefusal(captured);
313
+ if (mapRefusal !== null)
314
+ throw mapRefusal;
315
+ return validatePreparedPropTransition(captured);
316
+ }
317
+ function captureAndValidateCommandTransition(transition) {
318
+ // Component slots admit `undefined` as an omitted transition, but the raw Driver command protocol
319
+ // has no defaulting authority: every start/retarget must carry its authored fallback before a target
320
+ // can be prepared or either backend can schedule. Keep requiredness here, ahead of capture, because
321
+ // generic validation intentionally treats undefined as legal absence for component consumers.
322
+ if (transition === undefined) {
323
+ throw new InvalidTransitionError(undefined, 'transition', transition, 'a DriverCommand start or retarget requires an authored transition (REQ-DRIVER-030)');
324
+ }
325
+ const shapeRefusal = transitionShapeRefusal(transition);
326
+ if (shapeRefusal !== null)
327
+ throw shapeRefusal;
328
+ const captured = captureTransition(transition);
329
+ const refusal = validateTransitionRefusal(captured);
330
+ if (refusal !== null)
331
+ throw refusal;
332
+ const mapRefusal = transitionMapRefusal(captured);
333
+ if (mapRefusal !== null)
334
+ throw mapRefusal;
335
+ return captured;
336
+ }
337
+ function prepareStart(command) {
338
+ // Command transition first preserves the established defensive capture convention when one hostile
339
+ // object occupies both command and per-prop slots. The order is not public semantics; both backends
340
+ // observe only the resulting snapshot.
341
+ const commandTransition = captureAndValidateCommandTransition(command.transition);
342
+ const rawTargets = command.targets;
343
+ const releasesGesture = command.releasesGesture === true;
344
+ const targets = Object.create(null);
345
+ for (const key of Object.keys(rawTargets)) {
346
+ const rawTarget = rawTargets[key];
347
+ // One canonical read per field. Do not destructure/re-read inside either backend.
348
+ const to = rawTarget.to;
349
+ const from = rawTarget.from;
350
+ const velocity = rawTarget.velocity;
351
+ const transition = rawTarget.transition;
352
+ const preparedTransition = transition === undefined ? commandTransition : captureAndValidatePropTransition(transition);
353
+ const capturedTo = captureTarget(to);
354
+ const timingRefusal = keyframeTimingRefusal(undefined, key, capturedTo, preparedTransition);
355
+ if (timingRefusal !== null)
356
+ throw timingRefusal;
357
+ targets[key] = Object.freeze({
358
+ to: capturedTo,
359
+ from,
360
+ velocity: velocity ?? 0,
361
+ transition: preparedTransition,
362
+ });
363
+ }
364
+ const path = command.path === undefined
365
+ ? undefined
366
+ : Object.freeze({
367
+ fromX: command.path.fromX,
368
+ fromY: command.path.fromY,
369
+ toX: command.path.toX,
370
+ toY: command.path.toY,
371
+ peak: command.path.peak,
372
+ signedStrength: command.path.signedStrength,
373
+ rotationScale: command.path.rotationScale,
374
+ transition: captureAndValidatePropTransition(command.path.transition),
375
+ });
376
+ return Object.freeze({
377
+ kind: 'start',
378
+ targets: Object.freeze(targets),
379
+ releasesGesture,
380
+ ...(path === undefined ? {} : { path }),
381
+ });
382
+ }
383
+ function prepareRetarget(command) {
384
+ const transition = captureAndValidateCommandTransition(command.transition);
385
+ const rawTargets = command.targets;
386
+ const rawTargetTransitions = command.targetTransitions;
387
+ const targetTransitionsRefusal = targetTransitionsShapeRefusal(rawTargetTransitions, rawTargets);
388
+ if (targetTransitionsRefusal !== null)
389
+ throw targetTransitionsRefusal;
390
+ const targets = Object.create(null);
391
+ const targetTransitions = Object.create(null);
392
+ for (const key of Object.keys(rawTargets)) {
393
+ const target = rawTargets[key];
394
+ const hasRawTargetTransition = rawTargetTransitions !== undefined && Object.hasOwn(rawTargetTransitions, key);
395
+ const rawTargetTransition = hasRawTargetTransition ? rawTargetTransitions[key] : undefined;
396
+ if (hasRawTargetTransition && rawTargetTransition === undefined) {
397
+ throw new InvalidTransitionError(undefined, key, undefined, 'retarget targetTransitions entries must contain a transition (REQ-DRIVER-030)');
398
+ }
399
+ let targetTransition;
400
+ if (!hasRawTargetTransition) {
401
+ targetTransition = transition;
402
+ }
403
+ else {
404
+ // The undefined case was rejected immediately above; this assertion records that
405
+ // runtime proof for the narrowed prepared boundary.
406
+ targetTransition = captureAndValidatePropTransition(rawTargetTransition);
407
+ }
408
+ const timingRefusal = keyframeTimingRefusal(undefined, key, target, targetTransition);
409
+ if (timingRefusal !== null)
410
+ throw timingRefusal;
411
+ targets[key] = target;
412
+ if (hasRawTargetTransition) {
413
+ targetTransitions[key] = targetTransition;
414
+ }
415
+ }
416
+ return Object.freeze({
417
+ kind: 'retarget',
418
+ targets: Object.freeze(targets),
419
+ transition,
420
+ ...(Object.keys(targetTransitions).length === 0
421
+ ? {}
422
+ : { targetTransitions: Object.freeze(targetTransitions) }),
423
+ });
424
+ }
425
+ const PREPARED_STOP = Object.freeze({ kind: 'stop' });
426
+ export function prepareDriverCommand(command) {
427
+ switch (command.kind) {
428
+ case 'start':
429
+ return prepareStart(command);
430
+ case 'retarget':
431
+ return prepareRetarget(command);
432
+ case 'stop':
433
+ return PREPARED_STOP;
434
+ }
435
+ }
436
+ // Preserve the small public Driver surface while making the raw→prepared transition structural: a
437
+ // concrete backend implements PreparedDriver and therefore cannot accidentally inspect PropTarget.
438
+ export function adaptPreparedDriver(backend, assertCommandable) {
439
+ return {
440
+ register: (initial) => backend.register(initial),
441
+ command: (handle, command) => {
442
+ // Preserve each backend's lifecycle/handle failure precedence: an invalid destination fails
443
+ // before any hostile caller getter can run, just as it did before the shared adapter existed.
444
+ assertCommandable(handle);
445
+ backend.command(handle, prepareDriverCommand(command));
446
+ },
447
+ step: (elapsedMs) => backend.step(elapsedMs),
448
+ committed: (handle) => backend.committed(handle),
449
+ setActive: (handle, active) => backend.setActive(handle, active),
450
+ isActive: (handle) => backend.isActive(handle),
451
+ write: (handle, values, velocities) => backend.write(handle, values, velocities),
452
+ liveFor: (handle, key) => backend.liveFor(handle, key),
453
+ };
454
+ }