@playfast/reform 1.2.0 → 1.3.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 (383) hide show
  1. package/dist/boundary/boundary.d.ts +64 -0
  2. package/dist/boundary/boundary.d.ts.map +1 -0
  3. package/dist/boundary/boundary.js +102 -0
  4. package/dist/boundary/boundary.js.map +1 -0
  5. package/dist/calc/asyncCalc.d.ts +21 -0
  6. package/dist/calc/asyncCalc.d.ts.map +1 -0
  7. package/dist/calc/asyncCalc.js +90 -0
  8. package/dist/calc/asyncCalc.js.map +1 -0
  9. package/dist/calc/asyncCalcTypes.d.ts +78 -0
  10. package/dist/calc/asyncCalcTypes.d.ts.map +1 -0
  11. package/dist/calc/asyncCalcTypes.js +2 -0
  12. package/dist/calc/asyncCalcTypes.js.map +1 -0
  13. package/dist/calc/asyncData.d.ts +29 -0
  14. package/dist/calc/asyncData.d.ts.map +1 -0
  15. package/dist/calc/asyncData.js +21 -0
  16. package/dist/calc/asyncData.js.map +1 -0
  17. package/dist/calc/calc.d.ts +39 -0
  18. package/dist/calc/calc.d.ts.map +1 -0
  19. package/dist/calc/calc.js +48 -0
  20. package/dist/calc/calc.js.map +1 -0
  21. package/dist/calc/calcFamily.d.ts +40 -0
  22. package/dist/calc/calcFamily.d.ts.map +1 -0
  23. package/dist/calc/calcFamily.js +113 -0
  24. package/dist/calc/calcFamily.js.map +1 -0
  25. package/dist/calc/compose.d.ts +5 -0
  26. package/dist/calc/compose.d.ts.map +1 -0
  27. package/dist/calc/compose.js +5 -0
  28. package/dist/calc/compose.js.map +1 -0
  29. package/dist/calc/queryState.d.ts +12 -0
  30. package/dist/calc/queryState.d.ts.map +1 -0
  31. package/dist/calc/queryState.js +17 -0
  32. package/dist/calc/queryState.js.map +1 -0
  33. package/dist/channel/channel.d.ts +51 -0
  34. package/dist/channel/channel.d.ts.map +1 -0
  35. package/dist/channel/channel.js +86 -0
  36. package/dist/channel/channel.js.map +1 -0
  37. package/dist/channel/procedures.d.ts +24 -0
  38. package/dist/channel/procedures.d.ts.map +1 -0
  39. package/dist/channel/procedures.js +82 -0
  40. package/dist/channel/procedures.js.map +1 -0
  41. package/dist/compose/composition.d.ts +12 -0
  42. package/dist/compose/composition.d.ts.map +1 -0
  43. package/dist/compose/composition.js +56 -0
  44. package/dist/compose/composition.js.map +1 -0
  45. package/dist/compose/compositionTypes.d.ts +127 -0
  46. package/dist/compose/compositionTypes.d.ts.map +1 -0
  47. package/dist/compose/compositionTypes.js +2 -0
  48. package/dist/compose/compositionTypes.js.map +1 -0
  49. package/dist/compose/host.d.ts +6 -0
  50. package/dist/compose/host.d.ts.map +1 -0
  51. package/dist/compose/host.js +2 -0
  52. package/dist/compose/host.js.map +1 -0
  53. package/dist/compose/props.d.ts +10 -0
  54. package/dist/compose/props.d.ts.map +1 -0
  55. package/dist/compose/props.js +6 -0
  56. package/dist/compose/props.js.map +1 -0
  57. package/dist/compose/provide.d.ts +18 -0
  58. package/dist/compose/provide.d.ts.map +1 -0
  59. package/dist/compose/provide.js +49 -0
  60. package/dist/compose/provide.js.map +1 -0
  61. package/dist/compose/slot.d.ts +70 -0
  62. package/dist/compose/slot.d.ts.map +1 -0
  63. package/dist/compose/slot.js +26 -0
  64. package/dist/compose/slot.js.map +1 -0
  65. package/dist/compose/structure.d.ts +51 -0
  66. package/dist/compose/structure.d.ts.map +1 -0
  67. package/dist/compose/structure.js +92 -0
  68. package/dist/compose/structure.js.map +1 -0
  69. package/dist/compose/ui.d.ts +112 -0
  70. package/dist/compose/ui.d.ts.map +1 -0
  71. package/dist/compose/ui.js +47 -0
  72. package/dist/compose/ui.js.map +1 -0
  73. package/dist/definition/definition.d.ts +11 -0
  74. package/dist/definition/definition.d.ts.map +1 -0
  75. package/dist/definition/definition.js +35 -0
  76. package/dist/definition/definition.js.map +1 -0
  77. package/dist/event/event.d.ts +52 -0
  78. package/dist/event/event.d.ts.map +1 -0
  79. package/dist/event/event.js +67 -0
  80. package/dist/event/event.js.map +1 -0
  81. package/dist/event/eventGroup.d.ts +8 -0
  82. package/dist/event/eventGroup.d.ts.map +1 -0
  83. package/dist/event/eventGroup.js +3 -0
  84. package/dist/event/eventGroup.js.map +1 -0
  85. package/dist/feature/feature.d.ts +27 -0
  86. package/dist/feature/feature.d.ts.map +1 -0
  87. package/dist/feature/feature.js +20 -0
  88. package/dist/feature/feature.js.map +1 -0
  89. package/dist/feature/featureBinding.d.ts +85 -0
  90. package/dist/feature/featureBinding.d.ts.map +1 -0
  91. package/dist/feature/featureBinding.js +10 -0
  92. package/dist/feature/featureBinding.js.map +1 -0
  93. package/dist/feature/featureClass.d.ts +10 -0
  94. package/dist/feature/featureClass.d.ts.map +1 -0
  95. package/dist/feature/featureClass.js +2 -0
  96. package/dist/feature/featureClass.js.map +1 -0
  97. package/dist/feature/featureConfig.d.ts +86 -0
  98. package/dist/feature/featureConfig.d.ts.map +1 -0
  99. package/dist/feature/featureConfig.js +13 -0
  100. package/dist/feature/featureConfig.js.map +1 -0
  101. package/dist/feature/featureFactory.d.ts +15 -0
  102. package/dist/feature/featureFactory.d.ts.map +1 -0
  103. package/dist/feature/featureFactory.js +59 -0
  104. package/dist/feature/featureFactory.js.map +1 -0
  105. package/dist/feature/featureModule.d.ts +28 -0
  106. package/dist/feature/featureModule.d.ts.map +1 -0
  107. package/dist/feature/featureModule.js +33 -0
  108. package/dist/feature/featureModule.js.map +1 -0
  109. package/dist/feature/featureNativeTree.d.ts +26 -0
  110. package/dist/feature/featureNativeTree.d.ts.map +1 -0
  111. package/dist/feature/featureNativeTree.js +2 -0
  112. package/dist/feature/featureNativeTree.js.map +1 -0
  113. package/dist/feature/featureRequirement.d.ts +43 -0
  114. package/dist/feature/featureRequirement.d.ts.map +1 -0
  115. package/dist/feature/featureRequirement.js +6 -0
  116. package/dist/feature/featureRequirement.js.map +1 -0
  117. package/dist/feature/featureVariants.d.ts +9 -0
  118. package/dist/feature/featureVariants.d.ts.map +1 -0
  119. package/dist/feature/featureVariants.js +59 -0
  120. package/dist/feature/featureVariants.js.map +1 -0
  121. package/dist/feature/mountFeature.d.ts +10 -0
  122. package/dist/feature/mountFeature.d.ts.map +1 -0
  123. package/dist/feature/mountFeature.js +14 -0
  124. package/dist/feature/mountFeature.js.map +1 -0
  125. package/dist/graph/closure.d.ts +77 -0
  126. package/dist/graph/closure.d.ts.map +1 -0
  127. package/dist/graph/closure.js +4 -0
  128. package/dist/graph/closure.js.map +1 -0
  129. package/dist/graph/services.d.ts +30 -0
  130. package/dist/graph/services.d.ts.map +1 -0
  131. package/dist/graph/services.js +2 -0
  132. package/dist/graph/services.js.map +1 -0
  133. package/dist/graph/summary.d.ts +60 -0
  134. package/dist/graph/summary.d.ts.map +1 -0
  135. package/dist/graph/summary.js +7 -0
  136. package/dist/graph/summary.js.map +1 -0
  137. package/dist/index.d.ts +51 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +34 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/internal/bucketCache.d.ts +19 -0
  142. package/dist/internal/bucketCache.d.ts.map +1 -0
  143. package/dist/internal/bucketCache.js +20 -0
  144. package/dist/internal/bucketCache.js.map +1 -0
  145. package/dist/internal/capture.d.ts +17 -0
  146. package/dist/internal/capture.d.ts.map +1 -0
  147. package/dist/internal/capture.js +5 -0
  148. package/dist/internal/capture.js.map +1 -0
  149. package/dist/internal/env.d.ts +2 -0
  150. package/dist/internal/env.d.ts.map +1 -0
  151. package/dist/internal/env.js +5 -0
  152. package/dist/internal/env.js.map +1 -0
  153. package/dist/internal/errors.d.ts +48 -0
  154. package/dist/internal/errors.d.ts.map +1 -0
  155. package/dist/internal/errors.js +69 -0
  156. package/dist/internal/errors.js.map +1 -0
  157. package/dist/internal/inspect.d.ts +5 -0
  158. package/dist/internal/inspect.d.ts.map +1 -0
  159. package/dist/internal/inspect.js +20 -0
  160. package/dist/internal/inspect.js.map +1 -0
  161. package/dist/internal/queryDriver.d.ts +7 -0
  162. package/dist/internal/queryDriver.d.ts.map +1 -0
  163. package/dist/internal/queryDriver.js +198 -0
  164. package/dist/internal/queryDriver.js.map +1 -0
  165. package/dist/internal/queryDriverHydrate.d.ts +17 -0
  166. package/dist/internal/queryDriverHydrate.d.ts.map +1 -0
  167. package/dist/internal/queryDriverHydrate.js +19 -0
  168. package/dist/internal/queryDriverHydrate.js.map +1 -0
  169. package/dist/internal/queryDriverStore.d.ts +54 -0
  170. package/dist/internal/queryDriverStore.d.ts.map +1 -0
  171. package/dist/internal/queryDriverStore.js +77 -0
  172. package/dist/internal/queryDriverStore.js.map +1 -0
  173. package/dist/internal/queryDriverTypes.d.ts +41 -0
  174. package/dist/internal/queryDriverTypes.d.ts.map +1 -0
  175. package/dist/internal/queryDriverTypes.js +9 -0
  176. package/dist/internal/queryDriverTypes.js.map +1 -0
  177. package/dist/internal/queryEvents.d.ts +12 -0
  178. package/dist/internal/queryEvents.d.ts.map +1 -0
  179. package/dist/internal/queryEvents.js +11 -0
  180. package/dist/internal/queryEvents.js.map +1 -0
  181. package/dist/internal/queryStore.d.ts +13 -0
  182. package/dist/internal/queryStore.d.ts.map +1 -0
  183. package/dist/internal/queryStore.js +11 -0
  184. package/dist/internal/queryStore.js.map +1 -0
  185. package/dist/internal/reuse.d.ts +2 -0
  186. package/dist/internal/reuse.d.ts.map +1 -0
  187. package/dist/internal/reuse.js +55 -0
  188. package/dist/internal/reuse.js.map +1 -0
  189. package/dist/internal/scheduler.d.ts +13 -0
  190. package/dist/internal/scheduler.d.ts.map +1 -0
  191. package/dist/internal/scheduler.js +38 -0
  192. package/dist/internal/scheduler.js.map +1 -0
  193. package/dist/internal/seeds.d.ts +3 -0
  194. package/dist/internal/seeds.d.ts.map +1 -0
  195. package/dist/internal/seeds.js +3 -0
  196. package/dist/internal/seeds.js.map +1 -0
  197. package/dist/internal/sources.d.ts +20 -0
  198. package/dist/internal/sources.d.ts.map +1 -0
  199. package/dist/internal/sources.js +39 -0
  200. package/dist/internal/sources.js.map +1 -0
  201. package/dist/internal/stateRegistry.d.ts +2 -0
  202. package/dist/internal/stateRegistry.d.ts.map +1 -0
  203. package/dist/internal/stateRegistry.js +15 -0
  204. package/dist/internal/stateRegistry.js.map +1 -0
  205. package/dist/internal/store.d.ts +15 -0
  206. package/dist/internal/store.d.ts.map +1 -0
  207. package/dist/internal/store.js +61 -0
  208. package/dist/internal/store.js.map +1 -0
  209. package/dist/internal/track.d.ts +15 -0
  210. package/dist/internal/track.d.ts.map +1 -0
  211. package/dist/internal/track.js +11 -0
  212. package/dist/internal/track.js.map +1 -0
  213. package/dist/internal/variance.d.ts +4 -0
  214. package/dist/internal/variance.d.ts.map +1 -0
  215. package/dist/internal/variance.js +2 -0
  216. package/dist/internal/variance.js.map +1 -0
  217. package/dist/internal.d.ts +42 -0
  218. package/dist/internal.d.ts.map +1 -0
  219. package/dist/internal.js +58 -0
  220. package/dist/internal.js.map +1 -0
  221. package/dist/namespace/namespace.d.ts +24 -0
  222. package/dist/namespace/namespace.d.ts.map +1 -0
  223. package/dist/namespace/namespace.js +20 -0
  224. package/dist/namespace/namespace.js.map +1 -0
  225. package/dist/procedure/procedure.d.ts +32 -0
  226. package/dist/procedure/procedure.d.ts.map +1 -0
  227. package/dist/procedure/procedure.js +34 -0
  228. package/dist/procedure/procedure.js.map +1 -0
  229. package/dist/reducer/reducer.d.ts +35 -0
  230. package/dist/reducer/reducer.d.ts.map +1 -0
  231. package/dist/reducer/reducer.js +73 -0
  232. package/dist/reducer/reducer.js.map +1 -0
  233. package/dist/remote/pendingQueue.d.ts +20 -0
  234. package/dist/remote/pendingQueue.d.ts.map +1 -0
  235. package/dist/remote/pendingQueue.js +113 -0
  236. package/dist/remote/pendingQueue.js.map +1 -0
  237. package/dist/remote/remoteState.d.ts +13 -0
  238. package/dist/remote/remoteState.d.ts.map +1 -0
  239. package/dist/remote/remoteState.js +176 -0
  240. package/dist/remote/remoteState.js.map +1 -0
  241. package/dist/remote/remoteStateMake.d.ts +8 -0
  242. package/dist/remote/remoteStateMake.d.ts.map +1 -0
  243. package/dist/remote/remoteStateMake.js +46 -0
  244. package/dist/remote/remoteStateMake.js.map +1 -0
  245. package/dist/remote/remoteStateSend.d.ts +41 -0
  246. package/dist/remote/remoteStateSend.d.ts.map +1 -0
  247. package/dist/remote/remoteStateSend.js +67 -0
  248. package/dist/remote/remoteStateSend.js.map +1 -0
  249. package/dist/remote/remoteStateTypes.d.ts +103 -0
  250. package/dist/remote/remoteStateTypes.d.ts.map +1 -0
  251. package/dist/remote/remoteStateTypes.js +4 -0
  252. package/dist/remote/remoteStateTypes.js.map +1 -0
  253. package/dist/runtime/appRuntime.d.ts +10 -0
  254. package/dist/runtime/appRuntime.d.ts.map +1 -0
  255. package/dist/runtime/appRuntime.js +6 -0
  256. package/dist/runtime/appRuntime.js.map +1 -0
  257. package/dist/runtime/bus.d.ts +17 -0
  258. package/dist/runtime/bus.d.ts.map +1 -0
  259. package/dist/runtime/bus.js +12 -0
  260. package/dist/runtime/bus.js.map +1 -0
  261. package/dist/runtime/capturedAppRuntime.d.ts +7 -0
  262. package/dist/runtime/capturedAppRuntime.d.ts.map +1 -0
  263. package/dist/runtime/capturedAppRuntime.js +151 -0
  264. package/dist/runtime/capturedAppRuntime.js.map +1 -0
  265. package/dist/runtime/eventBudget.d.ts +32 -0
  266. package/dist/runtime/eventBudget.d.ts.map +1 -0
  267. package/dist/runtime/eventBudget.js +67 -0
  268. package/dist/runtime/eventBudget.js.map +1 -0
  269. package/dist/runtime/featureMount.d.ts +16 -0
  270. package/dist/runtime/featureMount.d.ts.map +1 -0
  271. package/dist/runtime/featureMount.js +54 -0
  272. package/dist/runtime/featureMount.js.map +1 -0
  273. package/dist/runtime/instrumentation.d.ts +18 -0
  274. package/dist/runtime/instrumentation.d.ts.map +1 -0
  275. package/dist/runtime/instrumentation.js +16 -0
  276. package/dist/runtime/instrumentation.js.map +1 -0
  277. package/dist/runtime/loop.d.ts +32 -0
  278. package/dist/runtime/loop.d.ts.map +1 -0
  279. package/dist/runtime/loop.js +128 -0
  280. package/dist/runtime/loop.js.map +1 -0
  281. package/dist/runtime/queries.d.ts +20 -0
  282. package/dist/runtime/queries.d.ts.map +1 -0
  283. package/dist/runtime/queries.js +21 -0
  284. package/dist/runtime/queries.js.map +1 -0
  285. package/dist/runtime/runtimeHandle.d.ts +56 -0
  286. package/dist/runtime/runtimeHandle.d.ts.map +1 -0
  287. package/dist/runtime/runtimeHandle.js +2 -0
  288. package/dist/runtime/runtimeHandle.js.map +1 -0
  289. package/dist/scene/scene.d.ts +55 -0
  290. package/dist/scene/scene.d.ts.map +1 -0
  291. package/dist/scene/scene.js +74 -0
  292. package/dist/scene/scene.js.map +1 -0
  293. package/dist/state/state.d.ts +54 -0
  294. package/dist/state/state.d.ts.map +1 -0
  295. package/dist/state/state.js +41 -0
  296. package/dist/state/state.js.map +1 -0
  297. package/dist/state/stateFamily.d.ts +59 -0
  298. package/dist/state/stateFamily.d.ts.map +1 -0
  299. package/dist/state/stateFamily.js +91 -0
  300. package/dist/state/stateFamily.js.map +1 -0
  301. package/dist/state/stateGroup.d.ts +36 -0
  302. package/dist/state/stateGroup.d.ts.map +1 -0
  303. package/dist/state/stateGroup.js +33 -0
  304. package/dist/state/stateGroup.js.map +1 -0
  305. package/dist/state/token.d.ts +35 -0
  306. package/dist/state/token.d.ts.map +1 -0
  307. package/dist/state/token.js +18 -0
  308. package/dist/state/token.js.map +1 -0
  309. package/dist/synced/syncedStore.d.ts +44 -0
  310. package/dist/synced/syncedStore.d.ts.map +1 -0
  311. package/dist/synced/syncedStore.js +32 -0
  312. package/dist/synced/syncedStore.js.map +1 -0
  313. package/dist/testkit/flight.testkit.d.ts +18 -0
  314. package/dist/testkit/flight.testkit.d.ts.map +1 -0
  315. package/dist/testkit/flight.testkit.js +45 -0
  316. package/dist/testkit/flight.testkit.js.map +1 -0
  317. package/dist/ui/node.d.ts +3 -0
  318. package/dist/ui/node.d.ts.map +1 -0
  319. package/dist/ui/node.js +2 -0
  320. package/dist/ui/node.js.map +1 -0
  321. package/dist/ui/trigger.d.ts +2 -0
  322. package/dist/ui/trigger.d.ts.map +1 -0
  323. package/dist/ui/trigger.js +2 -0
  324. package/dist/ui/trigger.js.map +1 -0
  325. package/dist/wire/tree.d.ts +32 -0
  326. package/dist/wire/tree.d.ts.map +1 -0
  327. package/dist/wire/tree.js +59 -0
  328. package/dist/wire/tree.js.map +1 -0
  329. package/dist/wire/triggers.d.ts +25 -0
  330. package/dist/wire/triggers.d.ts.map +1 -0
  331. package/dist/wire/triggers.js +31 -0
  332. package/dist/wire/triggers.js.map +1 -0
  333. package/package.json +17 -5
  334. package/src/boundary/boundary.test.ts +26 -23
  335. package/src/calc/asyncCalc.invalidate.test.ts +258 -55
  336. package/src/calc/asyncCalc.test.ts +225 -85
  337. package/src/calc/asyncCalc.ts +27 -141
  338. package/src/calc/asyncCalcTypes.test.ts +64 -0
  339. package/src/calc/asyncCalcTypes.ts +139 -0
  340. package/src/calc/calc.test.ts +10 -11
  341. package/src/calc/calcFamily.test.ts +12 -12
  342. package/src/channel/channel.ts +8 -106
  343. package/src/channel/procedures.ts +108 -0
  344. package/src/compose/composition.ts +49 -246
  345. package/src/compose/compositionTypes.ts +249 -0
  346. package/src/event/event.fromSource.test.ts +12 -12
  347. package/src/feature/feature.ts +50 -1210
  348. package/src/feature/feature.typecheck.ts +2 -61
  349. package/src/feature/featureBinding.ts +158 -0
  350. package/src/feature/featureClass.ts +162 -0
  351. package/src/feature/featureConfig.ts +192 -0
  352. package/src/feature/featureFactory.ts +163 -0
  353. package/src/feature/featureModule.ts +136 -0
  354. package/src/feature/featureModule.typecheck.ts +63 -0
  355. package/src/feature/featureNativeTree.ts +152 -0
  356. package/src/feature/featureRequirement.ts +82 -0
  357. package/src/feature/featureVariants.ts +234 -0
  358. package/src/feature/mountFeature.ts +54 -0
  359. package/src/graph/closure.ts +45 -226
  360. package/src/graph/services.ts +95 -0
  361. package/src/graph/summary.ts +134 -0
  362. package/src/internal/queryDriver.ts +20 -89
  363. package/src/internal/queryDriverHydrate.ts +45 -0
  364. package/src/internal/queryDriverTypes.ts +8 -10
  365. package/src/internal/store.test.ts +27 -6
  366. package/src/remote/pendingQueue.ts +145 -0
  367. package/src/remote/remoteState.test.ts +28 -22
  368. package/src/remote/remoteState.ts +63 -489
  369. package/src/remote/remoteStateMake.ts +97 -0
  370. package/src/remote/remoteStateSend.ts +122 -0
  371. package/src/remote/remoteStateTypes.ts +155 -0
  372. package/src/runtime/appRuntime.activation.test.ts +9 -5
  373. package/src/runtime/appRuntime.test.ts +40 -16
  374. package/src/runtime/appRuntime.ts +17 -472
  375. package/src/runtime/capturedAppRuntime.ts +274 -0
  376. package/src/runtime/eventBudget.test.ts +38 -8
  377. package/src/runtime/featureMount.ts +94 -0
  378. package/src/runtime/hardening.test.ts +18 -4
  379. package/src/runtime/instrumentation.test.ts +49 -12
  380. package/src/runtime/loop.test.ts +51 -10
  381. package/src/runtime/runtimeHandle.ts +124 -0
  382. package/src/state/stateFamily.test.ts +25 -8
  383. package/src/testkit/flight.testkit.ts +9 -0
@@ -0,0 +1,134 @@
1
+ import type { UiContract } from '../compose/ui'
2
+
3
+ export const FeatureGraphValidTypeId: unique symbol = Symbol.for('reform/FeatureGraphValid')
4
+ export type FeatureGraphValidTypeId = typeof FeatureGraphValidTypeId
5
+
6
+ export const FeatureModuleGraphValidTypeId: unique symbol = Symbol.for(
7
+ 'reform/FeatureModuleGraphValid',
8
+ )
9
+ export type FeatureModuleGraphValidTypeId = typeof FeatureModuleGraphValidTypeId
10
+
11
+ export const FeatureChildSummaryTypeId: unique symbol = Symbol.for('reform/FeatureChildSummary')
12
+ export type FeatureChildSummaryTypeId = typeof FeatureChildSummaryTypeId
13
+
14
+ export const FeatureGraphSummaryTypeId: unique symbol = Symbol.for('reform/FeatureGraphSummary')
15
+ export type FeatureGraphSummaryTypeId = typeof FeatureGraphSummaryTypeId
16
+
17
+ export const SlotBindingSummaryTypeId: unique symbol = Symbol.for('reform/SlotBindingSummary')
18
+ export type SlotBindingSummaryTypeId = typeof SlotBindingSummaryTypeId
19
+
20
+ export interface SlotBindingSummary {
21
+ readonly [SlotBindingSummaryTypeId]: SlotBindingSummaryTypeId
22
+ }
23
+
24
+ /**
25
+ * The constant-size proof published after a Feature's current module has passed
26
+ * graph validation. Type-only facts use empty-or-singleton tuples so the runtime
27
+ * summary stays compact while direct indexed access recovers their exact types.
28
+ */
29
+ export interface FeatureGraphSummary<
30
+ out FeatureIdentity,
31
+ out MountProps,
32
+ out CompositionProps,
33
+ out C extends UiContract,
34
+ out S extends ReadonlyArray<unknown>,
35
+ out N extends string,
36
+ out CompositionIdentity,
37
+ out OpenServices,
38
+ out SupportOpenServices,
39
+ out UsesInput extends boolean,
40
+ out Input,
41
+ > extends SlotBindingSummary {
42
+ readonly [FeatureGraphSummaryTypeId]: {
43
+ readonly featureIdentity: readonly [] | readonly [FeatureIdentity]
44
+ readonly mountProps: readonly [] | readonly [MountProps]
45
+ readonly compositionProps: readonly [] | readonly [CompositionProps]
46
+ readonly contract: readonly [] | readonly [C]
47
+ readonly states: readonly [] | readonly [S]
48
+ readonly name: N
49
+ readonly compositionIdentity: readonly [] | readonly [CompositionIdentity]
50
+ readonly openServices: readonly [] | readonly [OpenServices]
51
+ readonly supportOpenServices: readonly [] | readonly [SupportOpenServices]
52
+ readonly usesInput: readonly [] | readonly [UsesInput]
53
+ readonly input: readonly [] | readonly [Input]
54
+ readonly validated: FeatureModuleGraphValidTypeId
55
+ }
56
+ readonly [FeatureChildSummaryTypeId]: {
57
+ readonly featureIdentity: readonly [] | readonly [FeatureIdentity]
58
+ readonly compositionIdentity: readonly [] | readonly [CompositionIdentity]
59
+ }
60
+ }
61
+
62
+ export type FeatureChildSummary<
63
+ FeatureIdentity,
64
+ MountProps,
65
+ CompositionProps,
66
+ C extends UiContract,
67
+ S extends ReadonlyArray<unknown>,
68
+ N extends string,
69
+ CompositionIdentity,
70
+ OpenServices,
71
+ SupportOpenServices,
72
+ UsesInput extends boolean,
73
+ Input,
74
+ > = FeatureGraphSummary<
75
+ FeatureIdentity,
76
+ MountProps,
77
+ CompositionProps,
78
+ C,
79
+ S,
80
+ N,
81
+ CompositionIdentity,
82
+ OpenServices,
83
+ SupportOpenServices,
84
+ UsesInput,
85
+ Input
86
+ >
87
+
88
+ export type AnyFeatureGraphSummary = FeatureGraphSummary<
89
+ unknown,
90
+ unknown,
91
+ unknown,
92
+ UiContract,
93
+ ReadonlyArray<unknown>,
94
+ string,
95
+ unknown,
96
+ unknown,
97
+ unknown,
98
+ boolean,
99
+ unknown
100
+ >
101
+
102
+ export type FeatureSummaryIdentity<Summary extends AnyFeatureGraphSummary> =
103
+ Summary[FeatureGraphSummaryTypeId]['featureIdentity'][number]
104
+ export type FeatureSummaryMountProps<Summary extends AnyFeatureGraphSummary> =
105
+ Summary[FeatureGraphSummaryTypeId]['mountProps'][number]
106
+ export type FeatureSummaryCompositionProps<Summary extends AnyFeatureGraphSummary> =
107
+ Summary[FeatureGraphSummaryTypeId]['compositionProps'][number]
108
+ export type FeatureSummaryContract<Summary extends AnyFeatureGraphSummary> =
109
+ Summary[FeatureGraphSummaryTypeId]['contract'][number]
110
+ export type FeatureSummaryStates<Summary extends AnyFeatureGraphSummary> =
111
+ Summary[FeatureGraphSummaryTypeId]['states'][number]
112
+ export type FeatureSummaryName<Summary extends AnyFeatureGraphSummary> =
113
+ Summary[FeatureGraphSummaryTypeId]['name']
114
+ export type FeatureSummaryCompositionIdentity<Summary extends AnyFeatureGraphSummary> =
115
+ Summary[FeatureGraphSummaryTypeId]['compositionIdentity'][number]
116
+ export type FeatureSummaryOpenServices<Summary extends AnyFeatureGraphSummary> =
117
+ Summary[FeatureGraphSummaryTypeId]['openServices'][number]
118
+ export type FeatureSummarySupportOpenServices<Summary extends AnyFeatureGraphSummary> =
119
+ Summary[FeatureGraphSummaryTypeId]['supportOpenServices'][number]
120
+ export type FeatureSummaryUsesInput<Summary extends AnyFeatureGraphSummary> =
121
+ Summary[FeatureGraphSummaryTypeId]['usesInput'][number]
122
+ export type FeatureSummaryInput<Summary extends AnyFeatureGraphSummary> =
123
+ Summary[FeatureGraphSummaryTypeId]['input'][number]
124
+
125
+ export const CompositionChildSummaryTypeId: unique symbol = Symbol.for(
126
+ 'reform/CompositionChildSummary',
127
+ )
128
+ export type CompositionChildSummaryTypeId = typeof CompositionChildSummaryTypeId
129
+
130
+ export interface CompositionChildSummary<out CompositionIdentity> extends SlotBindingSummary {
131
+ readonly [CompositionChildSummaryTypeId]: {
132
+ readonly compositionIdentity: CompositionIdentity
133
+ }
134
+ }
@@ -1,21 +1,10 @@
1
- import {
2
- Cause,
3
- Chunk,
4
- Effect,
5
- MutableRef,
6
- Option,
7
- type ParseResult,
8
- Queue,
9
- Runtime,
10
- type Scope,
11
- Stream,
12
- } from 'effect'
13
- import { type AnyAsyncData } from '../calc/asyncData'
1
+ import { Cause, Chunk, Effect, MutableRef, Option, Queue, Runtime, type Scope, Stream } from 'effect'
14
2
  import { empty, type QueryState } from '../calc/queryState'
15
3
  import { Queries, type QueryHandle } from '../runtime/queries'
16
4
  import { reuse } from './reuse'
17
5
  import { resolveInstrumentation } from '../runtime/instrumentation'
18
6
  import { resolveQueryStore } from './queryStore'
7
+ import { hydratePersistedQuery } from './queryDriverHydrate'
19
8
  import {
20
9
  incrementGeneration,
21
10
  makeQueryProjection,
@@ -23,62 +12,24 @@ import {
23
12
  settleQueryError,
24
13
  withReaderTracking,
25
14
  } from './queryDriverStore'
15
+ import type { QueryDriver, QueryDriverOptions } from './queryDriverTypes'
26
16
  import { resolveScheduler } from './scheduler'
27
- import {
28
- type InputsObject,
29
- type InputStores,
30
- type InvalidateBy,
31
- sameKey,
32
- wireSources,
33
- } from './sources'
34
- import { makeStore, type Store } from './store'
17
+ import { type InputsObject, type InputStores, sameKey, wireSources } from './sources'
18
+ import { makeStore } from './store'
35
19
  import { type AnySource } from '../state/token'
36
20
 
37
21
  export {
38
22
  bumpRevision,
39
23
  gatedFlag,
40
24
  type GatedOf,
25
+ type QueryCodec,
26
+ type QueryDriver,
27
+ type QueryDriverOptions,
41
28
  Revision,
42
29
  RevisionSchema,
43
30
  revisionZero,
44
31
  } from './queryDriverTypes'
45
32
 
46
- // Keep schema invariance behind a covariant persistence boundary.
47
- export interface QueryCodec<A> {
48
- readonly encode: (value: A) => Effect.Effect<unknown, ParseResult.ParseError, never>
49
- readonly decode: (input: unknown) => Effect.Effect<A, ParseResult.ParseError, never>
50
- }
51
-
52
- export interface QueryDriverOptions<Inputs extends ReadonlyArray<AnySource>, A, E, R> {
53
- readonly name: string
54
- readonly label: string
55
- readonly gated: boolean
56
- readonly inputs: Inputs
57
- readonly query: (inputs: InputsObject<Inputs>) => Effect.Effect<A, E, R>
58
- readonly invalidateBy?: InvalidateBy<Inputs> | undefined
59
- readonly disabled?: ((inputs: InputsObject<Inputs>) => boolean) | undefined
60
- readonly coalesce?: 'switch' | 'trailing' | undefined
61
- readonly reuse?: boolean | undefined
62
- readonly extraKey?:
63
- | {
64
- readonly read: () => unknown
65
- readonly subscribe: (listener: () => void) => () => void
66
- }
67
- | undefined
68
- readonly persist?:
69
- | {
70
- readonly key: string | ((inputs: InputsObject<Inputs>) => string)
71
- readonly codec: QueryCodec<A>
72
- }
73
- | undefined
74
- readonly onSettled?: ((generation: number) => void) | undefined
75
- }
76
-
77
- export interface QueryDriver<A, E> {
78
- readonly store: Store<AnyAsyncData<A, E>>
79
- readonly requested: () => number
80
- }
81
-
82
33
  // oxlint-disable-next-line reform-rules/prefer-effect-fn -- generic export: Effect.fn's inferred type isn't portable under isolatedDeclarations
83
34
  export const makeQueryDriver = <Inputs extends ReadonlyArray<AnySource>, A, E, R>(
84
35
  options: QueryDriverOptions<Inputs, A, E, R>,
@@ -268,42 +219,22 @@ export const makeQueryDriver = <Inputs extends ReadonlyArray<AnySource>, A, E, R
268
219
  yield* Effect.addFinalizer(() => Effect.sync(unsubscribe))
269
220
 
270
221
  // Fork hydration so an asynchronous store never blocks layer acquisition.
271
- // Epoch, key, and settled-state guards prevent a late cache from replacing newer truth.
272
222
  if (persist !== undefined && !disabledNow()) {
273
223
  const hydrateArgs = sources.snapshot()
274
224
  const hydrateSourceKey = keyOf(hydrateArgs)
275
225
  const hydrateSourceEpoch = MutableRef.get(sourceEpoch)
276
- const hydrateStaleAtGeneration = MutableRef.get(generation)
277
- const hydrate = queryStore.get(persistKey(persist.key, hydrateArgs)).pipe(
278
- Effect.flatMap((cached) =>
279
- Option.match(cached, {
280
- onNone: () => Effect.void,
281
- onSome: (raw) =>
282
- persist.codec.decode(raw).pipe(
283
- Effect.tap((decoded) =>
284
- Effect.sync(() => {
285
- const state = stateStore.get()
286
- if (
287
- disabledNow() ||
288
- hydrateSourceEpoch !== MutableRef.get(sourceEpoch) ||
289
- !sameKey(hydrateSourceKey, keyOf(sources.snapshot())) ||
290
- Option.isSome(state.data) ||
291
- Option.isSome(state.error)
292
- ) {
293
- return
294
- }
295
- MutableRef.update(staleAtGeneration, (current) =>
296
- Math.max(current, hydrateStaleAtGeneration),
297
- )
298
- stateStore.set({ ...state, data: Option.some(decoded), isStale: true })
299
- }),
300
- ),
301
- ),
302
- }),
303
- ),
304
- // Corrupt/incompatible cache: ignore and fetch fresh.
305
- Effect.ignore,
306
- )
226
+ const hydrate = hydratePersistedQuery({
227
+ queryStore,
228
+ cacheKey: persistKey(persist.key, hydrateArgs),
229
+ codec: persist.codec,
230
+ stateStore,
231
+ staleAtGeneration,
232
+ capturedGeneration: MutableRef.get(generation),
233
+ isSuperseded: () =>
234
+ disabledNow() ||
235
+ hydrateSourceEpoch !== MutableRef.get(sourceEpoch) ||
236
+ !sameKey(hydrateSourceKey, keyOf(sources.snapshot())),
237
+ })
307
238
  const runtime = yield* Effect.runtime<never>()
308
239
  const scope = yield* Effect.scope
309
240
  Runtime.runFork(runtime)(hydrate, { immediate: true, scope })
@@ -0,0 +1,45 @@
1
+ import { Effect, MutableRef, Option } from 'effect'
2
+ import type { QueryState } from '../calc/queryState'
3
+ import type { QueryStoreApi } from './queryStore'
4
+ import type { QueryCodec } from './queryDriverTypes'
5
+ import type { Store } from './store'
6
+
7
+ interface HydratePersistedOptions<A, E> {
8
+ readonly queryStore: QueryStoreApi
9
+ readonly cacheKey: string
10
+ readonly codec: QueryCodec<A>
11
+ readonly stateStore: Store<QueryState<A, E>>
12
+ readonly staleAtGeneration: MutableRef.MutableRef<number>
13
+ readonly capturedGeneration: number
14
+ // True once the sources moved past the snapshot this hydration was captured for.
15
+ readonly isSuperseded: () => boolean
16
+ }
17
+
18
+ const paint = <A, E>(options: HydratePersistedOptions<A, E>, decoded: A): void => {
19
+ const state = options.stateStore.get()
20
+ if (options.isSuperseded() || Option.isSome(state.data) || Option.isSome(state.error)) {
21
+ return
22
+ }
23
+ MutableRef.update(options.staleAtGeneration, (current) =>
24
+ Math.max(current, options.capturedGeneration),
25
+ )
26
+ options.stateStore.set({ ...state, data: Option.some(decoded), isStale: true })
27
+ }
28
+
29
+ // Epoch, key, and settled-state guards prevent a late cache from replacing newer truth.
30
+ export const hydratePersistedQuery = <A, E>(
31
+ options: HydratePersistedOptions<A, E>,
32
+ ): Effect.Effect<void> =>
33
+ options.queryStore.get(options.cacheKey).pipe(
34
+ Effect.flatMap((cached) =>
35
+ Option.match(cached, {
36
+ onNone: () => Effect.void,
37
+ onSome: (raw) =>
38
+ options.codec
39
+ .decode(raw)
40
+ .pipe(Effect.tap((decoded) => Effect.sync(() => paint(options, decoded)))),
41
+ }),
42
+ ),
43
+ // Corrupt/incompatible cache: ignore and fetch fresh.
44
+ Effect.ignore,
45
+ )
@@ -1,9 +1,8 @@
1
- import { Brand, Effect, Schema } from 'effect'
1
+ import { Brand, Effect, type ParseResult, Schema } from 'effect'
2
2
  import type { AnyAsyncData } from '../calc/asyncData'
3
3
  import type { InputsObject, InvalidateBy } from './sources'
4
4
  import type { Store } from './store'
5
5
  import type { AnySource } from '../state/token'
6
- import type { AnySchema } from './variance'
7
6
 
8
7
  export type GatedOf<AlwaysOn extends boolean> = AlwaysOn extends true ? false : true
9
8
 
@@ -21,35 +20,34 @@ export const RevisionSchema: Schema.Schema<Revision, number> = Schema.Number.pip
21
20
  export const revisionZero: Revision = Revision(0)
22
21
  export const bumpRevision = (revision: Revision): Revision => Revision(revision + 1)
23
22
 
23
+ // Keep schema invariance behind a covariant persistence boundary.
24
+ export interface QueryCodec<A> {
25
+ readonly encode: (value: A) => Effect.Effect<unknown, ParseResult.ParseError, never>
26
+ readonly decode: (input: unknown) => Effect.Effect<A, ParseResult.ParseError, never>
27
+ }
28
+
24
29
  export interface QueryDriverOptions<Inputs extends ReadonlyArray<AnySource>, A, E, R> {
25
30
  readonly name: string
26
31
  readonly label: string
27
32
  readonly gated: boolean
28
33
  readonly inputs: Inputs
29
34
  readonly query: (inputs: InputsObject<Inputs>) => Effect.Effect<A, E, R>
30
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
31
35
  readonly invalidateBy?: InvalidateBy<Inputs> | undefined
32
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
33
36
  readonly disabled?: ((inputs: InputsObject<Inputs>) => boolean) | undefined
34
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
35
37
  readonly coalesce?: 'switch' | 'trailing' | undefined
36
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
37
38
  readonly reuse?: boolean | undefined
38
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
39
39
  readonly extraKey?:
40
40
  | {
41
41
  readonly read: () => unknown
42
42
  readonly subscribe: (listener: () => void) => () => void
43
43
  }
44
44
  | undefined
45
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
46
45
  readonly persist?:
47
46
  | {
48
47
  readonly key: string | ((inputs: InputsObject<Inputs>) => string)
49
- readonly schema: AnySchema<A>
48
+ readonly codec: QueryCodec<A>
50
49
  }
51
50
  | undefined
52
- // oxlint-disable-next-line reform-rules/no-optional-fields -- caller-omitted option built at the asyncCalc/remoteState construction site; Option<T> would change that call shape
53
51
  readonly onSettled?: ((generation: number) => void) | undefined
54
52
  }
55
53
 
@@ -1,7 +1,14 @@
1
1
  import { expect, test } from 'vitest'
2
2
  import { makeStore } from './store'
3
3
 
4
- const tick = () => new Promise<void>((resolve) => queueMicrotask(resolve))
4
+ // Notifications land on the microtask queue poll turns of it, never wall-clock.
5
+ const until = async (predicate: () => boolean, rounds = 1000): Promise<void> => {
6
+ if (predicate() || rounds <= 0) {
7
+ return
8
+ }
9
+ await new Promise<void>((resolve) => queueMicrotask(resolve))
10
+ return until(predicate, rounds - 1)
11
+ }
5
12
 
6
13
  const counter = () => ({ n: 0 })
7
14
 
@@ -14,19 +21,26 @@ test('subscribe observes changes; getSnapshot is always current', async () => {
14
21
 
15
22
  store.set(1)
16
23
  expect(store.getSnapshot()).toBe(1)
17
- await tick()
24
+ await until(() => notifications.n > 0)
18
25
  expect(notifications.n).toBe(1)
19
26
  })
20
27
 
21
28
  test('a no-op write notifies no one', async () => {
22
29
  const store = makeStore('same')
30
+ const witness = makeStore(0)
23
31
  const notifications = counter()
32
+ const witnessWakes = counter()
24
33
  store.subscribe(() => {
25
34
  notifications.n++
26
35
  })
36
+ witness.subscribe(() => {
37
+ witnessWakes.n++
38
+ })
27
39
 
28
40
  store.set('same')
29
- await tick()
41
+ // Both stores share the scheduler, so the witness flush drains anything the no-op could have queued.
42
+ witness.set(1)
43
+ await until(() => witnessWakes.n > 0)
30
44
  expect(notifications.n).toBe(0)
31
45
  expect(store.getSnapshot()).toBe('same')
32
46
  })
@@ -42,7 +56,8 @@ test('a batch of writes coalesces into a single notification per microtask', asy
42
56
  store.set(2)
43
57
  store.set(3)
44
58
  expect(store.getSnapshot()).toBe(3)
45
- await tick()
59
+ // The drain is synchronous: the first wake already carries every write the batch would have fired.
60
+ await until(() => notifications.n > 0)
46
61
  expect(notifications.n).toBe(1)
47
62
  })
48
63
 
@@ -59,20 +74,26 @@ test('stores are independent — one slice does not wake another', async () => {
59
74
  })
60
75
 
61
76
  a.set(1)
62
- await tick()
77
+ await until(() => aWakes.n > 0)
63
78
  expect(aWakes.n).toBe(1)
64
79
  expect(bWakes.n).toBe(0)
65
80
  })
66
81
 
67
82
  test('unsubscribe stops notifications', async () => {
68
83
  const store = makeStore(0)
84
+ const witness = makeStore(0)
69
85
  const notifications = counter()
86
+ const witnessWakes = counter()
70
87
  const unsubscribe = store.subscribe(() => {
71
88
  notifications.n++
72
89
  })
90
+ witness.subscribe(() => {
91
+ witnessWakes.n++
92
+ })
73
93
 
74
94
  unsubscribe()
75
95
  store.set(1)
76
- await tick()
96
+ witness.set(1)
97
+ await until(() => witnessWakes.n > 0)
77
98
  expect(notifications.n).toBe(0)
78
99
  })
@@ -0,0 +1,145 @@
1
+ import { Equal, MutableRef, Option } from 'effect'
2
+ import { inspectable } from '../internal/inspect'
3
+ import type { Scheduler } from '../internal/scheduler'
4
+ import type { Store } from '../internal/store'
5
+ import type { PendingIntent } from './remoteStateTypes'
6
+
7
+ export interface PendingNode<I> {
8
+ readonly entry: MutableRef.MutableRef<PendingIntent<I>>
9
+ }
10
+
11
+ // Duplicate opIds retain every node so indexed updates preserve the old map semantics.
12
+ export interface PendingOpEntry<I> {
13
+ readonly nodes: Set<PendingNode<I>>
14
+ }
15
+
16
+ export interface PendingQueue<I> {
17
+ readonly store: Store<ReadonlyArray<PendingIntent<I>>>
18
+ readonly revision: () => number
19
+ readonly fold: <B>(initial: B, combine: (accumulator: B, entry: PendingIntent<I>) => B) => B
20
+ readonly append: (entry: PendingIntent<I>) => void
21
+ readonly ack: (opId: string) => void
22
+ readonly settle: (opId: string) => void
23
+ }
24
+
25
+ export const makePendingQueue = <I>(scheduler: Scheduler): PendingQueue<I> => {
26
+ // Set insertion order preserves dispatch/apply order while allowing O(1) updates.
27
+ const ordered = new Set<PendingNode<I>>()
28
+ const byOpId = new Map<string, PendingOpEntry<I>>()
29
+ const listeners = new Set<() => void>()
30
+ const revision = MutableRef.make(0)
31
+ const initial: ReadonlyArray<PendingIntent<I>> = []
32
+ const snapshot = MutableRef.make<Option.Option<ReadonlyArray<PendingIntent<I>>>>(
33
+ Option.some(initial),
34
+ )
35
+
36
+ const materialize = (): ReadonlyArray<PendingIntent<I>> => {
37
+ const cached = MutableRef.get(snapshot)
38
+ if (Option.isSome(cached)) {
39
+ return cached.value
40
+ }
41
+ const current = Array.from(ordered, (node) => MutableRef.get(node.entry))
42
+ MutableRef.set(snapshot, Option.some(current))
43
+ return current
44
+ }
45
+
46
+ const announce = (next: Option.Option<ReadonlyArray<PendingIntent<I>>>): void => {
47
+ MutableRef.set(snapshot, next)
48
+ MutableRef.update(revision, (current) => current + 1)
49
+ scheduler.schedule(listeners)
50
+ }
51
+
52
+ const link = (entry: PendingIntent<I>): void => {
53
+ const node: PendingNode<I> = {
54
+ entry: MutableRef.make(entry),
55
+ }
56
+ ordered.add(node)
57
+ const indexed = byOpId.get(entry.opId)
58
+ if (indexed === undefined) {
59
+ byOpId.set(entry.opId, { nodes: new Set([node]) })
60
+ } else {
61
+ indexed.nodes.add(node)
62
+ }
63
+ }
64
+
65
+ const clear = (): void => {
66
+ ordered.clear()
67
+ byOpId.clear()
68
+ }
69
+
70
+ const fold = <B>(initialValue: B, combine: (accumulator: B, entry: PendingIntent<I>) => B): B => {
71
+ const folded = MutableRef.make(initialValue)
72
+ ordered.forEach((node) => {
73
+ MutableRef.update(folded, (accumulator) => combine(accumulator, MutableRef.get(node.entry)))
74
+ })
75
+ return MutableRef.get(folded)
76
+ }
77
+
78
+ const append = (entry: PendingIntent<I>): void => {
79
+ link(entry)
80
+ announce(Option.none())
81
+ }
82
+
83
+ const ack = (opId: string): void => {
84
+ const indexed = byOpId.get(opId)
85
+ if (indexed === undefined) {
86
+ return
87
+ }
88
+ const stillSending = MutableRef.make(false)
89
+ indexed.nodes.forEach((node) => {
90
+ if (MutableRef.get(node.entry).status === 'sending') {
91
+ MutableRef.set(stillSending, true)
92
+ }
93
+ })
94
+ if (!MutableRef.get(stillSending)) {
95
+ return
96
+ }
97
+ // Rebuild every duplicate once one is sending, matching the former array map.
98
+ indexed.nodes.forEach((node) => {
99
+ MutableRef.update(node.entry, (entry) => ({ ...entry, status: 'confirmed' as const }))
100
+ })
101
+ announce(Option.none())
102
+ }
103
+
104
+ const settle = (opId: string): void => {
105
+ const indexed = byOpId.get(opId)
106
+ if (indexed === undefined) {
107
+ return
108
+ }
109
+ indexed.nodes.forEach((node) => {
110
+ ordered.delete(node)
111
+ })
112
+ byOpId.delete(opId)
113
+ announce(Option.none())
114
+ }
115
+
116
+ const store: Store<ReadonlyArray<PendingIntent<I>>> = {
117
+ get: materialize,
118
+ getSnapshot: materialize,
119
+ getVersion: () => MutableRef.get(revision),
120
+ set: (next) => {
121
+ if (Equal.equals(materialize(), next)) {
122
+ return
123
+ }
124
+ clear()
125
+ next.forEach(link)
126
+ announce(Option.some(next))
127
+ },
128
+ subscribe: (listener) => {
129
+ listeners.add(listener)
130
+ return () => {
131
+ listeners.delete(listener)
132
+ }
133
+ },
134
+ ...inspectable(() => ({ _id: 'reform/Store', value: materialize() })),
135
+ }
136
+
137
+ return {
138
+ store,
139
+ revision: () => MutableRef.get(revision),
140
+ fold,
141
+ append,
142
+ ack,
143
+ settle,
144
+ }
145
+ }