sigmx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +54 -43
  2. package/dist/cli.js +3 -1
  3. package/dist/cli.js.map +2 -2
  4. package/dist/kernel/compile.d.ts +8 -9
  5. package/dist/kernel/compile.js +102 -67
  6. package/dist/kernel/compile.js.map +2 -2
  7. package/dist/kernel/contracts.d.ts +12 -13
  8. package/dist/kernel/index.d.ts +11 -12
  9. package/dist/kernel/index.js +14 -12
  10. package/dist/kernel/index.js.map +3 -3
  11. package/dist/kernel/precompile.d.ts +8 -12
  12. package/dist/kernel/precompile.js +33 -76
  13. package/dist/kernel/precompile.js.map +2 -2
  14. package/dist/kernel/precompiled.d.ts +7 -4
  15. package/dist/kernel/precompiled.js +5 -5
  16. package/dist/kernel/precompiled.js.map +2 -2
  17. package/dist/kernel/reactive.d.ts +13 -11
  18. package/dist/kernel/reactive.js +96 -71
  19. package/dist/kernel/reactive.js.map +3 -3
  20. package/dist/kernel/runtime.js +85 -63
  21. package/dist/kernel/runtime.js.map +3 -3
  22. package/dist/kernel/scan.d.ts +2 -11
  23. package/dist/kernel/scan.js +26 -9
  24. package/dist/kernel/scan.js.map +2 -2
  25. package/dist/kernel/state.d.ts +5 -8
  26. package/dist/kernel/state.js +114 -76
  27. package/dist/kernel/state.js.map +2 -2
  28. package/dist/kernel/strict-csp.d.ts +0 -5
  29. package/dist/kernel/strict-csp.js +2 -2
  30. package/dist/kernel/strict-csp.js.map +2 -2
  31. package/dist/lib/casing.d.ts +4 -1
  32. package/dist/lib/casing.js +8 -1
  33. package/dist/lib/casing.js.map +2 -2
  34. package/dist/lib/objects.d.ts +5 -4
  35. package/dist/lib/objects.js +13 -5
  36. package/dist/lib/objects.js.map +2 -2
  37. package/dist/lib/schedule.d.ts +3 -5
  38. package/dist/lib/schedule.js +12 -10
  39. package/dist/lib/schedule.js.map +2 -2
  40. package/dist/lib/sse.d.ts +10 -1
  41. package/dist/lib/sse.js +32 -28
  42. package/dist/lib/sse.js.map +2 -2
  43. package/dist/plugins/def.d.ts +5 -0
  44. package/dist/plugins/def.js +22 -0
  45. package/dist/plugins/def.js.map +7 -0
  46. package/dist/plugins/directives/attribute-sync.js +8 -12
  47. package/dist/plugins/directives/attribute-sync.js.map +2 -2
  48. package/dist/plugins/directives/boost.d.ts +1 -1
  49. package/dist/plugins/directives/boost.js +34 -30
  50. package/dist/plugins/directives/boost.js.map +2 -2
  51. package/dist/plugins/directives/busy-flag.js +13 -16
  52. package/dist/plugins/directives/busy-flag.js.map +2 -2
  53. package/dist/plugins/directives/class-toggle.js +17 -16
  54. package/dist/plugins/directives/class-toggle.js.map +2 -2
  55. package/dist/plugins/directives/cloak.js +2 -7
  56. package/dist/plugins/directives/cloak.js.map +2 -2
  57. package/dist/plugins/directives/collapse.js +36 -37
  58. package/dist/plugins/directives/collapse.js.map +2 -2
  59. package/dist/plugins/directives/declare.js +5 -8
  60. package/dist/plugins/directives/declare.js.map +2 -2
  61. package/dist/plugins/directives/derive.js +9 -15
  62. package/dist/plugins/directives/derive.js.map +2 -2
  63. package/dist/plugins/directives/element-ref.js +8 -8
  64. package/dist/plugins/directives/element-ref.js.map +2 -2
  65. package/dist/plugins/directives/event-listener.js +18 -23
  66. package/dist/plugins/directives/event-listener.js.map +2 -2
  67. package/dist/plugins/directives/frame-loop.js +9 -14
  68. package/dist/plugins/directives/frame-loop.js.map +2 -2
  69. package/dist/plugins/directives/html.js +6 -7
  70. package/dist/plugins/directives/html.js.map +2 -2
  71. package/dist/plugins/directives/inline-style.js +17 -20
  72. package/dist/plugins/directives/inline-style.js.map +2 -2
  73. package/dist/plugins/directives/mask.js +19 -23
  74. package/dist/plugins/directives/mask.js.map +2 -2
  75. package/dist/plugins/directives/media-query.d.ts +4 -1
  76. package/dist/plugins/directives/media-query.js +13 -12
  77. package/dist/plugins/directives/media-query.js.map +2 -2
  78. package/dist/plugins/directives/on-mount.js +4 -9
  79. package/dist/plugins/directives/on-mount.js.map +2 -2
  80. package/dist/plugins/directives/patch-listener.js +24 -19
  81. package/dist/plugins/directives/patch-listener.js.map +2 -2
  82. package/dist/plugins/directives/remove-me.js +7 -8
  83. package/dist/plugins/directives/remove-me.js.map +2 -2
  84. package/dist/plugins/directives/scroll-to.d.ts +4 -1
  85. package/dist/plugins/directives/scroll-to.js +8 -14
  86. package/dist/plugins/directives/scroll-to.js.map +2 -2
  87. package/dist/plugins/directives/size-watch.js +7 -12
  88. package/dist/plugins/directives/size-watch.js.map +2 -2
  89. package/dist/plugins/directives/state-dump.js +6 -10
  90. package/dist/plugins/directives/state-dump.js.map +2 -2
  91. package/dist/plugins/directives/storage-sync.js +13 -9
  92. package/dist/plugins/directives/storage-sync.js.map +2 -2
  93. package/dist/plugins/directives/teleport.js +8 -14
  94. package/dist/plugins/directives/teleport.js.map +2 -2
  95. package/dist/plugins/directives/text-content.js +6 -7
  96. package/dist/plugins/directives/text-content.js.map +2 -2
  97. package/dist/plugins/directives/timer.js +7 -13
  98. package/dist/plugins/directives/timer.js.map +2 -2
  99. package/dist/plugins/directives/transition.d.ts +1 -2
  100. package/dist/plugins/directives/transition.js +32 -34
  101. package/dist/plugins/directives/transition.js.map +2 -2
  102. package/dist/plugins/directives/tween.d.ts +2 -2
  103. package/dist/plugins/directives/tween.js +34 -50
  104. package/dist/plugins/directives/tween.js.map +2 -2
  105. package/dist/plugins/directives/two-way-bind.d.ts +1 -1
  106. package/dist/plugins/directives/two-way-bind.js +70 -67
  107. package/dist/plugins/directives/two-way-bind.js.map +2 -2
  108. package/dist/plugins/directives/url-params.js +36 -59
  109. package/dist/plugins/directives/url-params.js.map +3 -3
  110. package/dist/plugins/directives/url-replace.js +6 -7
  111. package/dist/plugins/directives/url-replace.js.map +2 -2
  112. package/dist/plugins/directives/validity.js +6 -9
  113. package/dist/plugins/directives/validity.js.map +2 -2
  114. package/dist/plugins/directives/viewport.js +18 -23
  115. package/dist/plugins/directives/viewport.js.map +2 -2
  116. package/dist/plugins/directives/visibility.js +9 -14
  117. package/dist/plugins/directives/visibility.js.map +2 -2
  118. package/dist/plugins/directives/watch.d.ts +0 -1
  119. package/dist/plugins/directives/watch.js +2 -8
  120. package/dist/plugins/directives/watch.js.map +2 -2
  121. package/dist/plugins/functions/bulk-set.js +10 -7
  122. package/dist/plugins/functions/bulk-set.js.map +2 -2
  123. package/dist/plugins/functions/bulk-toggle.js +10 -7
  124. package/dist/plugins/functions/bulk-toggle.js.map +2 -2
  125. package/dist/plugins/functions/confirm.js +2 -2
  126. package/dist/plugins/functions/confirm.js.map +2 -2
  127. package/dist/plugins/functions/copy-text.js +5 -5
  128. package/dist/plugins/functions/copy-text.js.map +2 -2
  129. package/dist/plugins/functions/dispatch.js +5 -5
  130. package/dist/plugins/functions/dispatch.js.map +2 -2
  131. package/dist/plugins/functions/locale-format.d.ts +4 -1
  132. package/dist/plugins/functions/locale-format.js +9 -16
  133. package/dist/plugins/functions/locale-format.js.map +2 -2
  134. package/dist/plugins/functions/range-map.js +6 -6
  135. package/dist/plugins/functions/range-map.js.map +2 -2
  136. package/dist/plugins/functions/request.d.ts +7 -3
  137. package/dist/plugins/functions/request.js +85 -80
  138. package/dist/plugins/functions/request.js.map +2 -2
  139. package/dist/plugins/functions/untracked-read.js +3 -2
  140. package/dist/plugins/functions/untracked-read.js.map +2 -2
  141. package/dist/plugins/functions/websocket.d.ts +3 -3
  142. package/dist/plugins/functions/websocket.js +18 -25
  143. package/dist/plugins/functions/websocket.js.map +2 -2
  144. package/dist/plugins/index.d.ts +34 -36
  145. package/dist/plugins/index.js +31 -35
  146. package/dist/plugins/index.js.map +2 -2
  147. package/dist/plugins/server-events/apply-elements.d.ts +1 -1
  148. package/dist/plugins/server-events/apply-elements.js +41 -40
  149. package/dist/plugins/server-events/apply-elements.js.map +2 -2
  150. package/dist/plugins/server-events/apply-state.js +9 -12
  151. package/dist/plugins/server-events/apply-state.js.map +2 -2
  152. package/dist/plugins/server-events/morph.d.ts +0 -2
  153. package/dist/plugins/server-events/morph.js +58 -55
  154. package/dist/plugins/server-events/morph.js.map +3 -3
  155. package/dist/presets/all.d.ts +0 -1
  156. package/dist/presets/all.js +22 -26
  157. package/dist/presets/all.js.map +2 -2
  158. package/dist/presets/essentials.d.ts +0 -1
  159. package/dist/presets/essentials.js +27 -9
  160. package/dist/presets/essentials.js.map +2 -2
  161. package/dist/presets/minimal.d.ts +0 -1
  162. package/dist/presets/minimal.js +2 -2
  163. package/dist/presets/minimal.js.map +2 -2
  164. package/dist/server.d.ts +108 -0
  165. package/dist/server.js +218 -0
  166. package/dist/server.js.map +7 -0
  167. package/dist/sigmx.standalone.js +1 -1
  168. package/dist/standalone.js.map +2 -2
  169. package/dist/vite.d.ts +2 -2
  170. package/dist/vite.js +48 -15
  171. package/dist/vite.js.map +2 -2
  172. package/package.json +21 -2
  173. package/dist/auto.d.ts +0 -1
  174. package/dist/auto.js +0 -7
  175. package/dist/auto.js.map +0 -7
  176. package/dist/core/csp.d.ts +0 -7
  177. package/dist/core/csp.js +0 -20
  178. package/dist/core/csp.js.map +0 -7
  179. package/dist/core/engine.d.ts +0 -10
  180. package/dist/core/engine.js +0 -200
  181. package/dist/core/engine.js.map +0 -7
  182. package/dist/core/expr.d.ts +0 -12
  183. package/dist/core/expr.js +0 -94
  184. package/dist/core/expr.js.map +0 -7
  185. package/dist/core/index.d.ts +0 -16
  186. package/dist/core/index.js +0 -46
  187. package/dist/core/index.js.map +0 -7
  188. package/dist/core/signals.d.ts +0 -42
  189. package/dist/core/signals.js +0 -195
  190. package/dist/core/signals.js.map +0 -7
  191. package/dist/core/store.d.ts +0 -32
  192. package/dist/core/store.js +0 -185
  193. package/dist/core/store.js.map +0 -7
  194. package/dist/core/types.d.ts +0 -101
  195. package/dist/core/types.js +0 -1
  196. package/dist/core/types.js.map +0 -7
  197. package/dist/plugins/actions/clipboard.d.ts +0 -2
  198. package/dist/plugins/actions/clipboard.js +0 -11
  199. package/dist/plugins/actions/clipboard.js.map +0 -7
  200. package/dist/plugins/actions/fit.d.ts +0 -2
  201. package/dist/plugins/actions/fit.js +0 -14
  202. package/dist/plugins/actions/fit.js.map +0 -7
  203. package/dist/plugins/actions/intl.d.ts +0 -2
  204. package/dist/plugins/actions/intl.js +0 -21
  205. package/dist/plugins/actions/intl.js.map +0 -7
  206. package/dist/plugins/actions/peek.d.ts +0 -2
  207. package/dist/plugins/actions/peek.js +0 -6
  208. package/dist/plugins/actions/peek.js.map +0 -7
  209. package/dist/plugins/actions/setAll.d.ts +0 -2
  210. package/dist/plugins/actions/setAll.js +0 -11
  211. package/dist/plugins/actions/setAll.js.map +0 -7
  212. package/dist/plugins/actions/toggleAll.d.ts +0 -2
  213. package/dist/plugins/actions/toggleAll.js +0 -11
  214. package/dist/plugins/actions/toggleAll.js.map +0 -7
  215. package/dist/plugins/attributes/attr.d.ts +0 -2
  216. package/dist/plugins/attributes/attr.js +0 -20
  217. package/dist/plugins/attributes/attr.js.map +0 -7
  218. package/dist/plugins/attributes/bind.d.ts +0 -6
  219. package/dist/plugins/attributes/bind.js +0 -81
  220. package/dist/plugins/attributes/bind.js.map +0 -7
  221. package/dist/plugins/attributes/class.d.ts +0 -2
  222. package/dist/plugins/attributes/class.js +0 -22
  223. package/dist/plugins/attributes/class.js.map +0 -7
  224. package/dist/plugins/attributes/computed.d.ts +0 -2
  225. package/dist/plugins/attributes/computed.js +0 -20
  226. package/dist/plugins/attributes/computed.js.map +0 -7
  227. package/dist/plugins/attributes/customValidity.d.ts +0 -2
  228. package/dist/plugins/attributes/customValidity.js +0 -14
  229. package/dist/plugins/attributes/customValidity.js.map +0 -7
  230. package/dist/plugins/attributes/effect.d.ts +0 -2
  231. package/dist/plugins/attributes/effect.js +0 -12
  232. package/dist/plugins/attributes/effect.js.map +0 -7
  233. package/dist/plugins/attributes/indicator.d.ts +0 -2
  234. package/dist/plugins/attributes/indicator.js +0 -20
  235. package/dist/plugins/attributes/indicator.js.map +0 -7
  236. package/dist/plugins/attributes/init.d.ts +0 -2
  237. package/dist/plugins/attributes/init.js +0 -14
  238. package/dist/plugins/attributes/init.js.map +0 -7
  239. package/dist/plugins/attributes/jsonSignals.d.ts +0 -2
  240. package/dist/plugins/attributes/jsonSignals.js +0 -15
  241. package/dist/plugins/attributes/jsonSignals.js.map +0 -7
  242. package/dist/plugins/attributes/matchMedia.d.ts +0 -2
  243. package/dist/plugins/attributes/matchMedia.js +0 -19
  244. package/dist/plugins/attributes/matchMedia.js.map +0 -7
  245. package/dist/plugins/attributes/on.d.ts +0 -5
  246. package/dist/plugins/attributes/on.js +0 -28
  247. package/dist/plugins/attributes/on.js.map +0 -7
  248. package/dist/plugins/attributes/onIntersect.d.ts +0 -2
  249. package/dist/plugins/attributes/onIntersect.js +0 -28
  250. package/dist/plugins/attributes/onIntersect.js.map +0 -7
  251. package/dist/plugins/attributes/onInterval.d.ts +0 -2
  252. package/dist/plugins/attributes/onInterval.js +0 -18
  253. package/dist/plugins/attributes/onInterval.js.map +0 -7
  254. package/dist/plugins/attributes/onRaf.d.ts +0 -1
  255. package/dist/plugins/attributes/onRaf.js +0 -19
  256. package/dist/plugins/attributes/onRaf.js.map +0 -7
  257. package/dist/plugins/attributes/onResize.d.ts +0 -1
  258. package/dist/plugins/attributes/onResize.js +0 -17
  259. package/dist/plugins/attributes/onResize.js.map +0 -7
  260. package/dist/plugins/attributes/onSignalPatch.d.ts +0 -5
  261. package/dist/plugins/attributes/onSignalPatch.js +0 -35
  262. package/dist/plugins/attributes/onSignalPatch.js.map +0 -7
  263. package/dist/plugins/attributes/persist.d.ts +0 -6
  264. package/dist/plugins/attributes/persist.js +0 -16
  265. package/dist/plugins/attributes/persist.js.map +0 -7
  266. package/dist/plugins/attributes/queryString.d.ts +0 -5
  267. package/dist/plugins/attributes/queryString.js +0 -54
  268. package/dist/plugins/attributes/queryString.js.map +0 -7
  269. package/dist/plugins/attributes/ref.d.ts +0 -2
  270. package/dist/plugins/attributes/ref.js +0 -13
  271. package/dist/plugins/attributes/ref.js.map +0 -7
  272. package/dist/plugins/attributes/replaceUrl.d.ts +0 -1
  273. package/dist/plugins/attributes/replaceUrl.js +0 -11
  274. package/dist/plugins/attributes/replaceUrl.js.map +0 -7
  275. package/dist/plugins/attributes/scrollIntoView.d.ts +0 -2
  276. package/dist/plugins/attributes/scrollIntoView.js +0 -19
  277. package/dist/plugins/attributes/scrollIntoView.js.map +0 -7
  278. package/dist/plugins/attributes/show.d.ts +0 -2
  279. package/dist/plugins/attributes/show.js +0 -19
  280. package/dist/plugins/attributes/show.js.map +0 -7
  281. package/dist/plugins/attributes/signals.d.ts +0 -2
  282. package/dist/plugins/attributes/signals.js +0 -13
  283. package/dist/plugins/attributes/signals.js.map +0 -7
  284. package/dist/plugins/attributes/style.d.ts +0 -2
  285. package/dist/plugins/attributes/style.js +0 -25
  286. package/dist/plugins/attributes/style.js.map +0 -7
  287. package/dist/plugins/attributes/text.d.ts +0 -1
  288. package/dist/plugins/attributes/text.js +0 -13
  289. package/dist/plugins/attributes/text.js.map +0 -7
  290. package/dist/plugins/attributes/viewTransition.d.ts +0 -1
  291. package/dist/plugins/attributes/viewTransition.js +0 -11
  292. package/dist/plugins/attributes/viewTransition.js.map +0 -7
  293. package/dist/plugins/directives/transition-name.d.ts +0 -1
  294. package/dist/plugins/directives/transition-name.js +0 -11
  295. package/dist/plugins/directives/transition-name.js.map +0 -7
  296. package/dist/plugins/directives/trap.d.ts +0 -5
  297. package/dist/plugins/directives/trap.js +0 -48
  298. package/dist/plugins/directives/trap.js.map +0 -7
  299. package/dist/sigmx.auto.js +0 -5
  300. package/dist/utils/object.d.ts +0 -9
  301. package/dist/utils/object.js +0 -26
  302. package/dist/utils/object.js.map +0 -7
  303. package/dist/utils/text.d.ts +0 -7
  304. package/dist/utils/text.js +0 -15
  305. package/dist/utils/text.js.map +0 -7
  306. package/dist/utils/timing.d.ts +0 -14
  307. package/dist/utils/timing.js +0 -58
  308. package/dist/utils/timing.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/plugins/functions/range-map.ts"],
4
- "sourcesContent": ["import { action } from '../../kernel/index.js'\n\n/** Map `v` from [inMin, inMax] onto [outMin, outMax]; optionally clamp and round. */\nexport const fit = action({\n name: 'fit',\n call: (_, v: number, inMin: number, inMax: number, outMin: number, outMax: number, clamp = false, round = false) => {\n let t = (v - inMin) / (inMax - inMin)\n if (clamp) t = Math.min(1, Math.max(0, t))\n const out = outMin + t * (outMax - outMin)\n return round ? Math.round(out) : out\n },\n})\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AAGhB,MAAM,MAAM,OAAO;AAAA,EACxB,MAAM;AAAA,EACN,MAAM,CAAC,GAAG,GAAW,OAAe,OAAe,QAAgB,QAAgB,QAAQ,OAAO,QAAQ,UAAU;AAClH,QAAI,KAAK,IAAI,UAAU,QAAQ;AAC/B,QAAI,MAAO,KAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACzC,UAAM,MAAM,SAAS,KAAK,SAAS;AACnC,WAAO,QAAQ,KAAK,MAAM,GAAG,IAAI;AAAA,EACnC;AACF,CAAC;",
4
+ "sourcesContent": ["import { act } from '../def.js';\n\n/** Map `v` from [inMin, inMax] onto [outMin, outMax]; optionally clamp and round. */\nexport const fit = act(\n 'fit',\n (_, v: number, inMin: number, inMax: number, outMin: number, outMax: number, clamp = false, round = false) => {\n let t = inMax === inMin ? 0 : (v - inMin) / (inMax - inMin);\n if (clamp) t = Math.min(1, Math.max(0, t));\n const out = outMin + t * (outMax - outMin);\n return round ? Math.round(out) : out;\n },\n);\n"],
5
+ "mappings": "AAAA,SAAS,WAAW;AAGb,MAAM,MAAM;AAAA,EACjB;AAAA,EACA,CAAC,GAAG,GAAW,OAAe,OAAe,QAAgB,QAAgB,QAAQ,OAAO,QAAQ,UAAU;AAC5G,QAAI,IAAI,UAAU,QAAQ,KAAK,IAAI,UAAU,QAAQ;AACrD,QAAI,MAAO,KAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACzC,UAAM,MAAM,SAAS,KAAK,SAAS;AACnC,WAAO,QAAQ,KAAK,MAAM,GAAG,IAAI;AAAA,EACnC;AACF;",
6
6
  "names": []
7
7
  }
@@ -9,9 +9,7 @@ export type RequestOptions = {
9
9
  /** Explicit body instead of signals. */
10
10
  payload?: unknown;
11
11
  /** 'replace' (default) aborts an earlier request with the same method and URL; 'none' lets them overlap. */
12
- abort?: 'replace' | 'none' | AbortController;
13
- /** Keep the connection open while the tab is hidden. Default: true except for GET streams. */
14
- openWhenHidden?: boolean;
12
+ abort?: 'replace' | 'none';
15
13
  /** Reconnect after a stream closes normally (long-lived event streams). */
16
14
  reconnect?: boolean;
17
15
  retry?: {
@@ -22,6 +20,12 @@ export type RequestOptions = {
22
20
  onStatusError?: boolean;
23
21
  };
24
22
  };
23
+ /** What a request resolves with: the last response that was applied (undefined when nothing was sent). */
24
+ export type RequestResult = {
25
+ url: string;
26
+ status: number;
27
+ redirected: boolean;
28
+ } | undefined;
25
29
  /** `@get(url, options)`: signals travel in the `sigmx` query parameter. */
26
30
  export declare const httpGet: import("../../kernel/contracts.js").ActionPlugin;
27
31
  export declare const httpPost: import("../../kernel/contracts.js").ActionPlugin;
@@ -1,13 +1,21 @@
1
- import { action, untracked } from "../../kernel/index.js";
1
+ import { untracked } from "../../kernel/index.js";
2
2
  import { camel } from "../../lib/casing.js";
3
3
  import { parseFields, readEvents } from "../../lib/sse.js";
4
+ import { act } from "../def.js";
4
5
  const inflight = /* @__PURE__ */ new Map();
5
6
  let seq = 0;
6
7
  const sleep = (ms, signal) => new Promise((resolve) => {
8
+ if (signal.aborted) return resolve();
7
9
  const t = setTimeout(resolve, ms);
8
- signal.addEventListener("abort", () => (clearTimeout(t), resolve()), { once: true });
10
+ signal.addEventListener(
11
+ "abort",
12
+ () => {
13
+ clearTimeout(t);
14
+ resolve();
15
+ },
16
+ { once: true }
17
+ );
9
18
  });
10
- const untilVisible = () => new Promise((resolve) => document.addEventListener("visibilitychange", () => !document.hidden && resolve(), { once: true }));
11
19
  const headerArgs = (res, names, data) => {
12
20
  for (const n of names) {
13
21
  const v = res.headers.get(`sigmx-${n}`);
@@ -15,128 +23,125 @@ const headerArgs = (res, names, data) => {
15
23
  }
16
24
  return data;
17
25
  };
18
- const runScript = (text) => {
19
- const s = document.createElement("script");
20
- s.text = text;
21
- document.head.append(s);
22
- s.remove();
23
- };
24
26
  const send = async (method, ctx, url, o = {}) => {
25
27
  const { el, evt, store, runtime, error } = ctx;
26
28
  if (!url) throw error(`@${method.toLowerCase()} needs a URL`);
27
- const key = `${method} ${url}`;
28
- const ac = o.abort instanceof AbortController ? o.abort : new AbortController();
29
+ const u = new URL(url, location.href);
30
+ const key = `${method} ${u.href}`;
31
+ const hasBody = method !== "GET" && method !== "DELETE";
32
+ const headers = new Headers({ Accept: "text/event-stream, text/html, application/json", "Sigmx-Request": "true" });
33
+ for (const [k, v] of new Headers(o.headers)) headers.set(k, v);
34
+ let body;
35
+ const contentType = (t) => {
36
+ if (hasBody && !headers.has("Content-Type")) headers.set("Content-Type", t);
37
+ };
38
+ if ((o.contentType ?? "json") === "json") {
39
+ const json = JSON.stringify(o.payload ?? untracked(() => store.snapshot(o.filter ?? { exclude: /(^|\.)_/ })));
40
+ if (hasBody) {
41
+ body = json;
42
+ contentType("application/json");
43
+ } else u.searchParams.set("sigmx", json);
44
+ } else {
45
+ const form = o.selector ? document.querySelector(o.selector) : el.closest("form");
46
+ if (!form) throw error("no form found", { selector: o.selector });
47
+ if (!form.noValidate && !form.checkValidity()) {
48
+ form.reportValidity();
49
+ return;
50
+ }
51
+ const submitter = evt instanceof SubmitEvent ? evt.submitter : el instanceof HTMLButtonElement && el.form === form && el.type === "submit" ? el : null;
52
+ const fd = new FormData(form, submitter);
53
+ const params = new URLSearchParams(fd);
54
+ if (!hasBody) for (const [k, v] of params) u.searchParams.append(k, v);
55
+ else if (form.enctype === "multipart/form-data") body = fd;
56
+ else {
57
+ body = params;
58
+ contentType("application/x-www-form-urlencoded");
59
+ }
60
+ }
61
+ const ac = new AbortController();
29
62
  if (o.abort !== "none") {
30
63
  inflight.get(key)?.abort();
31
64
  inflight.set(key, ac);
32
65
  }
33
- ctx.cleanup(() => ac.abort());
66
+ const forget = ctx.cleanup(() => ac.abort());
34
67
  const rid = ++seq;
35
- const emit = (type, extra = {}) => runtime.emit("fetch", { el, type, method, url, rid, ...extra });
36
- const retry = { attempts: 5, interval: 1e3, factor: 2, max: 3e4, onStatusError: false, ...o.retry };
37
- const pauseWhenHidden = !(o.openWhenHidden ?? method !== "GET");
38
- const bodyAllowed = method !== "GET" && method !== "DELETE";
39
- const build = () => {
40
- const u = new URL(url, location.href);
41
- const headers = {
42
- Accept: "text/event-stream, text/html, application/json",
43
- "Sigmx-Request": "true",
44
- ...o.headers
45
- };
46
- let body;
47
- if ((o.contentType ?? "json") === "json") {
48
- const payload = o.payload ?? untracked(() => store.snapshot(o.filter ?? { exclude: /(^|\.)_/ }));
49
- const json = JSON.stringify(payload);
50
- if (bodyAllowed) {
51
- body = json;
52
- headers["Content-Type"] ??= "application/json";
53
- } else u.searchParams.set("sigmx", json);
54
- } else {
55
- const form = o.selector ? document.querySelector(o.selector) : el.closest("form");
56
- if (!form) throw error("no form found", { selector: o.selector });
57
- if (!form.noValidate && !form.checkValidity()) {
58
- form.reportValidity();
59
- return;
60
- }
61
- const submitter = evt instanceof SubmitEvent ? evt.submitter : el instanceof HTMLButtonElement ? el : null;
62
- const fd = new FormData(form, submitter);
63
- const multipart = form.enctype === "multipart/form-data";
64
- if (bodyAllowed) {
65
- body = multipart ? fd : new URLSearchParams(fd);
66
- if (!multipart) headers["Content-Type"] = "application/x-www-form-urlencoded";
67
- } else {
68
- for (const [k, v] of new URLSearchParams(fd)) u.searchParams.append(k, v);
69
- }
70
- }
71
- return { url: u.toString(), init: { method, headers, body } };
72
- };
73
- const route = (event, data) => {
74
- const fields = parseFields(data);
75
- runtime.emit("server-event", { el, event, data: fields });
76
- runtime.handle(event, fields);
77
- };
68
+ const emit = (type, extra) => runtime.emit("fetch", { el, type, method, url, rid, ...extra });
69
+ const retry = { attempts: 5, interval: 1e3, factor: 2, max: 3e4, ...o.retry };
78
70
  let attempt = 0;
79
71
  let wait = retry.interval;
80
72
  let lastId;
73
+ let last;
74
+ let applyError;
75
+ const apply = (fn) => {
76
+ try {
77
+ fn();
78
+ } catch (e) {
79
+ applyError = e;
80
+ throw e;
81
+ }
82
+ };
81
83
  const backoff = async () => {
82
84
  if (attempt++ >= retry.attempts) throw new Error("retries exhausted");
83
- emit("retrying", { attempt });
84
85
  await sleep(wait, ac.signal);
85
86
  wait = Math.min(wait * retry.factor, retry.max);
86
87
  };
87
88
  emit("started");
88
89
  try {
89
90
  while (!ac.signal.aborted) {
90
- const req = build();
91
- if (!req) return;
92
- if (lastId) req.init.headers["Last-Event-ID"] = lastId;
93
- const hide = new AbortController();
94
- const onHide = () => document.hidden && hide.abort();
95
- if (pauseWhenHidden) document.addEventListener("visibilitychange", onHide);
91
+ if (lastId) headers.set("Last-Event-ID", lastId);
96
92
  try {
97
- const res = await fetch(req.url, { ...req.init, signal: AbortSignal.any([ac.signal, hide.signal]) });
93
+ const res = await fetch(u.href, { method, headers, body, signal: ac.signal });
94
+ last = { url: res.url, status: res.status, redirected: res.redirected };
98
95
  const ct = res.headers.get("content-type") ?? "";
99
- if (res.status >= 400) {
96
+ if (!res.ok) {
100
97
  emit("error", { status: res.status });
101
- if (!retry.onStatusError) return;
98
+ if (!retry.onStatusError) return last;
102
99
  await backoff();
103
100
  continue;
104
101
  }
105
- if (res.status === 204 || res.status === 304 || !res.body) return;
102
+ if (!res.body) return last;
106
103
  if (ct.includes("text/event-stream")) {
107
104
  attempt = 0;
108
105
  wait = retry.interval;
109
106
  await readEvents(res.body, (e) => {
110
107
  if (e.id !== void 0) lastId = e.id;
111
108
  if (e.retry) wait = retry.interval = e.retry;
112
- route(e.event, e.data);
109
+ const data = parseFields(e.data);
110
+ apply(() => {
111
+ runtime.emit("server-event", { el, event: e.event, data });
112
+ runtime.handle(e.event, data);
113
+ });
113
114
  });
114
- if (!o.reconnect) return;
115
+ if (!o.reconnect) return last;
115
116
  await backoff();
116
117
  continue;
117
118
  }
118
119
  const text = await res.text();
119
- if (ct.includes("text/html")) runtime.handle("patch-elements", headerArgs(res, ["selector", "mode", "use-view-transition"], { elements: text }));
120
- else if (ct.includes("application/json")) runtime.handle("patch-signals", headerArgs(res, ["only-if-missing"], { signals: text }));
121
- else if (ct.includes("javascript")) runScript(text);
122
- return;
123
- } catch {
124
- if (ac.signal.aborted) return;
125
- if (hide.signal.aborted) await untilVisible();
126
- else await backoff();
127
- } finally {
128
- document.removeEventListener("visibilitychange", onHide);
120
+ apply(() => {
121
+ if (ct.includes("text/html"))
122
+ runtime.handle("patch-elements", headerArgs(res, ["selector", "mode"], { elements: text }));
123
+ else if (ct.includes("application/json"))
124
+ runtime.handle("patch-signals", headerArgs(res, ["only-if-missing"], { signals: text }));
125
+ });
126
+ return last;
127
+ } catch (e) {
128
+ if (ac.signal.aborted) return last;
129
+ if (applyError) throw e;
130
+ await backoff();
129
131
  }
130
132
  }
133
+ return last;
131
134
  } catch (e) {
132
135
  emit("error", { message: e?.message });
133
136
  throw error(`${method} ${url}: ${e?.message ?? e}`);
134
137
  } finally {
135
- emit("finished");
138
+ ac.abort();
139
+ forget();
140
+ emit("finished", last);
136
141
  if (inflight.get(key) === ac) inflight.delete(key);
137
142
  }
138
143
  };
139
- const make = (name, method) => action({ name, call: (ctx, url, o) => send(method, ctx, url, o) });
144
+ const make = (name, method) => act(name, (ctx, url, o) => send(method, ctx, url, o));
140
145
  const httpGet = make("get", "GET");
141
146
  const httpPost = make("post", "POST");
142
147
  const httpPut = make("put", "PUT");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/plugins/functions/request.ts"],
4
- "sourcesContent": ["import { action, untracked, type ActionCtx, type Filter } from '../../kernel/index.js'\nimport { camel } from '../../lib/casing.js'\nimport { parseFields, readEvents } from '../../lib/sse.js'\n\nexport type RequestOptions = {\n headers?: Record<string, string>\n /** 'json' (default) sends filtered signals; 'form' serialises the closest (or `selector`) form. */\n contentType?: 'json' | 'form'\n selector?: string\n /** Which signals to send. Default: everything except paths starting with `_`. */\n filter?: Filter\n /** Explicit body instead of signals. */\n payload?: unknown\n /** 'replace' (default) aborts an earlier request with the same method and URL; 'none' lets them overlap. */\n abort?: 'replace' | 'none' | AbortController\n /** Keep the connection open while the tab is hidden. Default: true except for GET streams. */\n openWhenHidden?: boolean\n /** Reconnect after a stream closes normally (long-lived event streams). */\n reconnect?: boolean\n retry?: { attempts?: number; interval?: number; factor?: number; max?: number; onStatusError?: boolean }\n}\n\nconst inflight = new Map<string, AbortController>()\nlet seq = 0\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const t = setTimeout(resolve, ms)\n signal.addEventListener('abort', () => (clearTimeout(t), resolve()), { once: true })\n })\nconst untilVisible = () =>\n new Promise<void>((resolve) => document.addEventListener('visibilitychange', () => !document.hidden && resolve(), { once: true }))\n\nconst headerArgs = (res: Response, names: string[], data: Record<string, string>) => {\n for (const n of names) {\n const v = res.headers.get(`sigmx-${n}`)\n if (v) data[camel(n)] = v\n }\n return data\n}\n\nconst runScript = (text: string) => {\n const s = document.createElement('script')\n s.text = text\n document.head.append(s)\n s.remove()\n}\n\nconst send = async (method: string, ctx: ActionCtx, url: string, o: RequestOptions = {}): Promise<void> => {\n const { el, evt, store, runtime, error } = ctx\n if (!url) throw error(`@${method.toLowerCase()} needs a URL`)\n const key = `${method} ${url}`\n const ac = o.abort instanceof AbortController ? o.abort : new AbortController()\n if (o.abort !== 'none') {\n inflight.get(key)?.abort()\n inflight.set(key, ac)\n }\n ctx.cleanup(() => ac.abort())\n const rid = ++seq\n const emit = (type: string, extra: Record<string, unknown> = {}) => runtime.emit('fetch', { el, type, method, url, rid, ...extra })\n const retry = { attempts: 5, interval: 1000, factor: 2, max: 30_000, onStatusError: false, ...o.retry }\n const pauseWhenHidden = !(o.openWhenHidden ?? method !== 'GET')\n const bodyAllowed = method !== 'GET' && method !== 'DELETE'\n\n const build = (): { url: string; init: RequestInit } | undefined => {\n const u = new URL(url, location.href)\n const headers: Record<string, string> = {\n Accept: 'text/event-stream, text/html, application/json',\n 'Sigmx-Request': 'true',\n ...o.headers,\n }\n let body: BodyInit | undefined\n if ((o.contentType ?? 'json') === 'json') {\n const payload = o.payload ?? untracked(() => store.snapshot(o.filter ?? { exclude: /(^|\\.)_/ }))\n const json = JSON.stringify(payload)\n if (bodyAllowed) {\n body = json\n headers['Content-Type'] ??= 'application/json'\n } else u.searchParams.set('sigmx', json)\n } else {\n const form = (o.selector ? document.querySelector(o.selector) : el.closest('form')) as HTMLFormElement | null\n if (!form) throw error('no form found', { selector: o.selector })\n if (!form.noValidate && !form.checkValidity()) {\n form.reportValidity()\n return\n }\n const submitter = evt instanceof SubmitEvent ? evt.submitter : el instanceof HTMLButtonElement ? el : null\n const fd = new FormData(form, submitter)\n const multipart = form.enctype === 'multipart/form-data'\n if (bodyAllowed) {\n body = multipart ? fd : new URLSearchParams(fd as unknown as Record<string, string>)\n if (!multipart) headers['Content-Type'] = 'application/x-www-form-urlencoded'\n } else {\n for (const [k, v] of new URLSearchParams(fd as unknown as Record<string, string>)) u.searchParams.append(k, v)\n }\n }\n return { url: u.toString(), init: { method, headers, body } }\n }\n\n const route = (event: string, data: string) => {\n const fields = parseFields(data)\n runtime.emit('server-event', { el, event, data: fields })\n runtime.handle(event, fields)\n }\n\n let attempt = 0\n let wait = retry.interval\n let lastId: string | undefined\n const backoff = async () => {\n if (attempt++ >= retry.attempts) throw new Error('retries exhausted')\n emit('retrying', { attempt })\n await sleep(wait, ac.signal)\n wait = Math.min(wait * retry.factor, retry.max)\n }\n\n emit('started')\n try {\n while (!ac.signal.aborted) {\n const req = build()\n if (!req) return\n if (lastId) (req.init.headers as Record<string, string>)['Last-Event-ID'] = lastId\n const hide = new AbortController()\n const onHide = () => document.hidden && hide.abort()\n if (pauseWhenHidden) document.addEventListener('visibilitychange', onHide)\n try {\n const res = await fetch(req.url, { ...req.init, signal: AbortSignal.any([ac.signal, hide.signal]) })\n const ct = res.headers.get('content-type') ?? ''\n if (res.status >= 400) {\n emit('error', { status: res.status })\n if (!retry.onStatusError) return\n await backoff()\n continue\n }\n if (res.status === 204 || res.status === 304 || !res.body) return\n if (ct.includes('text/event-stream')) {\n attempt = 0\n wait = retry.interval\n await readEvents(res.body, (e) => {\n if (e.id !== undefined) lastId = e.id\n if (e.retry) wait = retry.interval = e.retry\n route(e.event, e.data)\n })\n if (!o.reconnect) return\n await backoff()\n continue\n }\n const text = await res.text()\n if (ct.includes('text/html')) runtime.handle('patch-elements', headerArgs(res, ['selector', 'mode', 'use-view-transition'], { elements: text }))\n else if (ct.includes('application/json')) runtime.handle('patch-signals', headerArgs(res, ['only-if-missing'], { signals: text }))\n else if (ct.includes('javascript')) runScript(text)\n return\n } catch {\n if (ac.signal.aborted) return\n if (hide.signal.aborted) await untilVisible()\n else await backoff()\n } finally {\n document.removeEventListener('visibilitychange', onHide)\n }\n }\n } catch (e: any) {\n emit('error', { message: e?.message })\n throw error(`${method} ${url}: ${e?.message ?? e}`)\n } finally {\n emit('finished')\n if (inflight.get(key) === ac) inflight.delete(key)\n }\n}\n\nconst make = (name: string, method: string) => action({ name, call: (ctx, url: string, o?: RequestOptions) => send(method, ctx, url, o) })\n\n/** `@get(url, options)`: signals travel in the `sigmx` query parameter. */\nexport const httpGet = make('get', 'GET')\nexport const httpPost = make('post', 'POST')\nexport const httpPut = make('put', 'PUT')\nexport const httpPatch = make('patch', 'PATCH')\nexport const httpDelete = make('delete', 'DELETE')\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type ActionCtx, type Filter, untracked } from '../../kernel/index.js';\nimport { camel } from '../../lib/casing.js';\nimport { parseFields, readEvents } from '../../lib/sse.js';\nimport { act } from '../def.js';\n\nexport type RequestOptions = {\n headers?: Record<string, string>;\n /** 'json' (default) sends filtered signals; 'form' serialises the closest (or `selector`) form. */\n contentType?: 'json' | 'form';\n selector?: string;\n /** Which signals to send. Default: everything except paths starting with `_`. */\n filter?: Filter;\n /** Explicit body instead of signals. */\n payload?: unknown;\n /** 'replace' (default) aborts an earlier request with the same method and URL; 'none' lets them overlap. */\n abort?: 'replace' | 'none';\n /** Reconnect after a stream closes normally (long-lived event streams). */\n reconnect?: boolean;\n retry?: { attempts?: number; interval?: number; factor?: number; max?: number; onStatusError?: boolean };\n};\n\n/** What a request resolves with: the last response that was applied (undefined when nothing was sent). */\nexport type RequestResult = { url: string; status: number; redirected: boolean } | undefined;\n\nconst inflight = new Map<string, AbortController>();\nlet seq = 0;\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) return resolve();\n const t = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(t);\n resolve();\n },\n { once: true },\n );\n });\n\n/** Response headers `sigmx-<name>` become camel-cased fields of `data`. */\nconst headerArgs = (res: Response, names: string[], data: Record<string, string>) => {\n for (const n of names) {\n const v = res.headers.get(`sigmx-${n}`);\n if (v) data[camel(n)] = v;\n }\n return data;\n};\n\nconst send = async (method: string, ctx: ActionCtx, url: string, o: RequestOptions = {}): Promise<RequestResult> => {\n const { el, evt, store, runtime, error } = ctx;\n if (!url) throw error(`@${method.toLowerCase()} needs a URL`);\n const u = new URL(url, location.href);\n const key = `${method} ${u.href}`; // resolved, before any signals are appended\n const hasBody = method !== 'GET' && method !== 'DELETE';\n const headers = new Headers({ Accept: 'text/event-stream, text/html, application/json', 'Sigmx-Request': 'true' });\n for (const [k, v] of new Headers(o.headers)) headers.set(k, v);\n let body: BodyInit | undefined;\n const contentType = (t: string) => {\n if (hasBody && !headers.has('Content-Type')) headers.set('Content-Type', t);\n };\n if ((o.contentType ?? 'json') === 'json') {\n const json = JSON.stringify(o.payload ?? untracked(() => store.snapshot(o.filter ?? { exclude: /(^|\\.)_/ })));\n if (hasBody) {\n body = json;\n contentType('application/json');\n } else u.searchParams.set('sigmx', json);\n } else {\n const form = (o.selector ? document.querySelector(o.selector) : el.closest('form')) as HTMLFormElement | null;\n if (!form) throw error('no form found', { selector: o.selector });\n if (!form.noValidate && !form.checkValidity()) {\n form.reportValidity();\n return;\n }\n // Only a real submit button may be the submitter; FormData throws for anything else.\n const submitter =\n evt instanceof SubmitEvent\n ? evt.submitter\n : el instanceof HTMLButtonElement && el.form === form && el.type === 'submit'\n ? el\n : null;\n const fd = new FormData(form, submitter);\n const params = new URLSearchParams(fd as unknown as Record<string, string>);\n if (!hasBody) for (const [k, v] of params) u.searchParams.append(k, v);\n else if (form.enctype === 'multipart/form-data') body = fd;\n else {\n body = params;\n contentType('application/x-www-form-urlencoded');\n }\n }\n\n const ac = new AbortController();\n if (o.abort !== 'none') {\n inflight.get(key)?.abort();\n inflight.set(key, ac);\n }\n const forget = ctx.cleanup(() => ac.abort());\n const rid = ++seq;\n const emit = (type: string, extra?: object) => runtime.emit('fetch', { el, type, method, url, rid, ...extra });\n const retry = { attempts: 5, interval: 1000, factor: 2, max: 30_000, ...o.retry };\n let attempt = 0;\n let wait = retry.interval;\n let lastId: string | undefined;\n let last: RequestResult;\n // Exceptions thrown while applying a response are the page's problem, not the network's: no retry.\n let applyError: unknown;\n const apply = (fn: () => void) => {\n try {\n fn();\n } catch (e) {\n applyError = e;\n throw e;\n }\n };\n const backoff = async () => {\n if (attempt++ >= retry.attempts) throw new Error('retries exhausted');\n await sleep(wait, ac.signal);\n wait = Math.min(wait * retry.factor, retry.max);\n };\n\n emit('started');\n try {\n while (!ac.signal.aborted) {\n if (lastId) headers.set('Last-Event-ID', lastId);\n try {\n const res = await fetch(u.href, { method, headers, body, signal: ac.signal });\n last = { url: res.url, status: res.status, redirected: res.redirected };\n const ct = res.headers.get('content-type') ?? '';\n if (!res.ok) {\n emit('error', { status: res.status });\n if (!retry.onStatusError) return last;\n await backoff();\n continue;\n }\n if (!res.body) return last;\n if (ct.includes('text/event-stream')) {\n attempt = 0;\n wait = retry.interval;\n await readEvents(res.body, (e) => {\n if (e.id !== undefined) lastId = e.id;\n if (e.retry) wait = retry.interval = e.retry;\n const data = parseFields(e.data);\n apply(() => {\n runtime.emit('server-event', { el, event: e.event, data });\n runtime.handle(e.event, data);\n });\n });\n if (!o.reconnect) return last;\n await backoff();\n continue;\n }\n const text = await res.text();\n apply(() => {\n if (ct.includes('text/html'))\n runtime.handle('patch-elements', headerArgs(res, ['selector', 'mode'], { elements: text }));\n else if (ct.includes('application/json'))\n runtime.handle('patch-signals', headerArgs(res, ['only-if-missing'], { signals: text }));\n });\n return last;\n } catch (e) {\n if (ac.signal.aborted) return last;\n if (applyError) throw e;\n await backoff();\n }\n }\n return last;\n } catch (e: any) {\n emit('error', { message: e?.message });\n throw error(`${method} ${url}: ${e?.message ?? e}`);\n } finally {\n ac.abort(); // releases a still-open body and any pending backoff\n forget();\n emit('finished', last);\n if (inflight.get(key) === ac) inflight.delete(key);\n }\n};\n\nconst make = (name: string, method: string) =>\n act(name, (ctx, url: string, o?: RequestOptions) => send(method, ctx, url, o));\n\n/** `@get(url, options)`: signals travel in the `sigmx` query parameter. */\nexport const httpGet = make('get', 'GET');\nexport const httpPost = make('post', 'POST');\nexport const httpPut = make('put', 'PUT');\nexport const httpPatch = make('patch', 'PATCH');\nexport const httpDelete = make('delete', 'DELETE');\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
- import { action, untracked } from "../../kernel/index.js";
2
- const peek = action({ name: "peek", call: (_, fn) => untracked(fn) });
1
+ import { untracked } from "../../kernel/index.js";
2
+ import { act } from "../def.js";
3
+ const peek = act("peek", (_, fn) => typeof fn === "function" ? untracked(fn) : fn);
3
4
  export {
4
5
  peek
5
6
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/plugins/functions/untracked-read.ts"],
4
- "sourcesContent": ["import { action, untracked } from '../../kernel/index.js'\n\n/** `@peek(() => $x)` reads signals without subscribing the surrounding effect. */\nexport const peek = action({ name: 'peek', call: (_, fn: () => unknown) => untracked(fn) })\n"],
5
- "mappings": "AAAA,SAAS,QAAQ,iBAAiB;AAG3B,MAAM,OAAO,OAAO,EAAE,MAAM,QAAQ,MAAM,CAAC,GAAG,OAAsB,UAAU,EAAE,EAAE,CAAC;",
4
+ "sourcesContent": ["import { untracked } from '../../kernel/index.js';\nimport { act } from '../def.js';\n\n/** `@peek(() => $x)` reads signals without subscribing the surrounding effect. */\nexport const peek = act('peek', (_, fn: unknown) => (typeof fn === 'function' ? untracked(fn as () => unknown) : fn));\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,WAAW;AAGb,MAAM,OAAO,IAAI,QAAQ,CAAC,GAAG,OAAiB,OAAO,OAAO,aAAa,UAAU,EAAmB,IAAI,EAAG;",
6
6
  "names": []
7
7
  }
@@ -4,8 +4,8 @@ export type Socket = {
4
4
  readonly socket: WebSocket;
5
5
  };
6
6
  /**
7
- * `@ws('/live')` opens a WebSocket and routes incoming messages like a server event stream: text
8
- * blocks of `event: name` and `data: key value` lines, separated by blank lines. Returns
9
- * `{ send, close, socket }`; `send` JSON-encodes objects. The socket closes when the element unmounts.
7
+ * `@ws(url, { protocols })` opens a WebSocket whose messages use the server-event wire format
8
+ * (`event <name>` and `data <key> <value>` lines, blank-line separated) and routes them to the
9
+ * same handlers as fetched streams. Returns `{ send, close, socket }`; closed when the element unmounts.
10
10
  */
11
11
  export declare const websocket: import("../../kernel/contracts.js").ActionPlugin;
@@ -1,34 +1,27 @@
1
- import { action } from "../../kernel/index.js";
2
- const websocket = action({
3
- name: "ws",
4
- call({ el, runtime, cleanup }, url, o = {}) {
1
+ import { parseBlock, parseFields } from "../../lib/sse.js";
2
+ import { act } from "../def.js";
3
+ const websocket = act(
4
+ "ws",
5
+ ({ el, runtime, cleanup }, url, o = {}) => {
5
6
  const socket = new WebSocket(new URL(url, location.href.replace(/^http/, "ws")), o.protocols);
6
7
  socket.onmessage = (m) => {
7
- for (const block of String(m.data).split(/\n\n+/)) {
8
- let event = "message";
9
- const data = {};
10
- for (const line of block.split("\n")) {
11
- const i = line.indexOf(":");
12
- if (i < 0) continue;
13
- const field = line.slice(0, i);
14
- const v = line.slice(i + 1).trimStart();
15
- if (field === "event") event = v;
16
- else if (field === "data") {
17
- const j = v.indexOf(" ");
18
- const k = j < 0 ? v : v.slice(0, j);
19
- const val = j < 0 ? "" : v.slice(j + 1);
20
- data[k] = k in data ? `${data[k]}
21
- ${val}` : val;
22
- }
23
- }
24
- runtime.emit("server-event", { el, event, data });
25
- runtime.handle(event, data);
8
+ if (typeof m.data !== "string") return;
9
+ for (const block of m.data.split(/(?:\r?\n){2,}/)) {
10
+ const e = parseBlock(block);
11
+ if (!e) continue;
12
+ const data = parseFields(e.data);
13
+ runtime.emit("server-event", { el, event: e.event, data });
14
+ runtime.handle(e.event, data);
26
15
  }
27
16
  };
28
17
  cleanup(() => socket.close());
29
- return { send: (d) => socket.send(typeof d === "string" ? d : JSON.stringify(d)), close: () => socket.close(), socket };
18
+ return {
19
+ send: (d) => socket.send(typeof d === "string" ? d : JSON.stringify(d)),
20
+ close: () => socket.close(),
21
+ socket
22
+ };
30
23
  }
31
- });
24
+ );
32
25
  export {
33
26
  websocket
34
27
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/plugins/functions/websocket.ts"],
4
- "sourcesContent": ["import { action } from '../../kernel/index.js'\n\nexport type Socket = { send(data: unknown): void; close(): void; readonly socket: WebSocket }\n\n/**\n * `@ws('/live')` opens a WebSocket and routes incoming messages like a server event stream: text\n * blocks of `event: name` and `data: key value` lines, separated by blank lines. Returns\n * `{ send, close, socket }`; `send` JSON-encodes objects. The socket closes when the element unmounts.\n */\nexport const websocket = action({\n name: 'ws',\n call({ el, runtime, cleanup }, url: string, o: { protocols?: string | string[] } = {}): Socket {\n const socket = new WebSocket(new URL(url, location.href.replace(/^http/, 'ws')), o.protocols)\n socket.onmessage = (m) => {\n for (const block of String(m.data).split(/\\n\\n+/)) {\n let event = 'message'\n const data: Record<string, string> = {}\n for (const line of block.split('\\n')) {\n const i = line.indexOf(':')\n if (i < 0) continue\n const field = line.slice(0, i)\n const v = line.slice(i + 1).trimStart()\n if (field === 'event') event = v\n else if (field === 'data') {\n const j = v.indexOf(' ')\n const k = j < 0 ? v : v.slice(0, j)\n const val = j < 0 ? '' : v.slice(j + 1)\n data[k] = k in data ? `${data[k]}\\n${val}` : val\n }\n }\n runtime.emit('server-event', { el, event, data })\n runtime.handle(event, data)\n }\n }\n cleanup(() => socket.close())\n return { send: (d) => socket.send(typeof d === 'string' ? d : JSON.stringify(d)), close: () => socket.close(), socket }\n },\n})\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AAShB,MAAM,YAAY,OAAO;AAAA,EAC9B,MAAM;AAAA,EACN,KAAK,EAAE,IAAI,SAAS,QAAQ,GAAG,KAAa,IAAuC,CAAC,GAAW;AAC7F,UAAM,SAAS,IAAI,UAAU,IAAI,IAAI,KAAK,SAAS,KAAK,QAAQ,SAAS,IAAI,CAAC,GAAG,EAAE,SAAS;AAC5F,WAAO,YAAY,CAAC,MAAM;AACxB,iBAAW,SAAS,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,GAAG;AACjD,YAAI,QAAQ;AACZ,cAAM,OAA+B,CAAC;AACtC,mBAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,gBAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,cAAI,IAAI,EAAG;AACX,gBAAM,QAAQ,KAAK,MAAM,GAAG,CAAC;AAC7B,gBAAM,IAAI,KAAK,MAAM,IAAI,CAAC,EAAE,UAAU;AACtC,cAAI,UAAU,QAAS,SAAQ;AAAA,mBACtB,UAAU,QAAQ;AACzB,kBAAM,IAAI,EAAE,QAAQ,GAAG;AACvB,kBAAM,IAAI,IAAI,IAAI,IAAI,EAAE,MAAM,GAAG,CAAC;AAClC,kBAAM,MAAM,IAAI,IAAI,KAAK,EAAE,MAAM,IAAI,CAAC;AACtC,iBAAK,CAAC,IAAI,KAAK,OAAO,GAAG,KAAK,CAAC,CAAC;AAAA,EAAK,GAAG,KAAK;AAAA,UAC/C;AAAA,QACF;AACA,gBAAQ,KAAK,gBAAgB,EAAE,IAAI,OAAO,KAAK,CAAC;AAChD,gBAAQ,OAAO,OAAO,IAAI;AAAA,MAC5B;AAAA,IACF;AACA,YAAQ,MAAM,OAAO,MAAM,CAAC;AAC5B,WAAO,EAAE,MAAM,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAC,GAAG,OAAO,MAAM,OAAO,MAAM,GAAG,OAAO;AAAA,EACxH;AACF,CAAC;",
4
+ "sourcesContent": ["import { parseBlock, parseFields } from '../../lib/sse.js';\nimport { act } from '../def.js';\n\nexport type Socket = { send(data: unknown): void; close(): void; readonly socket: WebSocket };\n\n/**\n * `@ws(url, { protocols })` opens a WebSocket whose messages use the server-event wire format\n * (`event <name>` and `data <key> <value>` lines, blank-line separated) and routes them to the\n * same handlers as fetched streams. Returns `{ send, close, socket }`; closed when the element unmounts.\n */\nexport const websocket = act(\n 'ws',\n ({ el, runtime, cleanup }, url: string, o: { protocols?: string | string[] } = {}): Socket => {\n const socket = new WebSocket(new URL(url, location.href.replace(/^http/, 'ws')), o.protocols);\n socket.onmessage = (m) => {\n if (typeof m.data !== 'string') return; // binary frames are not server events\n for (const block of m.data.split(/(?:\\r?\\n){2,}/)) {\n const e = parseBlock(block);\n if (!e) continue;\n const data = parseFields(e.data);\n runtime.emit('server-event', { el, event: e.event, data });\n runtime.handle(e.event, data);\n }\n };\n cleanup(() => socket.close());\n return {\n send: (d) => socket.send(typeof d === 'string' ? d : JSON.stringify(d)),\n close: () => socket.close(),\n socket,\n };\n },\n);\n"],
5
+ "mappings": "AAAA,SAAS,YAAY,mBAAmB;AACxC,SAAS,WAAW;AASb,MAAM,YAAY;AAAA,EACvB;AAAA,EACA,CAAC,EAAE,IAAI,SAAS,QAAQ,GAAG,KAAa,IAAuC,CAAC,MAAc;AAC5F,UAAM,SAAS,IAAI,UAAU,IAAI,IAAI,KAAK,SAAS,KAAK,QAAQ,SAAS,IAAI,CAAC,GAAG,EAAE,SAAS;AAC5F,WAAO,YAAY,CAAC,MAAM;AACxB,UAAI,OAAO,EAAE,SAAS,SAAU;AAChC,iBAAW,SAAS,EAAE,KAAK,MAAM,eAAe,GAAG;AACjD,cAAM,IAAI,WAAW,KAAK;AAC1B,YAAI,CAAC,EAAG;AACR,cAAM,OAAO,YAAY,EAAE,IAAI;AAC/B,gBAAQ,KAAK,gBAAgB,EAAE,IAAI,OAAO,EAAE,OAAO,KAAK,CAAC;AACzD,gBAAQ,OAAO,EAAE,OAAO,IAAI;AAAA,MAC9B;AAAA,IACF;AACA,YAAQ,MAAM,OAAO,MAAM,CAAC;AAC5B,WAAO;AAAA,MACL,MAAM,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAC;AAAA,MACtE,OAAO,MAAM,OAAO,MAAM;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,52 +1,50 @@
1
1
  export { attr } from './directives/attribute-sync.js';
2
- export { bind } from './directives/two-way-bind.js';
2
+ export { boost } from './directives/boost.js';
3
+ export { indicator } from './directives/busy-flag.js';
3
4
  export { className } from './directives/class-toggle.js';
5
+ export { cloak } from './directives/cloak.js';
6
+ export { collapse } from './directives/collapse.js';
7
+ export { signals } from './directives/declare.js';
4
8
  export { computed } from './directives/derive.js';
5
- export { customValidity } from './directives/validity.js';
6
- export { effect } from './directives/watch.js';
7
- export { indicator } from './directives/busy-flag.js';
8
- export { init } from './directives/on-mount.js';
9
- export { jsonSignals } from './directives/state-dump.js';
10
- export { matchMedia } from './directives/media-query.js';
9
+ export { ref } from './directives/element-ref.js';
11
10
  export { on } from './directives/event-listener.js';
12
- export { onIntersect } from './directives/viewport.js';
13
- export { onInterval } from './directives/timer.js';
14
11
  export { onRaf } from './directives/frame-loop.js';
15
- export { onResize } from './directives/size-watch.js';
12
+ export { html } from './directives/html.js';
13
+ export { style } from './directives/inline-style.js';
14
+ export { applyMask, mask } from './directives/mask.js';
15
+ export { matchMedia } from './directives/media-query.js';
16
+ export { init } from './directives/on-mount.js';
16
17
  export { onSignalPatch } from './directives/patch-listener.js';
18
+ export { removeMe } from './directives/remove-me.js';
19
+ export { scrollIntoView } from './directives/scroll-to.js';
20
+ export { onResize } from './directives/size-watch.js';
21
+ export { jsonSignals } from './directives/state-dump.js';
17
22
  export { persist } from './directives/storage-sync.js';
23
+ export { teleport } from './directives/teleport.js';
24
+ export { text } from './directives/text-content.js';
25
+ export { onInterval } from './directives/timer.js';
26
+ export { transition } from './directives/transition.js';
27
+ export { animate } from './directives/tween.js';
28
+ export { bind } from './directives/two-way-bind.js';
18
29
  export { queryString } from './directives/url-params.js';
19
- export { ref } from './directives/element-ref.js';
20
30
  export { replaceUrl } from './directives/url-replace.js';
21
- export { scrollIntoView } from './directives/scroll-to.js';
31
+ export { customValidity } from './directives/validity.js';
32
+ export { onIntersect } from './directives/viewport.js';
22
33
  export { show } from './directives/visibility.js';
23
- export { signals } from './directives/declare.js';
24
- export { style } from './directives/inline-style.js';
25
- export { text } from './directives/text-content.js';
26
- export { viewTransition } from './directives/transition-name.js';
27
- export { clipboard } from './functions/copy-text.js';
28
- export { fit } from './functions/range-map.js';
29
- export { intl } from './functions/locale-format.js';
30
- export { peek } from './functions/untracked-read.js';
34
+ export { effect } from './directives/watch.js';
31
35
  export { setAll } from './functions/bulk-set.js';
32
36
  export { toggleAll } from './functions/bulk-toggle.js';
33
- export { animate } from './directives/tween.js';
34
- export { httpDelete, httpGet, httpPatch, httpPost, httpPut } from './functions/request.js';
37
+ export { confirm } from './functions/confirm.js';
38
+ export { clipboard } from './functions/copy-text.js';
39
+ export { dispatch } from './functions/dispatch.js';
40
+ export { intl } from './functions/locale-format.js';
41
+ export { fit } from './functions/range-map.js';
35
42
  export type { RequestOptions } from './functions/request.js';
43
+ export { httpDelete, httpGet, httpPatch, httpPost, httpPut } from './functions/request.js';
44
+ export { peek } from './functions/untracked-read.js';
45
+ export type { Socket } from './functions/websocket.js';
46
+ export { websocket } from './functions/websocket.js';
36
47
  export { applyElements } from './server-events/apply-elements.js';
37
48
  export { applyState } from './server-events/apply-state.js';
38
- export { activateScripts, morph, morphInner } from './server-events/morph.js';
39
49
  export type { MorphOptions } from './server-events/morph.js';
40
- export { boost } from './directives/boost.js';
41
- export { cloak } from './directives/cloak.js';
42
- export { collapse } from './directives/collapse.js';
43
- export { html } from './directives/html.js';
44
- export { applyMask, mask } from './directives/mask.js';
45
- export { removeMe } from './directives/remove-me.js';
46
- export { teleport } from './directives/teleport.js';
47
- export { transition } from './directives/transition.js';
48
- export { trap } from './directives/trap.js';
49
- export { confirm } from './functions/confirm.js';
50
- export { dispatch } from './functions/dispatch.js';
51
- export { websocket } from './functions/websocket.js';
52
- export type { Socket } from './functions/websocket.js';
50
+ export { activateScripts, morph, morphInner } from './server-events/morph.js';