@demlik/tea 0.13.0 → 0.15.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/README.md +11 -9
- package/dist/agent/index.d.ts +638 -45
- package/dist/agent/index.js +9 -19
- package/dist/{chunk-RH5EFRVJ.js → chunk-22POWGFA.js} +31 -5
- package/dist/chunk-22POWGFA.js.map +1 -0
- package/dist/{chunk-DMYGQB4O.js → chunk-3B5X7SBB.js} +23 -6
- package/dist/chunk-3B5X7SBB.js.map +1 -0
- package/dist/chunk-66PY5WYF.js +1816 -0
- package/dist/chunk-66PY5WYF.js.map +1 -0
- package/dist/{chunk-25LBDCBC.js → chunk-6IQPODUF.js} +2 -2
- package/dist/{chunk-25LBDCBC.js.map → chunk-6IQPODUF.js.map} +1 -1
- package/dist/chunk-AYS5N6WY.js +505 -0
- package/dist/chunk-AYS5N6WY.js.map +1 -0
- package/dist/{chunk-EWJP3YYP.js → chunk-HWVW4AXC.js} +3 -3
- package/dist/chunk-HWVW4AXC.js.map +1 -0
- package/dist/{chunk-O4MVQTEJ.js → chunk-L3TND7LA.js} +20 -16
- package/dist/chunk-L3TND7LA.js.map +1 -0
- package/dist/{chunk-J6527VRQ.js → chunk-LFZUK6J2.js} +3 -3
- package/dist/chunk-LFZUK6J2.js.map +1 -0
- package/dist/{chunk-D736QUGF.js → chunk-MYXFNWAJ.js} +4 -5
- package/dist/chunk-MYXFNWAJ.js.map +1 -0
- package/dist/{chunk-K575PT4S.js → chunk-PR6TB3BL.js} +15 -4
- package/dist/chunk-PR6TB3BL.js.map +1 -0
- package/dist/{chunk-YUXP55T2.js → chunk-Q5CQTBFA.js} +57 -7
- package/dist/chunk-Q5CQTBFA.js.map +1 -0
- package/dist/{batch-window/index.js → chunk-QPEVCJ5Q.js} +4 -8
- package/dist/chunk-QPEVCJ5Q.js.map +1 -0
- package/dist/chunk-TCLBCERH.js +1480 -0
- package/dist/chunk-TCLBCERH.js.map +1 -0
- package/dist/{chunk-FV3Z5MBE.js → chunk-TXN2KFHF.js} +14 -4
- package/dist/chunk-TXN2KFHF.js.map +1 -0
- package/dist/{chunk-MUUROGIH.js → chunk-V46HSVCK.js} +62 -12
- package/dist/chunk-V46HSVCK.js.map +1 -0
- package/dist/{chunk-Q2UM7BXU.js → chunk-VBDU4JL6.js} +2 -2
- package/dist/chunk-VBDU4JL6.js.map +1 -0
- package/dist/{chunk-ZVJZ72HY.js → chunk-VTQI2NHP.js} +25 -11
- package/dist/chunk-VTQI2NHP.js.map +1 -0
- package/dist/{core-Co0LWkIA.d.ts → core-DnzqWy6u.d.ts} +72 -23
- package/dist/devtools/index.d.ts +2 -2
- package/dist/devtools/index.js +207 -2
- package/dist/devtools/index.js.map +1 -1
- package/dist/do/index.d.ts +43 -29
- package/dist/do/index.js +36 -25
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-C6uwzjb5.d.ts → durable-effects-CWzA-o2B.d.ts} +2 -2
- package/dist/extension/index.d.ts +300 -3
- package/dist/extension/index.js +596 -5
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +1284 -0
- package/dist/flow/index.js +732 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/idempotency/index.d.ts +90 -116
- package/dist/idempotency/index.js +4 -1
- package/dist/index-Bc40JWVl.d.ts +131 -0
- package/dist/{batch-window/index.d.ts → index-C2DwkjlP.d.ts} +7 -12
- package/dist/index-CA_7I5g9.d.ts +806 -0
- package/dist/index-CSyMYDAB.d.ts +69 -0
- package/dist/{fan-out/index.d.ts → index-CuNO9m8i.d.ts} +7 -9
- package/dist/{debounce/index.d.ts → index-DHSUmQTR.d.ts} +7 -7
- package/dist/{recorder/index.d.ts → index-DrIRb_Ch.d.ts} +4 -7
- package/dist/index-HSYgG2Yt.d.ts +380 -0
- package/dist/{monitored-run/index.d.ts → index-KXqTRJyb.d.ts} +37 -13
- package/dist/{snapshot/index.d.ts → index-ZCMhnVKN.d.ts} +15 -78
- package/dist/index.d.ts +771 -6
- package/dist/index.js +1 -2
- package/dist/jev/index.d.ts +646 -0
- package/dist/jev/index.js +485 -0
- package/dist/jev/index.js.map +1 -0
- package/dist/machine-DGlPWCWI.d.ts +1781 -0
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +12 -5
- package/dist/machine-viz/index.js.map +1 -1
- package/dist/mem/index.d.ts +18 -3
- package/dist/mem/index.js +22 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +104 -6
- package/dist/node/index.js +132 -9
- package/dist/node/index.js.map +1 -1
- package/dist/{paginator → paginate}/index.d.ts +140 -6
- package/dist/paginate/index.js +7 -0
- package/dist/parity/index.d.ts +4 -10
- package/dist/parity/index.js +4 -7
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +247 -5
- package/dist/pbt/index.js +124 -5
- package/dist/pbt/index.js.map +1 -1
- package/dist/{trace-replay → persistence}/index.d.ts +7 -5
- package/dist/persistence/index.js +6 -0
- package/dist/{protocol-B5YmJr0d.d.ts → protocol-D8VbH-VH.d.ts} +10 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -2
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +744 -0
- package/dist/resilience/index.js +657 -0
- package/dist/resilience/index.js.map +1 -0
- package/dist/retry-backoff/index.d.ts +3 -2
- package/dist/retry-backoff/index.js +1 -1
- package/dist/runtime-types-CMF13BBh.d.ts +998 -0
- package/dist/testing/index.d.ts +147 -3
- package/dist/testing/index.js +95 -3
- package/dist/testing/index.js.map +1 -1
- package/dist/{throttled-input → timing}/index.d.ts +143 -12
- package/dist/{throttled-input → timing}/index.js +57 -12
- package/dist/timing/index.js.map +1 -0
- package/dist/{types-XS_miRnB.d.ts → types-BseNPIhr.d.ts} +1 -1
- package/dist/work-queue/index.d.ts +130 -54
- package/dist/work-queue/index.js +3 -2
- package/dist/work-queue/index.js.map +1 -1
- package/package.json +45 -218
- package/dist/authed-call/index.d.ts +0 -120
- package/dist/authed-call/index.js +0 -134
- package/dist/authed-call/index.js.map +0 -1
- package/dist/await-terminal/index.d.ts +0 -101
- package/dist/await-terminal/index.js +0 -76
- package/dist/await-terminal/index.js.map +0 -1
- package/dist/batch-window/index.js.map +0 -1
- package/dist/cache/index.d.ts +0 -152
- package/dist/cache/index.js +0 -8
- package/dist/chart/index.d.ts +0 -218
- package/dist/chart/index.js +0 -16
- package/dist/chart/index.js.map +0 -1
- package/dist/chart/inspect/index.d.ts +0 -86
- package/dist/chart/inspect/index.js +0 -5
- package/dist/chart/inspect/react.d.ts +0 -113
- package/dist/chart/inspect/react.js +0 -11
- package/dist/chart/inspect/react.js.map +0 -1
- package/dist/chart/inspect/styles.css +0 -286
- package/dist/chart/lane/index.d.ts +0 -362
- package/dist/chart/lane/index.js +0 -9
- package/dist/chart/lane/index.js.map +0 -1
- package/dist/chart/lane/react.d.ts +0 -84
- package/dist/chart/lane/react.js +0 -559
- package/dist/chart/lane/react.js.map +0 -1
- package/dist/chart/lane/server.d.ts +0 -148
- package/dist/chart/lane/server.js +0 -209
- package/dist/chart/lane/server.js.map +0 -1
- package/dist/chart/lane/styles.css +0 -498
- package/dist/chart/lane/viewer/assets/abnfDiagram-N423BO3Z-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/architectureDiagram-T3A2C74G-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/blockDiagram-VBNYF7ZC-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/c4Diagram-5PPSVZJV-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-2GRJ4B5K-HdDf8SI3.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-5RXB4S5H-CcNtpUd7.js +0 -231
- package/dist/chart/lane/viewer/assets/chunk-5VM5RSS4-BP4fRS_5.js +0 -15
- package/dist/chart/lane/viewer/assets/chunk-KBJHAD2P-DZOw7xU6.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-NSK5VX7P-CBCyTZMs.js +0 -128
- package/dist/chart/lane/viewer/assets/classDiagram-JCYQIIEL-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/classDiagram-v2-OCEON4UE-D4uAiFr8.js +0 -206
- package/dist/chart/lane/viewer/assets/cose-bilkent-JH36ORCC-DOwCrGXH.js +0 -1
- package/dist/chart/lane/viewer/assets/cynefinDiagram-MW4NZA55-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/dagre-K_PewWfS.js +0 -1
- package/dist/chart/lane/viewer/assets/dagre-VZM6K2ZE-831qZcA8.js +0 -4
- package/dist/chart/lane/viewer/assets/diagram-7IWD3JNH-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-B4RE2ZJO-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-LBJQPF4R-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-Q27KOJAE-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-UB23O5K3-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ebnfDiagram-BXEA7PRR-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/erDiagram-JOGREHBK-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/flowDiagram-UKHOOZJN-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ganttDiagram-PKOTCBZU-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/gitGraphDiagram-DS77QQ5N-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/index-9SkH5NA3.css +0 -1
- package/dist/chart/lane/viewer/assets/index-Bgt4sXpW.js +0 -195
- package/dist/chart/lane/viewer/assets/infoDiagram-6WML65LV-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ishikawaDiagram-WSZJBQD7-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/journeyDiagram-NVQOT4AX-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/kanban-definition-27J2QSJJ-G7PaPzy-.js +0 -89
- package/dist/chart/lane/viewer/assets/katex-DOwCrGXH.js +0 -1
- package/dist/chart/lane/viewer/assets/mindmap-definition-FAOFIHXS-CGj9nrVk.js +0 -96
- package/dist/chart/lane/viewer/assets/path-fybaL0A-.js +0 -1
- package/dist/chart/lane/viewer/assets/pegDiagram-VL7TDLO6-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/pieDiagram-7S7Q4E2Y-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/quadrantDiagram-CIZ2JOQS-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/railroadDiagram-AXF67PYL-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/requirementDiagram-LRYGKXZP-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/sankeyDiagram-W5VNT64P-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/sequenceDiagram-SI44F4Z6-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/stateDiagram-OKZ733FA-B_88AzBS.js +0 -1
- package/dist/chart/lane/viewer/assets/stateDiagram-v2-UEYNNEHI-CR5gcgq6.js +0 -1
- package/dist/chart/lane/viewer/assets/swimlanes-SLNWSIFB-B4xK-rwg.js +0 -1
- package/dist/chart/lane/viewer/assets/swimlanesDiagram-ULZ7WXOC-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/timeline-definition-Z64GVDOM-CCv52MfY.js +0 -120
- package/dist/chart/lane/viewer/assets/vennDiagram-T6HMQDX7-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/wardleyDiagram-T6FBY63Y-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/xychartDiagram-ELKLHX3M-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/index.html +0 -15
- package/dist/chart/report/index.d.ts +0 -117
- package/dist/chart/report/index.js +0 -32
- package/dist/chart/report/index.js.map +0 -1
- package/dist/chunk-2YFLOCWX.js +0 -15
- package/dist/chunk-2YFLOCWX.js.map +0 -1
- package/dist/chunk-3I52QZZ5.js +0 -519
- package/dist/chunk-3I52QZZ5.js.map +0 -1
- package/dist/chunk-3ZAFFRRB.js +0 -457
- package/dist/chunk-3ZAFFRRB.js.map +0 -1
- package/dist/chunk-4EQZ76JC.js +0 -33
- package/dist/chunk-4EQZ76JC.js.map +0 -1
- package/dist/chunk-4KLG43GY.js +0 -223
- package/dist/chunk-4KLG43GY.js.map +0 -1
- package/dist/chunk-7FRZ2CT2.js +0 -424
- package/dist/chunk-7FRZ2CT2.js.map +0 -1
- package/dist/chunk-AZZLE7XR.js +0 -62
- package/dist/chunk-AZZLE7XR.js.map +0 -1
- package/dist/chunk-BYQUGDD3.js +0 -16
- package/dist/chunk-BYQUGDD3.js.map +0 -1
- package/dist/chunk-CIQG5L42.js +0 -685
- package/dist/chunk-CIQG5L42.js.map +0 -1
- package/dist/chunk-CLM6ERNO.js +0 -61
- package/dist/chunk-CLM6ERNO.js.map +0 -1
- package/dist/chunk-COS5MSTO.js +0 -88
- package/dist/chunk-COS5MSTO.js.map +0 -1
- package/dist/chunk-CRPZJVD2.js +0 -71
- package/dist/chunk-CRPZJVD2.js.map +0 -1
- package/dist/chunk-D62FDQKE.js +0 -53
- package/dist/chunk-D62FDQKE.js.map +0 -1
- package/dist/chunk-D736QUGF.js.map +0 -1
- package/dist/chunk-DGE4PV4A.js +0 -26
- package/dist/chunk-DGE4PV4A.js.map +0 -1
- package/dist/chunk-DMYGQB4O.js.map +0 -1
- package/dist/chunk-EWJP3YYP.js.map +0 -1
- package/dist/chunk-FV3Z5MBE.js.map +0 -1
- package/dist/chunk-HCEVHFJ7.js +0 -54
- package/dist/chunk-HCEVHFJ7.js.map +0 -1
- package/dist/chunk-J6527VRQ.js.map +0 -1
- package/dist/chunk-JELSZN25.js +0 -47
- package/dist/chunk-JELSZN25.js.map +0 -1
- package/dist/chunk-K575PT4S.js.map +0 -1
- package/dist/chunk-L57ZOT5K.js +0 -184
- package/dist/chunk-L57ZOT5K.js.map +0 -1
- package/dist/chunk-MU6MCIAC.js +0 -15
- package/dist/chunk-MU6MCIAC.js.map +0 -1
- package/dist/chunk-MUUROGIH.js.map +0 -1
- package/dist/chunk-NR7BU22N.js +0 -758
- package/dist/chunk-NR7BU22N.js.map +0 -1
- package/dist/chunk-O4MVQTEJ.js.map +0 -1
- package/dist/chunk-OOYD44JI.js +0 -317
- package/dist/chunk-OOYD44JI.js.map +0 -1
- package/dist/chunk-Q2UM7BXU.js.map +0 -1
- package/dist/chunk-RH5EFRVJ.js.map +0 -1
- package/dist/chunk-S2EGRPCM.js +0 -47
- package/dist/chunk-S2EGRPCM.js.map +0 -1
- package/dist/chunk-SINXE47Y.js +0 -55
- package/dist/chunk-SINXE47Y.js.map +0 -1
- package/dist/chunk-UF4MZSLT.js +0 -53
- package/dist/chunk-UF4MZSLT.js.map +0 -1
- package/dist/chunk-UOC2PL46.js +0 -13
- package/dist/chunk-UOC2PL46.js.map +0 -1
- package/dist/chunk-VYFAZN3F.js +0 -372
- package/dist/chunk-VYFAZN3F.js.map +0 -1
- package/dist/chunk-WRAA7GVZ.js +0 -209
- package/dist/chunk-WRAA7GVZ.js.map +0 -1
- package/dist/chunk-XGELA764.js +0 -163
- package/dist/chunk-XGELA764.js.map +0 -1
- package/dist/chunk-YUXP55T2.js.map +0 -1
- package/dist/chunk-YWIKDKHY.js +0 -307
- package/dist/chunk-YWIKDKHY.js.map +0 -1
- package/dist/chunk-ZJ5AOYBH.js +0 -8
- package/dist/chunk-ZJ5AOYBH.js.map +0 -1
- package/dist/chunk-ZSUUOEPL.js +0 -731
- package/dist/chunk-ZSUUOEPL.js.map +0 -1
- package/dist/chunk-ZVJZ72HY.js.map +0 -1
- package/dist/circuit-breaker/index.d.ts +0 -197
- package/dist/circuit-breaker/index.js +0 -3
- package/dist/circuit-breaker/index.js.map +0 -1
- package/dist/deadline/index.d.ts +0 -199
- package/dist/deadline/index.js +0 -6
- package/dist/deadline/index.js.map +0 -1
- package/dist/debounce/index.js +0 -3
- package/dist/debounce/index.js.map +0 -1
- package/dist/extension/react.d.ts +0 -121
- package/dist/extension/react.js +0 -75
- package/dist/extension/react.js.map +0 -1
- package/dist/extension/subs/index.d.ts +0 -45
- package/dist/extension/subs/index.js +0 -80
- package/dist/extension/subs/index.js.map +0 -1
- package/dist/extension/test-utils.d.ts +0 -128
- package/dist/extension/test-utils.js +0 -277
- package/dist/extension/test-utils.js.map +0 -1
- package/dist/fan-out/index.js +0 -5
- package/dist/fan-out/index.js.map +0 -1
- package/dist/fold-BJPdjc9w.d.ts +0 -435
- package/dist/graph-B3L5e0N1.d.ts +0 -923
- package/dist/idempotency/adapter.d.ts +0 -62
- package/dist/idempotency/adapter.js +0 -4
- package/dist/idempotency/adapter.js.map +0 -1
- package/dist/idempotent-intake/index.d.ts +0 -188
- package/dist/idempotent-intake/index.js +0 -7
- package/dist/idempotent-intake/index.js.map +0 -1
- package/dist/journal/index.d.ts +0 -373
- package/dist/journal/index.js +0 -3
- package/dist/journal/index.js.map +0 -1
- package/dist/llm-call/index.d.ts +0 -311
- package/dist/llm-call/index.js +0 -16
- package/dist/llm-call/index.js.map +0 -1
- package/dist/machine-BtFWQmmq.d.ts +0 -804
- package/dist/monitored-run/index.js +0 -10
- package/dist/monitored-run/index.js.map +0 -1
- package/dist/paginated-walk/index.d.ts +0 -235
- package/dist/paginated-walk/index.js +0 -16
- package/dist/paginated-walk/index.js.map +0 -1
- package/dist/paginator/index.js +0 -3
- package/dist/paginator/index.js.map +0 -1
- package/dist/pbt/arbitraries/index.d.ts +0 -136
- package/dist/pbt/arbitraries/index.js +0 -3
- package/dist/pbt/arbitraries/index.js.map +0 -1
- package/dist/pbt/runners/index.d.ts +0 -118
- package/dist/pbt/runners/index.js +0 -5
- package/dist/pbt/runners/index.js.map +0 -1
- package/dist/poller/index.d.ts +0 -352
- package/dist/poller/index.js +0 -115
- package/dist/poller/index.js.map +0 -1
- package/dist/prediction/index.d.ts +0 -131
- package/dist/prediction/index.js +0 -4
- package/dist/prediction/index.js.map +0 -1
- package/dist/pure/index.d.ts +0 -3
- package/dist/pure/index.js +0 -4
- package/dist/pure/index.js.map +0 -1
- package/dist/rate-limit/index.d.ts +0 -115
- package/dist/rate-limit/index.js +0 -3
- package/dist/rate-limit/index.js.map +0 -1
- package/dist/reconciler/index.d.ts +0 -193
- package/dist/reconciler/index.js +0 -164
- package/dist/reconciler/index.js.map +0 -1
- package/dist/recorder/index.js +0 -3
- package/dist/recorder/index.js.map +0 -1
- package/dist/reducer-4GNR6b8B.d.ts +0 -447
- package/dist/resilient-call/index.d.ts +0 -320
- package/dist/resilient-call/index.js +0 -14
- package/dist/resilient-call/index.js.map +0 -1
- package/dist/retry-to-success/index.d.ts +0 -109
- package/dist/retry-to-success/index.js +0 -37
- package/dist/retry-to-success/index.js.map +0 -1
- package/dist/run-pAcmBpah.d.ts +0 -713
- package/dist/runtime-types-CNLmhWif.d.ts +0 -517
- package/dist/saga/index.d.ts +0 -241
- package/dist/saga/index.js +0 -150
- package/dist/saga/index.js.map +0 -1
- package/dist/samples-B3My5oh2.d.ts +0 -59
- package/dist/snapshot/index.js +0 -5
- package/dist/snapshot/index.js.map +0 -1
- package/dist/subs/index.d.ts +0 -472
- package/dist/subs/index.js +0 -7
- package/dist/subs/index.js.map +0 -1
- package/dist/throttle/index.d.ts +0 -134
- package/dist/throttle/index.js +0 -3
- package/dist/throttle/index.js.map +0 -1
- package/dist/throttled-input/index.js.map +0 -1
- package/dist/token-refresh/index.d.ts +0 -216
- package/dist/token-refresh/index.js +0 -6
- package/dist/token-refresh/index.js.map +0 -1
- package/dist/trace-replay/index.js +0 -6
- package/dist/trace-replay/index.js.map +0 -1
- package/dist/with-deadline/index.d.ts +0 -197
- package/dist/with-deadline/index.js +0 -111
- package/dist/with-deadline/index.js.map +0 -1
- package/dist/with-resilience/index.d.ts +0 -225
- package/dist/with-resilience/index.js +0 -240
- package/dist/with-resilience/index.js.map +0 -1
- package/dist/with-telemetry/index.d.ts +0 -150
- package/dist/with-telemetry/index.js +0 -70
- package/dist/with-telemetry/index.js.map +0 -1
- package/dist/work-queue/adapter.d.ts +0 -84
- package/dist/work-queue/adapter.js +0 -4
- package/dist/work-queue/adapter.js.map +0 -1
- package/dist/work-queue/ops.d.ts +0 -71
- package/dist/work-queue/ops.js +0 -3
- package/dist/work-queue/ops.js.map +0 -1
- package/dist/workflow/index.d.ts +0 -485
- package/dist/workflow/index.js +0 -244
- package/dist/workflow/index.js.map +0 -1
- /package/dist/{cache → paginate}/index.js.map +0 -0
- /package/dist/{chart/inspect → persistence}/index.js.map +0 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import { a as CmdOf, b as CmdDef, C as Cmd, I as Interpret, l as CmdValue, g as PortEmitter, i as NoCtx, S as Sub, M as Machine } from '../core-DnzqWy6u.js';
|
|
2
|
+
import { M as MsgType } from '../protocol-D8VbH-VH.js';
|
|
3
|
+
import { e as ResilientConfig, a as ResilientState, R as RunCmd, S as SucceedMsg, F as FailMsg, f as ResilientTimerMsg, c as ResilientPorts } from '../index-CA_7I5g9.js';
|
|
4
|
+
export { g as CacheConfig, h as CallBudget, i as CallPhase, C as CircuitConfig, j as CircuitPolicy, k as CircuitState, l as DEFAULT_RESILIENT_NAME, n as DeadlineExceededError, o as DeadlineNameOf, p as DeadlineSettled, q as DefaultResilientName, M as MountConfig, r as MountableKnob, s as MountedCell, t as MountedResilientCall, b as RateLimitConfig, D as ResilientCallDeadlineConfig, u as ResilientDeadlineType, v as ResilientErrType, d as ResilientHandlers, w as ResilientOkType, x as ResilientRunType, y as RunCmdDef, z as Settle, A as SettleFold, B as SlidingWindow, T as TokenBucket, E as canPass, G as createResilientCall, H as defaultCircuitPolicy, I as initBucket, J as initCircuit, K as initWindow, L as liftResilience, m as mountResilientCall, N as onFailure, O as onSuccess, P as record, Q as refill, U as remaining, V as runCmdDef, W as tryConsume } from '../index-CA_7I5g9.js';
|
|
5
|
+
import { a as DeadlineSub } from '../index-HSYgG2Yt.js';
|
|
6
|
+
export { A as ArmTimer, e as CacheEntry, c as CacheEvictMsg, C as CacheEvictionSub, D as DeadlineExceeded, f as DeadlineMsgType, g as DeadlineOpts, T as TtlCache, h as cacheEvictMsg, i as cacheEvictionSub, j as cacheEvictionSubscribe, d as deadlineExceeded, k as deadlineMsgType, b as deadlineSub, l as evictExpired, m as get, n as has, o as initCache, r as remove, p as set, q as setTimeoutArmTimer, s as subscribeDeadline, t as subscribeWith } from '../index-HSYgG2Yt.js';
|
|
7
|
+
import { Rng, RetryPolicy } from '../retry-backoff/index.js';
|
|
8
|
+
import 'zod';
|
|
9
|
+
import '../types-BseNPIhr.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A minted credential: the opaque `value` to send on the wire, and the absolute
|
|
13
|
+
* `expiresAt` (epoch milliseconds — the `Date.now()` scale) the issuer stamped
|
|
14
|
+
* it with. Both fields are plain data so a `Token` is trivially serializable
|
|
15
|
+
* into a consumer's `Store<S>`.
|
|
16
|
+
*
|
|
17
|
+
* `value` is `string` because that is the universal shape of a bearer token /
|
|
18
|
+
* API key / session cookie on the wire. A consumer whose refresh port yields a
|
|
19
|
+
* richer object can JSON-encode it into `value`, or keep this module's slice as
|
|
20
|
+
* the "is it fresh?" gate and hold the richer object elsewhere in their Model.
|
|
21
|
+
*/
|
|
22
|
+
interface Token {
|
|
23
|
+
/** The opaque credential to present on each request (bearer token, API key). */
|
|
24
|
+
readonly value: string;
|
|
25
|
+
/** Absolute expiry instant, epoch milliseconds. The `Date.now()` scale. */
|
|
26
|
+
readonly expiresAt: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Pure configuration — the knob's only field. Carries no mutable state;
|
|
30
|
+
* `TokenState` does. One config is shared across all the refreshes of one
|
|
31
|
+
* logical credential (often a module-scope const).
|
|
32
|
+
*/
|
|
33
|
+
interface TokenRefreshConfig {
|
|
34
|
+
/**
|
|
35
|
+
* Refresh-ahead skew, in milliseconds. A token is treated as needing a
|
|
36
|
+
* refresh once `at >= expiresAt - skewMs`, i.e. `skewMs` BEFORE its hard
|
|
37
|
+
* expiry. This buys headroom so a call that starts just under the wire never
|
|
38
|
+
* races a token that lapses mid-flight, and absorbs small client/server clock
|
|
39
|
+
* disagreement. `0` means "refresh exactly at expiry"; a negative value is
|
|
40
|
+
* clamped to `0` (a skew that pushes the trigger past expiry is meaningless).
|
|
41
|
+
*
|
|
42
|
+
* @default 0
|
|
43
|
+
*/
|
|
44
|
+
readonly skewMs?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The token slice this knob owns — the Model field a consumer spreads in via
|
|
48
|
+
* `init()`. A two-field record rather than a phase union because the two
|
|
49
|
+
* dimensions are orthogonal: a token can be present-and-fresh, present-but-stale
|
|
50
|
+
* (a 401 came back), absent, or absent-and-stale (cleared after a failed
|
|
51
|
+
* refresh). Every combination is meaningful, so neither field implies the
|
|
52
|
+
* other's value and a discriminated union would only manufacture impossible
|
|
53
|
+
* arms.
|
|
54
|
+
*
|
|
55
|
+
* - `token` — the currently held credential, or `null` when none has been
|
|
56
|
+
* minted yet (fresh boot) or the last refresh failed.
|
|
57
|
+
* - `stale` — set by `on401` when the server rejected a request the client
|
|
58
|
+
* believed valid. Forces the next `needsRefresh` to true regardless of
|
|
59
|
+
* `expiresAt`; cleared by `refreshed` once a new credential lands.
|
|
60
|
+
*
|
|
61
|
+
* Both fields `readonly` so the whole slice threads safely through a pure
|
|
62
|
+
* reducer and serializes into a `Store<S>`.
|
|
63
|
+
*/
|
|
64
|
+
interface TokenState {
|
|
65
|
+
/** The held credential, or `null` if none is available (boot / failed refresh). */
|
|
66
|
+
readonly token: Token | null;
|
|
67
|
+
/** Whether a 401 has marked the held token unusable ahead of its `expiresAt`. */
|
|
68
|
+
readonly stale: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The starting slice: no token held, not stale. The first `needsRefresh` after
|
|
72
|
+
* this returns `true` (no token), so a consumer's first guarded call mints a
|
|
73
|
+
* credential before going out.
|
|
74
|
+
*/
|
|
75
|
+
declare function initTokenRefresh(): TokenState;
|
|
76
|
+
/**
|
|
77
|
+
* The Cmd this knob emits to ask the runtime to mint a fresh token. Plain data
|
|
78
|
+
* (no closure — invariant 1) carrying nothing: the refresh port takes no input,
|
|
79
|
+
* and the slice the result installs is read off the follow-up Msg, not this
|
|
80
|
+
* Cmd. The consumer unions this into their machine's Cmd type and lets
|
|
81
|
+
* `handlers` interpret it.
|
|
82
|
+
*/
|
|
83
|
+
declare const refreshToken: CmdDef<"refresh_token", Record<string, never>, Token, {
|
|
84
|
+
readonly [detail: string]: unknown;
|
|
85
|
+
readonly _tag: "token_refresh_failed";
|
|
86
|
+
}, unknown>;
|
|
87
|
+
type RefreshTokenCmd = CmdOf<typeof refreshToken>;
|
|
88
|
+
/** Construct a `refresh_token` Cmd. */
|
|
89
|
+
declare function refreshTokenCmd(): RefreshTokenCmd;
|
|
90
|
+
/**
|
|
91
|
+
* The Msg the refresh port dispatches on success — carries the freshly minted
|
|
92
|
+
* `Token`. The consumer unions this into their Msg type and handles it by
|
|
93
|
+
* folding `refreshed(state, msg.token)` into the slice.
|
|
94
|
+
*/
|
|
95
|
+
interface TokenRefreshedMsg {
|
|
96
|
+
readonly type: typeof MsgType.TokenRefreshed;
|
|
97
|
+
readonly token: Token;
|
|
98
|
+
}
|
|
99
|
+
/** Construct a `token_refreshed` Msg carrying the new token. */
|
|
100
|
+
declare function tokenRefreshedMsg(token: Token): TokenRefreshedMsg;
|
|
101
|
+
/**
|
|
102
|
+
* The Msg the refresh port dispatches on failure — carries the rejection so the
|
|
103
|
+
* consumer's reducer can decide whether to back off, surface an error, or give
|
|
104
|
+
* up (errors are data: it is dispatched, never swallowed). The slice itself is
|
|
105
|
+
* left untouched by a failed refresh — the held token (if any) stays held and
|
|
106
|
+
* stale stays set, so the consumer can retry; clearing it is the consumer's
|
|
107
|
+
* policy, expressed in their reducer.
|
|
108
|
+
*/
|
|
109
|
+
interface TokenRefreshFailedMsg {
|
|
110
|
+
readonly type: typeof MsgType.TokenRefreshFailed;
|
|
111
|
+
readonly error: unknown;
|
|
112
|
+
}
|
|
113
|
+
/** Construct a `token_refresh_failed` Msg carrying the rejection. */
|
|
114
|
+
declare function tokenRefreshFailedMsg(error: unknown): TokenRefreshFailedMsg;
|
|
115
|
+
/** The two Msgs the refresh port can produce. Union for a consumer's Msg type. */
|
|
116
|
+
type TokenRefreshMsg = TokenRefreshedMsg | TokenRefreshFailedMsg;
|
|
117
|
+
/** The ports this knob's `handlers` splice needs — the single refresh DI seam. */
|
|
118
|
+
interface TokenRefreshPorts {
|
|
119
|
+
/**
|
|
120
|
+
* Mint a fresh credential. Called ONLY from the `refresh_token` interpret
|
|
121
|
+
* handler — never from a verb — so the network I/O and any clock the issuer
|
|
122
|
+
* reads stay at the effect boundary. May reject; the rejection is routed to
|
|
123
|
+
* `token_refresh_failed` (errors are data), never thrown into the runtime.
|
|
124
|
+
*/
|
|
125
|
+
readonly refresh: () => Promise<Token>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The knob: hand it a config, get back the slice initializer, the pure verbs,
|
|
129
|
+
* and the `handlers` splice. Mirrors every L2 composition's uniform contract so
|
|
130
|
+
* the call sites read identically across bricks.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* const tr = createTokenRefresh({ skewMs: 30_000 });
|
|
134
|
+
* const s0 = tr.init();
|
|
135
|
+
* const [s1, cmds] = tr.ensureFresh(s0, Date.now()); // cmds = [refreshTokenCmd()]
|
|
136
|
+
*/
|
|
137
|
+
declare function createTokenRefresh(config?: TokenRefreshConfig): {
|
|
138
|
+
/** The slice initializer — spread into the consumer's `init`. */
|
|
139
|
+
init: typeof initTokenRefresh;
|
|
140
|
+
needsRefresh: (state: TokenState, at: number) => boolean;
|
|
141
|
+
on401: (state: TokenState) => TokenState;
|
|
142
|
+
refreshed: (state: TokenState, token: Token) => TokenState;
|
|
143
|
+
ensureFresh: (state: TokenState, at: number) => readonly [TokenState, readonly Cmd[]];
|
|
144
|
+
handlers: (ports: TokenRefreshPorts) => Interpret<TokenRefreshMsg, RefreshTokenCmd, unknown>;
|
|
145
|
+
};
|
|
146
|
+
/** The shape `createTokenRefresh` returns — useful for typing a held knob. */
|
|
147
|
+
type TokenRefresh = ReturnType<typeof createTokenRefresh>;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The authed-call knob. It is `ResilientConfig` widened by the token dimension:
|
|
151
|
+
*
|
|
152
|
+
* - `refresh` — the ONLY required field: the port that mints a fresh `Token`.
|
|
153
|
+
* Identical to token-refresh's `TokenRefreshPorts.refresh`; it is hoisted
|
|
154
|
+
* onto the config (not the ports) so the consumer spells the credential
|
|
155
|
+
* source once, the same way `rng` is curried onto the factory.
|
|
156
|
+
* - `skewMs` — token-refresh's refresh-ahead skew (optional, defaults to 0).
|
|
157
|
+
* - every `ResilientConfig` field (`retry` / `circuit` / `rateLimit` /
|
|
158
|
+
* `cache` / `deadline`) — passed straight through to the resilient-call
|
|
159
|
+
* knob, each still optional, each still "omit a brick → omit its gate".
|
|
160
|
+
*/
|
|
161
|
+
interface AuthedConfig extends ResilientConfig, TokenRefreshConfig {
|
|
162
|
+
/** Mint a fresh credential. The single DI seam the auth dimension adds. */
|
|
163
|
+
readonly refresh: () => Promise<Token>;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The composed slice. Three plain-data fields, no closures:
|
|
167
|
+
*
|
|
168
|
+
* - `resilience` — resilient-call's entire slice (circuit, bucket, retry,
|
|
169
|
+
* cache, calls). Owned and mutated only via resilient-call's verbs.
|
|
170
|
+
* - `auth` — token-refresh's `{ token, stale }` slice. Owned and mutated only
|
|
171
|
+
* via token-refresh's verbs.
|
|
172
|
+
* - `authRetry` — per-key count of auth-retries (401 → refresh → retry) spent
|
|
173
|
+
* on a call. Caps the dance at one retry per call so a credential the server
|
|
174
|
+
* keeps rejecting cannot spin forever. A key absent from the record has
|
|
175
|
+
* spent zero. Cleared when a call settles or a fresh `attempt` starts.
|
|
176
|
+
*
|
|
177
|
+
* `pendingAuthRetry` carries the inputs of calls parked waiting for the refresh
|
|
178
|
+
* to land, keyed so `onRefreshed` can re-issue exactly those calls. Plain data —
|
|
179
|
+
* `input` is the same payload the call was issued with.
|
|
180
|
+
*/
|
|
181
|
+
interface AuthedState<I, R> {
|
|
182
|
+
readonly resilience: ResilientState<I, R>;
|
|
183
|
+
readonly auth: TokenState;
|
|
184
|
+
readonly authRetry: Readonly<Record<string, number>>;
|
|
185
|
+
readonly pendingAuthRetry: Readonly<Record<string, I>>;
|
|
186
|
+
}
|
|
187
|
+
/** The two effects this knob emits: resilient-call's run, token-refresh's mint. */
|
|
188
|
+
type AuthedCmd<I> = RunCmd<I> | RefreshTokenCmd;
|
|
189
|
+
/**
|
|
190
|
+
* Every Msg the knob's verbs fold. Resilient-call's settle + timer Msgs, token-
|
|
191
|
+
* refresh's refresh-result Msgs, and the 401 trigger the consumer dispatches
|
|
192
|
+
* when a guarded call comes back unauthorized.
|
|
193
|
+
*/
|
|
194
|
+
type Unauthorized = {
|
|
195
|
+
readonly type: typeof MsgType.Unauthorized;
|
|
196
|
+
readonly key: string;
|
|
197
|
+
readonly at: number;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* The plain-data error a 401 we cannot fix settles with. A `{_tag, ...}`
|
|
201
|
+
* sentinel — NEVER a `new Error(...)` — so the slice stays JSON-serializable
|
|
202
|
+
* (an Error object round-trips to `{}` under JSON and carries a stack trace,
|
|
203
|
+
* both of which break the slice's durability invariant once it is persisted
|
|
204
|
+
* into a `Store<S>` and reloaded). Mirrors resilient-call's `DeadlineExceededError`
|
|
205
|
+
* sentinel discipline. `key` echoes the call that was rejected so a consumer can
|
|
206
|
+
* attribute the failure.
|
|
207
|
+
*/
|
|
208
|
+
type UnauthorizedError = {
|
|
209
|
+
readonly _tag: "unauthorized";
|
|
210
|
+
readonly key: string;
|
|
211
|
+
};
|
|
212
|
+
/** Ports `handlers` needs — resilient-call's run port AND token-refresh's refresh port. */
|
|
213
|
+
interface AuthedPorts<I, R> extends ResilientPorts<I, R> {
|
|
214
|
+
/** Mint a fresh credential. Mirrors `AuthedConfig.refresh` for symmetry at the splice site. */
|
|
215
|
+
readonly refresh: () => Promise<Token>;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Build an authed-call knob from `config`. `rng` is injected for retry jitter
|
|
219
|
+
* and threaded straight into the underlying resilient-call knob — pass a fixed
|
|
220
|
+
* `() => 0.5` in tests to pin backoff; defaults to `Math.random` (read only at
|
|
221
|
+
* the verb boundary inside resilient-call, never in a brick op).
|
|
222
|
+
*
|
|
223
|
+
* Returns the uniform L2 knob contract, widened by the auth verbs (`on401`,
|
|
224
|
+
* `onRefreshed`, `installToken`). `I` is the port input type, `R` the result.
|
|
225
|
+
*/
|
|
226
|
+
declare function createAuthedCall<I, R>(config: AuthedConfig, rng?: () => number): {
|
|
227
|
+
init: () => AuthedState<I, R>;
|
|
228
|
+
attempt: (s: AuthedState<I, R>, key: string, input: I, at: number) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
229
|
+
succeed: (s: AuthedState<I, R>, key: string, msg: SucceedMsg<R>) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
230
|
+
fail: (s: AuthedState<I, R>, key: string, msg: FailMsg) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
231
|
+
on401: (s: AuthedState<I, R>, key: string, at: number) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
232
|
+
installToken: (s: AuthedState<I, R>, token: Token) => AuthedState<I, R>;
|
|
233
|
+
onRefreshed: (s: AuthedState<I, R>, at: number) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
234
|
+
onTimer: (s: AuthedState<I, R>, msg: ResilientTimerMsg) => readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]];
|
|
235
|
+
/** token-refresh's call-boundary verb, re-exposed for proactive expiry refresh. */
|
|
236
|
+
needsRefresh: (s: AuthedState<I, R>, at: number) => boolean;
|
|
237
|
+
subs: (s: AuthedState<I, R>) => readonly DeadlineSub[];
|
|
238
|
+
handlers: (ports: AuthedPorts<I, R>) => {
|
|
239
|
+
refresh_token: (cmd: CmdValue<"refresh_token", Record<string, never>, {
|
|
240
|
+
readonly [detail: string]: unknown;
|
|
241
|
+
readonly _tag: "token_refresh_failed";
|
|
242
|
+
}, unknown>, ctx: PortEmitter, dispatch?: ((msg: TokenRefreshMsg) => void) | undefined) => Promise<void | TokenRefreshMsg>;
|
|
243
|
+
resilient_run: (cmd: CmdValue<"resilient_run", {
|
|
244
|
+
readonly key: string;
|
|
245
|
+
readonly input: I;
|
|
246
|
+
}, {
|
|
247
|
+
readonly [detail: string]: unknown;
|
|
248
|
+
readonly _tag: "deadline_exceeded";
|
|
249
|
+
} | {
|
|
250
|
+
readonly [detail: string]: unknown;
|
|
251
|
+
readonly _tag: "port_rejected";
|
|
252
|
+
}, unknown>, ctx: NoCtx) => Promise<SucceedMsg<R, "resilient"> | FailMsg<"resilient">>;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
/** The shape `createAuthedCall` returns — useful for typing a held knob. */
|
|
256
|
+
type AuthedCall<I, R> = ReturnType<typeof createAuthedCall<I, R>>;
|
|
257
|
+
/**
|
|
258
|
+
* Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice
|
|
259
|
+
* lives at `state.authed`. A convenience for the common single-slice host;
|
|
260
|
+
* consumers with a differently-named field spread by hand. Pure — a thin record
|
|
261
|
+
* rebuild, no clock / RNG.
|
|
262
|
+
*/
|
|
263
|
+
declare function liftAuthed<S extends {
|
|
264
|
+
authed: AuthedState<I, R>;
|
|
265
|
+
}, I, R, C extends Cmd>(state: S, [slice, cmds]: readonly [AuthedState<I, R>, readonly C[]]): readonly [S, readonly C[]];
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* internal/resilience/retry-to-success — run a fallible async port through BOUNDED
|
|
269
|
+
* retry + exponential backoff to success-or-terminal-give-up, and hand the
|
|
270
|
+
* caller a `Promise<R>` that resolves on the first success or REJECTS with a
|
|
271
|
+
* checkable {@link RetryExhaustedError} the moment the retry bound is hit.
|
|
272
|
+
*
|
|
273
|
+
* This is the retry-side parallel to `./await-terminal`. `runToTerminal` gives a
|
|
274
|
+
* caller with a one-shot decision *machine* a fire-and-await `Promise<State>`
|
|
275
|
+
* without wiring `observe`; this gives a caller with a one-shot fallible *port*
|
|
276
|
+
* a fire-and-await `Promise<R>` without either hand-folding the
|
|
277
|
+
* `./retry-backoff` primitives (`recordFailure` / `shouldRetry` / `nextDelayMs`)
|
|
278
|
+
* into their own reducer each time, or standing up the full `./resilient-call`
|
|
279
|
+
* verb contract (`init` + `attempt`/`succeed`/`fail`/`onTimer` + `subs` +
|
|
280
|
+
* `handlers`) when they want retry only and none of cache / circuit / rate-limit
|
|
281
|
+
* / deadline.
|
|
282
|
+
*
|
|
283
|
+
* It adds NO new runtime capability and NO new backoff math — the whole retry
|
|
284
|
+
* decision is `./retry-backoff` exactly as `resilient-call`'s `backoff` verb
|
|
285
|
+
* wires it:
|
|
286
|
+
* - `initRetry()` seeds the per-call attempt counter.
|
|
287
|
+
* - `recordFailure(retry, error)` folds each thrown failure (attempt++).
|
|
288
|
+
* - `shouldRetry(retry, policy)` is the terminal-at-`maxAttempts` bound — the
|
|
289
|
+
* `maxAttempts`-th recorded failure refuses another attempt (attempts
|
|
290
|
+
* `0 .. maxAttempts-1` run; the port is invoked at most `maxAttempts` times).
|
|
291
|
+
* - `nextDelayMs(retry, policy, rng)` is the exp+jitter+cap delay before the
|
|
292
|
+
* next attempt, computed on the incremented state — same call order as
|
|
293
|
+
* `resilient-call`.
|
|
294
|
+
*
|
|
295
|
+
* Where the clock / RNG live: injected, never read on the module's own behalf,
|
|
296
|
+
* mirroring `retry-backoff`'s purity discipline. `rng` is an {@link Rng} (branded
|
|
297
|
+
* `[0, 1)` via `asRng` at the caller boundary) threaded straight into
|
|
298
|
+
* `nextDelayMs`; the wait itself is a `sleep(ms)` port defaulting to a real
|
|
299
|
+
* `setTimeout`, injectable so a test can drive the backoff schedule
|
|
300
|
+
* deterministically without touching wall-clock timers.
|
|
301
|
+
*
|
|
302
|
+
* Exhaustion is NEVER a silent swallow: at the bound the returned promise
|
|
303
|
+
* rejects with {@link RetryExhaustedError} (carrying the attempt count and the
|
|
304
|
+
* final failure), the retry-side dialect of `await-terminal`'s
|
|
305
|
+
* `TerminalTimeoutError` — a distinct `instanceof`- and `_tag`-checkable
|
|
306
|
+
* rejection.
|
|
307
|
+
*
|
|
308
|
+
* NOT a substrate primitive: it depends on nothing from `../index`, only on the
|
|
309
|
+
* leaf `retry-backoff` utility. Internal to the package
|
|
310
|
+
* (`src/internal/resilience/`, #46), not a published subpath.
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Raised when `retryToSuccess` exhausts the retry bound without a success — the
|
|
315
|
+
* `maxAttempts`-th recorded failure refuses another attempt. Follows the tea
|
|
316
|
+
* error dialect (`override readonly name`) and carries a `_tag` discriminant, so
|
|
317
|
+
* a caller can branch on it by `instanceof` or on the tag. It carries the number
|
|
318
|
+
* of attempts made and the final failure (`lastError`) so the terminal give-up
|
|
319
|
+
* is a distinct, checkable rejection — never a silent resolve, and never a
|
|
320
|
+
* swallowed error.
|
|
321
|
+
*/
|
|
322
|
+
declare class RetryExhaustedError extends Error {
|
|
323
|
+
/** How many times the port was invoked before giving up (= `policy.maxAttempts`). */
|
|
324
|
+
readonly attempts: number;
|
|
325
|
+
/** The failure the final attempt threw — carried, never interpreted. */
|
|
326
|
+
readonly lastError: unknown;
|
|
327
|
+
readonly name = "RetryExhaustedError";
|
|
328
|
+
readonly _tag: "RetryExhaustedError";
|
|
329
|
+
constructor(
|
|
330
|
+
/** How many times the port was invoked before giving up (= `policy.maxAttempts`). */
|
|
331
|
+
attempts: number,
|
|
332
|
+
/** The failure the final attempt threw — carried, never interpreted. */
|
|
333
|
+
lastError: unknown);
|
|
334
|
+
}
|
|
335
|
+
/** Options for {@link retryToSuccess}. */
|
|
336
|
+
interface RetryToSuccessOptions {
|
|
337
|
+
/**
|
|
338
|
+
* The `[0, 1)` jitter source threaded into `nextDelayMs`, branded via
|
|
339
|
+
* `asRng` at construction (a raw `() => number` is rejected by the type).
|
|
340
|
+
* Injected for deterministic backoff in tests; defaults to `defaultRng`
|
|
341
|
+
* (`Math.random`), read only at the delay boundary — never inside the loop's
|
|
342
|
+
* decision.
|
|
343
|
+
*/
|
|
344
|
+
readonly rng?: Rng;
|
|
345
|
+
/**
|
|
346
|
+
* The wait between attempts, given the backoff delay in ms. Injectable so a
|
|
347
|
+
* test can drive the retry schedule without wall-clock timers (and assert the
|
|
348
|
+
* exact delays); defaults to a real `setTimeout`. The delay value is always
|
|
349
|
+
* `./retry-backoff`'s `nextDelayMs` — this port only realizes the wait.
|
|
350
|
+
*/
|
|
351
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Fire-and-await bounded retry: invoke the fallible `port`, and on each thrown
|
|
355
|
+
* failure fold it through `./retry-backoff` — record it, and if the policy
|
|
356
|
+
* still permits an attempt, back off `nextDelayMs` and retry; otherwise REJECT
|
|
357
|
+
* with {@link RetryExhaustedError}. Resolves with the port's value on the first
|
|
358
|
+
* success.
|
|
359
|
+
*
|
|
360
|
+
* The port is invoked at most `policy.maxAttempts` times (attempts
|
|
361
|
+
* `0 .. maxAttempts-1`); the `maxAttempts`-th failure is terminal. Every failure
|
|
362
|
+
* is either retried or surfaced on the rejection's `lastError` — none is
|
|
363
|
+
* swallowed.
|
|
364
|
+
*
|
|
365
|
+
* @param port the fallible work — throws on failure, resolves with `R` on success.
|
|
366
|
+
* @param policy the bounded-retry curve (`baseMs` / `factor` / `capMs` /
|
|
367
|
+
* `maxAttempts` / `jitter`) from `./retry-backoff`.
|
|
368
|
+
* @param opts optional injected `rng` (jitter) and `sleep` (the wait) — see
|
|
369
|
+
* {@link RetryToSuccessOptions}.
|
|
370
|
+
*/
|
|
371
|
+
declare function retryToSuccess<R>(port: () => Promise<R>, policy: RetryPolicy, opts?: RetryToSuccessOptions): Promise<R>;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The wrapper's Model slice. `phase` is the lifecycle (`armed` while the
|
|
375
|
+
* countdown runs, `exceeded` once the deadline fired); `seq` is the monotonic
|
|
376
|
+
* re-arm counter that keys the timer Sub's id so each accepted progress Msg
|
|
377
|
+
* retires the old timer and starts a fresh one.
|
|
378
|
+
*
|
|
379
|
+
* PURE and SERIALIZABLE: there is deliberately NO absolute timestamp here. A
|
|
380
|
+
* deadline timestamp would force the merged `update` to read the wall clock to
|
|
381
|
+
* compute it, which the conformance gate (property 2 — two replays under
|
|
382
|
+
* distinct ambient clocks) is built to catch. Instead, the timer arms a fresh
|
|
383
|
+
* full `ms` countdown per generation; no remaining-delay computation and no
|
|
384
|
+
* clock read occur in the slice or `update` — the only clock is `setTimeout`
|
|
385
|
+
* inside the Sub.
|
|
386
|
+
*/
|
|
387
|
+
interface DeadlineSlice {
|
|
388
|
+
/** `armed` while the countdown is live; `exceeded` once the deadline fired. */
|
|
389
|
+
readonly phase: "armed" | "exceeded";
|
|
390
|
+
/**
|
|
391
|
+
* Monotonic re-arm counter. Starts at 0; each accepted progress Msg (while
|
|
392
|
+
* armed) bumps it by one. Folds into the timer Sub's id, so a bump = a new id
|
|
393
|
+
* = the substrate retires the old timer and arms a fresh `ms` countdown.
|
|
394
|
+
*/
|
|
395
|
+
readonly seq: number;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* The composed Model. The base machine's state is nested under `base`; the
|
|
399
|
+
* wrapper's state lives in the NAMED `$deadline` slice — never hidden in a
|
|
400
|
+
* closure. Both fields are plain data, so the whole composed Model round-trips
|
|
401
|
+
* through `JSON.parse(JSON.stringify(...))` iff the base state does.
|
|
402
|
+
*/
|
|
403
|
+
interface DeadlineModel<S> {
|
|
404
|
+
readonly base: S;
|
|
405
|
+
readonly $deadline: DeadlineSlice;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* The relative-timeout Sub the wrapper arms while `phase === "armed"`. `delayMs`
|
|
409
|
+
* is the window `config.ms`; the id is `$deadline:timeout:{seq}`, so each
|
|
410
|
+
* progress bump produces a distinct id and the reconcile pass churns the timer.
|
|
411
|
+
* Same `delayMs`-on-the-Sub shape `fromTimeout` consumes.
|
|
412
|
+
*/
|
|
413
|
+
type DeadlineTimeoutSub = Sub<"$deadline:timeout"> & {
|
|
414
|
+
readonly delayMs: number;
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* The Msg the timeout Sub dispatches when `config.ms` elapses with no accepted
|
|
418
|
+
* progress. `seq` echoes the re-arm generation the timer was armed against, so a
|
|
419
|
+
* stale timer's Msg (one whose id was already retired) is identifiable; the
|
|
420
|
+
* merged `update` only honors it when it matches the current slice `seq`.
|
|
421
|
+
*/
|
|
422
|
+
interface DeadlineExceededMsg {
|
|
423
|
+
readonly type: "$deadline:exceeded";
|
|
424
|
+
/** The re-arm generation this timer was armed against. */
|
|
425
|
+
readonly seq: number;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Construct the deadline-exceeded Msg. Exported so the subscribe cell and tests
|
|
429
|
+
* share one constructor rather than two literals that can drift.
|
|
430
|
+
*/
|
|
431
|
+
declare function deadlineExceededMsg(seq: number): DeadlineExceededMsg;
|
|
432
|
+
/**
|
|
433
|
+
* The fire-and-forget decision Cmd the merged `update` appends on every
|
|
434
|
+
* transition, so the wrapper's choice is visible in the replayed Cmd log
|
|
435
|
+
* (rule 3) — `rearm` when an accepted progress Msg bumped `seq`, `expire` when
|
|
436
|
+
* the deadline-exceeded Msg flipped the phase, `idle` otherwise (a non-progress
|
|
437
|
+
* Msg, or a Msg after the machine already auto-failed). Plain data; its
|
|
438
|
+
* interpret handler is a no-op (the decision is the Cmd's presence in the log,
|
|
439
|
+
* not a side effect).
|
|
440
|
+
*/
|
|
441
|
+
declare const deadlineDecision: CmdDef<"$deadline:decision", {
|
|
442
|
+
decision: "idle" | "rearm" | "expire";
|
|
443
|
+
seq: number;
|
|
444
|
+
}, void, never, unknown>;
|
|
445
|
+
type DeadlineDecisionCmd = CmdOf<typeof deadlineDecision>;
|
|
446
|
+
/**
|
|
447
|
+
* A predicate over a Msg that decides whether it re-arms the deadline.
|
|
448
|
+
*
|
|
449
|
+
* The argument is `Readonly<M>` and the result is `boolean`: this is the
|
|
450
|
+
* STRONGEST purity TS can express here. `Readonly<M>` removes the most common
|
|
451
|
+
* impurity at compile time — the predicate cannot reassign the Msg's own
|
|
452
|
+
* fields — and `boolean` pins the return so a side-effecting `(msg) => { … }`
|
|
453
|
+
* with an implicit `undefined`/`void` return is rejected.
|
|
454
|
+
*
|
|
455
|
+
* What the type system CANNOT encode (TS has no effect tracking): observational
|
|
456
|
+
* purity. A body that reads `Date.now()`, calls `Math.random()`, performs IO,
|
|
457
|
+
* or closes over and mutates external state still type-checks. Those remain a
|
|
458
|
+
* CONVENTION enforced by review, not the compiler — the predicate runs inside
|
|
459
|
+
* the pure merged `update` (invariant 2), so it must be a pure function of the
|
|
460
|
+
* Msg alone: no clock, no RNG, no IO, no captured mutable state.
|
|
461
|
+
*/
|
|
462
|
+
type ProgressPredicate<M extends {
|
|
463
|
+
type: string;
|
|
464
|
+
}> = (msg: Readonly<M>) => boolean;
|
|
465
|
+
/**
|
|
466
|
+
* The deadline knob. `ms` is the inactivity window; `progress` is an OPTIONAL
|
|
467
|
+
* predicate picking which base Msgs count as activity (default: EVERY base Msg
|
|
468
|
+
* re-arms). The predicate runs INSIDE the pure merged `update`, so it must be a
|
|
469
|
+
* pure function of the Msg — see {@link ProgressPredicate} for what the type
|
|
470
|
+
* enforces vs. what stays a convention.
|
|
471
|
+
*/
|
|
472
|
+
interface DeadlineConfig<M extends {
|
|
473
|
+
type: string;
|
|
474
|
+
}> {
|
|
475
|
+
/** The inactivity window in milliseconds. Auto-fail after `ms` with no progress. */
|
|
476
|
+
readonly ms: number;
|
|
477
|
+
/**
|
|
478
|
+
* Predicate picking which base Msgs re-arm the timer. Default: every base Msg
|
|
479
|
+
* counts as progress. Pure — a function of the Msg alone (see
|
|
480
|
+
* {@link ProgressPredicate}).
|
|
481
|
+
*/
|
|
482
|
+
readonly progress?: ProgressPredicate<M>;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Wrap `base` with an inactivity deadline. Returns a NEW `Machine` over the
|
|
486
|
+
* composed Model `{ base, $deadline }`, with the base's Msgs/Cmds/Subs extended
|
|
487
|
+
* by the wrapper's own (`$deadline:exceeded` Msg, `$deadline:decision` Cmd,
|
|
488
|
+
* `$deadline:timeout` Sub).
|
|
489
|
+
*
|
|
490
|
+
* The composed machine:
|
|
491
|
+
* - `init` rehydrates the base (honoring the `[loaded, []]` contract) and
|
|
492
|
+
* seeds `$deadline: { phase: "armed", seq: 0 }`. Base init Cmds pass
|
|
493
|
+
* through unchanged.
|
|
494
|
+
* - `update` (Reducer form) runs the base reducer for base Msgs — base Cmds
|
|
495
|
+
* pass through UNCHANGED — and on an accepted progress Msg bumps `seq`
|
|
496
|
+
* (re-arm). The `$deadline:exceeded` Msg cell flips `phase` to `exceeded`.
|
|
497
|
+
* Every transition appends a `$deadline:decision` marker Cmd.
|
|
498
|
+
* - `subscriptions` is the base's, plus a `$deadline:timeout` Sub keyed on
|
|
499
|
+
* `seq` while `phase === "armed"`.
|
|
500
|
+
* - `subscribe` is the base's, plus the `$deadline:timeout` cell.
|
|
501
|
+
* - `interpret` is the base's, plus a no-op `$deadline:decision` handler.
|
|
502
|
+
*
|
|
503
|
+
* @param base any `Machine<S, M, C, U, Ctx>`.
|
|
504
|
+
* @param config the deadline knob (`ms` window + optional `progress` predicate).
|
|
505
|
+
*/
|
|
506
|
+
declare function withDeadline<S, M extends {
|
|
507
|
+
type: string;
|
|
508
|
+
}, C extends Cmd, U extends Sub, Ctx>(base: Machine<S, M, C, U, Ctx>, config: DeadlineConfig<M>): Machine<DeadlineModel<S>, M | DeadlineExceededMsg, C | DeadlineDecisionCmd, U | DeadlineTimeoutSub, Ctx>;
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* The `withResilience` knob. Extends resilient-call's `ResilientConfig` (every
|
|
512
|
+
* brick optional — omit a brick, omit its gate) with the two fields the wrapper
|
|
513
|
+
* needs that the bare knob does not:
|
|
514
|
+
*
|
|
515
|
+
* - `target` — the `Cmd.type` discriminant of the ONE base Cmd this wrapper
|
|
516
|
+
* hardens. Every other base Cmd passes through untouched.
|
|
517
|
+
* - `keyOf` — OPTIONAL. Derives the per-call resilience `key` from the target
|
|
518
|
+
* base Cmd, so distinct logical calls (distinct urls, distinct ids) get
|
|
519
|
+
* independent retry / cache state. Pure — it runs inside the merged
|
|
520
|
+
* `update`, so it must NOT read the clock / RNG. Defaults to a single
|
|
521
|
+
* shared key (`target`), i.e. one logical call at a time.
|
|
522
|
+
*/
|
|
523
|
+
interface ResilienceConfig<TC extends Cmd, TM extends {
|
|
524
|
+
type: string;
|
|
525
|
+
}> extends ResilientConfig {
|
|
526
|
+
/** The base `Cmd.type` to make resilient. */
|
|
527
|
+
readonly target: TC["type"];
|
|
528
|
+
/** Optional per-call key deriver. Pure. Defaults to the target type itself. */
|
|
529
|
+
readonly keyOf?: (cmd: TC) => string;
|
|
530
|
+
/**
|
|
531
|
+
* Extracts the real wall-clock time (ms) the TRIGGERING base Msg carries as
|
|
532
|
+
* data — the time-as-data convention `resilient-call` expects on `attempt`.
|
|
533
|
+
*
|
|
534
|
+
* The merged `update` is PURE: it cannot read `Date.now()` (rule 5). The COLD
|
|
535
|
+
* attempt's gate (the very first `attempt` for a target Cmd in a transition)
|
|
536
|
+
* still needs a real instant — the breaker compares `at` against its recorded
|
|
537
|
+
* `openedAtMs`, the cache against its write `at`, the bucket against its last
|
|
538
|
+
* refill `at`, and the deadline cap anchors at `at + deadline.ms`. Fabricating
|
|
539
|
+
* `at = 0` is the betrayal: a cold attempt after a real-time breaker trip then
|
|
540
|
+
* computes cooldown = `0 - realTripTime < 0` forever, so the breaker NEVER
|
|
541
|
+
* recovers, and a `0`-anchored deadline brick fires immediately.
|
|
542
|
+
*
|
|
543
|
+
* So the base Msg that drove this transition MUST carry its own wall time as
|
|
544
|
+
* data, and `at` reads it off that Msg. The merged update passes
|
|
545
|
+
* `at(currentBaseMsg)` into `rc.attempt` for every target Cmd that transition
|
|
546
|
+
* emitted — real time enters the cold gate as Msg data, never `0`.
|
|
547
|
+
*
|
|
548
|
+
* REQUIRED whenever any time-sensitive brick (`circuit`, `rateLimit`, `cache`,
|
|
549
|
+
* `deadline`) is configured — `withResilience` THROWS at construction if such
|
|
550
|
+
* a brick is set and `at` is absent (errors are data: the cold gate needs real
|
|
551
|
+
* time, refuse to build a breaker that can never recover). OPTIONAL when only
|
|
552
|
+
* `retry` is configured: retry backoff reads its time off the result / timer
|
|
553
|
+
* Msgs (`$resilience:err.at`, `$resilience:timer.atMs`), never the cold attempt
|
|
554
|
+
* — so a `0` cold `at` is harmless there. Pure: it runs inside the merged
|
|
555
|
+
* `update`, so it must NOT read the clock / RNG.
|
|
556
|
+
*/
|
|
557
|
+
readonly at?: (msg: TM) => number;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* The composed Model. The base machine's state nests under `base`; the wrapper
|
|
561
|
+
* owns the NAMED `$resilience` slice — resilient-call's `ResilientState` keyed
|
|
562
|
+
* by the per-call `key`, with `I = TC` (the original target base Cmd is the
|
|
563
|
+
* port input) and `R = unknown` (the base interpret handler's resolution — the
|
|
564
|
+
* wrapper never inspects it; it routes Ok/Err and delivers a Msg to the base).
|
|
565
|
+
* Plain data → round-trips through `JSON.parse(JSON.stringify(...))` iff the
|
|
566
|
+
* base state does.
|
|
567
|
+
*/
|
|
568
|
+
interface ResilienceModel<S, TC extends Cmd> {
|
|
569
|
+
readonly base: S;
|
|
570
|
+
readonly $resilience: ResilientState<TC, unknown>;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The carrier Cmd the wrapper emits when the gate ADMITS a target base Cmd. It
|
|
574
|
+
* RETAGS the original base Cmd into `input` — the base Cmd is plain data, so no
|
|
575
|
+
* closure (invariant 3). The interpret handler unwraps `input` and runs it
|
|
576
|
+
* through the BASE interpret for that Cmd type. `key` indexes the resilient
|
|
577
|
+
* call this run belongs to.
|
|
578
|
+
*/
|
|
579
|
+
declare function resilienceRunCmdDef<TC extends Cmd, TM>(): CmdDef<"$resilience:run", {
|
|
580
|
+
readonly key: string;
|
|
581
|
+
/** The ORIGINAL base Cmd, retagged — never swallowed. */
|
|
582
|
+
readonly input: TC;
|
|
583
|
+
}, void | TM, {
|
|
584
|
+
readonly [detail: string]: unknown;
|
|
585
|
+
readonly _tag: "deadline_exceeded";
|
|
586
|
+
} | {
|
|
587
|
+
readonly [detail: string]: unknown;
|
|
588
|
+
readonly _tag: "base_rejected";
|
|
589
|
+
}, unknown>;
|
|
590
|
+
type ResilienceRunCmd<TC extends Cmd> = CmdOf<ReturnType<typeof resilienceRunCmdDef<TC, unknown>>>;
|
|
591
|
+
/**
|
|
592
|
+
* The success Msg the `$resilience:run` handler dispatches back when the base
|
|
593
|
+
* interpret resolved OK. Routes to `succeed`, then `result` — the base
|
|
594
|
+
* handler's follow-up, when it is a Msg — is folded through the base reducer.
|
|
595
|
+
* `at` is stamped at the interpret boundary (the one permitted clock read).
|
|
596
|
+
*/
|
|
597
|
+
type ResilienceOkMsg = {
|
|
598
|
+
readonly type: "$resilience:ok";
|
|
599
|
+
readonly key: string;
|
|
600
|
+
readonly result: unknown;
|
|
601
|
+
readonly at: number;
|
|
602
|
+
};
|
|
603
|
+
/**
|
|
604
|
+
* The error Msg the `$resilience:run` handler dispatches back when the base
|
|
605
|
+
* interpret threw / rejected. Routes to `fail` (trip the breaker, then back off
|
|
606
|
+
* or settle). `at` is stamped at the interpret boundary.
|
|
607
|
+
*/
|
|
608
|
+
type ResilienceErrMsg = {
|
|
609
|
+
readonly type: "$resilience:err";
|
|
610
|
+
readonly key: string;
|
|
611
|
+
readonly error: unknown;
|
|
612
|
+
readonly at: number;
|
|
613
|
+
};
|
|
614
|
+
/**
|
|
615
|
+
* The retry / deadline timer Msg the `$resilience:timer` Sub dispatches when
|
|
616
|
+
* the wall clock crosses the armed instant. Routes to `onTimer`. `id` is the
|
|
617
|
+
* `$resilience:`-namespaced timer id; `atMs` is the fire instant (Msg data, the
|
|
618
|
+
* clock that drove it lives in the Sub).
|
|
619
|
+
*/
|
|
620
|
+
type ResilienceTimerMsg = {
|
|
621
|
+
readonly type: "$resilience:timer";
|
|
622
|
+
readonly id: string;
|
|
623
|
+
readonly atMs: number;
|
|
624
|
+
};
|
|
625
|
+
/** Every Msg the wrapper adds to the base's Msg union. */
|
|
626
|
+
type ResilienceMsg = ResilienceOkMsg | ResilienceErrMsg | ResilienceTimerMsg;
|
|
627
|
+
/** Every Cmd the wrapper adds to the base's Cmd union. */
|
|
628
|
+
type ResilienceCmd<TC extends Cmd> = ResilienceRunCmd<TC>;
|
|
629
|
+
/** The Sub the wrapper adds — a deadline-style timer in the `$resilience` family. */
|
|
630
|
+
type ResilienceTimerSub = Sub<"$resilience:timer"> & {
|
|
631
|
+
readonly atMs: number;
|
|
632
|
+
};
|
|
633
|
+
/**
|
|
634
|
+
* Wrap `base` so its `config.target` Cmd is run through the resilient-call
|
|
635
|
+
* concern (cache → circuit → rate-limit → retry, deadline-capped). Returns a
|
|
636
|
+
* NEW `Machine` over the composed Model `{ base, $resilience }`, with the
|
|
637
|
+
* base's Msgs/Cmds/Subs/Ctx extended by the wrapper's own.
|
|
638
|
+
*
|
|
639
|
+
* @param base any `Machine<S, M, C, U, Ctx>`.
|
|
640
|
+
* @param config the resilience knob: `target`, the optional bricks, `keyOf`.
|
|
641
|
+
* @param rng injected backoff jitter source. Pass a fixed `() => 0.5` in
|
|
642
|
+
* tests to pin retry delays; defaults to `Math.random` (read only
|
|
643
|
+
* at the verb boundary, never inside the merged `update`).
|
|
644
|
+
*/
|
|
645
|
+
declare function withResilience<S, M extends {
|
|
646
|
+
type: string;
|
|
647
|
+
}, C extends Cmd, U extends Sub, Ctx>(base: Machine<S, M, C, U, Ctx>, config: ResilienceConfig<C, M>, rng?: () => number): Machine<ResilienceModel<S, C>, M | ResilienceMsg, C | ResilienceCmd<C>, U | ResilienceTimerSub, Ctx>;
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* The datum handed to the sink. The default projector emits `{ seq, msgType }`;
|
|
651
|
+
* a custom `event` projector may return any extra fields. `at` is OPTIONAL and
|
|
652
|
+
* plain data — it is stamped at the interpret boundary by the `clock` port, not
|
|
653
|
+
* inside the (pure, clock-free) merged `update`. Every field is JSON-round-trip
|
|
654
|
+
* serializable so a recorded telemetry stream stays durable.
|
|
655
|
+
*/
|
|
656
|
+
interface TelemetryEvent {
|
|
657
|
+
/** Monotonic event counter — equals `$telemetry.seq` after the transition. */
|
|
658
|
+
readonly seq: number;
|
|
659
|
+
/** The `Msg.type` discriminant that drove this transition. */
|
|
660
|
+
readonly msgType: string;
|
|
661
|
+
/**
|
|
662
|
+
* Wall-clock instant the event was emitted, ms since epoch. Absent until the
|
|
663
|
+
* interpret boundary stamps it (the `clock` port). Optional because the
|
|
664
|
+
* merged `update` is clock-free — a verb must never read `Date.now()`.
|
|
665
|
+
*/
|
|
666
|
+
readonly at?: number;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* The wrapper's Model slice. A monotonic event counter — minimal but real, so
|
|
670
|
+
* the named-slice mechanism is exercised for the richer-slice wrappers that
|
|
671
|
+
* follow. `seq` equals the number of base Msgs folded so far: after replaying
|
|
672
|
+
* `N` msgs, `$telemetry.seq === N`. Plain data → serializes into a `Store<S>`.
|
|
673
|
+
*/
|
|
674
|
+
interface TelemetrySlice {
|
|
675
|
+
readonly seq: number;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* The composed Model. The base machine's state is nested under `base`; the
|
|
679
|
+
* wrapper's state lives in the NAMED `$telemetry` slice — never hidden in a
|
|
680
|
+
* closure. Both fields are plain data, so the whole composed Model round-trips
|
|
681
|
+
* through `JSON.parse(JSON.stringify(...))` iff the base state does.
|
|
682
|
+
*/
|
|
683
|
+
interface TelemetryModel<S> {
|
|
684
|
+
readonly base: S;
|
|
685
|
+
readonly $telemetry: TelemetrySlice;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* The fire-and-forget Cmd the merged `update` appends after every base
|
|
689
|
+
* transition. It carries the projected `event` as plain data (invariant 3 —
|
|
690
|
+
* no closure). The interpret handler hands `event` to the sink and dispatches
|
|
691
|
+
* NOTHING back, so there is no feedback loop.
|
|
692
|
+
*/
|
|
693
|
+
declare const telemetryEmit: CmdDef<"$telemetry:emit", {
|
|
694
|
+
event: TelemetryEvent;
|
|
695
|
+
}, void, never, TelemetryPorts>;
|
|
696
|
+
type TelemetryEmitCmd = CmdOf<typeof telemetryEmit>;
|
|
697
|
+
/**
|
|
698
|
+
* The side-effecting ports the wrapper adds to `Ctx`. The sink (PostHog / Sentry
|
|
699
|
+
* bridge) lives at the interpret boundary, never in a verb. `clock` is the
|
|
700
|
+
* optional wall-clock source used to stamp `event.at` at that same boundary —
|
|
701
|
+
* pass `Date.now` in production, a fixed `() => 1000` in tests; omit it to leave
|
|
702
|
+
* `at` unset.
|
|
703
|
+
*/
|
|
704
|
+
interface TelemetryPorts {
|
|
705
|
+
/** The side-effecting sink. Fire-and-forget — its result is never awaited into a Msg. */
|
|
706
|
+
readonly telemetrySink: (event: TelemetryEvent) => void | Promise<void>;
|
|
707
|
+
/** Optional clock for stamping `event.at` at the interpret boundary. */
|
|
708
|
+
readonly clock?: () => number;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* The telemetry knob. `event` is an OPTIONAL projector mapping
|
|
712
|
+
* `(msg, prev, next)` to a `TelemetryEvent`; omit it and the default emits
|
|
713
|
+
* `{ seq, msgType }` where `seq` is the post-transition counter. The projector
|
|
714
|
+
* runs INSIDE the pure merged `update`, so it must not read the clock — stamp
|
|
715
|
+
* `at` at the sink boundary via the `clock` port instead.
|
|
716
|
+
*/
|
|
717
|
+
interface TelemetryConfig<S, M extends {
|
|
718
|
+
type: string;
|
|
719
|
+
}> {
|
|
720
|
+
readonly event?: (msg: M, prev: TelemetryModel<S>, next: TelemetryModel<S>) => TelemetryEvent;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Wrap `base` with observe-only telemetry. Returns a NEW `Machine` over the
|
|
724
|
+
* composed Model `{ base, $telemetry }`, with the base's Msgs/Cmds/Subs/Ctx
|
|
725
|
+
* extended by the wrapper's own.
|
|
726
|
+
*
|
|
727
|
+
* The composed machine:
|
|
728
|
+
* - `init` rehydrates the base (honoring the `[loaded, []]` contract) and
|
|
729
|
+
* seeds `$telemetry: { seq: 0 }`. Base init Cmds pass through unchanged.
|
|
730
|
+
* - `update` (Reducer form, keyed by every base `Msg.type`) runs the base
|
|
731
|
+
* reducer, increments `seq`, and appends a `$telemetry:emit` Cmd carrying
|
|
732
|
+
* the projected event. Base Cmds pass through UNCHANGED — the observe-only
|
|
733
|
+
* property.
|
|
734
|
+
* - `subscriptions` / `subscribe` are the base's, verbatim (no timers).
|
|
735
|
+
* - `interpret` is `{ ...base.interpret, "$telemetry:emit": sink-handler }`.
|
|
736
|
+
*
|
|
737
|
+
* @param base any `Machine<S, M, C, U, Ctx>`.
|
|
738
|
+
* @param config the telemetry knob (optional `event` projector).
|
|
739
|
+
*/
|
|
740
|
+
declare function withTelemetry<S, M extends {
|
|
741
|
+
type: string;
|
|
742
|
+
}, C extends Cmd, U extends Sub, Ctx>(base: Machine<S, M, C, U, Ctx>, config?: TelemetryConfig<S, M>): Machine<TelemetryModel<S>, M, C | TelemetryEmitCmd, U, Ctx & TelemetryPorts>;
|
|
743
|
+
|
|
744
|
+
export { type AuthedCall, type AuthedCmd, type AuthedConfig, type AuthedPorts, type AuthedState, type DeadlineConfig, type DeadlineDecisionCmd, type DeadlineExceededMsg, type DeadlineModel, type DeadlineSlice, DeadlineSub, type DeadlineTimeoutSub, FailMsg, type ProgressPredicate, type RefreshTokenCmd, type ResilienceCmd, type ResilienceConfig, type ResilienceErrMsg, type ResilienceModel, type ResilienceMsg, type ResilienceOkMsg, type ResilienceRunCmd, type ResilienceTimerMsg, type ResilienceTimerSub, ResilientConfig, ResilientPorts, ResilientState, ResilientTimerMsg, RetryExhaustedError, type RetryToSuccessOptions, RunCmd, SucceedMsg, type TelemetryConfig, type TelemetryEmitCmd, type TelemetryEvent, type TelemetryModel, type TelemetryPorts, type TelemetrySlice, type Token, type TokenRefresh, type TokenRefreshConfig, type TokenRefreshFailedMsg, type TokenRefreshMsg, type TokenRefreshPorts, type TokenRefreshedMsg, type TokenState, type Unauthorized, type UnauthorizedError, createAuthedCall, createTokenRefresh, deadlineDecision, deadlineExceededMsg, initTokenRefresh, liftAuthed, refreshToken, refreshTokenCmd, resilienceRunCmdDef, retryToSuccess, telemetryEmit, tokenRefreshFailedMsg, tokenRefreshedMsg, withDeadline, withResilience, withTelemetry };
|