@graphrefly/graphrefly 0.47.1 → 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 +28 -19
- 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 +9 -9
- package/dist/base/index.cjs +294 -164
- 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 +77 -72
- package/dist/base/io/index.cjs +145 -85
- 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 +5 -5
- 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 +18 -12
- 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 +18 -12
- package/dist/base/sources/browser/index.js.map +1 -1
- package/dist/base/sources/event/index.cjs +29 -1
- 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 +5 -2
- package/dist/base/sources/index.cjs +96 -50
- 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 +7 -4
- package/dist/base/sources/node/index.cjs +43 -37
- package/dist/base/sources/node/index.cjs.map +1 -1
- package/dist/base/sources/node/index.js +43 -37
- package/dist/base/sources/node/index.js.map +1 -1
- package/dist/{chunk-J5WFUEO4.js → chunk-23MAWVOJ.js} +3 -3
- package/dist/{chunk-YXCPV26R.js → chunk-3REMCHSS.js} +39 -27
- 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-EVYY4X5A.js → chunk-46X2EFQH.js} +16 -5
- 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-RGMTUZCL.js → chunk-65OM4XLQ.js} +50 -4
- 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-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
- package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
- package/dist/{chunk-VLAGJZSL.js → chunk-7T7WLEPM.js} +25 -4
- 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-2OB3CEJS.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-MTTRCEJT.js → chunk-DVTDF5OI.js} +2 -2
- package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
- package/dist/chunk-F7EKHR32.js.map +1 -0
- package/dist/{chunk-A7KV5UK4.js → chunk-G7H6PN7P.js} +2 -2
- package/dist/{chunk-OCUDSN63.js → chunk-GGKHHG5Y.js} +110 -64
- package/dist/chunk-GGKHHG5Y.js.map +1 -0
- package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
- package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
- package/dist/{chunk-YJ4U2D2C.js → chunk-J5TBZFBD.js} +9 -7
- package/dist/chunk-J5TBZFBD.js.map +1 -0
- package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
- package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
- package/dist/{chunk-U225SKB4.js → chunk-K4ZYJ4EM.js} +569 -424
- package/dist/chunk-K4ZYJ4EM.js.map +1 -0
- package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
- package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
- package/dist/{chunk-IHTWQEDR.js → chunk-LTSI7ULC.js} +3 -3
- package/dist/{chunk-IHTWQEDR.js.map → chunk-LTSI7ULC.js.map} +1 -1
- package/dist/{chunk-DKNHAICT.js → chunk-MMHGYX44.js} +25 -9
- package/dist/chunk-MMHGYX44.js.map +1 -0
- package/dist/{chunk-K7PDZYQE.js → chunk-MQMTRKY3.js} +129 -50
- 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-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
- 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-DM4OMPWK.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-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
- package/dist/chunk-S7HN5FHL.js.map +1 -0
- package/dist/{chunk-4S53H2KR.js → chunk-SUNCHMML.js} +2 -2
- package/dist/{chunk-4GYMCUDZ.js → chunk-T2U6N3FV.js} +7 -7
- package/dist/{chunk-RJOG4IJU.js → chunk-T5URUIIY.js} +50 -35
- package/dist/chunk-T5URUIIY.js.map +1 -0
- package/dist/{chunk-B4AKFXGE.js → chunk-TPTZZV25.js} +6 -6
- package/dist/chunk-TPTZZV25.js.map +1 -0
- package/dist/{chunk-BU3SEFA5.js → chunk-V46JWFGV.js} +7 -6
- package/dist/chunk-V46JWFGV.js.map +1 -0
- package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
- package/dist/chunk-VLDRAMP7.js.map +1 -0
- package/dist/{chunk-6XZYT4SW.js → chunk-X6ESZDR6.js} +8 -9
- package/dist/chunk-X6ESZDR6.js.map +1 -0
- package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
- package/dist/chunk-X7BA5PWG.js.map +1 -0
- package/dist/{chunk-CXANAIZU.js → chunk-XEWV254I.js} +3 -3
- package/dist/{chunk-CXANAIZU.js.map → chunk-XEWV254I.js.map} +1 -1
- package/dist/{chunk-V4Y3TM7U.js → chunk-YBJVKMTM.js} +38 -16
- package/dist/chunk-YBJVKMTM.js.map +1 -0
- package/dist/{chunk-7ADWWI2T.js → chunk-ZW32BPXV.js} +17 -6
- package/dist/chunk-ZW32BPXV.js.map +1 -0
- package/dist/compat/index.cjs +52 -5
- 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 +7 -7
- package/dist/compat/nestjs/index.cjs +52 -5
- 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 +4 -4
- 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 +2387 -1707
- 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 +173 -150
- package/dist/index.js.map +1 -1
- package/dist/presets/ai/index.cjs +88 -26
- package/dist/presets/ai/index.cjs.map +1 -1
- package/dist/presets/ai/index.js +14 -14
- package/dist/presets/harness/index.cjs +183 -51
- 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 +26 -26
- package/dist/presets/index.cjs +298 -101
- 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 +49 -49
- 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 +64 -44
- 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 +6 -6
- 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 +239 -92
- 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 +32 -32
- 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/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
- 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 +10 -10
- package/dist/utils/ai/browser.js.map +1 -1
- package/dist/utils/ai/index.cjs +291 -191
- 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 +23 -21
- 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 +3 -3
- 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 +1 -1
- 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 +1 -1
- package/dist/utils/graphspec/index.cjs.map +1 -1
- package/dist/utils/graphspec/index.js +3 -3
- package/dist/utils/harness/index.cjs +16 -10
- package/dist/utils/harness/index.cjs.map +1 -1
- package/dist/utils/harness/index.js +1 -1
- package/dist/utils/index.cjs +1692 -1192
- 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 +77 -59
- 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 +570 -425
- package/dist/utils/memory/index.cjs.map +1 -1
- package/dist/utils/memory/index.d.cts +261 -33
- package/dist/utils/memory/index.d.ts +261 -33
- 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 +14 -3
- 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 +3 -3
- 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 +1 -1
- package/dist/utils/reduction/index.cjs.map +1 -1
- package/dist/utils/reduction/index.js +2 -2
- package/dist/utils/resilience/index.cjs +64 -43
- 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 +5 -5
- package/dist/utils/surface/index.cjs +1 -1
- 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-3PSLNJDU.js.map +0 -1
- package/dist/chunk-42FQ27MQ.js.map +0 -1
- package/dist/chunk-4XCHZRUJ.js.map +0 -1
- package/dist/chunk-6XZYT4SW.js.map +0 -1
- package/dist/chunk-7ADWWI2T.js.map +0 -1
- package/dist/chunk-B4AKFXGE.js.map +0 -1
- package/dist/chunk-BU3SEFA5.js.map +0 -1
- package/dist/chunk-BXGZFGZ4.js.map +0 -1
- package/dist/chunk-DKNHAICT.js.map +0 -1
- package/dist/chunk-E5OZPDIW.js.map +0 -1
- package/dist/chunk-EVYY4X5A.js.map +0 -1
- package/dist/chunk-IJRR6YAI.js.map +0 -1
- package/dist/chunk-K7PDZYQE.js.map +0 -1
- package/dist/chunk-NPRP3MCV.js.map +0 -1
- package/dist/chunk-NY2PYHNC.js.map +0 -1
- package/dist/chunk-OCUDSN63.js.map +0 -1
- package/dist/chunk-PKPO3JTZ.js.map +0 -1
- package/dist/chunk-PZWISPIQ.js.map +0 -1
- package/dist/chunk-RGMTUZCL.js.map +0 -1
- package/dist/chunk-RJOG4IJU.js.map +0 -1
- package/dist/chunk-SOOKUYVM.js.map +0 -1
- package/dist/chunk-U225SKB4.js.map +0 -1
- package/dist/chunk-V4Y3TM7U.js.map +0 -1
- package/dist/chunk-VLAGJZSL.js.map +0 -1
- package/dist/chunk-W2BOPXTI.js +0 -1
- package/dist/chunk-YJ4U2D2C.js.map +0 -1
- package/dist/chunk-YXCPV26R.js.map +0 -1
- package/dist/timeout-U5O4ESK3.js.map +0 -1
- /package/dist/{chunk-J5WFUEO4.js.map → chunk-23MAWVOJ.js.map} +0 -0
- /package/dist/{chunk-2OB3CEJS.js.map → chunk-B5Y5GPD5.js.map} +0 -0
- /package/dist/{chunk-MTTRCEJT.js.map → chunk-DVTDF5OI.js.map} +0 -0
- /package/dist/{chunk-A7KV5UK4.js.map → chunk-G7H6PN7P.js.map} +0 -0
- /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
- /package/dist/{chunk-DM4OMPWK.js.map → chunk-NSA5K5G2.js.map} +0 -0
- /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
- /package/dist/{chunk-4S53H2KR.js.map → chunk-SUNCHMML.js.map} +0 -0
- /package/dist/{chunk-4GYMCUDZ.js.map → chunk-T2U6N3FV.js.map} +0 -0
- /package/dist/{chunk-W2BOPXTI.js.map → timeout-BEABACRP.js.map} +0 -0
|
@@ -6,7 +6,7 @@ function sourceOpts(opts) {
|
|
|
6
6
|
return { describeKind: "producer", ...opts };
|
|
7
7
|
}
|
|
8
8
|
function fromRaf(opts) {
|
|
9
|
-
const { signal, ...rest } = opts ?? {};
|
|
9
|
+
const { signal, pauseWhenHidden = false, ...rest } = opts ?? {};
|
|
10
10
|
return node((_data, a) => {
|
|
11
11
|
let done = false;
|
|
12
12
|
let rafId;
|
|
@@ -46,7 +46,9 @@ function fromRaf(opts) {
|
|
|
46
46
|
};
|
|
47
47
|
const scheduleNext = () => {
|
|
48
48
|
if (done) return;
|
|
49
|
-
|
|
49
|
+
const hidden = doc !== void 0 && doc.visibilityState === "hidden";
|
|
50
|
+
if (pauseWhenHidden && hidden) return;
|
|
51
|
+
if (raf && !hidden) {
|
|
50
52
|
rafId = raf(tick);
|
|
51
53
|
} else {
|
|
52
54
|
fallbackTimer = setTimeout(() => tick(performance.now()), 16);
|
|
@@ -59,16 +61,16 @@ function fromRaf(opts) {
|
|
|
59
61
|
};
|
|
60
62
|
if (signal?.aborted) {
|
|
61
63
|
onAbort();
|
|
62
|
-
return
|
|
64
|
+
return;
|
|
63
65
|
}
|
|
64
66
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
65
67
|
abortListenerAdded = signal !== void 0;
|
|
66
|
-
if (doc && raf) {
|
|
68
|
+
if (doc && (raf || pauseWhenHidden)) {
|
|
67
69
|
doc.addEventListener("visibilitychange", onVisibilityChange);
|
|
68
70
|
visibilityListenerAdded = true;
|
|
69
71
|
}
|
|
70
72
|
scheduleNext();
|
|
71
|
-
return cleanup;
|
|
73
|
+
return { onDeactivation: cleanup };
|
|
72
74
|
}, sourceOpts(rest));
|
|
73
75
|
}
|
|
74
76
|
function fromEvent(target, type, opts) {
|
|
@@ -79,7 +81,7 @@ function fromEvent(target, type, opts) {
|
|
|
79
81
|
};
|
|
80
82
|
const options = { capture, passive, once };
|
|
81
83
|
target.addEventListener(type, handler, options);
|
|
82
|
-
return () => target.removeEventListener(type, handler, options);
|
|
84
|
+
return { onDeactivation: () => target.removeEventListener(type, handler, options) };
|
|
83
85
|
}, sourceOpts(rest));
|
|
84
86
|
}
|
|
85
87
|
|
|
@@ -104,9 +106,11 @@ function fromIDBRequest(req) {
|
|
|
104
106
|
clear();
|
|
105
107
|
a.down([[ERROR2, req.error ?? new Error("IndexedDB request failed")]]);
|
|
106
108
|
};
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
return {
|
|
110
|
+
onDeactivation: () => {
|
|
111
|
+
done = true;
|
|
112
|
+
clear();
|
|
113
|
+
}
|
|
110
114
|
};
|
|
111
115
|
});
|
|
112
116
|
}
|
|
@@ -136,9 +140,11 @@ function fromIDBTransaction(tx) {
|
|
|
136
140
|
clear();
|
|
137
141
|
a.down([[ERROR2, tx.error ?? new Error("IndexedDB transaction aborted")]]);
|
|
138
142
|
};
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
return {
|
|
144
|
+
onDeactivation: () => {
|
|
145
|
+
done = true;
|
|
146
|
+
clear();
|
|
147
|
+
}
|
|
142
148
|
};
|
|
143
149
|
});
|
|
144
150
|
}
|
|
@@ -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\tonAbort();\n\t\t\treturn cleanup;\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 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 () => 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\tdone = true;\n\t\t\tclear();\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\tdone = true;\n\t\t\tclear();\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":";;;AAMA,SAAS,OAAoC,YAAY;AAKzD,SAAS,WAAwB,MAAkC;AAClE,SAAO,EAAE,cAAc,YAAY,GAAG,KAAK;AAC5C;AAgBO,SAAS,QAAQ,MAAsC;AAC7D,QAAM,EAAE,QAAQ,GAAG,KAAK,IAAI,QAAQ,CAAC;AACrC,SAAO,KAAa,CAAC,OAAO,MAAM;AACjC,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACJ,QAAI,qBAAqB;AACzB,QAAI,0BAA0B;AAE9B,UAAM,MACL,OAAO,0BAA0B,aAAa,wBAAwB;AACvE,UAAM,MACL,OAAO,yBAAyB,aAAa,uBAAuB;AACrE,UAAM,MAA4B,OAAO,aAAa,cAAc,WAAW;AAE/E,UAAM,eAAe,MAAM;AAC1B,UAAI,UAAU,UAAa,IAAK,KAAI,KAAK;AACzC,UAAI,kBAAkB,OAAW,cAAa,aAAa;AAC3D,cAAQ;AACR,sBAAgB;AAAA,IACjB;AACA,UAAM,UAAU,MAAM;AACrB,aAAO;AACP,mBAAa;AACb,UAAI,oBAAoB;AACvB,gBAAQ,oBAAoB,SAAS,OAAO;AAC5C,6BAAqB;AAAA,MACtB;AACA,UAAI,2BAA2B,KAAK;AACnC,YAAI,oBAAoB,oBAAoB,kBAAkB;AAC9D,kCAA0B;AAAA,MAC3B;AAAA,IACD;AACA,UAAM,UAAU,MAAM;AACrB,UAAI,KAAM;AACV,cAAQ;AACR,QAAE,KAAK,CAAC,CAAC,OAAO,OAAQ,MAAM,CAAC,CAAC;AAAA,IACjC;AACA,UAAM,OAAO,CAAC,QAAgB;AAC7B,UAAI,KAAM;AACV,QAAE,KAAK,GAAG;AACV,mBAAa;AAAA,IACd;AACA,UAAM,eAAe,MAAM;AAC1B,UAAI,KAAM;AAIV,UAAI,QAAQ,CAAC,OAAO,IAAI,oBAAoB,WAAW;AACtD,gBAAQ,IAAI,IAAI;AAAA,MACjB,OAAO;AACN,wBAAgB,WAAW,MAAM,KAAK,YAAY,IAAI,CAAC,GAAG,EAAE;AAAA,MAC7D;AAAA,IACD;AACA,UAAM,qBAAqB,MAAM;AAChC,UAAI,KAAM;AAGV,mBAAa;AACb,mBAAa;AAAA,IACd;AAEA,QAAI,QAAQ,SAAS;AACpB,cAAQ;AACR,aAAO;AAAA,IACR;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,yBAAqB,WAAW;AAChC,QAAI,OAAO,KAAK;AACf,UAAI,iBAAiB,oBAAoB,kBAAkB;AAC3D,gCAA0B;AAAA,IAC3B;AACA,iBAAa;AACb,WAAO;AAAA,EACR,GAAG,WAAW,IAAI,CAAC;AACpB;AAmBO,SAAS,UACf,QACA,MACA,MACU;AACV,QAAM,EAAE,SAAS,SAAS,MAAM,GAAG,KAAK,IAAI,QAAQ,CAAC;AACrD,SAAO,KAAQ,CAAC,OAAO,MAAM;AAC5B,UAAM,UAAU,CAAC,MAAe;AAC/B,QAAE,KAAK,CAAM;AAAA,IACd;AACA,UAAM,UAAU,EAAE,SAAS,SAAS,KAAK;AACzC,WAAO,iBAAiB,MAAM,SAAS,OAAO;AAC9C,WAAO,MAAM,OAAO,oBAAoB,MAAM,SAAS,OAAO;AAAA,EAC/D,GAAG,WAAW,IAAI,CAAC;AACpB;;;AC3HA,SAAS,UAAU,MAAM,SAAAA,QAAkB,QAAAC,aAAY;AAahD,SAAS,eAAkB,KAA6B;AAC9D,SAAOA,MAAQ,CAAC,OAAO,MAAM;AAC5B,QAAI,OAAO;AACX,UAAM,QAAQ,MAAM;AACnB,UAAI,YAAY;AAChB,UAAI,UAAU;AAAA,IACf;AACA,QAAI,YAAY,MAAM;AACrB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AAAA,IACxC;AACA,QAAI,UAAU,MAAM;AACnB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACD,QAAO,IAAI,SAAS,IAAI,MAAM,0BAA0B,CAAC,CAAC,CAAC;AAAA,IACrE;AACA,WAAO,MAAM;AACZ,aAAO;AACP,YAAM;AAAA,IACP;AAAA,EACD,CAAC;AACF;AAWO,SAAS,mBAAmB,IAAgC;AAClE,SAAOC,MAAW,CAAC,OAAO,MAAM;AAC/B,QAAI,OAAO;AACX,UAAM,QAAQ,MAAM;AACnB,SAAG,aAAa;AAChB,SAAG,UAAU;AACb,SAAG,UAAU;AAAA,IACd;AACA,OAAG,aAAa,MAAM;AACrB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAAC,MAAM,MAAS,GAAG,CAAC,QAAQ,CAAC,CAAC;AAAA,IACvC;AACA,OAAG,UAAU,MAAM;AAClB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACD,QAAO,GAAG,SAAS,IAAI,MAAM,8BAA8B,CAAC,CAAC,CAAC;AAAA,IACxE;AACA,OAAG,UAAU,MAAM;AAClB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACA,QAAO,GAAG,SAAS,IAAI,MAAM,+BAA+B,CAAC,CAAC,CAAC;AAAA,IACzE;AACA,WAAO,MAAM;AACZ,aAAO;AACP,YAAM;AAAA,IACP;AAAA,EACD,CAAC;AACF;","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":";;;AAMA,SAAS,OAAoC,YAAY;AAKzD,SAAS,WAAwB,MAAkC;AAClE,SAAO,EAAE,cAAc,YAAY,GAAG,KAAK;AAC5C;AAmCO,SAAS,QAAQ,MAAqC;AAC5D,QAAM,EAAE,QAAQ,kBAAkB,OAAO,GAAG,KAAK,IAAI,QAAQ,CAAC;AAC9D,SAAO,KAAa,CAAC,OAAO,MAAM;AACjC,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACJ,QAAI,qBAAqB;AACzB,QAAI,0BAA0B;AAE9B,UAAM,MACL,OAAO,0BAA0B,aAAa,wBAAwB;AACvE,UAAM,MACL,OAAO,yBAAyB,aAAa,uBAAuB;AACrE,UAAM,MAA4B,OAAO,aAAa,cAAc,WAAW;AAE/E,UAAM,eAAe,MAAM;AAC1B,UAAI,UAAU,UAAa,IAAK,KAAI,KAAK;AACzC,UAAI,kBAAkB,OAAW,cAAa,aAAa;AAC3D,cAAQ;AACR,sBAAgB;AAAA,IACjB;AACA,UAAM,UAAU,MAAM;AACrB,aAAO;AACP,mBAAa;AACb,UAAI,oBAAoB;AACvB,gBAAQ,oBAAoB,SAAS,OAAO;AAC5C,6BAAqB;AAAA,MACtB;AACA,UAAI,2BAA2B,KAAK;AACnC,YAAI,oBAAoB,oBAAoB,kBAAkB;AAC9D,kCAA0B;AAAA,MAC3B;AAAA,IACD;AACA,UAAM,UAAU,MAAM;AACrB,UAAI,KAAM;AACV,cAAQ;AACR,QAAE,KAAK,CAAC,CAAC,OAAO,OAAQ,MAAM,CAAC,CAAC;AAAA,IACjC;AACA,UAAM,OAAO,CAAC,QAAgB;AAC7B,UAAI,KAAM;AACV,QAAE,KAAK,GAAG;AACV,mBAAa;AAAA,IACd;AACA,UAAM,eAAe,MAAM;AAC1B,UAAI,KAAM;AACV,YAAM,SAAS,QAAQ,UAAa,IAAI,oBAAoB;AAK5D,UAAI,mBAAmB,OAAQ;AAI/B,UAAI,OAAO,CAAC,QAAQ;AACnB,gBAAQ,IAAI,IAAI;AAAA,MACjB,OAAO;AACN,wBAAgB,WAAW,MAAM,KAAK,YAAY,IAAI,CAAC,GAAG,EAAE;AAAA,MAC7D;AAAA,IACD;AACA,UAAM,qBAAqB,MAAM;AAChC,UAAI,KAAM;AAGV,mBAAa;AACb,mBAAa;AAAA,IACd;AAEA,QAAI,QAAQ,SAAS;AAKpB,cAAQ;AACR;AAAA,IACD;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,yBAAqB,WAAW;AAChC,QAAI,QAAQ,OAAO,kBAAkB;AACpC,UAAI,iBAAiB,oBAAoB,kBAAkB;AAC3D,gCAA0B;AAAA,IAC3B;AACA,iBAAa;AACb,WAAO,EAAE,gBAAgB,QAAQ;AAAA,EAClC,GAAG,WAAW,IAAI,CAAC;AACpB;AAmBO,SAAS,UACf,QACA,MACA,MACU;AACV,QAAM,EAAE,SAAS,SAAS,MAAM,GAAG,KAAK,IAAI,QAAQ,CAAC;AACrD,SAAO,KAAQ,CAAC,OAAO,MAAM;AAC5B,UAAM,UAAU,CAAC,MAAe;AAC/B,QAAE,KAAK,CAAM;AAAA,IACd;AACA,UAAM,UAAU,EAAE,SAAS,SAAS,KAAK;AACzC,WAAO,iBAAiB,MAAM,SAAS,OAAO;AAC9C,WAAO,EAAE,gBAAgB,MAAM,OAAO,oBAAoB,MAAM,SAAS,OAAO,EAAE;AAAA,EACnF,GAAG,WAAW,IAAI,CAAC;AACpB;;;ACxJA,SAAS,UAAU,MAAM,SAAAA,QAAkB,QAAAC,aAAY;AAahD,SAAS,eAAkB,KAA6B;AAC9D,SAAOA,MAAQ,CAAC,OAAO,MAAM;AAC5B,QAAI,OAAO;AACX,UAAM,QAAQ,MAAM;AACnB,UAAI,YAAY;AAChB,UAAI,UAAU;AAAA,IACf;AACA,QAAI,YAAY,MAAM;AACrB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AAAA,IACxC;AACA,QAAI,UAAU,MAAM;AACnB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACD,QAAO,IAAI,SAAS,IAAI,MAAM,0BAA0B,CAAC,CAAC,CAAC;AAAA,IACrE;AACA,WAAO;AAAA,MACN,gBAAgB,MAAM;AACrB,eAAO;AACP,cAAM;AAAA,MACP;AAAA,IACD;AAAA,EACD,CAAC;AACF;AAWO,SAAS,mBAAmB,IAAgC;AAClE,SAAOC,MAAW,CAAC,OAAO,MAAM;AAC/B,QAAI,OAAO;AACX,UAAM,QAAQ,MAAM;AACnB,SAAG,aAAa;AAChB,SAAG,UAAU;AACb,SAAG,UAAU;AAAA,IACd;AACA,OAAG,aAAa,MAAM;AACrB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAAC,MAAM,MAAS,GAAG,CAAC,QAAQ,CAAC,CAAC;AAAA,IACvC;AACA,OAAG,UAAU,MAAM;AAClB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACD,QAAO,GAAG,SAAS,IAAI,MAAM,8BAA8B,CAAC,CAAC,CAAC;AAAA,IACxE;AACA,OAAG,UAAU,MAAM;AAClB,UAAI,KAAM;AACV,aAAO;AACP,YAAM;AACN,QAAE,KAAK,CAAC,CAACA,QAAO,GAAG,SAAS,IAAI,MAAM,+BAA+B,CAAC,CAAC,CAAC;AAAA,IACzE;AACA,WAAO;AAAA,MACN,gBAAgB,MAAM;AACrB,eAAO;AACP,cAAM;AAAA,MACP;AAAA,IACD;AAAA,EACD,CAAC;AACF;","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
|
});
|
|
@@ -90,9 +91,36 @@ function fromCron(expr, opts) {
|
|
|
90
91
|
};
|
|
91
92
|
check();
|
|
92
93
|
const id = setInterval(check, tickMs);
|
|
93
|
-
return () => clearInterval(id);
|
|
94
|
+
return { onDeactivation: () => clearInterval(id) };
|
|
94
95
|
},
|
|
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 () => 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,12 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
fromPushNotification
|
|
3
|
+
} from "../../../chunk-QSW4DFKE.js";
|
|
2
4
|
import {
|
|
3
5
|
fromCron,
|
|
4
6
|
matchesCron,
|
|
5
7
|
parseCron
|
|
6
|
-
} from "../../../chunk-
|
|
8
|
+
} from "../../../chunk-7BULJTL6.js";
|
|
7
9
|
import "../../../chunk-AZDQPQ3V.js";
|
|
8
10
|
export {
|
|
9
11
|
fromCron,
|
|
12
|
+
fromPushNotification,
|
|
10
13
|
matchesCron,
|
|
11
14
|
parseCron
|
|
12
15
|
};
|