@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.
- package/dist/base/composition/index.cjs +4 -3
- package/dist/base/composition/index.cjs.map +1 -1
- package/dist/base/composition/index.d.cts +14 -5
- package/dist/base/composition/index.d.ts +14 -5
- package/dist/base/composition/index.js +8 -8
- package/dist/base/index.cjs +152 -78
- package/dist/base/index.cjs.map +1 -1
- package/dist/base/index.d.cts +2 -2
- package/dist/base/index.d.ts +2 -2
- package/dist/base/index.js +75 -70
- package/dist/base/io/index.cjs +31 -17
- package/dist/base/io/index.cjs.map +1 -1
- package/dist/base/io/index.d.cts +32 -5
- package/dist/base/io/index.d.ts +32 -5
- package/dist/base/io/index.js +1 -1
- package/dist/base/mutation/index.cjs +21 -0
- package/dist/base/mutation/index.cjs.map +1 -1
- package/dist/base/mutation/index.d.cts +23 -1
- package/dist/base/mutation/index.d.ts +23 -1
- package/dist/base/mutation/index.js +3 -1
- package/dist/base/sources/browser/index.cjs +5 -3
- package/dist/base/sources/browser/index.cjs.map +1 -1
- package/dist/base/sources/browser/index.d.cts +20 -2
- package/dist/base/sources/browser/index.d.ts +20 -2
- package/dist/base/sources/browser/index.js +5 -3
- package/dist/base/sources/browser/index.js.map +1 -1
- package/dist/base/sources/event/index.cjs +28 -0
- package/dist/base/sources/event/index.cjs.map +1 -1
- package/dist/base/sources/event/index.d.cts +67 -3
- package/dist/base/sources/event/index.d.ts +67 -3
- package/dist/base/sources/event/index.js +4 -1
- package/dist/base/sources/index.cjs +75 -37
- package/dist/base/sources/index.cjs.map +1 -1
- package/dist/base/sources/index.d.cts +1 -1
- package/dist/base/sources/index.d.ts +1 -1
- package/dist/base/sources/index.js +5 -2
- package/dist/{chunk-R6ZCSXKX.js → chunk-23MAWVOJ.js} +3 -3
- package/dist/{chunk-MS3WPRJR.js → chunk-3REMCHSS.js} +6 -6
- package/dist/chunk-3REMCHSS.js.map +1 -0
- package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
- package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
- package/dist/{chunk-6ZLCPUXS.js → chunk-46X2EFQH.js} +15 -4
- package/dist/chunk-46X2EFQH.js.map +1 -0
- package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
- package/dist/chunk-5UY3PNFY.js.map +1 -0
- package/dist/{chunk-FQSQONOU.js → chunk-65OM4XLQ.js} +49 -3
- package/dist/chunk-65OM4XLQ.js.map +1 -0
- package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
- package/dist/chunk-6DQYBIHW.js.map +1 -0
- package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
- package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
- package/dist/{chunk-3O3NKZJW.js → chunk-7T7WLEPM.js} +24 -3
- package/dist/chunk-7T7WLEPM.js.map +1 -0
- package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
- package/dist/chunk-AQAKDE7F.js.map +1 -0
- package/dist/{chunk-6MRSX3YK.js → chunk-B5Y5GPD5.js} +2 -2
- package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
- package/dist/chunk-C5QD5DQX.js.map +1 -0
- package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
- package/dist/chunk-D5YGR4TP.js.map +1 -0
- package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
- package/dist/chunk-DHDCOOJU.js.map +1 -0
- package/dist/{chunk-VP3TIUDF.js → chunk-DVTDF5OI.js} +2 -2
- package/dist/{chunk-OXD5LFQP.js → chunk-G7H6PN7P.js} +2 -2
- package/dist/{chunk-EL5VHUGK.js → chunk-GGKHHG5Y.js} +32 -18
- package/dist/chunk-GGKHHG5Y.js.map +1 -0
- package/dist/{chunk-446I4EGD.js → chunk-J5TBZFBD.js} +2 -2
- package/dist/{chunk-7AVQIGF6.js → chunk-K4ZYJ4EM.js} +554 -460
- package/dist/chunk-K4ZYJ4EM.js.map +1 -0
- package/dist/{chunk-QFE5BQH7.js → chunk-LTSI7ULC.js} +2 -2
- package/dist/{chunk-5GVURVIG.js → chunk-MMHGYX44.js} +12 -2
- package/dist/{chunk-5GVURVIG.js.map → chunk-MMHGYX44.js.map} +1 -1
- package/dist/{chunk-KRFGO5QH.js → chunk-MQMTRKY3.js} +118 -43
- package/dist/chunk-MQMTRKY3.js.map +1 -0
- package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
- package/dist/chunk-MTODGQBR.js.map +1 -0
- package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
- package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
- package/dist/{chunk-KNU73RZW.js → chunk-NSA5K5G2.js} +2 -2
- package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
- package/dist/chunk-QSW4DFKE.js +31 -0
- package/dist/chunk-QSW4DFKE.js.map +1 -0
- package/dist/{chunk-VAZXUK6G.js → chunk-SUNCHMML.js} +2 -2
- package/dist/{chunk-EP4WVQLX.js → chunk-T2U6N3FV.js} +6 -6
- package/dist/{chunk-T7SP3EYR.js → chunk-T5URUIIY.js} +33 -24
- package/dist/chunk-T5URUIIY.js.map +1 -0
- package/dist/{chunk-VNXAF2KE.js → chunk-TPTZZV25.js} +6 -6
- package/dist/chunk-TPTZZV25.js.map +1 -0
- package/dist/{chunk-IOJDYUA7.js → chunk-V46JWFGV.js} +6 -5
- package/dist/chunk-V46JWFGV.js.map +1 -0
- package/dist/{chunk-WGDEBIP4.js → chunk-X6ESZDR6.js} +5 -6
- package/dist/chunk-X6ESZDR6.js.map +1 -0
- package/dist/{chunk-N65E26UL.js → chunk-XEWV254I.js} +2 -2
- package/dist/{chunk-N65E26UL.js.map → chunk-XEWV254I.js.map} +1 -1
- package/dist/{chunk-PTWADEH3.js → chunk-YBJVKMTM.js} +34 -14
- package/dist/chunk-YBJVKMTM.js.map +1 -0
- package/dist/{chunk-DDTS7F5O.js → chunk-ZW32BPXV.js} +12 -3
- package/dist/chunk-ZW32BPXV.js.map +1 -0
- package/dist/compat/index.cjs +51 -4
- package/dist/compat/index.cjs.map +1 -1
- package/dist/compat/index.d.cts +1 -1
- package/dist/compat/index.d.ts +1 -1
- package/dist/compat/index.js +6 -6
- package/dist/compat/nestjs/index.cjs +51 -4
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +1 -1
- package/dist/compat/nestjs/index.d.ts +1 -1
- package/dist/compat/nestjs/index.js +3 -3
- package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
- package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
- package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
- package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
- package/dist/index.cjs +2215 -1676
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +169 -146
- package/dist/index.js.map +1 -1
- package/dist/presets/ai/index.cjs +46 -0
- package/dist/presets/ai/index.cjs.map +1 -1
- package/dist/presets/ai/index.js +12 -12
- package/dist/presets/harness/index.cjs +130 -18
- package/dist/presets/harness/index.cjs.map +1 -1
- package/dist/presets/harness/index.d.cts +15 -5
- package/dist/presets/harness/index.d.ts +15 -5
- package/dist/presets/harness/index.js +22 -22
- package/dist/presets/index.cjs +222 -53
- package/dist/presets/index.cjs.map +1 -1
- package/dist/presets/index.d.cts +2 -2
- package/dist/presets/index.d.ts +2 -2
- package/dist/presets/index.js +45 -45
- package/dist/presets/inspect/index.cjs +63 -14
- package/dist/presets/inspect/index.cjs.map +1 -1
- package/dist/presets/inspect/index.d.cts +1 -1
- package/dist/presets/inspect/index.d.ts +1 -1
- package/dist/presets/inspect/index.js +6 -6
- package/dist/presets/resilience/index.cjs +29 -21
- package/dist/presets/resilience/index.cjs.map +1 -1
- package/dist/presets/resilience/index.d.cts +12 -8
- package/dist/presets/resilience/index.d.ts +12 -8
- package/dist/presets/resilience/index.js +3 -3
- package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
- package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
- package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
- package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
- package/dist/solutions/index.cjs +168 -47
- package/dist/solutions/index.cjs.map +1 -1
- package/dist/solutions/index.d.cts +2 -2
- package/dist/solutions/index.d.ts +2 -2
- package/dist/solutions/index.js +28 -28
- package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
- package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
- package/dist/testing/index.cjs +94 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +59 -0
- package/dist/testing/index.d.ts +59 -0
- package/dist/testing/index.js +73 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/utils/ai/browser.cjs.map +1 -1
- package/dist/utils/ai/browser.d.cts +2 -2
- package/dist/utils/ai/browser.d.ts +2 -2
- package/dist/utils/ai/browser.js +6 -6
- package/dist/utils/ai/browser.js.map +1 -1
- package/dist/utils/ai/index.cjs +250 -166
- package/dist/utils/ai/index.cjs.map +1 -1
- package/dist/utils/ai/index.d.cts +108 -12
- package/dist/utils/ai/index.d.ts +108 -12
- package/dist/utils/ai/index.js +21 -19
- package/dist/utils/ai/node.cjs.map +1 -1
- package/dist/utils/ai/node.d.cts +5 -5
- package/dist/utils/ai/node.d.ts +5 -5
- package/dist/utils/ai/node.js +2 -2
- package/dist/utils/ai/node.js.map +1 -1
- package/dist/utils/cqrs/index.cjs +29 -3
- package/dist/utils/cqrs/index.cjs.map +1 -1
- package/dist/utils/cqrs/index.d.cts +12 -7
- package/dist/utils/cqrs/index.d.ts +12 -7
- package/dist/utils/cqrs/index.js +2 -2
- package/dist/utils/demo-shell/index.cjs +45 -19
- package/dist/utils/demo-shell/index.cjs.map +1 -1
- package/dist/utils/demo-shell/index.d.cts +1 -1
- package/dist/utils/demo-shell/index.d.ts +1 -1
- package/dist/utils/demo-shell/index.js +2 -2
- package/dist/utils/domain-templates/index.cjs.map +1 -1
- package/dist/utils/domain-templates/index.js +3 -3
- package/dist/utils/graphspec/index.cjs.map +1 -1
- package/dist/utils/graphspec/index.js +3 -3
- package/dist/utils/index.cjs +1642 -1225
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.cts +7 -7
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/index.js +72 -54
- package/dist/utils/inspect/index.cjs +52 -4
- package/dist/utils/inspect/index.cjs.map +1 -1
- package/dist/utils/inspect/index.d.cts +32 -3
- package/dist/utils/inspect/index.d.ts +32 -3
- package/dist/utils/inspect/index.js +4 -4
- package/dist/utils/job-queue/index.cjs +46 -9
- package/dist/utils/job-queue/index.cjs.map +1 -1
- package/dist/utils/job-queue/index.d.cts +33 -3
- package/dist/utils/job-queue/index.d.ts +33 -3
- package/dist/utils/job-queue/index.js +2 -2
- package/dist/utils/memory/index.cjs +556 -462
- package/dist/utils/memory/index.cjs.map +1 -1
- package/dist/utils/memory/index.d.cts +203 -24
- package/dist/utils/memory/index.d.ts +203 -24
- package/dist/utils/memory/index.js +10 -2
- package/dist/utils/messaging/index.cjs.map +1 -1
- package/dist/utils/messaging/index.d.cts +4 -3
- package/dist/utils/messaging/index.d.ts +4 -3
- package/dist/utils/messaging/index.js +2 -2
- package/dist/utils/orchestration/index.cjs +9 -0
- package/dist/utils/orchestration/index.cjs.map +1 -1
- package/dist/utils/orchestration/index.js +3 -3
- package/dist/utils/process/index.cjs +32 -2
- package/dist/utils/process/index.cjs.map +1 -1
- package/dist/utils/process/index.d.cts +4 -3
- package/dist/utils/process/index.d.ts +4 -3
- package/dist/utils/process/index.js +2 -2
- package/dist/utils/reactive-layout/index.cjs +184 -55
- package/dist/utils/reactive-layout/index.cjs.map +1 -1
- package/dist/utils/reactive-layout/index.d.cts +128 -3
- package/dist/utils/reactive-layout/index.d.ts +128 -3
- package/dist/utils/reactive-layout/index.js +16 -8
- package/dist/utils/reduction/index.cjs.map +1 -1
- package/dist/utils/reduction/index.js +2 -2
- package/dist/utils/resilience/index.cjs +29 -20
- package/dist/utils/resilience/index.cjs.map +1 -1
- package/dist/utils/resilience/index.d.cts +1 -1
- package/dist/utils/resilience/index.d.ts +1 -1
- package/dist/utils/resilience/index.js +2 -2
- package/dist/utils/surface/index.cjs.map +1 -1
- package/dist/utils/surface/index.js +4 -4
- package/package.json +15 -3
- package/dist/chunk-3O3NKZJW.js.map +0 -1
- package/dist/chunk-3PSLNJDU.js.map +0 -1
- package/dist/chunk-42FQ27MQ.js.map +0 -1
- package/dist/chunk-4XCHZRUJ.js.map +0 -1
- package/dist/chunk-6ZLCPUXS.js.map +0 -1
- package/dist/chunk-7AVQIGF6.js.map +0 -1
- package/dist/chunk-BXGZFGZ4.js.map +0 -1
- package/dist/chunk-DDTS7F5O.js.map +0 -1
- package/dist/chunk-EL5VHUGK.js.map +0 -1
- package/dist/chunk-FQSQONOU.js.map +0 -1
- package/dist/chunk-IOJDYUA7.js.map +0 -1
- package/dist/chunk-KRFGO5QH.js.map +0 -1
- package/dist/chunk-MS3WPRJR.js.map +0 -1
- package/dist/chunk-NPRP3MCV.js.map +0 -1
- package/dist/chunk-NY2PYHNC.js.map +0 -1
- package/dist/chunk-PKPO3JTZ.js.map +0 -1
- package/dist/chunk-PTWADEH3.js.map +0 -1
- package/dist/chunk-T7SP3EYR.js.map +0 -1
- package/dist/chunk-VNXAF2KE.js.map +0 -1
- package/dist/chunk-W2BOPXTI.js +0 -1
- package/dist/chunk-W2BOPXTI.js.map +0 -1
- package/dist/chunk-WGDEBIP4.js.map +0 -1
- /package/dist/{chunk-R6ZCSXKX.js.map → chunk-23MAWVOJ.js.map} +0 -0
- /package/dist/{chunk-6MRSX3YK.js.map → chunk-B5Y5GPD5.js.map} +0 -0
- /package/dist/{chunk-VP3TIUDF.js.map → chunk-DVTDF5OI.js.map} +0 -0
- /package/dist/{chunk-OXD5LFQP.js.map → chunk-G7H6PN7P.js.map} +0 -0
- /package/dist/{chunk-446I4EGD.js.map → chunk-J5TBZFBD.js.map} +0 -0
- /package/dist/{chunk-QFE5BQH7.js.map → chunk-LTSI7ULC.js.map} +0 -0
- /package/dist/{chunk-KNU73RZW.js.map → chunk-NSA5K5G2.js.map} +0 -0
- /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
- /package/dist/{chunk-VAZXUK6G.js.map → chunk-SUNCHMML.js.map} +0 -0
- /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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
30
|
+
var import_core4;
|
|
31
31
|
var init_internal = __esm({
|
|
32
32
|
"src/base/resilience/_internal.ts"() {
|
|
33
33
|
"use strict";
|
|
34
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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([[
|
|
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,
|
|
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,
|
|
219
|
+
firedAt_ns: (0, import_core5.monotonicNs)(),
|
|
220
220
|
deadline_ns: ns
|
|
221
221
|
});
|
|
222
|
-
a.down([[
|
|
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 ===
|
|
232
|
-
else if (t ===
|
|
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 ===
|
|
236
|
-
else if (t ===
|
|
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,
|
|
241
|
+
settledAt_ns: (0, import_core5.monotonicNs)()
|
|
242
242
|
});
|
|
243
|
-
a.down([[
|
|
243
|
+
a.down([[import_core5.COMPLETE]]);
|
|
244
244
|
return;
|
|
245
|
-
} else if (t ===
|
|
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,
|
|
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 ===
|
|
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] !==
|
|
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
|
-
|
|
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,
|
|
319
|
+
meta: { ...callerMeta ?? {}, ...(0, import_core5.factoryTag)("withTimeout", factoryArgs) }
|
|
320
320
|
}
|
|
321
321
|
);
|
|
322
322
|
return { node: out, timeoutState };
|
|
323
323
|
}
|
|
324
|
-
var
|
|
324
|
+
var import_core5, TimeoutError;
|
|
325
325
|
var init_timeout = __esm({
|
|
326
326
|
"src/base/resilience/timeout.ts"() {
|
|
327
327
|
"use strict";
|
|
328
|
-
|
|
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
|
-
|
|
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
|
|
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] ===
|
|
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] ===
|
|
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] ===
|
|
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] ===
|
|
636
|
-
if (m[0] ===
|
|
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] ===
|
|
682
|
+
if (m[0] === import_core6.ERROR) {
|
|
645
683
|
settleError(m[1]);
|
|
646
684
|
detach();
|
|
647
685
|
return;
|
|
648
686
|
}
|
|
649
|
-
if (m[0] ===
|
|
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] ===
|
|
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] ===
|
|
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] ===
|
|
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,
|
|
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([[
|
|
790
|
+
counter.down([[import_core6.DIRTY], [import_core6.DATA, current + 1]]);
|
|
753
791
|
return true;
|
|
754
792
|
},
|
|
755
793
|
get() {
|