@graphrefly/graphrefly 0.47.2 → 0.48.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 (266) hide show
  1. package/dist/base/composition/index.cjs +4 -3
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.d.cts +14 -5
  4. package/dist/base/composition/index.d.ts +14 -5
  5. package/dist/base/composition/index.js +8 -8
  6. package/dist/base/index.cjs +152 -78
  7. package/dist/base/index.cjs.map +1 -1
  8. package/dist/base/index.d.cts +2 -2
  9. package/dist/base/index.d.ts +2 -2
  10. package/dist/base/index.js +75 -70
  11. package/dist/base/io/index.cjs +31 -17
  12. package/dist/base/io/index.cjs.map +1 -1
  13. package/dist/base/io/index.d.cts +32 -5
  14. package/dist/base/io/index.d.ts +32 -5
  15. package/dist/base/io/index.js +1 -1
  16. package/dist/base/mutation/index.cjs +21 -0
  17. package/dist/base/mutation/index.cjs.map +1 -1
  18. package/dist/base/mutation/index.d.cts +23 -1
  19. package/dist/base/mutation/index.d.ts +23 -1
  20. package/dist/base/mutation/index.js +3 -1
  21. package/dist/base/sources/browser/index.cjs +5 -3
  22. package/dist/base/sources/browser/index.cjs.map +1 -1
  23. package/dist/base/sources/browser/index.d.cts +20 -2
  24. package/dist/base/sources/browser/index.d.ts +20 -2
  25. package/dist/base/sources/browser/index.js +5 -3
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +28 -0
  28. package/dist/base/sources/event/index.cjs.map +1 -1
  29. package/dist/base/sources/event/index.d.cts +67 -3
  30. package/dist/base/sources/event/index.d.ts +67 -3
  31. package/dist/base/sources/event/index.js +4 -1
  32. package/dist/base/sources/index.cjs +75 -37
  33. package/dist/base/sources/index.cjs.map +1 -1
  34. package/dist/base/sources/index.d.cts +1 -1
  35. package/dist/base/sources/index.d.ts +1 -1
  36. package/dist/base/sources/index.js +5 -2
  37. package/dist/{chunk-R6ZCSXKX.js → chunk-23MAWVOJ.js} +3 -3
  38. package/dist/{chunk-MS3WPRJR.js → chunk-3REMCHSS.js} +6 -6
  39. package/dist/chunk-3REMCHSS.js.map +1 -0
  40. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  41. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  42. package/dist/{chunk-6ZLCPUXS.js → chunk-46X2EFQH.js} +15 -4
  43. package/dist/chunk-46X2EFQH.js.map +1 -0
  44. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  45. package/dist/chunk-5UY3PNFY.js.map +1 -0
  46. package/dist/{chunk-FQSQONOU.js → chunk-65OM4XLQ.js} +49 -3
  47. package/dist/chunk-65OM4XLQ.js.map +1 -0
  48. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  49. package/dist/chunk-6DQYBIHW.js.map +1 -0
  50. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  51. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  52. package/dist/{chunk-3O3NKZJW.js → chunk-7T7WLEPM.js} +24 -3
  53. package/dist/chunk-7T7WLEPM.js.map +1 -0
  54. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  55. package/dist/chunk-AQAKDE7F.js.map +1 -0
  56. package/dist/{chunk-6MRSX3YK.js → chunk-B5Y5GPD5.js} +2 -2
  57. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  58. package/dist/chunk-C5QD5DQX.js.map +1 -0
  59. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  60. package/dist/chunk-D5YGR4TP.js.map +1 -0
  61. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  62. package/dist/chunk-DHDCOOJU.js.map +1 -0
  63. package/dist/{chunk-VP3TIUDF.js → chunk-DVTDF5OI.js} +2 -2
  64. package/dist/{chunk-OXD5LFQP.js → chunk-G7H6PN7P.js} +2 -2
  65. package/dist/{chunk-EL5VHUGK.js → chunk-GGKHHG5Y.js} +32 -18
  66. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  67. package/dist/{chunk-446I4EGD.js → chunk-J5TBZFBD.js} +2 -2
  68. package/dist/{chunk-7AVQIGF6.js → chunk-K4ZYJ4EM.js} +554 -460
  69. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  70. package/dist/{chunk-QFE5BQH7.js → chunk-LTSI7ULC.js} +2 -2
  71. package/dist/{chunk-5GVURVIG.js → chunk-MMHGYX44.js} +12 -2
  72. package/dist/{chunk-5GVURVIG.js.map → chunk-MMHGYX44.js.map} +1 -1
  73. package/dist/{chunk-KRFGO5QH.js → chunk-MQMTRKY3.js} +118 -43
  74. package/dist/chunk-MQMTRKY3.js.map +1 -0
  75. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  76. package/dist/chunk-MTODGQBR.js.map +1 -0
  77. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  78. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  79. package/dist/{chunk-KNU73RZW.js → chunk-NSA5K5G2.js} +2 -2
  80. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  81. package/dist/chunk-QSW4DFKE.js +31 -0
  82. package/dist/chunk-QSW4DFKE.js.map +1 -0
  83. package/dist/{chunk-VAZXUK6G.js → chunk-SUNCHMML.js} +2 -2
  84. package/dist/{chunk-EP4WVQLX.js → chunk-T2U6N3FV.js} +6 -6
  85. package/dist/{chunk-T7SP3EYR.js → chunk-T5URUIIY.js} +33 -24
  86. package/dist/chunk-T5URUIIY.js.map +1 -0
  87. package/dist/{chunk-VNXAF2KE.js → chunk-TPTZZV25.js} +6 -6
  88. package/dist/chunk-TPTZZV25.js.map +1 -0
  89. package/dist/{chunk-IOJDYUA7.js → chunk-V46JWFGV.js} +6 -5
  90. package/dist/chunk-V46JWFGV.js.map +1 -0
  91. package/dist/{chunk-WGDEBIP4.js → chunk-X6ESZDR6.js} +5 -6
  92. package/dist/chunk-X6ESZDR6.js.map +1 -0
  93. package/dist/{chunk-N65E26UL.js → chunk-XEWV254I.js} +2 -2
  94. package/dist/{chunk-N65E26UL.js.map → chunk-XEWV254I.js.map} +1 -1
  95. package/dist/{chunk-PTWADEH3.js → chunk-YBJVKMTM.js} +34 -14
  96. package/dist/chunk-YBJVKMTM.js.map +1 -0
  97. package/dist/{chunk-DDTS7F5O.js → chunk-ZW32BPXV.js} +12 -3
  98. package/dist/chunk-ZW32BPXV.js.map +1 -0
  99. package/dist/compat/index.cjs +51 -4
  100. package/dist/compat/index.cjs.map +1 -1
  101. package/dist/compat/index.d.cts +1 -1
  102. package/dist/compat/index.d.ts +1 -1
  103. package/dist/compat/index.js +6 -6
  104. package/dist/compat/nestjs/index.cjs +51 -4
  105. package/dist/compat/nestjs/index.cjs.map +1 -1
  106. package/dist/compat/nestjs/index.d.cts +1 -1
  107. package/dist/compat/nestjs/index.d.ts +1 -1
  108. package/dist/compat/nestjs/index.js +3 -3
  109. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  110. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  111. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  112. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  113. package/dist/index.cjs +2215 -1676
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.d.cts +10 -10
  116. package/dist/index.d.ts +10 -10
  117. package/dist/index.js +169 -146
  118. package/dist/index.js.map +1 -1
  119. package/dist/presets/ai/index.cjs +46 -0
  120. package/dist/presets/ai/index.cjs.map +1 -1
  121. package/dist/presets/ai/index.js +12 -12
  122. package/dist/presets/harness/index.cjs +130 -18
  123. package/dist/presets/harness/index.cjs.map +1 -1
  124. package/dist/presets/harness/index.d.cts +15 -5
  125. package/dist/presets/harness/index.d.ts +15 -5
  126. package/dist/presets/harness/index.js +22 -22
  127. package/dist/presets/index.cjs +222 -53
  128. package/dist/presets/index.cjs.map +1 -1
  129. package/dist/presets/index.d.cts +2 -2
  130. package/dist/presets/index.d.ts +2 -2
  131. package/dist/presets/index.js +45 -45
  132. package/dist/presets/inspect/index.cjs +63 -14
  133. package/dist/presets/inspect/index.cjs.map +1 -1
  134. package/dist/presets/inspect/index.d.cts +1 -1
  135. package/dist/presets/inspect/index.d.ts +1 -1
  136. package/dist/presets/inspect/index.js +6 -6
  137. package/dist/presets/resilience/index.cjs +29 -21
  138. package/dist/presets/resilience/index.cjs.map +1 -1
  139. package/dist/presets/resilience/index.d.cts +12 -8
  140. package/dist/presets/resilience/index.d.ts +12 -8
  141. package/dist/presets/resilience/index.js +3 -3
  142. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  143. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  144. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  145. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  146. package/dist/solutions/index.cjs +168 -47
  147. package/dist/solutions/index.cjs.map +1 -1
  148. package/dist/solutions/index.d.cts +2 -2
  149. package/dist/solutions/index.d.ts +2 -2
  150. package/dist/solutions/index.js +28 -28
  151. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  152. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  153. package/dist/testing/index.cjs +94 -0
  154. package/dist/testing/index.cjs.map +1 -0
  155. package/dist/testing/index.d.cts +59 -0
  156. package/dist/testing/index.d.ts +59 -0
  157. package/dist/testing/index.js +73 -0
  158. package/dist/testing/index.js.map +1 -0
  159. package/dist/utils/ai/browser.cjs.map +1 -1
  160. package/dist/utils/ai/browser.d.cts +2 -2
  161. package/dist/utils/ai/browser.d.ts +2 -2
  162. package/dist/utils/ai/browser.js +6 -6
  163. package/dist/utils/ai/browser.js.map +1 -1
  164. package/dist/utils/ai/index.cjs +250 -166
  165. package/dist/utils/ai/index.cjs.map +1 -1
  166. package/dist/utils/ai/index.d.cts +108 -12
  167. package/dist/utils/ai/index.d.ts +108 -12
  168. package/dist/utils/ai/index.js +21 -19
  169. package/dist/utils/ai/node.cjs.map +1 -1
  170. package/dist/utils/ai/node.d.cts +5 -5
  171. package/dist/utils/ai/node.d.ts +5 -5
  172. package/dist/utils/ai/node.js +2 -2
  173. package/dist/utils/ai/node.js.map +1 -1
  174. package/dist/utils/cqrs/index.cjs +29 -3
  175. package/dist/utils/cqrs/index.cjs.map +1 -1
  176. package/dist/utils/cqrs/index.d.cts +12 -7
  177. package/dist/utils/cqrs/index.d.ts +12 -7
  178. package/dist/utils/cqrs/index.js +2 -2
  179. package/dist/utils/demo-shell/index.cjs +45 -19
  180. package/dist/utils/demo-shell/index.cjs.map +1 -1
  181. package/dist/utils/demo-shell/index.d.cts +1 -1
  182. package/dist/utils/demo-shell/index.d.ts +1 -1
  183. package/dist/utils/demo-shell/index.js +2 -2
  184. package/dist/utils/domain-templates/index.cjs.map +1 -1
  185. package/dist/utils/domain-templates/index.js +3 -3
  186. package/dist/utils/graphspec/index.cjs.map +1 -1
  187. package/dist/utils/graphspec/index.js +3 -3
  188. package/dist/utils/index.cjs +1642 -1225
  189. package/dist/utils/index.cjs.map +1 -1
  190. package/dist/utils/index.d.cts +7 -7
  191. package/dist/utils/index.d.ts +7 -7
  192. package/dist/utils/index.js +72 -54
  193. package/dist/utils/inspect/index.cjs +52 -4
  194. package/dist/utils/inspect/index.cjs.map +1 -1
  195. package/dist/utils/inspect/index.d.cts +32 -3
  196. package/dist/utils/inspect/index.d.ts +32 -3
  197. package/dist/utils/inspect/index.js +4 -4
  198. package/dist/utils/job-queue/index.cjs +46 -9
  199. package/dist/utils/job-queue/index.cjs.map +1 -1
  200. package/dist/utils/job-queue/index.d.cts +33 -3
  201. package/dist/utils/job-queue/index.d.ts +33 -3
  202. package/dist/utils/job-queue/index.js +2 -2
  203. package/dist/utils/memory/index.cjs +556 -462
  204. package/dist/utils/memory/index.cjs.map +1 -1
  205. package/dist/utils/memory/index.d.cts +203 -24
  206. package/dist/utils/memory/index.d.ts +203 -24
  207. package/dist/utils/memory/index.js +10 -2
  208. package/dist/utils/messaging/index.cjs.map +1 -1
  209. package/dist/utils/messaging/index.d.cts +4 -3
  210. package/dist/utils/messaging/index.d.ts +4 -3
  211. package/dist/utils/messaging/index.js +2 -2
  212. package/dist/utils/orchestration/index.cjs +9 -0
  213. package/dist/utils/orchestration/index.cjs.map +1 -1
  214. package/dist/utils/orchestration/index.js +3 -3
  215. package/dist/utils/process/index.cjs +32 -2
  216. package/dist/utils/process/index.cjs.map +1 -1
  217. package/dist/utils/process/index.d.cts +4 -3
  218. package/dist/utils/process/index.d.ts +4 -3
  219. package/dist/utils/process/index.js +2 -2
  220. package/dist/utils/reactive-layout/index.cjs +184 -55
  221. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  222. package/dist/utils/reactive-layout/index.d.cts +128 -3
  223. package/dist/utils/reactive-layout/index.d.ts +128 -3
  224. package/dist/utils/reactive-layout/index.js +16 -8
  225. package/dist/utils/reduction/index.cjs.map +1 -1
  226. package/dist/utils/reduction/index.js +2 -2
  227. package/dist/utils/resilience/index.cjs +29 -20
  228. package/dist/utils/resilience/index.cjs.map +1 -1
  229. package/dist/utils/resilience/index.d.cts +1 -1
  230. package/dist/utils/resilience/index.d.ts +1 -1
  231. package/dist/utils/resilience/index.js +2 -2
  232. package/dist/utils/surface/index.cjs.map +1 -1
  233. package/dist/utils/surface/index.js +4 -4
  234. package/package.json +15 -3
  235. package/dist/chunk-3O3NKZJW.js.map +0 -1
  236. package/dist/chunk-3PSLNJDU.js.map +0 -1
  237. package/dist/chunk-42FQ27MQ.js.map +0 -1
  238. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  239. package/dist/chunk-6ZLCPUXS.js.map +0 -1
  240. package/dist/chunk-7AVQIGF6.js.map +0 -1
  241. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  242. package/dist/chunk-DDTS7F5O.js.map +0 -1
  243. package/dist/chunk-EL5VHUGK.js.map +0 -1
  244. package/dist/chunk-FQSQONOU.js.map +0 -1
  245. package/dist/chunk-IOJDYUA7.js.map +0 -1
  246. package/dist/chunk-KRFGO5QH.js.map +0 -1
  247. package/dist/chunk-MS3WPRJR.js.map +0 -1
  248. package/dist/chunk-NPRP3MCV.js.map +0 -1
  249. package/dist/chunk-NY2PYHNC.js.map +0 -1
  250. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  251. package/dist/chunk-PTWADEH3.js.map +0 -1
  252. package/dist/chunk-T7SP3EYR.js.map +0 -1
  253. package/dist/chunk-VNXAF2KE.js.map +0 -1
  254. package/dist/chunk-W2BOPXTI.js +0 -1
  255. package/dist/chunk-W2BOPXTI.js.map +0 -1
  256. package/dist/chunk-WGDEBIP4.js.map +0 -1
  257. /package/dist/{chunk-R6ZCSXKX.js.map → chunk-23MAWVOJ.js.map} +0 -0
  258. /package/dist/{chunk-6MRSX3YK.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  259. /package/dist/{chunk-VP3TIUDF.js.map → chunk-DVTDF5OI.js.map} +0 -0
  260. /package/dist/{chunk-OXD5LFQP.js.map → chunk-G7H6PN7P.js.map} +0 -0
  261. /package/dist/{chunk-446I4EGD.js.map → chunk-J5TBZFBD.js.map} +0 -0
  262. /package/dist/{chunk-QFE5BQH7.js.map → chunk-LTSI7ULC.js.map} +0 -0
  263. /package/dist/{chunk-KNU73RZW.js.map → chunk-NSA5K5G2.js.map} +0 -0
  264. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  265. /package/dist/{chunk-VAZXUK6G.js.map → chunk-SUNCHMML.js.map} +0 -0
  266. /package/dist/{chunk-EP4WVQLX.js.map → chunk-T2U6N3FV.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/base/sources/event/dom.ts","../../../../src/base/sources/browser/idb.ts"],"sourcesContent":["/**\n * DOM-based reactive event sources (browser-layer).\n *\n * Moved from extra/sources/event.ts (fromEvent, fromRaf) during cleave A2.\n */\n\nimport { ERROR, type Node, type NodeOptions, node } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\ntype AsyncSourceOpts = ExtraOpts & { signal?: AbortSignal };\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** DOM-style event target (browser or `node:events`). */\nexport type EventTargetLike = {\n\taddEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n\tremoveEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n};\n\nexport function fromRaf(opts?: AsyncSourceOpts): Node<number> {\n\tconst { signal, ...rest } = opts ?? {};\n\treturn node<number>((_data, a) => {\n\t\tlet done = false;\n\t\tlet rafId: number | undefined;\n\t\tlet fallbackTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet abortListenerAdded = false;\n\t\tlet visibilityListenerAdded = false;\n\n\t\tconst raf: typeof requestAnimationFrame | undefined =\n\t\t\ttypeof requestAnimationFrame === \"function\" ? requestAnimationFrame : undefined;\n\t\tconst caf: typeof cancelAnimationFrame | undefined =\n\t\t\ttypeof cancelAnimationFrame === \"function\" ? cancelAnimationFrame : undefined;\n\t\tconst doc: Document | undefined = typeof document !== \"undefined\" ? document : undefined;\n\n\t\tconst clearPending = () => {\n\t\t\tif (rafId !== undefined && caf) caf(rafId);\n\t\t\tif (fallbackTimer !== undefined) clearTimeout(fallbackTimer);\n\t\t\trafId = undefined;\n\t\t\tfallbackTimer = undefined;\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tdone = true;\n\t\t\tclearPending();\n\t\t\tif (abortListenerAdded) {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tabortListenerAdded = false;\n\t\t\t}\n\t\t\tif (visibilityListenerAdded && doc) {\n\t\t\t\tdoc.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\t\tvisibilityListenerAdded = false;\n\t\t\t}\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tif (done) return;\n\t\t\tcleanup();\n\t\t\ta.down([[ERROR, signal!.reason]]);\n\t\t};\n\t\tconst tick = (now: number) => {\n\t\t\tif (done) return;\n\t\t\ta.emit(now);\n\t\t\tscheduleNext();\n\t\t};\n\t\tconst scheduleNext = () => {\n\t\t\tif (done) return;\n\t\t\t// Prefer rAF for display-synced ticks when the tab is visible; when\n\t\t\t// hidden, rAF is throttled to ~0 by the browser, so fall back to\n\t\t\t// setTimeout so downstream state continues updating.\n\t\t\tif (raf && (!doc || doc.visibilityState !== \"hidden\")) {\n\t\t\t\trafId = raf(tick);\n\t\t\t} else {\n\t\t\t\tfallbackTimer = setTimeout(() => tick(performance.now()), 16);\n\t\t\t}\n\t\t};\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (done) return;\n\t\t\t// Cancel any pending schedule and re-schedule via the path now\n\t\t\t// appropriate for the current visibility state.\n\t\t\tclearPending();\n\t\t\tscheduleNext();\n\t\t};\n\n\t\tif (signal?.aborted) {\n\t\t\t// Already aborted before activation — `onAbort()` ran `cleanup()`\n\t\t\t// and emitted ERROR. No listeners/timers were registered yet, so\n\t\t\t// there is nothing to tear down on deactivation; returning a\n\t\t\t// cleanup here would just re-run the (idempotent) no-op.\n\t\t\tonAbort();\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tabortListenerAdded = signal !== undefined;\n\t\tif (doc && raf) {\n\t\t\tdoc.addEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\tvisibilityListenerAdded = true;\n\t\t}\n\t\tscheduleNext();\n\t\treturn { onDeactivation: cleanup };\n\t}, sourceOpts(rest));\n}\n\n/**\n * Wraps a DOM-style `addEventListener` target; each event becomes a `DATA` emission.\n *\n * @param target - Object with `addEventListener` / `removeEventListener`.\n * @param type - Event name (e.g. `\"click\"`).\n * @param opts - Producer options plus listener options (`capture`, `passive`, `once`).\n * @returns `Node<T>` — event payloads; teardown removes the listener.\n *\n * @example\n * ```ts\n * import { fromEvent } from \"@graphrefly/graphrefly-ts\";\n *\n * fromEvent(document.body, \"click\");\n * ```\n *\n * @category extra\n */\nexport function fromEvent<T = unknown>(\n\ttarget: EventTargetLike,\n\ttype: string,\n\topts?: ExtraOpts & { capture?: boolean; passive?: boolean; once?: boolean },\n): Node<T> {\n\tconst { capture, passive, once, ...rest } = opts ?? {};\n\treturn node<T>((_data, a) => {\n\t\tconst handler = (e: unknown) => {\n\t\t\ta.emit(e as T);\n\t\t};\n\t\tconst options = { capture, passive, once };\n\t\ttarget.addEventListener(type, handler, options);\n\t\treturn { onDeactivation: () => target.removeEventListener(type, handler, options) };\n\t}, sourceOpts(rest));\n}\n","/**\n * Browser-only IndexedDB reactive sources.\n *\n * `fromIDBRequest` / `fromIDBTransaction` wrap raw IDB primitives as reactive\n * sources. The old `indexedDbStorage` kv adapter has been replaced by\n * `indexedDbKv` in `./storage-tiers-browser.js` (Audit 4, 2026-04-24).\n *\n * Imports require the DOM lib — not safe to pull into Node-only bundles\n * without `lib: [\"dom\"]` in the consumer's tsconfig.\n *\n * @module\n */\n/// <reference lib=\"dom\" />\n\nimport { COMPLETE, DATA, ERROR, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// IndexedDbStorageSpec is no longer needed here — it's defined in storage-tiers-browser.ts.\n\n/**\n * Wraps an `IDBRequest` as a one-shot reactive source.\n *\n * @param req - Request whose callbacks are converted to protocol messages.\n * @returns `Node<T>` that emits `DATA` once on success then `COMPLETE`;\n * emits `ERROR` on failure.\n *\n * @category extra\n */\nexport function fromIDBRequest<T>(req: IDBRequest<T>): Node<T> {\n\treturn node<T>((_data, a) => {\n\t\tlet done = false;\n\t\tconst clear = () => {\n\t\t\treq.onsuccess = null;\n\t\t\treq.onerror = null;\n\t\t};\n\t\treq.onsuccess = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[DATA, req.result], [COMPLETE]]);\n\t\t};\n\t\treq.onerror = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, req.error ?? new Error(\"IndexedDB request failed\")]]);\n\t\t};\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tclear();\n\t\t\t},\n\t\t};\n\t});\n}\n\n/**\n * Wraps an `IDBTransaction` terminal lifecycle as a one-shot reactive source.\n *\n * @param tx - Transaction to observe.\n * @returns `Node<void>` that emits `DATA` (`undefined`) then `COMPLETE` on\n * success; emits `ERROR` on `error`/`abort`.\n *\n * @category extra\n */\nexport function fromIDBTransaction(tx: IDBTransaction): Node<void> {\n\treturn node<void>((_data, a) => {\n\t\tlet done = false;\n\t\tconst clear = () => {\n\t\t\ttx.oncomplete = null;\n\t\t\ttx.onerror = null;\n\t\t\ttx.onabort = null;\n\t\t};\n\t\ttx.oncomplete = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[DATA, undefined], [COMPLETE]]);\n\t\t};\n\t\ttx.onerror = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, tx.error ?? new Error(\"IndexedDB transaction failed\")]]);\n\t\t};\n\t\ttx.onabort = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, tx.error ?? new Error(\"IndexedDB transaction aborted\")]]);\n\t\t};\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tclear();\n\t\t\t},\n\t\t};\n\t});\n}\n\n// The old `indexedDbStorage` kv adapter has been removed.\n// Use `indexedDbKv` from `./storage-tiers-browser.js` instead (Audit 4, 2026-04-24).\n"],"mappings":"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","names":["ERROR","node"]}
1
+ {"version":3,"sources":["../../../../src/base/sources/event/dom.ts","../../../../src/base/sources/browser/idb.ts"],"sourcesContent":["/**\n * DOM-based reactive event sources (browser-layer).\n *\n * Moved from extra/sources/event.ts (fromEvent, fromRaf) during cleave A2.\n */\n\nimport { ERROR, type Node, type NodeOptions, node } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\ntype AsyncSourceOpts = ExtraOpts & { signal?: AbortSignal };\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** DOM-style event target (browser or `node:events`). */\nexport type EventTargetLike = {\n\taddEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n\tremoveEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n};\n\n/** Options for {@link fromRaf}. */\nexport type FromRafOptions = AsyncSourceOpts & {\n\t/**\n\t * When `true`, the source **fully parks** while the document is hidden\n\t * (`document.visibilityState === \"hidden\"`) instead of falling back to a\n\t * `setTimeout(16ms)` keep-alive. It resumes from the next animation frame\n\t * on `visibilitychange` back to visible. Strict battery-correctness for\n\t * mobile/background consumers — a backgrounded recompute/timer loop is a\n\t * footgun for them. Default `false` (legacy: keep ticking via setTimeout).\n\t *\n\t * **No `document` ⇒ never auto-pauses.** React Native / Hermes has no\n\t * `document`, so visibility cannot be observed here; the host must drive\n\t * background pause itself (e.g. an `AppState`-fed source gating the\n\t * downstream, or unsubscribing this node on background). This option only\n\t * affects environments that expose `document.visibilityState`.\n\t */\n\tpauseWhenHidden?: boolean;\n};\n\nexport function fromRaf(opts?: FromRafOptions): Node<number> {\n\tconst { signal, pauseWhenHidden = false, ...rest } = opts ?? {};\n\treturn node<number>((_data, a) => {\n\t\tlet done = false;\n\t\tlet rafId: number | undefined;\n\t\tlet fallbackTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet abortListenerAdded = false;\n\t\tlet visibilityListenerAdded = false;\n\n\t\tconst raf: typeof requestAnimationFrame | undefined =\n\t\t\ttypeof requestAnimationFrame === \"function\" ? requestAnimationFrame : undefined;\n\t\tconst caf: typeof cancelAnimationFrame | undefined =\n\t\t\ttypeof cancelAnimationFrame === \"function\" ? cancelAnimationFrame : undefined;\n\t\tconst doc: Document | undefined = typeof document !== \"undefined\" ? document : undefined;\n\n\t\tconst clearPending = () => {\n\t\t\tif (rafId !== undefined && caf) caf(rafId);\n\t\t\tif (fallbackTimer !== undefined) clearTimeout(fallbackTimer);\n\t\t\trafId = undefined;\n\t\t\tfallbackTimer = undefined;\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tdone = true;\n\t\t\tclearPending();\n\t\t\tif (abortListenerAdded) {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tabortListenerAdded = false;\n\t\t\t}\n\t\t\tif (visibilityListenerAdded && doc) {\n\t\t\t\tdoc.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\t\tvisibilityListenerAdded = false;\n\t\t\t}\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tif (done) return;\n\t\t\tcleanup();\n\t\t\ta.down([[ERROR, signal!.reason]]);\n\t\t};\n\t\tconst tick = (now: number) => {\n\t\t\tif (done) return;\n\t\t\ta.emit(now);\n\t\t\tscheduleNext();\n\t\t};\n\t\tconst scheduleNext = () => {\n\t\t\tif (done) return;\n\t\t\tconst hidden = doc !== undefined && doc.visibilityState === \"hidden\";\n\t\t\t// Strict pause: when hidden and the consumer opted in, schedule\n\t\t\t// nothing at all (no rAF, no setTimeout keep-alive). The\n\t\t\t// `visibilitychange` listener re-enters `scheduleNext()` when the\n\t\t\t// document becomes visible again, resuming from the next frame.\n\t\t\tif (pauseWhenHidden && hidden) return;\n\t\t\t// Prefer rAF for display-synced ticks when the tab is visible; when\n\t\t\t// hidden, rAF is throttled to ~0 by the browser, so fall back to\n\t\t\t// setTimeout so downstream state continues updating.\n\t\t\tif (raf && !hidden) {\n\t\t\t\trafId = raf(tick);\n\t\t\t} else {\n\t\t\t\tfallbackTimer = setTimeout(() => tick(performance.now()), 16);\n\t\t\t}\n\t\t};\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (done) return;\n\t\t\t// Cancel any pending schedule and re-schedule via the path now\n\t\t\t// appropriate for the current visibility state.\n\t\t\tclearPending();\n\t\t\tscheduleNext();\n\t\t};\n\n\t\tif (signal?.aborted) {\n\t\t\t// Already aborted before activation — `onAbort()` ran `cleanup()`\n\t\t\t// and emitted ERROR. No listeners/timers were registered yet, so\n\t\t\t// there is nothing to tear down on deactivation; returning a\n\t\t\t// cleanup here would just re-run the (idempotent) no-op.\n\t\t\tonAbort();\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tabortListenerAdded = signal !== undefined;\n\t\tif (doc && (raf || pauseWhenHidden)) {\n\t\t\tdoc.addEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\tvisibilityListenerAdded = true;\n\t\t}\n\t\tscheduleNext();\n\t\treturn { onDeactivation: cleanup };\n\t}, sourceOpts(rest));\n}\n\n/**\n * Wraps a DOM-style `addEventListener` target; each event becomes a `DATA` emission.\n *\n * @param target - Object with `addEventListener` / `removeEventListener`.\n * @param type - Event name (e.g. `\"click\"`).\n * @param opts - Producer options plus listener options (`capture`, `passive`, `once`).\n * @returns `Node<T>` — event payloads; teardown removes the listener.\n *\n * @example\n * ```ts\n * import { fromEvent } from \"@graphrefly/graphrefly-ts\";\n *\n * fromEvent(document.body, \"click\");\n * ```\n *\n * @category extra\n */\nexport function fromEvent<T = unknown>(\n\ttarget: EventTargetLike,\n\ttype: string,\n\topts?: ExtraOpts & { capture?: boolean; passive?: boolean; once?: boolean },\n): Node<T> {\n\tconst { capture, passive, once, ...rest } = opts ?? {};\n\treturn node<T>((_data, a) => {\n\t\tconst handler = (e: unknown) => {\n\t\t\ta.emit(e as T);\n\t\t};\n\t\tconst options = { capture, passive, once };\n\t\ttarget.addEventListener(type, handler, options);\n\t\treturn { onDeactivation: () => target.removeEventListener(type, handler, options) };\n\t}, sourceOpts(rest));\n}\n","/**\n * Browser-only IndexedDB reactive sources.\n *\n * `fromIDBRequest` / `fromIDBTransaction` wrap raw IDB primitives as reactive\n * sources. The old `indexedDbStorage` kv adapter has been replaced by\n * `indexedDbKv` in `./storage-tiers-browser.js` (Audit 4, 2026-04-24).\n *\n * Imports require the DOM lib — not safe to pull into Node-only bundles\n * without `lib: [\"dom\"]` in the consumer's tsconfig.\n *\n * @module\n */\n/// <reference lib=\"dom\" />\n\nimport { COMPLETE, DATA, ERROR, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// IndexedDbStorageSpec is no longer needed here — it's defined in storage-tiers-browser.ts.\n\n/**\n * Wraps an `IDBRequest` as a one-shot reactive source.\n *\n * @param req - Request whose callbacks are converted to protocol messages.\n * @returns `Node<T>` that emits `DATA` once on success then `COMPLETE`;\n * emits `ERROR` on failure.\n *\n * @category extra\n */\nexport function fromIDBRequest<T>(req: IDBRequest<T>): Node<T> {\n\treturn node<T>((_data, a) => {\n\t\tlet done = false;\n\t\tconst clear = () => {\n\t\t\treq.onsuccess = null;\n\t\t\treq.onerror = null;\n\t\t};\n\t\treq.onsuccess = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[DATA, req.result], [COMPLETE]]);\n\t\t};\n\t\treq.onerror = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, req.error ?? new Error(\"IndexedDB request failed\")]]);\n\t\t};\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tclear();\n\t\t\t},\n\t\t};\n\t});\n}\n\n/**\n * Wraps an `IDBTransaction` terminal lifecycle as a one-shot reactive source.\n *\n * @param tx - Transaction to observe.\n * @returns `Node<void>` that emits `DATA` (`undefined`) then `COMPLETE` on\n * success; emits `ERROR` on `error`/`abort`.\n *\n * @category extra\n */\nexport function fromIDBTransaction(tx: IDBTransaction): Node<void> {\n\treturn node<void>((_data, a) => {\n\t\tlet done = false;\n\t\tconst clear = () => {\n\t\t\ttx.oncomplete = null;\n\t\t\ttx.onerror = null;\n\t\t\ttx.onabort = null;\n\t\t};\n\t\ttx.oncomplete = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[DATA, undefined], [COMPLETE]]);\n\t\t};\n\t\ttx.onerror = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, tx.error ?? new Error(\"IndexedDB transaction failed\")]]);\n\t\t};\n\t\ttx.onabort = () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclear();\n\t\t\ta.down([[ERROR, tx.error ?? new Error(\"IndexedDB transaction aborted\")]]);\n\t\t};\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tclear();\n\t\t\t},\n\t\t};\n\t});\n}\n\n// The old `indexedDbStorage` kv adapter has been removed.\n// Use `indexedDbKv` from `./storage-tiers-browser.js` instead (Audit 4, 2026-04-24).\n"],"mappings":"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","names":["ERROR","node"]}
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var event_exports = {};
22
22
  __export(event_exports, {
23
23
  fromCron: () => fromCron,
24
+ fromPushNotification: () => fromPushNotification,
24
25
  matchesCron: () => matchesCron,
25
26
  parseCron: () => parseCron
26
27
  });
@@ -95,4 +96,31 @@ function fromCron(expr, opts) {
95
96
  { ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` }
96
97
  );
97
98
  }
99
+
100
+ // src/base/sources/event/push.ts
101
+ var import_core2 = require("@graphrefly/pure-ts/core");
102
+ function sourceOpts2(opts) {
103
+ return { describeKind: "producer", ...opts };
104
+ }
105
+ function fromPushNotification(register, opts) {
106
+ if (typeof register !== "function") {
107
+ throw new TypeError(
108
+ "fromPushNotification: a (deliver) => unsubscribe registration function is required"
109
+ );
110
+ }
111
+ return (0, import_core2.node)((_data, a) => {
112
+ let done = false;
113
+ const deliver = (payload) => {
114
+ if (done) return;
115
+ a.emit(payload);
116
+ };
117
+ const unsubscribe = register(deliver);
118
+ return {
119
+ onDeactivation: () => {
120
+ done = true;
121
+ if (typeof unsubscribe === "function") unsubscribe();
122
+ }
123
+ };
124
+ }, sourceOpts2(opts));
125
+ }
98
126
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/base/sources/event/index.ts","../../../../src/base/sources/event/cron.ts"],"sourcesContent":["/**\n * Event sources — cron (presentation) and DOM (browser-only).\n *\n * For the DOM subpath, import from @graphrefly/graphrefly/base/sources/browser.\n * fromTimer is substrate; import from @graphrefly/pure-ts/extra.\n *\n * @module\n */\n\nexport * from \"./cron.js\";\n// dom.ts is browser-only; exposed via the browser subpath entry\n","/**\n * Cron-based reactive sources and schedule types.\n *\n * Merged from extra/cron.ts + extra/sources/event.ts (fromCron) during cleave A2.\n * `fromCron` relocated here from `dom.ts` (post-cleave /qa A1): it uses zero DOM\n * APIs (only `setInterval` + `new Date()`), so it belongs on the universal\n * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.\n */\n\nimport { type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** Options for {@link fromCron}. */\nexport type FromCronOptions = ExtraOpts & {\n\t/** Polling interval in ms. Default `60_000`. */\n\ttickMs?: number;\n\t/** Output format: `\"timestamp_ns\"` (default) emits wall-clock nanoseconds; `\"date\"` emits a `Date` object. */\n\toutput?: \"timestamp_ns\" | \"date\";\n};\n\n/**\n * Minimal 5-field cron parser and matcher (minute hour day-of-month month day-of-week).\n * Ported from callbag-recharge `extra/cron.ts` for `fromCron` (roadmap §2.3).\n */\nexport interface CronSchedule {\n\tminutes: Set<number>;\n\thours: Set<number>;\n\tdaysOfMonth: Set<number>;\n\tmonths: Set<number>;\n\tdaysOfWeek: Set<number>;\n}\n\nfunction parseField(field: string, min: number, max: number): Set<number> {\n\tconst result = new Set<number>();\n\tfor (const part of field.split(\",\")) {\n\t\tconst [range, stepStr] = part.split(\"/\");\n\t\tconst step = stepStr ? Number.parseInt(stepStr, 10) : 1;\n\t\tif (Number.isNaN(step) || step < 1) throw new Error(`Invalid cron step: ${part}`);\n\t\tlet start: number;\n\t\tlet end: number;\n\t\tif (range === \"*\") {\n\t\t\tstart = min;\n\t\t\tend = max;\n\t\t} else if (range.includes(\"-\")) {\n\t\t\tconst [a, b] = range.split(\"-\");\n\t\t\tstart = Number.parseInt(a, 10);\n\t\t\tend = Number.parseInt(b, 10);\n\t\t} else {\n\t\t\tstart = Number.parseInt(range, 10);\n\t\t\tend = start;\n\t\t}\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) throw new Error(`Invalid cron field: ${field}`);\n\t\tif (start < min || end > max)\n\t\t\tthrow new Error(`Cron field out of range: ${field} (${min}-${max})`);\n\t\tif (start > end) throw new Error(`Invalid cron range: ${start}-${end} in ${field}`);\n\t\tfor (let i = start; i <= end; i += step) result.add(i);\n\t}\n\treturn result;\n}\n\n/**\n * Parses a standard 5-field cron expression into a {@link CronSchedule}.\n *\n * Supports `*`, ranges (`1-5`), steps (`*\\/5`, `0-30/10`), and comma-separated\n * lists. Fields are: minute (0–59), hour (0–23), day-of-month (1–31),\n * month (1–12), day-of-week (0–6, Sunday = 0).\n *\n * @param expr - Five-field whitespace-separated cron string (e.g. `\"0 9 * * 1-5\"`).\n * @returns Parsed {@link CronSchedule} with one `Set<number>` per field.\n * @throws Error when the expression does not have exactly 5 fields, contains\n * out-of-range values, or uses an invalid step.\n *\n * @example\n * ```ts\n * import { parseCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"0 9 * * 1-5\"); // weekdays at 09:00\n * sched.hours; // Set { 9 }\n * sched.daysOfWeek; // Set { 1, 2, 3, 4, 5 }\n * ```\n */\nexport function parseCron(expr: string): CronSchedule {\n\tconst parts = expr.trim().split(/\\s+/);\n\tif (parts.length !== 5) throw new Error(`Invalid cron: expected 5 fields, got ${parts.length}`);\n\treturn {\n\t\tminutes: parseField(parts[0], 0, 59),\n\t\thours: parseField(parts[1], 0, 23),\n\t\tdaysOfMonth: parseField(parts[2], 1, 31),\n\t\tmonths: parseField(parts[3], 1, 12),\n\t\tdaysOfWeek: parseField(parts[4], 0, 6),\n\t};\n}\n\n/**\n * Returns `true` if `date` satisfies every field of `schedule`.\n *\n * @param schedule - Parsed schedule from {@link parseCron}.\n * @param date - Moment to test (local time via `getMinutes`, `getHours`, etc.).\n * @returns `true` when all five cron fields match the given date.\n *\n * @example\n * ```ts\n * import { parseCron, matchesCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"30 8 * * 1\"); // Mondays at 08:30\n * const monday = new Date(\"2026-03-30T08:30:00\"); // a Monday\n * matchesCron(sched, monday); // true\n * ```\n */\nexport function matchesCron(schedule: CronSchedule, date: Date): boolean {\n\treturn (\n\t\tschedule.minutes.has(date.getMinutes()) &&\n\t\tschedule.hours.has(date.getHours()) &&\n\t\tschedule.daysOfMonth.has(date.getDate()) &&\n\t\tschedule.months.has(date.getMonth() + 1) &&\n\t\tschedule.daysOfWeek.has(date.getDay())\n\t);\n}\n\n/**\n * Polls on an interval; when the current minute matches a 5-field cron expression, emits once (see {@link parseCron}).\n *\n * @param expr - Cron string (`min hour dom month dow`).\n * @param opts - Producer options plus `tickMs` (default `60_000`) and `output` (`timestamp_ns` default, or `date` for `Date` values).\n * @returns `Node<number>` (nanosecond timestamp) or `Node<Date>` when `output: \"date\"`.\n *\n * @example\n * ```ts\n * import { fromCron } from \"@graphrefly/graphrefly\";\n *\n * fromCron(\"0 9 * * 1\");\n * ```\n *\n * @category extra\n */\nexport function fromCron(expr: string, opts?: FromCronOptions & { output: \"date\" }): Node<Date>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number | Date> {\n\tconst schedule: CronSchedule = parseCron(expr);\n\tconst { tickMs: tickOpt, output, ...rest } = opts ?? {};\n\tconst tickMs = tickOpt ?? 60_000;\n\tconst emitDate = output === \"date\";\n\treturn node<number | Date>(\n\t\t(_data, a) => {\n\t\t\tlet lastFiredKey = -1;\n\t\t\tconst check = () => {\n\t\t\t\tconst now = new Date();\n\t\t\t\tconst key =\n\t\t\t\t\tnow.getFullYear() * 100_000_000 +\n\t\t\t\t\t(now.getMonth() + 1) * 1_000_000 +\n\t\t\t\t\tnow.getDate() * 10_000 +\n\t\t\t\t\tnow.getHours() * 100 +\n\t\t\t\t\tnow.getMinutes();\n\t\t\t\tif (key !== lastFiredKey && matchesCron(schedule, now)) {\n\t\t\t\t\tlastFiredKey = key;\n\t\t\t\t\ta.emit(emitDate ? now : wallClockNs());\n\t\t\t\t}\n\t\t\t};\n\t\t\tcheck();\n\t\t\tconst id = setInterval(check, tickMs);\n\t\t\treturn { onDeactivation: () => clearInterval(id) };\n\t\t},\n\t\t{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` },\n\t);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/base/sources/event/index.ts","../../../../src/base/sources/event/cron.ts","../../../../src/base/sources/event/push.ts"],"sourcesContent":["/**\n * Event sources — cron + push (universal) and DOM (browser-only).\n *\n * For the DOM subpath, import from @graphrefly/graphrefly/base/sources/browser.\n * fromTimer is substrate; import from @graphrefly/pure-ts/extra.\n *\n * @module\n */\n\nexport * from \"./cron.js\";\nexport * from \"./push.js\";\n// dom.ts is browser-only; exposed via the browser subpath entry\n","/**\n * Cron-based reactive sources and schedule types.\n *\n * Merged from extra/cron.ts + extra/sources/event.ts (fromCron) during cleave A2.\n * `fromCron` relocated here from `dom.ts` (post-cleave /qa A1): it uses zero DOM\n * APIs (only `setInterval` + `new Date()`), so it belongs on the universal\n * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.\n */\n\nimport { type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** Options for {@link fromCron}. */\nexport type FromCronOptions = ExtraOpts & {\n\t/** Polling interval in ms. Default `60_000`. */\n\ttickMs?: number;\n\t/** Output format: `\"timestamp_ns\"` (default) emits wall-clock nanoseconds; `\"date\"` emits a `Date` object. */\n\toutput?: \"timestamp_ns\" | \"date\";\n};\n\n/**\n * Minimal 5-field cron parser and matcher (minute hour day-of-month month day-of-week).\n * Ported from callbag-recharge `extra/cron.ts` for `fromCron` (roadmap §2.3).\n */\nexport interface CronSchedule {\n\tminutes: Set<number>;\n\thours: Set<number>;\n\tdaysOfMonth: Set<number>;\n\tmonths: Set<number>;\n\tdaysOfWeek: Set<number>;\n}\n\nfunction parseField(field: string, min: number, max: number): Set<number> {\n\tconst result = new Set<number>();\n\tfor (const part of field.split(\",\")) {\n\t\tconst [range, stepStr] = part.split(\"/\");\n\t\tconst step = stepStr ? Number.parseInt(stepStr, 10) : 1;\n\t\tif (Number.isNaN(step) || step < 1) throw new Error(`Invalid cron step: ${part}`);\n\t\tlet start: number;\n\t\tlet end: number;\n\t\tif (range === \"*\") {\n\t\t\tstart = min;\n\t\t\tend = max;\n\t\t} else if (range.includes(\"-\")) {\n\t\t\tconst [a, b] = range.split(\"-\");\n\t\t\tstart = Number.parseInt(a, 10);\n\t\t\tend = Number.parseInt(b, 10);\n\t\t} else {\n\t\t\tstart = Number.parseInt(range, 10);\n\t\t\tend = start;\n\t\t}\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) throw new Error(`Invalid cron field: ${field}`);\n\t\tif (start < min || end > max)\n\t\t\tthrow new Error(`Cron field out of range: ${field} (${min}-${max})`);\n\t\tif (start > end) throw new Error(`Invalid cron range: ${start}-${end} in ${field}`);\n\t\tfor (let i = start; i <= end; i += step) result.add(i);\n\t}\n\treturn result;\n}\n\n/**\n * Parses a standard 5-field cron expression into a {@link CronSchedule}.\n *\n * Supports `*`, ranges (`1-5`), steps (`*\\/5`, `0-30/10`), and comma-separated\n * lists. Fields are: minute (0–59), hour (0–23), day-of-month (1–31),\n * month (1–12), day-of-week (0–6, Sunday = 0).\n *\n * @param expr - Five-field whitespace-separated cron string (e.g. `\"0 9 * * 1-5\"`).\n * @returns Parsed {@link CronSchedule} with one `Set<number>` per field.\n * @throws Error when the expression does not have exactly 5 fields, contains\n * out-of-range values, or uses an invalid step.\n *\n * @example\n * ```ts\n * import { parseCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"0 9 * * 1-5\"); // weekdays at 09:00\n * sched.hours; // Set { 9 }\n * sched.daysOfWeek; // Set { 1, 2, 3, 4, 5 }\n * ```\n */\nexport function parseCron(expr: string): CronSchedule {\n\tconst parts = expr.trim().split(/\\s+/);\n\tif (parts.length !== 5) throw new Error(`Invalid cron: expected 5 fields, got ${parts.length}`);\n\treturn {\n\t\tminutes: parseField(parts[0], 0, 59),\n\t\thours: parseField(parts[1], 0, 23),\n\t\tdaysOfMonth: parseField(parts[2], 1, 31),\n\t\tmonths: parseField(parts[3], 1, 12),\n\t\tdaysOfWeek: parseField(parts[4], 0, 6),\n\t};\n}\n\n/**\n * Returns `true` if `date` satisfies every field of `schedule`.\n *\n * @param schedule - Parsed schedule from {@link parseCron}.\n * @param date - Moment to test (local time via `getMinutes`, `getHours`, etc.).\n * @returns `true` when all five cron fields match the given date.\n *\n * @example\n * ```ts\n * import { parseCron, matchesCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"30 8 * * 1\"); // Mondays at 08:30\n * const monday = new Date(\"2026-03-30T08:30:00\"); // a Monday\n * matchesCron(sched, monday); // true\n * ```\n */\nexport function matchesCron(schedule: CronSchedule, date: Date): boolean {\n\treturn (\n\t\tschedule.minutes.has(date.getMinutes()) &&\n\t\tschedule.hours.has(date.getHours()) &&\n\t\tschedule.daysOfMonth.has(date.getDate()) &&\n\t\tschedule.months.has(date.getMonth() + 1) &&\n\t\tschedule.daysOfWeek.has(date.getDay())\n\t);\n}\n\n/**\n * Polls on an interval; when the current minute matches a 5-field cron expression, emits once (see {@link parseCron}).\n *\n * @param expr - Cron string (`min hour dom month dow`).\n * @param opts - Producer options plus `tickMs` (default `60_000`) and `output` (`timestamp_ns` default, or `date` for `Date` values).\n * @returns `Node<number>` (nanosecond timestamp) or `Node<Date>` when `output: \"date\"`.\n *\n * @example\n * ```ts\n * import { fromCron } from \"@graphrefly/graphrefly\";\n *\n * fromCron(\"0 9 * * 1\");\n * ```\n *\n * @category extra\n */\nexport function fromCron(expr: string, opts?: FromCronOptions & { output: \"date\" }): Node<Date>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number | Date> {\n\tconst schedule: CronSchedule = parseCron(expr);\n\tconst { tickMs: tickOpt, output, ...rest } = opts ?? {};\n\tconst tickMs = tickOpt ?? 60_000;\n\tconst emitDate = output === \"date\";\n\treturn node<number | Date>(\n\t\t(_data, a) => {\n\t\t\tlet lastFiredKey = -1;\n\t\t\tconst check = () => {\n\t\t\t\tconst now = new Date();\n\t\t\t\tconst key =\n\t\t\t\t\tnow.getFullYear() * 100_000_000 +\n\t\t\t\t\t(now.getMonth() + 1) * 1_000_000 +\n\t\t\t\t\tnow.getDate() * 10_000 +\n\t\t\t\t\tnow.getHours() * 100 +\n\t\t\t\t\tnow.getMinutes();\n\t\t\t\tif (key !== lastFiredKey && matchesCron(schedule, now)) {\n\t\t\t\t\tlastFiredKey = key;\n\t\t\t\t\ta.emit(emitDate ? now : wallClockNs());\n\t\t\t\t}\n\t\t\t};\n\t\t\tcheck();\n\t\t\tconst id = setInterval(check, tickMs);\n\t\t\treturn { onDeactivation: () => clearInterval(id) };\n\t\t},\n\t\t{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` },\n\t);\n}\n","/**\n * Push-notification reactive source (universal — transport-agnostic).\n *\n * `fromPushNotification` turns host-delivered push messages (FCM / APNS /\n * Expo push / Web Push / a NestJS gateway fan-out — any transport) into a\n * reactive `Node<T>`. It is the sanctioned async/external boundary for the\n * two-reactive-island architecture: a mobile/web island and a backend island,\n * each reactive internally, with push as the transport between them (spec\n * §5.10 — async boundaries live in sources, NOT in node fns/operators; this\n * is the same shape as `fromEvent`, not an imperative trigger into the graph).\n *\n * The native/network bridge stays entirely inside the host-supplied\n * `register` callback — this primitive owns only the reactive emission and\n * teardown, so it carries zero `node:*` / DOM / SDK dependency and is\n * Hermes-safe by construction.\n */\n\nimport { type Node, type NodeOptions, node } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** Tears down a push registration (remove listener, close channel, abort). */\nexport type PushUnsubscribe = () => void;\n\n/**\n * Host registration callback for {@link fromPushNotification}.\n *\n * Called once on node activation with a `deliver` sink. Wire your push\n * transport here and call `deliver(payload)` for **each** incoming message.\n * Return a {@link PushUnsubscribe} (or nothing if there is no teardown).\n *\n * Any async/native setup belongs **inside** this callback (spec §5.10). The\n * returned unsubscribe must be synchronous; if your SDK registration is\n * async, kick it off here and return a function that aborts/detaches it\n * (the async boundary stays in the host, not in the reactive layer).\n */\nexport type PushRegister<T> = (deliver: (payload: T) => void) => PushUnsubscribe | undefined;\n\n/**\n * Wraps a host push transport; each delivered message becomes a `DATA`\n * emission. Teardown invokes the registration's unsubscribe.\n *\n * @param register - Called on activation with a `deliver(payload)` sink;\n * returns an optional unsubscribe.\n * @param opts - Producer node options (`name`, `meta`, …).\n * @returns `Node<T>` — push payloads as a reactive stream.\n *\n * @example\n * ```ts\n * import { fromPushNotification } from \"@graphrefly/graphrefly\";\n *\n * // memo:Re premium backend — opt-in cloud audit pushed (not polled).\n * const auditPushes = fromPushNotification<AuditEvent>((deliver) => {\n * const sub = messaging.onMessage((msg) => deliver(msg.data as AuditEvent));\n * return () => sub.remove();\n * });\n * ```\n *\n * @remarks\n * A synchronous throw inside `register` propagates as an activation failure\n * (it is not caught here) — same shape as `fromEvent`. Push transports are\n * open-ended: this source never emits `COMPLETE`/`ERROR` on its own; the\n * stream ends only via `onDeactivation` (unsubscribe). Surface a terminal\n * yourself (e.g. compose a downstream operator) if the host needs one.\n *\n * @category extra\n */\nexport function fromPushNotification<T = unknown>(\n\tregister: PushRegister<T>,\n\topts?: ExtraOpts,\n): Node<T> {\n\tif (typeof register !== \"function\") {\n\t\tthrow new TypeError(\n\t\t\t\"fromPushNotification: a (deliver) => unsubscribe registration function is required\",\n\t\t);\n\t}\n\treturn node<T>((_data, a) => {\n\t\tlet done = false;\n\t\tconst deliver = (payload: T) => {\n\t\t\tif (done) return;\n\t\t\ta.emit(payload);\n\t\t};\n\t\tconst unsubscribe = register(deliver);\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tif (typeof unsubscribe === \"function\") unsubscribe();\n\t\t\t},\n\t\t};\n\t}, sourceOpts(opts));\n}\n"],"mappings":"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","names":["import_core","sourceOpts"]}
@@ -9,9 +9,9 @@ import { NodeOptions, Node } from '@graphrefly/pure-ts/core';
9
9
  * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.
10
10
  */
11
11
 
12
- type ExtraOpts = Omit<NodeOptions, "describeKind">;
12
+ type ExtraOpts$1 = Omit<NodeOptions, "describeKind">;
13
13
  /** Options for {@link fromCron}. */
14
- type FromCronOptions = ExtraOpts & {
14
+ type FromCronOptions = ExtraOpts$1 & {
15
15
  /** Polling interval in ms. Default `60_000`. */
16
16
  tickMs?: number;
17
17
  /** Output format: `"timestamp_ns"` (default) emits wall-clock nanoseconds; `"date"` emits a `Date` object. */
@@ -88,4 +88,68 @@ declare function fromCron(expr: string, opts?: FromCronOptions & {
88
88
  }): Node<Date>;
89
89
  declare function fromCron(expr: string, opts?: FromCronOptions): Node<number>;
90
90
 
91
- export { type CronSchedule, type FromCronOptions, fromCron, matchesCron, parseCron };
91
+ /**
92
+ * Push-notification reactive source (universal — transport-agnostic).
93
+ *
94
+ * `fromPushNotification` turns host-delivered push messages (FCM / APNS /
95
+ * Expo push / Web Push / a NestJS gateway fan-out — any transport) into a
96
+ * reactive `Node<T>`. It is the sanctioned async/external boundary for the
97
+ * two-reactive-island architecture: a mobile/web island and a backend island,
98
+ * each reactive internally, with push as the transport between them (spec
99
+ * §5.10 — async boundaries live in sources, NOT in node fns/operators; this
100
+ * is the same shape as `fromEvent`, not an imperative trigger into the graph).
101
+ *
102
+ * The native/network bridge stays entirely inside the host-supplied
103
+ * `register` callback — this primitive owns only the reactive emission and
104
+ * teardown, so it carries zero `node:*` / DOM / SDK dependency and is
105
+ * Hermes-safe by construction.
106
+ */
107
+
108
+ type ExtraOpts = Omit<NodeOptions, "describeKind">;
109
+ /** Tears down a push registration (remove listener, close channel, abort). */
110
+ type PushUnsubscribe = () => void;
111
+ /**
112
+ * Host registration callback for {@link fromPushNotification}.
113
+ *
114
+ * Called once on node activation with a `deliver` sink. Wire your push
115
+ * transport here and call `deliver(payload)` for **each** incoming message.
116
+ * Return a {@link PushUnsubscribe} (or nothing if there is no teardown).
117
+ *
118
+ * Any async/native setup belongs **inside** this callback (spec §5.10). The
119
+ * returned unsubscribe must be synchronous; if your SDK registration is
120
+ * async, kick it off here and return a function that aborts/detaches it
121
+ * (the async boundary stays in the host, not in the reactive layer).
122
+ */
123
+ type PushRegister<T> = (deliver: (payload: T) => void) => PushUnsubscribe | undefined;
124
+ /**
125
+ * Wraps a host push transport; each delivered message becomes a `DATA`
126
+ * emission. Teardown invokes the registration's unsubscribe.
127
+ *
128
+ * @param register - Called on activation with a `deliver(payload)` sink;
129
+ * returns an optional unsubscribe.
130
+ * @param opts - Producer node options (`name`, `meta`, …).
131
+ * @returns `Node<T>` — push payloads as a reactive stream.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import { fromPushNotification } from "@graphrefly/graphrefly";
136
+ *
137
+ * // memo:Re premium backend — opt-in cloud audit pushed (not polled).
138
+ * const auditPushes = fromPushNotification<AuditEvent>((deliver) => {
139
+ * const sub = messaging.onMessage((msg) => deliver(msg.data as AuditEvent));
140
+ * return () => sub.remove();
141
+ * });
142
+ * ```
143
+ *
144
+ * @remarks
145
+ * A synchronous throw inside `register` propagates as an activation failure
146
+ * (it is not caught here) — same shape as `fromEvent`. Push transports are
147
+ * open-ended: this source never emits `COMPLETE`/`ERROR` on its own; the
148
+ * stream ends only via `onDeactivation` (unsubscribe). Surface a terminal
149
+ * yourself (e.g. compose a downstream operator) if the host needs one.
150
+ *
151
+ * @category extra
152
+ */
153
+ declare function fromPushNotification<T = unknown>(register: PushRegister<T>, opts?: ExtraOpts): Node<T>;
154
+
155
+ export { type CronSchedule, type FromCronOptions, type PushRegister, type PushUnsubscribe, fromCron, fromPushNotification, matchesCron, parseCron };
@@ -9,9 +9,9 @@ import { NodeOptions, Node } from '@graphrefly/pure-ts/core';
9
9
  * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.
10
10
  */
11
11
 
12
- type ExtraOpts = Omit<NodeOptions, "describeKind">;
12
+ type ExtraOpts$1 = Omit<NodeOptions, "describeKind">;
13
13
  /** Options for {@link fromCron}. */
14
- type FromCronOptions = ExtraOpts & {
14
+ type FromCronOptions = ExtraOpts$1 & {
15
15
  /** Polling interval in ms. Default `60_000`. */
16
16
  tickMs?: number;
17
17
  /** Output format: `"timestamp_ns"` (default) emits wall-clock nanoseconds; `"date"` emits a `Date` object. */
@@ -88,4 +88,68 @@ declare function fromCron(expr: string, opts?: FromCronOptions & {
88
88
  }): Node<Date>;
89
89
  declare function fromCron(expr: string, opts?: FromCronOptions): Node<number>;
90
90
 
91
- export { type CronSchedule, type FromCronOptions, fromCron, matchesCron, parseCron };
91
+ /**
92
+ * Push-notification reactive source (universal — transport-agnostic).
93
+ *
94
+ * `fromPushNotification` turns host-delivered push messages (FCM / APNS /
95
+ * Expo push / Web Push / a NestJS gateway fan-out — any transport) into a
96
+ * reactive `Node<T>`. It is the sanctioned async/external boundary for the
97
+ * two-reactive-island architecture: a mobile/web island and a backend island,
98
+ * each reactive internally, with push as the transport between them (spec
99
+ * §5.10 — async boundaries live in sources, NOT in node fns/operators; this
100
+ * is the same shape as `fromEvent`, not an imperative trigger into the graph).
101
+ *
102
+ * The native/network bridge stays entirely inside the host-supplied
103
+ * `register` callback — this primitive owns only the reactive emission and
104
+ * teardown, so it carries zero `node:*` / DOM / SDK dependency and is
105
+ * Hermes-safe by construction.
106
+ */
107
+
108
+ type ExtraOpts = Omit<NodeOptions, "describeKind">;
109
+ /** Tears down a push registration (remove listener, close channel, abort). */
110
+ type PushUnsubscribe = () => void;
111
+ /**
112
+ * Host registration callback for {@link fromPushNotification}.
113
+ *
114
+ * Called once on node activation with a `deliver` sink. Wire your push
115
+ * transport here and call `deliver(payload)` for **each** incoming message.
116
+ * Return a {@link PushUnsubscribe} (or nothing if there is no teardown).
117
+ *
118
+ * Any async/native setup belongs **inside** this callback (spec §5.10). The
119
+ * returned unsubscribe must be synchronous; if your SDK registration is
120
+ * async, kick it off here and return a function that aborts/detaches it
121
+ * (the async boundary stays in the host, not in the reactive layer).
122
+ */
123
+ type PushRegister<T> = (deliver: (payload: T) => void) => PushUnsubscribe | undefined;
124
+ /**
125
+ * Wraps a host push transport; each delivered message becomes a `DATA`
126
+ * emission. Teardown invokes the registration's unsubscribe.
127
+ *
128
+ * @param register - Called on activation with a `deliver(payload)` sink;
129
+ * returns an optional unsubscribe.
130
+ * @param opts - Producer node options (`name`, `meta`, …).
131
+ * @returns `Node<T>` — push payloads as a reactive stream.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import { fromPushNotification } from "@graphrefly/graphrefly";
136
+ *
137
+ * // memo:Re premium backend — opt-in cloud audit pushed (not polled).
138
+ * const auditPushes = fromPushNotification<AuditEvent>((deliver) => {
139
+ * const sub = messaging.onMessage((msg) => deliver(msg.data as AuditEvent));
140
+ * return () => sub.remove();
141
+ * });
142
+ * ```
143
+ *
144
+ * @remarks
145
+ * A synchronous throw inside `register` propagates as an activation failure
146
+ * (it is not caught here) — same shape as `fromEvent`. Push transports are
147
+ * open-ended: this source never emits `COMPLETE`/`ERROR` on its own; the
148
+ * stream ends only via `onDeactivation` (unsubscribe). Surface a terminal
149
+ * yourself (e.g. compose a downstream operator) if the host needs one.
150
+ *
151
+ * @category extra
152
+ */
153
+ declare function fromPushNotification<T = unknown>(register: PushRegister<T>, opts?: ExtraOpts): Node<T>;
154
+
155
+ export { type CronSchedule, type FromCronOptions, type PushRegister, type PushUnsubscribe, fromCron, fromPushNotification, matchesCron, parseCron };
@@ -1,4 +1,6 @@
1
- import "../../../chunk-W2BOPXTI.js";
1
+ import {
2
+ fromPushNotification
3
+ } from "../../../chunk-QSW4DFKE.js";
2
4
  import {
3
5
  fromCron,
4
6
  matchesCron,
@@ -7,6 +9,7 @@ import {
7
9
  import "../../../chunk-AZDQPQ3V.js";
8
10
  export {
9
11
  fromCron,
12
+ fromPushNotification,
10
13
  matchesCron,
11
14
  parseCron
12
15
  };
@@ -27,11 +27,11 @@ function operatorOpts(opts) {
27
27
  function isNode(x) {
28
28
  return x != null && typeof x === "object" && "cache" in x && typeof x.subscribe === "function";
29
29
  }
30
- var import_core3;
30
+ var import_core4;
31
31
  var init_internal = __esm({
32
32
  "src/base/resilience/_internal.ts"() {
33
33
  "use strict";
34
- import_core3 = require("@graphrefly/pure-ts/core");
34
+ import_core4 = require("@graphrefly/pure-ts/core");
35
35
  }
36
36
  });
37
37
 
@@ -180,21 +180,21 @@ function withTimeout(source, opts, extraOpts) {
180
180
  }
181
181
  const callerMeta = extraOpts?.meta;
182
182
  const factoryArgs = isReactive ? { ns: "Node<Partial<TimeoutOptions>>" } : { ns: latestOpts.ns };
183
- const timeoutState = (0, import_core4.node)([], {
183
+ const timeoutState = (0, import_core5.node)([], {
184
184
  name: "timeoutState",
185
185
  describeKind: "state",
186
186
  initial: { status: "pending" },
187
187
  equals: (a, b) => a === b || a != null && b != null && typeof a === "object" && typeof b === "object" && a.status === b.status && JSON.stringify(a) === JSON.stringify(b)
188
188
  });
189
- const out = (0, import_core4.node)(
189
+ const out = (0, import_core5.node)(
190
190
  (_data, a) => {
191
191
  let stopped = false;
192
192
  let lastDeadlineNs = 0;
193
- const timer = new import_core4.ResettableTimer();
193
+ const timer = new import_core5.ResettableTimer();
194
194
  let optsUnsub = null;
195
195
  let srcUnsub = null;
196
196
  function emitState(next) {
197
- timeoutState.down([[import_core4.DIRTY], [import_core4.DATA, next]]);
197
+ timeoutState.down([[import_core5.DIRTY], [import_core5.DATA, next]]);
198
198
  }
199
199
  function startTimer() {
200
200
  if (stopped) return;
@@ -203,7 +203,7 @@ function withTimeout(source, opts, extraOpts) {
203
203
  }
204
204
  const ns = latestOpts.ns;
205
205
  lastDeadlineNs = ns;
206
- const startedAt = (0, import_core4.monotonicNs)();
206
+ const startedAt = (0, import_core5.monotonicNs)();
207
207
  const delayMs = ns / NS_PER_MS;
208
208
  emitState({
209
209
  status: "running",
@@ -216,10 +216,10 @@ function withTimeout(source, opts, extraOpts) {
216
216
  srcUnsub?.();
217
217
  emitState({
218
218
  status: "errored",
219
- firedAt_ns: (0, import_core4.monotonicNs)(),
219
+ firedAt_ns: (0, import_core5.monotonicNs)(),
220
220
  deadline_ns: ns
221
221
  });
222
- a.down([[import_core4.ERROR, new TimeoutError(ns)]]);
222
+ a.down([[import_core5.ERROR, new TimeoutError(ns)]]);
223
223
  });
224
224
  }
225
225
  function attachSource() {
@@ -228,31 +228,31 @@ function withTimeout(source, opts, extraOpts) {
228
228
  for (const m of msgs) {
229
229
  if (stopped) return;
230
230
  const t = m[0];
231
- if (t === import_core4.DIRTY) a.down([[import_core4.DIRTY]]);
232
- else if (t === import_core4.DATA) {
231
+ if (t === import_core5.DIRTY) a.down([[import_core5.DIRTY]]);
232
+ else if (t === import_core5.DATA) {
233
233
  startTimer();
234
234
  a.emit(m[1]);
235
- } else if (t === import_core4.RESOLVED) a.down([[import_core4.RESOLVED]]);
236
- else if (t === import_core4.COMPLETE) {
235
+ } else if (t === import_core5.RESOLVED) a.down([[import_core5.RESOLVED]]);
236
+ else if (t === import_core5.COMPLETE) {
237
237
  timer.cancel();
238
238
  stopped = true;
239
239
  emitState({
240
240
  status: "completed",
241
- settledAt_ns: (0, import_core4.monotonicNs)()
241
+ settledAt_ns: (0, import_core5.monotonicNs)()
242
242
  });
243
- a.down([[import_core4.COMPLETE]]);
243
+ a.down([[import_core5.COMPLETE]]);
244
244
  return;
245
- } else if (t === import_core4.ERROR) {
245
+ } else if (t === import_core5.ERROR) {
246
246
  timer.cancel();
247
247
  stopped = true;
248
248
  emitState({
249
249
  status: "errored",
250
- firedAt_ns: (0, import_core4.monotonicNs)(),
250
+ firedAt_ns: (0, import_core5.monotonicNs)(),
251
251
  deadline_ns: lastDeadlineNs
252
252
  });
253
253
  a.down([m]);
254
254
  return;
255
- } else if (t === import_core4.TEARDOWN) {
255
+ } else if (t === import_core5.TEARDOWN) {
256
256
  timer.cancel();
257
257
  stopped = true;
258
258
  a.down([m]);
@@ -268,7 +268,7 @@ function withTimeout(source, opts, extraOpts) {
268
268
  const optsNode = opts;
269
269
  optsUnsub = optsNode.subscribe((msgs) => {
270
270
  for (const m of msgs) {
271
- if (m[0] !== import_core4.DATA) continue;
271
+ if (m[0] !== import_core5.DATA) continue;
272
272
  const next = m[1];
273
273
  if (next == null || typeof next !== "object") continue;
274
274
  const keys = Object.keys(next);
@@ -279,7 +279,7 @@ function withTimeout(source, opts, extraOpts) {
279
279
  stopped = true;
280
280
  a.down([
281
281
  [
282
- import_core4.ERROR,
282
+ import_core5.ERROR,
283
283
  new RangeError(
284
284
  "withTimeout: opts.ns must be a positive finite number on first settle"
285
285
  )
@@ -316,16 +316,16 @@ function withTimeout(source, opts, extraOpts) {
316
316
  {
317
317
  ...operatorOpts(),
318
318
  initial: source.cache,
319
- meta: { ...callerMeta ?? {}, ...(0, import_core4.factoryTag)("withTimeout", factoryArgs) }
319
+ meta: { ...callerMeta ?? {}, ...(0, import_core5.factoryTag)("withTimeout", factoryArgs) }
320
320
  }
321
321
  );
322
322
  return { node: out, timeoutState };
323
323
  }
324
- var import_core4, TimeoutError;
324
+ var import_core5, TimeoutError;
325
325
  var init_timeout = __esm({
326
326
  "src/base/resilience/timeout.ts"() {
327
327
  "use strict";
328
- import_core4 = require("@graphrefly/pure-ts/core");
328
+ import_core5 = require("@graphrefly/pure-ts/core");
329
329
  init_internal();
330
330
  init_backoff();
331
331
  TimeoutError = class extends Error {
@@ -347,6 +347,7 @@ __export(sources_exports, {
347
347
  firstWhere: () => firstWhere,
348
348
  forEach: () => forEach,
349
349
  fromCron: () => fromCron,
350
+ fromPushNotification: () => fromPushNotification,
350
351
  matchesCron: () => matchesCron,
351
352
  nodeSignal: () => nodeSignal,
352
353
  parseCron: () => parseCron,
@@ -449,8 +450,18 @@ function toArray(source, opts) {
449
450
  },
450
451
  {
451
452
  describeKind: "derived",
453
+ ...opts,
454
+ // Operator-required flags spread AFTER user `opts` so a caller
455
+ // cannot accidentally override them (QA F2 spread-order fix,
456
+ // DS-2.7.A `/qa` 2026-05-20 — matches the
457
+ // `reduce`/`scan`/`take`/`last` substrate pattern). Spec §2.7
458
+ // R2.7.1 (DS-2.7.A): Reduce-class shape — fn must fire on
459
+ // upstream COMPLETE to emit the accumulated array (or `[]` for an
460
+ // empty source) followed by its own `[[COMPLETE]]`. Required
461
+ // because `completeWhenDepsComplete: false` means auto-COMPLETE
462
+ // is OFF; without the opt-in `toArray(empty())` never completes.
452
463
  completeWhenDepsComplete: false,
453
- ...opts
464
+ terminalAsRealInput: true
454
465
  }
455
466
  );
456
467
  }
@@ -550,8 +561,35 @@ function fromCron(expr, opts) {
550
561
  );
551
562
  }
552
563
 
564
+ // src/base/sources/event/push.ts
565
+ var import_core3 = require("@graphrefly/pure-ts/core");
566
+ function sourceOpts3(opts) {
567
+ return { describeKind: "producer", ...opts };
568
+ }
569
+ function fromPushNotification(register, opts) {
570
+ if (typeof register !== "function") {
571
+ throw new TypeError(
572
+ "fromPushNotification: a (deliver) => unsubscribe registration function is required"
573
+ );
574
+ }
575
+ return (0, import_core3.node)((_data, a) => {
576
+ let done = false;
577
+ const deliver = (payload) => {
578
+ if (done) return;
579
+ a.emit(payload);
580
+ };
581
+ const unsubscribe = register(deliver);
582
+ return {
583
+ onDeactivation: () => {
584
+ done = true;
585
+ if (typeof unsubscribe === "function") unsubscribe();
586
+ }
587
+ };
588
+ }, sourceOpts3(opts));
589
+ }
590
+
553
591
  // src/base/sources/settled.ts
554
- var import_core5 = require("@graphrefly/pure-ts/core");
592
+ var import_core6 = require("@graphrefly/pure-ts/core");
555
593
  function firstValueFrom(source) {
556
594
  return new Promise((resolve, reject) => {
557
595
  let settled = false;
@@ -560,7 +598,7 @@ function firstValueFrom(source) {
560
598
  unsub = source.subscribe((msgs) => {
561
599
  for (const m of msgs) {
562
600
  if (settled) return;
563
- if (m[0] === import_core5.DATA) {
601
+ if (m[0] === import_core6.DATA) {
564
602
  settled = true;
565
603
  resolve(m[1]);
566
604
  if (unsub) {
@@ -569,7 +607,7 @@ function firstValueFrom(source) {
569
607
  } else shouldUnsub = true;
570
608
  return;
571
609
  }
572
- if (m[0] === import_core5.ERROR) {
610
+ if (m[0] === import_core6.ERROR) {
573
611
  settled = true;
574
612
  reject(m[1]);
575
613
  if (unsub) {
@@ -578,7 +616,7 @@ function firstValueFrom(source) {
578
616
  } else shouldUnsub = true;
579
617
  return;
580
618
  }
581
- if (m[0] === import_core5.COMPLETE) {
619
+ if (m[0] === import_core6.COMPLETE) {
582
620
  settled = true;
583
621
  reject(new Error("completed without DATA"));
584
622
  if (unsub) {
@@ -632,8 +670,8 @@ function firstWhere(source, predicate, opts) {
632
670
  if (settled) return;
633
671
  for (const m of msgs) {
634
672
  if (settled) return;
635
- if (inInitialSyncPhase && m[0] === import_core5.DATA) continue;
636
- if (m[0] === import_core5.DATA) {
673
+ if (inInitialSyncPhase && m[0] === import_core6.DATA) continue;
674
+ if (m[0] === import_core6.DATA) {
637
675
  const v = m[1];
638
676
  if (predicate(v)) {
639
677
  settleData(v);
@@ -641,12 +679,12 @@ function firstWhere(source, predicate, opts) {
641
679
  return;
642
680
  }
643
681
  }
644
- if (m[0] === import_core5.ERROR) {
682
+ if (m[0] === import_core6.ERROR) {
645
683
  settleError(m[1]);
646
684
  detach();
647
685
  return;
648
686
  }
649
- if (m[0] === import_core5.COMPLETE) {
687
+ if (m[0] === import_core6.COMPLETE) {
650
688
  settleComplete();
651
689
  detach();
652
690
  return;
@@ -712,17 +750,17 @@ function nodeSignal(source, opts) {
712
750
  unsub = source.subscribe((msgs) => {
713
751
  if (ctrl.signal.aborted) return;
714
752
  for (const m of msgs) {
715
- if (m[0] === import_core5.DATA && m[1] === true) {
753
+ if (m[0] === import_core6.DATA && m[1] === true) {
716
754
  ctrl.abort(reason);
717
755
  done();
718
756
  return;
719
757
  }
720
- if (m[0] === import_core5.ERROR) {
758
+ if (m[0] === import_core6.ERROR) {
721
759
  ctrl.abort(m[1]);
722
760
  done();
723
761
  return;
724
762
  }
725
- if (m[0] === import_core5.COMPLETE) {
763
+ if (m[0] === import_core6.COMPLETE) {
726
764
  done();
727
765
  return;
728
766
  }
@@ -743,13 +781,13 @@ function nodeSignal(source, opts) {
743
781
  };
744
782
  }
745
783
  function reactiveCounter(cap) {
746
- const counter = (0, import_core5.node)([], { initial: 0 });
784
+ const counter = (0, import_core6.node)([], { initial: 0 });
747
785
  return {
748
786
  node: counter,
749
787
  increment() {
750
788
  const current = counter.cache ?? 0;
751
789
  if (current >= cap) return false;
752
- counter.down([[import_core5.DIRTY], [import_core5.DATA, current + 1]]);
790
+ counter.down([[import_core6.DIRTY], [import_core6.DATA, current + 1]]);
753
791
  return true;
754
792
  },
755
793
  get() {