@aussieljk/forkbook 0.0.0-stage → 0.0.1-2

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 (598) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +77 -2
  3. package/assets/addon-vitest/templates/vitest.config.4.template.ts +36 -0
  4. package/assets/browser/favicon-wrapper.svg +46 -0
  5. package/assets/browser/favicon.svg +1 -0
  6. package/assets/browser/nunito-sans-bold-italic.woff2 +0 -0
  7. package/assets/browser/nunito-sans-bold.woff2 +0 -0
  8. package/assets/browser/nunito-sans-italic.woff2 +0 -0
  9. package/assets/browser/nunito-sans-regular.woff2 +0 -0
  10. package/assets/builder-vite/iframe.html +99 -0
  11. package/assets/create/rendererAssets/.eslintrc.json +16 -0
  12. package/assets/create/rendererAssets/common/Configure.mdx +388 -0
  13. package/assets/create/rendererAssets/common/assets/accessibility.png +0 -0
  14. package/assets/create/rendererAssets/common/assets/accessibility.svg +1 -0
  15. package/assets/create/rendererAssets/common/assets/addon-library.png +0 -0
  16. package/assets/create/rendererAssets/common/assets/assets.png +0 -0
  17. package/assets/create/rendererAssets/common/assets/avif-test-image.avif +0 -0
  18. package/assets/create/rendererAssets/common/assets/context.png +0 -0
  19. package/assets/create/rendererAssets/common/assets/discord.svg +1 -0
  20. package/assets/create/rendererAssets/common/assets/docs.png +0 -0
  21. package/assets/create/rendererAssets/common/assets/figma-plugin.png +0 -0
  22. package/assets/create/rendererAssets/common/assets/github.svg +1 -0
  23. package/assets/create/rendererAssets/common/assets/share.png +0 -0
  24. package/assets/create/rendererAssets/common/assets/styling.png +0 -0
  25. package/assets/create/rendererAssets/common/assets/testing.png +0 -0
  26. package/assets/create/rendererAssets/common/assets/theming.png +0 -0
  27. package/assets/create/rendererAssets/common/assets/tutorials.svg +1 -0
  28. package/assets/create/rendererAssets/common/assets/youtube.svg +1 -0
  29. package/assets/create/rendererAssets/common/button.css +30 -0
  30. package/assets/create/rendererAssets/common/header.css +32 -0
  31. package/assets/create/rendererAssets/common/page.css +68 -0
  32. package/assets/create/templates/react-native/index.expo.js +20 -0
  33. package/assets/create/templates/react-native/index.js +25 -0
  34. package/assets/docs/addon-backgrounds.gif +0 -0
  35. package/assets/docs/addon-controls-args-annotated.png +0 -0
  36. package/assets/docs/addon-controls-args-background-color.png +0 -0
  37. package/assets/docs/addon-controls-args-background-string.png +0 -0
  38. package/assets/docs/addon-controls-args-docs.png +0 -0
  39. package/assets/docs/addon-controls-args-logging.png +0 -0
  40. package/assets/docs/addon-controls-args-no-annotation.png +0 -0
  41. package/assets/docs/addon-controls-args-reflow-slider.png +0 -0
  42. package/assets/docs/addon-controls-args-reflow.png +0 -0
  43. package/assets/docs/addon-controls-args-template.png +0 -0
  44. package/assets/docs/addon-controls-expanded.png +0 -0
  45. package/assets/docs/addon-controls-hero.gif +0 -0
  46. package/assets/docs/addon-controls-install.png +0 -0
  47. package/assets/docs/hero.gif +0 -0
  48. package/assets/docs/highlight.png +0 -0
  49. package/assets/docs/message-reference.png +0 -0
  50. package/assets/docs/viewport.png +0 -0
  51. package/assets/react-native/metro-env.d.ts +69 -0
  52. package/assets/server/addon.tsconfig.json +6 -0
  53. package/assets/server/base-preview-body.html +116 -0
  54. package/assets/server/base-preview-head.html +494 -0
  55. package/assets/server/openBrowser.applescript +94 -0
  56. package/assets/server/template.ejs +87 -0
  57. package/dist/.build-stamp +0 -0
  58. package/dist/_browser-chunks/Color-PCLE2J36.js +1025 -0
  59. package/dist/_browser-chunks/DocsRenderer-JXND3PVQ.js +10 -0
  60. package/dist/_browser-chunks/WithTooltip-XSQFTBMB.js +1619 -0
  61. package/dist/_browser-chunks/chunk-2JDQ35OA.js +247 -0
  62. package/dist/_browser-chunks/chunk-2JT76G3E.js +104 -0
  63. package/dist/_browser-chunks/chunk-2MEPHP35.js +17 -0
  64. package/dist/_browser-chunks/chunk-2SCZVUKW.js +24 -0
  65. package/dist/_browser-chunks/chunk-46Z7NGAS.js +41 -0
  66. package/dist/_browser-chunks/chunk-4ATQOMJB.js +30 -0
  67. package/dist/_browser-chunks/chunk-52CW7FF7.js +49 -0
  68. package/dist/_browser-chunks/chunk-5I7E4GMO.js +62 -0
  69. package/dist/_browser-chunks/chunk-5P4QA6MY.js +20 -0
  70. package/dist/_browser-chunks/chunk-5WMXI5GW.js +816 -0
  71. package/dist/_browser-chunks/chunk-66OCZ54D.js +2604 -0
  72. package/dist/_browser-chunks/chunk-6SXYJQST.js +253 -0
  73. package/dist/_browser-chunks/chunk-AYRF6EJB.js +36 -0
  74. package/dist/_browser-chunks/chunk-B4AUNNU4.js +156 -0
  75. package/dist/_browser-chunks/chunk-BCP2FFXF.js +369 -0
  76. package/dist/_browser-chunks/chunk-C3D4HEFY.js +3396 -0
  77. package/dist/_browser-chunks/chunk-CFAOWGL7.js +43 -0
  78. package/dist/_browser-chunks/chunk-CFWWCCZV.js +223 -0
  79. package/dist/_browser-chunks/chunk-CNED6RYH.js +18 -0
  80. package/dist/_browser-chunks/chunk-DRQKCCAG.js +136 -0
  81. package/dist/_browser-chunks/chunk-ECQ75MKQ.js +0 -0
  82. package/dist/_browser-chunks/chunk-EIYAKDGP.js +302 -0
  83. package/dist/_browser-chunks/chunk-ENKK74N4.js +12 -0
  84. package/dist/_browser-chunks/chunk-FENFYI4X.js +698 -0
  85. package/dist/_browser-chunks/chunk-FJGWSWL5.js +155 -0
  86. package/dist/_browser-chunks/chunk-FVSEPWWX.js +2810 -0
  87. package/dist/_browser-chunks/chunk-GNZY6LOW.js +10116 -0
  88. package/dist/_browser-chunks/chunk-IE4ULIOQ.js +14 -0
  89. package/dist/_browser-chunks/chunk-IMSF75WX.js +42 -0
  90. package/dist/_browser-chunks/chunk-IWQGIXJS.js +14 -0
  91. package/dist/_browser-chunks/chunk-JMC7AIKT.js +108 -0
  92. package/dist/_browser-chunks/chunk-JPUOQJUI.js +8 -0
  93. package/dist/_browser-chunks/chunk-JSBKXXXF.js +218 -0
  94. package/dist/_browser-chunks/chunk-JWSKPMPC.js +73 -0
  95. package/dist/_browser-chunks/chunk-JYVGARBG.js +2943 -0
  96. package/dist/_browser-chunks/chunk-KBK7GKBN.js +35 -0
  97. package/dist/_browser-chunks/chunk-KQ7CRO2B.js +108 -0
  98. package/dist/_browser-chunks/chunk-LAWXTSXK.js +18 -0
  99. package/dist/_browser-chunks/chunk-LE2LTDW3.js +14 -0
  100. package/dist/_browser-chunks/chunk-LEVSOOLQ.js +48 -0
  101. package/dist/_browser-chunks/chunk-LV5PIKV4.js +93 -0
  102. package/dist/_browser-chunks/chunk-MDOL7KML.js +56 -0
  103. package/dist/_browser-chunks/chunk-MFMDGWP6.js +34 -0
  104. package/dist/_browser-chunks/chunk-MLBOOZL4.js +67 -0
  105. package/dist/_browser-chunks/chunk-NQUD4B7H.js +91 -0
  106. package/dist/_browser-chunks/chunk-NULBREPZ.js +43 -0
  107. package/dist/_browser-chunks/chunk-NZMVUW5T.js +7 -0
  108. package/dist/_browser-chunks/chunk-OD6IBQCC.js +117 -0
  109. package/dist/_browser-chunks/chunk-ODVJ3XZJ.js +157 -0
  110. package/dist/_browser-chunks/chunk-OQWFVDIN.js +386 -0
  111. package/dist/_browser-chunks/chunk-OVH3USFB.js +20 -0
  112. package/dist/_browser-chunks/chunk-OZZPSYQR.js +14 -0
  113. package/dist/_browser-chunks/chunk-P6PK47TQ.js +1970 -0
  114. package/dist/_browser-chunks/chunk-PFRNG3UC.js +22 -0
  115. package/dist/_browser-chunks/chunk-PJSMZ27A.js +13 -0
  116. package/dist/_browser-chunks/chunk-QC4HFS6K.js +41 -0
  117. package/dist/_browser-chunks/chunk-QDXFODK4.js +274 -0
  118. package/dist/_browser-chunks/chunk-RBCHTR7C.js +28 -0
  119. package/dist/_browser-chunks/chunk-RMYQ3QN3.js +294 -0
  120. package/dist/_browser-chunks/chunk-RNTIV5CE.js +411 -0
  121. package/dist/_browser-chunks/chunk-RSPAQWVJ.js +80 -0
  122. package/dist/_browser-chunks/chunk-S6TK43XQ.js +836 -0
  123. package/dist/_browser-chunks/chunk-SC67XZST.js +18 -0
  124. package/dist/_browser-chunks/chunk-SFVA4SBT.js +683 -0
  125. package/dist/_browser-chunks/chunk-SZYFOQMT.js +25 -0
  126. package/dist/_browser-chunks/chunk-TBJCJXK4.js +18 -0
  127. package/dist/_browser-chunks/chunk-UMDO3KB7.js +205 -0
  128. package/dist/_browser-chunks/chunk-V2G23FV7.js +11 -0
  129. package/dist/_browser-chunks/chunk-V5M7Q4HT.js +88 -0
  130. package/dist/_browser-chunks/chunk-W5M6NPS3.js +198 -0
  131. package/dist/_browser-chunks/chunk-W7NE2JXW.js +84 -0
  132. package/dist/_browser-chunks/chunk-WGVAAM43.js +1110 -0
  133. package/dist/_browser-chunks/chunk-WHQRNHXD.js +8 -0
  134. package/dist/_browser-chunks/chunk-WOSOWBCA.js +12 -0
  135. package/dist/_browser-chunks/chunk-WZ7KR7SH.js +85 -0
  136. package/dist/_browser-chunks/chunk-YEKO6BHJ.js +20 -0
  137. package/dist/_browser-chunks/chunk-YI2IBIQS.js +7 -0
  138. package/dist/_browser-chunks/chunk-YTIT5NA5.js +1966 -0
  139. package/dist/_browser-chunks/chunk-ZEHRUHTA.js +6 -0
  140. package/dist/_browser-chunks/chunk-ZTQ5MEJ5.js +203 -0
  141. package/dist/_browser-chunks/matchers-YLBYSYAF.js +1263 -0
  142. package/dist/_browser-chunks/react-dom-client-4OHE3QDV.js +9 -0
  143. package/dist/_node-chunks/attached-runtime-I2G4WLJI.js +282 -0
  144. package/dist/_node-chunks/builder-manager-VVHYQXCH.js +1661 -0
  145. package/dist/_node-chunks/chunk-2BUPBPFB.js +82 -0
  146. package/dist/_node-chunks/chunk-2DSIDINN.js +884 -0
  147. package/dist/_node-chunks/chunk-2RU7BKEW.js +23 -0
  148. package/dist/_node-chunks/chunk-34EO2PUT.js +26 -0
  149. package/dist/_node-chunks/chunk-3BKIBF5S.js +3008 -0
  150. package/dist/_node-chunks/chunk-3SDUYDXH.js +98 -0
  151. package/dist/_node-chunks/chunk-3T4ZSNPH.js +662 -0
  152. package/dist/_node-chunks/chunk-3W7F2B7P.js +79 -0
  153. package/dist/_node-chunks/chunk-3Y6AAY5H.js +80 -0
  154. package/dist/_node-chunks/chunk-42UWAGPW.js +1193 -0
  155. package/dist/_node-chunks/chunk-4FEANXWU.js +1986 -0
  156. package/dist/_node-chunks/chunk-5DQFEZEA.js +40 -0
  157. package/dist/_node-chunks/chunk-6KEHV53S.js +1218 -0
  158. package/dist/_node-chunks/chunk-6LMWA44H.js +3837 -0
  159. package/dist/_node-chunks/chunk-6YKBRP72.js +20 -0
  160. package/dist/_node-chunks/chunk-7GUDZVRQ.js +110 -0
  161. package/dist/_node-chunks/chunk-B5J7Q7J4.js +21 -0
  162. package/dist/_node-chunks/chunk-BS2WMZMR.js +90 -0
  163. package/dist/_node-chunks/chunk-BSKFCWQD.js +47 -0
  164. package/dist/_node-chunks/chunk-BXVYT7SB.js +36 -0
  165. package/dist/_node-chunks/chunk-CCOTJ4FC.js +688 -0
  166. package/dist/_node-chunks/chunk-CQKMLL5J.js +33 -0
  167. package/dist/_node-chunks/chunk-CXRU72ET.js +63 -0
  168. package/dist/_node-chunks/chunk-DC7DKR5K.js +20 -0
  169. package/dist/_node-chunks/chunk-DHJFTB4K.js +25 -0
  170. package/dist/_node-chunks/chunk-DUR55DJJ.js +1094 -0
  171. package/dist/_node-chunks/chunk-E6YESGLX.js +7032 -0
  172. package/dist/_node-chunks/chunk-EHUNOMDD.js +52345 -0
  173. package/dist/_node-chunks/chunk-ET7PWEO7.js +298 -0
  174. package/dist/_node-chunks/chunk-EWOX7HPJ.js +20 -0
  175. package/dist/_node-chunks/chunk-F3ZFKBHY.js +844 -0
  176. package/dist/_node-chunks/chunk-FK7KGTV7.js +26 -0
  177. package/dist/_node-chunks/chunk-FPZB5OI7.js +35 -0
  178. package/dist/_node-chunks/chunk-FW2HQUBQ.js +25 -0
  179. package/dist/_node-chunks/chunk-FY6UXW6G.js +11 -0
  180. package/dist/_node-chunks/chunk-GEFQN7K3.js +499 -0
  181. package/dist/_node-chunks/chunk-GRBLDJCR.js +989 -0
  182. package/dist/_node-chunks/chunk-H32DYT6P.js +87 -0
  183. package/dist/_node-chunks/chunk-H4PPVMP5.js +23 -0
  184. package/dist/_node-chunks/chunk-HMP2G23X.js +25 -0
  185. package/dist/_node-chunks/chunk-ICPBH3E7.js +59 -0
  186. package/dist/_node-chunks/chunk-IDEL2J23.js +23 -0
  187. package/dist/_node-chunks/chunk-IK4FGZLR.js +223 -0
  188. package/dist/_node-chunks/chunk-JDXXX6LB.js +68 -0
  189. package/dist/_node-chunks/chunk-JGGFYIG5.js +5468 -0
  190. package/dist/_node-chunks/chunk-JPAZLYJ3.js +29 -0
  191. package/dist/_node-chunks/chunk-JZOO7GGC.js +48 -0
  192. package/dist/_node-chunks/chunk-KISMCCP5.js +4744 -0
  193. package/dist/_node-chunks/chunk-LCK4ZQKG.js +115 -0
  194. package/dist/_node-chunks/chunk-LO56I6E2.js +132 -0
  195. package/dist/_node-chunks/chunk-LXBD4UL4.js +104 -0
  196. package/dist/_node-chunks/chunk-MA24JW6C.js +7162 -0
  197. package/dist/_node-chunks/chunk-MK2KYAI6.js +5414 -0
  198. package/dist/_node-chunks/chunk-NDWKJ4CL.js +2102 -0
  199. package/dist/_node-chunks/chunk-NIBT7XDZ.js +168 -0
  200. package/dist/_node-chunks/chunk-NPO45IMH.js +33 -0
  201. package/dist/_node-chunks/chunk-OISX4AGQ.js +414 -0
  202. package/dist/_node-chunks/chunk-OUW6R7C3.js +26 -0
  203. package/dist/_node-chunks/chunk-OWNFOGTA.js +194 -0
  204. package/dist/_node-chunks/chunk-Q7QNUM5H.js +49 -0
  205. package/dist/_node-chunks/chunk-QEQLR22M.js +1877 -0
  206. package/dist/_node-chunks/chunk-RRN2V55B.js +45 -0
  207. package/dist/_node-chunks/chunk-RWFIFGO5.js +144 -0
  208. package/dist/_node-chunks/chunk-RXNJDRGO.js +209 -0
  209. package/dist/_node-chunks/chunk-RZMQ7S3L.js +92 -0
  210. package/dist/_node-chunks/chunk-SIEXZVCR.js +51 -0
  211. package/dist/_node-chunks/chunk-SLWQNBGO.js +2034 -0
  212. package/dist/_node-chunks/chunk-SVTBJ7Q3.js +35 -0
  213. package/dist/_node-chunks/chunk-SWO5CDYU.js +32 -0
  214. package/dist/_node-chunks/chunk-TD5PLLQG.js +121 -0
  215. package/dist/_node-chunks/chunk-TEQGGSP7.js +33 -0
  216. package/dist/_node-chunks/chunk-TFCEV2XX.js +155 -0
  217. package/dist/_node-chunks/chunk-TFEWM4E3.js +1073 -0
  218. package/dist/_node-chunks/chunk-TINO74G5.js +21 -0
  219. package/dist/_node-chunks/chunk-TNJIXE37.js +40 -0
  220. package/dist/_node-chunks/chunk-TWI4GVH7.js +19 -0
  221. package/dist/_node-chunks/chunk-U5W53R2J.js +1011 -0
  222. package/dist/_node-chunks/chunk-UBOGWBEA.js +148 -0
  223. package/dist/_node-chunks/chunk-UMUBKI6E.js +3787 -0
  224. package/dist/_node-chunks/chunk-UVINFLGY.js +56 -0
  225. package/dist/_node-chunks/chunk-V2DY426G.js +1133 -0
  226. package/dist/_node-chunks/chunk-VLOXOXXE.js +45 -0
  227. package/dist/_node-chunks/chunk-W4OMYLSP.js +1297 -0
  228. package/dist/_node-chunks/chunk-WAXVSN3O.js +18 -0
  229. package/dist/_node-chunks/chunk-WCULS5TP.js +3767 -0
  230. package/dist/_node-chunks/chunk-WNZ2UMFM.js +153 -0
  231. package/dist/_node-chunks/chunk-WZJF2OUH.js +74 -0
  232. package/dist/_node-chunks/chunk-XGIUVY52.js +611 -0
  233. package/dist/_node-chunks/chunk-XHL2LVBM.js +415 -0
  234. package/dist/_node-chunks/chunk-XI6B6P47.js +59 -0
  235. package/dist/_node-chunks/chunk-XLP2ZKXV.js +60 -0
  236. package/dist/_node-chunks/chunk-YCMRA6RX.js +95 -0
  237. package/dist/_node-chunks/chunk-YZFWBA5L.js +2700 -0
  238. package/dist/_node-chunks/chunk-ZMAMXQKP.js +4731 -0
  239. package/dist/_node-chunks/chunk-ZWBCQ4MJ.js +110 -0
  240. package/dist/_node-chunks/dist-VTSTZKUB.js +16 -0
  241. package/dist/_node-chunks/globby-OEMHPBFY.js +2884 -0
  242. package/dist/_node-chunks/jiti-NFX6UU74.js +3848 -0
  243. package/dist/_node-chunks/lib-MDVARD7W.js +366 -0
  244. package/dist/_node-chunks/local-runtime-NTKIN2QT.js +78 -0
  245. package/dist/_node-chunks/mdx-plugin-MWISDWDQ.js +5680 -0
  246. package/dist/_node-chunks/monorepo-LIRFMQXE.js +121 -0
  247. package/dist/_node-chunks/monorepo-optimized-tests-relaxed-limits-no-story-deletion-CPDW4XTW.js +121 -0
  248. package/dist/_node-chunks/optimized-tests-HDOLR3EY.js +22 -0
  249. package/dist/_node-chunks/pattern-copy-play-EKHIUAU2.js +807 -0
  250. package/dist/_node-chunks/postinstallAddon-GYGMWDL6.js +20 -0
  251. package/dist/_node-chunks/rehype-external-links-BUN3HUCO.js +121 -0
  252. package/dist/_node-chunks/rehype-slug-PLADX223.js +52 -0
  253. package/dist/_node-chunks/relaxed-limits-GBHW6KIQ.js +117 -0
  254. package/dist/_node-chunks/run-KDTIZNSY.js +103 -0
  255. package/dist/_node-chunks/run-NL66VTNR.js +4532 -0
  256. package/dist/_node-chunks/session-id-KIIVOQLR.js +23 -0
  257. package/dist/_node-chunks/setup-MGC7BQO6.js +278 -0
  258. package/dist/_node-chunks/vitest.config.4.template-JH2THZFG.js +54 -0
  259. package/dist/actions/decorator.d.ts +7 -0
  260. package/dist/actions/decorator.js +43 -0
  261. package/dist/actions/index.d.ts +67 -0
  262. package/dist/actions/index.js +28 -0
  263. package/dist/addon-a11y/index.d.ts +52 -0
  264. package/dist/addon-a11y/index.js +16 -0
  265. package/dist/addon-a11y/manager.js +1429 -0
  266. package/dist/addon-a11y/preset.js +18 -0
  267. package/dist/addon-a11y/preview.d.ts +17 -0
  268. package/dist/addon-a11y/preview.js +15 -0
  269. package/dist/addon-docs/blocks.d.ts +1061 -0
  270. package/dist/addon-docs/blocks.js +2461 -0
  271. package/dist/addon-docs/index.d.ts +236 -0
  272. package/dist/addon-docs/index.js +16 -0
  273. package/dist/addon-docs/manager.js +90 -0
  274. package/dist/addon-docs/mdx-react-shim.d.ts +1 -0
  275. package/dist/addon-docs/mdx-react-shim.js +2 -0
  276. package/dist/addon-docs/preset.js +378 -0
  277. package/dist/addon-docs/preview.d.ts +4 -0
  278. package/dist/addon-docs/preview.js +7 -0
  279. package/dist/addon-links/index.d.ts +22 -0
  280. package/dist/addon-links/index.js +20 -0
  281. package/dist/addon-links/manager.js +14 -0
  282. package/dist/addon-links/preview.d.ts +6 -0
  283. package/dist/addon-links/preview.js +9 -0
  284. package/dist/addon-links/react/index.d.ts +25 -0
  285. package/dist/addon-links/react/index.js +52 -0
  286. package/dist/addon-mcp/preset.js +1345 -0
  287. package/dist/addon-mcp/preview.js +29 -0
  288. package/dist/addon-mcp/tools/preview-stories/preview-stories-app-script.js +1153 -0
  289. package/dist/addon-ondevice-actions/index.d.ts +4 -0
  290. package/dist/addon-ondevice-actions/index.js +7 -0
  291. package/dist/addon-ondevice-actions/register.js +7 -0
  292. package/dist/addon-ondevice-controls/index.d.ts +6 -0
  293. package/dist/addon-ondevice-controls/index.js +14 -0
  294. package/dist/addon-ondevice-controls/register.js +10 -0
  295. package/dist/addon-pseudo-states/index.d.ts +35 -0
  296. package/dist/addon-pseudo-states/index.js +15 -0
  297. package/dist/addon-pseudo-states/manager.js +53 -0
  298. package/dist/addon-pseudo-states/preview.d.ts +9 -0
  299. package/dist/addon-pseudo-states/preview.js +10 -0
  300. package/dist/addon-themes/index.d.ts +80 -0
  301. package/dist/addon-themes/index.js +104 -0
  302. package/dist/addon-themes/manager.js +97 -0
  303. package/dist/addon-themes/postinstall.js +33 -0
  304. package/dist/addon-themes/preview.d.ts +6 -0
  305. package/dist/addon-themes/preview.js +8 -0
  306. package/dist/addon-vitest/constants.d.ts +132 -0
  307. package/dist/addon-vitest/constants.js +66 -0
  308. package/dist/addon-vitest/index.d.ts +4 -0
  309. package/dist/addon-vitest/index.js +8 -0
  310. package/dist/addon-vitest/manager.js +665 -0
  311. package/dist/addon-vitest/node/coverage-reporter.js +1284 -0
  312. package/dist/addon-vitest/node/vitest.js +674 -0
  313. package/dist/addon-vitest/postinstall.js +24 -0
  314. package/dist/addon-vitest/preset.js +943 -0
  315. package/dist/addon-vitest/vitest-plugin/global-setup.js +162 -0
  316. package/dist/addon-vitest/vitest-plugin/index.d.ts +56 -0
  317. package/dist/addon-vitest/vitest-plugin/index.js +463 -0
  318. package/dist/addon-vitest/vitest-plugin/setup-file-with-project-annotations.js +9 -0
  319. package/dist/addon-vitest/vitest-plugin/setup-file.browser.4.js +20 -0
  320. package/dist/addon-vitest/vitest-plugin/setup-file.js +50 -0
  321. package/dist/addon-vitest/vitest-plugin/test-utils.js +128 -0
  322. package/dist/babel/index.d.ts +2301 -0
  323. package/dist/babel/index.js +61 -0
  324. package/dist/backgrounds/index.d.ts +6 -0
  325. package/dist/backgrounds/index.js +15 -0
  326. package/dist/bin/core.js +1705 -0
  327. package/dist/bin/dispatcher.js +43 -0
  328. package/dist/bin/loader.js +115 -0
  329. package/dist/builder-vite/index.d.ts +50 -0
  330. package/dist/builder-vite/index.js +245 -0
  331. package/dist/builder-vite/preset.js +27 -0
  332. package/dist/channels/index.d.ts +2 -0
  333. package/dist/channels/index.js +352 -0
  334. package/dist/chunk-B2t1tGRh.d.ts +284 -0
  335. package/dist/chunk-BCoJQE-f.d.ts +602 -0
  336. package/dist/chunk-BGkgZp-H.d.ts +214 -0
  337. package/dist/chunk-BTVrWliM.d.ts +115 -0
  338. package/dist/chunk-BXLImeLy.d.ts +197 -0
  339. package/dist/chunk-BfBzpgct.d.ts +916 -0
  340. package/dist/chunk-Bpi2tz-6.d.ts +511 -0
  341. package/dist/chunk-BxWufQms.d.ts +142 -0
  342. package/dist/chunk-C-bavsOH.d.ts +34 -0
  343. package/dist/chunk-C2p5r3Vt.d.ts +13001 -0
  344. package/dist/chunk-C6PAgHLT.d.ts +12 -0
  345. package/dist/chunk-CMeX4WRe.d.ts +179 -0
  346. package/dist/chunk-Ct9xd6IM.d.ts +65 -0
  347. package/dist/chunk-CwQKNaYL.d.ts +15 -0
  348. package/dist/chunk-D7DwV41f.d.ts +3622 -0
  349. package/dist/chunk-DEVFdzxR.d.ts +253 -0
  350. package/dist/chunk-DaVb55-_.d.ts +124 -0
  351. package/dist/chunk-DgRNfmj_.d.ts +40 -0
  352. package/dist/chunk-DoIGKY33.d.ts +666 -0
  353. package/dist/chunk-Dr3R0DXk.d.ts +40 -0
  354. package/dist/chunk-DtHByo32.d.ts +55 -0
  355. package/dist/chunk-Dw1z1Pki.d.ts +933 -0
  356. package/dist/chunk-DzuY9DXg.d.ts +79 -0
  357. package/dist/chunk-FjDVzZTj.d.ts +62 -0
  358. package/dist/chunk-Kt1tW-D1.d.ts +59 -0
  359. package/dist/chunk-XP8oHzpE.d.ts +6 -0
  360. package/dist/chunk-gCX81nOr.d.ts +61 -0
  361. package/dist/chunk-pcFXCimE.d.ts +741 -0
  362. package/dist/cli/index.d.ts +1174 -0
  363. package/dist/cli/index.js +5613 -0
  364. package/dist/cli/skills/content/index.d.ts +72 -0
  365. package/dist/cli/skills/content/index.js +335 -0
  366. package/dist/cli/tools/sdk/child-host.d.ts +61 -0
  367. package/dist/cli/tools/sdk/child-host.js +131 -0
  368. package/dist/cli/tools/sdk/index.d.ts +137 -0
  369. package/dist/cli/tools/sdk/index.js +44 -0
  370. package/dist/cli-commands/bin/index.js +25 -0
  371. package/dist/client-logger/index.d.ts +31 -0
  372. package/dist/client-logger/index.js +63 -0
  373. package/dist/common/index.d.ts +1103 -0
  374. package/dist/common/index.js +364 -0
  375. package/dist/components/index.d.ts +1924 -0
  376. package/dist/components/index.js +13759 -0
  377. package/dist/core-events/index.d.ts +155 -0
  378. package/dist/core-events/index.js +136 -0
  379. package/dist/core-server/index.d.ts +2200 -0
  380. package/dist/core-server/index.js +4165 -0
  381. package/dist/core-server/presets/common-manager.css +170 -0
  382. package/dist/core-server/presets/common-manager.js +4168 -0
  383. package/dist/core-server/presets/common-override-preset.js +81 -0
  384. package/dist/core-server/presets/common-preset.js +2187 -0
  385. package/dist/create/bin/index.js +23 -0
  386. package/dist/create/index.js +25 -0
  387. package/dist/csf/csf-utils.d.ts +2 -0
  388. package/dist/csf/csf-utils.js +19 -0
  389. package/dist/csf/index.d.ts +7 -0
  390. package/dist/csf/index.js +2142 -0
  391. package/dist/csf-tools/index.d.ts +1157 -0
  392. package/dist/csf-tools/index.js +736 -0
  393. package/dist/docs-tools/index.d.ts +197 -0
  394. package/dist/docs-tools/index.js +61 -0
  395. package/dist/highlight/index.d.ts +10 -0
  396. package/dist/highlight/index.js +15 -0
  397. package/dist/instrumenter/index.d.ts +113 -0
  398. package/dist/instrumenter/index.js +2595 -0
  399. package/dist/manager/_manager-chunks/globals-runtime-8HkdnU2F.js +6144 -0
  400. package/dist/manager/_manager-chunks/globals-runtime-BIKBO23x.js +3389 -0
  401. package/dist/manager/_manager-chunks/globals-runtime-BdGkqNEt.js +45 -0
  402. package/dist/manager/_manager-chunks/globals-runtime-BmphMP6z.js +1190 -0
  403. package/dist/manager/_manager-chunks/globals-runtime-BvM-NY2g.js +13307 -0
  404. package/dist/manager/_manager-chunks/globals-runtime-C02fvgcd.js +9243 -0
  405. package/dist/manager/_manager-chunks/globals-runtime-DkAPXCvc.js +9766 -0
  406. package/dist/manager/_manager-chunks/globals-runtime-DpNYC-qi.js +5564 -0
  407. package/dist/manager/_manager-chunks/globals-runtime-DtcOnYHR.js +5357 -0
  408. package/dist/manager/_manager-chunks/globals-runtime-DwFqpqya.js +722 -0
  409. package/dist/manager/_manager-chunks/globals-runtime-FC201B7s.js +6837 -0
  410. package/dist/manager/_manager-chunks/globals-runtime-Rp_zjGnh.js +121 -0
  411. package/dist/manager/_manager-chunks/globals-runtime-ZHrQFa3-.js +10463 -0
  412. package/dist/manager/_manager-chunks/globals-runtime-eLPTIfX-.js +7759 -0
  413. package/dist/manager/_manager-chunks/globals-runtime-qKvCZHFY.js +4468 -0
  414. package/dist/manager/_manager-chunks/globals-runtime-sGpNlIJ1.js +5102 -0
  415. package/dist/manager/_manager-chunks/runtime-B1L9TH89.js +452 -0
  416. package/dist/manager/_manager-chunks/runtime-BK0zF7FN.js +22 -0
  417. package/dist/manager/_manager-chunks/runtime-BhkT2CXl.js +1408 -0
  418. package/dist/manager/_manager-chunks/runtime-Bly6WJLN.js +998 -0
  419. package/dist/manager/_manager-chunks/runtime-D0UOWx0U.js +979 -0
  420. package/dist/manager/_manager-chunks/runtime-D94Vq3oR.js +4014 -0
  421. package/dist/manager/_manager-chunks/runtime-DMig8mT7.js +77 -0
  422. package/dist/manager/_manager-chunks/runtime-EITvVbkG.js +46 -0
  423. package/dist/manager/_manager-chunks/runtime-cp5pazY2.js +8936 -0
  424. package/dist/manager/_manager-chunks/runtime-eDVAYF7N.js +6860 -0
  425. package/dist/manager/_manager-chunks/runtime-i-PDAHLR.js +2493 -0
  426. package/dist/manager/globals-runtime.js +2 -0
  427. package/dist/manager/globals.d.ts +23 -0
  428. package/dist/manager/globals.js +26 -0
  429. package/dist/manager/manager-stores.d.ts +2 -0
  430. package/dist/manager/manager-stores.js +19 -0
  431. package/dist/manager/runtime.js +2 -0
  432. package/dist/manager-api/index.d.ts +2 -0
  433. package/dist/manager-api/index.js +3257 -0
  434. package/dist/manager-errors.d.ts +89 -0
  435. package/dist/manager-errors.js +20 -0
  436. package/dist/mcp/index.d.ts +270 -0
  437. package/dist/mcp/index.js +305 -0
  438. package/dist/mocking-utils/index.d.ts +689 -0
  439. package/dist/mocking-utils/index.js +521 -0
  440. package/dist/mocking-utils/mocker-runtime.js +1254 -0
  441. package/dist/nextjs-vite/config/preview.js +3 -0
  442. package/dist/nextjs-vite/export-mocks/cache/index.d.ts +25 -0
  443. package/dist/nextjs-vite/export-mocks/cache/index.js +29 -0
  444. package/dist/nextjs-vite/export-mocks/headers/index.d.ts +37 -0
  445. package/dist/nextjs-vite/export-mocks/headers/index.js +54 -0
  446. package/dist/nextjs-vite/export-mocks/link/index.d.ts +9 -0
  447. package/dist/nextjs-vite/export-mocks/link/index.js +59 -0
  448. package/dist/nextjs-vite/export-mocks/navigation/index.d.ts +28 -0
  449. package/dist/nextjs-vite/export-mocks/navigation/index.js +58 -0
  450. package/dist/nextjs-vite/export-mocks/router/index.d.ts +34 -0
  451. package/dist/nextjs-vite/export-mocks/router/index.js +63 -0
  452. package/dist/nextjs-vite/index.d.ts +92 -0
  453. package/dist/nextjs-vite/index.js +58 -0
  454. package/dist/nextjs-vite/node/index.d.ts +6 -0
  455. package/dist/nextjs-vite/node/index.js +20 -0
  456. package/dist/nextjs-vite/preset.js +648 -0
  457. package/dist/nextjs-vite/preview.d.ts +20 -0
  458. package/dist/nextjs-vite/preview.js +11 -0
  459. package/dist/nextjs-vite/vite-plugin/index.d.ts +6 -0
  460. package/dist/nextjs-vite/vite-plugin/index.js +19 -0
  461. package/dist/nextjs-vite-plugin/index.d.ts +28 -0
  462. package/dist/nextjs-vite-plugin/index.js +923 -0
  463. package/dist/nextjs-vite-plugin/mocks/storybook.global.js +29 -0
  464. package/dist/nextjs-vite-plugin/plugins/next-image/alias/image-context.js +8 -0
  465. package/dist/nextjs-vite-plugin/plugins/next-image/alias/image-default-loader.js +23 -0
  466. package/dist/nextjs-vite-plugin/plugins/next-image/alias/next-image.js +28 -0
  467. package/dist/nextjs-vite-plugin/plugins/next-image/alias/next-legacy-image.js +17 -0
  468. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/cache/index.js +29 -0
  469. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/dynamic/index.js +40 -0
  470. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/headers/index.js +55 -0
  471. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/link/index.js +63 -0
  472. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/navigation/index.js +71 -0
  473. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/router/index.js +74 -0
  474. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/rsc/server-only.js +7 -0
  475. package/dist/nextjs-vite-plugin/plugins/next-mocks/alias/safe-stable-stringify/index.js +13 -0
  476. package/dist/nextjs-vite-plugin/plugins/next-mocks/compatibility/draft-mode.compat.js +7 -0
  477. package/dist/node-logger/index.d.ts +495 -0
  478. package/dist/node-logger/index.js +42 -0
  479. package/dist/oxc-parser/index.d.ts +99 -0
  480. package/dist/oxc-parser/index.js +275 -0
  481. package/dist/oxc-parser/worker.js +37 -0
  482. package/dist/preview/_chunks/runtime-BIzIdDbb.js +10263 -0
  483. package/dist/preview/_chunks/runtime-BTPtVY1p.js +1102 -0
  484. package/dist/preview/_chunks/runtime-BZSnICCg.js +8484 -0
  485. package/dist/preview/_chunks/runtime-BdGkqNEt.js +45 -0
  486. package/dist/preview/_chunks/runtime-BwSxqk3t.js +63 -0
  487. package/dist/preview/_chunks/runtime-CqHxptiK.js +6143 -0
  488. package/dist/preview/_chunks/runtime-CsV4HZPV.js +5064 -0
  489. package/dist/preview/_chunks/runtime-CxZK_Ln3.js +284 -0
  490. package/dist/preview/_chunks/runtime-DZToXfvO.js +9767 -0
  491. package/dist/preview/_chunks/runtime-DrTjJkPY.js +3872 -0
  492. package/dist/preview/_chunks/runtime-DuHdxMFA.js +387 -0
  493. package/dist/preview/_chunks/runtime-iXdo8CR8.js +3635 -0
  494. package/dist/preview/_chunks/runtime-q3wt0e_N.js +6838 -0
  495. package/dist/preview/globals.d.ts +16 -0
  496. package/dist/preview/globals.js +20 -0
  497. package/dist/preview/runtime.js +6 -0
  498. package/dist/preview-api/index.d.ts +4 -0
  499. package/dist/preview-api/index.js +147 -0
  500. package/dist/preview-errors.d.ts +186 -0
  501. package/dist/preview-errors.js +56 -0
  502. package/dist/react/docgen/docgen-worker.js +38 -0
  503. package/dist/react/entry-preview-argtypes.js +9 -0
  504. package/dist/react/entry-preview-docs.js +12 -0
  505. package/dist/react/entry-preview-rsc.js +11 -0
  506. package/dist/react/entry-preview.js +21 -0
  507. package/dist/react/index.d.ts +119 -0
  508. package/dist/react/index.js +26 -0
  509. package/dist/react/preset.js +301 -0
  510. package/dist/react/preview.d.ts +2 -0
  511. package/dist/react/preview.js +11 -0
  512. package/dist/react-native/bin/get-stories.js +34 -0
  513. package/dist/react-native/index.d.ts +160 -0
  514. package/dist/react-native/index.js +1547 -0
  515. package/dist/react-native/metro/node.d.ts +83 -0
  516. package/dist/react-native/metro/node.js +21 -0
  517. package/dist/react-native/metro/withStorybook.d.ts +2269 -0
  518. package/dist/react-native/metro/withStorybook.js +138 -0
  519. package/dist/react-native/preview.d.ts +6 -0
  520. package/dist/react-native/preview.js +20 -0
  521. package/dist/react-native/scripts/generate.d.ts +22 -0
  522. package/dist/react-native/scripts/generate.js +18 -0
  523. package/dist/react-native/stub.js +20 -0
  524. package/dist/react-native-theming/index.d.ts +175 -0
  525. package/dist/react-native-theming/index.js +2591 -0
  526. package/dist/react-native-ui/index.d.ts +130 -0
  527. package/dist/react-native-ui/index.js +1448 -0
  528. package/dist/react-native-ui-common/index.d.ts +230 -0
  529. package/dist/react-native-ui-common/index.js +514 -0
  530. package/dist/react-vite/index.d.ts +4 -0
  531. package/dist/react-vite/index.js +8 -0
  532. package/dist/react-vite/node/index.d.ts +6 -0
  533. package/dist/react-vite/node/index.js +20 -0
  534. package/dist/react-vite/preset.d.ts +6 -0
  535. package/dist/react-vite/preset.js +21 -0
  536. package/dist/router/index.d.ts +141 -0
  537. package/dist/router/index.js +1217 -0
  538. package/dist/server-errors.d.ts +2 -0
  539. package/dist/server-errors.js +149 -0
  540. package/dist/shared/open-service/index.d.ts +3 -0
  541. package/dist/shared/open-service/index.js +115 -0
  542. package/dist/shared/open-service/services/docgen/worker/docgen-worker.js +75 -0
  543. package/dist/shared/open-service/toolsets/docs/public.d.ts +1296 -0
  544. package/dist/shared/open-service/toolsets/docs/public.js +991 -0
  545. package/dist/telemetry/index.d.ts +314 -0
  546. package/dist/telemetry/index.js +103 -0
  547. package/dist/test/index.d.ts +158 -0
  548. package/dist/test/index.js +19421 -0
  549. package/dist/theming/create.d.ts +15 -0
  550. package/dist/theming/create.js +11 -0
  551. package/dist/theming/index.d.ts +54 -0
  552. package/dist/theming/index.js +511 -0
  553. package/dist/types/index.d.ts +2 -0
  554. package/dist/types/index.js +48 -0
  555. package/dist/viewport/index.d.ts +275 -0
  556. package/dist/viewport/index.js +21 -0
  557. package/package.json +1054 -4
  558. package/template/nextjs-vite/cli/.eslintrc.json +6 -0
  559. package/template/nextjs-vite/cli/js/Button.jsx +42 -0
  560. package/template/nextjs-vite/cli/js/Button.stories.js +49 -0
  561. package/template/nextjs-vite/cli/js/Configure.mdx +470 -0
  562. package/template/nextjs-vite/cli/js/Header.jsx +58 -0
  563. package/template/nextjs-vite/cli/js/Header.stories.js +30 -0
  564. package/template/nextjs-vite/cli/js/Page.jsx +68 -0
  565. package/template/nextjs-vite/cli/js/Page.stories.js +28 -0
  566. package/template/nextjs-vite/cli/ts/Button.stories.ts +54 -0
  567. package/template/nextjs-vite/cli/ts/Button.tsx +39 -0
  568. package/template/nextjs-vite/cli/ts/Configure.mdx +446 -0
  569. package/template/nextjs-vite/cli/ts/Header.stories.ts +34 -0
  570. package/template/nextjs-vite/cli/ts/Header.tsx +54 -0
  571. package/template/nextjs-vite/cli/ts/Page.stories.ts +33 -0
  572. package/template/nextjs-vite/cli/ts/Page.tsx +73 -0
  573. package/template/react/cli/.eslintrc.json +5 -0
  574. package/template/react/cli/js/Button.jsx +39 -0
  575. package/template/react/cli/js/Button.stories.js +49 -0
  576. package/template/react/cli/js/Header.jsx +56 -0
  577. package/template/react/cli/js/Header.stories.js +29 -0
  578. package/template/react/cli/js/Page.jsx +69 -0
  579. package/template/react/cli/js/Page.stories.js +28 -0
  580. package/template/react/cli/ts/Button.stories.ts +54 -0
  581. package/template/react/cli/ts/Button.tsx +37 -0
  582. package/template/react/cli/ts/Header.stories.ts +34 -0
  583. package/template/react/cli/ts/Header.tsx +56 -0
  584. package/template/react/cli/ts/Page.stories.ts +33 -0
  585. package/template/react/cli/ts/Page.tsx +73 -0
  586. package/template/react-vite/cli/.eslintrc.json +5 -0
  587. package/template/react-vite/cli/js/Button.jsx +39 -0
  588. package/template/react-vite/cli/js/Button.stories.js +49 -0
  589. package/template/react-vite/cli/js/Header.jsx +56 -0
  590. package/template/react-vite/cli/js/Header.stories.js +29 -0
  591. package/template/react-vite/cli/js/Page.jsx +69 -0
  592. package/template/react-vite/cli/js/Page.stories.js +28 -0
  593. package/template/react-vite/cli/ts/Button.stories.ts +54 -0
  594. package/template/react-vite/cli/ts/Button.tsx +37 -0
  595. package/template/react-vite/cli/ts/Header.stories.ts +34 -0
  596. package/template/react-vite/cli/ts/Header.tsx +56 -0
  597. package/template/react-vite/cli/ts/Page.stories.ts +33 -0
  598. package/template/react-vite/cli/ts/Page.tsx +73 -0
@@ -0,0 +1,3635 @@
1
+ import { n as __esmMin } from "./runtime-BdGkqNEt.js";
2
+ import { A as variant, C as pipe, D as string, E as safeParse, J as isEqual, M as init_index_browser, N as nanoid, O as undefined_, S as optional, T as safeInteger, _ as looseObject, b as number, c as slash, d as l, f as array, g as literal, h as init_dist, j as void_, k as unknown, l as g, lt as dedent, m as custom, p as check, q as init_isEqual, u as init_deepsignal_core_module, ut as init_esm, v as minLength, w as record, x as object, y as minValue } from "./runtime-BZSnICCg.js";
3
+ import { n as init_storybook_error, t as StorybookError } from "./runtime-BwSxqk3t.js";
4
+ import { At as init_manager_errors, Dt as OpenServiceStaticSnapshotInvalidError, F as init_channel_slot, Ot as OpenServiceStaticSnapshotLoadError, P as getChannel, Tt as logger, f as init_storyDocsCodePanel, kt as UniversalStoreFollowerTimeoutError, p as shouldSkipStoryDocsEmit, r as definePreviewAddon, wt as init_client_logger } from "./runtime-DrTjJkPY.js";
5
+ import { i as j, n as g$1, o as n, r as init_signals_core_module, s as y, t as f } from "./runtime-BTPtVY1p.js";
6
+ import { i as emitTransformCode, r as init_preview_api } from "./runtime-CsV4HZPV.js";
7
+ //#region src/shared/universal-store/first-party-store-ids.ts
8
+ var FIRST_PARTY_STORE_IDS, isFirstPartyStoreId;
9
+ function init_first_party_store_ids() {
10
+ return (init_first_party_store_ids = __esmMin((() => {
11
+ FIRST_PARTY_STORE_IDS = /* @__PURE__ */ new Set([
12
+ "@aussieljk/forkbook/status",
13
+ "@aussieljk/forkbook/test-provider",
14
+ "@aussieljk/forkbook/test"
15
+ ]);
16
+ isFirstPartyStoreId = (id) => FIRST_PARTY_STORE_IDS.has(id);
17
+ })))();
18
+ }
19
+ //#endregion
20
+ //#region src/shared/universal-store/instances.ts
21
+ var instances;
22
+ function init_instances() {
23
+ return (init_instances = __esmMin((() => {
24
+ instances = /* @__PURE__ */ new Map();
25
+ })))();
26
+ }
27
+ //#endregion
28
+ //#region src/shared/universal-store/index.ts
29
+ var CHANNEL_EVENT_PREFIX, ProgressState, UniversalStore;
30
+ function init_universal_store() {
31
+ return (init_universal_store = __esmMin((() => {
32
+ init_esm();
33
+ init_manager_errors();
34
+ init_first_party_store_ids();
35
+ init_instances();
36
+ CHANNEL_EVENT_PREFIX = "UNIVERSAL_STORE:";
37
+ ProgressState = {
38
+ PENDING: "PENDING",
39
+ RESOLVED: "RESOLVED",
40
+ REJECTED: "REJECTED"
41
+ };
42
+ UniversalStore = class UniversalStore {
43
+ static {
44
+ this.ActorType = {
45
+ LEADER: "LEADER",
46
+ FOLLOWER: "FOLLOWER"
47
+ };
48
+ }
49
+ static {
50
+ this.Environment = {
51
+ SERVER: "SERVER",
52
+ MANAGER: "MANAGER",
53
+ PREVIEW: "PREVIEW",
54
+ UNKNOWN: "UNKNOWN",
55
+ MOCK: "MOCK"
56
+ };
57
+ }
58
+ /** The environment this realm was prepared as, or `undefined` before `__prepare`. */
59
+ static get preparedEnvironment() {
60
+ return UniversalStore.preparation.environment;
61
+ }
62
+ static {
63
+ this.InternalEventType = {
64
+ EXISTING_STATE_REQUEST: "__EXISTING_STATE_REQUEST",
65
+ EXISTING_STATE_RESPONSE: "__EXISTING_STATE_RESPONSE",
66
+ SET_STATE: "__SET_STATE",
67
+ LEADER_CREATED: "__LEADER_CREATED",
68
+ FOLLOWER_CREATED: "__FOLLOWER_CREATED"
69
+ };
70
+ }
71
+ static {
72
+ this.Status = {
73
+ UNPREPARED: "UNPREPARED",
74
+ SYNCING: "SYNCING",
75
+ READY: "READY",
76
+ ERROR: "ERROR"
77
+ };
78
+ }
79
+ static {
80
+ this.isInternalConstructing = false;
81
+ }
82
+ static {
83
+ UniversalStore.setupPreparationPromise();
84
+ }
85
+ static setupPreparationPromise() {
86
+ let resolveRef;
87
+ let rejectRef;
88
+ const promise = new Promise((resolve, reject) => {
89
+ resolveRef = (args) => {
90
+ resolve(args);
91
+ };
92
+ rejectRef = (...args) => {
93
+ reject(args);
94
+ };
95
+ });
96
+ UniversalStore.preparation = {
97
+ resolve: resolveRef,
98
+ reject: rejectRef,
99
+ promise
100
+ };
101
+ }
102
+ /** The actor object representing the store instance with a unique ID and a type */
103
+ get actor() {
104
+ return Object.freeze({
105
+ id: this.actorId,
106
+ type: this.actorType,
107
+ environment: this.environment ?? UniversalStore.Environment.UNKNOWN
108
+ });
109
+ }
110
+ /**
111
+ * The current state of the store, that signals both if the store is prepared by Forkbook and
112
+ * also - in the case of a follower - if the state has been synced with the leader's state.
113
+ */
114
+ get status() {
115
+ if (!this.channel || !this.environment) return UniversalStore.Status.UNPREPARED;
116
+ switch (this.syncing?.state) {
117
+ case ProgressState.PENDING:
118
+ case void 0: return UniversalStore.Status.SYNCING;
119
+ case ProgressState.REJECTED: return UniversalStore.Status.ERROR;
120
+ case ProgressState.RESOLVED:
121
+ default: return UniversalStore.Status.READY;
122
+ }
123
+ }
124
+ /**
125
+ * A promise that resolves when the store is fully ready. A leader will be ready when the store
126
+ * has been prepared by Forkbook, which is almost instantly.
127
+ *
128
+ * A follower will be ready when the state has been synced with the leader's state, within a few
129
+ * hundred milliseconds.
130
+ */
131
+ untilReady() {
132
+ const preparation = this.channel && this.environment ? Promise.resolve() : UniversalStore.preparation.promise;
133
+ return Promise.all([preparation, this.syncing?.promise]);
134
+ }
135
+ constructor(options, environmentOverrides) {
136
+ this.debugging = false;
137
+ this.listeners = /* @__PURE__ */ new Map([["*", /* @__PURE__ */ new Set()]]);
138
+ this.getState = () => {
139
+ this.debug("getState", { state: this.state });
140
+ return this.state;
141
+ };
142
+ this.subscribe = (eventTypeOrListener, maybeListener) => {
143
+ const subscribesToAllEvents = typeof eventTypeOrListener === "function";
144
+ const eventType = subscribesToAllEvents ? "*" : eventTypeOrListener;
145
+ const listener = subscribesToAllEvents ? eventTypeOrListener : maybeListener;
146
+ this.debug("subscribe", {
147
+ eventType,
148
+ listener
149
+ });
150
+ if (!listener) throw new TypeError(`Missing first subscribe argument, or second if first is the event type, when subscribing to a UniversalStore with id '${this.id}'`);
151
+ if (!this.listeners.has(eventType)) this.listeners.set(eventType, /* @__PURE__ */ new Set());
152
+ this.listeners.get(eventType).add(listener);
153
+ return () => {
154
+ this.debug("unsubscribe", {
155
+ eventType,
156
+ listener
157
+ });
158
+ if (!this.listeners.has(eventType)) return;
159
+ this.listeners.get(eventType).delete(listener);
160
+ if (this.listeners.get(eventType)?.size === 0) this.listeners.delete(eventType);
161
+ };
162
+ };
163
+ this.send = (event) => {
164
+ this.debug("send", { event });
165
+ if (this.status !== UniversalStore.Status.READY) throw new TypeError(dedent`Cannot send event before store is ready. You can get the current status with store.status,
166
+ or await store.readyPromise to wait for the store to be ready before sending events.
167
+ ${JSON.stringify({
168
+ event,
169
+ id: this.id,
170
+ actor: this.actor,
171
+ environment: this.environment
172
+ }, null, 2)}`);
173
+ this.emitToListeners(event, { actor: this.actor });
174
+ this.emitToChannel(event, { actor: this.actor });
175
+ };
176
+ this.debugging = options.debug ?? false;
177
+ if (!UniversalStore.isInternalConstructing) throw new TypeError("UniversalStore is not constructable - use UniversalStore.create() instead");
178
+ UniversalStore.isInternalConstructing = false;
179
+ this.id = options.id;
180
+ this.actorId = Date.now().toString(36) + Math.random().toString(36).substring(2);
181
+ this.actorType = options.leader ? UniversalStore.ActorType.LEADER : UniversalStore.ActorType.FOLLOWER;
182
+ this.state = options.initialState;
183
+ this.channelEventName = `${CHANNEL_EVENT_PREFIX}${this.id}`;
184
+ this.debug("constructor", {
185
+ options,
186
+ environmentOverrides,
187
+ channelEventName: this.channelEventName
188
+ });
189
+ if (this.actor.type === UniversalStore.ActorType.LEADER) this.syncing = {
190
+ state: ProgressState.RESOLVED,
191
+ promise: Promise.resolve()
192
+ };
193
+ else {
194
+ let syncingResolve;
195
+ let syncingReject;
196
+ const syncingPromise = new Promise((resolve, reject) => {
197
+ syncingResolve = () => {
198
+ if (this.syncing.state !== ProgressState.PENDING) return;
199
+ this.syncing.state = ProgressState.RESOLVED;
200
+ resolve();
201
+ };
202
+ syncingReject = (reason) => {
203
+ if (this.syncing.state !== ProgressState.PENDING) return;
204
+ this.syncing.state = ProgressState.REJECTED;
205
+ reject(reason);
206
+ };
207
+ });
208
+ this.syncing = {
209
+ state: ProgressState.PENDING,
210
+ promise: syncingPromise,
211
+ resolve: syncingResolve,
212
+ reject: syncingReject
213
+ };
214
+ }
215
+ this.getState = this.getState.bind(this);
216
+ this.setState = this.setState.bind(this);
217
+ this.subscribe = this.subscribe.bind(this);
218
+ this.onStateChange = this.onStateChange.bind(this);
219
+ this.send = this.send.bind(this);
220
+ this.emitToChannel = this.emitToChannel.bind(this);
221
+ this.prepareThis = this.prepareThis.bind(this);
222
+ this.emitToListeners = this.emitToListeners.bind(this);
223
+ this.handleChannelEvents = this.handleChannelEvents.bind(this);
224
+ this.debug = this.debug.bind(this);
225
+ this.channel = environmentOverrides?.channel ?? UniversalStore.preparation.channel;
226
+ this.environment = environmentOverrides?.environment ?? UniversalStore.preparation.environment;
227
+ if (this.channel && this.environment) {
228
+ if (!environmentOverrides) UniversalStore.preparation.resolve({
229
+ channel: this.channel,
230
+ environment: this.environment
231
+ });
232
+ this.prepareThis({
233
+ channel: this.channel,
234
+ environment: this.environment
235
+ });
236
+ } else UniversalStore.preparation.promise.then(this.prepareThis);
237
+ }
238
+ /** Creates a new instance of UniversalStore */
239
+ static create(options) {
240
+ if (!options || typeof options?.id !== "string") throw new TypeError("id is required and must be a string, when creating a UniversalStore");
241
+ if (!isFirstPartyStoreId(options.id)) throw new TypeError(`UniversalStore is internal to Forkbook and cannot create a store with id "${options.id}"`);
242
+ if (options.debug) console.debug(dedent`[UniversalStore]
243
+ create`, { options });
244
+ const existing = instances.get(options.id);
245
+ if (existing) {
246
+ console.warn(dedent`UniversalStore with id "${options.id}" already exists in this environment, re-using existing.
247
+ You should reuse the existing instance instead of trying to create a new one.`);
248
+ return existing;
249
+ }
250
+ UniversalStore.isInternalConstructing = true;
251
+ const store = new UniversalStore(options);
252
+ instances.set(options.id, store);
253
+ return store;
254
+ }
255
+ /**
256
+ * Used by Forkbook to set the channel for all instances of UniversalStore in the given
257
+ * environment.
258
+ *
259
+ * @internal
260
+ */
261
+ static __prepare(channel, environment) {
262
+ UniversalStore.preparation.channel = channel;
263
+ UniversalStore.preparation.environment = environment;
264
+ UniversalStore.preparation.resolve({
265
+ channel,
266
+ environment
267
+ });
268
+ }
269
+ /**
270
+ * Updates the store's state
271
+ *
272
+ * Either a new state or a state updater function can be passed to the method.
273
+ */
274
+ setState(updater) {
275
+ const previousState = this.state;
276
+ const newState = typeof updater === "function" ? updater(previousState) : updater;
277
+ this.debug("setState", {
278
+ newState,
279
+ previousState,
280
+ updater
281
+ });
282
+ if (this.status !== UniversalStore.Status.READY) throw new TypeError(dedent`Cannot set state before store is ready. You can get the current status with store.status,
283
+ or await store.readyPromise to wait for the store to be ready before sending events.
284
+ ${JSON.stringify({
285
+ newState,
286
+ id: this.id,
287
+ actor: this.actor,
288
+ environment: this.environment
289
+ }, null, 2)}`);
290
+ this.state = newState;
291
+ const event = {
292
+ type: UniversalStore.InternalEventType.SET_STATE,
293
+ payload: {
294
+ state: newState,
295
+ previousState
296
+ }
297
+ };
298
+ this.emitToChannel(event, { actor: this.actor });
299
+ this.emitToListeners(event, { actor: this.actor });
300
+ }
301
+ /**
302
+ * Subscribes to state changes
303
+ *
304
+ * @returns Unsubscribe function
305
+ */
306
+ onStateChange(listener) {
307
+ this.debug("onStateChange", { listener });
308
+ return this.subscribe(UniversalStore.InternalEventType.SET_STATE, ({ payload }, eventInfo) => {
309
+ listener(payload.state, payload.previousState, eventInfo);
310
+ });
311
+ }
312
+ emitToChannel(event, eventInfo) {
313
+ this.debug("emitToChannel", {
314
+ event,
315
+ eventInfo,
316
+ channel: !!this.channel
317
+ });
318
+ this.channel?.emit(this.channelEventName, {
319
+ event,
320
+ eventInfo
321
+ });
322
+ }
323
+ prepareThis({ channel, environment }) {
324
+ this.channel = channel;
325
+ this.environment = environment;
326
+ this.debug("prepared", {
327
+ channel: !!channel,
328
+ environment
329
+ });
330
+ this.channel.on(this.channelEventName, this.handleChannelEvents);
331
+ if (this.actor.type === UniversalStore.ActorType.LEADER) this.emitToChannel({ type: UniversalStore.InternalEventType.LEADER_CREATED }, { actor: this.actor });
332
+ else {
333
+ this.emitToChannel({ type: UniversalStore.InternalEventType.FOLLOWER_CREATED }, { actor: this.actor });
334
+ this.emitToChannel({ type: UniversalStore.InternalEventType.EXISTING_STATE_REQUEST }, { actor: this.actor });
335
+ setTimeout(() => {
336
+ this.syncing.reject(new UniversalStoreFollowerTimeoutError(this.id));
337
+ }, 1e3);
338
+ }
339
+ }
340
+ emitToListeners(event, eventInfo) {
341
+ const eventTypeListeners = this.listeners.get(event.type);
342
+ const everythingListeners = this.listeners.get("*");
343
+ this.debug("emitToListeners", {
344
+ event,
345
+ eventInfo,
346
+ eventTypeListeners,
347
+ everythingListeners
348
+ });
349
+ [...eventTypeListeners ?? [], ...everythingListeners ?? []].forEach((listener) => listener(event, eventInfo));
350
+ }
351
+ handleChannelEvents(channelEvent) {
352
+ const { event, eventInfo } = channelEvent;
353
+ if ([eventInfo.actor.id, eventInfo.forwardingActor?.id].includes(this.actor.id)) {
354
+ this.debug("handleChannelEvents: Ignoring event from self", { channelEvent });
355
+ return;
356
+ } else if (this.syncing?.state === ProgressState.PENDING && event.type !== UniversalStore.InternalEventType.EXISTING_STATE_RESPONSE) {
357
+ this.debug("handleChannelEvents: Ignoring event while syncing", { channelEvent });
358
+ return;
359
+ }
360
+ this.debug("handleChannelEvents", { channelEvent });
361
+ if (this.actor.type === UniversalStore.ActorType.LEADER) {
362
+ let shouldForwardEvent = true;
363
+ switch (event.type) {
364
+ case UniversalStore.InternalEventType.EXISTING_STATE_REQUEST:
365
+ shouldForwardEvent = false;
366
+ const responseEvent = {
367
+ type: UniversalStore.InternalEventType.EXISTING_STATE_RESPONSE,
368
+ payload: this.state
369
+ };
370
+ this.debug("handleChannelEvents: responding to existing state request", { responseEvent });
371
+ this.emitToChannel(responseEvent, { actor: this.actor });
372
+ this.emitToListeners(responseEvent, { actor: this.actor });
373
+ break;
374
+ case UniversalStore.InternalEventType.LEADER_CREATED:
375
+ shouldForwardEvent = false;
376
+ this.syncing.state = ProgressState.REJECTED;
377
+ this.debug("handleChannelEvents: erroring due to second leader being created", { event });
378
+ console.error(dedent`Detected multiple UniversalStore leaders created with the same id "${this.id}".
379
+ Only one leader can exists at a time, your stores are now in an invalid state.
380
+ Leaders detected:
381
+ this: ${JSON.stringify(this.actor, null, 2)}
382
+ other: ${JSON.stringify(eventInfo.actor, null, 2)}`);
383
+ }
384
+ if (shouldForwardEvent) {
385
+ this.debug("handleChannelEvents: forwarding event", { channelEvent });
386
+ this.emitToChannel(event, {
387
+ actor: eventInfo.actor,
388
+ forwardingActor: this.actor
389
+ });
390
+ }
391
+ }
392
+ if (this.actor.type === UniversalStore.ActorType.FOLLOWER) switch (event.type) {
393
+ case UniversalStore.InternalEventType.EXISTING_STATE_RESPONSE:
394
+ this.debug("handleChannelEvents: Setting state from leader's existing state response", { event });
395
+ if (this.syncing?.state !== ProgressState.PENDING) break;
396
+ this.syncing.resolve?.();
397
+ const setStateEvent = {
398
+ type: UniversalStore.InternalEventType.SET_STATE,
399
+ payload: {
400
+ state: event.payload,
401
+ previousState: this.state
402
+ }
403
+ };
404
+ this.state = event.payload;
405
+ this.emitToListeners(setStateEvent, eventInfo);
406
+ }
407
+ switch (event.type) {
408
+ case UniversalStore.InternalEventType.SET_STATE:
409
+ this.debug("handleChannelEvents: Setting state", { event });
410
+ this.state = event.payload.state;
411
+ }
412
+ this.emitToListeners(event, { actor: eventInfo.actor });
413
+ }
414
+ debug(message, data) {
415
+ if (this.debugging) console.debug(dedent`[UniversalStore::${this.id}::${this.environment ?? UniversalStore.Environment.UNKNOWN}]
416
+ ${message}`, JSON.stringify({
417
+ data,
418
+ actor: this.actor,
419
+ state: this.state,
420
+ status: this.status
421
+ }, null, 2));
422
+ }
423
+ /**
424
+ * Used to reset the static fields of the UniversalStore class when cleaning up tests
425
+ *
426
+ * @internal
427
+ */
428
+ static __reset() {
429
+ UniversalStore.preparation.reject(/* @__PURE__ */ new Error("reset"));
430
+ UniversalStore.setupPreparationPromise();
431
+ UniversalStore.isInternalConstructing = false;
432
+ }
433
+ };
434
+ })))();
435
+ }
436
+ //#endregion
437
+ //#region src/shared/utils/local-hosts.ts
438
+ var LOOPBACK_HOSTNAMES, matchesHostname;
439
+ function init_local_hosts() {
440
+ return (init_local_hosts = __esmMin((() => {
441
+ LOOPBACK_HOSTNAMES = [
442
+ "localhost",
443
+ "*.localhost",
444
+ "127.0.0.1"
445
+ ];
446
+ matchesHostname = (hostname, patterns) => patterns.some((pattern) => pattern.startsWith("*.") ? hostname.endsWith(pattern.slice(1)) : hostname === pattern);
447
+ })))();
448
+ }
449
+ //#endregion
450
+ //#region src/shared/open-service/errors.ts
451
+ /**
452
+ * Formats a schema issue path into the field notation shown in validation errors.
453
+ *
454
+ * Examples:
455
+ * - `['foo']` -> `foo`
456
+ * - `['items', 0, 'id']` -> `items[0].id`
457
+ */
458
+ function formatIssuePath(path) {
459
+ if (!path?.length) return "";
460
+ return path.map((segment) => {
461
+ const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
462
+ return typeof key === "number" ? `[${key}]` : `.${String(key)}`;
463
+ }).join("").replace(/^\./, "");
464
+ }
465
+ /**
466
+ * Converts schema issues into the newline-separated detail block appended to user-facing errors.
467
+ */
468
+ function formatIssues(issues) {
469
+ return issues.map((issue) => {
470
+ const path = formatIssuePath(issue.path);
471
+ return path === "" ? issue.message : `${path}: ${issue.message}`;
472
+ }).join("\n");
473
+ }
474
+ //#endregion
475
+ //#region src/server-errors.ts
476
+ var OpenServiceValidationError, OpenServiceMissingServiceError, OpenServiceInternalServiceError, OpenServiceUnimplementedOperationError, OpenServiceInvalidStaticPathError, OpenServiceAsyncSchemaError, OpenServiceLoadedDrainExceededError, OpenServiceMissingChannelError, OpenServiceRemoteCommandDisconnectedError, OpenServiceRemoteCommandUnhandledError, OpenServiceRemoteCommandConfigDriftError, OpenServiceAsyncRecipeError, OpenServiceCyclicStateError, OpenServiceOperationNameCollisionError;
477
+ function init_server_errors() {
478
+ return (init_server_errors = __esmMin((() => {
479
+ init_storybook_error();
480
+ OpenServiceValidationError = class extends StorybookError {
481
+ constructor(data) {
482
+ super({
483
+ name: "OpenServiceValidationError",
484
+ category: "CORE-COMMON",
485
+ code: 5,
486
+ message: `Invalid ${data.phase} for ${data.kind} "${data.serviceId}.${data.name}":\n${formatIssues(data.issues)}`
487
+ });
488
+ this.data = data;
489
+ }
490
+ };
491
+ OpenServiceMissingServiceError = class extends StorybookError {
492
+ constructor(data) {
493
+ super({
494
+ name: "OpenServiceMissingServiceError",
495
+ category: "CORE-COMMON",
496
+ code: 7,
497
+ message: `No registered service with id "${data.serviceId}" exists in this environment.`
498
+ });
499
+ this.data = data;
500
+ }
501
+ };
502
+ OpenServiceInternalServiceError = class extends StorybookError {
503
+ constructor(data) {
504
+ super({
505
+ name: "OpenServiceInternalServiceError",
506
+ category: "CORE-COMMON",
507
+ code: 19,
508
+ message: `Service "${data.serviceId}" is internal. Pass { internal: true } to getService() only if you intentionally depend on an unstable OSA surface. Internal services may change without notice.`
509
+ });
510
+ this.data = data;
511
+ }
512
+ };
513
+ OpenServiceUnimplementedOperationError = class extends StorybookError {
514
+ constructor(data) {
515
+ super({
516
+ name: "OpenServiceUnimplementedOperationError",
517
+ category: "CORE-COMMON",
518
+ code: 8,
519
+ message: `${data.kind[0].toUpperCase()}${data.kind.slice(1)} "${data.serviceId}.${data.name}" is not implemented for this environment.`
520
+ });
521
+ this.data = data;
522
+ }
523
+ };
524
+ OpenServiceInvalidStaticPathError = class extends StorybookError {
525
+ constructor(data) {
526
+ super({
527
+ name: "OpenServiceInvalidStaticPathError",
528
+ category: "CORE-COMMON",
529
+ code: 10,
530
+ message: `Invalid static path "${data.path}" for query "${data.serviceId}.${data.name}": use a relative path with forward slashes and no ".." segments.`
531
+ });
532
+ this.data = data;
533
+ }
534
+ };
535
+ OpenServiceAsyncSchemaError = class extends StorybookError {
536
+ constructor(data) {
537
+ super({
538
+ name: "OpenServiceAsyncSchemaError",
539
+ category: "CORE-COMMON",
540
+ code: 9,
541
+ message: `Async schema for ${data.kind} "${data.serviceId}.${data.name}" (${data.phase}): query input and output schemas must validate synchronously.`
542
+ });
543
+ this.data = data;
544
+ }
545
+ };
546
+ OpenServiceLoadedDrainExceededError = class extends StorybookError {
547
+ constructor(data) {
548
+ super({
549
+ name: "OpenServiceLoadedDrainExceededError",
550
+ category: "CORE-COMMON",
551
+ code: 11,
552
+ message: `Query "${data.serviceId}.${data.name}".loaded(...) did not settle after ${data.iterations} drain iterations. Check for handlers that keep discovering new dependencies after every state change.`
553
+ });
554
+ this.data = data;
555
+ }
556
+ };
557
+ OpenServiceMissingChannelError = class extends StorybookError {
558
+ constructor(data = {}) {
559
+ super({
560
+ name: "OpenServiceMissingChannelError",
561
+ category: "CORE-COMMON",
562
+ code: 13,
563
+ message: data.serviceId ? `Cannot register service "${data.serviceId}": the Forkbook addons channel is not installed in this runtime.` : "The Forkbook addons channel is not installed in this runtime."
564
+ });
565
+ this.data = data;
566
+ }
567
+ };
568
+ OpenServiceRemoteCommandDisconnectedError = class extends StorybookError {
569
+ constructor(data) {
570
+ super({
571
+ name: "OpenServiceRemoteCommandDisconnectedError",
572
+ category: "CORE-COMMON",
573
+ code: 14,
574
+ message: `Service "${data.serviceId}" was unregistered before a remote command resolved.`
575
+ });
576
+ this.data = data;
577
+ }
578
+ };
579
+ OpenServiceRemoteCommandUnhandledError = class extends StorybookError {
580
+ constructor(data) {
581
+ super({
582
+ name: "OpenServiceRemoteCommandUnhandledError",
583
+ category: "CORE-COMMON",
584
+ code: 15,
585
+ message: data.delegated ? `The Forkbook this runtime is attached to did not acknowledge remote command "${data.serviceId}.${data.commandName}" in time — it may be busy or unreachable. Retry; note the command may still have executed on that instance.` : `No runtime acknowledged remote command "${data.serviceId}.${data.commandName}"; its handler is not implemented in any connected runtime.`
586
+ });
587
+ this.data = data;
588
+ }
589
+ };
590
+ OpenServiceRemoteCommandConfigDriftError = class extends StorybookError {
591
+ constructor(data) {
592
+ super({
593
+ name: "OpenServiceRemoteCommandConfigDriftError",
594
+ category: "CORE-COMMON",
595
+ code: 30,
596
+ message: `The Forkbook this runtime is attached to reported it has no handler for remote command "${data.serviceId}.${data.commandName}". The two processes are running different configurations (for example a feature flag enabled in one but not the other). Restart the attached Forkbook with a configuration matching this process.`
597
+ });
598
+ this.data = data;
599
+ }
600
+ };
601
+ OpenServiceAsyncRecipeError = class extends StorybookError {
602
+ constructor() {
603
+ super({
604
+ name: "OpenServiceAsyncRecipeError",
605
+ category: "CORE-COMMON",
606
+ code: 31,
607
+ message: "setState recipes must be synchronous. A write after an await would change state without authoring a sync entry. Split the command into one setState per synchronous step."
608
+ });
609
+ }
610
+ };
611
+ OpenServiceCyclicStateError = class extends StorybookError {
612
+ constructor() {
613
+ super({
614
+ name: "OpenServiceCyclicStateError",
615
+ category: "CORE-COMMON",
616
+ code: 32,
617
+ message: "Service state must be JSON-serializable, but the value is cyclic or nested more than 256 levels deep. Store an id and look the value up in a query instead of a reference."
618
+ });
619
+ }
620
+ };
621
+ OpenServiceOperationNameCollisionError = class extends StorybookError {
622
+ constructor(data) {
623
+ super({
624
+ name: "OpenServiceOperationNameCollisionError",
625
+ category: "CORE-COMMON",
626
+ code: 16,
627
+ message: `Service "${data.serviceId}" cannot register "${data.operationName}" as both a query and a command.`
628
+ });
629
+ this.data = data;
630
+ }
631
+ };
632
+ })))();
633
+ }
634
+ //#endregion
635
+ //#region src/shared/status-store/index.ts
636
+ var CHANGE_DETECTION_STATUS_TYPE_ID, REVIEW_STATUS_TYPE_ID, NON_AGGREGATED_STATUS_TYPE_IDS;
637
+ function init_status_store() {
638
+ return (init_status_store = __esmMin((() => {
639
+ CHANGE_DETECTION_STATUS_TYPE_ID = "@aussieljk/forkbook/change-detection";
640
+ REVIEW_STATUS_TYPE_ID = "@aussieljk/forkbook/review";
641
+ NON_AGGREGATED_STATUS_TYPE_IDS = [CHANGE_DETECTION_STATUS_TYPE_ID, REVIEW_STATUS_TYPE_ID];
642
+ })))();
643
+ }
644
+ //#endregion
645
+ //#region src/shared/review/events.ts
646
+ var REVIEW_NAMESPACE, REVIEW_EVENTS;
647
+ function init_events() {
648
+ return (init_events = __esmMin((() => {
649
+ REVIEW_NAMESPACE = "@aussieljk/forkbook/review";
650
+ REVIEW_EVENTS = { PAGEVIEW: `${REVIEW_NAMESPACE}/pageview` };
651
+ })))();
652
+ }
653
+ //#endregion
654
+ //#region src/shared/constants/tags.ts
655
+ var Tag;
656
+ function init_tags() {
657
+ return (init_tags = __esmMin((() => {
658
+ Tag = {
659
+ /** Indicates that autodocs should be generated for this component */
660
+ AUTODOCS: "autodocs",
661
+ /** MDX documentation attached to a component's stories file */
662
+ ATTACHED_MDX: "attached-mdx",
663
+ /** Standalone MDX documentation not attached to stories */
664
+ UNATTACHED_MDX: "unattached-mdx",
665
+ /** Story has a play function */
666
+ PLAY_FN: "play-fn",
667
+ /** Story has a test function */
668
+ TEST_FN: "test-fn",
669
+ /** Development environment tag */
670
+ DEV: "dev",
671
+ /** Test environment tag */
672
+ TEST: "test",
673
+ /** Manifest generation tag */
674
+ MANIFEST: "manifest"
675
+ };
676
+ })))();
677
+ }
678
+ //#endregion
679
+ //#region src/shared/open-service/plain-object.ts
680
+ function isReservedKey(key) {
681
+ return FORBIDDEN_KEYS.has(key) || key.startsWith("$");
682
+ }
683
+ function hasOwn(obj, key) {
684
+ return Object.prototype.hasOwnProperty.call(obj, key);
685
+ }
686
+ function isPlainObject$1(value) {
687
+ return typeof value === "object" && value !== null && !Array.isArray(value);
688
+ }
689
+ /**
690
+ * Deep-copies a JSON-shaped value, giving every path its own object.
691
+ *
692
+ * Used for every value on its way into service state: a definition's `initialState`, an object
693
+ * assigned inside a `setState` recipe, and the value carried by a recorded op.
694
+ *
695
+ * Not `structuredClone`. That keeps a reference shared between two keys as one object, so a write
696
+ * through one key would also change the other on the writing runtime, while peers, who only ever
697
+ * receive JSON, would see one path change. Rebuilding key by key removes the alias on both sides.
698
+ * It also accepts proxies and drops the prototype-pollution keys in {@link FORBIDDEN_KEYS} and
699
+ * deepsignal's `$`-prefixed accessor keys. A cyclic value throws {@link OpenServiceCyclicStateError},
700
+ * since it could never be serialized for peers; the check is a depth limit, which costs nothing per
701
+ * node. An `undefined` property is dropped, as JSON drops it, so the author and peers agree on which
702
+ * keys exist.
703
+ *
704
+ * Only own enumerable string keys are copied. Class instances, `Map`, `Set`, and `Date` become plain
705
+ * objects, which matches the JSON-serializable contract service state already has.
706
+ */
707
+ function clonePlain(value, depth = 0) {
708
+ if (value === null || typeof value !== "object") return value;
709
+ if (depth > MAX_DEPTH) throw new OpenServiceCyclicStateError();
710
+ if (Array.isArray(value)) return value.map((entry) => clonePlain(entry, depth + 1));
711
+ const copy = {};
712
+ for (const key of Object.keys(value)) {
713
+ const entry = value[key];
714
+ if (isReservedKey(key) || entry === void 0) continue;
715
+ copy[key] = clonePlain(entry, depth + 1);
716
+ }
717
+ return copy;
718
+ }
719
+ var FORBIDDEN_KEYS, MAX_DEPTH;
720
+ function init_plain_object() {
721
+ return (init_plain_object = __esmMin((() => {
722
+ init_server_errors();
723
+ FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
724
+ "__proto__",
725
+ "constructor",
726
+ "prototype"
727
+ ]);
728
+ MAX_DEPTH = 256;
729
+ })))();
730
+ }
731
+ //#endregion
732
+ //#region src/shared/open-service/service-channel.ts
733
+ function decodeSegment(encoded) {
734
+ return encoded.replaceAll("~1", "/").replaceAll("~0", "~");
735
+ }
736
+ /** RFC 6901 JSON Pointer: `~0` encodes `~`, `~1` encodes `/`; no segments is the root pointer `''`. */
737
+ function encodePointer(segments) {
738
+ return segments.map((segment) => `/${segment.replaceAll("~", "~0").replaceAll("/", "~1")}`).join("");
739
+ }
740
+ /** Inverse of {@link encodePointer}. `pointer` must be empty or start with `/`. */
741
+ function decodePointer(pointer) {
742
+ if (pointer === "") return [];
743
+ return pointer.slice(1).split("/").map(decodeSegment);
744
+ }
745
+ function isRfc6901Pointer(pointer) {
746
+ if (!pointer.startsWith("/")) return false;
747
+ return pointer.slice(1).split("/").every((encoded) => {
748
+ return RFC_6901_ENCODED_SEGMENT.test(encoded) && !isReservedKey(decodeSegment(encoded));
749
+ });
750
+ }
751
+ /** Canonical log key and warning label: `seq:runtimeId:counter`. */
752
+ function entryStampKey(stamp) {
753
+ return `${stamp.seq}:${stamp.runtimeId}:${stamp.counter}`;
754
+ }
755
+ /**
756
+ * Unique id for one service registration.
757
+ *
758
+ * It is the loop guard that drops a runtime's own `services:sync-request`. `nanoid` is used (over
759
+ * `Math.random`) so collisions cannot silently break that determinism.
760
+ */
761
+ function generateRuntimeId() {
762
+ return nanoid();
763
+ }
764
+ /** Unique id for one remote-command invocation. Replies correlate on this, not on `runtimeId`. */
765
+ function generateCallId() {
766
+ return nanoid();
767
+ }
768
+ var SERVICE_SYNC_REQUEST, SERVICE_SYNC_REPLY, SERVICE_ENTRY, SERVICE_COMMAND_INVOKE, SERVICE_COMMAND_ACK, SERVICE_COMMAND_RESULT, SERVICE_COMMAND_ERROR, SERVICE_COMMAND_UNHANDLED, stateSnapshotSchema, nonNegativeSafeInteger, positiveSafeInteger, RFC_6901_ENCODED_SEGMENT, jsonPointerSchema, jsonPatchOperationSchema, entryStampSchema, entrySchema, frontierSchema, syncRequestSchema, syncReplySchema, commandInvokeSchema, commandAckSchema, commandUnhandledSchema, commandResultSchema, commandErrorSchema;
769
+ function init_service_channel() {
770
+ return (init_service_channel = __esmMin((() => {
771
+ init_index_browser();
772
+ init_dist();
773
+ init_plain_object();
774
+ SERVICE_SYNC_REQUEST = "services:sync-request";
775
+ SERVICE_SYNC_REPLY = "services:sync-reply";
776
+ SERVICE_ENTRY = "services:entry";
777
+ SERVICE_COMMAND_INVOKE = "services:command-invoke";
778
+ SERVICE_COMMAND_ACK = "services:command-ack";
779
+ SERVICE_COMMAND_RESULT = "services:command-result";
780
+ SERVICE_COMMAND_ERROR = "services:command-error";
781
+ SERVICE_COMMAND_UNHANDLED = "services:command-unhandled";
782
+ stateSnapshotSchema = custom(isPlainObject$1);
783
+ nonNegativeSafeInteger = pipe(number(), safeInteger(), minValue(0));
784
+ positiveSafeInteger = pipe(number(), safeInteger(), minValue(1));
785
+ RFC_6901_ENCODED_SEGMENT = /^(?:[^~]|~[01])*$/;
786
+ jsonPointerSchema = pipe(string(), check(isRfc6901Pointer));
787
+ jsonPatchOperationSchema = variant("op", [
788
+ object({
789
+ op: literal("add"),
790
+ path: jsonPointerSchema,
791
+ value: unknown()
792
+ }),
793
+ object({
794
+ op: literal("replace"),
795
+ path: jsonPointerSchema,
796
+ value: unknown()
797
+ }),
798
+ object({
799
+ op: literal("remove"),
800
+ path: jsonPointerSchema
801
+ })
802
+ ]);
803
+ entryStampSchema = object({
804
+ seq: positiveSafeInteger,
805
+ runtimeId: string(),
806
+ counter: positiveSafeInteger
807
+ });
808
+ entrySchema = object({
809
+ serviceId: string(),
810
+ stamp: entryStampSchema,
811
+ command: string(),
812
+ patch: pipe(array(jsonPatchOperationSchema), minLength(1))
813
+ });
814
+ frontierSchema = object({
815
+ vector: record(string(), nonNegativeSafeInteger),
816
+ clock: nonNegativeSafeInteger
817
+ });
818
+ syncRequestSchema = object({
819
+ serviceId: string(),
820
+ runtimeId: string(),
821
+ frontier: frontierSchema
822
+ });
823
+ syncReplySchema = object({
824
+ serviceId: string(),
825
+ runtimeId: string(),
826
+ frontier: frontierSchema,
827
+ state: stateSnapshotSchema
828
+ });
829
+ commandInvokeSchema = object({
830
+ serviceId: string(),
831
+ commandName: string(),
832
+ input: optional(unknown()),
833
+ callId: string(),
834
+ runtimeId: string()
835
+ });
836
+ commandAckSchema = object({
837
+ serviceId: string(),
838
+ callId: string(),
839
+ runtimeId: string()
840
+ });
841
+ commandUnhandledSchema = object({
842
+ serviceId: string(),
843
+ callId: string()
844
+ });
845
+ commandResultSchema = object({
846
+ serviceId: string(),
847
+ callId: string(),
848
+ result: optional(unknown()),
849
+ runtimeId: string()
850
+ });
851
+ commandErrorSchema = object({
852
+ serviceId: string(),
853
+ callId: string(),
854
+ error: custom((value) => typeof value === "object" && value !== null && !Array.isArray(value)),
855
+ runtimeId: string()
856
+ });
857
+ })))();
858
+ }
859
+ //#endregion
860
+ //#region src/shared/open-service/patch-recorder.ts
861
+ function peekProp(obj, key) {
862
+ return g(obj, key);
863
+ }
864
+ function isPrimitive(value) {
865
+ return value === null || typeof value !== "object";
866
+ }
867
+ function ownValue(inner, name) {
868
+ const existed = hasOwn(inner, name);
869
+ return {
870
+ existed,
871
+ firstValue: existed ? clonePlain(peekProp(inner, name)) : void 0
872
+ };
873
+ }
874
+ function readPath(root, segments) {
875
+ let current = root;
876
+ for (const segment of segments) {
877
+ if (current === null || typeof current !== "object") return {
878
+ found: false,
879
+ value: void 0
880
+ };
881
+ if (!hasOwn(current, segment)) return {
882
+ found: false,
883
+ value: void 0
884
+ };
885
+ current = peekProp(current, segment);
886
+ }
887
+ return {
888
+ found: true,
889
+ value: current
890
+ };
891
+ }
892
+ /**
893
+ * Runs `mutate` against `state` through a recording proxy and passes the resulting RFC 6902 ops to
894
+ * `author`.
895
+ *
896
+ * `author` runs even when the recipe throws, with the paths written before the throw, and the
897
+ * throw then propagates. A recipe that changes nothing does not call `author`. A call made inside
898
+ * another recipe on the same state writes into that recipe's draft, so its writes join that entry.
899
+ */
900
+ function recordPatch(state, mutate, author) {
901
+ const activeDraft = activeDrafts.get(state);
902
+ if (activeDraft) {
903
+ const nested = mutate(activeDraft);
904
+ if (isThenable(nested)) {
905
+ Promise.resolve(nested).catch(() => {});
906
+ throw new OpenServiceAsyncRecipeError();
907
+ }
908
+ return;
909
+ }
910
+ const root = state;
911
+ const touches = /* @__PURE__ */ new Map();
912
+ const order = [];
913
+ const wrapperByTarget = /* @__PURE__ */ new WeakMap();
914
+ const targetByWrapper = /* @__PURE__ */ new WeakMap();
915
+ const revokes = [];
916
+ const preRecipeValue = (segments) => {
917
+ const { found, value } = readPath(root, segments);
918
+ if (!found) return {
919
+ existed: false,
920
+ firstValue: void 0
921
+ };
922
+ const snapshot = clonePlain(value);
923
+ const prefix = encodePointer(segments);
924
+ for (const pointer of order.toReversed()) {
925
+ if (!pointer.startsWith(`${prefix}/`)) continue;
926
+ const descendant = touches.get(pointer);
927
+ let parent = snapshot;
928
+ const relative = descendant.segments.slice(segments.length);
929
+ for (const segment of relative.slice(0, -1)) parent = parent !== null && typeof parent === "object" ? parent[segment] : void 0;
930
+ if (parent === null || typeof parent !== "object") continue;
931
+ const key = relative[relative.length - 1];
932
+ if (descendant.existed) parent[key] = clonePlain(descendant.firstValue);
933
+ else delete parent[key];
934
+ }
935
+ return {
936
+ existed: true,
937
+ firstValue: snapshot
938
+ };
939
+ };
940
+ const note = (segments, capture) => {
941
+ const pointer = encodePointer(segments);
942
+ if (touches.has(pointer)) return;
943
+ const prefix = `${pointer}/`;
944
+ const subsumesEarlierTouch = order.some((recorded) => recorded.startsWith(prefix));
945
+ touches.set(pointer, {
946
+ segments,
947
+ ...subsumesEarlierTouch ? preRecipeValue(segments) : capture()
948
+ });
949
+ order.push(pointer);
950
+ };
951
+ const noteArray = (arrayRoot) => {
952
+ note(arrayRoot.path, () => ({
953
+ existed: true,
954
+ firstValue: clonePlain(arrayRoot.target)
955
+ }));
956
+ };
957
+ const storedValue = (value) => {
958
+ if (value === null || typeof value !== "object") return value;
959
+ return clonePlain(targetByWrapper.get(value) ?? value);
960
+ };
961
+ const isUnchangedAssignment = (inner, name, value) => {
962
+ if (!hasOwn(inner, name)) return false;
963
+ if (Object.is(peekProp(inner, name), value)) return true;
964
+ if (value !== null && typeof value === "object") {
965
+ const target = targetByWrapper.get(value);
966
+ return target !== void 0 && Object.is(Reflect.get(inner, name), target);
967
+ }
968
+ return false;
969
+ };
970
+ const wrap = (target, path, arrayRoot) => {
971
+ const cached = wrapperByTarget.get(target);
972
+ if (cached) return cached;
973
+ const effectiveArrayRoot = arrayRoot ?? (Array.isArray(target) ? {
974
+ path,
975
+ target
976
+ } : null);
977
+ const { proxy, revoke } = Proxy.revocable(target, {
978
+ get(inner, key) {
979
+ if (typeof key === "symbol") return Reflect.get(inner, key);
980
+ if (FORBIDDEN_KEYS.has(key) || key.startsWith("$")) return;
981
+ const value = Reflect.get(inner, key);
982
+ if (typeof value === "function") return value;
983
+ if (value !== null && typeof value === "object") return wrap(value, path.concat(String(key)), effectiveArrayRoot);
984
+ return value;
985
+ },
986
+ set(inner, key, value) {
987
+ if (typeof key === "symbol") return Reflect.set(inner, key, value);
988
+ const name = String(key);
989
+ if (FORBIDDEN_KEYS.has(name)) return true;
990
+ if (name.startsWith("$")) {
991
+ const plain = name.slice(1);
992
+ if (FORBIDDEN_KEYS.has(plain)) return true;
993
+ note(path.concat(plain), () => ownValue(inner, plain));
994
+ return Reflect.set(inner, key, value);
995
+ }
996
+ if (isUnchangedAssignment(inner, name, value)) return true;
997
+ if (effectiveArrayRoot) {
998
+ noteArray(effectiveArrayRoot);
999
+ return Reflect.set(inner, name, storedValue(value));
1000
+ }
1001
+ const segments = path.concat(name);
1002
+ if (value === void 0) {
1003
+ if (hasOwn(inner, name)) {
1004
+ note(segments, () => ownValue(inner, name));
1005
+ return Reflect.deleteProperty(inner, name);
1006
+ }
1007
+ return true;
1008
+ }
1009
+ note(segments, () => ownValue(inner, name));
1010
+ return Reflect.set(inner, name, storedValue(value));
1011
+ },
1012
+ deleteProperty(inner, key) {
1013
+ if (typeof key === "symbol") return Reflect.deleteProperty(inner, key);
1014
+ const name = String(key);
1015
+ if (FORBIDDEN_KEYS.has(name)) return true;
1016
+ if (name.startsWith("$")) return Reflect.deleteProperty(inner, key);
1017
+ if (!hasOwn(inner, name)) return true;
1018
+ if (effectiveArrayRoot) {
1019
+ noteArray(effectiveArrayRoot);
1020
+ return Reflect.deleteProperty(inner, name);
1021
+ }
1022
+ note(path.concat(name), () => ownValue(inner, name));
1023
+ return Reflect.deleteProperty(inner, name);
1024
+ }
1025
+ });
1026
+ revokes.push(revoke);
1027
+ wrapperByTarget.set(target, proxy);
1028
+ targetByWrapper.set(proxy, target);
1029
+ return proxy;
1030
+ };
1031
+ const flush = () => {
1032
+ const touched = new Set(order);
1033
+ const ops = [];
1034
+ const inverse = [];
1035
+ for (const pointer of order) {
1036
+ const touch = touches.get(pointer);
1037
+ if (touch.segments.some((_, index) => {
1038
+ if (index === 0) return false;
1039
+ return touched.has(encodePointer(touch.segments.slice(0, index)));
1040
+ })) continue;
1041
+ const { found, value } = readPath(root, touch.segments);
1042
+ if (!found) {
1043
+ if (touch.existed) {
1044
+ ops.push({
1045
+ op: "remove",
1046
+ path: pointer
1047
+ });
1048
+ inverse.push({
1049
+ op: "add",
1050
+ path: pointer,
1051
+ value: touch.firstValue
1052
+ });
1053
+ }
1054
+ continue;
1055
+ }
1056
+ if (isPrimitive(value) && Object.is(value, touch.firstValue)) continue;
1057
+ ops.push({
1058
+ op: touch.existed ? "replace" : "add",
1059
+ path: pointer,
1060
+ value: clonePlain(value)
1061
+ });
1062
+ inverse.push(touch.existed ? {
1063
+ op: "replace",
1064
+ path: pointer,
1065
+ value: touch.firstValue
1066
+ } : {
1067
+ op: "remove",
1068
+ path: pointer
1069
+ });
1070
+ }
1071
+ return {
1072
+ ops,
1073
+ inverse
1074
+ };
1075
+ };
1076
+ n(() => {
1077
+ f(() => {
1078
+ const draft = wrap(state, [], null);
1079
+ activeDrafts.set(state, draft);
1080
+ try {
1081
+ const result = mutate(draft);
1082
+ if (isThenable(result)) {
1083
+ Promise.resolve(result).catch(() => {});
1084
+ throw new OpenServiceAsyncRecipeError();
1085
+ }
1086
+ } finally {
1087
+ activeDrafts.delete(state);
1088
+ for (const revoke of revokes) revoke();
1089
+ const recorded = flush();
1090
+ if (recorded.ops.length > 0) author(recorded);
1091
+ }
1092
+ });
1093
+ });
1094
+ }
1095
+ function isThenable(value) {
1096
+ return value !== null && typeof value === "object" && typeof value.then === "function";
1097
+ }
1098
+ var activeDrafts;
1099
+ function init_patch_recorder() {
1100
+ return (init_patch_recorder = __esmMin((() => {
1101
+ init_signals_core_module();
1102
+ init_deepsignal_core_module();
1103
+ init_server_errors();
1104
+ init_plain_object();
1105
+ init_service_channel();
1106
+ activeDrafts = /* @__PURE__ */ new WeakMap();
1107
+ })))();
1108
+ }
1109
+ //#endregion
1110
+ //#region src/shared/open-service/query-state.ts
1111
+ /**
1112
+ * Expands a `(data, lifecycle)` pair into the full {@link QueryState} surfaced to subscribers.
1113
+ *
1114
+ * The single source of truth for the derived booleans, shared by the runtime's subscription
1115
+ * emissions and `useServiceQuery`'s first-render seed so they can never drift apart. The booleans
1116
+ * intentionally follow our `load` vocabulary: `isLoading` is "any load in flight" (TanStack's
1117
+ * `isFetching`) and `isInitialLoading` is "the first load with nothing to show yet".
1118
+ *
1119
+ * Unlike TanStack Query, a subscription here can attach to a query whose `data` is already cached in
1120
+ * service state (e.g. revisiting an already-extracted component): its per-subscription lifecycle
1121
+ * still starts `pending`/`loading`, but `data` is present from the first emission. `isInitialLoading`
1122
+ * therefore also requires `data === undefined`, so consumers gate a "nothing yet" skeleton on it
1123
+ * without flashing it over already-available data while a background (re)load runs.
1124
+ */
1125
+ function buildQueryState(data, lifecycle) {
1126
+ const isPending = lifecycle.status === "pending";
1127
+ const isLoading = lifecycle.loadStatus === "loading";
1128
+ return {
1129
+ data,
1130
+ error: lifecycle.error,
1131
+ status: lifecycle.status,
1132
+ loadStatus: lifecycle.loadStatus,
1133
+ isPending,
1134
+ isSuccess: lifecycle.status === "success",
1135
+ isError: lifecycle.status === "error",
1136
+ isLoading,
1137
+ isInitialLoading: isPending && isLoading && data === void 0,
1138
+ isRefreshing: isLoading && !isPending
1139
+ };
1140
+ }
1141
+ /** Coerces an unknown thrown value into an `Error` for the `error` field of a {@link QueryState}. */
1142
+ function toError(value) {
1143
+ return value instanceof Error ? value : new Error(String(value));
1144
+ }
1145
+ //#endregion
1146
+ //#region src/shared/open-service/service-validation.ts
1147
+ /**
1148
+ * Re-throws asynchronous subscription failures on the microtask queue so they are not silently
1149
+ * swallowed by promise chains started from reactive listeners.
1150
+ */
1151
+ function rethrowAsync(error) {
1152
+ queueMicrotask(() => {
1153
+ throw error;
1154
+ });
1155
+ }
1156
+ /**
1157
+ * Validates a value with a Standard Schema and returns the parsed output value.
1158
+ *
1159
+ * Any schema issues are wrapped in `OpenServiceValidationError`, which standardizes the operation
1160
+ * metadata while preserving the schema's own expectation text for the actionable details.
1161
+ */
1162
+ async function validateSchema(schema, value, meta) {
1163
+ const validationResult = await schema["~standard"].validate(value);
1164
+ if (validationResult.issues) throw new OpenServiceValidationError({
1165
+ ...meta,
1166
+ issues: validationResult.issues
1167
+ });
1168
+ return validationResult.value;
1169
+ }
1170
+ /**
1171
+ * Synchronous variant of `validateSchema` used on the sync query call path.
1172
+ *
1173
+ * Query input and output schemas must produce sync validation results so the public `query(input)`
1174
+ * function can return a value immediately. If a schema accidentally returns a Promise, the runtime
1175
+ * surfaces a dedicated error instead of silently switching to async behavior.
1176
+ */
1177
+ function validateSchemaSync(schema, value, meta) {
1178
+ const validationResult = schema["~standard"].validate(value);
1179
+ if (validationResult instanceof Promise) throw new OpenServiceAsyncSchemaError({
1180
+ kind: meta.kind,
1181
+ serviceId: meta.serviceId,
1182
+ name: meta.name,
1183
+ phase: meta.phase
1184
+ });
1185
+ if (validationResult.issues) throw new OpenServiceValidationError({
1186
+ ...meta,
1187
+ issues: validationResult.issues
1188
+ });
1189
+ return validationResult.value;
1190
+ }
1191
+ function init_service_validation() {
1192
+ return (init_service_validation = __esmMin((() => {
1193
+ init_server_errors();
1194
+ })))();
1195
+ }
1196
+ //#endregion
1197
+ //#region src/shared/open-service/query-runtime.ts
1198
+ /**
1199
+ * Returns a deterministic JSON encoding so the same logical input always produces the same key.
1200
+ *
1201
+ * Object keys are sorted recursively so `{a:1, b:2}` and `{b:2, a:1}` hash identically. Inputs are
1202
+ * expected to be JSON-safe; non-serializable values like `Date`, `Map`, or functions fall back to
1203
+ * `JSON.stringify`'s defaults and may produce ambiguous keys.
1204
+ */
1205
+ function stableHash(value) {
1206
+ const encode = (raw) => {
1207
+ if (raw === void 0) return { __t: "undefined" };
1208
+ if (raw === null || typeof raw !== "object") return raw;
1209
+ if (Array.isArray(raw)) return raw.map(encode);
1210
+ const sorted = {};
1211
+ for (const k of Object.keys(raw).sort()) sorted[k] = encode(raw[k]);
1212
+ return {
1213
+ __t: "object",
1214
+ value: sorted
1215
+ };
1216
+ };
1217
+ return JSON.stringify(encode(value));
1218
+ }
1219
+ /**
1220
+ * Scopes a load key to one runtime instance for {@link inFlightLoads}.
1221
+ *
1222
+ * The unscoped {@link makeLoadKey} stays the identity used for cycle detection and settled-key
1223
+ * bookkeeping, which are per-load-graph rather than per-runtime.
1224
+ */
1225
+ function makeInFlightKey(loadScopeId, loadKey) {
1226
+ return `${loadScopeId}::${loadKey}`;
1227
+ }
1228
+ function makeLoadKey(serviceId, queryName, validatedInput) {
1229
+ return `${serviceId}::${queryName}::${stableHash(validatedInput)}`;
1230
+ }
1231
+ /**
1232
+ * Surfaces the first rejected settlement as the thrown error, aggregating the rest under `cause`.
1233
+ *
1234
+ * Drainers use `Promise.allSettled` so one rejected dependency does not prevent siblings from
1235
+ * finishing their work; this helper preserves all rejections without losing the primary one.
1236
+ */
1237
+ function surfaceRejections(settlements) {
1238
+ const rejections = settlements.filter((s) => s.status === "rejected");
1239
+ if (rejections.length === 0) return;
1240
+ const [first, ...rest] = rejections.map((r) => r.reason);
1241
+ if (rest.length > 0) {
1242
+ if (first instanceof Error) {
1243
+ if (Reflect.get(first, "cause") === void 0) try {
1244
+ Reflect.set(first, "cause", { aggregated: rest });
1245
+ } catch {}
1246
+ }
1247
+ }
1248
+ throw first;
1249
+ }
1250
+ /**
1251
+ * Drains a collector of pending load promises until no new entries appear.
1252
+ *
1253
+ * Each iteration snapshots the current entries, clears the collector, awaits them with
1254
+ * `Promise.allSettled`, marks their keys as settled (for caller bookkeeping), then loops if the
1255
+ * settled loads' bodies surfaced more entries. Caps at `MAX_DRAIN_ITERATIONS` to avoid hanging on
1256
+ * pathological oscillation.
1257
+ */
1258
+ async function drainCollector(collector, settledKeys, serviceId, queryName) {
1259
+ let iterations = 0;
1260
+ while (collector.size > 0) {
1261
+ if (iterations++ > MAX_DRAIN_ITERATIONS) throw new OpenServiceLoadedDrainExceededError({
1262
+ serviceId,
1263
+ name: queryName,
1264
+ iterations: MAX_DRAIN_ITERATIONS
1265
+ });
1266
+ const pending = [...collector];
1267
+ collector.clear();
1268
+ const settlements = await Promise.allSettled(pending.map((entry) => entry.promise));
1269
+ if (settledKeys) for (const entry of pending) settledKeys.add(entry.key);
1270
+ surfaceRejections(settlements);
1271
+ }
1272
+ }
1273
+ /**
1274
+ * Detaches a value from the reactive deep-signal proxy into a plain snapshot.
1275
+ *
1276
+ * Used for `selector` results on the subscription path: a selector can return a live proxy slice,
1277
+ * which we strip so consumers get plain, comparable data (and reads inside the `computed` register
1278
+ * only the fields the selector actually touched). Primitives pass through untouched.
1279
+ *
1280
+ * `structuredClone` cannot clone a `Proxy`, so this uses a JSON round-trip. That is sufficient
1281
+ * because open-service state is required to be JSON-serializable (the same constraint the
1282
+ * static-build pipeline relies on). For plain (already-detached) values such as the whole-state
1283
+ * snapshot, the runtime uses `structuredClone` directly instead.
1284
+ */
1285
+ function detachSnapshot(value) {
1286
+ if (value === null || typeof value !== "object") return value;
1287
+ return JSON.parse(JSON.stringify(value));
1288
+ }
1289
+ /**
1290
+ * Validates query input synchronously, falling through to the dedicated async-schema error if a
1291
+ * schema returns a Promise.
1292
+ */
1293
+ function validateQueryInput(refs, queryName, queryDef, input) {
1294
+ return validateSchemaSync(queryDef.input, input, {
1295
+ kind: "query",
1296
+ serviceId: refs.serviceId,
1297
+ name: queryName,
1298
+ phase: "input"
1299
+ });
1300
+ }
1301
+ function validateQueryOutput(refs, queryName, queryDef, output) {
1302
+ return validateSchemaSync(queryDef.output, output, {
1303
+ kind: "query",
1304
+ serviceId: refs.serviceId,
1305
+ name: queryName,
1306
+ phase: "output"
1307
+ });
1308
+ }
1309
+ /**
1310
+ * Runs the query handler synchronously and returns its raw result (no output validation).
1311
+ *
1312
+ * The `selfQueries` parameter lets the caller swap in load-aware wrappers when running inside a
1313
+ * load body or a `.loaded()` discovery pass; ordinary handler calls pass the default queries.
1314
+ */
1315
+ function runHandlerSync(refs, queryName, queryDef, validatedInput, selfQueries, getService) {
1316
+ if (!queryDef.handler) throw new OpenServiceUnimplementedOperationError({
1317
+ kind: "query",
1318
+ serviceId: refs.serviceId,
1319
+ name: queryName
1320
+ });
1321
+ const handlerCtx = {
1322
+ self: {
1323
+ get state() {
1324
+ return refs.state;
1325
+ },
1326
+ queries: selfQueries
1327
+ },
1328
+ getService
1329
+ };
1330
+ return queryDef.handler(validatedInput, handlerCtx);
1331
+ }
1332
+ /**
1333
+ * Builds the shared synchronous `get(input)` for a query: validate input → fire the load per the
1334
+ * caller's policy → run the handler against `selfQueries` → validate output.
1335
+ *
1336
+ * The three query surfaces — the default consumer query ({@link createDefaultQuery}), the reactive
1337
+ * subscription-load wrappers ({@link buildReactiveLoadQueries}), and the awaited load-body wrappers
1338
+ * ({@link buildLoadWrappedQueries}) — differ only in (a) which `self.queries` map the handler sees
1339
+ * and (b) how a load-backed read fires its `load`. Everything else (the validate → run → validate
1340
+ * skeleton, the zero-arg input resolution) is identical, so each surface passes its own `selfQueries`
1341
+ * map and `fireLoad` policy and shares this body.
1342
+ *
1343
+ * `fireLoad` is invoked only for queries that declare a `load`; queries without one skip it. Pass
1344
+ * `selfQueries` by reference even when it is the same map being populated by the caller — `get`
1345
+ * closes over the reference, which is fully built by the time any handler runs.
1346
+ */
1347
+ function createQueryGet(refs, queryName, queryDef, selfQueries, fireLoad) {
1348
+ return function get(input) {
1349
+ const validatedInput = validateQueryInput(refs, queryName, queryDef, arguments.length === 0 ? void 0 : input);
1350
+ if (queryDef.load) fireLoad?.(validatedInput);
1351
+ return validateQueryOutput(refs, queryName, queryDef, runHandlerSync(refs, queryName, queryDef, validatedInput, selfQueries, refs.registryApi.getService));
1352
+ };
1353
+ }
1354
+ /**
1355
+ * Triggers a `load` if one is not already in flight for the same key, or returns the existing
1356
+ * promise so concurrent callers share the work.
1357
+ *
1358
+ * Returns the in-flight promise (whether newly created or reused). The body runs through
1359
+ * {@link runLoadBody} and its promise is registered into {@link inFlightLoads} **before** that
1360
+ * body starts; see the "register before run" comment below for why.
1361
+ *
1362
+ * ### Ancestor chain
1363
+ *
1364
+ * The parent caller passes its own ancestor chain (the load keys it is currently nested
1365
+ * inside). This function extends that chain with the dependency's own key before kicking off the
1366
+ * body, so any transitive read of an ancestor — e.g. `a.load` calls `b.load` which calls `a.load`
1367
+ * again — short-circuits via cycle detection in {@link createDefaultQuery} and
1368
+ * {@link buildLoadWrappedQueries}, rather than deadlocking on its own ancestor's promise.
1369
+ *
1370
+ * ### Cycle example
1371
+ *
1372
+ * Service exposes queries `a` and `b`; `a.load` reads `b`, `b.load` reads `a`.
1373
+ *
1374
+ * 1. `a.loaded()` → `triggerLoad(a, parentChain={})` → body runs with chain `{a}`.
1375
+ * 2. `a.load` body calls `ctx.self.queries.b.get(...)` → wrapper calls
1376
+ * `triggerLoad(b, parentChain={a})` → body runs with chain `{a, b}`.
1377
+ * 3. `b.load` body calls `ctx.self.queries.a.get(...)` → wrapper calls
1378
+ * `triggerLoad(a, parentChain={a, b})`. `a` is already in flight, so the existing promise is
1379
+ * reused. But the wrapper sees `aKey ∈ ancestorChain` and **skips** adding it to b's local
1380
+ * collector — `b` does not wait on `a`'s promise.
1381
+ * 4. Both load bodies progress past their sync reads, await their commands, settle.
1382
+ */
1383
+ function triggerLoad(refs, queryName, queryDef, validatedInput, loadKey, parentAncestorChain) {
1384
+ const inFlightKey = makeInFlightKey(refs.loadScopeId, loadKey);
1385
+ const existing = inFlightLoads.get(inFlightKey);
1386
+ if (existing) return existing;
1387
+ const extendedChain = new Set(parentAncestorChain);
1388
+ extendedChain.add(loadKey);
1389
+ const promise = Promise.resolve().then(() => runLoadBody(refs, queryName, queryDef, validatedInput, extendedChain)).finally(() => {
1390
+ if (inFlightLoads.get(inFlightKey) === promise) inFlightLoads.delete(inFlightKey);
1391
+ });
1392
+ inFlightLoads.set(inFlightKey, promise);
1393
+ return promise;
1394
+ }
1395
+ /**
1396
+ * Executes one `load` invocation with its own local collector and a wrapped `self`.
1397
+ *
1398
+ * Each `load` invocation gets its **own** collector and its **own** map of wrapped queries. This
1399
+ * matters because the wrappers close over the collector and ancestor chain that belong to this
1400
+ * particular load — a different load running concurrently for a different key has a different
1401
+ * collector and a different chain, so the same `defaultQuery` cannot serve both.
1402
+ *
1403
+ * The wrapper around `self.queries` registers transitively triggered loads into the local
1404
+ * collector. After the user's load body resolves, we still await that collector via
1405
+ * {@link drainCollector} so the returned promise only resolves once every dependency the load
1406
+ * body touched has also settled. That is what gives `.loaded()` its transitive guarantee through
1407
+ * async load bodies: an outer caller awaiting this load promise is, by construction, also
1408
+ * waiting for all descendant loads triggered by self.queries reads.
1409
+ *
1410
+ * Cross-service `ctx.getService(id).queries.*` calls are intentionally **not** wrapped — that
1411
+ * would require recursively wrapping the registry's runtime services and would entangle load
1412
+ * scoping across service boundaries. Authors must use `.loaded()` explicitly when they need a
1413
+ * cross-service dependency awaited from inside a load body.
1414
+ */
1415
+ async function runLoadBody(refs, queryName, queryDef, validatedInput, ancestorChain) {
1416
+ if (!queryDef.load) return;
1417
+ const collector = /* @__PURE__ */ new Set();
1418
+ const loadCtx = {
1419
+ self: {
1420
+ get state() {
1421
+ return refs.state;
1422
+ },
1423
+ queries: buildLoadWrappedQueries(refs, ancestorChain, collector),
1424
+ commands: refs.getLoadCommands()
1425
+ },
1426
+ getService: refs.registryApi.getService
1427
+ };
1428
+ await Promise.resolve(queryDef.load(validatedInput, loadCtx));
1429
+ await drainCollector(collector, void 0, refs.serviceId, queryName);
1430
+ }
1431
+ /**
1432
+ * Runs a query's `load` as the body of a subscription's reactive effect.
1433
+ *
1434
+ * Unlike {@link runLoadBody}, this is invoked synchronously inside an `effect()`, so the external
1435
+ * signals the load reads in its synchronous prefix are tracked — when they later change, the effect
1436
+ * re-runs and the load re-fires, turning it into a reactive async resource. Loads are therefore an
1437
+ * idempotent warming step (the documented guideline, now a hard contract); one-shot side effects
1438
+ * belong in a command.
1439
+ *
1440
+ * Writes go through gated commands: if `isCurrent()` flips to false because a newer run started
1441
+ * (deps changed again), this run's later writes are dropped, so a slow stale load cannot clobber the
1442
+ * newer result. Sibling reads use the default queries — their loads fire-and-forget — so transitive
1443
+ * dependencies stay warm without the `.loaded()` drain, which is only meaningful for awaited pulls.
1444
+ */
1445
+ async function runReactiveLoad(refs, queryName, queryDef, validatedInput, isCurrent) {
1446
+ if (!queryDef.load) return;
1447
+ const loadCtx = {
1448
+ self: {
1449
+ get state() {
1450
+ return refs.state;
1451
+ },
1452
+ queries: refs.reactiveLoadQueries,
1453
+ commands: refs.buildGatedCommands(isCurrent)
1454
+ },
1455
+ getService: refs.registryApi.getService
1456
+ };
1457
+ await Promise.resolve(queryDef.load(validatedInput, loadCtx));
1458
+ }
1459
+ /**
1460
+ * Builds the `ctx.self.queries` map used inside a *reactive subscription load* body.
1461
+ *
1462
+ * `.get()` here fires the dependency's `load` fire-and-forget (deduped via {@link inFlightLoads}),
1463
+ * scoped to the reactive-load context only, so a plain consumer `.get()` stays a pure read. There is no drain: a subscription
1464
+ * does not await its dependencies, it re-fires reactively when their tracked state changes.
1465
+ */
1466
+ function buildReactiveLoadQueries(refs) {
1467
+ const wrappedQueries = {};
1468
+ for (const [name, queryDef] of refs.queryDefinitions) {
1469
+ const defaultQuery = refs.defaultQueries[name];
1470
+ wrappedQueries[name] = {
1471
+ get: createQueryGet(refs, name, queryDef, wrappedQueries, (validatedInput) => {
1472
+ const loadKey = makeLoadKey(refs.serviceId, name, validatedInput);
1473
+ triggerLoad(refs, name, queryDef, validatedInput, loadKey, EMPTY_SET).catch(rethrowAsync);
1474
+ }),
1475
+ loaded: defaultQuery.loaded,
1476
+ subscribe: defaultQuery.subscribe
1477
+ };
1478
+ }
1479
+ return wrappedQueries;
1480
+ }
1481
+ /**
1482
+ * Builds the wrapped `self.queries` map exposed inside one load body.
1483
+ *
1484
+ * The returned map shadows the runtime's default query map. Each wrapped call still validates
1485
+ * input, fires the dependency's `load` via {@link triggerLoad}, and runs the same sync handler —
1486
+ * but it also **registers the dependency's load promise into the load-local collector** so
1487
+ * `runLoadBody`'s drain can await it before returning. The wrappers therefore turn an ordinary
1488
+ * sync read of a dependency into "fire load + remember it for the outer drain to wait on."
1489
+ *
1490
+ * ### Why one map per load invocation?
1491
+ *
1492
+ * Two separate `load` calls (different keys, possibly different services) have different
1493
+ * `ancestorChain` sets and different `collector` instances. Each wrapped function closes over
1494
+ * those values, so the maps cannot be reused — they are recreated cheaply per invocation. Inside
1495
+ * a single load body, all nested handler reads share **this same** wrapped map (the closure
1496
+ * captures `wrappedQueries` from this scope), so transitive dependency reads continue to register
1497
+ * against the same collector.
1498
+ *
1499
+ * ### Cycle detection
1500
+ *
1501
+ * If the dependency's load key is already on this load's ancestor chain, we still call
1502
+ * `triggerLoad` (it returns the in-flight promise) but we **skip** adding it to the collector.
1503
+ * Adding it would deadlock: the outer load's drain would wait on its own ancestor's promise,
1504
+ * which is itself waiting on this load. See {@link triggerLoad}'s walkthrough for a concrete
1505
+ * `a → b → a` example.
1506
+ *
1507
+ * ### `.loaded()` and `.subscribe` on the wrapped queries
1508
+ *
1509
+ * `.loaded()` on a wrapped query forwards the **current** ancestor chain so a load body author
1510
+ * can write `await ctx.self.queries.foo.loaded(input)` and trust that the resulting drain
1511
+ * inherits the right cycle-protection set. `.subscribe` is passed through unchanged because
1512
+ * subscriptions are never part of a load drain — they have their own lifecycle.
1513
+ */
1514
+ function buildLoadWrappedQueries(refs, ancestorChain, collector) {
1515
+ const wrappedQueries = {};
1516
+ for (const [name, queryDef] of refs.queryDefinitions) {
1517
+ const defaultQuery = refs.defaultQueries[name];
1518
+ wrappedQueries[name] = {
1519
+ get: createQueryGet(refs, name, queryDef, wrappedQueries, (validatedInput) => {
1520
+ const loadKey = makeLoadKey(refs.serviceId, name, validatedInput);
1521
+ const promise = triggerLoad(refs, name, queryDef, validatedInput, loadKey, ancestorChain);
1522
+ if (!ancestorChain.has(loadKey)) collector.add({
1523
+ key: loadKey,
1524
+ promise
1525
+ });
1526
+ }),
1527
+ loaded(input) {
1528
+ return runLoaded(refs, name, queryDef, arguments.length === 0 ? void 0 : input, ancestorChain);
1529
+ },
1530
+ subscribe: defaultQuery.subscribe
1531
+ };
1532
+ }
1533
+ return wrappedQueries;
1534
+ }
1535
+ /**
1536
+ * Implements `query.loaded(input)`.
1537
+ *
1538
+ * Returns a promise that resolves to the validated handler output once this query's `load` and
1539
+ * every dependency the handler transitively reads has settled.
1540
+ *
1541
+ * ### Algorithm
1542
+ *
1543
+ * 1. **Setup** — validate input, build the session (`ancestorChain` extended with this load key,
1544
+ * an empty `collector`, an empty `settledKeys`). The ancestor chain inherits from
1545
+ * `parentAncestorChain` when this is called from inside a wrapped query (so the inner
1546
+ * `.loaded()` shares cycle protection with the outer load body).
1547
+ *
1548
+ * 2. **Fire own load** — if this query has a `load` hook, push its in-flight promise into
1549
+ * `session.collector`. Skip if the key is already on the parent's ancestor chain (we are
1550
+ * inside that load already; we cannot deadlock on ourselves).
1551
+ *
1552
+ * 3. **Drain + discover loop**, up to {@link MAX_DRAIN_ITERATIONS} times:
1553
+ * - Inner loop: while `session.collector` has entries, snapshot them, clear, await with
1554
+ * `Promise.allSettled`, mark their keys in `session.settledKeys`, surface rejections.
1555
+ * - Run the handler synchronously under `activeHandlerLoadSession = session` (a discovery
1556
+ * pass). Sync reads of dependencies inside the handler go through {@link createDefaultQuery}
1557
+ * and register any non-settled, non-ancestor load into `session.collector`.
1558
+ * - If the handler threw, swallow (state might still be partial; a later iteration may fix it).
1559
+ * - If the handler added nothing to the collector, we have converged — exit the loop.
1560
+ *
1561
+ * 4. **Return** — run the handler one final time under the same session (so dependency reads
1562
+ * respect `settledKeys` and do not refire loads) and return the validated output. This is the
1563
+ * value the caller sees. If state is still incomplete at this point the handler may throw, and
1564
+ * that throw propagates.
1565
+ *
1566
+ * ### Worked example: `bar.loaded(input)` where `bar.handler` reads `foo`
1567
+ *
1568
+ * Assume `bar` has no `load` of its own; `foo` does.
1569
+ *
1570
+ * - **Setup**: session = `{ ancestorChain: {barKey}, collector: ∅, settledKeys: ∅ }`. No own load
1571
+ * to fire (`bar.load` is undefined).
1572
+ * - **Iteration 1**:
1573
+ * - Inner drain: collector is empty, skip.
1574
+ * - Discovery pass: handler runs, reads `ctx.self.queries.foo.get(...)`. The default `foo` query
1575
+ * sees `activeHandlerLoadSession === session`, sees that `fooKey` is neither in
1576
+ * `ancestorChain` nor in `settledKeys`, and fires + registers foo's load into
1577
+ * `session.collector`. The handler returns (possibly with stale state).
1578
+ * - `hasMoreWork = true` (collector now has one entry).
1579
+ * - **Iteration 2**:
1580
+ * - Inner drain: `await Promise.allSettled([fooPromise])`, mark `fooKey` settled, surface any
1581
+ * rejection.
1582
+ * - Discovery pass: handler runs again. The default `foo` query is now in `settledKeys`, so
1583
+ * it fires nothing and the collector stays empty.
1584
+ * - `hasMoreWork = false`; exit.
1585
+ * - **Final**: run handler once more (state is now populated by foo's load), validate, return.
1586
+ *
1587
+ * The same machinery handles deeper chains — every settled load may have populated state that
1588
+ * causes the handler to read **more** queries on the next iteration. The loop keeps draining
1589
+ * until the read set stabilizes.
1590
+ */
1591
+ async function runLoaded(refs, queryName, queryDef, rawInput, parentAncestorChain = EMPTY_SET) {
1592
+ const validatedInput = validateQueryInput(refs, queryName, queryDef, rawInput);
1593
+ const loadKey = makeLoadKey(refs.serviceId, queryName, validatedInput);
1594
+ const ancestorChain = new Set(parentAncestorChain);
1595
+ ancestorChain.add(loadKey);
1596
+ const session = {
1597
+ ancestorChain,
1598
+ collector: /* @__PURE__ */ new Set(),
1599
+ settledKeys: /* @__PURE__ */ new Set()
1600
+ };
1601
+ if (queryDef.load && !parentAncestorChain.has(loadKey)) {
1602
+ const promise = triggerLoad(refs, queryName, queryDef, validatedInput, loadKey, parentAncestorChain);
1603
+ session.collector.add({
1604
+ key: loadKey,
1605
+ promise
1606
+ });
1607
+ }
1608
+ let iterations = 0;
1609
+ let hasMoreWork = true;
1610
+ while (hasMoreWork) {
1611
+ if (iterations++ > MAX_DRAIN_ITERATIONS) throw new OpenServiceLoadedDrainExceededError({
1612
+ serviceId: refs.serviceId,
1613
+ name: queryName,
1614
+ iterations: MAX_DRAIN_ITERATIONS
1615
+ });
1616
+ while (session.collector.size > 0) {
1617
+ const pending = [...session.collector];
1618
+ session.collector.clear();
1619
+ const settlements = await Promise.allSettled(pending.map((entry) => entry.promise));
1620
+ for (const entry of pending) session.settledKeys.add(entry.key);
1621
+ surfaceRejections(settlements);
1622
+ }
1623
+ const previousSession = activeHandlerLoadSession;
1624
+ activeHandlerLoadSession = session;
1625
+ try {
1626
+ runHandlerSync(refs, queryName, queryDef, validatedInput, refs.defaultQueries, refs.registryApi.getService);
1627
+ } catch {} finally {
1628
+ activeHandlerLoadSession = previousSession;
1629
+ }
1630
+ hasMoreWork = session.collector.size > 0;
1631
+ }
1632
+ const previousSession = activeHandlerLoadSession;
1633
+ activeHandlerLoadSession = session;
1634
+ try {
1635
+ return validateQueryOutput(refs, queryName, queryDef, runHandlerSync(refs, queryName, queryDef, validatedInput, refs.defaultQueries, refs.registryApi.getService));
1636
+ } finally {
1637
+ activeHandlerLoadSession = previousSession;
1638
+ }
1639
+ }
1640
+ /**
1641
+ * Creates the default query object exposed on the service runtime as `service.queries.foo`.
1642
+ *
1643
+ * The returned object is what consumers use directly **and** what handlers see in
1644
+ * `ctx.self.queries.foo` (load bodies see a different, wrapped version — see
1645
+ * {@link buildLoadWrappedQueries}). Its surface:
1646
+ *
1647
+ * - **`get(input)`** validates input synchronously, runs the handler against current state, and
1648
+ * returns the validated result. It does **not** fire this query's `load` for an ordinary consumer
1649
+ * read. The one exception is dependency tracking: when a `.get()` runs inside a `.loaded()`
1650
+ * discovery pass (`activeHandlerLoadSession` set), it fires + registers the load into the session
1651
+ * collector so the outer drain awaits it (skipping cycles and already-settled keys). Reactive
1652
+ * subscription loads warm dependencies through {@link buildReactiveLoadQueries} instead.
1653
+ * - **`loaded(input)`** drives the full `.loaded()` drain.
1654
+ * - **`subscribe(...)`** sets up a reactive subscription (this is what fires the reactive `load`).
1655
+ */
1656
+ function createDefaultQuery(refs, queryName, queryDef) {
1657
+ const resolveInput = (input, argsLength) => argsLength === 0 ? void 0 : input;
1658
+ const get = createQueryGet(refs, queryName, queryDef, refs.defaultQueries, (validatedInput) => {
1659
+ const session = activeHandlerLoadSession;
1660
+ if (!session) return;
1661
+ const loadKey = makeLoadKey(refs.serviceId, queryName, validatedInput);
1662
+ if (!session.ancestorChain.has(loadKey) && !session.settledKeys.has(loadKey)) {
1663
+ const promise = triggerLoad(refs, queryName, queryDef, validatedInput, loadKey, session.ancestorChain);
1664
+ session.collector.add({
1665
+ key: loadKey,
1666
+ promise
1667
+ });
1668
+ }
1669
+ });
1670
+ const subscribe = ((...args) => {
1671
+ if (args.length === 1 && typeof args[0] === "function") return subscribeToQuery(refs, queryName, queryDef, void 0, void 0, args[0]);
1672
+ if (args.length === 2 && typeof args[0] === "function" && typeof args[1] === "function") return subscribeToQuery(refs, queryName, queryDef, void 0, args[0], args[1]);
1673
+ const [input, selectorOrCallback, maybeCallback] = args;
1674
+ return subscribeToQuery(refs, queryName, queryDef, input, maybeCallback ? selectorOrCallback : void 0, maybeCallback ?? selectorOrCallback);
1675
+ });
1676
+ return {
1677
+ get,
1678
+ loaded(input) {
1679
+ return runLoaded(refs, queryName, queryDef, resolveInput(input, arguments.length));
1680
+ },
1681
+ subscribe
1682
+ };
1683
+ }
1684
+ /**
1685
+ * Subscribes to a query by running its handler inside a deep-signal-aware `computed()` and
1686
+ * notifying through an `effect()`. The callback receives a {@link QueryState} combining the current
1687
+ * `data` (the synchronous handler result, or the selected slice) with the per-subscription `load`
1688
+ * lifecycle (`status` / `loadStatus` / `error`).
1689
+ *
1690
+ * The first emission is **synchronous**: `subscribe()` invokes the callback once with the current
1691
+ * {@link QueryState} before it returns the unsubscribe handle. The runtime kicks `load` off in the
1692
+ * background but does not wait for it — subscribers see the current state immediately (a load-backed
1693
+ * query already reads `loading`, since the reactive-load effect runs synchronously here) and
1694
+ * follow-up emissions as the load settles (or re-fires) and tracked state changes.
1695
+ *
1696
+ * Three layers keep emissions precise:
1697
+ *
1698
+ * 1. **Fine-grained reads** — the handler (and the optional `selector`) read through the deep-signal
1699
+ * proxy, so the computed only re-runs when the exact fields it touched change. A write to an
1700
+ * unrelated key or field never re-runs the handler.
1701
+ * 2. **Lifecycle signal** — a separate per-subscription signal carries `status`/`loadStatus`/`error`,
1702
+ * updated by the reactive-load effect. The emit effect reads both it and the data computed, so a
1703
+ * status change (e.g. a background re-load) fires the callback even when the selected slice is
1704
+ * unchanged.
1705
+ * 3. **Object dedup** — the whole emitted `QueryState` is compared with the previously emitted one via
1706
+ * `isEqual`; a re-run that produces a deeply-equal state (e.g. a load rewriting an equal payload
1707
+ * with no status change) does not fire the callback.
1708
+ *
1709
+ * Error sources all surface as `status: 'error'` (keeping the last successful `data`): input
1710
+ * validation failure, a synchronous handler / output-validation throw, and a `load` rejection.
1711
+ */
1712
+ function subscribeToQuery(refs, queryName, queryDef, rawInput, selector, callback) {
1713
+ let active = true;
1714
+ let validatedInput;
1715
+ try {
1716
+ validatedInput = validateQueryInput(refs, queryName, queryDef, rawInput);
1717
+ } catch (error) {
1718
+ callback(buildQueryState(void 0, {
1719
+ status: "error",
1720
+ error: toError(error),
1721
+ loadStatus: "idle"
1722
+ }));
1723
+ return () => {
1724
+ active = false;
1725
+ };
1726
+ }
1727
+ const lifecycle = y({
1728
+ status: queryDef.load ? "pending" : "success",
1729
+ error: void 0,
1730
+ loadStatus: "idle"
1731
+ });
1732
+ let loadTeardown;
1733
+ if (queryDef.load) {
1734
+ let epoch = 0;
1735
+ loadTeardown = j(() => {
1736
+ const myEpoch = ++epoch;
1737
+ const isCurrent = () => myEpoch === epoch;
1738
+ const previous = f(() => lifecycle.value);
1739
+ lifecycle.value = {
1740
+ status: previous.status,
1741
+ error: previous.error,
1742
+ loadStatus: "loading"
1743
+ };
1744
+ runReactiveLoad(refs, queryName, queryDef, validatedInput, isCurrent).then(() => {
1745
+ if (!isCurrent()) return;
1746
+ lifecycle.value = {
1747
+ status: "success",
1748
+ error: void 0,
1749
+ loadStatus: "idle"
1750
+ };
1751
+ }, (error) => {
1752
+ if (!isCurrent()) return;
1753
+ lifecycle.value = {
1754
+ status: "error",
1755
+ error: toError(error),
1756
+ loadStatus: "idle"
1757
+ };
1758
+ });
1759
+ });
1760
+ }
1761
+ const comp = g$1(() => {
1762
+ const output = runHandlerSync(refs, queryName, queryDef, validatedInput, refs.defaultQueries, refs.registryApi.getService);
1763
+ if (selector) {
1764
+ const validated = f(() => validateQueryOutput(refs, queryName, queryDef, output));
1765
+ selector(output);
1766
+ return detachSnapshot(selector(validated));
1767
+ }
1768
+ return detachSnapshot(validateQueryOutput(refs, queryName, queryDef, output));
1769
+ });
1770
+ let hasEmitted = false;
1771
+ let lastEmitted;
1772
+ let lastData;
1773
+ const teardown = j(() => {
1774
+ const life = lifecycle.value;
1775
+ let data;
1776
+ let status = life.status;
1777
+ let error = life.error;
1778
+ try {
1779
+ data = comp.value;
1780
+ lastData = data;
1781
+ } catch (handlerError) {
1782
+ data = lastData;
1783
+ status = "error";
1784
+ error = toError(handlerError);
1785
+ }
1786
+ if (!active) return;
1787
+ const state = buildQueryState(data, {
1788
+ status,
1789
+ error,
1790
+ loadStatus: life.loadStatus
1791
+ });
1792
+ if (hasEmitted && isEqual(state, lastEmitted)) return;
1793
+ hasEmitted = true;
1794
+ lastEmitted = state;
1795
+ callback(state);
1796
+ });
1797
+ return () => {
1798
+ active = false;
1799
+ teardown();
1800
+ loadTeardown?.();
1801
+ };
1802
+ }
1803
+ /** Builds the runtime query map for one service runtime. */
1804
+ function buildQueries(refs) {
1805
+ const result = {};
1806
+ for (const [name, queryDef] of refs.queryDefinitions) result[name] = createDefaultQuery(refs, name, queryDef);
1807
+ return result;
1808
+ }
1809
+ var MAX_DRAIN_ITERATIONS, inFlightLoads, nextLoadScopeSequence, nextLoadScopeId, activeHandlerLoadSession, EMPTY_SET;
1810
+ function init_query_runtime() {
1811
+ return (init_query_runtime = __esmMin((() => {
1812
+ init_signals_core_module();
1813
+ init_isEqual();
1814
+ init_server_errors();
1815
+ init_service_validation();
1816
+ MAX_DRAIN_ITERATIONS = 32;
1817
+ inFlightLoads = /* @__PURE__ */ new Map();
1818
+ nextLoadScopeSequence = 0;
1819
+ nextLoadScopeId = () => String(nextLoadScopeSequence += 1);
1820
+ EMPTY_SET = /* @__PURE__ */ new Set();
1821
+ })))();
1822
+ }
1823
+ //#endregion
1824
+ //#region src/shared/open-service/json-patch.ts
1825
+ function parentAt(root, segments) {
1826
+ let current = root;
1827
+ for (let index = 0; index < segments.length - 1; index += 1) {
1828
+ const segment = segments[index];
1829
+ if (isReservedKey(segment) || !isPlainObject$1(current) || !hasOwn(current, segment)) return;
1830
+ current = current[segment];
1831
+ }
1832
+ if (!isPlainObject$1(current)) return;
1833
+ return {
1834
+ parent: current,
1835
+ key: segments[segments.length - 1]
1836
+ };
1837
+ }
1838
+ /**
1839
+ * Apply an RFC 6902 document onto `target` in place.
1840
+ *
1841
+ * `add` and `replace` both upsert. `remove` of a missing key calls `onMissingRemove` and continues.
1842
+ * Incoming values are cloned. On success, `inverse` is reverse apply order. A missing parent, reserved
1843
+ * key, or invalid pointer rolls back every op already applied in this call and returns
1844
+ * `{ ok: false, path }` for that pointer. Any other throw also rolls back and reports
1845
+ * `{ ok: false, path }` for the op that threw.
1846
+ */
1847
+ function applyJsonPatch(target, patch, onMissingRemove) {
1848
+ const undo = [];
1849
+ const inverse = [];
1850
+ let currentPath = "";
1851
+ try {
1852
+ for (const operation of patch) {
1853
+ currentPath = operation.path;
1854
+ applyOne(target, operation, undo, inverse, onMissingRemove);
1855
+ }
1856
+ return {
1857
+ ok: true,
1858
+ inverse: inverse.toReversed()
1859
+ };
1860
+ } catch (error) {
1861
+ for (const revert of undo.toReversed()) revert();
1862
+ if (error instanceof MissingParentError) return {
1863
+ ok: false,
1864
+ path: error.path
1865
+ };
1866
+ return {
1867
+ ok: false,
1868
+ path: currentPath
1869
+ };
1870
+ }
1871
+ }
1872
+ function applyOne(target, operation, undo, inverse, onMissingRemove) {
1873
+ if (!isRfc6901Pointer(operation.path)) throw new MissingParentError(operation.path);
1874
+ const location = parentAt(target, decodePointer(operation.path));
1875
+ if (!location || isReservedKey(location.key)) throw new MissingParentError(operation.path);
1876
+ const { parent, key } = location;
1877
+ switch (operation.op) {
1878
+ case "add":
1879
+ case "replace": {
1880
+ const existed = hasOwn(parent, key);
1881
+ const previous = existed ? clonePlain(parent[key]) : void 0;
1882
+ parent[key] = clonePlain(operation.value);
1883
+ undo.push(() => {
1884
+ if (existed) parent[key] = previous;
1885
+ else delete parent[key];
1886
+ });
1887
+ if (existed) inverse.push({
1888
+ op: "replace",
1889
+ path: operation.path,
1890
+ value: previous
1891
+ });
1892
+ else inverse.push({
1893
+ op: "remove",
1894
+ path: operation.path
1895
+ });
1896
+ return;
1897
+ }
1898
+ case "remove": {
1899
+ if (!hasOwn(parent, key)) {
1900
+ onMissingRemove(operation.path);
1901
+ return;
1902
+ }
1903
+ const previous = clonePlain(parent[key]);
1904
+ delete parent[key];
1905
+ undo.push(() => {
1906
+ parent[key] = previous;
1907
+ });
1908
+ inverse.push({
1909
+ op: "add",
1910
+ path: operation.path,
1911
+ value: previous
1912
+ });
1913
+ return;
1914
+ }
1915
+ }
1916
+ }
1917
+ var MissingParentError;
1918
+ function init_json_patch() {
1919
+ return (init_json_patch = __esmMin((() => {
1920
+ init_plain_object();
1921
+ init_service_channel();
1922
+ MissingParentError = class extends Error {
1923
+ constructor(path) {
1924
+ super(`Missing parent for JSON Patch path ${path}`);
1925
+ this.name = "MissingParentError";
1926
+ this.path = path;
1927
+ }
1928
+ };
1929
+ })))();
1930
+ }
1931
+ //#endregion
1932
+ //#region src/shared/open-service/service-sync.ts
1933
+ function vectorCounter(vector, runtimeId) {
1934
+ return vector[runtimeId] ?? 0;
1935
+ }
1936
+ function isEmptyVector(vector) {
1937
+ for (const value of Object.values(vector)) if (value > 0) return false;
1938
+ return true;
1939
+ }
1940
+ /**
1941
+ * Returns whether `left` dominates `right`.
1942
+ *
1943
+ * Every counter in `left` is at least the matching counter in `right`, and at least one is
1944
+ * greater. An empty `right` is dominated by any non-empty `left`, so a fresh joiner hears from
1945
+ * every peer that has writes. Two empty vectors do not dominate each other.
1946
+ */
1947
+ function vectorDominates(left, right) {
1948
+ if (isEmptyVector(right)) return !isEmptyVector(left);
1949
+ const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
1950
+ let greater = false;
1951
+ for (const key of keys) {
1952
+ const leftCount = vectorCounter(left, key);
1953
+ const rightCount = vectorCounter(right, key);
1954
+ if (leftCount < rightCount) return false;
1955
+ if (leftCount > rightCount) greater = true;
1956
+ }
1957
+ return greater;
1958
+ }
1959
+ function vectorsEqual(left, right) {
1960
+ const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
1961
+ for (const key of keys) if (vectorCounter(left, key) !== vectorCounter(right, key)) return false;
1962
+ return true;
1963
+ }
1964
+ function vectorsConcurrent(left, right) {
1965
+ return !vectorDominates(left, right) && !vectorDominates(right, left) && !vectorsEqual(left, right);
1966
+ }
1967
+ function formatFrontier(frontier) {
1968
+ const body = Object.keys(frontier.vector).sort().map((key) => `${key}:${frontier.vector[key]}`).join(",");
1969
+ return `{clock:${frontier.clock} vector:{${body}}}`;
1970
+ }
1971
+ /**
1972
+ * Canonical entry order: ascending `seq`, then ascending `runtimeId` with plain string comparison.
1973
+ *
1974
+ * "Later in the log", "greater stamp", and "wins the path" coincide.
1975
+ */
1976
+ function compareStamps(left, right) {
1977
+ if (left.seq !== right.seq) return left.seq < right.seq ? -1 : 1;
1978
+ if (left.runtimeId !== right.runtimeId) return left.runtimeId < right.runtimeId ? -1 : 1;
1979
+ return 0;
1980
+ }
1981
+ /**
1982
+ * Applies `source` onto `target` in place while preserving object identity for unchanged branches.
1983
+ *
1984
+ * Open-service runtimes expose their state through deep-signal proxies. Replacing the whole state
1985
+ * object for every incoming snapshot would invalidate all subscriptions, even when only one nested
1986
+ * field changed. This helper instead walks both objects and mutates `target` only where values
1987
+ * differ:
1988
+ *
1989
+ * - Recurses into plain objects so nested deep-signal subscriptions stay attached.
1990
+ * - Replaces arrays wholesale, matching the sync contract that arrays are values rather than maps.
1991
+ * - Assigns primitives only when changed to avoid spurious signal invalidation.
1992
+ * - Copies `source` through `clonePlain` first, like every other value on its way into state, so
1993
+ * untrusted channel payloads and static files cannot pollute prototypes or write deepsignal's
1994
+ * `$` accessors, and state never shares an object with the payload.
1995
+ *
1996
+ * The `preserveMissingKeys` mode selects the source contract:
1997
+ *
1998
+ * - `false` means `source` is a full peer snapshot. Keys missing from `source` are deleted from
1999
+ * `target`, allowing deletions to propagate through cross-peer sync.
2000
+ * - `true` means `source` is a partial static snapshot. Keys missing from `source` are left alone
2001
+ * so snapshots for one static query input do not erase state populated by other inputs.
2002
+ */
2003
+ function applyStatePatch(target, source, options) {
2004
+ mergeInto(target, clonePlain(source), options.preserveMissingKeys);
2005
+ }
2006
+ function mergeInto(target, source, preserveMissingKeys) {
2007
+ if (!preserveMissingKeys) {
2008
+ for (const key of Object.keys(target)) if (!hasOwn(source, key)) delete target[key];
2009
+ }
2010
+ for (const key of Object.keys(source)) {
2011
+ const sourceValue = source[key];
2012
+ const targetValue = target[key];
2013
+ if (isPlainObject$1(sourceValue) && isPlainObject$1(targetValue)) mergeInto(targetValue, sourceValue, preserveMissingKeys);
2014
+ else if (targetValue !== sourceValue) target[key] = sourceValue;
2015
+ }
2016
+ }
2017
+ /**
2018
+ * Builds a {@link Reconciler} bound to one runtime's state.
2019
+ *
2020
+ * `setState` is the runtime's `applyLocal` adapted to a plain record, so placing and installing never
2021
+ * author an entry. `serviceId` names the service in warnings. `window` bounds the Log.
2022
+ */
2023
+ function createReconciler(options) {
2024
+ const { serviceId, setState } = options;
2025
+ const logWindow = {
2026
+ maxAgeMs: options.window?.maxAgeMs ?? 15e3,
2027
+ maxEntries: options.window?.maxEntries ?? 256
2028
+ };
2029
+ let clock = 0;
2030
+ let snapshotSeq = 0;
2031
+ const vector = /* @__PURE__ */ new Map();
2032
+ const log = [];
2033
+ const logKeys = /* @__PURE__ */ new Set();
2034
+ const dropped = /* @__PURE__ */ new Set();
2035
+ let floor;
2036
+ const vectorRecord = () => Object.fromEntries(vector);
2037
+ const vectorOf = (runtimeId) => vector.get(runtimeId) ?? 0;
2038
+ const hasStamp = (stamp) => logKeys.has(entryStampKey(stamp));
2039
+ const silentMissingRemove = () => void 0;
2040
+ const patchPaths = (patch) => patch.map((operation) => operation.path).join(",");
2041
+ const undoEntry = (current, entry) => {
2042
+ const result = applyJsonPatch(current, entry.inverse, silentMissingRemove);
2043
+ if (!result.ok) logger.warn(`Open-service sync: undo failed. service=${serviceId} stamp=${entryStampKey(entry.stamp)} path=${result.path} command=${entry.command}`);
2044
+ };
2045
+ const redoEntry = (current, entry) => {
2046
+ const result = applyJsonPatch(current, entry.patch, silentMissingRemove);
2047
+ entry.inverse = result.ok ? result.inverse : [];
2048
+ if (!result.ok) logger.warn(`Open-service sync: replay failed. service=${serviceId} stamp=${entryStampKey(entry.stamp)} path=${result.path} command=${entry.command}`);
2049
+ };
2050
+ const undoToIndex = (current, index) => {
2051
+ const undone = [];
2052
+ for (let cursor = log.length - 1; cursor >= index; cursor -= 1) {
2053
+ const entry = log[cursor];
2054
+ undoEntry(current, entry);
2055
+ undone.push(entry);
2056
+ }
2057
+ return undone;
2058
+ };
2059
+ const redoUndone = (current, undone) => {
2060
+ for (let cursor = undone.length - 1; cursor >= 0; cursor -= 1) redoEntry(current, undone[cursor]);
2061
+ };
2062
+ const insertIndexFor = (stamp) => {
2063
+ let index = 0;
2064
+ while (index < log.length && compareStamps(log[index].stamp, stamp) < 0) index += 1;
2065
+ return index;
2066
+ };
2067
+ const tryAdvanceVector = (stamp) => {
2068
+ if (stamp.counter !== vectorOf(stamp.runtimeId) + 1) return;
2069
+ const counters = /* @__PURE__ */ new Set();
2070
+ for (const entry of log) if (entry.stamp.runtimeId === stamp.runtimeId) counters.add(entry.stamp.counter);
2071
+ let next = stamp.counter;
2072
+ while (counters.has(next + 1)) next += 1;
2073
+ vector.set(stamp.runtimeId, next);
2074
+ };
2075
+ const evict = (now) => {
2076
+ const minKeepIndex = Math.max(0, log.length - logWindow.maxEntries);
2077
+ const kept = [];
2078
+ for (let index = 0; index < log.length; index += 1) {
2079
+ const entry = log[index];
2080
+ const amongNewest = index >= minKeepIndex;
2081
+ const young = now - entry.appliedAt < logWindow.maxAgeMs;
2082
+ if (amongNewest || young) kept.push(entry);
2083
+ else {
2084
+ logKeys.delete(entryStampKey(entry.stamp));
2085
+ if (floor === void 0 || compareStamps(entry.stamp, floor) > 0) floor = entry.stamp;
2086
+ }
2087
+ }
2088
+ if (kept.length === log.length) return;
2089
+ log.length = 0;
2090
+ log.push(...kept);
2091
+ };
2092
+ const remember = (entry) => {
2093
+ logKeys.add(entryStampKey(entry.stamp));
2094
+ tryAdvanceVector(entry.stamp);
2095
+ evict(entry.appliedAt);
2096
+ };
2097
+ const appendOrInsert = (entry, index) => {
2098
+ if (index === log.length) log.push(entry);
2099
+ else log.splice(index, 0, entry);
2100
+ remember(entry);
2101
+ };
2102
+ const advanceClock = (seq) => {
2103
+ if (seq > clock) clock = seq;
2104
+ };
2105
+ return {
2106
+ get clock() {
2107
+ return clock;
2108
+ },
2109
+ get vector() {
2110
+ return vectorRecord();
2111
+ },
2112
+ get frontier() {
2113
+ return {
2114
+ vector: vectorRecord(),
2115
+ clock
2116
+ };
2117
+ },
2118
+ get log() {
2119
+ return log.map(({ stamp, command, patch }) => ({
2120
+ stamp,
2121
+ command,
2122
+ patch
2123
+ }));
2124
+ },
2125
+ has(stamp) {
2126
+ return hasStamp(stamp);
2127
+ },
2128
+ advanceLocal(runtimeId, authored) {
2129
+ const stamp = {
2130
+ seq: clock + 1,
2131
+ runtimeId,
2132
+ counter: vectorOf(runtimeId) + 1
2133
+ };
2134
+ advanceClock(stamp.seq);
2135
+ const now = Date.now();
2136
+ const entry = {
2137
+ stamp,
2138
+ command: authored.command,
2139
+ patch: [...authored.patch],
2140
+ inverse: [...authored.inverse],
2141
+ appliedAt: now
2142
+ };
2143
+ appendOrInsert(entry, log.length);
2144
+ return stamp;
2145
+ },
2146
+ tryInstall(frontier, state) {
2147
+ advanceClock(frontier.clock);
2148
+ const localVector = vectorRecord();
2149
+ if (!vectorDominates(frontier.vector, localVector)) return vectorsConcurrent(frontier.vector, localVector) ? "concurrent" : "not-ahead";
2150
+ snapshotSeq = frontier.clock;
2151
+ const remaining = [];
2152
+ for (const entry of log) {
2153
+ if (entry.stamp.counter <= vectorCounter(frontier.vector, entry.stamp.runtimeId)) continue;
2154
+ if (entry.stamp.seq <= frontier.clock) {
2155
+ logger.warn(`Open-service sync: uncovered entry at or below the reply clock dropped on install. service=${serviceId} stamp=${entryStampKey(entry.stamp)} clock=${frontier.clock} paths=${patchPaths(entry.patch)} command=${entry.command}`);
2156
+ continue;
2157
+ }
2158
+ remaining.push(entry);
2159
+ }
2160
+ setState((current) => {
2161
+ applyStatePatch(current, state, { preserveMissingKeys: false });
2162
+ logKeys.clear();
2163
+ dropped.clear();
2164
+ log.length = 0;
2165
+ floor = void 0;
2166
+ vector.clear();
2167
+ for (const [runtimeId, counter] of Object.entries(frontier.vector)) if (counter > 0) vector.set(runtimeId, counter);
2168
+ for (const entry of remaining) {
2169
+ const replayed = { ...entry };
2170
+ redoEntry(current, replayed);
2171
+ log.push(replayed);
2172
+ remember(replayed);
2173
+ }
2174
+ });
2175
+ return "installed";
2176
+ },
2177
+ tryPlaceEntry(incoming) {
2178
+ const { stamp, command, patch } = incoming;
2179
+ const key = entryStampKey(stamp);
2180
+ advanceClock(stamp.seq);
2181
+ if (hasStamp(stamp) || dropped.has(key) || stamp.counter <= vectorOf(stamp.runtimeId)) return "duplicate";
2182
+ if (stamp.seq <= snapshotSeq || floor && compareStamps(stamp, floor) <= 0) {
2183
+ const floorLabel = floor ? `,${entryStampKey(floor)}` : "";
2184
+ logger.warn(`Open-service sync: entry beyond the log window. service=${serviceId} stamps=${key}${floorLabel} paths=${patchPaths(patch)} command=${command}`);
2185
+ dropped.add(key);
2186
+ return "beyond-window";
2187
+ }
2188
+ const gap = stamp.counter > vectorOf(stamp.runtimeId) + 1;
2189
+ const index = insertIndexFor(stamp);
2190
+ const now = Date.now();
2191
+ let failedPath;
2192
+ setState((current) => {
2193
+ let inverse = [];
2194
+ const undone = index === log.length ? [] : undoToIndex(current, index);
2195
+ const result = applyJsonPatch(current, patch, (path) => {
2196
+ logger.debug(`Open-service sync: remove of missing key. service=${serviceId} stamp=${key} path=${path} command=${command}`);
2197
+ });
2198
+ if (result.ok) inverse = result.inverse;
2199
+ else failedPath = result.path;
2200
+ redoUndone(current, undone);
2201
+ appendOrInsert({
2202
+ stamp,
2203
+ command,
2204
+ patch: [...patch],
2205
+ inverse,
2206
+ appliedAt: now
2207
+ }, index);
2208
+ });
2209
+ if (failedPath !== void 0) logger.warn(`Open-service sync: entry did not apply, kept as a no-op. service=${serviceId} stamp=${key} path=${failedPath} command=${command}`);
2210
+ if (gap) logger.warn(`Open-service sync: gap in writer counters. service=${serviceId} stamps=${key} paths=${patchPaths(patch)} command=${command}`);
2211
+ return gap ? "gap" : failedPath !== void 0 ? "unapplied" : "accepted";
2212
+ }
2213
+ };
2214
+ }
2215
+ function init_service_sync() {
2216
+ return (init_service_sync = __esmMin((() => {
2217
+ init_client_logger();
2218
+ init_json_patch();
2219
+ init_plain_object();
2220
+ init_service_channel();
2221
+ })))();
2222
+ }
2223
+ //#endregion
2224
+ //#region src/shared/open-service/service-runtime.ts
2225
+ /**
2226
+ * Resolves which serialized static-state file should back a query input.
2227
+ *
2228
+ * The returned value is a logical slash-separated store key scoped under the service id, not a raw
2229
+ * filesystem path.
2230
+ */
2231
+ function normalizeStaticStoragePath(serviceId, name, rawPath) {
2232
+ const segments = rawPath.replaceAll("\\", "/").split("/").filter((segment) => segment.length > 0 && segment !== ".");
2233
+ if (segments.length === 0 || segments.some((segment) => segment === "..")) throw new OpenServiceInvalidStaticPathError({
2234
+ serviceId,
2235
+ name,
2236
+ path: rawPath
2237
+ });
2238
+ return segments.join("/");
2239
+ }
2240
+ function resolveStaticPath(serviceId, name, queryDef, input) {
2241
+ return `${serviceId}/${normalizeStaticStoragePath(serviceId, name, queryDef.staticPath(input))}`;
2242
+ }
2243
+ /**
2244
+ * Builds the runtime command map from the declarative command definitions.
2245
+ *
2246
+ * Each runtime command validates raw caller input, invokes the handler with parsed values, and
2247
+ * validates the resolved output before returning it to the caller.
2248
+ */
2249
+ function buildCommands(serviceId, commands, createCommandCtx) {
2250
+ return Object.fromEntries(Object.entries(commands).map(([name, def]) => {
2251
+ return [name, async (input) => {
2252
+ if (!def.handler) throw new OpenServiceUnimplementedOperationError({
2253
+ kind: "command",
2254
+ serviceId,
2255
+ name
2256
+ });
2257
+ const validatedInput = await validateSchema(def.input, input, {
2258
+ kind: "command",
2259
+ serviceId,
2260
+ name,
2261
+ phase: "input"
2262
+ });
2263
+ const output = await def.handler(validatedInput, createCommandCtx(name));
2264
+ return validateSchema(def.output, output, {
2265
+ kind: "command",
2266
+ serviceId,
2267
+ name,
2268
+ phase: "output"
2269
+ });
2270
+ }];
2271
+ }));
2272
+ }
2273
+ /**
2274
+ * When a browser static loader is active, queries with `staticPath` fetch prebuilt JSON instead of
2275
+ * running their authored `load` hook (which typically invokes server-only commands).
2276
+ */
2277
+ function buildQueryDefinitionsWithStaticLoader(serviceId, queries, staticLoader, applyLocal) {
2278
+ return new Map(Object.entries(queries).map(([name, queryDef]) => {
2279
+ if (!queryDef.staticPath) return [name, queryDef];
2280
+ const { staticPath } = queryDef;
2281
+ return [name, {
2282
+ ...queryDef,
2283
+ load: async (input) => {
2284
+ const snapshot = await staticLoader(resolveStaticPath(serviceId, name, { staticPath }, input), {
2285
+ serviceId,
2286
+ queryName: name,
2287
+ input
2288
+ });
2289
+ applyLocal((state) => {
2290
+ applyStatePatch(state, snapshot, { preserveMissingKeys: true });
2291
+ });
2292
+ }
2293
+ }];
2294
+ }));
2295
+ }
2296
+ /**
2297
+ * Creates the full runtime backing for a service definition.
2298
+ *
2299
+ * Callers must supply the registry API that query and command contexts should expose.
2300
+ */
2301
+ function createServiceRuntime(def, runtimeOptions, initialState = def.initialState) {
2302
+ const rawState = clonePlain(initialState);
2303
+ const state = l(rawState);
2304
+ const getStateSnapshot = () => structuredClone(rawState);
2305
+ const { registryApi, staticLoader } = runtimeOptions;
2306
+ let author = () => {};
2307
+ const applyLocal = (mutate) => {
2308
+ n(() => {
2309
+ mutate(state);
2310
+ });
2311
+ };
2312
+ const writeState = (command, mutate) => {
2313
+ recordPatch(state, mutate, (recorded) => author({
2314
+ command,
2315
+ ...recorded
2316
+ }));
2317
+ };
2318
+ const defaultQueries = {};
2319
+ const reactiveLoadQueries = {};
2320
+ const createCommandSelf = (setState, commandsForSelf) => ({
2321
+ get state() {
2322
+ return state;
2323
+ },
2324
+ setState,
2325
+ queries: defaultQueries,
2326
+ get commands() {
2327
+ return commandsForSelf();
2328
+ }
2329
+ });
2330
+ const commands = buildCommands(def.id, def.commands, (commandName) => ({
2331
+ self: createCommandSelf((mutate) => writeState(commandName, mutate), () => commands),
2332
+ getService: registryApi.getService
2333
+ }));
2334
+ let loadCommands = commands;
2335
+ const remoteCommandNames = /* @__PURE__ */ new Set();
2336
+ const queryDefinitions = staticLoader ? buildQueryDefinitionsWithStaticLoader(def.id, def.queries, staticLoader, applyLocal) : new Map(Object.entries(def.queries));
2337
+ const buildGatedCommands = (isCurrent) => {
2338
+ const gated = buildCommands(def.id, def.commands, (commandName) => ({
2339
+ self: createCommandSelf((mutate) => {
2340
+ if (isCurrent()) writeState(commandName, mutate);
2341
+ }, () => gated),
2342
+ getService: registryApi.getService
2343
+ }));
2344
+ if (remoteCommandNames.size === 0) return gated;
2345
+ return Object.fromEntries(Object.keys(def.commands).map((name) => [name, remoteCommandNames.has(name) ? loadCommands[name] : gated[name]]));
2346
+ };
2347
+ const loadScopeId = nextLoadScopeId();
2348
+ const refs = {
2349
+ serviceId: def.id,
2350
+ loadScopeId,
2351
+ state,
2352
+ registryApi,
2353
+ queryDefinitions,
2354
+ defaultQueries,
2355
+ reactiveLoadQueries,
2356
+ getLoadCommands: () => loadCommands,
2357
+ buildGatedCommands
2358
+ };
2359
+ const builtQueries = buildQueries(refs);
2360
+ for (const [name, query] of Object.entries(builtQueries)) defaultQueries[name] = query;
2361
+ for (const [name, query] of Object.entries(buildReactiveLoadQueries(refs))) reactiveLoadQueries[name] = query;
2362
+ const queries = defaultQueries;
2363
+ const queryCtx = {
2364
+ self: {
2365
+ get state() {
2366
+ return state;
2367
+ },
2368
+ queries: defaultQueries
2369
+ },
2370
+ getService: registryApi.getService
2371
+ };
2372
+ const loadCtxForStatic = {
2373
+ self: {
2374
+ get state() {
2375
+ return state;
2376
+ },
2377
+ queries: defaultQueries,
2378
+ commands
2379
+ },
2380
+ getService: registryApi.getService
2381
+ };
2382
+ /**
2383
+ * Runs one query's `load` body against this runtime instance, drained to completion.
2384
+ *
2385
+ * Used by the static build pipeline to populate state for a single input without holding the
2386
+ * load in the in-flight registry afterwards.
2387
+ */
2388
+ const runLoadOnce = async (queryName, validatedInput) => {
2389
+ const queryDef = queryDefinitions.get(queryName);
2390
+ if (!queryDef || !queryDef.load) return;
2391
+ const loadKey = makeLoadKey(def.id, queryName, validatedInput);
2392
+ const ancestorChain = /* @__PURE__ */ new Set([loadKey]);
2393
+ const inFlightKey = makeInFlightKey(loadScopeId, loadKey);
2394
+ const promise = Promise.resolve().then(() => runLoadBody(refs, queryName, queryDef, validatedInput, ancestorChain)).finally(() => {
2395
+ if (inFlightLoads.get(inFlightKey) === promise) inFlightLoads.delete(inFlightKey);
2396
+ });
2397
+ inFlightLoads.set(inFlightKey, promise);
2398
+ await promise;
2399
+ };
2400
+ const attachChannelCommands = (channelCommands, implementedCommandNames) => {
2401
+ loadCommands = channelCommands;
2402
+ remoteCommandNames.clear();
2403
+ for (const name of Object.keys(def.commands)) if (!implementedCommandNames.has(name)) remoteCommandNames.add(name);
2404
+ };
2405
+ return {
2406
+ getStateSnapshot,
2407
+ applyLocal,
2408
+ attachEntryAuthor: (next) => {
2409
+ author = next;
2410
+ },
2411
+ queryCtx,
2412
+ loadCtxForStatic,
2413
+ commands,
2414
+ queries,
2415
+ runLoadOnce,
2416
+ attachChannelCommands
2417
+ };
2418
+ }
2419
+ function init_service_runtime() {
2420
+ return (init_service_runtime = __esmMin((() => {
2421
+ init_signals_core_module();
2422
+ init_deepsignal_core_module();
2423
+ init_server_errors();
2424
+ init_patch_recorder();
2425
+ init_plain_object();
2426
+ init_query_runtime();
2427
+ init_service_sync();
2428
+ init_service_validation();
2429
+ })))();
2430
+ }
2431
+ //#endregion
2432
+ //#region src/shared/open-service/service-error-serialization.ts
2433
+ function isPlainObject(value) {
2434
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2435
+ }
2436
+ function isSerializedError(value) {
2437
+ return isPlainObject(value) && value[ERROR_MARKER] === true;
2438
+ }
2439
+ /**
2440
+ * Reduces any value to one a channel transport can clone.
2441
+ *
2442
+ * Functions, symbols, and `undefined` are dropped (structuredClone throws on functions/symbols);
2443
+ * `bigint` is stringified; plain objects and arrays are walked; `Error`s become {@link SerializedError}.
2444
+ */
2445
+ function toTransportSafe(value) {
2446
+ if (value instanceof Error) return serializeError(value);
2447
+ if (Array.isArray(value)) return value.map((entry) => toTransportSafe(entry));
2448
+ if (isPlainObject(value)) {
2449
+ const result = {};
2450
+ for (const [key, entry] of Object.entries(value)) result[key] = toTransportSafe(entry);
2451
+ return result;
2452
+ }
2453
+ if (value === null) return null;
2454
+ const kind = typeof value;
2455
+ if (kind === "string" || kind === "number" || kind === "boolean") return value;
2456
+ if (kind === "bigint") return value.toString();
2457
+ }
2458
+ /**
2459
+ * Converts a thrown value into a transport-safe {@link SerializedError}.
2460
+ *
2461
+ * Non-`Error` throws (a string, an object, …) are wrapped in a synthetic error whose message is the
2462
+ * stringified value, so the requester always receives a real `Error` to reject with.
2463
+ */
2464
+ function serializeError(value) {
2465
+ if (!(value instanceof Error)) return {
2466
+ [ERROR_MARKER]: true,
2467
+ name: "Error",
2468
+ message: String(value)
2469
+ };
2470
+ const properties = {};
2471
+ for (const key of Object.keys(value)) if (!RESERVED_KEYS.has(key)) properties[key] = toTransportSafe(value[key]);
2472
+ return {
2473
+ [ERROR_MARKER]: true,
2474
+ name: value.name,
2475
+ message: value.message,
2476
+ ...value.stack !== void 0 ? { stack: value.stack } : {},
2477
+ ...value.cause !== void 0 ? { cause: toTransportSafe(value.cause) } : {},
2478
+ ...Object.keys(properties).length > 0 ? { properties } : {}
2479
+ };
2480
+ }
2481
+ /** Inverse of {@link toTransportSafe}: rebuilds nested errors while passing other values through. */
2482
+ function fromTransportSafe(value) {
2483
+ if (isSerializedError(value)) return deserializeError(value);
2484
+ if (Array.isArray(value)) return value.map((entry) => fromTransportSafe(entry));
2485
+ if (isPlainObject(value)) {
2486
+ const result = {};
2487
+ for (const [key, entry] of Object.entries(value)) result[key] = fromTransportSafe(entry);
2488
+ return result;
2489
+ }
2490
+ return value;
2491
+ }
2492
+ /**
2493
+ * Rebuilds an `Error` from a {@link SerializedError}, restoring its `name`, `stack`, `cause` chain,
2494
+ * and any extra fields (so a Forkbook error arrives with `code`/`fromStorybook` intact).
2495
+ *
2496
+ * The result is a plain `Error`, not the original subclass — reconstructing arbitrary classes across
2497
+ * a realm boundary is neither possible nor needed; callers branch on the restored fields instead.
2498
+ */
2499
+ function deserializeError(serialized) {
2500
+ const error = new Error(serialized.message);
2501
+ error.name = serialized.name;
2502
+ if (serialized.stack !== void 0) error.stack = serialized.stack;
2503
+ if ("cause" in serialized) error.cause = fromTransportSafe(serialized.cause);
2504
+ if (serialized.properties) for (const [key, value] of Object.entries(serialized.properties)) error[key] = fromTransportSafe(value);
2505
+ return error;
2506
+ }
2507
+ var ERROR_MARKER, RESERVED_KEYS;
2508
+ function init_service_error_serialization() {
2509
+ return (init_service_error_serialization = __esmMin((() => {
2510
+ ERROR_MARKER = "__openServiceError__";
2511
+ RESERVED_KEYS = /* @__PURE__ */ new Set([
2512
+ ERROR_MARKER,
2513
+ "name",
2514
+ "message",
2515
+ "stack",
2516
+ "cause"
2517
+ ]);
2518
+ })))();
2519
+ }
2520
+ //#endregion
2521
+ //#region src/shared/open-service/service-transport.ts
2522
+ /**
2523
+ * Builds the receiver for the entries a runtime's `setState` recipes author.
2524
+ *
2525
+ * Each entry is stamped BEFORE it is emitted, so the copy that bounces back carries a stamp this
2526
+ * runtime has already seen and is dropped as a duplicate.
2527
+ */
2528
+ function createEntryAuthor(context) {
2529
+ const { serviceId, ownRuntimeId, reconciler, channel } = context;
2530
+ return ({ command, ops, inverse }) => {
2531
+ const stamp = reconciler.advanceLocal(ownRuntimeId, {
2532
+ command,
2533
+ patch: ops,
2534
+ inverse
2535
+ });
2536
+ channel.emit(SERVICE_ENTRY, {
2537
+ serviceId,
2538
+ stamp,
2539
+ command,
2540
+ patch: ops
2541
+ });
2542
+ };
2543
+ }
2544
+ /**
2545
+ * Attaches the channel listeners that keep one runtime in sync with its peers, and returns a teardown.
2546
+ *
2547
+ * Wires three handlers and emits a bootstrap `services:sync-request` so a freshly-registered runtime
2548
+ * catches up to state authored before it joined:
2549
+ * - sync-request → reply `{ frontier, state }` only when our vector dominates the requester's;
2550
+ * - sync-reply → install only if dominating (and, on a relay hub, forward the original payload);
2551
+ * - entry → place by the five-case rule (and, on a relay hub, forward when logged or dropped as
2552
+ * beyond-window for the first time).
2553
+ *
2554
+ * A hub forwards the original payload object so unknown fields survive the hop. Leaves keep
2555
+ * `relay: false`. The request policy (one outstanding, {@link SYNC_REQUEST_SILENCE_MS} of silence,
2556
+ * one queued repair) is in the README's Bootstrap and repair section.
2557
+ */
2558
+ function connectRuntimeToChannel(context) {
2559
+ const { serviceId, ownRuntimeId, reconciler, getSnapshot, channel, relay } = context;
2560
+ let requestOutstanding = false;
2561
+ let withinReplyWindow = false;
2562
+ let repairQueued = false;
2563
+ let silenceTimer;
2564
+ let forwardedReplyKey;
2565
+ const clearSilenceTimer = () => {
2566
+ if (silenceTimer !== void 0) {
2567
+ clearTimeout(silenceTimer);
2568
+ silenceTimer = void 0;
2569
+ }
2570
+ };
2571
+ const emitSyncRequest = () => {
2572
+ if (requestOutstanding) {
2573
+ repairQueued = true;
2574
+ return;
2575
+ }
2576
+ requestOutstanding = true;
2577
+ withinReplyWindow = true;
2578
+ repairQueued = false;
2579
+ channel.emit(SERVICE_SYNC_REQUEST, {
2580
+ serviceId,
2581
+ runtimeId: ownRuntimeId,
2582
+ frontier: reconciler.frontier
2583
+ });
2584
+ clearSilenceTimer();
2585
+ silenceTimer = setTimeout(() => {
2586
+ silenceTimer = void 0;
2587
+ withinReplyWindow = false;
2588
+ settleOutstandingRequest();
2589
+ }, SYNC_REQUEST_SILENCE_MS);
2590
+ };
2591
+ const settleOutstandingRequest = () => {
2592
+ const queued = repairQueued;
2593
+ requestOutstanding = false;
2594
+ if (queued) emitSyncRequest();
2595
+ };
2596
+ const onSyncRequest = (payload) => {
2597
+ const request = safeParse(syncRequestSchema, payload);
2598
+ if (!request.success || request.output.serviceId !== serviceId || request.output.runtimeId === ownRuntimeId) return;
2599
+ if (!vectorDominates(reconciler.vector, request.output.frontier.vector)) return;
2600
+ channel.emit(SERVICE_SYNC_REPLY, {
2601
+ serviceId,
2602
+ runtimeId: ownRuntimeId,
2603
+ frontier: reconciler.frontier,
2604
+ state: getSnapshot()
2605
+ });
2606
+ };
2607
+ const onSyncReply = (payload) => {
2608
+ const snapshot = safeParse(syncReplySchema, payload);
2609
+ if (!snapshot.success || snapshot.output.serviceId !== serviceId || snapshot.output.runtimeId === ownRuntimeId) return;
2610
+ const replyKey = `${snapshot.output.runtimeId}:${formatFrontier(snapshot.output.frontier)}`;
2611
+ if (replyKey === forwardedReplyKey) return;
2612
+ let outcome;
2613
+ try {
2614
+ outcome = reconciler.tryInstall(snapshot.output.frontier, snapshot.output.state);
2615
+ } finally {
2616
+ const settled = outcome ?? "installed";
2617
+ if (settled === "concurrent" && withinReplyWindow) {
2618
+ logger.warn(`Open-service sync: concurrent snapshot reply dropped. service=${serviceId} local=${formatFrontier(reconciler.frontier)} reply=${formatFrontier(snapshot.output.frontier)}`);
2619
+ repairQueued = true;
2620
+ }
2621
+ settleOutstandingRequest();
2622
+ if (settled === "installed" && relay) {
2623
+ forwardedReplyKey = replyKey;
2624
+ channel.emit(SERVICE_SYNC_REPLY, payload);
2625
+ }
2626
+ }
2627
+ };
2628
+ const onEntry = (payload) => {
2629
+ const parsed = safeParse(entrySchema, payload);
2630
+ if (!parsed.success || parsed.output.serviceId !== serviceId) return;
2631
+ let outcome;
2632
+ try {
2633
+ outcome = reconciler.tryPlaceEntry(parsed.output);
2634
+ } finally {
2635
+ const placed = outcome ?? (reconciler.has(parsed.output.stamp) ? "unapplied" : void 0);
2636
+ const logged = placed === "accepted" || placed === "gap" || placed === "unapplied";
2637
+ const needsRepair = placed === "gap" || placed === "beyond-window" || placed === "unapplied";
2638
+ if ((logged || placed === "beyond-window") && relay) channel.emit(SERVICE_ENTRY, payload);
2639
+ if (needsRepair) emitSyncRequest();
2640
+ }
2641
+ };
2642
+ channel.on(SERVICE_SYNC_REQUEST, onSyncRequest);
2643
+ channel.on(SERVICE_SYNC_REPLY, onSyncReply);
2644
+ channel.on(SERVICE_ENTRY, onEntry);
2645
+ emitSyncRequest();
2646
+ return () => {
2647
+ channel.off(SERVICE_SYNC_REQUEST, onSyncRequest);
2648
+ channel.off(SERVICE_SYNC_REPLY, onSyncReply);
2649
+ channel.off(SERVICE_ENTRY, onEntry);
2650
+ repairQueued = false;
2651
+ requestOutstanding = false;
2652
+ withinReplyWindow = false;
2653
+ clearSilenceTimer();
2654
+ };
2655
+ }
2656
+ /**
2657
+ * Wires the remote-command-execution protocol for one service runtime, returning the command map
2658
+ * callers should use plus a teardown.
2659
+ *
2660
+ * ## Why this exists
2661
+ *
2662
+ * A command handler can be supplied at registration in only *some* runtimes — the canonical case is
2663
+ * a server-only handler that needs Node APIs or server context. Runtimes without a local handler
2664
+ * still expose the command (so `useServiceCommand`, tests, and other services can call it), but they
2665
+ * cannot run it themselves; they must ask a peer that can.
2666
+ *
2667
+ * ## Protocol
2668
+ *
2669
+ * - **Requester** (no local handler): the returned command emits `services:command-invoke` with a
2670
+ * unique `callId` and returns a promise. The promise resolves with the first
2671
+ * `services:command-result` for that `callId`, or rejects with the (deserialized) error from the
2672
+ * first `services:command-error`.
2673
+ * - **Responder** (has a local handler): on a matching `services:command-invoke` it emits
2674
+ * `services:command-ack` immediately, runs the command locally on a deferred macrotask — so an
2675
+ * async channel flushes the ack before any handler work starts (each `setState` write is
2676
+ * broadcast by the entry author, so peers converge as usual) — then emits
2677
+ * `services:command-result` or `services:command-error`.
2678
+ * - **Non-implementer**: a non-delegated runtime that hosts the service but not the invoked
2679
+ * command's handler replies `services:command-unhandled` instead of staying silent. Only a
2680
+ * delegated requester acts on that reply (rejecting with config-drift guidance, since the
2681
+ * Forkbook it attached to is the only runtime that sees its invokes); other requesters ignore
2682
+ * it, because one peer's report cannot speak for the others.
2683
+ *
2684
+ * Both roles coexist on one runtime: it responds for the commands it implements and requests the
2685
+ * ones it does not. A runtime never requests a command it implements (it runs that locally), so it
2686
+ * never answers its own invoke echo.
2687
+ *
2688
+ * ## Multiple implementers
2689
+ *
2690
+ * If several peers implement the same command they will each run it and reply. The requester keeps
2691
+ * only the first reply per `callId` and ignores the rest. Running a command in more than one runtime
2692
+ * is therefore possible by construction; it is constrained by usage conventions and documentation
2693
+ * rather than enforced here.
2694
+ *
2695
+ * ## Topology
2696
+ *
2697
+ * Replies travel back over the same channel the invoke went out on. A request reaches every peer on
2698
+ * the requester's transports; the manager is connected to both the dev server and the preview, so it
2699
+ * can invoke a command implemented in either. Command events are *not* relayed across a hub's other
2700
+ * transports, so a preview cannot directly invoke a server-only command (and vice versa) — route such
2701
+ * calls through the manager or implement the command on a directly-connected peer.
2702
+ *
2703
+ * ## Delegated mode
2704
+ *
2705
+ * A delegated runtime (one attached to an already-running Forkbook) owns no dispatch at all: it
2706
+ * requests *every* command over the channel and answers no invoke, because the runtime it attached
2707
+ * to is the implementer. Local handlers stay registered and inspectable but never run, so a service
2708
+ * author writes the same definition either way.
2709
+ */
2710
+ function connectCommandTransport(context) {
2711
+ const { serviceId, ownRuntimeId, channel, localCommands, implementedCommandNames, commandNames, delegated } = context;
2712
+ const dispatchesLocally = (commandName) => !delegated && implementedCommandNames.has(commandName);
2713
+ const pending = /* @__PURE__ */ new Map();
2714
+ const settle = (callId, apply) => {
2715
+ const entry = pending.get(callId);
2716
+ if (!entry) return;
2717
+ pending.delete(callId);
2718
+ clearTimeout(entry.noAckTimer);
2719
+ apply(entry);
2720
+ };
2721
+ const onInvoke = (payload) => {
2722
+ const parsed = safeParse(commandInvokeSchema, payload);
2723
+ if (!parsed.success || parsed.output.serviceId !== serviceId) return;
2724
+ const invoke = parsed.output;
2725
+ if (!dispatchesLocally(invoke.commandName)) {
2726
+ if (!delegated && invoke.runtimeId !== ownRuntimeId) channel.emit(SERVICE_COMMAND_UNHANDLED, {
2727
+ serviceId,
2728
+ callId: invoke.callId
2729
+ });
2730
+ return;
2731
+ }
2732
+ channel.emit(SERVICE_COMMAND_ACK, {
2733
+ serviceId,
2734
+ callId: invoke.callId,
2735
+ runtimeId: ownRuntimeId
2736
+ });
2737
+ setTimeout(() => {
2738
+ Promise.resolve().then(() => localCommands[invoke.commandName](invoke.input)).then((result) => {
2739
+ channel.emit(SERVICE_COMMAND_RESULT, {
2740
+ serviceId,
2741
+ callId: invoke.callId,
2742
+ result,
2743
+ runtimeId: ownRuntimeId
2744
+ });
2745
+ }, (error) => {
2746
+ channel.emit(SERVICE_COMMAND_ERROR, {
2747
+ serviceId,
2748
+ callId: invoke.callId,
2749
+ error: serializeError(error),
2750
+ runtimeId: ownRuntimeId
2751
+ });
2752
+ });
2753
+ }, 0);
2754
+ };
2755
+ const onResult = (payload) => {
2756
+ const result = safeParse(commandResultSchema, payload);
2757
+ if (!result.success || result.output.serviceId !== serviceId) return;
2758
+ settle(result.output.callId, (entry) => entry.resolve(result.output.result));
2759
+ };
2760
+ const onError = (payload) => {
2761
+ const failure = safeParse(commandErrorSchema, payload);
2762
+ if (!failure.success || failure.output.serviceId !== serviceId) return;
2763
+ settle(failure.output.callId, (entry) => entry.reject(deserializeError(failure.output.error)));
2764
+ };
2765
+ const onAck = (payload) => {
2766
+ const ack = safeParse(commandAckSchema, payload);
2767
+ if (!ack.success || ack.output.serviceId !== serviceId) return;
2768
+ const entry = pending.get(ack.output.callId);
2769
+ if (!entry) return;
2770
+ clearTimeout(entry.noAckTimer);
2771
+ };
2772
+ const onUnhandled = (payload) => {
2773
+ const report = safeParse(commandUnhandledSchema, payload);
2774
+ if (!report.success || report.output.serviceId !== serviceId || !delegated) return;
2775
+ settle(report.output.callId, (entry) => entry.reject(new OpenServiceRemoteCommandConfigDriftError({
2776
+ serviceId,
2777
+ commandName: entry.commandName
2778
+ })));
2779
+ };
2780
+ channel.on(SERVICE_COMMAND_INVOKE, onInvoke);
2781
+ channel.on(SERVICE_COMMAND_RESULT, onResult);
2782
+ channel.on(SERVICE_COMMAND_ERROR, onError);
2783
+ channel.on(SERVICE_COMMAND_ACK, onAck);
2784
+ channel.on(SERVICE_COMMAND_UNHANDLED, onUnhandled);
2785
+ const requestRemote = (commandName, input) => {
2786
+ const callId = generateCallId();
2787
+ return new Promise((resolve, reject) => {
2788
+ const noAckTimer = setTimeout(() => {
2789
+ settle(callId, (entry) => entry.reject(new OpenServiceRemoteCommandUnhandledError({
2790
+ serviceId,
2791
+ commandName: entry.commandName,
2792
+ delegated
2793
+ })));
2794
+ }, 300);
2795
+ pending.set(callId, {
2796
+ commandName,
2797
+ resolve,
2798
+ reject,
2799
+ noAckTimer
2800
+ });
2801
+ channel.emit(SERVICE_COMMAND_INVOKE, {
2802
+ serviceId,
2803
+ commandName,
2804
+ input,
2805
+ callId,
2806
+ runtimeId: ownRuntimeId
2807
+ });
2808
+ });
2809
+ };
2810
+ const commands = {};
2811
+ for (const name of commandNames) commands[name] = dispatchesLocally(name) ? localCommands[name] : (input) => requestRemote(name, input);
2812
+ return {
2813
+ commands,
2814
+ disconnect: () => {
2815
+ channel.off(SERVICE_COMMAND_INVOKE, onInvoke);
2816
+ channel.off(SERVICE_COMMAND_RESULT, onResult);
2817
+ channel.off(SERVICE_COMMAND_ERROR, onError);
2818
+ channel.off(SERVICE_COMMAND_ACK, onAck);
2819
+ channel.off(SERVICE_COMMAND_UNHANDLED, onUnhandled);
2820
+ for (const [, entry] of pending) {
2821
+ clearTimeout(entry.noAckTimer);
2822
+ entry.reject(new OpenServiceRemoteCommandDisconnectedError({ serviceId }));
2823
+ }
2824
+ pending.clear();
2825
+ }
2826
+ };
2827
+ }
2828
+ /**
2829
+ * Reports invokes for service ids this realm does not register at all.
2830
+ *
2831
+ * The per-service transport can only speak for services it hosts, but the common config-drift
2832
+ * shape — a feature flag gating a whole service — leaves the invoke with no listener at all. One
2833
+ * realm-global listener closes that gap: a non-delegated realm replies `services:command-unhandled`
2834
+ * for any invoke whose service id it has never registered, so a delegated caller learns the drift
2835
+ * positively instead of waiting out the ack window. Delegated realms answer nothing, a realm never
2836
+ * reports its own invokes (they are only ever for services it registers), and an id that was
2837
+ * registered before (mid-HMR re-registration) is not reported — staying silent degrades to the
2838
+ * retryable timeout error, while a false report would hand out restart guidance for a transient.
2839
+ */
2840
+ function connectUnknownServiceReporter(context) {
2841
+ const { channel, isServiceRegistered, isDelegated } = context;
2842
+ const onInvoke = (payload) => {
2843
+ const parsed = safeParse(commandInvokeSchema, payload);
2844
+ if (!parsed.success || isDelegated() || isServiceRegistered(parsed.output.serviceId)) return;
2845
+ channel.emit(SERVICE_COMMAND_UNHANDLED, {
2846
+ serviceId: parsed.output.serviceId,
2847
+ callId: parsed.output.callId
2848
+ });
2849
+ };
2850
+ channel.on(SERVICE_COMMAND_INVOKE, onInvoke);
2851
+ return () => {
2852
+ channel.off(SERVICE_COMMAND_INVOKE, onInvoke);
2853
+ };
2854
+ }
2855
+ /**
2856
+ * Wires one service runtime to the channel end to end and returns the command map callers expose plus
2857
+ * a single teardown.
2858
+ *
2859
+ * This is the one entry point `registerService` uses, so the three transport halves — entry
2860
+ * authoring, the remote-command protocol, and the sync-request + entry listeners — are always
2861
+ * assembled together against the same `channel` and can never drift into using different channels.
2862
+ * The channel-routed command map is also installed on the runtime so load bodies invoke
2863
+ * peer-implemented commands remotely instead of throwing locally.
2864
+ */
2865
+ function connectServiceToChannel(context) {
2866
+ const { serviceId, ownRuntimeId, reconciler, getSnapshot, channel, relay, commands, implementedCommandNames, commandNames, delegated, runtime } = context;
2867
+ runtime.attachEntryAuthor(createEntryAuthor({
2868
+ serviceId,
2869
+ ownRuntimeId,
2870
+ reconciler,
2871
+ channel
2872
+ }));
2873
+ const commandTransport = connectCommandTransport({
2874
+ serviceId,
2875
+ ownRuntimeId,
2876
+ channel,
2877
+ localCommands: commands,
2878
+ implementedCommandNames,
2879
+ commandNames,
2880
+ delegated
2881
+ });
2882
+ const disconnectSync = connectRuntimeToChannel({
2883
+ serviceId,
2884
+ ownRuntimeId,
2885
+ reconciler,
2886
+ getSnapshot,
2887
+ channel,
2888
+ relay
2889
+ });
2890
+ runtime.attachChannelCommands(commandTransport.commands, delegated ? /* @__PURE__ */ new Set() : implementedCommandNames);
2891
+ return {
2892
+ commands: commandTransport.commands,
2893
+ disconnect: () => {
2894
+ disconnectSync();
2895
+ commandTransport.disconnect();
2896
+ }
2897
+ };
2898
+ }
2899
+ var SYNC_REQUEST_SILENCE_MS;
2900
+ function init_service_transport() {
2901
+ return (init_service_transport = __esmMin((() => {
2902
+ init_dist();
2903
+ init_client_logger();
2904
+ init_server_errors();
2905
+ init_service_channel();
2906
+ init_service_error_serialization();
2907
+ init_service_sync();
2908
+ SYNC_REQUEST_SILENCE_MS = 1e3;
2909
+ })))();
2910
+ }
2911
+ //#endregion
2912
+ //#region src/shared/open-service/service-registry.ts
2913
+ /**
2914
+ * Returns the realm-global inventory backing service registration and delegated mode.
2915
+ *
2916
+ * Lazily created so importing the module does not eagerly mutate global state. Anchoring it on a
2917
+ * `globalThis` symbol keeps the service map and the delegated-mode flag shared even when this file is
2918
+ * reached through different import paths.
2919
+ */
2920
+ function getInventory() {
2921
+ const registryGlobal = globalThis;
2922
+ registryGlobal[REGISTRY_SYMBOL] ??= {
2923
+ entries: /* @__PURE__ */ new Map(),
2924
+ delegatedMode: false,
2925
+ everRegisteredIds: /* @__PURE__ */ new Set()
2926
+ };
2927
+ return registryGlobal[REGISTRY_SYMBOL];
2928
+ }
2929
+ function getRegistry() {
2930
+ return getInventory().entries;
2931
+ }
2932
+ /** Whether this runtime delegates command dispatch to the Forkbook it is attached to. */
2933
+ function isDelegatedMode() {
2934
+ return getInventory().delegatedMode;
2935
+ }
2936
+ function ensureUnknownServiceReporter(channel) {
2937
+ const inventory = getInventory();
2938
+ if (inventory.unknownServiceReporter?.channel === channel) return;
2939
+ inventory.unknownServiceReporter?.disconnect();
2940
+ inventory.unknownServiceReporter = {
2941
+ channel,
2942
+ disconnect: connectUnknownServiceReporter({
2943
+ channel,
2944
+ isServiceRegistered: (serviceId) => inventory.everRegisteredIds.has(serviceId),
2945
+ isDelegated: () => inventory.delegatedMode
2946
+ })
2947
+ };
2948
+ }
2949
+ function assertUniqueOperationNames(definition) {
2950
+ const duplicateName = Object.keys(definition.queries).find((name) => Object.hasOwn(definition.commands, name));
2951
+ if (duplicateName) throw new OpenServiceOperationNameCollisionError({
2952
+ serviceId: definition.id,
2953
+ operationName: duplicateName
2954
+ });
2955
+ }
2956
+ /**
2957
+ * Converts one service definition into the serializable descriptor returned by registry metadata APIs.
2958
+ *
2959
+ * Descriptors expose schemas and descriptions but not runtime handlers, so callers can inspect a
2960
+ * service's contract without gaining access to executable behavior. `staticPath: true` is surfaced so
2961
+ * manager code can choose between live runtime queries and prebuilt JSON snapshots.
2962
+ */
2963
+ function describeDefinition(definition) {
2964
+ return {
2965
+ id: definition.id,
2966
+ description: definition.description,
2967
+ queries: Object.fromEntries(Object.entries(definition.queries).filter(([, query]) => !query.internal).map(([name, query]) => [name, {
2968
+ name,
2969
+ description: query.description,
2970
+ input: query.input,
2971
+ output: query.output,
2972
+ ...query.staticPath ? { staticPath: true } : {}
2973
+ }])),
2974
+ commands: Object.fromEntries(Object.entries(definition.commands).filter(([, command]) => !command.internal).map(([name, command]) => [name, {
2975
+ name,
2976
+ description: command.description,
2977
+ input: command.input,
2978
+ output: command.output
2979
+ }]))
2980
+ };
2981
+ }
2982
+ /** Derives the lightweight summary returned by `listServices()` from a full descriptor. */
2983
+ function summarizeDescriptor(descriptor) {
2984
+ return {
2985
+ id: descriptor.id,
2986
+ description: descriptor.description,
2987
+ queryNames: Object.keys(descriptor.queries),
2988
+ commandNames: Object.keys(descriptor.commands)
2989
+ };
2990
+ }
2991
+ /**
2992
+ * Applies optional registration overrides to an authored definition.
2993
+ *
2994
+ * Query registration may supply dependency-aware `staticInputs` (used by server static builds);
2995
+ * command registration may override the `handler`. Anything not overridden is forwarded unchanged so
2996
+ * every runtime shares the same contract.
2997
+ */
2998
+ function applyRegistration(definition, registration) {
2999
+ if (!registration) return definition;
3000
+ return {
3001
+ ...definition,
3002
+ queries: Object.fromEntries(Object.entries(definition.queries).map(([name, query]) => {
3003
+ const override = registration.queries?.[name];
3004
+ return [name, override && "staticInputs" in override ? {
3005
+ ...query,
3006
+ staticInputs: override.staticInputs
3007
+ } : query];
3008
+ })),
3009
+ commands: Object.fromEntries(Object.entries(definition.commands).map(([name, command]) => {
3010
+ const override = registration.commands?.[name];
3011
+ return [name, override && "handler" in override ? {
3012
+ ...command,
3013
+ handler: override.handler
3014
+ } : command];
3015
+ }))
3016
+ };
3017
+ }
3018
+ /**
3019
+ * Registers one service definition in the realm-global registry and returns its runtime surface.
3020
+ *
3021
+ * Registration resolves any registration-time overrides, builds the runtime that query and command
3022
+ * callers use, installs the entry author that emits a `services:entry` for each `setState` write, and
3023
+ * joins the cross-peer sync protocol as a hub or leaf (`relay`). Each runtime must install the addons
3024
+ * channel at its entry boundary before calling this (builders, manager boot, server `services`
3025
+ * preset, or Node import bootstrap). Registration is idempotent by id: a repeated registration
3026
+ * returns the existing runtime.
3027
+ */
3028
+ function registerService$1(definition, registration, { relay = false, staticLoader, window } = {}) {
3029
+ assertUniqueOperationNames(definition);
3030
+ const registry = getRegistry();
3031
+ const existingEntry = registry.get(definition.id);
3032
+ if (existingEntry) return existingEntry.instance;
3033
+ const ownRuntimeId = generateRuntimeId();
3034
+ const resolvedDefinition = applyRegistration(definition, registration);
3035
+ const runtime = createServiceRuntime(resolvedDefinition, {
3036
+ registryApi: serviceRegistryApi,
3037
+ staticLoader
3038
+ });
3039
+ const reconciler = createReconciler({
3040
+ serviceId: definition.id,
3041
+ setState: (mutate) => runtime.applyLocal((state) => mutate(state)),
3042
+ window
3043
+ });
3044
+ const getSnapshot = () => runtime.getStateSnapshot();
3045
+ const descriptor = describeDefinition(resolvedDefinition);
3046
+ const channel = getChannel();
3047
+ if (!channel) throw new OpenServiceMissingChannelError({ serviceId: definition.id });
3048
+ getInventory().everRegisteredIds.add(definition.id);
3049
+ ensureUnknownServiceReporter(channel);
3050
+ const implementedCommandNames = new Set(Object.entries(resolvedDefinition.commands).filter(([, command]) => typeof command.handler === "function").map(([name]) => name));
3051
+ const { commands, disconnect } = connectServiceToChannel({
3052
+ serviceId: definition.id,
3053
+ ownRuntimeId,
3054
+ reconciler,
3055
+ getSnapshot,
3056
+ channel,
3057
+ relay: isDelegatedMode() ? false : relay,
3058
+ commands: runtime.commands,
3059
+ implementedCommandNames,
3060
+ commandNames: Object.keys(resolvedDefinition.commands),
3061
+ delegated: isDelegatedMode(),
3062
+ runtime
3063
+ });
3064
+ const instance = {
3065
+ queries: runtime.queries,
3066
+ commands,
3067
+ ...serviceRegistryApi
3068
+ };
3069
+ registry.set(definition.id, {
3070
+ definition: resolvedDefinition,
3071
+ instance,
3072
+ descriptor,
3073
+ summary: summarizeDescriptor(descriptor),
3074
+ disconnect
3075
+ });
3076
+ return instance;
3077
+ }
3078
+ /** Returns one summary entry per registered service — the lowest-cost discovery endpoint. */
3079
+ async function listServices() {
3080
+ return Array.from(getRegistry().values()).filter(({ definition }) => !definition.internal).map(({ summary }) => summary);
3081
+ }
3082
+ /** Returns the schema-backed descriptor for one registered service. */
3083
+ async function describeService(serviceId) {
3084
+ const entry = getRegistry().get(serviceId);
3085
+ if (!entry) throw new OpenServiceMissingServiceError({ serviceId });
3086
+ return entry.descriptor;
3087
+ }
3088
+ /**
3089
+ * Resolves a registered runtime service by id from the current realm.
3090
+ *
3091
+ * Query and command contexts delegate cross-service calls through this lookup so one service can reuse
3092
+ * another's runtime contract. Synchronous because callers need it inside sync query handlers.
3093
+ *
3094
+ * Services with `internal: true` require `{ internal: true }` — otherwise this throws
3095
+ * {@link OpenServiceInternalServiceError}. Do not depend on internal OSA from public adapters;
3096
+ * prefer public toolsets (`defineToolset`) instead.
3097
+ */
3098
+ function getService$1(serviceId, options) {
3099
+ const entry = getRegistry().get(serviceId);
3100
+ if (!entry) throw new OpenServiceMissingServiceError({ serviceId });
3101
+ if (entry.definition.internal && !options?.internal) throw new OpenServiceInternalServiceError({ serviceId });
3102
+ return entry.instance;
3103
+ }
3104
+ var REGISTRY_SYMBOL, serviceRegistryApi;
3105
+ function init_service_registry() {
3106
+ return (init_service_registry = __esmMin((() => {
3107
+ init_channel_slot();
3108
+ init_server_errors();
3109
+ init_service_channel();
3110
+ init_service_runtime();
3111
+ init_service_sync();
3112
+ init_service_transport();
3113
+ REGISTRY_SYMBOL = Symbol.for("storybook.open-service.registry");
3114
+ serviceRegistryApi = {
3115
+ listServices,
3116
+ describeService,
3117
+ getService: getService$1
3118
+ };
3119
+ })))();
3120
+ }
3121
+ //#endregion
3122
+ //#region src/shared/open-service/static-fetch.ts
3123
+ function shouldUseBrowserStaticLoader() {
3124
+ return globalThis.CONFIG_TYPE === "PRODUCTION";
3125
+ }
3126
+ /**
3127
+ * Returns a fetch-based loader for static build output, or `undefined` in development.
3128
+ *
3129
+ * Snapshot paths are logical keys such as `core/docgen/foo.json`, resolved relative to the
3130
+ * document (`./services/...`), so the manager and preview iframes share the same base wherever
3131
+ * the build is deployed.
3132
+ */
3133
+ function createBrowserStaticLoader() {
3134
+ if (!shouldUseBrowserStaticLoader()) return;
3135
+ return async (logicalPath, context) => {
3136
+ const url = `${STATIC_SERVICES_PREFIX}${logicalPath}`;
3137
+ let res;
3138
+ try {
3139
+ res = await fetch(url);
3140
+ } catch (cause) {
3141
+ throw new OpenServiceStaticSnapshotLoadError({
3142
+ ...context,
3143
+ logicalPath,
3144
+ url,
3145
+ cause
3146
+ });
3147
+ }
3148
+ if (!res.ok) {
3149
+ const cause = {
3150
+ status: res.status,
3151
+ statusText: res.statusText
3152
+ };
3153
+ throw new OpenServiceStaticSnapshotLoadError({
3154
+ ...context,
3155
+ logicalPath,
3156
+ url,
3157
+ cause,
3158
+ status: res.status,
3159
+ statusText: res.statusText
3160
+ });
3161
+ }
3162
+ let snapshot;
3163
+ try {
3164
+ snapshot = await res.json();
3165
+ } catch (cause) {
3166
+ throw new OpenServiceStaticSnapshotLoadError({
3167
+ ...context,
3168
+ logicalPath,
3169
+ url,
3170
+ cause
3171
+ });
3172
+ }
3173
+ if (snapshot && typeof snapshot === "object" && !Array.isArray(snapshot)) return snapshot;
3174
+ throw new OpenServiceStaticSnapshotInvalidError({
3175
+ ...context,
3176
+ logicalPath,
3177
+ url,
3178
+ received: snapshot
3179
+ });
3180
+ };
3181
+ }
3182
+ var STATIC_SERVICES_PREFIX;
3183
+ function init_static_fetch() {
3184
+ return (init_static_fetch = __esmMin((() => {
3185
+ init_manager_errors();
3186
+ STATIC_SERVICES_PREFIX = "./services/";
3187
+ })))();
3188
+ }
3189
+ //#endregion
3190
+ //#region src/shared/open-service/preview.ts
3191
+ /**
3192
+ * Registers a service in the preview and returns its runtime surface.
3193
+ *
3194
+ * The preview is a leaf (`relay: false`). Builders install the addons channel before preview config
3195
+ * loads, so registration can assume the channel is already present.
3196
+ */
3197
+ function registerService(definition, registration) {
3198
+ return registerService$1(definition, registration, { staticLoader: createBrowserStaticLoader() });
3199
+ }
3200
+ var getService;
3201
+ function init_preview() {
3202
+ return (init_preview = __esmMin((() => {
3203
+ init_service_registry();
3204
+ init_static_fetch();
3205
+ getService = getService$1;
3206
+ })))();
3207
+ }
3208
+ //#endregion
3209
+ //#region src/shared/open-service/service-definition.ts
3210
+ var defineService;
3211
+ function init_service_definition() {
3212
+ return (init_service_definition = __esmMin((() => {
3213
+ defineService = (def) => def;
3214
+ })))();
3215
+ }
3216
+ //#endregion
3217
+ //#region src/shared/open-service/services/story-docs/snippet.ts
3218
+ /** Prepends a CSF file import block to a story snippet for display in docs and the Code panel. */
3219
+ function prependImportToSnippet(importBlock, snippet) {
3220
+ const trimmedImport = importBlock?.trim();
3221
+ if (!trimmedImport) return snippet;
3222
+ return `${trimmedImport}\n\n${snippet}`;
3223
+ }
3224
+ /** Resolves the display snippet for one story from a story-docs payload. */
3225
+ function selectSnippetForStory(payload, storyId) {
3226
+ const story = payload?.stories[storyId];
3227
+ if (story?.snippet === void 0) return;
3228
+ return prependImportToSnippet(payload?.import, story.snippet);
3229
+ }
3230
+ /** Resolves the incomplete-snippet warning for one story from a story-docs payload. */
3231
+ function selectWarningForStory(payload, storyId) {
3232
+ return payload?.stories[storyId]?.warning;
3233
+ }
3234
+ //#endregion
3235
+ //#region src/shared/open-service/services/docgen/paths.ts
3236
+ /**
3237
+ * Relative path segment for one component's static snapshot file, as returned by the `docgen`
3238
+ * query's `staticPath`. Must stay aligned with {@link docgenServiceDef} and manifest `$ref`s.
3239
+ */
3240
+ function docgenQueryStaticPath(id) {
3241
+ return `${id}.json`;
3242
+ }
3243
+ function init_paths() {
3244
+ return (init_paths = __esmMin((() => {})))();
3245
+ }
3246
+ //#endregion
3247
+ //#region src/shared/open-service/services/docgen/definition.ts
3248
+ var docgenInputSchema, argTypesSchema, docgenErrorSchema, docgenJsDocTagsSchema, docgenComponentFields, docgenSubcomponentFields, docgenSubcomponentSchema, docgenPayloadSchema, docgenOutputSchema, docgenServiceDef;
3249
+ function init_definition() {
3250
+ return (init_definition = __esmMin((() => {
3251
+ init_dist();
3252
+ init_service_definition();
3253
+ init_paths();
3254
+ docgenInputSchema = object({ id: string() });
3255
+ argTypesSchema = custom((value) => typeof value === "object" && value !== null && !Array.isArray(value));
3256
+ docgenErrorSchema = object({
3257
+ name: string(),
3258
+ message: string()
3259
+ });
3260
+ docgenJsDocTagsSchema = record(string(), array(string()));
3261
+ docgenComponentFields = {
3262
+ name: string(),
3263
+ path: string(),
3264
+ description: optional(string()),
3265
+ summary: optional(string()),
3266
+ jsDocTags: docgenJsDocTagsSchema,
3267
+ argTypes: optional(argTypesSchema),
3268
+ error: optional(docgenErrorSchema)
3269
+ };
3270
+ docgenSubcomponentFields = {
3271
+ ...docgenComponentFields,
3272
+ import: optional(string())
3273
+ };
3274
+ docgenSubcomponentSchema = looseObject(docgenSubcomponentFields);
3275
+ docgenPayloadSchema = looseObject({
3276
+ id: string(),
3277
+ ...docgenComponentFields,
3278
+ subcomponents: optional(record(string(), docgenSubcomponentSchema))
3279
+ });
3280
+ docgenOutputSchema = optional(docgenPayloadSchema);
3281
+ docgenServiceDef = defineService({
3282
+ id: "core/docgen",
3283
+ internal: true,
3284
+ description: "Component documentation (name, description, props, JSDoc tags) keyed by component id.",
3285
+ initialState: { components: {} },
3286
+ queries: {
3287
+ docgen: {
3288
+ description: "Returns the docgen payload for one component id, or undefined when not loaded.",
3289
+ input: docgenInputSchema,
3290
+ output: docgenOutputSchema,
3291
+ handler: (input, ctx) => ctx.self.state.components[input.id],
3292
+ load: async (input, ctx) => {
3293
+ await ctx.self.commands.extractDocgen(input);
3294
+ },
3295
+ staticPath: (input) => docgenQueryStaticPath(input.id)
3296
+ },
3297
+ docgenForAllComponents: {
3298
+ description: "Returns docgen payloads for every component in the story index.",
3299
+ input: void_(),
3300
+ output: record(string(), docgenPayloadSchema),
3301
+ handler: (_input, ctx) => ctx.self.state.components,
3302
+ load: async (_input, ctx) => {
3303
+ await ctx.self.commands.extractAllDocgen(void 0);
3304
+ }
3305
+ }
3306
+ },
3307
+ commands: {
3308
+ extractDocgen: {
3309
+ description: "Resolves the story entry for a component id, runs the registered provider chain, writes the result into state, and returns it (or undefined when no provider produced docgen).",
3310
+ input: docgenInputSchema,
3311
+ output: docgenOutputSchema
3312
+ },
3313
+ extractAllDocgen: {
3314
+ description: "Extracts docgen for every component id in the story index by invoking `extractDocgen` for each.",
3315
+ input: undefined_(),
3316
+ output: void_()
3317
+ }
3318
+ }
3319
+ });
3320
+ })))();
3321
+ }
3322
+ //#endregion
3323
+ //#region src/shared/open-service/services/docgen/preview.ts
3324
+ var preview_default$1;
3325
+ function init_preview$1() {
3326
+ return (init_preview$1 = __esmMin((() => {
3327
+ init_preview();
3328
+ init_definition();
3329
+ preview_default$1 = () => definePreviewAddon({ beforeAll: () => {
3330
+ if (globalThis.FEATURES?.docgenServer) registerService(docgenServiceDef);
3331
+ } });
3332
+ })))();
3333
+ }
3334
+ //#endregion
3335
+ //#region src/shared/open-service/services/story-docs/paths.ts
3336
+ /**
3337
+ * Relative path segment for one component's static snapshot file, as returned by the `storyDocs`
3338
+ * query's `staticPath`. Must stay aligned with {@link storyDocsServiceDef} and manifest `$ref`s.
3339
+ */
3340
+ function storyDocsQueryStaticPath(id) {
3341
+ return `${id}.json`;
3342
+ }
3343
+ function init_paths$1() {
3344
+ return (init_paths$1 = __esmMin((() => {})))();
3345
+ }
3346
+ //#endregion
3347
+ //#region src/shared/open-service/services/story-docs/definition.ts
3348
+ var storyDocsInputSchema, storyDocsErrorSchema, storyDocSchema, storyDocsPayloadSchema, storyDocsOutputSchema, storyDocsServiceDef;
3349
+ function init_definition$1() {
3350
+ return (init_definition$1 = __esmMin((() => {
3351
+ init_dist();
3352
+ init_service_definition();
3353
+ init_paths$1();
3354
+ storyDocsInputSchema = object({ id: string() });
3355
+ storyDocsErrorSchema = object({
3356
+ name: string(),
3357
+ message: string()
3358
+ });
3359
+ storyDocSchema = object({
3360
+ id: string(),
3361
+ name: string(),
3362
+ snippet: optional(string()),
3363
+ description: optional(string()),
3364
+ summary: optional(string()),
3365
+ warning: optional(string()),
3366
+ error: optional(storyDocsErrorSchema)
3367
+ });
3368
+ storyDocsPayloadSchema = object({
3369
+ id: string(),
3370
+ name: string(),
3371
+ path: string(),
3372
+ import: optional(string()),
3373
+ stories: record(string(), storyDocSchema),
3374
+ error: optional(storyDocsErrorSchema)
3375
+ });
3376
+ storyDocsOutputSchema = optional(storyDocsPayloadSchema);
3377
+ storyDocsServiceDef = defineService({
3378
+ id: "core/story-docs",
3379
+ internal: true,
3380
+ description: "Story documentation (snippets, descriptions, imports) keyed by component id.",
3381
+ initialState: { components: {} },
3382
+ queries: {
3383
+ storyDocs: {
3384
+ description: "Returns the story-docs payload for one component id, or undefined when not loaded.",
3385
+ input: storyDocsInputSchema,
3386
+ output: storyDocsOutputSchema,
3387
+ handler: (input, ctx) => Object.hasOwn(ctx.self.state.components, input.id) ? ctx.self.state.components[input.id] : void 0,
3388
+ load: async (input, ctx) => {
3389
+ await ctx.self.commands.extractStoryDocs(input);
3390
+ },
3391
+ staticPath: (input) => storyDocsQueryStaticPath(input.id)
3392
+ },
3393
+ storyDocsForAllComponents: {
3394
+ description: "Returns story-docs payloads for every component in the story index.",
3395
+ input: void_(),
3396
+ output: record(string(), storyDocsPayloadSchema),
3397
+ handler: (_input, ctx) => ctx.self.state.components,
3398
+ load: async (_input, ctx) => {
3399
+ await ctx.self.commands.extractAllStoryDocs(void 0);
3400
+ }
3401
+ }
3402
+ },
3403
+ commands: {
3404
+ extractStoryDocs: {
3405
+ description: "Resolves the story entry for a component id, runs the registered provider chain, writes the result into state, and returns it (or undefined when no provider produced story docs).",
3406
+ input: storyDocsInputSchema,
3407
+ output: storyDocsOutputSchema
3408
+ },
3409
+ extractAllStoryDocs: {
3410
+ description: "Extracts story docs for every component id in the story index by invoking `extractStoryDocs` for each.",
3411
+ input: undefined_(),
3412
+ output: void_()
3413
+ }
3414
+ }
3415
+ });
3416
+ })))();
3417
+ }
3418
+ //#endregion
3419
+ //#region src/shared/open-service/services/story-docs/story-docs-source-before-each.ts
3420
+ /**
3421
+ * Preview `beforeEach` hook that emits a static story-docs snippet to the manager Code panel via
3422
+ * {@link emitTransformCode}. Runs once per story invocation; the snippet itself is static.
3423
+ */
3424
+ function storyDocsSourceBeforeEach(context) {
3425
+ if (!globalThis.FEATURES?.docgenServer) return;
3426
+ if (shouldSkipStoryDocsEmit(context.parameters)) return;
3427
+ const service = (() => {
3428
+ try {
3429
+ return getService("core/story-docs", { internal: true });
3430
+ } catch {
3431
+ return;
3432
+ }
3433
+ })();
3434
+ if (!service) return;
3435
+ const storyId = context.id;
3436
+ const componentId = storyId.split("--")[0];
3437
+ let cancelled = false;
3438
+ const codePanelSnippetPromise = service.queries.storyDocs.loaded({ id: componentId }).then((payload) => {
3439
+ if (cancelled) return;
3440
+ const snippet = selectSnippetForStory(payload, storyId);
3441
+ const source = snippet ?? context.parameters?.docs?.source?.originalSource;
3442
+ if (source === void 0) return;
3443
+ const warning = selectWarningForStory(payload, storyId);
3444
+ return emitTransformCode(source, context, snippet === void 0 && warning ? `${warning} Showing the story source instead.` : warning);
3445
+ }).catch((error) => {
3446
+ logger.debug(`Story docs snippet for ${storyId} unavailable: ${String(error)}`);
3447
+ });
3448
+ return () => {
3449
+ cancelled = true;
3450
+ return codePanelSnippetPromise;
3451
+ };
3452
+ }
3453
+ function init_story_docs_source_before_each() {
3454
+ return (init_story_docs_source_before_each = __esmMin((() => {
3455
+ init_client_logger();
3456
+ init_storyDocsCodePanel();
3457
+ init_preview_api();
3458
+ })))();
3459
+ }
3460
+ //#endregion
3461
+ //#region src/shared/open-service/services/story-docs/preview.ts
3462
+ var preview_default;
3463
+ function init_preview$2() {
3464
+ return (init_preview$2 = __esmMin((() => {
3465
+ init_preview();
3466
+ init_definition$1();
3467
+ init_story_docs_source_before_each();
3468
+ preview_default = () => {
3469
+ if (!("FEATURES" in globalThis && globalThis.FEATURES?.docgenServer)) return definePreviewAddon({});
3470
+ return definePreviewAddon({
3471
+ beforeAll: () => {
3472
+ registerService(storyDocsServiceDef);
3473
+ },
3474
+ beforeEach: storyDocsSourceBeforeEach
3475
+ });
3476
+ };
3477
+ })))();
3478
+ }
3479
+ //#endregion
3480
+ //#region src/shared/story-index/autoTitle.ts
3481
+ /**
3482
+ * Combines path parts together, without duplicating separators (slashes). Used instead of
3483
+ * `path.join` because this code runs in the browser.
3484
+ *
3485
+ * @param paths Array of paths to join together.
3486
+ * @returns Joined path string, with single '/' between parts
3487
+ */
3488
+ function pathJoin(paths) {
3489
+ return paths.flatMap((p) => p.split("/")).filter(Boolean).join("/");
3490
+ }
3491
+ var sanitize, userOrAutoTitleFromSpecifier, userOrAutoTitle;
3492
+ function init_autoTitle() {
3493
+ return (init_autoTitle = __esmMin((() => {
3494
+ sanitize = (parts) => {
3495
+ if (parts.length === 0) return parts;
3496
+ const last = parts[parts.length - 1];
3497
+ const lastStripped = last?.replace(/(?:[.](?:story|stories))?([.][^.]+)$/i, "");
3498
+ if (parts.length === 1) return [lastStripped];
3499
+ const nextToLast = parts[parts.length - 2];
3500
+ if (lastStripped && nextToLast && lastStripped.toLowerCase() === nextToLast.toLowerCase()) return [...parts.slice(0, -2), lastStripped];
3501
+ return lastStripped && (/^(story|stories)([.][^.]+)$/i.test(last) || /^index$/i.test(lastStripped)) ? parts.slice(0, -1) : [...parts.slice(0, -1), lastStripped];
3502
+ };
3503
+ userOrAutoTitleFromSpecifier = (fileName, entry, userTitle) => {
3504
+ const { directory, importPathMatcher, titlePrefix = "" } = entry || {};
3505
+ const normalizedFileName = slash(String(fileName));
3506
+ if (importPathMatcher.exec(normalizedFileName)) {
3507
+ if (!userTitle) {
3508
+ let parts = pathJoin([titlePrefix, normalizedFileName.replace(directory, "")]).split("/");
3509
+ parts = sanitize(parts);
3510
+ return parts.join("/");
3511
+ }
3512
+ if (!titlePrefix) return userTitle;
3513
+ return pathJoin([titlePrefix, userTitle]);
3514
+ }
3515
+ };
3516
+ userOrAutoTitle = (fileName, storiesEntries, userTitle) => {
3517
+ for (let i = 0; i < storiesEntries.length; i += 1) {
3518
+ const title = userOrAutoTitleFromSpecifier(fileName, storiesEntries[i], userTitle);
3519
+ if (title) return title;
3520
+ }
3521
+ return userTitle || void 0;
3522
+ };
3523
+ })))();
3524
+ }
3525
+ //#endregion
3526
+ //#region src/shared/story-index/storySort.ts
3527
+ var STORY_KIND_PATH_SEPARATOR, storySort;
3528
+ function init_storySort() {
3529
+ return (init_storySort = __esmMin((() => {
3530
+ STORY_KIND_PATH_SEPARATOR = /\s*\/\s*/;
3531
+ storySort = (options = {}) => (a, b) => {
3532
+ if (a.title === b.title && !options.includeNames) return 0;
3533
+ const method = options.method || "configure";
3534
+ let order = options.order || [];
3535
+ const storyTitleA = a.title.trim().split(STORY_KIND_PATH_SEPARATOR);
3536
+ const storyTitleB = b.title.trim().split(STORY_KIND_PATH_SEPARATOR);
3537
+ if (options.includeNames) {
3538
+ storyTitleA.push(a.name);
3539
+ storyTitleB.push(b.name);
3540
+ }
3541
+ let depth = 0;
3542
+ while (storyTitleA[depth] || storyTitleB[depth]) {
3543
+ if (!storyTitleA[depth]) return -1;
3544
+ if (!storyTitleB[depth]) return 1;
3545
+ const nameA = storyTitleA[depth];
3546
+ const nameB = storyTitleB[depth];
3547
+ if (nameA !== nameB) {
3548
+ let indexA = order.indexOf(nameA);
3549
+ let indexB = order.indexOf(nameB);
3550
+ const indexWildcard = order.indexOf("*");
3551
+ if (indexA !== -1 || indexB !== -1) {
3552
+ if (indexA === -1) {
3553
+ if (indexWildcard !== -1) indexA = indexWildcard;
3554
+ else indexA = order.length;
3555
+ }
3556
+ if (indexB === -1) {
3557
+ if (indexWildcard !== -1) indexB = indexWildcard;
3558
+ else indexB = order.length;
3559
+ }
3560
+ return indexA - indexB;
3561
+ }
3562
+ if (method === "configure") return 0;
3563
+ return nameA.localeCompare(nameB, options.locales ? options.locales : void 0, {
3564
+ numeric: true,
3565
+ sensitivity: "accent"
3566
+ });
3567
+ }
3568
+ let index = order.indexOf(nameA);
3569
+ if (index === -1) index = order.indexOf("*");
3570
+ const nestedOrder = order[index + 1];
3571
+ order = index !== -1 && Array.isArray(nestedOrder) ? nestedOrder : [];
3572
+ depth += 1;
3573
+ }
3574
+ /* istanbul ignore next */
3575
+ return 0;
3576
+ };
3577
+ })))();
3578
+ }
3579
+ //#endregion
3580
+ //#region src/shared/story-index/sortStories.ts
3581
+ var toComparator, sortStoriesCommon, sortStoriesV7;
3582
+ function init_sortStories() {
3583
+ return (init_sortStories = __esmMin((() => {
3584
+ init_esm();
3585
+ init_storySort();
3586
+ toComparator = (storySortParameter) => typeof storySortParameter === "function" ? storySortParameter : storySort(Array.isArray(storySortParameter) ? { order: storySortParameter } : storySortParameter);
3587
+ sortStoriesCommon = (stories, storySortParameter, fileNameOrder) => {
3588
+ if (storySortParameter) stories.sort(toComparator(storySortParameter));
3589
+ else stories.sort((s1, s2) => fileNameOrder.indexOf(s1.importPath) - fileNameOrder.indexOf(s2.importPath));
3590
+ return stories;
3591
+ };
3592
+ sortStoriesV7 = (stories, storySortParameter, fileNameOrder) => {
3593
+ try {
3594
+ return sortStoriesCommon(stories, storySortParameter, fileNameOrder);
3595
+ } catch (err) {
3596
+ throw new Error(dedent`
3597
+ Error sorting stories with sort parameter ${storySortParameter}:
3598
+
3599
+ > ${err.message}
3600
+
3601
+ Are you using a V6-style sort function in V7 mode?
3602
+
3603
+ More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#v7-style-story-sort
3604
+ `);
3605
+ }
3606
+ };
3607
+ })))();
3608
+ }
3609
+ //#endregion
3610
+ //#region src/shared/utils/story-index-filters.ts
3611
+ /** Was this docs entry generated by a `.mdx` file (as opposed to an autodocs page)? */
3612
+ function isMdxEntry({ tags }) {
3613
+ return tags?.includes(Tag.UNATTACHED_MDX) || tags?.includes(Tag.ATTACHED_MDX);
3614
+ }
3615
+ function init_story_index_filters() {
3616
+ return (init_story_index_filters = __esmMin((() => {
3617
+ init_tags();
3618
+ })))();
3619
+ }
3620
+ //#endregion
3621
+ //#region src/shared/constants/iframe-resize.ts
3622
+ var IFRAME_RESIZE_CONTEXT, IFRAME_RESIZE_REQUEST_CONTEXT, iframeResizeViewportsEqual;
3623
+ function init_iframe_resize() {
3624
+ return (init_iframe_resize = __esmMin((() => {
3625
+ IFRAME_RESIZE_CONTEXT = "iframe.resize";
3626
+ IFRAME_RESIZE_REQUEST_CONTEXT = "iframe.resize.request";
3627
+ iframeResizeViewportsEqual = (left, right) => {
3628
+ if (left === right) return true;
3629
+ if (!left || !right) return !left && !right;
3630
+ return left.name === right.name && left.value === right.value && left.width === right.width && left.height === right.height;
3631
+ };
3632
+ })))();
3633
+ }
3634
+ //#endregion
3635
+ export { matchesHostname as A, init_events as C, init_status_store as D, REVIEW_STATUS_TYPE_ID as E, init_universal_store as M, LOOPBACK_HOSTNAMES as O, REVIEW_NAMESPACE as S, NON_AGGREGATED_STATUS_TYPE_IDS as T, init_preview as _, init_story_index_filters as a, init_tags as b, sortStoriesV7 as c, userOrAutoTitleFromSpecifier as d, init_preview$2 as f, getService as g, preview_default$1 as h, init_iframe_resize as i, UniversalStore as j, init_local_hosts as k, init_autoTitle as l, init_preview$1 as m, IFRAME_RESIZE_REQUEST_CONTEXT as n, isMdxEntry as o, preview_default as p, iframeResizeViewportsEqual as r, init_sortStories as s, IFRAME_RESIZE_CONTEXT as t, userOrAutoTitle as u, registerService as v, CHANGE_DETECTION_STATUS_TYPE_ID as w, REVIEW_EVENTS as x, Tag as y };