@demlik/tea 0.20.0 → 0.22.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 -2
- package/dist/agent/index.d.ts +36 -23
- package/dist/agent/index.js +12 -10
- package/dist/{chunk-UHAQ4PI6.js → chunk-2A5XTKQQ.js} +205 -59
- package/dist/chunk-2A5XTKQQ.js.map +1 -0
- package/dist/{chunk-JAKM53JF.js → chunk-7K7SEYCH.js} +16 -15
- package/dist/chunk-7K7SEYCH.js.map +1 -0
- package/dist/{chunk-RKVUVALV.js → chunk-A3QPMX5M.js} +31 -9
- package/dist/chunk-A3QPMX5M.js.map +1 -0
- package/dist/{chunk-DVNXEB7V.js → chunk-BVFS3ICC.js} +3 -3
- package/dist/chunk-BVFS3ICC.js.map +1 -0
- package/dist/{chunk-FTCYY2DO.js → chunk-CPE5ZQXQ.js} +3 -3
- package/dist/{chunk-FTCYY2DO.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
- package/dist/chunk-DEAPNMKO.js +11 -0
- package/dist/chunk-DEAPNMKO.js.map +1 -0
- package/dist/{chunk-XWIE5V7R.js → chunk-ETRNSJCR.js} +4 -4
- package/dist/{chunk-XWIE5V7R.js.map → chunk-ETRNSJCR.js.map} +1 -1
- package/dist/chunk-FXWDDNGD.js +98 -0
- package/dist/chunk-FXWDDNGD.js.map +1 -0
- package/dist/chunk-GFGOQIEC.js +10 -0
- package/dist/chunk-GFGOQIEC.js.map +1 -0
- package/dist/{chunk-2R62Q5UK.js → chunk-IAHEYPYJ.js} +3 -3
- package/dist/{chunk-2R62Q5UK.js.map → chunk-IAHEYPYJ.js.map} +1 -1
- package/dist/{chunk-NGYFBD5G.js → chunk-J5JNLXO7.js} +3 -3
- package/dist/{chunk-NGYFBD5G.js.map → chunk-J5JNLXO7.js.map} +1 -1
- package/dist/{chunk-72CN3VPY.js → chunk-NFOGYDRS.js} +5 -5
- package/dist/{chunk-72CN3VPY.js.map → chunk-NFOGYDRS.js.map} +1 -1
- package/dist/{chunk-A73IJ27X.js → chunk-R2YNDRGL.js} +3 -3
- package/dist/{chunk-A73IJ27X.js.map → chunk-R2YNDRGL.js.map} +1 -1
- package/dist/{chunk-5HVKDHY6.js → chunk-R5NXOPL2.js} +3 -3
- package/dist/{chunk-5HVKDHY6.js.map → chunk-R5NXOPL2.js.map} +1 -1
- package/dist/chunk-RMBZ7YH7.js +52 -0
- package/dist/chunk-RMBZ7YH7.js.map +1 -0
- package/dist/{chunk-AIXC33SE.js → chunk-SHXUSTHM.js} +62 -15
- package/dist/chunk-SHXUSTHM.js.map +1 -0
- package/dist/{chunk-TAKPC6SD.js → chunk-UGTC33VH.js} +4 -4
- package/dist/{chunk-TAKPC6SD.js.map → chunk-UGTC33VH.js.map} +1 -1
- package/dist/{chunk-EA47MI5Q.js → chunk-VH5MYJ2K.js} +4 -4
- package/dist/{chunk-EA47MI5Q.js.map → chunk-VH5MYJ2K.js.map} +1 -1
- package/dist/{chunk-2774I242.js → chunk-WZEDIUWR.js} +21 -20
- package/dist/chunk-WZEDIUWR.js.map +1 -0
- package/dist/{chunk-TGJLXZAU.js → chunk-ZRRET3RP.js} +11 -11
- package/dist/chunk-ZRRET3RP.js.map +1 -0
- package/dist/{core-CtvAyPpH.d.ts → core-DNDAEhO2.d.ts} +9 -12
- package/dist/devtools/index.d.ts +4 -4
- package/dist/do/index.d.ts +103 -38
- package/dist/do/index.js +40 -21
- package/dist/do/index.js.map +1 -1
- package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
- package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
- package/dist/effect/index.d.ts +121 -28
- package/dist/effect/index.js +45 -31
- package/dist/effect/index.js.map +1 -1
- package/dist/extension/index.d.ts +3 -3
- package/dist/extension/index.js +2 -2
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +17 -13
- package/dist/flow/index.js +15 -12
- package/dist/flow/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +3 -3
- package/dist/idempotency/index.js +3 -3
- package/dist/{index-CrcoVfd_.d.ts → index-9P1PaXIV.d.ts} +2 -2
- package/dist/{index-D3NY3VCN.d.ts → index-BBLI2WWN.d.ts} +1 -1
- package/dist/{index-Dl71V7kt.d.ts → index-C1WhBZ6a.d.ts} +4 -5
- package/dist/{index-CKtEKGyp.d.ts → index-C92SgNWz.d.ts} +3 -3
- package/dist/{index-CZZ3HI-Z.d.ts → index-CAsUE_d1.d.ts} +4 -4
- package/dist/{index-EIgfl83J.d.ts → index-CO34esWT.d.ts} +2 -2
- package/dist/{index-Bk2hBUdK.d.ts → index-COeX9Bmz.d.ts} +11 -9
- package/dist/{index-CVU7SK3a.d.ts → index-Cg0QXnzf.d.ts} +2 -2
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/dist/jev/index.d.ts +15 -15
- package/dist/jev/index.js +5 -5
- package/dist/jev/index.js.map +1 -1
- package/dist/laws-DTtCDDlU.d.ts +135 -0
- package/dist/loop-DReKGV1X.d.ts +70 -0
- package/dist/{machine-Dtre2TmN.d.ts → machine-C0WhOAyB.d.ts} +7 -7
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +4 -3
- package/dist/mem/index.js +2 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +57 -9
- package/dist/node/index.js +175 -68
- package/dist/node/index.js.map +1 -1
- package/dist/otel/index.d.ts +28 -12
- package/dist/otel/index.js +25 -21
- package/dist/otel/index.js.map +1 -1
- package/dist/paginate/index.d.ts +4 -4
- package/dist/paginate/index.js +5 -5
- package/dist/parity/index.d.ts +11 -15
- package/dist/parity/index.js +4 -9
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +279 -67
- package/dist/pbt/index.js +299 -58
- package/dist/pbt/index.js.map +1 -1
- package/dist/persistence/index.d.ts +10 -12
- package/dist/persistence/index.js +4 -4
- package/dist/promise/index.d.ts +118 -21
- package/dist/promise/index.js +6 -4
- package/dist/react/index.d.ts +34 -21
- package/dist/react/index.js +271 -42
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +6 -6
- package/dist/resilience/index.js +5 -5
- package/dist/{run-fiBe7pOG.d.ts → run-Cnw4qXj2.d.ts} +105 -23
- package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-CP-ykwgf.d.ts} +157 -29
- package/dist/testing/effect/index.d.ts +162 -9
- package/dist/testing/effect/index.js +110 -10
- package/dist/testing/effect/index.js.map +1 -1
- package/dist/testing/index.d.ts +6 -8
- package/dist/testing/index.js +4 -24
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/promise/index.d.ts +4 -4
- package/dist/testing/promise/index.js +5 -5
- package/dist/timing/index.d.ts +4 -4
- package/dist/timing/index.js +4 -4
- package/dist/{types-CXNzlbAg.d.ts → types-S53WDy_h.d.ts} +1 -1
- package/dist/work-queue/index.d.ts +4 -4
- package/dist/work-queue/index.js +2 -2
- package/package.json +19 -17
- package/dist/chunk-2774I242.js.map +0 -1
- package/dist/chunk-AIXC33SE.js.map +0 -1
- package/dist/chunk-DVNXEB7V.js.map +0 -1
- package/dist/chunk-HX3C6LDK.js +0 -34
- package/dist/chunk-HX3C6LDK.js.map +0 -1
- package/dist/chunk-JAKM53JF.js.map +0 -1
- package/dist/chunk-RKVUVALV.js.map +0 -1
- package/dist/chunk-TGJLXZAU.js.map +0 -1
- package/dist/chunk-UHAQ4PI6.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { typeOf, indexPath, unionKeys, keyPath } from './chunk-HWVW4AXC.js';
|
|
2
|
-
import {
|
|
2
|
+
import { foldMsgs } from './chunk-A3QPMX5M.js';
|
|
3
3
|
|
|
4
4
|
// src/internal/persistence/recorder/index.ts
|
|
5
5
|
function recorder(runtime, opts) {
|
|
@@ -32,7 +32,7 @@ function recorder(runtime, opts) {
|
|
|
32
32
|
}
|
|
33
33
|
return {
|
|
34
34
|
// sawBoot ⇒ `loaded` is the captured boot state; otherwise the
|
|
35
|
-
//
|
|
35
|
+
// trace has no replay anchor and replayTrace will refuse it.
|
|
36
36
|
loaded: sawBoot ? loaded : null,
|
|
37
37
|
msgs: msgs.slice(),
|
|
38
38
|
finalState,
|
|
@@ -157,13 +157,13 @@ function traceToJSONL(trace) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// src/internal/persistence/trace-replay/index.ts
|
|
160
|
-
function replayTrace(machine, trace,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
const actual =
|
|
160
|
+
function replayTrace(machine, trace, _ctx) {
|
|
161
|
+
if (trace.loaded === null) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"tea replayTrace: trace has no captured initial State; record boot before replaying."
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const actual = foldMsgs(machine, trace.loaded, trace.msgs);
|
|
167
167
|
const expected = trace.finalState;
|
|
168
168
|
const divergence = firstDivergence(expected, actual, "state");
|
|
169
169
|
if (divergence === null) {
|
|
@@ -204,5 +204,5 @@ function firstDivergence(expected, actual, path) {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
export { breadcrumbsFromTrace, deepEqual, parseJSONL, recorder, replayTrace, traceAttachment };
|
|
207
|
-
//# sourceMappingURL=chunk-
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
207
|
+
//# sourceMappingURL=chunk-ZRRET3RP.js.map
|
|
208
|
+
//# sourceMappingURL=chunk-ZRRET3RP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/recorder/index.ts","../src/internal/persistence/trace-replay/index.ts"],"names":[],"mappings":";;;;AAwKO,SAAS,QAAA,CACd,SACA,IAAA,EACgB;AAIhB,EAAA,MAAM,UAAU,IAAA,EAAM,UAAA;AACtB,EAAA,MAAM,UAAA,GACJ,OAAA,KAAY,MAAA,IAAa,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,IAAK,OAAA,IAAW,CAAA,GACzD,CAAA,GACA,IAAA,CAAK,GAAA,CAAI,SAAS,CAAC,CAAA;AACzB,EAAA,MAAM,YAAA,GAAe,MAAM,YAAA,IAAgB,KAAA;AAI3C,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,OAAA,GAAU,KAAA;AAGd,EAAA,MAAM,OAAY,EAAC;AAEnB,EAAA,MAAM,QAAgC,EAAC;AAKvC,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,OAAA,GAAU,KAAA;AAMd,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,CAAC,KAAA,KAAU;AACvC,IAAA,UAAA,GAAa,KAAA;AACb,IAAA,MAAA,GAAS,KAAA;AACT,IAAA,OAAA,GAAU,IAAA;AAAA,EACZ,CAAC,CAAA;AAED,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAK,KAAA,KAAU;AAGhD,IAAA,UAAA,GAAa,KAAA;AAKb,IAAA,IAAI,UAAA,GAAa,CAAA,IAAK,IAAA,CAAK,MAAA,MAAY,UAAA,EAAY;AAEnD,IAAA,IAAA,CAAK,KAAK,GAAG,CAAA;AACb,IAAA,IAAI,cAAc,KAAA,CAAM,IAAA,CAAK,EAAE,GAAA,EAAK,OAAO,CAAA;AAAA,EAC7C,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,IAAA,GAAoB;AAClB,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SAEF;AAAA,MACF;AACA,MAAA,OAAO;AAAA;AAAA;AAAA,QAGL,MAAA,EAAQ,UAAW,MAAA,GAAe,IAAA;AAAA,QAClC,IAAA,EAAM,KAAK,KAAA,EAAM;AAAA,QACjB,UAAA;AAAA;AAAA;AAAA,QAGA,GAAI,eAAe,EAAE,KAAA,EAAO,MAAM,KAAA,EAAM,KAAM;AAAC,OACjD;AAAA,IACF,CAAA;AAAA,IACA,OAAA,GAAkB;AAChB,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SAEF;AAAA,MACF;AAQA,MAAA,OAAO,YAAA,CAAa,IAAA,CAAK,IAAA,EAAM,CAAA;AAAA,IACjC,CAAA;AAAA,IACA,IAAA,GAAa;AACX,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,SAAA,EAAU;AACV,MAAA,MAAA,EAAO;AAAA,IACT;AAAA,GACF;AACF;AA4BO,SAAS,WAAiB,KAAA,EAA4B;AAC3D,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AACjC,EAAA,IAAI,MAAA,GAAmB,IAAA;AACvB,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,OAAY,EAAC;AACnB,EAAA,MAAM,QAAgC,EAAC;AACvC,EAAA,IAAI,UAAA,GAAuB,IAAA;AAC3B,EAAA,IAAI,UAAA,GAAa,KAAA;AAEjB,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,OAAO,CAAA,IAAK,QAAA,CAAS,SAAQ,EAAG;AAC7C,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,IAAI,SAAS,EAAA,EAAI;AAEjB,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IAC1B,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,2CAA2C,CAAA,GAAI,CAAC,CAAA,kBAAA,EAAqB,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,OAClF;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC1B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,0DAAA,EAA6D,IAAI,CAAC,CAAA,oCAAA;AAAA,SAEpE;AAAA,MACF;AACA,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,MAAA,GAAS,MAAA,CAAO,KAAA;AAGhB,MAAA,UAAA,GAAa,MAAA,CAAO,KAAA;AACpB,MAAA,UAAA,GAAa,OAAO,KAAA,KAAU,IAAA;AAAA,IAChC,CAAA,MAAA,IAAW,MAAA,CAAO,IAAA,KAAS,MAAA,EAAQ;AACjC,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,wEAAA,EAA2E,IAAI,CAAC,CAAA,CAAA;AAAA,SAClF;AAAA,MACF;AACA,MAAA,IAAA,CAAK,IAAA,CAAK,OAAO,GAAG,CAAA;AACpB,MAAA,IAAI,MAAA,CAAO,UAAU,IAAA,EAAM;AACzB,QAAA,KAAA,CAAM,IAAA,CAAK,EAAE,GAAA,EAAK,MAAA,CAAO,KAAK,KAAA,EAAO,MAAA,CAAO,OAAO,CAAA;AACnD,QAAA,UAAA,GAAa,MAAA,CAAO,KAAA;AACpB,QAAA,UAAA,GAAa,IAAA;AAAA,MACf;AAAA,IACF,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,6DAA6D,CAAA,GAAI,CAAC,OAChE,IAAA,CAAK,SAAA,CAAW,OAA8B,IAAI;AAAA,OACtD;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,IAAI,CAAC,UAAA,IAAc,UAAA,KAAe,IAAA,EAAM;AACtC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,UAAA,EAAY,KAAA,EAAM;AAC3C;AAyDO,SAAS,gBACd,KAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,iBAAA;AAAA,IACV,IAAA,EAAM,aAAa,KAAK,CAAA;AAAA,IACxB,WAAA,EAAa;AAAA,GACf;AACF;AA2DO,SAAS,oBAAA,CACd,OACA,IAAA,EACmB;AACnB,EAAA,MAAM,WAAW,IAAA,EAAM,KAAA;AAIvB,EAAA,MAAM,KAAA,GACJ,QAAA,KAAa,MAAA,IAAa,MAAA,CAAO,MAAM,QAAQ,CAAA,GAC3C,GAAA,GACA,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAC,CAAA;AACtC,EAAA,MAAM,YAAY,IAAA,EAAM,SAAA;AAMxB,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,KAAA,CAAM,IAAA,CAAK,SAAS,KAAK,CAAA;AACnD,EAAA,MAAM,IAAA,GAAO,UAAU,CAAA,GAAI,KAAA,CAAM,OAAO,KAAA,CAAM,IAAA,CAAK,MAAM,KAAK,CAAA;AAE9D,EAAA,MAAM,SAA4B,EAAC;AACnC,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,QAAA,EAAU,KAAA;AAAA,MACV,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO,MAAA;AAAA,MACP,OAAA,EAAS,SAAA,GAAY,SAAA,CAAU,GAAG,IAAI,GAAA,CAAI,IAAA;AAAA;AAAA;AAAA;AAAA,MAI1C,IAAA,EAAM,EAAE,GAAA;AAA+C,KACxD,CAAA;AAAA,EACH;AACA,EAAA,OAAO,MAAA;AACT;AAgBA,SAAS,aAAmB,KAAA,EAA4B;AACtD,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAO,KAAA,CAAM,MAAA,EAAQ,CAAC,CAAA;AAEhE,EAAA,MAAM,QAAQ,KAAA,CAAM,KAAA;AACpB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AAC1C,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA;AACxB,IAAA,MAAM,SAAA,GAAY,KAAA,GACd,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,GACV,CAAA,KAAM,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAA,GACxB,KAAA,CAAM,UAAA,GACN,IAAA;AACN,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,QAAQ,GAAA,EAAK,KAAA,EAAO,SAAA,EAAW,CAAC,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;;;AC1fO,SAAS,WAAA,CAOd,OAAA,EACA,KAAA,EACA,IAAA,EACiB;AACjB,EAAA,IAAI,KAAA,CAAM,WAAW,IAAA,EAAM;AACzB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,SAAS,QAAA,CAAS,OAAA,EAAS,KAAA,CAAM,MAAA,EAAQ,MAAM,IAAI,CAAA;AACzD,EAAA,MAAM,WAAW,KAAA,CAAM,UAAA;AAEvB,EAAA,MAAM,UAAA,GAAa,eAAA,CAAgB,QAAA,EAAU,MAAA,EAAQ,OAAO,CAAA;AAC5D,EAAA,IAAI,eAAe,IAAA,EAAM;AACvB,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,MAAA,EAAO;AAAA,EAC3C;AACA,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,QAAQ,UAAA,EAAW;AACxD;AAaO,SAAS,SAAA,CAAU,GAAY,CAAA,EAAqB;AACzD,EAAA,OAAO,eAAA,CAAgB,CAAA,EAAG,CAAA,EAAG,OAAO,CAAA,KAAM,IAAA;AAC5C;AA2BA,SAAS,eAAA,CACP,QAAA,EACA,MAAA,EACA,IAAA,EACY;AAIZ,EAAA,IAAI,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,MAAM,GAAG,OAAO,IAAA;AAExC,EAAA,MAAM,EAAA,GAAK,OAAO,QAAQ,CAAA;AAC1B,EAAA,MAAM,EAAA,GAAK,OAAO,MAAM,CAAA;AAGxB,EAAA,IAAI,OAAO,EAAA,EAAI,OAAO,EAAE,IAAA,EAAM,UAAU,MAAA,EAAO;AAE/C,EAAA,IAAI,OAAO,OAAA,EAAS;AAClB,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAA,GAAK,MAAA;AACX,IAAA,IAAI,EAAA,CAAG,MAAA,KAAW,EAAA,CAAG,MAAA,EAAQ;AAG3B,MAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,MAAA,EAAO;AAAA,IAClC;AACA,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,CAAG,QAAQ,CAAA,EAAA,EAAK;AAClC,MAAA,MAAM,CAAA,GAAI,eAAA,CAAgB,EAAA,CAAG,CAAC,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,EAAG,SAAA,CAAU,IAAA,EAAM,CAAC,CAAC,CAAA;AAC1D,MAAA,IAAI,CAAA,KAAM,MAAM,OAAO,CAAA;AAAA,IACzB;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,QAAA,EAAU;AACnB,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAA,GAAK,MAAA;AAIX,IAAA,KAAA,MAAW,GAAA,IAAO,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA,EAAG;AACnC,MAAA,MAAM,CAAA,GAAI,eAAA,CAAgB,EAAA,CAAG,GAAG,CAAA,EAAG,EAAA,CAAG,GAAG,CAAA,EAAG,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAC,CAAA;AAC9D,MAAA,IAAI,CAAA,KAAM,MAAM,OAAO,CAAA;AAAA,IACzB;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAIA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,MAAA,EAAO;AAClC","file":"chunk-ZRRET3RP.js","sourcesContent":["// Capture the post-init State and applied Msg sequence for update-only replay.\n// `loaded` is the replay anchor, not the raw value read from the Store.\n// `trace-replay` folds from it without init, handlers or host context.\n\nimport type { BootingRuntime } from \"../../../index\";\n\n/**\n * A recorded run, sufficient to reproduce it via `../trace-replay`.\n *\n * The recorder OWNS this type; `../trace-replay` imports it. It is the wire\n * contract between the two halves of the record/replay feature.\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport interface Trace<S, M> {\n /**\n * The post-init State captured by `onBoot`, used directly as the recorded\n * replay's starting State. No init call is repeated. `null` marks a missing\n * anchor in an older or incomplete trace; `replayTrace` refuses it.\n */\n readonly loaded: S | null;\n /** The ordered, non-null msgs as observed. The replay input sequence. */\n readonly msgs: readonly M[];\n /**\n * The last observed state. The oracle: `replayTrace` deep-compares its\n * recomputed final state against this. Equal ⇒ the reducer still produces\n * the same result for this input.\n */\n readonly finalState: S;\n /**\n * Per-transition `(msg, post-state)` pairs, in dispatch order, for human\n * inspection (e.g. \"show me the state after step 3\"). When present, there\n * is exactly one entry per recorded msg and `state` is the POST-transition\n * state for that msg (so `steps[i].msg === msgs[i]` and `steps.at(-1).state`\n * deep-equals `finalState`). NOT required for replay — replay only needs\n * `loaded` + `msgs` + `finalState`.\n *\n * Present iff the recorder was created with `captureSteps: true`, OR the\n * Trace was re-hydrated from JSONL via {@link parseJSONL} (the JSONL format\n * carries per-step state, so the parser populates `steps` whenever a step\n * line had a non-null state). A trace dumped with `captureSteps: false` has\n * `steps === undefined`.\n */\n readonly steps?: readonly { readonly msg: M; readonly state: S }[];\n}\n\n/** Options for {@link recorder}. */\nexport interface RecorderOptions {\n /**\n * Fraction of msgs to record, in `(0, 1]`. Default `1` (record everything).\n *\n * IMPORTANT — sampling breaks replay fidelity. A sampled trace DROPS msgs,\n * so its `msgs` array is no longer the complete input sequence and\n * `replayTrace` will NOT reproduce `finalState`. Sampling exists for\n * VOLUME STATISTICS (how much traffic, msg-type distribution), not for\n * faithful replay. Use the default `1` for any trace you intend to replay.\n *\n * The boot observe (`msg === null`) is ALWAYS recorded regardless of\n * `sampleRate` — `loaded` and `finalState` are anchors, not samples.\n */\n readonly sampleRate?: number;\n /**\n * When `true`, retain a per-transition `(msg, post-state)` pair in\n * {@link Trace.steps} for EVERY recorded transition. Default `false`.\n *\n * Size vs. fidelity — the precise trade-off:\n *\n * - `captureSteps: false` (default) — the MINIMAL replay-faithful trace.\n * The recorder keeps `loaded` (boot state), the ordered `msgs`, and\n * `finalState`. That is exactly what `../trace-replay` needs to refold\n * the run and diff the result. Memory is O(msgs) — one msg ref each, no\n * per-step state snapshot. `dump().steps` is `undefined`; in JSONL,\n * interior step lines carry `state: null` (the post-states were never\n * retained) and only the LAST step line carries `finalState`. A trace\n * dumped this way REPLAYS correctly but cannot answer \"what was the\n * state after step 3?\".\n *\n * - `captureSteps: true` — the FULL-fidelity trace. The recorder also\n * snapshots the post-transition state for every msg, so `dump().steps`\n * holds `{ msg, state }` for every transition (post-state) and\n * `toJSONL()` emits a non-null `state` on every step line. Memory is\n * O(msgs × |state|) — a state ref per transition (refs, not deep\n * copies; TEA states are conventionally immutable, so the runtime's\n * post-transition state object is retained by reference). This is the\n * mode for per-step inspection and for a JSONL file that round-trips\n * `steps` exactly via {@link parseJSONL}.\n *\n * Replay fidelity is identical in both modes — `../trace-replay` reads\n * `loaded` + `msgs` + `finalState` only and never touches `steps`. The\n * choice is purely \"do I also want to inspect intermediate states?\".\n */\n readonly captureSteps?: boolean;\n}\n\n/**\n * A live recorder attached to a Runtime. Mirrors `historyTracker`'s\n * stop/snapshot semantics: `dump()` works at any time (including after\n * `stop()`), `stop()` is idempotent and detaches the observer.\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport interface Recorder<S, M> {\n /**\n * Snapshot the recording so far as a {@link Trace}. Returns a fresh object\n * with copied arrays on every call (callers may slice/replay without\n * affecting the recorder's buffers). Entry values are references to the\n * originals (TEA states/msgs are conventionally immutable).\n *\n * Throws only if called before the boot observe has fired AND no msgs have\n * been recorded — i.e. there is no `finalState` yet. In practice callers\n * `await runtime.ready` before `dump()`, after which boot has fired.\n */\n dump(): Trace<S, M>;\n /**\n * Serialize the recording as JSONL (one JSON object per line) — a streamable\n * format that appends cleanly and survives truncation (a partial tail line\n * is the only loss). The first line is the boot header\n * `{ kind: \"boot\", state }`; each subsequent line is\n * `{ kind: \"step\", msg, state }` for one recorded transition.\n *\n * Re-hydrate with {@link parseJSONL}.\n *\n * Round-trip fidelity depends on `captureSteps`:\n * - `captureSteps: true` — every step line carries its non-null\n * post-state, so `parseJSONL(rec.toJSONL())` reconstructs `steps`\n * EXACTLY and deep-equals `rec.dump()` (which also has `steps`). Full\n * per-step fidelity round-trips.\n * - `captureSteps: false` (default) — interior step lines carry\n * `state: null` (those post-states were never retained); only the last\n * step line carries `finalState`. `parseJSONL` then populates `steps`\n * with just the entries that had a non-null state (the final one), so\n * the re-hydrated Trace is NOT deep-equal to `rec.dump()` (which has no\n * `steps`). It is still fully replay-faithful: `loaded`, `msgs`, and\n * `finalState` are intact. Record with `captureSteps: true` if you need\n * interior states to survive the round-trip.\n */\n toJSONL(): string;\n /**\n * Detach the underlying observer. After `stop()`:\n * - no new transitions are recorded;\n * - `dump()` / `toJSONL()` keep working against the buffered contents;\n * - the recorder holds no observer reference into the runtime.\n *\n * Idempotent — subsequent calls are no-ops.\n */\n stop(): void;\n}\n\n/**\n * Attach a recorder to a Runtime. Subscribes via `runtime.observe`, capturing\n * the boot state as `loaded` and every subsequent non-null msg in order.\n *\n * One-line creation, mirroring `historyTracker`:\n *\n * const rec = recorder(runtime);\n * // ... drive the runtime ...\n * await runtime.ready; // boot observe has fired\n * const trace = rec.dump(); // { loaded, msgs, finalState }\n *\n * @param runtime - any `BootingRuntime<S, M>` (a full `Runtime` satisfies it).\n * The recorder uses only `observe`, which is total before boot\n * — so you attach it to the synchronous `run()` handle to\n * capture the boot transition as `loaded`.\n * @param opts - see {@link RecorderOptions}. Default records everything,\n * no step retention.\n */\nexport function recorder<S, M extends { type: string }>(\n runtime: BootingRuntime<S, M>,\n opts?: RecorderOptions,\n): Recorder<S, M> {\n // Clamp sampleRate into (0, 1]. A non-positive or NaN rate would record\n // nothing (and break replay anchors); default it to 1 (record all). Rates\n // above 1 are meaningless — clamp to 1.\n const rawRate = opts?.sampleRate;\n const sampleRate =\n rawRate === undefined || Number.isNaN(rawRate) || rawRate <= 0\n ? 1\n : Math.min(rawRate, 1);\n const captureSteps = opts?.captureSteps ?? false;\n\n // `loaded` is the post-init state captured at the boot observe. `undefined`\n // sentinel = boot not yet seen; a trace without that anchor cannot replay.\n let loaded: S | undefined;\n let sawBoot = false;\n // The ordered, recorded msgs. Unbounded (the recorder's whole point vs the\n // bounded historyTracker).\n const msgs: M[] = [];\n // Per-step (msg, state) pairs — only populated when captureSteps is on.\n const steps: { msg: M; state: S }[] = [];\n // The last observed state — boot state until the first step, then advances\n // with every recorded OR dropped transition (finalState must reflect the\n // true latest state even under sampling, so it stays a valid replay oracle\n // for the unsampled case and an honest \"where the run ended\" marker).\n let finalState: S | undefined;\n let stopped = false;\n\n // The boot transition — the post-init initial state the old `observe(msg ===\n // null)` arm captured as `loaded` — now arrives via `onBoot` (#47). Always\n // captured (boot is the replay anchor). It is also the first `finalState`\n // until the first applied transition advances it.\n const unboot = runtime.onBoot((state) => {\n finalState = state;\n loaded = state;\n sawBoot = true;\n });\n\n const unobserve = runtime.observe((msg, state) => {\n // finalState tracks the latest state regardless of sampling — it is the\n // run's endpoint, an anchor, not a sample.\n finalState = state;\n\n // Sampling decision. sampleRate === 1 short-circuits to always-record so\n // the common (faithful) path pays no RNG cost. Math.random() < rate keeps\n // a `rate` fraction in expectation.\n if (sampleRate < 1 && Math.random() >= sampleRate) return;\n\n msgs.push(msg);\n if (captureSteps) steps.push({ msg, state });\n });\n\n return {\n dump(): Trace<S, M> {\n if (finalState === undefined) {\n throw new Error(\n \"tea recorder: dump() called before any transition was observed. \" +\n \"Await `runtime.ready` (boot fires the first observe) before dumping.\",\n );\n }\n return {\n // sawBoot ⇒ `loaded` is the captured boot state; otherwise the\n // trace has no replay anchor and replayTrace will refuse it.\n loaded: sawBoot ? (loaded as S) : null,\n msgs: msgs.slice(),\n finalState,\n // Only attach `steps` when captureSteps was requested — keeps the\n // dumped Trace shape honest about what was retained.\n ...(captureSteps ? { steps: steps.slice() } : {}),\n };\n },\n toJSONL(): string {\n if (finalState === undefined) {\n throw new Error(\n \"tea recorder: toJSONL() called before any transition was observed. \" +\n \"Await `runtime.ready` (boot fires the first observe) before serializing.\",\n );\n }\n // Serialize the live snapshot via the shared Trace→JSONL writer so the\n // recorder and the standalone `traceAttachment` path emit byte-identical\n // wire format. `dump()` resolves the captureSteps logic (steps present\n // iff captureSteps was on); `traceToJSONL` then emits a non-null state\n // on every step line when steps exist, or `null` interior + finalState\n // on the last line when they don't. The replay path never reads step\n // states, so the captureSteps:false null-interiors never affect replay.\n return traceToJSONL(this.dump());\n },\n stop(): void {\n if (stopped) return;\n stopped = true;\n unobserve();\n unboot();\n },\n };\n}\n\n/** One line of the JSONL format produced by {@link Recorder.toJSONL}. */\ntype JsonlLine<S, M> =\n | { readonly kind: \"boot\"; readonly state: S | null }\n | { readonly kind: \"step\"; readonly msg: M; readonly state: S | null };\n\n/**\n * Re-hydrate a {@link Trace} from the JSONL produced by\n * {@link Recorder.toJSONL}. The inverse of `toJSONL`, so\n * `parseJSONL(rec.toJSONL())` deep-equals `rec.dump()`.\n *\n * Parsing rules:\n * - The first non-blank line MUST be the boot header `{ kind: \"boot\", state }`.\n * Its `state` becomes `Trace.loaded`.\n * - Each `{ kind: \"step\", msg, state }` line contributes `msg` to\n * `Trace.msgs` and `(msg, state)` to `Trace.steps`.\n * - `Trace.finalState` is the `state` of the LAST step line, or the boot\n * `state` when there are no steps (a boot-only trace).\n *\n * Blank lines are skipped (tolerant of trailing newlines / hand-edited files).\n * A malformed line, a missing/duplicate boot header, or a step with `null`\n * final state throws — the trace is unusable for replay and silent corruption\n * is worse than a loud parse error (errors are data; surface them).\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport function parseJSONL<S, M>(jsonl: string): Trace<S, M> {\n const rawLines = jsonl.split(\"\\n\");\n let loaded: S | null = null;\n let sawBoot = false;\n const msgs: M[] = [];\n const steps: { msg: M; state: S }[] = [];\n let finalState: S | null = null;\n let finalKnown = false;\n\n for (const [i, rawLine] of rawLines.entries()) {\n const line = rawLine.trim();\n if (line === \"\") continue; // tolerate blank / trailing-newline lines\n\n let parsed: JsonlLine<S, M>;\n try {\n parsed = JSON.parse(line) as JsonlLine<S, M>;\n } catch (err) {\n throw new Error(\n `tea recorder: parseJSONL failed on line ${i + 1}: not valid JSON. ${String(err)}`,\n );\n }\n\n if (parsed.kind === \"boot\") {\n if (sawBoot) {\n throw new Error(\n `tea recorder: parseJSONL saw a second boot header on line ${i + 1}. ` +\n \"A trace has exactly one boot line.\",\n );\n }\n sawBoot = true;\n loaded = parsed.state;\n // The boot state is the finalState until a step supersedes it (boot-only\n // trace case).\n finalState = parsed.state;\n finalKnown = parsed.state !== null;\n } else if (parsed.kind === \"step\") {\n if (!sawBoot) {\n throw new Error(\n `tea recorder: parseJSONL saw a step line before the boot header on line ${i + 1}.`,\n );\n }\n msgs.push(parsed.msg);\n if (parsed.state !== null) {\n steps.push({ msg: parsed.msg, state: parsed.state });\n finalState = parsed.state;\n finalKnown = true;\n }\n } else {\n throw new Error(\n `tea recorder: parseJSONL saw an unknown line kind on line ${i + 1}: ` +\n JSON.stringify((parsed as { kind?: unknown }).kind),\n );\n }\n }\n\n if (!sawBoot) {\n throw new Error(\n \"tea recorder: parseJSONL found no boot header. A trace must start with \" +\n '{ kind: \"boot\", state }.',\n );\n }\n if (!finalKnown || finalState === null) {\n throw new Error(\n \"tea recorder: parseJSONL could not determine finalState — the boot state \" +\n \"was null and no step carried a non-null state. The trace is unusable for replay.\",\n );\n }\n\n return { loaded, msgs, finalState, steps };\n}\n\n// ---------------------------------------------------------------------------\n// Sentry crash→trace adapters — pure, zero-dependency, Sentry-SHAPED.\n//\n// The killer DevX flow: a machine crashes in prod → its recorded Trace rides\n// along on the Sentry event → one click later you `parseJSONL` it and replay\n// the exact run locally against the (possibly fixed) reducer. These two\n// adapters are the bridge from a `Trace` to the two Sentry surfaces that carry\n// it: an Attachment (the full replayable trace) and Breadcrumbs (a human-\n// readable timeline of the msgs that led to the crash).\n//\n// They take NO dependency on `@sentry/*`. They emit PLAIN OBJECTS whose shapes\n// match Sentry's public `Attachment` and `Breadcrumb` types, and the consumer\n// wires them at their own Sentry boundary (browser or node). This keeps the\n// recorder runnable anywhere a machine runs (a Worker, a Node script, a test)\n// and lets the consumer pin whatever Sentry SDK version they already ship.\n// ---------------------------------------------------------------------------\n\n/**\n * A Sentry-shaped attachment carrying the full {@link Trace} as JSONL. The\n * shape matches Sentry's `Attachment` type so the consumer can hand it\n * straight to `addAttachment(...)` without any adaptation.\n *\n * - `filename` — `\"tea-trace.jsonl\"`. The name Sentry shows in the UI and\n * uses for download; `.jsonl` signals the streamable format.\n * - `data` — the trace serialized via {@link Recorder.toJSONL}'s format\n * (re-hydrate downstream with {@link parseJSONL}).\n * - `contentType` — `\"application/x-ndjson\"`, the standard MIME for\n * newline-delimited JSON.\n */\nexport interface TraceAttachment {\n readonly filename: string;\n readonly data: string;\n readonly contentType: string;\n}\n\n/**\n * Serialize a {@link Trace} into a Sentry-shaped attachment so a crash event\n * carries the full, replayable run. Pure — no Sentry import, no I/O. The\n * returned object matches Sentry's `Attachment` shape exactly.\n *\n * The `data` field is JSONL in the same format {@link Recorder.toJSONL}\n * produces, so the home-side flow is `parseJSONL(attachment.data)` →\n * `replayTrace(machine, trace, ctx)`.\n *\n * Consumer wiring (no Sentry import here — the consumer owns the boundary):\n *\n * ```ts\n * import * as Sentry from \"@sentry/browser\"; // or \"@sentry/node\"\n * import { traceAttachment } from \"./internal/persistence/recorder\"; // internal module\n * Sentry.getCurrentScope().addAttachment(traceAttachment(rec.dump()));\n * ```\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport function traceAttachment<S, M extends { type: string }>(\n trace: Trace<S, M>,\n): TraceAttachment {\n return {\n filename: \"tea-trace.jsonl\",\n data: traceToJSONL(trace),\n contentType: \"application/x-ndjson\",\n };\n}\n\n/** A Sentry-shaped breadcrumb. Matches the fields of Sentry's `Breadcrumb`. */\nexport interface TraceBreadcrumb {\n readonly category: string;\n readonly type: string;\n readonly level: string;\n readonly message: string;\n readonly data?: Record<string, unknown>;\n}\n\n/** Options for {@link breadcrumbsFromTrace}. */\nexport interface BreadcrumbsOptions<M> {\n /**\n * Max breadcrumbs to emit. Default `100` (Sentry's own default breadcrumb\n * cap). When the trace has more msgs than `limit`, the OLDEST are dropped —\n * the returned list is the newest-last tail, matching how a Sentry\n * breadcrumb ring buffer behaves (the crash context is the recent past).\n */\n readonly limit?: number;\n /**\n * Customize each breadcrumb's `message`. Default uses `msg.type`. Provide a\n * serializer to surface a richer one-liner (e.g. include an id). The msg is\n * ALSO always attached verbatim under `data.msg` regardless of this option,\n * so the structured payload is never lost to the string rendering.\n */\n readonly serialize?: (msg: M) => string;\n}\n\n/**\n * Project a {@link Trace}'s recorded msgs into Sentry-shaped breadcrumbs — a\n * human-readable timeline of \"what happened before the crash\". Pure — no\n * Sentry import, no I/O. Each returned object matches Sentry's `Breadcrumb`\n * shape, so the consumer can loop and call `addBreadcrumb(...)`.\n *\n * One breadcrumb per recorded msg, in dispatch order (NEWEST LAST — Sentry\n * renders breadcrumbs oldest-first and the crash is the most recent event).\n * Each breadcrumb:\n * - `category` — `\"tea\"` (groups them in the Sentry breadcrumb panel).\n * - `type` — `\"default\"` (the generic Sentry breadcrumb type).\n * - `level` — `\"info\"` (msgs are normal flow; the crash is the event).\n * - `message` — `msg.type`, or `serialize(msg)` when provided.\n * - `data.msg` — the full msg object (structured detail kept verbatim).\n *\n * Capped at `opts.limit` (default 100). When the trace has more msgs than the\n * cap, the OLDEST are dropped and the newest `limit` are kept — the recent\n * lead-up to the crash is the useful context.\n *\n * Consumer wiring (no Sentry import here — the consumer owns the boundary):\n *\n * ```ts\n * import * as Sentry from \"@sentry/browser\"; // or \"@sentry/node\"\n * import { breadcrumbsFromTrace } from \"./internal/persistence/recorder\"; // internal module\n * for (const b of breadcrumbsFromTrace(rec.dump())) Sentry.addBreadcrumb(b);\n * ```\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport function breadcrumbsFromTrace<S, M extends { type: string }>(\n trace: Trace<S, M>,\n opts?: BreadcrumbsOptions<M>,\n): TraceBreadcrumb[] {\n const rawLimit = opts?.limit;\n // Clamp limit to a non-negative integer. `undefined`/NaN → 100 (Sentry's\n // default). A negative or zero limit yields an empty list (the caller asked\n // for no breadcrumbs); fractional values floor (you can't emit half a crumb).\n const limit =\n rawLimit === undefined || Number.isNaN(rawLimit)\n ? 100\n : Math.max(0, Math.floor(rawLimit));\n const serialize = opts?.serialize;\n\n // Keep the NEWEST `limit` msgs (the recent lead-up to the crash). Slicing\n // from `length - limit` (clamped to 0) preserves dispatch order within the\n // kept window. Note we do NOT use `slice(-limit)`: `slice(-0)` is `slice(0)`\n // (JS treats -0 as 0), which would return the WHOLE array for `limit: 0`.\n const start = Math.max(0, trace.msgs.length - limit);\n const kept = start === 0 ? trace.msgs : trace.msgs.slice(start);\n\n const crumbs: TraceBreadcrumb[] = [];\n for (const msg of kept) {\n crumbs.push({\n category: \"tea\",\n type: \"default\",\n level: \"info\",\n message: serialize ? serialize(msg) : msg.type,\n // Full msg kept verbatim so the structured payload survives even when\n // `message` is a terse one-liner. Cast: a Msg is a `{ type: string }`\n // record, which is a `Record<string, unknown>` for Sentry's `data`.\n data: { msg: msg as unknown as Record<string, unknown> },\n });\n }\n return crumbs;\n}\n\n// ---------------------------------------------------------------------------\n// traceToJSONL — serialize a STANDALONE Trace to JSONL (the dump→file path).\n//\n// `Recorder.toJSONL()` serializes the recorder's live buffers. `traceAttachment`\n// works over a plain `Trace` value (a `dump()` snapshot or a `parseJSONL`\n// result), which has no recorder behind it — so it needs a Trace→JSONL\n// function. This is the same wire format `Recorder.toJSONL` emits and\n// `parseJSONL` reads, so `parseJSONL(traceToJSONL(trace))` round-trips.\n//\n// When `trace.steps` is present (captureSteps was on, or the trace came from\n// parseJSONL), every step line carries its post-state. When absent, interior\n// step states are unknown — emitted as `null`, with `finalState` on the LAST\n// step line (identical to the captureSteps:false path in `Recorder.toJSONL`).\n// ---------------------------------------------------------------------------\nfunction traceToJSONL<S, M>(trace: Trace<S, M>): string {\n const lines: string[] = [];\n lines.push(JSON.stringify({ kind: \"boot\", state: trace.loaded }));\n\n const steps = trace.steps;\n for (let i = 0; i < trace.msgs.length; i++) {\n const msg = trace.msgs[i];\n const stepState = steps\n ? steps[i]?.state\n : i === trace.msgs.length - 1\n ? trace.finalState\n : null;\n lines.push(JSON.stringify({ kind: \"step\", msg, state: stepState }));\n }\n return lines.join(\"\\n\");\n}\n","// Recorded replay folds captured post-init State and reports the first divergence.\n\nimport { type Cmd, foldMsgs, type Machine, type Sub } from \"../../../index\";\nimport type { Trace } from \"../recorder\";\nimport { indexPath, keyPath, typeOf, unionKeys } from \"./state-walk\";\n\n/**\n * The outcome of {@link replayTrace}.\n *\n * @typeParam S - the machine's State type.\n */\nexport interface ReplayResult<S> {\n /** `true` iff the recomputed final state deep-equals the trace's `finalState`. */\n matches: boolean;\n /** The trace's recorded `finalState` — what the run originally produced. */\n expected: S;\n /** The state the current reducer produced for the same input. */\n actual: S;\n /**\n * Present iff `matches` is `false`. The FIRST point of divergence found by\n * a deterministic pre-order walk:\n * - `path` — a dotted/indexed access path from the state root, e.g.\n * `state.queue[2].status` or `state` for a root-level\n * scalar mismatch.\n * - `expected` — the recorded value at that path.\n * - `actual` — the recomputed value at that path.\n */\n divergence?: { path: string; expected: unknown; actual: unknown };\n}\n\n/**\n * Replay a recorded {@link Trace} against `machine` and diff the recomputed\n * final state against the recorded one.\n *\n * Folds recorded messages from the captured post-init State, without calling\n * init, Cmd handlers, Store operations or Sub runners. A trace without that\n * anchor is refused; a fresh init cannot reconstruct its starting State.\n *\n * @param machine - the machine to replay against. Mutating its reducer (the\n * whole point of a regression check) is what makes a recorded\n * trace diverge.\n * @param trace - a Trace from `../recorder` (live `dump()` or a re-hydrated\n * `parseJSONL(...)`).\n * @param _ctx - optional legacy argument; recorded replay needs no init context.\n */\nexport function replayTrace<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n trace: Trace<S, M>,\n _ctx?: Ctx,\n): ReplayResult<S> {\n if (trace.loaded === null) {\n throw new Error(\n \"tea replayTrace: trace has no captured initial State; record boot before replaying.\",\n );\n }\n const actual = foldMsgs(machine, trace.loaded, trace.msgs);\n const expected = trace.finalState;\n\n const divergence = firstDivergence(expected, actual, \"state\");\n if (divergence === null) {\n return { matches: true, expected, actual };\n }\n return { matches: false, expected, actual, divergence };\n}\n\n/**\n * Order-insensitive structural deep-equality — the comparator the\n * record/replay lane owns, exposed so consumers (e.g. `@demlik/tea/parity`'s\n * `parityEqual`) reuse it rather than adding a third deep-compare. It is one\n * of two structural walkers in the package, divergent on purpose:\n * this `deepEqual` returns a boolean verdict (order-insensitive, for\n * replay/parity equality), and `devtools`'s `diffState` returns a change list\n * (for a human-readable diff view) — same shape, two jobs, not one to collapse.\n * `true` iff `a` and `b` are structurally equal under {@link firstDivergence}'s\n * walk (object keys compared order-insensitively, arrays index-by-index).\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n return firstDivergence(a, b, \"state\") === null;\n}\n\n// === deep-equal + first-divergence path finder ===\n//\n// One walk does both jobs: it returns the FIRST point where `expected` and\n// `actual` differ (as a path + the two values), or `null` when they are\n// deeply equal. \"First\" is defined by a deterministic pre-order traversal:\n// the node itself, then its children in a stable key order (array index\n// order; object keys sorted so the result does not depend on insertion\n// order — two states with the same data but different key order are equal,\n// and the reported path for a real mismatch is reproducible run-to-run).\n//\n// The path grammar (`typeOf`, `indexPath`/`keyPath` segments, `unionKeys`\n// order) is shared with devtools' `diffState` via `./state-walk`, so the two\n// walkers print the SAME path for the same cell by construction. Only the leaf\n// policy differs, on purpose: first-hit here, collect-all there.\n//\n// Scope: TEA state is plain JSON-shaped data (the substrate's __DEV__ guards\n// reject closures in Cmds, and states are conventionally serializable). So\n// the walk handles: primitives, null/undefined, arrays, and plain objects.\n// It treats other object kinds (Date, Map, Set, class instances) by reference\n// identity + a structural fallback via Object.entries — sufficient for the\n// JSON-shaped states this tool targets; a richer differ lives in ../devtools.\n\n/** A divergence record, or `null` when the two values are deeply equal. */\ntype Divergence = { path: string; expected: unknown; actual: unknown } | null;\n\nfunction firstDivergence(\n expected: unknown,\n actual: unknown,\n path: string,\n): Divergence {\n // Identity / primitive fast path. Handles same-reference, and equal\n // primitives. `Object.is` so NaN === NaN and +0 ≠ -0 (matches structural\n // equality intent better than ===).\n if (Object.is(expected, actual)) return null;\n\n const te = typeOf(expected);\n const ta = typeOf(actual);\n // Different kinds at this node (e.g. object vs array, number vs string,\n // present vs null) — this node IS the divergence.\n if (te !== ta) return { path, expected, actual };\n\n if (te === \"array\") {\n const ae = expected as unknown[];\n const aa = actual as unknown[];\n if (ae.length !== aa.length) {\n // Length mismatch reported at the array node itself — the shape differs,\n // which is more informative than an arbitrary element index.\n return { path, expected, actual };\n }\n for (let i = 0; i < ae.length; i++) {\n const d = firstDivergence(ae[i], aa[i], indexPath(path, i));\n if (d !== null) return d;\n }\n return null;\n }\n\n if (te === \"object\") {\n const oe = expected as Record<string, unknown>;\n const oa = actual as Record<string, unknown>;\n // Sorted union of keys for deterministic traversal. A key present in one\n // but absent in the other diverges at that child path (its value is\n // `undefined` on the missing side).\n for (const key of unionKeys(oe, oa)) {\n const d = firstDivergence(oe[key], oa[key], keyPath(path, key));\n if (d !== null) return d;\n }\n return null;\n }\n\n // Primitives (string/number/boolean/bigint/symbol/function) that failed the\n // Object.is fast path, or null/undefined that differ — this node diverges.\n return { path, expected, actual };\n}\n"]}
|
|
@@ -11,7 +11,7 @@ import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
|
11
11
|
* **Dependency direction (the actual decoupling):** this module imports
|
|
12
12
|
* NOTHING from the runtime — no `run`/host/`Store`. The
|
|
13
13
|
* runtime (`run`, the host, interpret, `Store`, subscribe — all in
|
|
14
|
-
* `../index.ts`) imports *from* here; never the reverse. The root
|
|
14
|
+
* `../index.ts`) imports *from* here; never the reverse. The root entry
|
|
15
15
|
* re-exports this surface through `./index.ts`, and
|
|
16
16
|
* `pure/import-graph.test.ts` is the regression fence asserting the pure
|
|
17
17
|
* entrypoint's import graph never reaches `run`.
|
|
@@ -712,17 +712,14 @@ type Machine<S, M extends {
|
|
|
712
712
|
* Boot the runtime. Called once by `run(...)` with whatever `Store.load()`
|
|
713
713
|
* returned: `null` on fresh boot, the persisted state on rehydrate.
|
|
714
714
|
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
*
|
|
715
|
+
* Both fresh and restored init may return a new State and Cmds. Init is
|
|
716
|
+
* pure: it describes effects; handlers perform them. The engine saves the
|
|
717
|
+
* returned State, reconciles its Subs, then runs its Cmds.
|
|
718
718
|
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
* Violations are caught at runtime by `replay` (which throws with a
|
|
725
|
-
* pointer to the alternatives).
|
|
719
|
+
* Put state-conditional resume work here, with the in-flight phase in the
|
|
720
|
+
* returned State so a fast Sub cannot start it again. A host Msg sent after
|
|
721
|
+
* `ready` runs after Subs have opened and supplies no such ordering.
|
|
722
|
+
* Recorded replay starts from captured post-init State without calling init.
|
|
726
723
|
*/
|
|
727
724
|
init: (loaded: S | null, ctx: Ctx) => readonly [S, readonly C[]];
|
|
728
725
|
/**
|
|
@@ -808,4 +805,4 @@ declare function foldMsgs<S, M extends {
|
|
|
808
805
|
type: string;
|
|
809
806
|
}, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, base: S, msgs: readonly M[]): S;
|
|
810
807
|
|
|
811
|
-
export { applyCell as $, type AnyCmdDef as A, type BuiltinSub as B, Cmd as C, type Dispose as D, type ErrorsOf as E, type InterpretCell as F, type MachineShape as G, type HandlerCtx as H, type Interpret as I, type NoCtx as J, OutcomeContractError as K, type OutcomeHelpers as L, type Machine as M, NoCellError as N, Outcome as O, type Port as P, type PortEmitter as Q, type RunHandlers as R, type
|
|
808
|
+
export { applyCell as $, type AnyCmdDef as A, type BuiltinSub as B, Cmd as C, type Dispose as D, type ErrorsOf as E, type InterpretCell as F, type MachineShape as G, type HandlerCtx as H, type Interpret as I, type NoCtx as J, OutcomeContractError as K, type OutcomeHelpers as L, type Machine as M, NoCellError as N, Outcome as O, type Port as P, type PortEmitter as Q, type RunHandlers as R, type Subscribe as S, type TimerDeps as T, type UpdateForm as U, type SubscribeArg as V, type Wired as W, type SyncReturn as X, UndeclaredFailureError as Y, acceptedTypes as Z, acceptsOf as _, type CmdOf as a, applyCellChecked as a0, describeMachine as a1, detectUpdateForm as a2, foldMsgs as a3, foldUpdates as a4, formOf as a5, msgKeysOf as a6, structuralHash as a7, subId as a8, subIdOf as a9, type CmdDef as b, type Sub as c, type SettledOk as d, type SettledErr as e, type TaggedError as f, type Reducer as g, type Settled as h, type Transitions as i, type InterpretDetached as j, type CmdInput as k, type SubId as l, type DepKeyedSub as m, type TimerSub as n, type MalformedResult as o, type OkOfCmd as p, type DeclaredErrorsOf as q, type BuiltinSubType as r, type Tagged as s, type OkOf as t, type ErrOf as u, type CmdValue as v, AsyncSchemaError as w, type ExhaustiveTransitions as x, type Identity as y, type InterpretArg as z };
|
package/dist/devtools/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
|
|
3
3
|
/** One row of the `MsgLog`: a timestamp, a category and the text to show. */
|
|
4
4
|
type MsgLogEntry = {
|
|
@@ -17,7 +17,7 @@ interface MsgLogProps {
|
|
|
17
17
|
/**
|
|
18
18
|
* React component that renders a list of `MsgLogEntry` rows as a message log.
|
|
19
19
|
*/
|
|
20
|
-
declare function MsgLog({ history, className }: MsgLogProps):
|
|
20
|
+
declare function MsgLog({ history, className }: MsgLogProps): react.JSX.Element;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* A single differing leaf value between two states.
|
|
@@ -102,7 +102,7 @@ interface StateDiffProps {
|
|
|
102
102
|
* When the states are deeply equal the list is empty; a tiny "no changes" hint
|
|
103
103
|
* renders so an operator can tell "equal" from "not yet computed".
|
|
104
104
|
*/
|
|
105
|
-
declare function StateDiff({ expected, actual, className }: StateDiffProps):
|
|
105
|
+
declare function StateDiff({ expected, actual, className }: StateDiffProps): react.JSX.Element;
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* Props for `StateInspector`: the `state` to show, an optional `flashKey`, and
|
|
@@ -128,7 +128,7 @@ interface StateInspectorProps {
|
|
|
128
128
|
* React component that renders a state value as formatted JSON, and flashes
|
|
129
129
|
* when `flashKey` changes.
|
|
130
130
|
*/
|
|
131
|
-
declare function StateInspector({ state, flashKey, className, }: StateInspectorProps):
|
|
131
|
+
declare function StateInspector({ state, flashKey, className, }: StateInspectorProps): react.JSX.Element;
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
134
|
* Wrap a `dispatch` so every Msg it sees lands in a bounded history buffer.
|
package/dist/do/index.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { P as PendingEffectsRecorder, b as EffectOwed, a as EffectConfirmed } from '../durable-effects-
|
|
3
|
-
export { D as DeliveryId, E as EffectLedgerEvent, O as OwedEffect, c as PendingEffectsLedger, d as applyEffectEvent, e as emptyLedger, f as foldLedger, i as isOwed, p as pendingEffectsLedger, s as survivingEffects } from '../durable-effects-
|
|
1
|
+
import { b as Store, a as BootedRunHandle, E as EngineRun, S as StoreAndOnError, e as RunHandle, M as Migrated, F as FencedStore, D as DeletableStore } from '../runtime-types-CP-ykwgf.js';
|
|
2
|
+
import { P as PendingEffectsRecorder, b as EffectOwed, a as EffectConfirmed } from '../durable-effects-wLWEdy9H.js';
|
|
3
|
+
export { D as DeliveryId, E as EffectLedgerEvent, O as OwedEffect, c as PendingEffectsLedger, d as applyEffectEvent, e as emptyLedger, f as foldLedger, i as isOwed, p as pendingEffectsLedger, s as survivingEffects } from '../durable-effects-wLWEdy9H.js';
|
|
4
4
|
import { RetryPolicy, Rng } from '../retry-backoff/index.js';
|
|
5
|
-
import { C as Cmd,
|
|
6
|
-
import { A as AgentEvent, f as AgentTurn, e as AgentState, i as AgentMachineMsg, K as AgentStatus } from '../machine-
|
|
5
|
+
import { C as Cmd, c as Sub, M as Machine, W as Wired } from '../core-DNDAEhO2.js';
|
|
6
|
+
import { A as AgentEvent, f as AgentTurn, e as AgentState, i as AgentMachineMsg, K as AgentStatus } from '../machine-C0WhOAyB.js';
|
|
7
7
|
import '@standard-schema/spec';
|
|
8
|
-
import '../index-
|
|
9
|
-
import '../index-
|
|
10
|
-
import '../index-
|
|
11
|
-
import '../types-
|
|
12
|
-
import '../index-
|
|
13
|
-
import '../index-
|
|
8
|
+
import '../index-CAsUE_d1.js';
|
|
9
|
+
import '../index-COeX9Bmz.js';
|
|
10
|
+
import '../index-Cg0QXnzf.js';
|
|
11
|
+
import '../types-S53WDy_h.js';
|
|
12
|
+
import '../index-BBLI2WWN.js';
|
|
13
|
+
import '../index-C92SgNWz.js';
|
|
14
14
|
import '../protocol-ClRZBhHQ.js';
|
|
15
|
-
import '../index-
|
|
15
|
+
import '../index-9P1PaXIV.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @demlik/tea/do — `stepHost`, the HTTP-PULL control carrier (#92).
|
|
@@ -344,14 +344,48 @@ declare function stepHost<R, I, O>(ctx: StepCtx, engine: StepEngine<R, I, O>, co
|
|
|
344
344
|
type StepTransport<R, I, O> = (request: StepRequest<R>) => Promise<DeferredStepResponse<I, O>>;
|
|
345
345
|
/** Execute one tool call and produce its result (the consumer's hands). */
|
|
346
346
|
type ExecuteStep<I, R> = (step: NextStep<I>) => Promise<R> | R;
|
|
347
|
-
/**
|
|
348
|
-
|
|
347
|
+
/**
|
|
348
|
+
* What {@link ExecutedSteps} holds for a step that ran. The box is what tells a
|
|
349
|
+
* step whose result is `undefined` apart from a step that never ran, and it
|
|
350
|
+
* still does after a JSON round trip (`{}` is a box; a missing entry is not).
|
|
351
|
+
*/
|
|
352
|
+
interface ExecutedStep<R> {
|
|
353
|
+
readonly result: R;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The hands' memory of the steps they already ran, keyed by `callId`. Pass one
|
|
357
|
+
* that outlives the process and a restarted {@link runStepLoop} re-POSTs a saved
|
|
358
|
+
* result instead of running the step again.
|
|
359
|
+
*
|
|
360
|
+
* Use one store per run: a host's `callId` only has to be unique inside its run.
|
|
361
|
+
* The caller owns where it lives and when it is cleared.
|
|
362
|
+
*/
|
|
363
|
+
interface ExecutedSteps<R> {
|
|
364
|
+
/** The box saved for `callId`, or `undefined` if that step never ran. */
|
|
365
|
+
get(callId: string): Promise<ExecutedStep<R> | undefined> | ExecutedStep<R> | undefined;
|
|
366
|
+
/** Save the box for `callId`. Settled before the result is POSTed. */
|
|
367
|
+
set(callId: string, step: ExecutedStep<R>): Promise<void> | void;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Tuning for {@link runStepLoop}.
|
|
371
|
+
*
|
|
372
|
+
* `R` is the step result type, and only {@link RunStepLoopConfig.executed} reads
|
|
373
|
+
* it. It defaults to `never` because a bare `RunStepLoopConfig` says nothing
|
|
374
|
+
* about results: it has to fit a drive of any `R`, and a store of `unknown`
|
|
375
|
+
* could not hand a drive its `R` back.
|
|
376
|
+
*/
|
|
377
|
+
interface RunStepLoopConfig<R = never> {
|
|
349
378
|
/**
|
|
350
379
|
* Absolute wall-clock deadline (ms epoch). The loop stops asking once `now()`
|
|
351
380
|
* reaches it, surfacing a `deadline_exceeded` outcome. Mirrors the poller's
|
|
352
381
|
* absolute-deadline contract (a resumed loop honors the SAME moment).
|
|
353
382
|
*/
|
|
354
383
|
readonly deadlineMs: number;
|
|
384
|
+
/**
|
|
385
|
+
* Where the hands remember the steps they ran. Defaults to a fresh in-memory
|
|
386
|
+
* store per drive, which a restart forgets. See {@link runStepLoop}.
|
|
387
|
+
*/
|
|
388
|
+
readonly executed?: ExecutedSteps<R>;
|
|
355
389
|
/** Backoff policy for FAILED `/step` POSTs. Defaults to `defaultRetryPolicy`. */
|
|
356
390
|
readonly retry?: RetryPolicy;
|
|
357
391
|
/** Clock, injected for determinism. Defaults to `Date.now`. */
|
|
@@ -384,17 +418,29 @@ type StepLoopOutcome<O> = {
|
|
|
384
418
|
* - **backoff** — a FAILED POST (transport reject) is retried on the
|
|
385
419
|
* `retry-backoff` curve; once `shouldRetry` is exhausted the loop stops
|
|
386
420
|
* with `gave_up`. A success resets the retry counter.
|
|
387
|
-
* - **dedupe** — a step whose `callId`
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
421
|
+
* - **dedupe** — a step whose `callId` is already in `config.executed` is not
|
|
422
|
+
* re-executed; its saved result is re-POSTed. Closes the "the host re-handed
|
|
423
|
+
* the same step after a duplicate settle" path (the host side is idempotent;
|
|
424
|
+
* the hands match it). That memory lives in `config.executed`. Left out, it
|
|
425
|
+
* is a fresh in-memory store that lasts one drive, so hands that restart
|
|
426
|
+
* forget it and run the re-handed step again; pass a store that outlives the
|
|
427
|
+
* process to keep it across restarts.
|
|
428
|
+
* - **store failure** — a `get` or `set` that throws or rejects ends the drive
|
|
429
|
+
* with `gave_up` carrying the store's error. A failed `get` runs nothing. A
|
|
430
|
+
* failed `set` means the step ran but is not remembered, and its result is
|
|
431
|
+
* not POSTed, so a later drive over that store would run it again.
|
|
391
432
|
* - **not-ready re-poll** — a {@link StepWorking} arm (a defer-resume host
|
|
392
433
|
* computing the step out-of-band) is not a step: the loop re-POSTs the SAME
|
|
393
434
|
* `posted` after a backoff (honoring the host's advisory `retryAfterMs`),
|
|
394
435
|
* until a real step / done arrives. Bounded only by the absolute deadline —
|
|
395
436
|
* an inline host never returns this arm, so its drive is unchanged.
|
|
437
|
+
*
|
|
438
|
+
* **Crash window:** a store gives "run once per `callId`" except between
|
|
439
|
+
* `execute` finishing and `set` landing. Hands that die there have run the step
|
|
440
|
+
* with no record of it, and the next drive runs it again. No store can close
|
|
441
|
+
* that: the step itself has to be safe to repeat, as with `idempotentEffect`.
|
|
396
442
|
*/
|
|
397
|
-
declare function runStepLoop<R, I, O>(token: string, runId: string, transport: StepTransport<R, I, O>, execute: ExecuteStep<I, R>, config: RunStepLoopConfig): Promise<StepLoopOutcome<O>>;
|
|
443
|
+
declare function runStepLoop<R, I, O>(token: string, runId: string, transport: StepTransport<R, I, O>, execute: ExecuteStep<I, R>, config: RunStepLoopConfig<R>): Promise<StepLoopOutcome<O>>;
|
|
398
444
|
|
|
399
445
|
/**
|
|
400
446
|
* `@demlik/tea/do` — the durable-timer / alarm-carrier port (#180).
|
|
@@ -533,9 +579,9 @@ declare function durableTimer(config: DurableTimerConfig): DurableTimer;
|
|
|
533
579
|
* replay bounded by ELAPSED TIME/TICKS rather than raw event count drives
|
|
534
580
|
* `snapshotNow()` instead (or alongside) — see that method.
|
|
535
581
|
* 3. On activation REBUILDS state by FOLDING the log on top of the latest
|
|
536
|
-
* snapshot. The fold
|
|
537
|
-
*
|
|
538
|
-
*
|
|
582
|
+
* snapshot. The fold starts directly from a snapshot; without one it
|
|
583
|
+
* starts from fresh init. One reducer step per logged Msg follows, with
|
|
584
|
+
* no Cmd handlers executed: **side effects are suppressed on replay**
|
|
539
585
|
* (recovery.md). This is the same primitive `foldEvents` / the recorder
|
|
540
586
|
* use; it is reused here, not reinvented (ADR 0003: "the fold already
|
|
541
587
|
* exists; wire it to Store").
|
|
@@ -702,12 +748,10 @@ interface EventSourcedStore<S, M> {
|
|
|
702
748
|
* count-based snapshot is taken. The cooperating DO additionally calls
|
|
703
749
|
* `append(msg)` from `runtime.observe` to grow the log.
|
|
704
750
|
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
* fold; an event-sourced machine's `init` rehydrate branch is a pure
|
|
710
|
-
* passthrough (TEA invariant 2), so the fold touches nothing in `ctx`.
|
|
751
|
+
* Snapshot recovery folds update directly from the captured State; it does
|
|
752
|
+
* not initialize that State again. With no snapshot, `ctx` supplies the fresh
|
|
753
|
+
* init used to establish the event log's starting State. Live resume commands
|
|
754
|
+
* belong to the runtime's subsequent init over the recovered State.
|
|
711
755
|
*/
|
|
712
756
|
declare function doEventSourcedStore<S, M extends {
|
|
713
757
|
type: string;
|
|
@@ -1078,8 +1122,16 @@ declare function sseFromAgentEvents<R, Frame>(runtime: {
|
|
|
1078
1122
|
* `C`/`U`/`Ctx` are the machine's own Cmd/Sub/Ctx — inferred from
|
|
1079
1123
|
* `buildMachine` at the `createAgentHost` call site (never name them by hand;
|
|
1080
1124
|
* an erased `any` here cost consumers all checking on this layer — #278).
|
|
1125
|
+
*
|
|
1126
|
+
* `store` is required, and a fenced one needs an `onError` (see
|
|
1127
|
+
* {@link StoreAndOnError}).
|
|
1081
1128
|
*/
|
|
1082
|
-
|
|
1129
|
+
type AgentHostConfig<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown> = AgentHostWiring<Stage, P, O, R, Frame, C, U, Ctx> & StoreAndOnError<AgentState<Stage, P, O, R>> & {
|
|
1130
|
+
/** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */
|
|
1131
|
+
readonly store: Store<AgentState<Stage, P, O, R>>;
|
|
1132
|
+
};
|
|
1133
|
+
/** The part of {@link AgentHostConfig} that is not the store and its sink. */
|
|
1134
|
+
interface AgentHostWiring<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown> {
|
|
1083
1135
|
/**
|
|
1084
1136
|
* The engine's `run` — `run` from `@demlik/tea/promise`, or any engine's. The
|
|
1085
1137
|
* host imports no engine; it boots the machine through this.
|
|
@@ -1093,8 +1145,6 @@ interface AgentHostConfig<Stage, P extends string, O extends Record<P, AgentTurn
|
|
|
1093
1145
|
* here, and `agent.toMachine({ toolInterpret })` already returns exactly this.
|
|
1094
1146
|
*/
|
|
1095
1147
|
readonly buildMachine: () => Wired<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, C, U, Ctx>;
|
|
1096
|
-
/** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */
|
|
1097
|
-
readonly store: Store<AgentState<Stage, P, O, R>>;
|
|
1098
1148
|
/** The plain Ctx the machine threads to its interpret cells. */
|
|
1099
1149
|
readonly ctx: Ctx;
|
|
1100
1150
|
/**
|
|
@@ -1153,9 +1203,11 @@ interface AgentHost<Stage, P extends string, O extends Record<P, AgentTurn>, R,
|
|
|
1153
1203
|
*/
|
|
1154
1204
|
readonly sse: SseHub<Frame>;
|
|
1155
1205
|
/**
|
|
1156
|
-
* Tear down the cached runtime (drain its tail via `stop()`,
|
|
1157
|
-
* so the next `runtime()` rebuilds
|
|
1158
|
-
*
|
|
1206
|
+
* Tear down the cached runtime (drain its tail via `stop()`, up to
|
|
1207
|
+
* `disposeTimeoutMs`, then drop the handle) so the next `runtime()` rebuilds
|
|
1208
|
+
* from storage. A Cmd handler that has not returned by the limit is given up
|
|
1209
|
+
* on, so `reset()` returns either way. The framework `settle` test seam,
|
|
1210
|
+
* owned once.
|
|
1159
1211
|
*/
|
|
1160
1212
|
reset(): Promise<void>;
|
|
1161
1213
|
}
|
|
@@ -1436,6 +1488,11 @@ interface ResumePort<S, M extends {
|
|
|
1436
1488
|
* hand-written `if (isResumable(state)) await runtime.dispatch(bootMsg(now()))`
|
|
1437
1489
|
* every DO-hosted machine otherwise re-derives.
|
|
1438
1490
|
*
|
|
1491
|
+
* Use this when the host deliberately supplies a Msg after boot, including
|
|
1492
|
+
* the existing agent boot protocol. Subs are already open after `ready`; this
|
|
1493
|
+
* helper cannot order resume ahead of their Msgs. Prefer restored init Cmds
|
|
1494
|
+
* when the machine owns its first move, and do not resume the same work twice.
|
|
1495
|
+
*
|
|
1439
1496
|
* `await ready` is the single boot gate (issue #45): `getState()` is total only
|
|
1440
1497
|
* on the booted handle, so state cannot be inspected before boot populates
|
|
1441
1498
|
* it. Takes any engine's {@link RunHandle}. `now` is injected (the sole boot clock read) so a test can pin it.
|
|
@@ -1462,7 +1519,7 @@ declare function agentIsResumable<Stage, P extends string, O extends Record<P, u
|
|
|
1462
1519
|
* resumable, a no-op on a fresh DO. Wires the agent slice's `agentIsResumable`
|
|
1463
1520
|
* predicate + the agent-owned `agentBootMsg` typed port into the
|
|
1464
1521
|
* generalized helper, so the resume dispatch lives in ONE place — see
|
|
1465
|
-
* `bootResume`'s docblock for the boot
|
|
1522
|
+
* `bootResume`'s docblock for the post-boot ordering boundary.
|
|
1466
1523
|
*
|
|
1467
1524
|
* `now` is injected (the host's only clock read for boot) so a test can pin it.
|
|
1468
1525
|
*/
|
|
@@ -1953,7 +2010,8 @@ interface DoStoreOptions<S> {
|
|
|
1953
2010
|
* `storage.transaction`, so the read, the compare and the two writes are one
|
|
1954
2011
|
* atomic unit. Inside a single DO the platform already guarantees one writer;
|
|
1955
2012
|
* fencing is what refuses a SECOND grain — a stale zombie isolate mid-migration,
|
|
1956
|
-
* or a second DO id pointed at the same state by a routing bug.
|
|
2013
|
+
* or a second DO id pointed at the same state by a routing bug. A run on a
|
|
2014
|
+
* fenced store must be given an `onError` (see `StoreAndOnError`).
|
|
1957
2015
|
*/
|
|
1958
2016
|
readonly fenced?: true;
|
|
1959
2017
|
}
|
|
@@ -2000,10 +2058,17 @@ interface DoStoreOptions<S> {
|
|
|
2000
2058
|
*
|
|
2001
2059
|
* Either form is a `DeletableStore<S>`: `delete()` removes the state cell and
|
|
2002
2060
|
* its `<key>@@version` cell in one `storage.delete` call.
|
|
2061
|
+
*
|
|
2062
|
+
* Options whose `fenced` is known only at runtime, such as a value typed
|
|
2063
|
+
* {@link DoStoreOptions}, return a store that may be fenced, so a run on it must
|
|
2064
|
+
* be given an `onError` (see `StoreAndOnError`).
|
|
2003
2065
|
*/
|
|
2004
2066
|
declare function doStore<S>(storage: DurableObjectStorage, parse: (raw: unknown) => Migrated<S>, keyOrOptions: DoStoreOptions<S> & {
|
|
2005
2067
|
readonly fenced: true;
|
|
2006
2068
|
}): FencedStore<S> & DeletableStore<S>;
|
|
2007
|
-
declare function doStore<S>(storage: DurableObjectStorage, parse: (raw: unknown) => Migrated<S>, keyOrOptions?: string | DoStoreOptions<S>
|
|
2069
|
+
declare function doStore<S>(storage: DurableObjectStorage, parse: (raw: unknown) => Migrated<S>, keyOrOptions?: string | (DoStoreOptions<S> & {
|
|
2070
|
+
readonly fenced?: never;
|
|
2071
|
+
})): DeletableStore<S>;
|
|
2072
|
+
declare function doStore<S>(storage: DurableObjectStorage, parse: (raw: unknown) => Migrated<S>, keyOrOptions?: string | DoStoreOptions<S>): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>);
|
|
2008
2073
|
|
|
2009
|
-
export { type AgentHost, type AgentHostConfig, type AlarmStorage, type AppliedEffects, type AppliedEffectsEvent, type AppliedEffectsGuard, type AttachableSocket, type BroadcastOptions, type BroadcastReport, type DeferResumeHook, type DeferStepHostConfig, type DeferredGateway, type DeferredStepOutcome, type DeferredStepResponse, type DoStoreOptions, type DurableCommandCarrier, type DurableTimer, type DurableTimerConfig, type EffectApplied, EffectConfirmed, type EffectForgotten, type EffectKey, EffectOwed, type EventLogRange, type EventSourcedOptions, type EventSourcedStore, type ExecuteStep, type HibernatableCtx, type IdempotentEffect, type IdempotentOutcome, type NextStep, PendingEffectsRecorder, type PersistedEvent, type PresenceCtx, type PresenceSocket, type PresenceUpgrade, type Projection, type ProjectionErrorContext, type ProjectionId, type ProjectionOnError, type ProjectionRegistry, type ProjectionRunner, type ProjectionUpdate, type RegisterOptions, type ResumePort, type RunStepLoopConfig, type SseHub, type StepCtx, type StepEngine, type StepHostConfig, type StepLoopOutcome, type StepOutcome, type StepRequest, type StepResponse, type StepResult, type StepTransport, type StepWorking, WS_READY_STATE_OPEN, acceptCommandSocket, acceptDurableCommandSocket, acceptPresenceSocket, agentIsResumable, appliedEffects, applyAppliedEvent, autoBoot, bootResume, broadcast, broadcastFrame, broadcastHibernatable, constantTimeEqual, createAgentHost, deferredGateway, doEventSourcedStore, doStore, driveProjections, durableCommandCarrier, durableDeferredGateway, durableTimer, emptyApplied, foldApplied, idempotentEffect, isApplied, mintRunToken, presenceCount, projectionIdString, projectionRegistry, rebuildProjection, registerHibernatableSocket, reissueSurvivingEffects, runProjection, runStepLoop, sseFromAgentEvents, sseHub, sseProjection, stepHost };
|
|
2074
|
+
export { type AgentHost, type AgentHostConfig, type AgentHostWiring, type AlarmStorage, type AppliedEffects, type AppliedEffectsEvent, type AppliedEffectsGuard, type AttachableSocket, type BroadcastOptions, type BroadcastReport, type DeferResumeHook, type DeferStepHostConfig, type DeferredGateway, type DeferredStepOutcome, type DeferredStepResponse, type DoStoreOptions, type DurableCommandCarrier, type DurableTimer, type DurableTimerConfig, type EffectApplied, EffectConfirmed, type EffectForgotten, type EffectKey, EffectOwed, type EventLogRange, type EventSourcedOptions, type EventSourcedStore, type ExecuteStep, type ExecutedStep, type ExecutedSteps, type HibernatableCtx, type IdempotentEffect, type IdempotentOutcome, type NextStep, PendingEffectsRecorder, type PersistedEvent, type PresenceCtx, type PresenceSocket, type PresenceUpgrade, type Projection, type ProjectionErrorContext, type ProjectionId, type ProjectionOnError, type ProjectionRegistry, type ProjectionRunner, type ProjectionUpdate, type RegisterOptions, type ResumePort, type RunStepLoopConfig, type SseHub, type StepCtx, type StepEngine, type StepHostConfig, type StepLoopOutcome, type StepOutcome, type StepRequest, type StepResponse, type StepResult, type StepTransport, type StepWorking, WS_READY_STATE_OPEN, acceptCommandSocket, acceptDurableCommandSocket, acceptPresenceSocket, agentIsResumable, appliedEffects, applyAppliedEvent, autoBoot, bootResume, broadcast, broadcastFrame, broadcastHibernatable, constantTimeEqual, createAgentHost, deferredGateway, doEventSourcedStore, doStore, driveProjections, durableCommandCarrier, durableDeferredGateway, durableTimer, emptyApplied, foldApplied, idempotentEffect, isApplied, mintRunToken, presenceCount, projectionIdString, projectionRegistry, rebuildProjection, registerHibernatableSocket, reissueSurvivingEffects, runProjection, runStepLoop, sseFromAgentEvents, sseHub, sseProjection, stepHost };
|
package/dist/do/index.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { status, agentBootMsg, AGENT_EVENT_TYPES, agentEvents } from '../chunk-
|
|
2
|
-
import { createIntake } from '../chunk-
|
|
1
|
+
import { status, agentBootMsg, AGENT_EVENT_TYPES, agentEvents } from '../chunk-WZEDIUWR.js';
|
|
2
|
+
import { createIntake } from '../chunk-J5JNLXO7.js';
|
|
3
3
|
export { applyEffectEvent, emptyLedger, foldLedger, isOwed, pendingEffectsLedger, survivingEffects } from '../chunk-6IQPODUF.js';
|
|
4
|
-
import '../chunk-
|
|
5
|
-
import '../chunk-
|
|
4
|
+
import '../chunk-VH5MYJ2K.js';
|
|
5
|
+
import '../chunk-IAHEYPYJ.js';
|
|
6
6
|
import '../chunk-QNGYQMZB.js';
|
|
7
|
-
import '../chunk-
|
|
8
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-NFOGYDRS.js';
|
|
8
|
+
import '../chunk-UGTC33VH.js';
|
|
9
9
|
import { defaultRetryPolicy, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-VBDU4JL6.js';
|
|
10
10
|
import '../chunk-PR6TB3BL.js';
|
|
11
11
|
import '../chunk-4LPY67E4.js';
|
|
12
12
|
import '../chunk-TXN2KFHF.js';
|
|
13
|
-
import '../chunk-
|
|
14
|
-
import '../chunk-
|
|
15
|
-
import '../chunk-
|
|
16
|
-
import '../chunk-
|
|
17
|
-
import
|
|
13
|
+
import '../chunk-7K7SEYCH.js';
|
|
14
|
+
import '../chunk-GFGOQIEC.js';
|
|
15
|
+
import '../chunk-2A5XTKQQ.js';
|
|
16
|
+
import '../chunk-R2YNDRGL.js';
|
|
17
|
+
import '../chunk-DEAPNMKO.js';
|
|
18
|
+
import '../chunk-BVFS3ICC.js';
|
|
19
|
+
import { StoreConflictError, replay, foldMsgs } from '../chunk-A3QPMX5M.js';
|
|
18
20
|
|
|
19
21
|
// src/do/durable-timer.ts
|
|
20
22
|
function durableTimer(config) {
|
|
@@ -101,11 +103,7 @@ function doEventSourcedStore(storage, machine, ctx, opts = {}) {
|
|
|
101
103
|
snapshotSeq = snapSeq;
|
|
102
104
|
lastSeq = await readMeta();
|
|
103
105
|
const events = await readLog(snapSeq);
|
|
104
|
-
const {
|
|
105
|
-
msgs: events,
|
|
106
|
-
ctx,
|
|
107
|
-
loaded: snapshot
|
|
108
|
-
});
|
|
106
|
+
const state = snapshot === null ? replay(machine, { msgs: events, ctx }).state : foldMsgs(machine, snapshot, events);
|
|
109
107
|
if (!readyFired) {
|
|
110
108
|
readyFired = true;
|
|
111
109
|
opts.onReady?.(state);
|
|
@@ -253,6 +251,7 @@ function createAgentHost(config) {
|
|
|
253
251
|
...wired,
|
|
254
252
|
...ctxArg,
|
|
255
253
|
store: config.store,
|
|
254
|
+
onError: config.onError,
|
|
256
255
|
events: agentEvents()
|
|
257
256
|
});
|
|
258
257
|
sseFromAgentEvents(booting, sse, config.toSseFrame);
|
|
@@ -658,7 +657,7 @@ async function runStepLoop(token, runId, transport, execute, config) {
|
|
|
658
657
|
const now = config.now ?? Date.now;
|
|
659
658
|
const sleep = config.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
660
659
|
const rng = config.rng;
|
|
661
|
-
const executed =
|
|
660
|
+
const executed = config.executed ?? stepsInMemory();
|
|
662
661
|
let posted = null;
|
|
663
662
|
let pollRetry = initRetry();
|
|
664
663
|
for (; ; ) {
|
|
@@ -685,12 +684,32 @@ async function runStepLoop(token, runId, transport, execute, config) {
|
|
|
685
684
|
}
|
|
686
685
|
pollRetry = initRetry();
|
|
687
686
|
const step = response.step;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
687
|
+
let saved;
|
|
688
|
+
try {
|
|
689
|
+
saved = await executed.get(step.callId);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
return { kind: "gave_up", error };
|
|
692
|
+
}
|
|
693
|
+
if (saved === void 0) {
|
|
694
|
+
saved = { result: await execute(step) };
|
|
695
|
+
try {
|
|
696
|
+
await executed.set(step.callId, saved);
|
|
697
|
+
} catch (error) {
|
|
698
|
+
return { kind: "gave_up", error };
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
posted = { callId: step.callId, result: saved.result };
|
|
692
702
|
}
|
|
693
703
|
}
|
|
704
|
+
function stepsInMemory() {
|
|
705
|
+
const steps = /* @__PURE__ */ new Map();
|
|
706
|
+
return {
|
|
707
|
+
get: (callId) => steps.get(callId),
|
|
708
|
+
set: (callId, step) => {
|
|
709
|
+
steps.set(callId, step);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
694
713
|
|
|
695
714
|
// src/do/index.ts
|
|
696
715
|
var DEFAULT_STATE_KEY = "@@state";
|