@askrjs/askr 0.0.48 → 0.0.49

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 (394) hide show
  1. package/dist/benchmark.js +6 -6
  2. package/dist/benchmark.js.map +1 -1
  3. package/dist/boot/hydration.js +140 -0
  4. package/dist/boot/hydration.js.map +1 -0
  5. package/dist/boot/index.d.ts +3 -56
  6. package/dist/boot/index.d.ts.map +1 -1
  7. package/dist/boot/index.js +62 -410
  8. package/dist/boot/index.js.map +1 -1
  9. package/dist/boot/root-lifecycle.d.ts +15 -0
  10. package/dist/boot/root-lifecycle.d.ts.map +1 -0
  11. package/dist/boot/root-lifecycle.js +205 -0
  12. package/dist/boot/root-lifecycle.js.map +1 -0
  13. package/dist/boot/route-startup.js +45 -0
  14. package/dist/boot/route-startup.js.map +1 -0
  15. package/dist/boot/types.d.ts +62 -0
  16. package/dist/boot/types.d.ts.map +1 -0
  17. package/dist/common/component.d.ts +3 -1
  18. package/dist/common/component.d.ts.map +1 -1
  19. package/dist/common/jsx.d.ts +1 -1
  20. package/dist/common/jsx.d.ts.map +1 -1
  21. package/dist/common/jsx.js.map +1 -1
  22. package/dist/common/props.d.ts +1 -1
  23. package/dist/common/render-context.js +55 -0
  24. package/dist/common/render-context.js.map +1 -0
  25. package/dist/common/router.d.ts +5 -1
  26. package/dist/common/router.d.ts.map +1 -1
  27. package/dist/common/ssr.d.ts +1 -1
  28. package/dist/common/ssr.js.map +1 -1
  29. package/dist/common/vnode.d.ts +5 -4
  30. package/dist/common/vnode.d.ts.map +1 -1
  31. package/dist/common/vnode.js.map +1 -1
  32. package/dist/components/error-boundary.d.ts +1 -1
  33. package/dist/components/error-boundary.js +2 -1
  34. package/dist/components/error-boundary.js.map +1 -1
  35. package/dist/components/link.js.map +1 -1
  36. package/dist/control/for.js +2 -1
  37. package/dist/control/for.js.map +1 -1
  38. package/dist/data/data-runtime.d.ts +28 -0
  39. package/dist/data/data-runtime.d.ts.map +1 -0
  40. package/dist/data/data-runtime.js +114 -0
  41. package/dist/data/data-runtime.js.map +1 -0
  42. package/dist/data/index.d.ts +6 -142
  43. package/dist/data/index.js +5 -539
  44. package/dist/data/invalidation.d.ts +9 -0
  45. package/dist/data/invalidation.d.ts.map +1 -0
  46. package/dist/data/invalidation.js +27 -0
  47. package/dist/data/invalidation.js.map +1 -0
  48. package/dist/data/mutation-cell.d.ts +28 -0
  49. package/dist/data/mutation-cell.d.ts.map +1 -0
  50. package/dist/data/mutation-cell.js +132 -0
  51. package/dist/data/mutation-cell.js.map +1 -0
  52. package/dist/data/query-cell.d.ts +46 -0
  53. package/dist/data/query-cell.d.ts.map +1 -0
  54. package/dist/data/query-cell.js +290 -0
  55. package/dist/data/query-cell.js.map +1 -0
  56. package/dist/data/query-key.js +42 -0
  57. package/dist/data/query-key.js.map +1 -0
  58. package/dist/data/shared.js +20 -0
  59. package/dist/data/shared.js.map +1 -0
  60. package/dist/data/types.d.ts +161 -0
  61. package/dist/data/types.d.ts.map +1 -0
  62. package/dist/foundations/icon/icon.d.ts.map +1 -1
  63. package/dist/foundations/icon/icon.js.map +1 -1
  64. package/dist/foundations/structures/portal.d.ts +1 -1
  65. package/dist/foundations/structures/portal.d.ts.map +1 -1
  66. package/dist/foundations/structures/portal.js +2 -1
  67. package/dist/foundations/structures/portal.js.map +1 -1
  68. package/dist/fx/fx.d.ts +1 -1
  69. package/dist/fx/fx.js +5 -4
  70. package/dist/fx/fx.js.map +1 -1
  71. package/dist/fx/timing.d.ts.map +1 -1
  72. package/dist/fx/timing.js +10 -6
  73. package/dist/fx/timing.js.map +1 -1
  74. package/dist/index.d.ts +3 -2
  75. package/dist/index.js +4 -2
  76. package/dist/index.js.map +1 -1
  77. package/dist/jsx/index.d.ts +2 -1
  78. package/dist/jsx/index.js +3 -2
  79. package/dist/jsx/types.d.ts +2 -0
  80. package/dist/jsx/types.js +2 -1
  81. package/dist/jsx-runtime2.d.ts +2 -1
  82. package/dist/jsx-runtime2.js +3 -2
  83. package/dist/renderer/attributes.js +231 -0
  84. package/dist/renderer/attributes.js.map +1 -0
  85. package/dist/renderer/boundaries.js +266 -0
  86. package/dist/renderer/boundaries.js.map +1 -0
  87. package/dist/renderer/child-shape.js +85 -0
  88. package/dist/renderer/child-shape.js.map +1 -0
  89. package/dist/renderer/children.js +3 -2
  90. package/dist/renderer/children.js.map +1 -1
  91. package/dist/renderer/cleanup.js +2 -1
  92. package/dist/renderer/cleanup.js.map +1 -1
  93. package/dist/renderer/component-host-cleanup.js +68 -0
  94. package/dist/renderer/component-host-cleanup.js.map +1 -0
  95. package/dist/renderer/component-host-instances.js +65 -0
  96. package/dist/renderer/component-host-instances.js.map +1 -0
  97. package/dist/renderer/component-host.js +247 -0
  98. package/dist/renderer/component-host.js.map +1 -0
  99. package/dist/renderer/dom-host.js +13 -0
  100. package/dist/renderer/dom-host.js.map +1 -0
  101. package/dist/renderer/dom-internal.js +185 -0
  102. package/dist/renderer/dom-internal.js.map +1 -0
  103. package/dist/renderer/dom.js +9 -2356
  104. package/dist/renderer/element-children.js +279 -0
  105. package/dist/renderer/element-children.js.map +1 -0
  106. package/dist/renderer/env.js +2 -1
  107. package/dist/renderer/error-boundary-dom.js +29 -0
  108. package/dist/renderer/error-boundary-dom.js.map +1 -0
  109. package/dist/renderer/evaluate-dom-range.js +85 -0
  110. package/dist/renderer/evaluate-dom-range.js.map +1 -0
  111. package/dist/renderer/evaluate-reconcile.js +281 -0
  112. package/dist/renderer/evaluate-reconcile.js.map +1 -0
  113. package/dist/renderer/evaluate.js +13 -399
  114. package/dist/renderer/evaluate.js.map +1 -1
  115. package/dist/renderer/fastpath.js +6 -5
  116. package/dist/renderer/fastpath.js.map +1 -1
  117. package/dist/renderer/for-commit-dom-map.js +83 -0
  118. package/dist/renderer/for-commit-dom-map.js.map +1 -0
  119. package/dist/renderer/for-commit-removal.js +36 -0
  120. package/dist/renderer/for-commit-removal.js.map +1 -0
  121. package/dist/renderer/for-commit-reorder.js +89 -0
  122. package/dist/renderer/for-commit-reorder.js.map +1 -0
  123. package/dist/renderer/for-commit.js +6 -188
  124. package/dist/renderer/for-commit.js.map +1 -1
  125. package/dist/renderer/index.js +28 -16
  126. package/dist/renderer/index.js.map +1 -1
  127. package/dist/renderer/keyed-children.js +32 -0
  128. package/dist/renderer/keyed-children.js.map +1 -0
  129. package/dist/renderer/keyed.js +2 -15
  130. package/dist/renderer/keyed.js.map +1 -1
  131. package/dist/renderer/namespaces.js +23 -0
  132. package/dist/renderer/namespaces.js.map +1 -0
  133. package/dist/renderer/prop-bindings.js +280 -0
  134. package/dist/renderer/prop-bindings.js.map +1 -0
  135. package/dist/renderer/reactive-child-dom.js +133 -0
  136. package/dist/renderer/reactive-child-dom.js.map +1 -0
  137. package/dist/renderer/reactive-child-sources.js +184 -0
  138. package/dist/renderer/reactive-child-sources.js.map +1 -0
  139. package/dist/renderer/reactive-children.js +294 -0
  140. package/dist/renderer/reactive-children.js.map +1 -0
  141. package/dist/renderer/reconcile-commit.js +38 -0
  142. package/dist/renderer/reconcile-commit.js.map +1 -0
  143. package/dist/renderer/reconcile-fastpaths.js +106 -0
  144. package/dist/renderer/reconcile-fastpaths.js.map +1 -0
  145. package/dist/renderer/reconcile-resolution.js +187 -0
  146. package/dist/renderer/reconcile-resolution.js.map +1 -0
  147. package/dist/renderer/reconcile.js +9 -371
  148. package/dist/renderer/reconcile.js.map +1 -1
  149. package/dist/renderer/retained-element-rollback.js +276 -0
  150. package/dist/renderer/retained-element-rollback.js.map +1 -0
  151. package/dist/renderer/stable-patch.js +82 -0
  152. package/dist/renderer/stable-patch.js.map +1 -0
  153. package/dist/renderer/static-reuse.js +96 -0
  154. package/dist/renderer/static-reuse.js.map +1 -0
  155. package/dist/renderer/types.js +2 -1
  156. package/dist/renderer/utils.js +4 -4
  157. package/dist/renderer/utils.js.map +1 -1
  158. package/dist/resources/index.d.ts +3 -2
  159. package/dist/resources/index.js +5 -2
  160. package/dist/router/access.js +36 -0
  161. package/dist/router/access.js.map +1 -0
  162. package/dist/router/activity.d.ts +10 -0
  163. package/dist/router/activity.d.ts.map +1 -0
  164. package/dist/router/activity.js +89 -0
  165. package/dist/router/activity.js.map +1 -0
  166. package/dist/router/authoring.d.ts +22 -0
  167. package/dist/router/authoring.d.ts.map +1 -0
  168. package/dist/router/authoring.js +198 -0
  169. package/dist/router/authoring.js.map +1 -0
  170. package/dist/router/index.d.ts +12 -4
  171. package/dist/router/index.js +10 -3
  172. package/dist/router/internal-types.d.ts +31 -0
  173. package/dist/router/internal-types.d.ts.map +1 -0
  174. package/dist/router/lazy.d.ts +19 -0
  175. package/dist/router/lazy.d.ts.map +1 -0
  176. package/dist/router/lazy.js +52 -0
  177. package/dist/router/lazy.js.map +1 -0
  178. package/dist/router/manifest.d.ts +10 -0
  179. package/dist/router/manifest.d.ts.map +1 -0
  180. package/dist/router/manifest.js +49 -0
  181. package/dist/router/manifest.js.map +1 -0
  182. package/dist/router/navigate.d.ts +4 -51
  183. package/dist/router/navigate.d.ts.map +1 -1
  184. package/dist/router/navigate.js +16 -459
  185. package/dist/router/navigate.js.map +1 -1
  186. package/dist/router/navigation-registry.d.ts +32 -0
  187. package/dist/router/navigation-registry.d.ts.map +1 -0
  188. package/dist/router/navigation-registry.js +105 -0
  189. package/dist/router/navigation-registry.js.map +1 -0
  190. package/dist/router/navigation-scroll.d.ts +14 -0
  191. package/dist/router/navigation-scroll.d.ts.map +1 -0
  192. package/dist/router/navigation-scroll.js +93 -0
  193. package/dist/router/navigation-scroll.js.map +1 -0
  194. package/dist/router/navigation-targets.d.ts +31 -0
  195. package/dist/router/navigation-targets.d.ts.map +1 -0
  196. package/dist/router/navigation-targets.js +253 -0
  197. package/dist/router/navigation-targets.js.map +1 -0
  198. package/dist/router/policy.d.ts +7 -1
  199. package/dist/router/policy.d.ts.map +1 -1
  200. package/dist/router/policy.js +27 -9
  201. package/dist/router/policy.js.map +1 -1
  202. package/dist/router/rendering.d.ts +10 -0
  203. package/dist/router/rendering.d.ts.map +1 -0
  204. package/dist/router/rendering.js +45 -0
  205. package/dist/router/rendering.js.map +1 -0
  206. package/dist/router/resolution.d.ts +17 -0
  207. package/dist/router/resolution.d.ts.map +1 -0
  208. package/dist/router/resolution.js +253 -0
  209. package/dist/router/resolution.js.map +1 -0
  210. package/dist/router/route-query.d.ts +17 -0
  211. package/dist/router/route-query.d.ts.map +1 -0
  212. package/dist/router/route-query.js +47 -0
  213. package/dist/router/route-query.js.map +1 -0
  214. package/dist/router/route.d.ts +8 -149
  215. package/dist/router/route.js +8 -854
  216. package/dist/router/store.d.ts +50 -0
  217. package/dist/router/store.d.ts.map +1 -0
  218. package/dist/router/store.js +171 -0
  219. package/dist/router/store.js.map +1 -0
  220. package/dist/runtime/access.js +45 -0
  221. package/dist/runtime/access.js.map +1 -0
  222. package/dist/runtime/child-scope.d.ts +1 -1
  223. package/dist/runtime/child-scope.js +4 -1
  224. package/dist/runtime/child-scope.js.map +1 -1
  225. package/dist/runtime/component-cleanup.d.ts +16 -0
  226. package/dist/runtime/component-cleanup.d.ts.map +1 -0
  227. package/dist/runtime/component-cleanup.js +70 -0
  228. package/dist/runtime/component-cleanup.js.map +1 -0
  229. package/dist/runtime/component-commit.js +165 -0
  230. package/dist/runtime/component-commit.js.map +1 -0
  231. package/dist/runtime/component-contracts.js +5 -0
  232. package/dist/runtime/component-facade.d.ts +5 -0
  233. package/dist/runtime/component-facade.js +4 -0
  234. package/dist/runtime/component-internal.d.ts +100 -0
  235. package/dist/runtime/component-internal.d.ts.map +1 -0
  236. package/dist/runtime/component-internal.js +219 -0
  237. package/dist/runtime/component-internal.js.map +1 -0
  238. package/dist/runtime/component-lifecycle.d.ts +49 -0
  239. package/dist/runtime/component-lifecycle.d.ts.map +1 -0
  240. package/dist/runtime/component-lifecycle.js +176 -0
  241. package/dist/runtime/component-lifecycle.js.map +1 -0
  242. package/dist/runtime/component-scope.d.ts +55 -0
  243. package/dist/runtime/component-scope.d.ts.map +1 -0
  244. package/dist/runtime/component-scope.js +154 -0
  245. package/dist/runtime/component-scope.js.map +1 -0
  246. package/dist/runtime/component.d.ts +4 -152
  247. package/dist/runtime/component.js +4 -692
  248. package/dist/runtime/context.d.ts +1 -1
  249. package/dist/runtime/context.js +6 -4
  250. package/dist/runtime/context.js.map +1 -1
  251. package/dist/runtime/control.d.ts +3 -3
  252. package/dist/runtime/control.js +2 -1
  253. package/dist/runtime/control.js.map +1 -1
  254. package/dist/runtime/derive.d.ts.map +1 -1
  255. package/dist/runtime/derive.js +6 -5
  256. package/dist/runtime/derive.js.map +1 -1
  257. package/dist/runtime/effect.d.ts +1 -1
  258. package/dist/runtime/effect.d.ts.map +1 -1
  259. package/dist/runtime/effect.js +2 -2
  260. package/dist/runtime/effect.js.map +1 -1
  261. package/dist/runtime/events.js +3 -3
  262. package/dist/runtime/events.js.map +1 -1
  263. package/dist/runtime/fastlane.js +13 -27
  264. package/dist/runtime/fastlane.js.map +1 -1
  265. package/dist/runtime/for-bench.d.ts +1 -1
  266. package/dist/runtime/for-bench.js +1 -4
  267. package/dist/runtime/for-bench.js.map +1 -1
  268. package/dist/runtime/for-internal.d.ts +43 -0
  269. package/dist/runtime/for-internal.d.ts.map +1 -0
  270. package/dist/runtime/for-internal.js +98 -0
  271. package/dist/runtime/for-internal.js.map +1 -0
  272. package/dist/runtime/for-reconcile.d.ts +8 -0
  273. package/dist/runtime/for-reconcile.d.ts.map +1 -0
  274. package/dist/runtime/for-reconcile.js +389 -0
  275. package/dist/runtime/for-reconcile.js.map +1 -0
  276. package/dist/runtime/for-scopes.d.ts +26 -0
  277. package/dist/runtime/for-scopes.d.ts.map +1 -0
  278. package/dist/runtime/for-scopes.js +160 -0
  279. package/dist/runtime/for-scopes.js.map +1 -0
  280. package/dist/runtime/for-signals.d.ts +30 -0
  281. package/dist/runtime/for-signals.d.ts.map +1 -0
  282. package/dist/runtime/for-signals.js +139 -0
  283. package/dist/runtime/for-signals.js.map +1 -0
  284. package/dist/runtime/for.d.ts +4 -66
  285. package/dist/runtime/for.js +5 -773
  286. package/dist/runtime/lifecycle-operations.d.ts +31 -0
  287. package/dist/runtime/lifecycle-operations.d.ts.map +1 -0
  288. package/dist/runtime/lifecycle-operations.js +195 -0
  289. package/dist/runtime/lifecycle-operations.js.map +1 -0
  290. package/dist/runtime/operations.d.ts +3 -40
  291. package/dist/runtime/operations.js +2 -357
  292. package/dist/runtime/readable.d.ts +2 -3
  293. package/dist/runtime/readable.d.ts.map +1 -1
  294. package/dist/runtime/readable.js +6 -5
  295. package/dist/runtime/readable.js.map +1 -1
  296. package/dist/runtime/resource-cell.js +2 -2
  297. package/dist/runtime/resource-cell.js.map +1 -1
  298. package/dist/runtime/resource-operation.d.ts +13 -0
  299. package/dist/runtime/resource-operation.d.ts.map +1 -0
  300. package/dist/runtime/resource-operation.js +170 -0
  301. package/dist/runtime/resource-operation.js.map +1 -0
  302. package/dist/runtime/runtime.d.ts +42 -0
  303. package/dist/runtime/runtime.d.ts.map +1 -0
  304. package/dist/runtime/runtime.js +59 -0
  305. package/dist/runtime/runtime.js.map +1 -0
  306. package/dist/runtime/scheduler.d.ts +5 -1
  307. package/dist/runtime/scheduler.d.ts.map +1 -1
  308. package/dist/runtime/scheduler.js +17 -17
  309. package/dist/runtime/scheduler.js.map +1 -1
  310. package/dist/runtime/selector.js +4 -3
  311. package/dist/runtime/selector.js.map +1 -1
  312. package/dist/runtime/snapshot-source.d.ts +1 -1
  313. package/dist/runtime/snapshot-source.js +1 -1
  314. package/dist/runtime/state.js +2 -1
  315. package/dist/runtime/state.js.map +1 -1
  316. package/dist/ssg/batch-render.js +2 -1
  317. package/dist/ssg/batch-render.js.map +1 -1
  318. package/dist/ssg/create-static-gen.d.ts +9 -3
  319. package/dist/ssg/create-static-gen.d.ts.map +1 -1
  320. package/dist/ssg/create-static-gen.js +12 -99
  321. package/dist/ssg/create-static-gen.js.map +1 -1
  322. package/dist/ssg/generation-plan.js +70 -0
  323. package/dist/ssg/generation-plan.js.map +1 -0
  324. package/dist/ssg/incremental-manifest.js +1 -1
  325. package/dist/ssg/static-routes.js +101 -0
  326. package/dist/ssg/static-routes.js.map +1 -0
  327. package/dist/ssg/types.d.ts +15 -7
  328. package/dist/ssg/types.d.ts.map +1 -1
  329. package/dist/ssr/boundaries.js +113 -0
  330. package/dist/ssr/boundaries.js.map +1 -0
  331. package/dist/ssr/component-runtime.js +111 -0
  332. package/dist/ssr/component-runtime.js.map +1 -0
  333. package/dist/ssr/context.d.ts +3 -1
  334. package/dist/ssr/context.d.ts.map +1 -1
  335. package/dist/ssr/context.js +9 -2
  336. package/dist/ssr/context.js.map +1 -1
  337. package/dist/ssr/errors.d.ts +2 -0
  338. package/dist/ssr/errors.js +11 -1
  339. package/dist/ssr/errors.js.map +1 -1
  340. package/dist/ssr/escape.js +3 -3
  341. package/dist/ssr/escape.js.map +1 -1
  342. package/dist/ssr/hydration-data.js +10 -0
  343. package/dist/ssr/hydration-data.js.map +1 -0
  344. package/dist/ssr/hydration-verify.d.ts +12 -0
  345. package/dist/ssr/hydration-verify.d.ts.map +1 -0
  346. package/dist/ssr/hydration-verify.js +144 -0
  347. package/dist/ssr/hydration-verify.js.map +1 -0
  348. package/dist/ssr/index-internal.d.ts +15 -0
  349. package/dist/ssr/index-internal.d.ts.map +1 -0
  350. package/dist/ssr/index-internal.js +19 -0
  351. package/dist/ssr/index-internal.js.map +1 -0
  352. package/dist/ssr/index.d.ts +5 -57
  353. package/dist/ssr/index.js +3 -738
  354. package/dist/ssr/render-keys.js +9 -32
  355. package/dist/ssr/render-keys.js.map +1 -1
  356. package/dist/ssr/render-resolved.d.ts +1 -1
  357. package/dist/ssr/render-resolved.d.ts.map +1 -1
  358. package/dist/ssr/render-resolved.js +4 -2
  359. package/dist/ssr/render-resolved.js.map +1 -1
  360. package/dist/ssr/render-sync.d.ts +21 -0
  361. package/dist/ssr/render-sync.d.ts.map +1 -0
  362. package/dist/ssr/render-sync.js +275 -0
  363. package/dist/ssr/render-sync.js.map +1 -0
  364. package/dist/ssr/route-policy-resolution.js +77 -0
  365. package/dist/ssr/route-policy-resolution.js.map +1 -0
  366. package/dist/ssr/route-render.d.ts +77 -0
  367. package/dist/ssr/route-render.d.ts.map +1 -0
  368. package/dist/ssr/route-render.js +119 -0
  369. package/dist/ssr/route-render.js.map +1 -0
  370. package/dist/ssr/sink.d.ts +26 -0
  371. package/dist/ssr/sink.d.ts.map +1 -0
  372. package/dist/ssr/types.d.ts +1 -0
  373. package/dist/ssr/types.d.ts.map +1 -1
  374. package/dist/ssr/verify-hydration.js.map +1 -1
  375. package/dist/testing/index.d.ts +1 -2
  376. package/dist/testing/index.d.ts.map +1 -1
  377. package/dist/testing/index.js +2 -1
  378. package/dist/testing/index.js.map +1 -1
  379. package/package.json +4 -3
  380. package/dist/data/index.d.ts.map +0 -1
  381. package/dist/data/index.js.map +0 -1
  382. package/dist/renderer/dom.js.map +0 -1
  383. package/dist/router/route.d.ts.map +0 -1
  384. package/dist/router/route.js.map +0 -1
  385. package/dist/runtime/component.d.ts.map +0 -1
  386. package/dist/runtime/component.js.map +0 -1
  387. package/dist/runtime/for.d.ts.map +0 -1
  388. package/dist/runtime/for.js.map +0 -1
  389. package/dist/runtime/operations.d.ts.map +0 -1
  390. package/dist/runtime/operations.js.map +0 -1
  391. package/dist/runtime/ssr-bridge.js +0 -24
  392. package/dist/runtime/ssr-bridge.js.map +0 -1
  393. package/dist/ssr/index.d.ts.map +0 -1
  394. package/dist/ssr/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.js","names":[],"sources":["../../src/runtime/scheduler.ts"],"sourcesContent":["/**\n * Serialized update scheduler — safer design (no inline execution, explicit flush)\n *\n * Key ideas:\n * - Never execute a task inline from `enqueue`.\n * - `flush()` is explicit and non-reentrant.\n * - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks\n * inline; it runs `fn` and then does an explicit `flush()`.\n * - `waitForFlush()` is race-free with a monotonic `flushVersion`.\n */\n\nimport { isDevelopmentEnvironment } from '../common/env';\nimport { assertSchedulingPrecondition, invariant } from '../dev/invariant';\nimport { logger } from '../dev/logger';\nimport { recordSchedulerFlushTaskCount } from './perf-metrics';\n\nconst MAX_FLUSH_DEPTH = 50;\n\ntype Task = () => void;\nexport type SchedulerLane = 'derived' | 'component' | 'reactive' | 'post';\n\nconst SCHEDULER_LANES: SchedulerLane[] = [\n 'derived',\n 'component',\n 'reactive',\n 'post',\n];\n\ninterface LaneQueue {\n tasks: Task[];\n head: number;\n}\n\nfunction isBulkCommitActive(): boolean {\n try {\n const fb = (\n globalThis as {\n __ASKR_FASTLANE?: { isBulkCommitActive?: () => boolean };\n }\n ).__ASKR_FASTLANE;\n return typeof fb?.isBulkCommitActive === 'function'\n ? !!fb.isBulkCommitActive()\n : false;\n } catch (e) {\n void e;\n return false;\n }\n}\n\nexport class Scheduler {\n private lanes: Record<SchedulerLane, LaneQueue> = {\n derived: { tasks: [], head: 0 },\n component: { tasks: [], head: 0 },\n reactive: { tasks: [], head: 0 },\n post: { tasks: [], head: 0 },\n };\n\n private running = false;\n private inHandler = false;\n private depth = 0;\n private executionDepth = 0; // for compat with existing diagnostics\n\n // Monotonic flush version increments at end of each flush\n private flushVersion = 0;\n\n // Best-effort microtask kick scheduling\n private kickScheduled = false;\n\n // Escape hatch flag for runWithSyncProgress\n private allowSyncProgress = false;\n\n // Waiters waiting for flushVersion >= target\n private waiters: Array<{\n target: number;\n resolve: () => void;\n reject: (err: unknown) => void;\n timer?: ReturnType<typeof setTimeout>;\n }> = [];\n\n // Keep a lightweight taskCount for compatibility/diagnostics\n private taskCount = 0;\n\n private hasPendingTasks(): boolean {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head < queue.tasks.length) {\n return true;\n }\n }\n return false;\n }\n\n private getPendingTaskCount(): number {\n let total = 0;\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n total += queue.tasks.length - queue.head;\n }\n return total;\n }\n\n private compactLane(queue: LaneQueue): void {\n if (queue.head >= queue.tasks.length) {\n queue.tasks.length = 0;\n queue.head = 0;\n return;\n }\n\n if (queue.head <= 0) {\n return;\n }\n\n const remaining = queue.tasks.length - queue.head;\n for (let i = 0; i < remaining; i++) {\n queue.tasks[i] = queue.tasks[queue.head + i];\n }\n queue.tasks.length = remaining;\n queue.head = 0;\n }\n\n private scheduleFlushKick(): void {\n if (\n this.running ||\n this.kickScheduled ||\n this.inHandler ||\n this.allowSyncProgress ||\n isBulkCommitActive() ||\n !this.hasPendingTasks()\n ) {\n return;\n }\n\n this.kickScheduled = true;\n queueMicrotask(() => {\n this.kickScheduled = false;\n if (this.running || isBulkCommitActive() || !this.hasPendingTasks()) {\n return;\n }\n try {\n this.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n\n enqueue(task: Task): void {\n this.enqueueInLane('component', task);\n }\n\n enqueueInLane(lane: SchedulerLane, task: Task): void {\n assertSchedulingPrecondition(\n typeof task === 'function',\n 'enqueue() requires a function'\n );\n\n // Strict rule: during bulk commit, only allow enqueues if runWithSyncProgress enabled\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[Scheduler] enqueue() during bulk commit (not allowed)'\n );\n }\n return;\n }\n\n // Enqueue task and account counts\n this.lanes[lane].tasks.push(task);\n this.taskCount++;\n\n this.scheduleFlushKick();\n }\n\n flush(): void {\n invariant(\n !this.running,\n '[Scheduler] flush() called while already running'\n );\n\n // Dev-only guard: disallow flush during bulk commit unless allowed\n if (isDevelopmentEnvironment()) {\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n throw new Error(\n '[Scheduler] flush() started during bulk commit (not allowed)'\n );\n }\n }\n\n this.running = true;\n this.depth = 0;\n let fatal: unknown = null;\n let executedTaskCount = 0;\n const checkFlushDepth = isDevelopmentEnvironment();\n const executionsByTask = checkFlushDepth ? new Map<Task, number>() : null;\n\n try {\n while (fatal === null) {\n let didRunTask = false;\n\n for (const lane of SCHEDULER_LANES) {\n const laneQueue = this.lanes[lane];\n let executedInLane = 0;\n\n while (laneQueue.head < laneQueue.tasks.length) {\n const task = laneQueue.tasks[laneQueue.head++];\n if (executionsByTask) {\n const taskExecutions = (executionsByTask.get(task) ?? 0) + 1;\n executionsByTask.set(task, taskExecutions);\n this.depth = Math.max(this.depth, taskExecutions);\n if (taskExecutions > MAX_FLUSH_DEPTH) {\n throw new Error(\n `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`\n );\n }\n }\n\n try {\n this.executionDepth++;\n task();\n this.executionDepth--;\n executedTaskCount++;\n executedInLane++;\n didRunTask = true;\n } catch (err) {\n // ensure executionDepth stays balanced for the task that threw\n if (this.executionDepth > 0) this.executionDepth--;\n fatal = err;\n break;\n }\n }\n\n if (executedInLane > 0) {\n this.taskCount = Math.max(0, this.taskCount - executedInLane);\n }\n\n if (fatal !== null) {\n break;\n }\n }\n\n if (!didRunTask) {\n break;\n }\n }\n } finally {\n this.running = false;\n this.depth = 0;\n this.executionDepth = 0;\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head > 0) {\n this.compactLane(queue);\n }\n }\n\n // Advance flush epoch and resolve waiters\n this.flushVersion++;\n this.resolveWaiters();\n recordSchedulerFlushTaskCount(executedTaskCount);\n }\n\n if (fatal) throw fatal;\n }\n\n runWithSyncProgress<T>(fn: () => T): T {\n const prev = this.allowSyncProgress;\n this.allowSyncProgress = true;\n\n // Snapshot flushVersion so we can ensure we always complete an epoch\n const startVersion = this.flushVersion;\n\n try {\n const res = fn();\n\n // Flush deterministically if tasks were enqueued (and we're not already running)\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n\n if (isDevelopmentEnvironment()) {\n if (!this.running && this.hasPendingTasks()) {\n throw new Error(\n '[Scheduler] tasks remain after runWithSyncProgress flush'\n );\n }\n }\n\n return res;\n } finally {\n // If no flush happened during the protected window, complete an epoch so\n // observers (tests) see progress even when fast-lane did synchronous work\n // without enqueuing tasks.\n try {\n if (this.flushVersion === startVersion) {\n this.flushVersion++;\n this.resolveWaiters();\n }\n } catch (e) {\n void e;\n }\n\n this.allowSyncProgress = prev;\n }\n }\n\n waitForFlush(targetVersion?: number, timeoutMs = 2000): Promise<void> {\n const target =\n typeof targetVersion === 'number' ? targetVersion : this.flushVersion + 1;\n if (this.flushVersion >= target) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const ns =\n (\n globalThis as unknown as Record<string, unknown> & {\n __ASKR__?: Record<string, unknown>;\n }\n ).__ASKR__ || {};\n const diag = {\n flushVersion: this.flushVersion,\n queueLen: this.getPendingTaskCount(),\n running: this.running,\n inHandler: this.inHandler,\n bulk: isBulkCommitActive(),\n namespace: ns,\n };\n reject(\n new Error(\n `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`\n )\n );\n }, timeoutMs);\n\n this.waiters.push({ target, resolve, reject, timer });\n });\n }\n\n getState() {\n // Provide the compatibility shape expected by diagnostics/tests\n return {\n queueLength: this.getPendingTaskCount(),\n running: this.running,\n depth: this.depth,\n executionDepth: this.executionDepth,\n taskCount: this.taskCount,\n flushVersion: this.flushVersion,\n laneQueues: {\n derived: this.lanes.derived.tasks.length - this.lanes.derived.head,\n component:\n this.lanes.component.tasks.length - this.lanes.component.head,\n reactive: this.lanes.reactive.tasks.length - this.lanes.reactive.head,\n post: this.lanes.post.tasks.length - this.lanes.post.head,\n },\n // New fields for optional inspection\n inHandler: this.inHandler,\n allowSyncProgress: this.allowSyncProgress,\n };\n }\n\n getFlushVersion(): number {\n return this.flushVersion;\n }\n\n flushIfQueued(): void {\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n }\n\n runInHandlerScope<T>(fn: () => T, flushMode: 'defer' | 'sync' = 'defer'): T {\n const prevInHandler = this.inHandler;\n this.inHandler = true;\n\n try {\n return fn();\n } finally {\n this.inHandler = prevInHandler;\n\n if (!this.inHandler) {\n if (flushMode === 'sync') {\n this.flushIfQueued();\n } else {\n this.scheduleFlushKick();\n }\n }\n }\n }\n\n setInHandler(v: boolean) {\n this.inHandler = v;\n if (!v) {\n this.scheduleFlushKick();\n }\n }\n\n isInHandler(): boolean {\n return this.inHandler;\n }\n\n isExecuting(): boolean {\n return this.running || this.executionDepth > 0;\n }\n\n // Clear pending synchronous tasks (used by fastlane enter/exit)\n clearPendingSyncTasks(): number {\n const remaining = this.getPendingTaskCount();\n if (remaining <= 0) return 0;\n\n if (this.running) {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = queue.head;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n queueMicrotask(() => {\n try {\n this.flushVersion++;\n this.resolveWaiters();\n } catch (e) {\n void e;\n }\n });\n return remaining;\n }\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = 0;\n queue.head = 0;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n this.flushVersion++;\n this.resolveWaiters();\n return remaining;\n }\n\n private resolveWaiters() {\n if (this.waiters.length === 0) return;\n const ready: Array<() => void> = [];\n const remaining: typeof this.waiters = [];\n\n for (const w of this.waiters) {\n if (this.flushVersion >= w.target) {\n if (w.timer) clearTimeout(w.timer);\n ready.push(w.resolve);\n } else {\n remaining.push(w);\n }\n }\n\n this.waiters = remaining;\n for (const r of ready) r();\n }\n}\n\nexport const globalScheduler = new Scheduler();\n\nexport function isSchedulerExecuting(): boolean {\n return globalScheduler.isExecuting();\n}\n\nexport function scheduleEventHandler(handler: EventListener): EventListener {\n return (event: Event) => {\n try {\n globalScheduler.runInHandlerScope(() => {\n handler.call(null, event);\n });\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n }\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scheduler.js","names":[],"sources":["../../src/runtime/scheduler.ts"],"sourcesContent":["/**\n * Serialized update scheduler — safer design (no inline execution, explicit flush)\n *\n * Key ideas:\n * - Never execute a task inline from `enqueue`.\n * - `flush()` is explicit and non-reentrant.\n * - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks\n * inline; it runs `fn` and then does an explicit `flush()`.\n * - `waitForFlush()` is race-free with a monotonic `flushVersion`.\n */\n\nimport { isDevelopmentEnvironment } from '../common/env';\nimport { assertSchedulingPrecondition, invariant } from '../dev/invariant';\nimport { logger } from '../dev/logger';\nimport { recordSchedulerFlushTaskCount } from './perf-metrics';\n\nconst MAX_FLUSH_DEPTH = 50;\n\ntype Task = () => void;\nexport type SchedulerLane = 'derived' | 'component' | 'reactive' | 'post';\nexport type SchedulerBulkCommitProbe = () => boolean;\n\nconst SCHEDULER_LANES: SchedulerLane[] = [\n 'derived',\n 'component',\n 'reactive',\n 'post',\n];\n\ninterface LaneQueue {\n tasks: Task[];\n head: number;\n}\n\nexport class Scheduler {\n private bulkCommitProbe: SchedulerBulkCommitProbe = () => false;\n\n private lanes: Record<SchedulerLane, LaneQueue> = {\n derived: { tasks: [], head: 0 },\n component: { tasks: [], head: 0 },\n reactive: { tasks: [], head: 0 },\n post: { tasks: [], head: 0 },\n };\n\n private running = false;\n private inHandler = false;\n private depth = 0;\n private executionDepth = 0; // for compat with existing diagnostics\n\n // Monotonic flush version increments at end of each flush\n private flushVersion = 0;\n\n // Best-effort microtask kick scheduling\n private kickScheduled = false;\n\n // Escape hatch flag for runWithSyncProgress\n private allowSyncProgress = false;\n\n // Waiters waiting for flushVersion >= target\n private waiters: Array<{\n target: number;\n resolve: () => void;\n reject: (err: unknown) => void;\n timer?: ReturnType<typeof setTimeout>;\n }> = [];\n\n // Keep a lightweight taskCount for compatibility/diagnostics\n private taskCount = 0;\n\n setBulkCommitProbe(probe: SchedulerBulkCommitProbe): void {\n this.bulkCommitProbe = probe;\n }\n\n private isBulkCommitActive(): boolean {\n try {\n return this.bulkCommitProbe();\n } catch (e) {\n void e;\n return false;\n }\n }\n\n private hasPendingTasks(): boolean {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head < queue.tasks.length) {\n return true;\n }\n }\n return false;\n }\n\n private getPendingTaskCount(): number {\n let total = 0;\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n total += queue.tasks.length - queue.head;\n }\n return total;\n }\n\n private compactLane(queue: LaneQueue): void {\n if (queue.head >= queue.tasks.length) {\n queue.tasks.length = 0;\n queue.head = 0;\n return;\n }\n\n if (queue.head <= 0) {\n return;\n }\n\n const remaining = queue.tasks.length - queue.head;\n for (let i = 0; i < remaining; i++) {\n queue.tasks[i] = queue.tasks[queue.head + i];\n }\n queue.tasks.length = remaining;\n queue.head = 0;\n }\n\n private scheduleFlushKick(): void {\n if (\n this.running ||\n this.kickScheduled ||\n this.inHandler ||\n this.allowSyncProgress ||\n this.isBulkCommitActive() ||\n !this.hasPendingTasks()\n ) {\n return;\n }\n\n this.kickScheduled = true;\n queueMicrotask(() => {\n this.kickScheduled = false;\n if (\n this.running ||\n this.isBulkCommitActive() ||\n !this.hasPendingTasks()\n ) {\n return;\n }\n try {\n this.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n\n enqueue(task: Task): void {\n this.enqueueInLane('component', task);\n }\n\n enqueueInLane(lane: SchedulerLane, task: Task): void {\n assertSchedulingPrecondition(\n typeof task === 'function',\n 'enqueue() requires a function'\n );\n\n // Strict rule: during bulk commit, only allow enqueues if runWithSyncProgress enabled\n if (this.isBulkCommitActive() && !this.allowSyncProgress) {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[Scheduler] enqueue() during bulk commit (not allowed)'\n );\n }\n return;\n }\n\n // Enqueue task and account counts\n this.lanes[lane].tasks.push(task);\n this.taskCount++;\n\n this.scheduleFlushKick();\n }\n\n flush(): void {\n invariant(\n !this.running,\n '[Scheduler] flush() called while already running'\n );\n\n // Dev-only guard: disallow flush during bulk commit unless allowed\n if (isDevelopmentEnvironment()) {\n if (this.isBulkCommitActive() && !this.allowSyncProgress) {\n throw new Error(\n '[Scheduler] flush() started during bulk commit (not allowed)'\n );\n }\n }\n\n this.running = true;\n this.depth = 0;\n let fatal: unknown = null;\n let executedTaskCount = 0;\n const checkFlushDepth = isDevelopmentEnvironment();\n const executionsByTask = checkFlushDepth ? new Map<Task, number>() : null;\n\n try {\n while (fatal === null) {\n let didRunTask = false;\n\n for (const lane of SCHEDULER_LANES) {\n const laneQueue = this.lanes[lane];\n let executedInLane = 0;\n\n while (laneQueue.head < laneQueue.tasks.length) {\n const task = laneQueue.tasks[laneQueue.head++];\n if (executionsByTask) {\n const taskExecutions = (executionsByTask.get(task) ?? 0) + 1;\n executionsByTask.set(task, taskExecutions);\n this.depth = Math.max(this.depth, taskExecutions);\n if (taskExecutions > MAX_FLUSH_DEPTH) {\n throw new Error(\n `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`\n );\n }\n }\n\n try {\n this.executionDepth++;\n task();\n this.executionDepth--;\n executedTaskCount++;\n executedInLane++;\n didRunTask = true;\n } catch (err) {\n // ensure executionDepth stays balanced for the task that threw\n if (this.executionDepth > 0) this.executionDepth--;\n fatal = err;\n break;\n }\n }\n\n if (executedInLane > 0) {\n this.taskCount = Math.max(0, this.taskCount - executedInLane);\n }\n\n if (fatal !== null) {\n break;\n }\n }\n\n if (!didRunTask) {\n break;\n }\n }\n } finally {\n this.running = false;\n this.depth = 0;\n this.executionDepth = 0;\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head > 0) {\n this.compactLane(queue);\n }\n }\n\n // Advance flush epoch and resolve waiters\n this.flushVersion++;\n this.resolveWaiters();\n recordSchedulerFlushTaskCount(executedTaskCount);\n }\n\n if (fatal) throw fatal;\n }\n\n runWithSyncProgress<T>(fn: () => T): T {\n const prev = this.allowSyncProgress;\n this.allowSyncProgress = true;\n\n // Snapshot flushVersion so we can ensure we always complete an epoch\n const startVersion = this.flushVersion;\n\n try {\n const res = fn();\n\n // Flush deterministically if tasks were enqueued (and we're not already running)\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n\n if (isDevelopmentEnvironment()) {\n if (!this.running && this.hasPendingTasks()) {\n throw new Error(\n '[Scheduler] tasks remain after runWithSyncProgress flush'\n );\n }\n }\n\n return res;\n } finally {\n // If no flush happened during the protected window, complete an epoch so\n // observers (tests) see progress even when fast-lane did synchronous work\n // without enqueuing tasks.\n try {\n if (this.flushVersion === startVersion) {\n this.flushVersion++;\n this.resolveWaiters();\n }\n } catch (e) {\n void e;\n }\n\n this.allowSyncProgress = prev;\n }\n }\n\n waitForFlush(targetVersion?: number, timeoutMs = 2000): Promise<void> {\n const target =\n typeof targetVersion === 'number' ? targetVersion : this.flushVersion + 1;\n if (this.flushVersion >= target) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const ns =\n (\n globalThis as unknown as Record<string, unknown> & {\n __ASKR__?: Record<string, unknown>;\n }\n ).__ASKR__ || {};\n const diag = {\n flushVersion: this.flushVersion,\n queueLen: this.getPendingTaskCount(),\n running: this.running,\n inHandler: this.inHandler,\n bulk: this.isBulkCommitActive(),\n namespace: ns,\n };\n reject(\n new Error(\n `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`\n )\n );\n }, timeoutMs);\n\n this.waiters.push({ target, resolve, reject, timer });\n });\n }\n\n getState() {\n // Provide the compatibility shape expected by diagnostics/tests\n return {\n queueLength: this.getPendingTaskCount(),\n running: this.running,\n depth: this.depth,\n executionDepth: this.executionDepth,\n taskCount: this.taskCount,\n flushVersion: this.flushVersion,\n laneQueues: {\n derived: this.lanes.derived.tasks.length - this.lanes.derived.head,\n component:\n this.lanes.component.tasks.length - this.lanes.component.head,\n reactive: this.lanes.reactive.tasks.length - this.lanes.reactive.head,\n post: this.lanes.post.tasks.length - this.lanes.post.head,\n },\n // New fields for optional inspection\n inHandler: this.inHandler,\n allowSyncProgress: this.allowSyncProgress,\n };\n }\n\n getFlushVersion(): number {\n return this.flushVersion;\n }\n\n flushIfQueued(): void {\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n }\n\n runInHandlerScope<T>(fn: () => T, flushMode: 'defer' | 'sync' = 'defer'): T {\n const prevInHandler = this.inHandler;\n this.inHandler = true;\n\n try {\n return fn();\n } finally {\n this.inHandler = prevInHandler;\n\n if (!this.inHandler) {\n if (flushMode === 'sync') {\n this.flushIfQueued();\n } else {\n this.scheduleFlushKick();\n }\n }\n }\n }\n\n setInHandler(v: boolean) {\n this.inHandler = v;\n if (!v) {\n this.scheduleFlushKick();\n }\n }\n\n isInHandler(): boolean {\n return this.inHandler;\n }\n\n isExecuting(): boolean {\n return this.running || this.executionDepth > 0;\n }\n\n // Clear pending synchronous tasks (used by fastlane enter/exit)\n clearPendingSyncTasks(): number {\n const remaining = this.getPendingTaskCount();\n if (remaining <= 0) return 0;\n\n if (this.running) {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = queue.head;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n queueMicrotask(() => {\n try {\n this.flushVersion++;\n this.resolveWaiters();\n } catch (e) {\n void e;\n }\n });\n return remaining;\n }\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = 0;\n queue.head = 0;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n this.flushVersion++;\n this.resolveWaiters();\n return remaining;\n }\n\n private resolveWaiters() {\n if (this.waiters.length === 0) return;\n const ready: Array<() => void> = [];\n const remaining: typeof this.waiters = [];\n\n for (const w of this.waiters) {\n if (this.flushVersion >= w.target) {\n if (w.timer) clearTimeout(w.timer);\n ready.push(w.resolve);\n } else {\n remaining.push(w);\n }\n }\n\n this.waiters = remaining;\n for (const r of ready) r();\n }\n}\n\nexport const globalScheduler = new Scheduler();\n\nexport function isSchedulerExecuting(): boolean {\n return globalScheduler.isExecuting();\n}\n\nexport function scheduleEventHandler(handler: EventListener): EventListener {\n return (event: Event) => {\n try {\n globalScheduler.runInHandlerScope(() => {\n handler.call(null, event);\n });\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n }\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { getPerfMetricsStore, incrementPerfMetric } from "./perf-metrics.js";
2
- import { globalScheduler } from "./scheduler.js";
2
+ import { enqueueRuntimeLane } from "./access.js";
3
3
  import { clearDerivedDependencySubscriptions, markReactivePropsDirtySource, markReadableDerivedSubscribersDirty, markReadableUsage, notifyReadableReaders, recordReadableRead, syncDerivedDependencySubscriptions, withDerivedReadTracking } from "./readable.js";
4
- import { claimHookIndex, getCurrentInstance } from "./component.js";
4
+ import { claimHookIndex, getCurrentInstance } from "./component-scope.js";
5
+ import "./component.js";
5
6
  //#region src/runtime/selector.ts
6
7
  const selectorCells = /* @__PURE__ */ new WeakMap();
7
8
  const selectorRecords = /* @__PURE__ */ new WeakMap();
@@ -18,7 +19,7 @@ function getSelectorStore(instance) {
18
19
  function scheduleSelectorFlush() {
19
20
  if (hasPendingSelectorFlush) return;
20
21
  hasPendingSelectorFlush = true;
21
- globalScheduler.enqueueInLane("derived", flushDirtySelectorRecords);
22
+ enqueueRuntimeLane("derived", flushDirtySelectorRecords);
22
23
  }
23
24
  function markSelectorRecordDirty(record) {
24
25
  record._dirty = true;
@@ -1 +1 @@
1
- {"version":3,"file":"selector.js","names":[],"sources":["../../src/runtime/selector.ts"],"sourcesContent":["import {\n claimHookIndex,\n getCurrentInstance,\n type ComponentInstance,\n} from './component';\nimport { globalScheduler } from './scheduler';\nimport {\n clearDerivedDependencySubscriptions,\n markReadableDerivedSubscribersDirty,\n markReadableUsage,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n recordReadableRead,\n syncDerivedDependencySubscriptions,\n withDerivedReadTracking,\n type DerivedSubscriber,\n type ReadableSource,\n} from './readable';\nimport { getPerfMetricsStore, incrementPerfMetric } from './perf-metrics';\n\ntype PrimitiveKey =\n | string\n | number\n | boolean\n | symbol\n | bigint\n | null\n | undefined;\n\nexport interface Selector<T> {\n (candidate: T): boolean;\n}\n\ninterface SelectorCandidateSource<T> extends ReadableSource<boolean> {\n _candidate: T;\n}\n\ntype SelectorEquals<T> = {\n bivarianceHack(a: T, b: T): boolean;\n}['bivarianceHack'];\n\ninterface SelectorLane<T> {\n _record: SelectorSourceRecord<T>;\n _equals: SelectorEquals<T>;\n _bindingCount: number;\n _primitiveCandidates: Map<PrimitiveKey, SelectorCandidateSource<T>>;\n _objectCandidates: WeakMap<object, SelectorCandidateSource<T>>;\n _objectCandidateSources: Set<SelectorCandidateSource<T>>;\n _cleanup(): void;\n}\n\ninterface SelectorSourceRecord<T> extends DerivedSubscriber {\n _source: () => T;\n _value: T;\n _hasValue: boolean;\n _dirty: boolean;\n _scheduled: boolean;\n _evaluating: boolean;\n _sources: Set<ReadableSource<unknown>>;\n _pendingDependencySources?: Set<ReadableSource<unknown>>;\n _lanes: Map<SelectorEquals<T>, SelectorLane<T>>;\n _cleanup(): void;\n}\n\ninterface SelectorHook<T> extends Selector<T> {\n _owner: ComponentInstance;\n _hookIndex: number;\n _source: () => T;\n _equals: SelectorEquals<T>;\n _record: SelectorSourceRecord<T> | null;\n _lane: SelectorLane<T> | null;\n _cleanup(): void;\n}\n\nconst selectorCells = new WeakMap<\n ComponentInstance,\n Map<number, SelectorHook<unknown>>\n>();\nconst selectorRecords = new WeakMap<\n ReadableSource<unknown>,\n SelectorSourceRecord<unknown>\n>();\nconst dirtySelectorRecords = new Set<SelectorSourceRecord<unknown>>();\nlet hasPendingSelectorFlush = false;\n\nfunction getSelectorStore(\n instance: ComponentInstance\n): Map<number, SelectorHook<unknown>> {\n let store = selectorCells.get(instance);\n if (!store) {\n store = new Map();\n selectorCells.set(instance, store);\n }\n return store;\n}\n\nfunction scheduleSelectorFlush(): void {\n if (hasPendingSelectorFlush) {\n return;\n }\n\n hasPendingSelectorFlush = true;\n globalScheduler.enqueueInLane('derived', flushDirtySelectorRecords);\n}\n\nfunction markSelectorRecordDirty(record: SelectorSourceRecord<unknown>): void {\n record._dirty = true;\n if (record._scheduled) {\n return;\n }\n\n record._scheduled = true;\n dirtySelectorRecords.add(record);\n scheduleSelectorFlush();\n}\n\nfunction flushDirtySelectorRecords(): void {\n hasPendingSelectorFlush = false;\n\n if (dirtySelectorRecords.size === 0) {\n return;\n }\n\n const pending = Array.from(dirtySelectorRecords);\n dirtySelectorRecords.clear();\n\n for (const record of pending) {\n record._scheduled = false;\n if (!record._dirty) {\n continue;\n }\n recomputeSelectorSourceRecord(record, true);\n }\n}\n\nfunction isObjectCandidate(value: unknown): value is object {\n return (\n (typeof value === 'object' && value !== null) || typeof value === 'function'\n );\n}\n\nfunction isDefaultSelectorEquals<T>(equals: SelectorEquals<T>): boolean {\n return equals === Object.is;\n}\n\nfunction createCandidateSource<T>(candidate: T): SelectorCandidateSource<T> {\n const source = (() => false) as SelectorCandidateSource<T>;\n source._candidate = candidate;\n return source;\n}\n\nfunction getCandidateSource<T>(\n lane: SelectorLane<T>,\n candidate: T\n): SelectorCandidateSource<T> {\n if (isObjectCandidate(candidate)) {\n const cached = lane._objectCandidates.get(candidate);\n if (cached) {\n return cached;\n }\n\n const created = createCandidateSource(candidate);\n lane._objectCandidates.set(candidate, created);\n lane._objectCandidateSources.add(created);\n return created;\n }\n\n const key = candidate as PrimitiveKey;\n const cached = lane._primitiveCandidates.get(key);\n if (cached) {\n return cached;\n }\n\n const created = createCandidateSource(candidate);\n lane._primitiveCandidates.set(key, created);\n return created;\n}\n\nfunction peekCandidateSource<T>(\n lane: SelectorLane<T>,\n candidate: T\n): SelectorCandidateSource<T> | undefined {\n if (isObjectCandidate(candidate)) {\n return lane._objectCandidates.get(candidate);\n }\n return lane._primitiveCandidates.get(candidate as PrimitiveKey);\n}\n\nfunction getSelectorSourceRecord<T>(source: () => T): SelectorSourceRecord<T> {\n const cached = selectorRecords.get(source);\n if (cached) {\n return cached as SelectorSourceRecord<T>;\n }\n\n const record = createSelectorSourceRecord(source);\n selectorRecords.set(source, record as SelectorSourceRecord<unknown>);\n return record;\n}\n\nfunction createSelectorSourceRecord<T>(\n source: () => T\n): SelectorSourceRecord<T> {\n let record!: SelectorSourceRecord<T>;\n\n record = {\n _source: source,\n _value: undefined as T,\n _hasValue: false,\n _dirty: true,\n _scheduled: false,\n _evaluating: false,\n _sources: new Set(),\n _lanes: new Map(),\n _markDirty: () => {\n markSelectorRecordDirty(record as SelectorSourceRecord<unknown>);\n },\n _cleanup: () => {\n record._scheduled = false;\n record._dirty = false;\n record._hasValue = false;\n record._evaluating = false;\n record._pendingDependencySources = undefined;\n clearDerivedDependencySubscriptions(record, record._sources);\n record._lanes.clear();\n selectorRecords.delete(source as ReadableSource<unknown>);\n },\n };\n\n return record;\n}\n\nfunction getSelectorLane<T>(\n record: SelectorSourceRecord<T>,\n equals: SelectorEquals<T>\n): SelectorLane<T> {\n const cached = record._lanes.get(equals);\n if (cached) {\n return cached;\n }\n\n const lane = createSelectorLane(record, equals);\n record._lanes.set(equals, lane);\n return lane;\n}\n\nfunction createSelectorLane<T>(\n record: SelectorSourceRecord<T>,\n equals: SelectorEquals<T>\n): SelectorLane<T> {\n const lane: SelectorLane<T> = {\n _record: record,\n _equals: equals,\n _bindingCount: 0,\n _primitiveCandidates: new Map(),\n _objectCandidates: new WeakMap(),\n _objectCandidateSources: new Set(),\n _cleanup: () => {\n for (const sourceRef of lane._primitiveCandidates.values()) {\n sourceRef._readers?.clear();\n sourceRef._derivedSubscribers?.clear();\n }\n for (const sourceRef of lane._objectCandidateSources) {\n sourceRef._readers?.clear();\n sourceRef._derivedSubscribers?.clear();\n }\n lane._primitiveCandidates.clear();\n lane._objectCandidates = new WeakMap();\n lane._objectCandidateSources.clear();\n },\n };\n\n return lane;\n}\n\nfunction notifySelectorSource(source: SelectorCandidateSource<unknown>): void {\n incrementPerfMetric('selectorInvalidations');\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n}\n\nfunction notifyAllSelectorSources<T>(lane: SelectorLane<T>): void {\n for (const source of lane._primitiveCandidates.values()) {\n notifySelectorSource(source);\n }\n for (const source of lane._objectCandidateSources) {\n notifySelectorSource(source);\n }\n}\n\nfunction notifySelectorLaneValueChange<T>(\n lane: SelectorLane<T>,\n prevValue: T,\n nextValue: T\n): void {\n if (!lane._bindingCount) {\n return;\n }\n\n if (!isDefaultSelectorEquals(lane._equals)) {\n if (lane._equals(prevValue, nextValue)) {\n return;\n }\n notifyAllSelectorSources(lane);\n return;\n }\n\n if (!Object.is(prevValue, nextValue)) {\n // Only notify candidate sources that were actually materialized by a read.\n // Using getCandidateSource here would create-on-miss and leak a candidate\n // source for every distinct value the source ever passed through, even\n // those no component reads. Peek instead so the candidate cache stays\n // bounded to values that components actually compare against.\n const prevSource = peekCandidateSource(lane, prevValue);\n if (prevSource) {\n notifySelectorSource(prevSource);\n }\n const nextSource = peekCandidateSource(lane, nextValue);\n if (nextSource) {\n notifySelectorSource(nextSource);\n }\n }\n}\n\nfunction recomputeSelectorSourceRecord<T>(\n record: SelectorSourceRecord<T>,\n notifyDownstream: boolean\n): T {\n if (!record._dirty && record._hasValue) {\n return record._value;\n }\n\n if (record._evaluating) {\n throw new Error('selector() cannot read itself recursively');\n }\n\n record._evaluating = true;\n record._dirty = false;\n record._pendingDependencySources = new Set();\n\n const prevSources = record._sources;\n const hadValue = record._hasValue;\n const prevValue = record._value;\n let nextValue: T;\n\n try {\n nextValue = withDerivedReadTracking(record, record._source);\n } catch (error) {\n record._dirty = true;\n record._pendingDependencySources = undefined;\n throw error;\n } finally {\n record._evaluating = false;\n }\n\n const nextSources = record._pendingDependencySources ?? new Set();\n record._pendingDependencySources = undefined;\n syncDerivedDependencySubscriptions(record, prevSources, nextSources);\n record._sources = nextSources;\n\n const valueChanged = !record._hasValue || !Object.is(prevValue, nextValue);\n record._hasValue = true;\n record._value = nextValue;\n\n if (valueChanged && notifyDownstream && hadValue) {\n for (const lane of Array.from(record._lanes.values())) {\n notifySelectorLaneValueChange(lane, prevValue, nextValue);\n }\n }\n\n return record._value;\n}\n\nfunction attachSelectorHookBinding<T>(\n hook: SelectorHook<T>,\n source: () => T,\n equals: SelectorEquals<T>\n): void {\n const record = getSelectorSourceRecord(source);\n const lane = getSelectorLane(record, equals);\n\n hook._source = source;\n hook._equals = equals;\n hook._record = record;\n hook._lane = lane;\n lane._bindingCount += 1;\n}\n\nfunction detachSelectorHookBinding<T>(hook: SelectorHook<T>): void {\n const record = hook._record;\n const lane = hook._lane;\n\n hook._record = null;\n hook._lane = null;\n\n if (!record || !lane) {\n return;\n }\n\n if (lane._bindingCount > 0) {\n lane._bindingCount -= 1;\n }\n\n if (lane._bindingCount > 0) {\n return;\n }\n\n lane._cleanup();\n record._lanes.delete(lane._equals);\n\n if (record._lanes.size === 0) {\n record._cleanup();\n }\n}\n\nfunction ensureSelectorHookBinding<T>(\n hook: SelectorHook<T>\n): SelectorSourceRecord<T> {\n const record = hook._record;\n const lane = hook._lane;\n\n if (\n record &&\n lane &&\n lane._bindingCount > 0 &&\n record._lanes.get(hook._equals) === lane &&\n record._source === hook._source\n ) {\n return record;\n }\n\n if (hook._record || hook._lane) {\n detachSelectorHookBinding(hook);\n }\n\n attachSelectorHookBinding(hook, hook._source, hook._equals);\n return hook._record!;\n}\n\nfunction createSelectorHook<T>(\n instance: ComponentInstance,\n hookIndex: number,\n source: () => T,\n equals: SelectorEquals<T>\n): SelectorHook<T> {\n const hook = function selectorPredicate(candidate: T): boolean {\n const selectorHook = hook as SelectorHook<T>;\n const record = ensureSelectorHookBinding(selectorHook);\n const lane = selectorHook._lane;\n if (!lane) {\n throw new Error('selector() binding could not be established.');\n }\n\n const sourceRef = getCandidateSource(lane, candidate);\n recordReadableRead(sourceRef);\n\n const perfMetricsStore = getPerfMetricsStore();\n if (perfMetricsStore) {\n perfMetricsStore.selectorCandidateReads += 1;\n }\n\n const current =\n record._dirty || !record._hasValue\n ? recomputeSelectorSourceRecord(record, record._scheduled)\n : record._value;\n\n return lane._equals(current, candidate);\n } as SelectorHook<T>;\n\n hook._owner = instance;\n hook._hookIndex = hookIndex;\n hook._source = source;\n hook._equals = equals;\n hook._record = null;\n hook._lane = null;\n hook._cleanup = () => {\n detachSelectorHookBinding(hook);\n };\n\n attachSelectorHookBinding(hook, source, equals);\n\n (instance.cleanupFns ??= []).push(() => {\n hook._cleanup();\n selectorCells.get(instance)?.delete(hookIndex);\n });\n\n return hook;\n}\n\nfunction getOrCreateSelectorHook<T>(\n instance: ComponentInstance,\n hookIndex: number,\n source: () => T,\n equals: SelectorEquals<T>\n): SelectorHook<T> {\n const store = getSelectorStore(instance);\n const existing = store.get(hookIndex) as SelectorHook<T> | undefined;\n if (existing) {\n if (existing._source !== source || existing._equals !== equals) {\n detachSelectorHookBinding(existing);\n attachSelectorHookBinding(existing, source, equals);\n } else {\n ensureSelectorHookBinding(existing);\n }\n return existing;\n }\n\n const created = createSelectorHook(instance, hookIndex, source, equals);\n store.set(hookIndex, created as unknown as SelectorHook<unknown>);\n return created;\n}\n\nexport function selector<T>(\n source: () => T,\n equals: SelectorEquals<T> = Object.is\n): Selector<T> {\n markReadableUsage(source);\n\n const instance = getCurrentInstance();\n if (!instance) {\n throw new Error(\n 'selector() can only be called during component render execution. ' +\n 'Move selector() calls to the top level of your component function.'\n );\n }\n\n const hookIndex = claimHookIndex(instance, 'selector');\n const hook = getOrCreateSelectorHook(instance, hookIndex, source, equals);\n if (!hook._record) {\n throw new Error('selector() record binding was not established.');\n }\n\n if (!hook._record._hasValue) {\n // Initialize the shared source once so downstream selectors can subscribe,\n // but do not consume a pending dirty state on later renders.\n recomputeSelectorSourceRecord(hook._record, false);\n }\n return hook;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"selector.js","names":[],"sources":["../../src/runtime/selector.ts"],"sourcesContent":["import {\n claimHookIndex,\n getCurrentInstance,\n type ComponentInstance,\n} from './component';\nimport { enqueueRuntimeLane } from './access';\nimport {\n clearDerivedDependencySubscriptions,\n markReadableDerivedSubscribersDirty,\n markReadableUsage,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n recordReadableRead,\n syncDerivedDependencySubscriptions,\n withDerivedReadTracking,\n type DerivedSubscriber,\n type ReadableSource,\n} from './readable';\nimport { getPerfMetricsStore, incrementPerfMetric } from './perf-metrics';\n\ntype PrimitiveKey =\n | string\n | number\n | boolean\n | symbol\n | bigint\n | null\n | undefined;\n\nexport interface Selector<T> {\n (candidate: T): boolean;\n}\n\ninterface SelectorCandidateSource<T> extends ReadableSource<boolean> {\n _candidate: T;\n}\n\ntype SelectorEquals<T> = {\n bivarianceHack(a: T, b: T): boolean;\n}['bivarianceHack'];\n\ninterface SelectorLane<T> {\n _record: SelectorSourceRecord<T>;\n _equals: SelectorEquals<T>;\n _bindingCount: number;\n _primitiveCandidates: Map<PrimitiveKey, SelectorCandidateSource<T>>;\n _objectCandidates: WeakMap<object, SelectorCandidateSource<T>>;\n _objectCandidateSources: Set<SelectorCandidateSource<T>>;\n _cleanup(): void;\n}\n\ninterface SelectorSourceRecord<T> extends DerivedSubscriber {\n _source: () => T;\n _value: T;\n _hasValue: boolean;\n _dirty: boolean;\n _scheduled: boolean;\n _evaluating: boolean;\n _sources: Set<ReadableSource<unknown>>;\n _pendingDependencySources?: Set<ReadableSource<unknown>>;\n _lanes: Map<SelectorEquals<T>, SelectorLane<T>>;\n _cleanup(): void;\n}\n\ninterface SelectorHook<T> extends Selector<T> {\n _owner: ComponentInstance;\n _hookIndex: number;\n _source: () => T;\n _equals: SelectorEquals<T>;\n _record: SelectorSourceRecord<T> | null;\n _lane: SelectorLane<T> | null;\n _cleanup(): void;\n}\n\nconst selectorCells = new WeakMap<\n ComponentInstance,\n Map<number, SelectorHook<unknown>>\n>();\nconst selectorRecords = new WeakMap<\n ReadableSource<unknown>,\n SelectorSourceRecord<unknown>\n>();\nconst dirtySelectorRecords = new Set<SelectorSourceRecord<unknown>>();\nlet hasPendingSelectorFlush = false;\n\nfunction getSelectorStore(\n instance: ComponentInstance\n): Map<number, SelectorHook<unknown>> {\n let store = selectorCells.get(instance);\n if (!store) {\n store = new Map();\n selectorCells.set(instance, store);\n }\n return store;\n}\n\nfunction scheduleSelectorFlush(): void {\n if (hasPendingSelectorFlush) {\n return;\n }\n\n hasPendingSelectorFlush = true;\n enqueueRuntimeLane('derived', flushDirtySelectorRecords);\n}\n\nfunction markSelectorRecordDirty(record: SelectorSourceRecord<unknown>): void {\n record._dirty = true;\n if (record._scheduled) {\n return;\n }\n\n record._scheduled = true;\n dirtySelectorRecords.add(record);\n scheduleSelectorFlush();\n}\n\nfunction flushDirtySelectorRecords(): void {\n hasPendingSelectorFlush = false;\n\n if (dirtySelectorRecords.size === 0) {\n return;\n }\n\n const pending = Array.from(dirtySelectorRecords);\n dirtySelectorRecords.clear();\n\n for (const record of pending) {\n record._scheduled = false;\n if (!record._dirty) {\n continue;\n }\n recomputeSelectorSourceRecord(record, true);\n }\n}\n\nfunction isObjectCandidate(value: unknown): value is object {\n return (\n (typeof value === 'object' && value !== null) || typeof value === 'function'\n );\n}\n\nfunction isDefaultSelectorEquals<T>(equals: SelectorEquals<T>): boolean {\n return equals === Object.is;\n}\n\nfunction createCandidateSource<T>(candidate: T): SelectorCandidateSource<T> {\n const source = (() => false) as SelectorCandidateSource<T>;\n source._candidate = candidate;\n return source;\n}\n\nfunction getCandidateSource<T>(\n lane: SelectorLane<T>,\n candidate: T\n): SelectorCandidateSource<T> {\n if (isObjectCandidate(candidate)) {\n const cached = lane._objectCandidates.get(candidate);\n if (cached) {\n return cached;\n }\n\n const created = createCandidateSource(candidate);\n lane._objectCandidates.set(candidate, created);\n lane._objectCandidateSources.add(created);\n return created;\n }\n\n const key = candidate as PrimitiveKey;\n const cached = lane._primitiveCandidates.get(key);\n if (cached) {\n return cached;\n }\n\n const created = createCandidateSource(candidate);\n lane._primitiveCandidates.set(key, created);\n return created;\n}\n\nfunction peekCandidateSource<T>(\n lane: SelectorLane<T>,\n candidate: T\n): SelectorCandidateSource<T> | undefined {\n if (isObjectCandidate(candidate)) {\n return lane._objectCandidates.get(candidate);\n }\n return lane._primitiveCandidates.get(candidate as PrimitiveKey);\n}\n\nfunction getSelectorSourceRecord<T>(source: () => T): SelectorSourceRecord<T> {\n const cached = selectorRecords.get(source);\n if (cached) {\n return cached as SelectorSourceRecord<T>;\n }\n\n const record = createSelectorSourceRecord(source);\n selectorRecords.set(source, record as SelectorSourceRecord<unknown>);\n return record;\n}\n\nfunction createSelectorSourceRecord<T>(\n source: () => T\n): SelectorSourceRecord<T> {\n let record!: SelectorSourceRecord<T>;\n\n record = {\n _source: source,\n _value: undefined as T,\n _hasValue: false,\n _dirty: true,\n _scheduled: false,\n _evaluating: false,\n _sources: new Set(),\n _lanes: new Map(),\n _markDirty: () => {\n markSelectorRecordDirty(record as SelectorSourceRecord<unknown>);\n },\n _cleanup: () => {\n record._scheduled = false;\n record._dirty = false;\n record._hasValue = false;\n record._evaluating = false;\n record._pendingDependencySources = undefined;\n clearDerivedDependencySubscriptions(record, record._sources);\n record._lanes.clear();\n selectorRecords.delete(source as ReadableSource<unknown>);\n },\n };\n\n return record;\n}\n\nfunction getSelectorLane<T>(\n record: SelectorSourceRecord<T>,\n equals: SelectorEquals<T>\n): SelectorLane<T> {\n const cached = record._lanes.get(equals);\n if (cached) {\n return cached;\n }\n\n const lane = createSelectorLane(record, equals);\n record._lanes.set(equals, lane);\n return lane;\n}\n\nfunction createSelectorLane<T>(\n record: SelectorSourceRecord<T>,\n equals: SelectorEquals<T>\n): SelectorLane<T> {\n const lane: SelectorLane<T> = {\n _record: record,\n _equals: equals,\n _bindingCount: 0,\n _primitiveCandidates: new Map(),\n _objectCandidates: new WeakMap(),\n _objectCandidateSources: new Set(),\n _cleanup: () => {\n for (const sourceRef of lane._primitiveCandidates.values()) {\n sourceRef._readers?.clear();\n sourceRef._derivedSubscribers?.clear();\n }\n for (const sourceRef of lane._objectCandidateSources) {\n sourceRef._readers?.clear();\n sourceRef._derivedSubscribers?.clear();\n }\n lane._primitiveCandidates.clear();\n lane._objectCandidates = new WeakMap();\n lane._objectCandidateSources.clear();\n },\n };\n\n return lane;\n}\n\nfunction notifySelectorSource(source: SelectorCandidateSource<unknown>): void {\n incrementPerfMetric('selectorInvalidations');\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n}\n\nfunction notifyAllSelectorSources<T>(lane: SelectorLane<T>): void {\n for (const source of lane._primitiveCandidates.values()) {\n notifySelectorSource(source);\n }\n for (const source of lane._objectCandidateSources) {\n notifySelectorSource(source);\n }\n}\n\nfunction notifySelectorLaneValueChange<T>(\n lane: SelectorLane<T>,\n prevValue: T,\n nextValue: T\n): void {\n if (!lane._bindingCount) {\n return;\n }\n\n if (!isDefaultSelectorEquals(lane._equals)) {\n if (lane._equals(prevValue, nextValue)) {\n return;\n }\n notifyAllSelectorSources(lane);\n return;\n }\n\n if (!Object.is(prevValue, nextValue)) {\n // Only notify candidate sources that were actually materialized by a read.\n // Using getCandidateSource here would create-on-miss and leak a candidate\n // source for every distinct value the source ever passed through, even\n // those no component reads. Peek instead so the candidate cache stays\n // bounded to values that components actually compare against.\n const prevSource = peekCandidateSource(lane, prevValue);\n if (prevSource) {\n notifySelectorSource(prevSource);\n }\n const nextSource = peekCandidateSource(lane, nextValue);\n if (nextSource) {\n notifySelectorSource(nextSource);\n }\n }\n}\n\nfunction recomputeSelectorSourceRecord<T>(\n record: SelectorSourceRecord<T>,\n notifyDownstream: boolean\n): T {\n if (!record._dirty && record._hasValue) {\n return record._value;\n }\n\n if (record._evaluating) {\n throw new Error('selector() cannot read itself recursively');\n }\n\n record._evaluating = true;\n record._dirty = false;\n record._pendingDependencySources = new Set();\n\n const prevSources = record._sources;\n const hadValue = record._hasValue;\n const prevValue = record._value;\n let nextValue: T;\n\n try {\n nextValue = withDerivedReadTracking(record, record._source);\n } catch (error) {\n record._dirty = true;\n record._pendingDependencySources = undefined;\n throw error;\n } finally {\n record._evaluating = false;\n }\n\n const nextSources = record._pendingDependencySources ?? new Set();\n record._pendingDependencySources = undefined;\n syncDerivedDependencySubscriptions(record, prevSources, nextSources);\n record._sources = nextSources;\n\n const valueChanged = !record._hasValue || !Object.is(prevValue, nextValue);\n record._hasValue = true;\n record._value = nextValue;\n\n if (valueChanged && notifyDownstream && hadValue) {\n for (const lane of Array.from(record._lanes.values())) {\n notifySelectorLaneValueChange(lane, prevValue, nextValue);\n }\n }\n\n return record._value;\n}\n\nfunction attachSelectorHookBinding<T>(\n hook: SelectorHook<T>,\n source: () => T,\n equals: SelectorEquals<T>\n): void {\n const record = getSelectorSourceRecord(source);\n const lane = getSelectorLane(record, equals);\n\n hook._source = source;\n hook._equals = equals;\n hook._record = record;\n hook._lane = lane;\n lane._bindingCount += 1;\n}\n\nfunction detachSelectorHookBinding<T>(hook: SelectorHook<T>): void {\n const record = hook._record;\n const lane = hook._lane;\n\n hook._record = null;\n hook._lane = null;\n\n if (!record || !lane) {\n return;\n }\n\n if (lane._bindingCount > 0) {\n lane._bindingCount -= 1;\n }\n\n if (lane._bindingCount > 0) {\n return;\n }\n\n lane._cleanup();\n record._lanes.delete(lane._equals);\n\n if (record._lanes.size === 0) {\n record._cleanup();\n }\n}\n\nfunction ensureSelectorHookBinding<T>(\n hook: SelectorHook<T>\n): SelectorSourceRecord<T> {\n const record = hook._record;\n const lane = hook._lane;\n\n if (\n record &&\n lane &&\n lane._bindingCount > 0 &&\n record._lanes.get(hook._equals) === lane &&\n record._source === hook._source\n ) {\n return record;\n }\n\n if (hook._record || hook._lane) {\n detachSelectorHookBinding(hook);\n }\n\n attachSelectorHookBinding(hook, hook._source, hook._equals);\n return hook._record!;\n}\n\nfunction createSelectorHook<T>(\n instance: ComponentInstance,\n hookIndex: number,\n source: () => T,\n equals: SelectorEquals<T>\n): SelectorHook<T> {\n const hook = function selectorPredicate(candidate: T): boolean {\n const selectorHook = hook as SelectorHook<T>;\n const record = ensureSelectorHookBinding(selectorHook);\n const lane = selectorHook._lane;\n if (!lane) {\n throw new Error('selector() binding could not be established.');\n }\n\n const sourceRef = getCandidateSource(lane, candidate);\n recordReadableRead(sourceRef);\n\n const perfMetricsStore = getPerfMetricsStore();\n if (perfMetricsStore) {\n perfMetricsStore.selectorCandidateReads += 1;\n }\n\n const current =\n record._dirty || !record._hasValue\n ? recomputeSelectorSourceRecord(record, record._scheduled)\n : record._value;\n\n return lane._equals(current, candidate);\n } as SelectorHook<T>;\n\n hook._owner = instance;\n hook._hookIndex = hookIndex;\n hook._source = source;\n hook._equals = equals;\n hook._record = null;\n hook._lane = null;\n hook._cleanup = () => {\n detachSelectorHookBinding(hook);\n };\n\n attachSelectorHookBinding(hook, source, equals);\n\n (instance.cleanupFns ??= []).push(() => {\n hook._cleanup();\n selectorCells.get(instance)?.delete(hookIndex);\n });\n\n return hook;\n}\n\nfunction getOrCreateSelectorHook<T>(\n instance: ComponentInstance,\n hookIndex: number,\n source: () => T,\n equals: SelectorEquals<T>\n): SelectorHook<T> {\n const store = getSelectorStore(instance);\n const existing = store.get(hookIndex) as SelectorHook<T> | undefined;\n if (existing) {\n if (existing._source !== source || existing._equals !== equals) {\n detachSelectorHookBinding(existing);\n attachSelectorHookBinding(existing, source, equals);\n } else {\n ensureSelectorHookBinding(existing);\n }\n return existing;\n }\n\n const created = createSelectorHook(instance, hookIndex, source, equals);\n store.set(hookIndex, created as unknown as SelectorHook<unknown>);\n return created;\n}\n\nexport function selector<T>(\n source: () => T,\n equals: SelectorEquals<T> = Object.is\n): Selector<T> {\n markReadableUsage(source);\n\n const instance = getCurrentInstance();\n if (!instance) {\n throw new Error(\n 'selector() can only be called during component render execution. ' +\n 'Move selector() calls to the top level of your component function.'\n );\n }\n\n const hookIndex = claimHookIndex(instance, 'selector');\n const hook = getOrCreateSelectorHook(instance, hookIndex, source, equals);\n if (!hook._record) {\n throw new Error('selector() record binding was not established.');\n }\n\n if (!hook._record._hasValue) {\n // Initialize the shared source once so downstream selectors can subscribe,\n // but do not consume a pending dirty state on later renders.\n recomputeSelectorSourceRecord(hook._record, false);\n }\n return hook;\n}\n"],"mappings":"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"}
@@ -6,5 +6,5 @@ type SnapshotSourceBrand = {
6
6
  declare function brandSnapshotSource<T extends object>(value: T): T & SnapshotSourceBrand;
7
7
  declare function isSnapshotSource(value: unknown): value is SnapshotSourceBrand;
8
8
  //#endregion
9
- export { SnapshotSourceBrand };
9
+ export { SNAPSHOT_SOURCE_BRAND, SnapshotSourceBrand };
10
10
  //# sourceMappingURL=snapshot-source.d.ts.map
@@ -12,6 +12,6 @@ function isSnapshotSource(value) {
12
12
  return !!value && typeof value === "object" && value[SNAPSHOT_SOURCE_BRAND] === true;
13
13
  }
14
14
  //#endregion
15
- export { brandSnapshotSource, isSnapshotSource };
15
+ export { SNAPSHOT_SOURCE_BRAND, brandSnapshotSource, isSnapshotSource };
16
16
 
17
17
  //# sourceMappingURL=snapshot-source.js.map
@@ -1,6 +1,7 @@
1
1
  import { markReactivePropsDirtySource, markReadableDerivedSubscribersDirty, notifyReadableReaders, recordReadableRead } from "./readable.js";
2
+ import { claimHookIndex, getCurrentInstance } from "./component-scope.js";
2
3
  import { isBulkCommitActive } from "./fastlane.js";
3
- import { claimHookIndex, getCurrentInstance } from "./component.js";
4
+ import "./component.js";
4
5
  //#region src/runtime/state.ts
5
6
  /**
6
7
  * State primitive for Askr components
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","names":[],"sources":["../../src/runtime/state.ts"],"sourcesContent":["/**\n * State primitive for Askr components\n * Optimized for minimal overhead and fast updates\n *\n * INVARIANTS ENFORCED:\n * - state() only callable during component render (currentInstance exists)\n * - state() called at top-level only (indices must be monotonically increasing)\n * - state values persist across re-renders (stored in stateValues array)\n * - state.set() cannot be called during render (causes infinite loops)\n * - state.set() always enqueues through scheduler (never direct mutation)\n * - state.set() callback (notifyUpdate) always available\n */\n\nimport {\n claimHookIndex,\n getCurrentInstance,\n type ComponentInstance,\n} from './component';\nimport { isBulkCommitActive } from './fastlane';\nimport {\n recordReadableRead,\n type ReadableSource,\n markReadableDerivedSubscribersDirty,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n} from './readable';\n\n/**\n * State value holder - callable to read, has set method to update\n * @example\n * const count = state(0);\n * count(); // read: 0\n * count.set(1); // write: triggers re-render\n */\nexport interface State<T> extends ReadableSource<T> {\n (): T;\n set(...args: StateSetterArgs<T>): void;\n [Symbol.iterator](): IterableIterator<StateTuple<T>[number]>; // Allows destructuring: const [get, set] = state(0)\n}\n\ntype StateUpdater<T> = (prev: T) => T;\n\ntype StateSetterArgs<T> = [Extract<T, (...args: any[]) => unknown>] extends [\n never,\n]\n ? [value: T] | [updater: StateUpdater<T>]\n : [updater: StateUpdater<T>];\n\n/**\n * Public setter type for state cells.\n */\nexport type StateSetter<T> = (...args: StateSetterArgs<T>) => void;\n\n/**\n * Tuple-first state handle returned by `state()`.\n */\nexport type StateTuple<T> = [get: State<T>, set: StateSetter<T>] & State<T>;\n\n/**\n * Creates a local state value for a component\n * Optimized for:\n * - O(1) read performance\n * - Minimal allocation per state\n * - Fast scheduler integration\n *\n * IMPORTANT: state() must be called during component render execution.\n * It captures the current component instance from context.\n * Calling outside a component function will throw an error.\n *\n * @example\n * ```ts\n * // ✅ Correct: called during render\n * export function Counter() {\n * const [count, setCount] = state(0);\n * return { type: 'button', children: [count()] };\n * }\n *\n * // ❌ Wrong: called outside component\n * const count = state(0);\n * export function BadComponent() {\n * return { type: 'div' };\n * }\n * ```\n */\nexport function state<T>(initialValue: T): StateTuple<T> {\n // INVARIANT: state() must be called during component render\n const instance = getCurrentInstance();\n if (!instance) {\n throw new Error(\n 'state() can only be called during component render execution. ' +\n 'Move state() calls to the top level of your component function.'\n );\n }\n\n const index = claimHookIndex(instance, 'state');\n const stateValues = instance.stateValues;\n\n // INVARIANT: Reuse existing state if it exists (fast path on re-renders)\n // This ensures state identity and persistence and enforces ownership stability\n if (stateValues[index]) {\n const existing = stateValues[index] as State<T> & {\n _owner?: ComponentInstance;\n };\n // Ownership must be stable: the state cell belongs to the instance that\n // created it and must never change. This checks for accidental reuse.\n if (existing._owner !== instance) {\n throw new Error(\n `State ownership violation: state() called at index ${index} is owned by a different component instance. ` +\n `State ownership is positional and immutable.`\n );\n }\n return existing as StateTuple<T>;\n }\n\n // Create new state (slow path, only on first render) — delegated to helper\n const cell = createStateCell(initialValue, instance);\n\n // INVARIANT: Store state in instance for persistence across renders\n stateValues[index] = cell as unknown as State<unknown>;\n\n return cell as StateTuple<T>;\n}\n\n/**\n * Internal helper: create the backing state cell (value + readers + set semantics)\n * This extraction makes it easier to later split hook wiring from storage.\n */\nfunction createStateCell<T>(\n initialValue: T,\n instance: ComponentInstance\n): State<T> {\n let value = initialValue;\n\n // Per-state reader map: component -> last-committed render token\n const readers = new Map<ComponentInstance, number>();\n\n // Use a function as the state object (callable directly)\n function read(): T {\n (read as State<T>)._hasBeenRead = true;\n recordReadableRead(read as State<T>);\n\n return value;\n }\n\n // Attach the readers map to the callable so other runtime parts can access it\n (read as State<T>)._readers = readers;\n\n // Record explicit ownership of this state cell. Ownership is the component\n // instance that created the state cell and must never change for the life\n // of the cell. We expose this for runtime invariant checks/tests.\n (read as State<T> & { _owner?: ComponentInstance })._owner = instance;\n\n // Attach set method directly to function\n read.set = (newValueOrUpdater: T | ((prev: T) => T)): void => {\n // INVARIANT: State cannot be mutated during component render\n // (when currentInstance is non-null). It must be scheduled for consistency.\n const currentInst = getCurrentInstance();\n if (currentInst !== null) {\n throw new Error(\n `[Askr] state.set() cannot be called during component render. ` +\n `State mutations during render break the actor model and cause infinite loops. ` +\n `Move state updates to event handlers or use conditional rendering instead.`\n );\n }\n\n // Compute new value if an updater was provided\n let newValue: T;\n if (typeof newValueOrUpdater === 'function') {\n // Note: function-valued state cannot be set directly via a function argument;\n // such an argument is treated as a functional updater (this follows the common\n // convention from other libraries). If you need to store a function as state,\n // wrap it in an object.\n const updater = newValueOrUpdater as (prev: T) => T;\n newValue = updater(value);\n } else {\n newValue = newValueOrUpdater as T;\n }\n\n // Skip work if value didn't change\n if (Object.is(value, newValue)) return;\n\n // If a bulk commit is active, update backing value only and DO NOT notify or enqueue.\n // Bulk commits must be side-effect silent with respect to runtime notifications.\n if (isBulkCommitActive()) {\n // In bulk commit mode we must be side-effect free: update backing\n // value only and do not notify, enqueue, or log.\n value = newValue;\n return;\n }\n\n // INVARIANT: Update the value\n value = newValue;\n\n markReadableDerivedSubscribersDirty(read as State<T>);\n markReactivePropsDirtySource(read as State<T>);\n\n // notifyUpdate may be temporarily unavailable (e.g. during hydration).\n // We intentionally avoid logging here to keep the state mutation path\n // side-effect free. The scheduler will process updates when the system\n // is stable.\n // After value change, notify only components that *read* this state in their last committed render.\n void notifyReadableReaders(read as State<T>);\n };\n\n // Allow destructuring assignment: const [get, set] = state(0);\n // The state function is iterable and yields the getter then the setter\n (\n read as unknown as {\n [Symbol.iterator]?: () => Iterator<State<T> | State<T>['set']>;\n }\n )[Symbol.iterator] = function* (): Generator<\n State<T> | State<T>['set'],\n void,\n unknown\n > {\n yield read;\n yield read.set;\n };\n\n return read as State<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFA,SAAgB,MAAS,cAAgC;CAEvD,MAAM,WAAW,mBAAmB;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,+HAEF;CAGF,MAAM,QAAQ,eAAe,UAAU,OAAO;CAC9C,MAAM,cAAc,SAAS;CAI7B,IAAI,YAAY,QAAQ;EACtB,MAAM,WAAW,YAAY;EAK7B,IAAI,SAAS,WAAW,UACtB,MAAM,IAAI,MACR,sDAAsD,MAAM,0FAE9D;EAEF,OAAO;CACT;CAGA,MAAM,OAAO,gBAAgB,cAAc,QAAQ;CAGnD,YAAY,SAAS;CAErB,OAAO;AACT;;;;;AAMA,SAAS,gBACP,cACA,UACU;CACV,IAAI,QAAQ;CAGZ,MAAM,0BAAU,IAAI,IAA+B;CAGnD,SAAS,OAAU;EACjB,KAAmB,eAAe;EAClC,mBAAmB,IAAgB;EAEnC,OAAO;CACT;CAGA,KAAmB,WAAW;CAK9B,KAAoD,SAAS;CAG7D,KAAK,OAAO,sBAAkD;EAI5D,IADoB,mBAChB,MAAgB,MAClB,MAAM,IAAI,MACR,uNAGF;EAIF,IAAI;EACJ,IAAI,OAAO,sBAAsB,YAM/B,WAAW,kBAAQ,KAAK;OAExB,WAAW;EAIb,IAAI,OAAO,GAAG,OAAO,QAAQ,GAAG;EAIhC,IAAI,mBAAmB,GAAG;GAGxB,QAAQ;GACR;EACF;EAGA,QAAQ;EAER,oCAAoC,IAAgB;EACpD,6BAA6B,IAAgB;EAO7C,AAAK,sBAAsB,IAAgB;CAC7C;CAIA,KAIE,OAAO,YAAY,aAInB;EACA,MAAM;EACN,MAAM,KAAK;CACb;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"state.js","names":[],"sources":["../../src/runtime/state.ts"],"sourcesContent":["/**\n * State primitive for Askr components\n * Optimized for minimal overhead and fast updates\n *\n * INVARIANTS ENFORCED:\n * - state() only callable during component render (currentInstance exists)\n * - state() called at top-level only (indices must be monotonically increasing)\n * - state values persist across re-renders (stored in stateValues array)\n * - state.set() cannot be called during render (causes infinite loops)\n * - state.set() always enqueues through scheduler (never direct mutation)\n * - state.set() callback (notifyUpdate) always available\n */\n\nimport {\n claimHookIndex,\n getCurrentInstance,\n type ComponentInstance,\n} from './component';\nimport { isBulkCommitActive } from './fastlane';\nimport {\n recordReadableRead,\n type ReadableSource,\n markReadableDerivedSubscribersDirty,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n} from './readable';\n\n/**\n * State value holder - callable to read, has set method to update\n * @example\n * const count = state(0);\n * count(); // read: 0\n * count.set(1); // write: triggers re-render\n */\nexport interface State<T> extends ReadableSource<T> {\n (): T;\n set(...args: StateSetterArgs<T>): void;\n [Symbol.iterator](): IterableIterator<StateTuple<T>[number]>; // Allows destructuring: const [get, set] = state(0)\n}\n\ntype StateUpdater<T> = (prev: T) => T;\n\ntype StateSetterArgs<T> = [Extract<T, (...args: any[]) => unknown>] extends [\n never,\n]\n ? [value: T] | [updater: StateUpdater<T>]\n : [updater: StateUpdater<T>];\n\n/**\n * Public setter type for state cells.\n */\nexport type StateSetter<T> = (...args: StateSetterArgs<T>) => void;\n\n/**\n * Tuple-first state handle returned by `state()`.\n */\nexport type StateTuple<T> = [get: State<T>, set: StateSetter<T>] & State<T>;\n\n/**\n * Creates a local state value for a component\n * Optimized for:\n * - O(1) read performance\n * - Minimal allocation per state\n * - Fast scheduler integration\n *\n * IMPORTANT: state() must be called during component render execution.\n * It captures the current component instance from context.\n * Calling outside a component function will throw an error.\n *\n * @example\n * ```ts\n * // ✅ Correct: called during render\n * export function Counter() {\n * const [count, setCount] = state(0);\n * return { type: 'button', children: [count()] };\n * }\n *\n * // ❌ Wrong: called outside component\n * const count = state(0);\n * export function BadComponent() {\n * return { type: 'div' };\n * }\n * ```\n */\nexport function state<T>(initialValue: T): StateTuple<T> {\n // INVARIANT: state() must be called during component render\n const instance = getCurrentInstance();\n if (!instance) {\n throw new Error(\n 'state() can only be called during component render execution. ' +\n 'Move state() calls to the top level of your component function.'\n );\n }\n\n const index = claimHookIndex(instance, 'state');\n const stateValues = instance.stateValues;\n\n // INVARIANT: Reuse existing state if it exists (fast path on re-renders)\n // This ensures state identity and persistence and enforces ownership stability\n if (stateValues[index]) {\n const existing = stateValues[index] as State<T> & {\n _owner?: ComponentInstance;\n };\n // Ownership must be stable: the state cell belongs to the instance that\n // created it and must never change. This checks for accidental reuse.\n if (existing._owner !== instance) {\n throw new Error(\n `State ownership violation: state() called at index ${index} is owned by a different component instance. ` +\n `State ownership is positional and immutable.`\n );\n }\n return existing as StateTuple<T>;\n }\n\n // Create new state (slow path, only on first render) — delegated to helper\n const cell = createStateCell(initialValue, instance);\n\n // INVARIANT: Store state in instance for persistence across renders\n stateValues[index] = cell as unknown as State<unknown>;\n\n return cell as StateTuple<T>;\n}\n\n/**\n * Internal helper: create the backing state cell (value + readers + set semantics)\n * This extraction makes it easier to later split hook wiring from storage.\n */\nfunction createStateCell<T>(\n initialValue: T,\n instance: ComponentInstance\n): State<T> {\n let value = initialValue;\n\n // Per-state reader map: component -> last-committed render token\n const readers = new Map<ComponentInstance, number>();\n\n // Use a function as the state object (callable directly)\n function read(): T {\n (read as State<T>)._hasBeenRead = true;\n recordReadableRead(read as State<T>);\n\n return value;\n }\n\n // Attach the readers map to the callable so other runtime parts can access it\n (read as State<T>)._readers = readers;\n\n // Record explicit ownership of this state cell. Ownership is the component\n // instance that created the state cell and must never change for the life\n // of the cell. We expose this for runtime invariant checks/tests.\n (read as State<T> & { _owner?: ComponentInstance })._owner = instance;\n\n // Attach set method directly to function\n read.set = (newValueOrUpdater: T | ((prev: T) => T)): void => {\n // INVARIANT: State cannot be mutated during component render\n // (when currentInstance is non-null). It must be scheduled for consistency.\n const currentInst = getCurrentInstance();\n if (currentInst !== null) {\n throw new Error(\n `[Askr] state.set() cannot be called during component render. ` +\n `State mutations during render break the actor model and cause infinite loops. ` +\n `Move state updates to event handlers or use conditional rendering instead.`\n );\n }\n\n // Compute new value if an updater was provided\n let newValue: T;\n if (typeof newValueOrUpdater === 'function') {\n // Note: function-valued state cannot be set directly via a function argument;\n // such an argument is treated as a functional updater (this follows the common\n // convention from other libraries). If you need to store a function as state,\n // wrap it in an object.\n const updater = newValueOrUpdater as (prev: T) => T;\n newValue = updater(value);\n } else {\n newValue = newValueOrUpdater as T;\n }\n\n // Skip work if value didn't change\n if (Object.is(value, newValue)) return;\n\n // If a bulk commit is active, update backing value only and DO NOT notify or enqueue.\n // Bulk commits must be side-effect silent with respect to runtime notifications.\n if (isBulkCommitActive()) {\n // In bulk commit mode we must be side-effect free: update backing\n // value only and do not notify, enqueue, or log.\n value = newValue;\n return;\n }\n\n // INVARIANT: Update the value\n value = newValue;\n\n markReadableDerivedSubscribersDirty(read as State<T>);\n markReactivePropsDirtySource(read as State<T>);\n\n // notifyUpdate may be temporarily unavailable (e.g. during hydration).\n // We intentionally avoid logging here to keep the state mutation path\n // side-effect free. The scheduler will process updates when the system\n // is stable.\n // After value change, notify only components that *read* this state in their last committed render.\n void notifyReadableReaders(read as State<T>);\n };\n\n // Allow destructuring assignment: const [get, set] = state(0);\n // The state function is iterable and yields the getter then the setter\n (\n read as unknown as {\n [Symbol.iterator]?: () => Iterator<State<T> | State<T>['set']>;\n }\n )[Symbol.iterator] = function* (): Generator<\n State<T> | State<T>['set'],\n void,\n unknown\n > {\n yield read;\n yield read.set;\n };\n\n return read as State<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFA,SAAgB,MAAS,cAAgC;CAEvD,MAAM,WAAW,mBAAmB;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,+HAEF;CAGF,MAAM,QAAQ,eAAe,UAAU,OAAO;CAC9C,MAAM,cAAc,SAAS;CAI7B,IAAI,YAAY,QAAQ;EACtB,MAAM,WAAW,YAAY;EAK7B,IAAI,SAAS,WAAW,UACtB,MAAM,IAAI,MACR,sDAAsD,MAAM,0FAE9D;EAEF,OAAO;CACT;CAGA,MAAM,OAAO,gBAAgB,cAAc,QAAQ;CAGnD,YAAY,SAAS;CAErB,OAAO;AACT;;;;;AAMA,SAAS,gBACP,cACA,UACU;CACV,IAAI,QAAQ;CAGZ,MAAM,0BAAU,IAAI,IAA+B;CAGnD,SAAS,OAAU;EACjB,KAAmB,eAAe;EAClC,mBAAmB,IAAgB;EAEnC,OAAO;CACT;CAGA,KAAmB,WAAW;CAK9B,KAAoD,SAAS;CAG7D,KAAK,OAAO,sBAAkD;EAI5D,IADoB,mBAChB,MAAgB,MAClB,MAAM,IAAI,MACR,uNAGF;EAIF,IAAI;EACJ,IAAI,OAAO,sBAAsB,YAM/B,WAAW,kBAAQ,KAAK;OAExB,WAAW;EAIb,IAAI,OAAO,GAAG,OAAO,QAAQ,GAAG;EAIhC,IAAI,mBAAmB,GAAG;GAGxB,QAAQ;GACR;EACF;EAGA,QAAQ;EAER,oCAAoC,IAAgB;EACpD,6BAA6B,IAAgB;EAO7C,AAAK,sBAAsB,IAAgB;CAC7C;CAIA,KAIE,OAAO,YAAY,aAInB;EACA,MAAM;EACN,MAAM,KAAK;CACb;CAEA,OAAO;AACT"}
@@ -1,5 +1,6 @@
1
1
  import { renderDocument } from "../common/ssr.js";
2
- import { renderToString } from "../ssr/index.js";
2
+ import { renderToString } from "../ssr/index-internal.js";
3
+ import "../ssr/index.js";
3
4
  import { getOutputFilePath, interpolateRoutePath } from "./route-utils.js";
4
5
  import { resolveSsgRouteData } from "./resolve-ssg-data.js";
5
6
  //#region src/ssg/batch-render.ts
@@ -1 +1 @@
1
- {"version":3,"file":"batch-render.js","names":[],"sources":["../../src/ssg/batch-render.ts"],"sourcesContent":["/**\n * Batch rendering of multiple routes for SSG\n */\n\nimport { renderToString } from '../ssr';\nimport { renderDocument, type DocumentRenderer } from '../common/ssr';\nimport type { RouteConfig, RouteRenderResult } from './types';\nimport type { RouteHandler } from '../common/router';\nimport type { ComponentFunction } from '../common/component';\nimport type { SSRData } from '../common/ssr';\nimport { resolveSsgRouteData } from './resolve-ssg-data';\nimport { getOutputFilePath, interpolateRoutePath } from './route-utils';\n\ninterface BatchRenderOptions {\n seed?: number;\n dataMap?: Record<string, SSRData>;\n concurrency?: number;\n document?: DocumentRenderer;\n}\n\n/**\n * Render multiple routes in parallel with error handling\n */\nexport async function batchRenderRoutes(\n routes: RouteConfig[],\n options: BatchRenderOptions = {}\n): Promise<RouteRenderResult[]> {\n const { seed = 12345, dataMap = {}, concurrency = 1, document } = options;\n\n const workerCount = Math.max(1, Math.min(concurrency, routes.length || 1));\n const results: RouteRenderResult[] = [];\n results.length = routes.length;\n let nextIndex = 0;\n\n const renderOne = async (route: RouteConfig): Promise<RouteRenderResult> => {\n const startTime = performance.now();\n const url = interpolateRoutePath(route.path, route.params);\n const requestUrl = new URL(url, 'http://localhost');\n const resolvedData = resolveSsgRouteData(dataMap, route.path, url);\n const baseData = resolvedData.hasData ? resolvedData.data : undefined;\n const resourceCount =\n resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;\n\n const mergedHandler: RouteHandler = route.handler\n ? route.handler\n : (params, ctx?: unknown) => {\n const component = route.component as ComponentFunction;\n return component(\n { ...route.props, ...params },\n ctx as { signal: AbortSignal; ssr?: unknown }\n );\n };\n\n try {\n const routeEntry = {\n path: route.path,\n handler: mergedHandler,\n namespace: route.namespace,\n };\n\n const html = renderToString({\n url,\n routes: [routeEntry],\n seed,\n data: baseData,\n document:\n document === undefined\n ? undefined\n : ({ appHtml }) =>\n renderDocument(\n document,\n {\n appHtml,\n context: {\n mode: 'ssg',\n url,\n pathname: requestUrl.pathname,\n search: requestUrl.search,\n hash: requestUrl.hash,\n params: route.params ?? {},\n data: baseData,\n seed,\n route: {\n path: route.path,\n namespace: route.namespace,\n },\n },\n },\n 'createStaticGen()'\n ),\n });\n\n const duration = performance.now() - startTime;\n return {\n path: url,\n filePath: getOutputFilePath(url),\n html,\n fileSize: Buffer.byteLength(html, 'utf8'),\n renderDuration: Math.round(duration),\n resourceCount,\n status: 'success',\n reason: 'full',\n written: false,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n return {\n path: url,\n filePath: getOutputFilePath(url),\n html: '',\n fileSize: 0,\n renderDuration: Math.round(duration),\n resourceCount,\n status: 'error',\n reason: 'full',\n written: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n const worker = async () => {\n while (true) {\n const current = nextIndex;\n nextIndex += 1;\n if (current >= routes.length) return;\n results[current] = await renderOne(routes[current]);\n }\n };\n\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n"],"mappings":";;;;;;;;;;;AAuBA,eAAsB,kBACpB,QACA,UAA8B,CAAC,GACD;CAC9B,MAAM,EAAE,OAAO,OAAO,UAAU,CAAC,GAAG,cAAc,GAAG,aAAa;CAElE,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,aAAa,OAAO,UAAU,CAAC,CAAC;CACzE,MAAM,UAA+B,CAAC;CACtC,QAAQ,SAAS,OAAO;CACxB,IAAI,YAAY;CAEhB,MAAM,YAAY,OAAO,UAAmD;EAC1E,MAAM,YAAY,YAAY,IAAI;EAClC,MAAM,MAAM,qBAAqB,MAAM,MAAM,MAAM,MAAM;EACzD,MAAM,aAAa,IAAI,IAAI,KAAK,kBAAkB;EAClD,MAAM,eAAe,oBAAoB,SAAS,MAAM,MAAM,GAAG;EACjE,MAAM,WAAW,aAAa,UAAU,aAAa,OAAO;EAC5D,MAAM,gBACJ,aAAa,WAAW,WAAW,OAAO,KAAK,QAAQ,CAAC,CAAC,SAAS;EAEpE,MAAM,gBAA8B,MAAM,UACtC,MAAM,WACL,QAAQ,QAAkB;GACzB,MAAM,YAAY,MAAM;GACxB,OAAO,UACL;IAAE,GAAG,MAAM;IAAO,GAAG;GAAO,GAC5B,GACF;EACF;EAEJ,IAAI;GAOF,MAAM,OAAO,eAAe;IAC1B;IACA,QAAQ,CAAC;KAPT,MAAM,MAAM;KACZ,SAAS;KACT,WAAW,MAAM;IAKR,CAAU;IACnB;IACA,MAAM;IACN,UACE,aAAa,SACT,UACC,EAAE,cACD,eACE,UACA;KACE;KACA,SAAS;MACP,MAAM;MACN;MACA,UAAU,WAAW;MACrB,QAAQ,WAAW;MACnB,MAAM,WAAW;MACjB,QAAQ,MAAM,UAAU,CAAC;MACzB,MAAM;MACN;MACA,OAAO;OACL,MAAM,MAAM;OACZ,WAAW,MAAM;MACnB;KACF;IACF,GACA,mBACF;GACV,CAAC;GAED,MAAM,WAAW,YAAY,IAAI,IAAI;GACrC,OAAO;IACL,MAAM;IACN,UAAU,kBAAkB,GAAG;IAC/B;IACA,UAAU,OAAO,WAAW,MAAM,MAAM;IACxC,gBAAgB,KAAK,MAAM,QAAQ;IACnC;IACA,QAAQ;IACR,QAAQ;IACR,SAAS;GACX;EACF,SAAS,OAAO;GACd,MAAM,WAAW,YAAY,IAAI,IAAI;GACrC,OAAO;IACL,MAAM;IACN,UAAU,kBAAkB,GAAG;IAC/B,MAAM;IACN,UAAU;IACV,gBAAgB,KAAK,MAAM,QAAQ;IACnC;IACA,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D;EACF;CACF;CAEA,MAAM,SAAS,YAAY;EACzB,OAAO,MAAM;GACX,MAAM,UAAU;GAChB,aAAa;GACb,IAAI,WAAW,OAAO,QAAQ;GAC9B,QAAQ,WAAW,MAAM,UAAU,OAAO,QAAQ;EACpD;CACF;CAEA,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,YAAY,SAAS,OAAO,CAAC,CAAC;CACrE,OAAO;AACT"}
1
+ {"version":3,"file":"batch-render.js","names":[],"sources":["../../src/ssg/batch-render.ts"],"sourcesContent":["/**\n * Batch rendering of multiple routes for SSG\n */\n\nimport { renderToString } from '../ssr';\nimport { renderDocument, type DocumentRenderer } from '../common/ssr';\nimport type { RouteConfig, RouteRenderResult } from './types';\nimport type { RouteHandler } from '../common/router';\nimport type { ComponentFunction } from '../common/component';\nimport type { SSRData } from '../common/ssr';\nimport { resolveSsgRouteData } from './resolve-ssg-data';\nimport { getOutputFilePath, interpolateRoutePath } from './route-utils';\n\ninterface BatchRenderOptions {\n seed?: number;\n dataMap?: Record<string, SSRData>;\n concurrency?: number;\n document?: DocumentRenderer;\n}\n\n/**\n * Render multiple routes in parallel with error handling\n */\nexport async function batchRenderRoutes(\n routes: RouteConfig[],\n options: BatchRenderOptions = {}\n): Promise<RouteRenderResult[]> {\n const { seed = 12345, dataMap = {}, concurrency = 1, document } = options;\n\n const workerCount = Math.max(1, Math.min(concurrency, routes.length || 1));\n const results: RouteRenderResult[] = [];\n results.length = routes.length;\n let nextIndex = 0;\n\n const renderOne = async (route: RouteConfig): Promise<RouteRenderResult> => {\n const startTime = performance.now();\n const url = interpolateRoutePath(route.path, route.params);\n const requestUrl = new URL(url, 'http://localhost');\n const resolvedData = resolveSsgRouteData(dataMap, route.path, url);\n const baseData = resolvedData.hasData ? resolvedData.data : undefined;\n const resourceCount =\n resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;\n\n const mergedHandler: RouteHandler = route.handler\n ? route.handler\n : (params, ctx?: unknown) => {\n const component = route.component as ComponentFunction;\n return component(\n { ...route.props, ...params },\n ctx as Parameters<ComponentFunction>[1]\n );\n };\n\n try {\n const routeEntry = {\n path: route.path,\n handler: mergedHandler,\n namespace: route.namespace,\n };\n\n const html = renderToString({\n url,\n routes: [routeEntry],\n seed,\n data: baseData,\n document:\n document === undefined\n ? undefined\n : ({ appHtml }) =>\n renderDocument(\n document,\n {\n appHtml,\n context: {\n mode: 'ssg',\n url,\n pathname: requestUrl.pathname,\n search: requestUrl.search,\n hash: requestUrl.hash,\n params: route.params ?? {},\n data: baseData,\n seed,\n route: {\n path: route.path,\n namespace: route.namespace,\n },\n },\n },\n 'createStaticGen()'\n ),\n });\n\n const duration = performance.now() - startTime;\n return {\n path: url,\n filePath: getOutputFilePath(url),\n html,\n fileSize: Buffer.byteLength(html, 'utf8'),\n renderDuration: Math.round(duration),\n resourceCount,\n status: 'success',\n reason: 'full',\n written: false,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n return {\n path: url,\n filePath: getOutputFilePath(url),\n html: '',\n fileSize: 0,\n renderDuration: Math.round(duration),\n resourceCount,\n status: 'error',\n reason: 'full',\n written: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n const worker = async () => {\n while (true) {\n const current = nextIndex;\n nextIndex += 1;\n if (current >= routes.length) return;\n results[current] = await renderOne(routes[current]);\n }\n };\n\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n"],"mappings":";;;;;;;;;;;;AAuBA,eAAsB,kBACpB,QACA,UAA8B,CAAC,GACD;CAC9B,MAAM,EAAE,OAAO,OAAO,UAAU,CAAC,GAAG,cAAc,GAAG,aAAa;CAElE,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,aAAa,OAAO,UAAU,CAAC,CAAC;CACzE,MAAM,UAA+B,CAAC;CACtC,QAAQ,SAAS,OAAO;CACxB,IAAI,YAAY;CAEhB,MAAM,YAAY,OAAO,UAAmD;EAC1E,MAAM,YAAY,YAAY,IAAI;EAClC,MAAM,MAAM,qBAAqB,MAAM,MAAM,MAAM,MAAM;EACzD,MAAM,aAAa,IAAI,IAAI,KAAK,kBAAkB;EAClD,MAAM,eAAe,oBAAoB,SAAS,MAAM,MAAM,GAAG;EACjE,MAAM,WAAW,aAAa,UAAU,aAAa,OAAO;EAC5D,MAAM,gBACJ,aAAa,WAAW,WAAW,OAAO,KAAK,QAAQ,CAAC,CAAC,SAAS;EAEpE,MAAM,gBAA8B,MAAM,UACtC,MAAM,WACL,QAAQ,QAAkB;GACzB,MAAM,YAAY,MAAM;GACxB,OAAO,UACL;IAAE,GAAG,MAAM;IAAO,GAAG;GAAO,GAC5B,GACF;EACF;EAEJ,IAAI;GAOF,MAAM,OAAO,eAAe;IAC1B;IACA,QAAQ,CAAC;KAPT,MAAM,MAAM;KACZ,SAAS;KACT,WAAW,MAAM;IAKR,CAAU;IACnB;IACA,MAAM;IACN,UACE,aAAa,SACT,UACC,EAAE,cACD,eACE,UACA;KACE;KACA,SAAS;MACP,MAAM;MACN;MACA,UAAU,WAAW;MACrB,QAAQ,WAAW;MACnB,MAAM,WAAW;MACjB,QAAQ,MAAM,UAAU,CAAC;MACzB,MAAM;MACN;MACA,OAAO;OACL,MAAM,MAAM;OACZ,WAAW,MAAM;MACnB;KACF;IACF,GACA,mBACF;GACV,CAAC;GAED,MAAM,WAAW,YAAY,IAAI,IAAI;GACrC,OAAO;IACL,MAAM;IACN,UAAU,kBAAkB,GAAG;IAC/B;IACA,UAAU,OAAO,WAAW,MAAM,MAAM;IACxC,gBAAgB,KAAK,MAAM,QAAQ;IACnC;IACA,QAAQ;IACR,QAAQ;IACR,SAAS;GACX;EACF,SAAS,OAAO;GACd,MAAM,WAAW,YAAY,IAAI,IAAI;GACrC,OAAO;IACL,MAAM;IACN,UAAU,kBAAkB,GAAG;IAC/B,MAAM;IACN,UAAU;IACV,gBAAgB,KAAK,MAAM,QAAQ;IACnC;IACA,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D;EACF;CACF;CAEA,MAAM,SAAS,YAAY;EACzB,OAAO,MAAM;GACX,MAAM,UAAU;GAChB,aAAa;GACb,IAAI,WAAW,OAAO,QAAQ;GAC9B,QAAQ,WAAW,MAAM,UAAU,OAAO,QAAQ;EACpD;CACF;CAEA,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,YAAY,SAAS,OAAO,CAAC,CAAC;CACrE,OAAO;AACT"}
@@ -1,3 +1,4 @@
1
+ import { RouteRegistry } from "../common/router.js";
1
2
  import { RouteConfig, SSGGenerateOptions, SSGOptions, SSGResult } from "./types.js";
2
3
 
3
4
  //#region src/ssg/create-static-gen.d.ts
@@ -7,6 +8,13 @@ type StrictRouteConfig<TRoute extends AnyRouteConfig> = Omit<TRoute, 'params' |
7
8
  entries?: RouteConfig<TRoute['path']>['entries'];
8
9
  };
9
10
  type StrictRouteConfigs<TRoutes extends readonly AnyRouteConfig[]> = { [TIndex in keyof TRoutes]: TRoutes[TIndex] extends AnyRouteConfig ? StrictRouteConfig<TRoutes[TIndex]> : never };
11
+ type StaticGenRouteSource<TRoutes extends readonly AnyRouteConfig[]> = {
12
+ routes: TRoutes & StrictRouteConfigs<TRoutes>;
13
+ registry?: never;
14
+ } | {
15
+ registry: RouteRegistry;
16
+ routes?: never;
17
+ };
10
18
  /**
11
19
  * Create a Static Site Generator
12
20
  *
@@ -27,9 +35,7 @@ type StrictRouteConfigs<TRoutes extends readonly AnyRouteConfig[]> = { [TIndex i
27
35
  * console.log(`Generated ${result.successful}/${result.totalRoutes} routes`);
28
36
  * ```
29
37
  */
30
- declare function createStaticGen<const TRoutes extends readonly AnyRouteConfig[]>(options: Omit<SSGOptions<TRoutes>, 'routes'> & {
31
- routes: TRoutes & StrictRouteConfigs<TRoutes>;
32
- }): {
38
+ declare function createStaticGen<const TRoutes extends readonly AnyRouteConfig[]>(options: Omit<SSGOptions<TRoutes>, 'routes' | 'registry'> & StaticGenRouteSource<TRoutes>): {
33
39
  /**
34
40
  * Generate static HTML for all routes
35
41
  * Writes to outputDir with metadata.json
@@ -1 +1 @@
1
- {"version":3,"file":"create-static-gen.d.ts","names":[],"sources":["../../src/ssg/create-static-gen.ts"],"mappings":";;;KA4CK,cAAA,GAAiB,WAAW;AAAA,KAE5B,iBAAA,gBAAiC,cAAA,IAAkB,IAAA,CACtD,MAAA;EAGA,MAAA,GAAS,WAAA,CAAY,MAAA;EACrB,OAAA,GAAU,WAAA,CAAY,MAAA;AAAA;AAAA,KAGnB,kBAAA,0BAA4C,cAAA,yBAC9B,OAAA,GAAU,OAAA,CAAQ,MAAA,UAAgB,cAAA,GAC/C,iBAAA,CAAkB,OAAA,CAAQ,MAAA;;;;;;;;;;;;;;;;;;AALF;AAAA;;iBAqFd,eAAA,gCACiB,cAAA,IAE/B,OAAA,EAAS,IAAA,CAAK,UAAA,CAAW,OAAA;EACvB,MAAA,EAAQ,OAAA,GAAU,kBAAA,CAAmB,OAAA;AAAA;EArFZ;;;;;6BA+GN,kBAAA,GAChB,OAAA,CAAQ,SAAA;EA/GQ;;;;;;;;;;;EAAS;;AAAM;AAgFtC;eAwRiB,SAAA;AAAA"}
1
+ {"version":3,"file":"create-static-gen.d.ts","names":[],"sources":["../../src/ssg/create-static-gen.ts"],"mappings":";;;;KA+CK,cAAA,GAAiB,WAAW;AAAA,KAE5B,iBAAA,gBAAiC,cAAA,IAAkB,IAAA,CACtD,MAAA;EAGA,MAAA,GAAS,WAAA,CAAY,MAAA;EACrB,OAAA,GAAU,WAAA,CAAY,MAAA;AAAA;AAAA,KAGnB,kBAAA,0BAA4C,cAAA,yBAC9B,OAAA,GAAU,OAAA,CAAQ,MAAA,UAAgB,cAAA,GAC/C,iBAAA,CAAkB,OAAA,CAAQ,MAAA;AAAA,KAI3B,oBAAA,0BAA8C,cAAA;EAE7C,MAAA,EAAQ,OAAA,GAAU,kBAAA,CAAmB,OAAA;EACrC,QAAA;AAAA;EAGA,QAAA,EAAU,aAAA;EACV,MAAA;AAAA;;;;;;;;;;;AAhBwB;AAAA;;;;;;;;;iBAiEd,eAAA,gCACiB,cAAA,IAE/B,OAAA,EAAS,IAAA,CAAK,UAAA,CAAW,OAAA,4BACvB,oBAAA,CAAqB,OAAA;EAhEnB;;;;;6BA0FiB,kBAAA,GAChB,OAAA,CAAQ,SAAA;EA5FI;;;;;;;;;;;EAKM;;;;eA+TR,SAAA;AAAA"}
@@ -1,19 +1,13 @@
1
1
  import { addPerfDuration, incrementPerfMetric } from "../runtime/perf-metrics.js";
2
2
  import { resolveRouteDescriptor } from "./route-utils.js";
3
- import { expandRoutes, resolveSsgData, resolveSsgRouteData, validateRoutes } from "./resolve-ssg-data.js";
3
+ import { resolveSsgData, resolveSsgRouteData, validateRoutes } from "./resolve-ssg-data.js";
4
4
  import { batchRenderRoutes } from "./batch-render.js";
5
5
  import { writeStaticFiles } from "./write-static-files.js";
6
6
  import { generateSSGResult, resultToMetadata, writeMetadata } from "./generate-metadata.js";
7
7
  import { getExistingOutputFileSize, hashHtml, outputFileExists, readIncrementalManifest, writeIncrementalManifest } from "./incremental-manifest.js";
8
+ import { collectRemovedRouteResults, dedupeStrings, getRoutesToRender, selectRouteForGeneration } from "./generation-plan.js";
9
+ import { normalizeStaticRoutes, splitStaticRoutes } from "./static-routes.js";
8
10
  //#region src/ssg/create-static-gen.ts
9
- function getRuntimeOnlyDiagnostic(descriptor) {
10
- const { route } = descriptor;
11
- if (route.auth === true) return `Skipped prerender for "${descriptor.path}": authenticated routes are runtime-only by default.`;
12
- if (route.role) return `Skipped prerender for "${descriptor.path}": role-gated routes are runtime-only by default.`;
13
- if (route.permission) return `Skipped prerender for "${descriptor.path}": permission-gated routes are runtime-only by default.`;
14
- if (route.policies && route.policies.length > 0) return `Skipped prerender for "${descriptor.path}": routes with custom policies are runtime-only by default.`;
15
- return null;
16
- }
17
11
  function resolveParallelism(requested) {
18
12
  if (requested !== "auto") return Math.max(1, requested ?? 1);
19
13
  const maybeNavigator = globalThis;
@@ -44,10 +38,11 @@ function resolveParallelism(requested) {
44
38
  */
45
39
  function createStaticGen(options) {
46
40
  let result = null;
41
+ const configuredRoutes = normalizeStaticRoutes(options);
47
42
  const seed = options.seed ?? 12345;
48
43
  const resolvedParallelism = resolveParallelism(options.parallelism);
49
44
  const resolvedConcurrency = Math.max(1, options.concurrency ?? resolvedParallelism);
50
- if (!Array.isArray(options.routes) || options.routes.length === 0) throw new Error("routes array is required");
45
+ if (configuredRoutes.length === 0) throw new Error("routes array or route registry is required");
51
46
  if (!options.outputDir || options.outputDir.trim().length === 0) throw new Error("outputDir is required");
52
47
  return {
53
48
  /**
@@ -56,7 +51,8 @@ function createStaticGen(options) {
56
51
  * Returns detailed results
57
52
  */
58
53
  async generate(generateOptions = {}) {
59
- const routes = await expandRoutes(options.routes);
54
+ const splitRoutes = await splitStaticRoutes(configuredRoutes);
55
+ const routes = splitRoutes.routes;
60
56
  validateRoutes(routes);
61
57
  const changedKeys = dedupeStrings(generateOptions.changedKeys);
62
58
  const changedRoutes = dedupeStrings(generateOptions.changedRoutes);
@@ -66,26 +62,8 @@ function createStaticGen(options) {
66
62
  const dataMap = resolveSsgData(routes, { dataOverrides: options.dataOverrides });
67
63
  const descriptors = routes.map(resolveRouteDescriptor);
68
64
  const routeResultsById = /* @__PURE__ */ new Map();
69
- const eligibleDescriptors = [];
70
- for (const descriptor of descriptors) {
71
- const diagnostic = getRuntimeOnlyDiagnostic(descriptor);
72
- if (diagnostic) {
73
- routeResultsById.set(descriptor.routeId, {
74
- path: descriptor.path,
75
- filePath: descriptor.filePath,
76
- html: "",
77
- fileSize: 0,
78
- renderDuration: 0,
79
- resourceCount: 0,
80
- status: "skipped",
81
- reason: "runtime-only",
82
- written: false,
83
- error: diagnostic
84
- });
85
- continue;
86
- }
87
- eligibleDescriptors.push(descriptor);
88
- }
65
+ for (const runtimeRoute of splitRoutes.runtimeOnly) routeResultsById.set(runtimeRoute.routeId, runtimeRoute.result);
66
+ const eligibleDescriptors = descriptors;
89
67
  const previousEntries = new Map((previousManifest?.routes ?? []).map((entry) => [entry.routeId, entry]));
90
68
  const currentRouteIds = new Set(eligibleDescriptors.map((descriptor) => descriptor.routeId));
91
69
  const selected = [];
@@ -169,8 +147,8 @@ function createStaticGen(options) {
169
147
  }
170
148
  const removedResults = collectRemovedRouteResults(previousManifest, currentRouteIds);
171
149
  const routeResults = [];
172
- for (let index = 0; index < descriptors.length; index += 1) {
173
- const routeResult = routeResultsById.get(descriptors[index].routeId);
150
+ for (const routeId of splitRoutes.outputRouteIds) {
151
+ const routeResult = routeResultsById.get(routeId);
174
152
  if (routeResult) routeResults.push(routeResult);
175
153
  }
176
154
  routeResults.push(...removedResults);
@@ -197,7 +175,7 @@ function createStaticGen(options) {
197
175
  */
198
176
  getConfig() {
199
177
  return {
200
- routeCount: options.routes.length,
178
+ routeCount: configuredRoutes.length,
201
179
  outputDir: options.outputDir,
202
180
  seed,
203
181
  concurrency: resolvedConcurrency,
@@ -214,71 +192,6 @@ function createStaticGen(options) {
214
192
  }
215
193
  };
216
194
  }
217
- function dedupeStrings(values) {
218
- return values ? Array.from(new Set(values)) : [];
219
- }
220
- function getRoutesToRender(selected) {
221
- const routes = [];
222
- for (let index = 0; index < selected.length; index += 1) routes.push(selected[index].descriptor.route);
223
- return routes;
224
- }
225
- function selectRouteForGeneration(descriptor, previous, mode, changedKeys, changedRoutes) {
226
- if (mode === "full") return {
227
- descriptor,
228
- reason: "full",
229
- previous
230
- };
231
- if (!previous) return {
232
- descriptor,
233
- reason: "new-route",
234
- previous
235
- };
236
- if (previous.lastStatus === "error" || previous.htmlHash === null) return {
237
- descriptor,
238
- reason: "new-route",
239
- previous
240
- };
241
- if (descriptor.invalidationKeys.length === 0) return {
242
- descriptor,
243
- reason: "no-keys",
244
- previous
245
- };
246
- if (changedRoutes.includes(descriptor.path)) return {
247
- descriptor,
248
- reason: "changed-route",
249
- previous
250
- };
251
- if (descriptor.invalidationKeys.some((key) => changedKeys.includes(key))) return {
252
- descriptor,
253
- reason: "changed-key",
254
- previous
255
- };
256
- return {
257
- descriptor,
258
- reason: "unchanged",
259
- previous
260
- };
261
- }
262
- function collectRemovedRouteResults(manifest, currentRouteIds) {
263
- if (!manifest) return [];
264
- const results = [];
265
- for (let index = 0; index < manifest.routes.length; index += 1) {
266
- const entry = manifest.routes[index];
267
- if (currentRouteIds.has(entry.routeId)) continue;
268
- results.push({
269
- path: entry.path,
270
- filePath: entry.filePath,
271
- html: "",
272
- fileSize: 0,
273
- renderDuration: 0,
274
- resourceCount: 0,
275
- status: "removed",
276
- reason: "deleted",
277
- written: false
278
- });
279
- }
280
- return results;
281
- }
282
195
  //#endregion
283
196
  export { createStaticGen };
284
197