@unbrained/pm-cli 2026.6.5 → 2026.6.6
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/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +1 -0
- package/README.md +12 -4
- package/dist/cli/commands/completion.js +21 -7
- package/dist/cli/commands/completion.js.map +1 -1
- package/dist/cli/commands/contracts.js +5 -3
- package/dist/cli/commands/contracts.js.map +1 -1
- package/dist/cli/commands/gc.d.ts +8 -1
- package/dist/cli/commands/gc.js +26 -3
- package/dist/cli/commands/gc.js.map +1 -1
- package/dist/cli/commands/health.js +15 -2
- package/dist/cli/commands/health.js.map +1 -1
- package/dist/cli/commands/history.d.ts +4 -1
- package/dist/cli/commands/history.js +12 -24
- package/dist/cli/commands/history.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +3 -2
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/stats.d.ts +8 -1
- package/dist/cli/commands/stats.js +18 -17
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/cli/commands/telemetry.d.ts +8 -0
- package/dist/cli/commands/telemetry.js +253 -0
- package/dist/cli/commands/telemetry.js.map +1 -0
- package/dist/cli/help-content.js +11 -2
- package/dist/cli/help-content.js.map +1 -1
- package/dist/cli/register-list-query.js +7 -4
- package/dist/cli/register-list-query.js.map +1 -1
- package/dist/cli/register-operations.js +29 -6
- package/dist/cli/register-operations.js.map +1 -1
- package/dist/cli-bundle/chunks/activity-CVXTCHYY.js +16 -0
- package/dist/cli-bundle/chunks/{activity-3RWRRNOW.js → activity-JM6DTDND.js} +11 -11
- package/dist/cli-bundle/chunks/{aggregate-J35K7WZB.js → aggregate-GI7WSZPH.js} +10 -10
- package/dist/cli-bundle/chunks/{aggregate-OOAGKWRG.js → aggregate-VNAHUNNF.js} +10 -10
- package/dist/cli-bundle/chunks/{append-5JE3YGXU.js → append-7H5N64R2.js} +8 -8
- package/dist/cli-bundle/chunks/{append-PNN6UIBZ.js → append-WMKRXKCE.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-D5X7N6FI.js → chunk-23YFZ3IG.js} +12 -12
- package/dist/cli-bundle/chunks/{chunk-FVXM3GKS.js → chunk-2HVRUSQA.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-V3JQ4IPL.js → chunk-2RSPZI5H.js} +102 -22
- package/dist/cli-bundle/chunks/chunk-2RSPZI5H.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-SZQCER6S.js → chunk-2VBQ6ZXS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-A6PEXMYN.js → chunk-2VVCGBNV.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-UOVLWB47.js → chunk-3BWOVLTH.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-5IRA6IYD.js → chunk-3FI4SEFT.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-3OH4DAEB.js → chunk-3LB74A67.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-GFWHHMVV.js → chunk-3VAM3NNM.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-4QCR4QW3.js → chunk-4LL6TK5B.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-TBQVUNVH.js → chunk-52MAOQ6J.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-5KB4X7YY.js → chunk-5FDATVEZ.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-4LTBNUYT.js → chunk-5SFCOO6B.js} +76 -7
- package/dist/cli-bundle/chunks/{chunk-4LTBNUYT.js.map → chunk-5SFCOO6B.js.map} +1 -1
- package/dist/cli-bundle/chunks/{chunk-GCAVRLGE.js → chunk-6ER6DXH5.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-26KTVXJW.js → chunk-6P6GGMOG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-MISYRLEP.js → chunk-77GFDHGF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HPH6X4ZZ.js → chunk-AO442XLN.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-ORTLYVA4.js → chunk-AY43SBIN.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-D3WUNETC.js → chunk-BQHUILZS.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-KCMIYKTA.js → chunk-E5FJNAZA.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-OIQIXTNB.js → chunk-ECCMVXGM.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-5PAOPUZX.js → chunk-FRAUKRU2.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-JQI2HE55.js → chunk-GCOQW437.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-CWT53EYT.js → chunk-GGYWB3Y2.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-ATBVS6WW.js → chunk-GU2ZJ2VV.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-ASQM3IF5.js → chunk-GXSUPCBV.js} +5 -4
- package/dist/cli-bundle/chunks/{chunk-SSFMJCPF.js → chunk-HZGUAH22.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-GPGAYASV.js → chunk-I2HSTRJF.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-RQAWLSUU.js → chunk-I6BNFU5X.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-OUF3ARDX.js → chunk-IJGCZSIQ.js} +12 -12
- package/dist/cli-bundle/chunks/chunk-IOR7LGF4.js +1491 -0
- package/dist/cli-bundle/chunks/chunk-IOR7LGF4.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-XX5LRINW.js → chunk-KAG77BY3.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BNL6S7QH.js → chunk-KJ33AOCY.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-4PJQSECJ.js → chunk-KJE2FOS4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-GNHVV4SY.js → chunk-KWU6HG75.js} +37 -14
- package/dist/cli-bundle/chunks/chunk-KWU6HG75.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-KBG47ZOY.js → chunk-LB3RUL2N.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-HZOS2UG5.js → chunk-LCM63IXU.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-VGO2T3TY.js → chunk-ME2YH6FN.js} +7 -7
- package/dist/cli-bundle/chunks/{chunk-ZRVSMOMR.js → chunk-MITFDD35.js} +12 -12
- package/dist/cli-bundle/chunks/{chunk-Y2AKE2HS.js → chunk-N7ACFE26.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-AXXKLVTW.js → chunk-NUZ5HKR4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-KK6YPGH3.js → chunk-OVQCEJPG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-B3N4TKNL.js → chunk-OZ7NSQHL.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-TK633IVD.js → chunk-PDDFYW4X.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-DFIZGAI5.js → chunk-PIU6RSL3.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-O5IBJE6S.js → chunk-PO3GSVFV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-WIKR7KFL.js → chunk-PRJCELV4.js} +108 -21
- package/dist/cli-bundle/chunks/chunk-PRJCELV4.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-ZQ4F6EC4.js → chunk-PROOHX4K.js} +38 -15
- package/dist/cli-bundle/chunks/chunk-PROOHX4K.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-N5YF3VFN.js → chunk-Q7SPNHLW.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-RANZIID5.js → chunk-QO7EBBOM.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-LDJK7SHN.js → chunk-R6UFP2GE.js} +7 -7
- package/dist/cli-bundle/chunks/{chunk-CCUFLH4J.js → chunk-RWPSOIEE.js} +7 -7
- package/dist/cli-bundle/chunks/{chunk-UFCG3SWR.js → chunk-RZN6VURO.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-SKJWX3QB.js +1501 -0
- package/dist/cli-bundle/chunks/chunk-SKJWX3QB.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-KA24HME7.js → chunk-T3LC5LKB.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NQP3ECKF.js → chunk-TCD3R2PF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-EHXPIKKP.js → chunk-TPH5XEVD.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-SRVKTWPR.js → chunk-U5SXYXSY.js} +75 -6
- package/dist/cli-bundle/chunks/{chunk-SRVKTWPR.js.map → chunk-U5SXYXSY.js.map} +2 -2
- package/dist/cli-bundle/chunks/{chunk-T2ORWB6D.js → chunk-UBOPJT3S.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-S3CV7JGJ.js → chunk-UM2GBHS4.js} +5 -4
- package/dist/cli-bundle/chunks/{chunk-G5BACLYH.js → chunk-W7XABV4H.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-K47WAYQV.js → chunk-WKY2IBOQ.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-7K24QB23.js → chunk-WOPXVEZP.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-DFUSXJDE.js → chunk-WSMOSYRD.js} +12 -12
- package/dist/cli-bundle/chunks/{chunk-CJDQB2ED.js → chunk-XLTD2HN3.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-TQ3ZK6VD.js → chunk-XSJCLN2R.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-XG7RJYHV.js → chunk-XTGLRY2Y.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-JB4RGL4V.js → chunk-YL2DJGYS.js} +7 -7
- package/dist/cli-bundle/chunks/{claim-AZLP7YFO.js → claim-4DBF6RVG.js} +10 -10
- package/dist/cli-bundle/chunks/{claim-ZFA7HKU4.js → claim-X2P4IB7J.js} +10 -10
- package/dist/cli-bundle/chunks/close-3HDWDA3L.js +13 -0
- package/dist/cli-bundle/chunks/{close-65DFYAVS.js → close-PTYFA45H.js} +8 -8
- package/dist/cli-bundle/chunks/{close-many-UKZSQGM7.js → close-many-7CGIDPTJ.js} +15 -15
- package/dist/cli-bundle/chunks/{close-many-AKPZNSGI.js → close-many-ERFO6LHR.js} +15 -15
- package/dist/cli-bundle/chunks/{comments-CJWXQI7X.js → comments-OONGABK5.js} +10 -10
- package/dist/cli-bundle/chunks/{comments-AGBNSJJH.js → comments-QKBYCWKD.js} +10 -10
- package/dist/cli-bundle/chunks/{config-DDPYIA6J.js → config-6FXCQKIF.js} +8 -8
- package/dist/cli-bundle/chunks/{config-5ON3ADRH.js → config-U4RZ4CQB.js} +8 -8
- package/dist/cli-bundle/chunks/{context-E6U3DWVM.js → context-FYYJGFWS.js} +13 -13
- package/dist/cli-bundle/chunks/{context-ZOJEN5VT.js → context-YMJ3Z2HQ.js} +13 -13
- package/dist/cli-bundle/chunks/{contracts-LCSX5R53.js → contracts-QDB6OMLQ.js} +10 -6
- package/dist/cli-bundle/chunks/contracts-QDB6OMLQ.js.map +1 -0
- package/dist/cli-bundle/chunks/{contracts-CXSFM45B.js → contracts-RFSPGVVJ.js} +9 -5
- package/dist/cli-bundle/chunks/{contracts-LCSX5R53.js.map → contracts-RFSPGVVJ.js.map} +7 -1
- package/dist/cli-bundle/chunks/{create-WYFKN2U4.js → create-I5L4EYYZ.js} +13 -13
- package/dist/cli-bundle/chunks/create-JTZNCTQC.js +27 -0
- package/dist/cli-bundle/chunks/{delete-ZXWC56WO.js → delete-7HPGI4GZ.js} +7 -7
- package/dist/cli-bundle/chunks/{delete-RONNH4F4.js → delete-VXUPH5RS.js} +7 -7
- package/dist/cli-bundle/chunks/{deps-WII3O4HB.js → deps-JSRRWC3Z.js} +7 -7
- package/dist/cli-bundle/chunks/{deps-EHXIFOZR.js → deps-SCZIFK2Z.js} +7 -7
- package/dist/cli-bundle/chunks/{docs-PDREIMLS.js → docs-GJK6K4VP.js} +9 -9
- package/dist/cli-bundle/chunks/{docs-JMVCFNHH.js → docs-WW4N3MBM.js} +9 -9
- package/dist/cli-bundle/chunks/{extension-G6UXSM5S.js → extension-J7IBMFTD.js} +4 -4
- package/dist/cli-bundle/chunks/{extension-B4ROVWJD.js → extension-VDY2U33M.js} +4 -4
- package/dist/cli-bundle/chunks/{files-2PN7UM2D.js → files-ISZJGILQ.js} +9 -9
- package/dist/cli-bundle/chunks/{files-MXYS5W4M.js → files-WKOD326U.js} +9 -9
- package/dist/cli-bundle/chunks/gc-JDVJ6OQ2.js +362 -0
- package/dist/cli-bundle/chunks/gc-JDVJ6OQ2.js.map +1 -0
- package/dist/cli-bundle/chunks/gc-ULPVWMTN.js +372 -0
- package/dist/cli-bundle/chunks/gc-ULPVWMTN.js.map +7 -0
- package/dist/cli-bundle/chunks/{get-WXMLRG7M.js → get-RV37HT3G.js} +10 -10
- package/dist/cli-bundle/chunks/{get-CINFEUIY.js → get-WXU6WI5P.js} +10 -10
- package/dist/cli-bundle/chunks/{health-IT5DBPRJ.js → health-6L6EH4WR.js} +26 -13
- package/dist/cli-bundle/chunks/health-6L6EH4WR.js.map +7 -0
- package/dist/cli-bundle/chunks/{health-5VPOJQ53.js → health-LOI2HHKB.js} +27 -14
- package/dist/cli-bundle/chunks/health-LOI2HHKB.js.map +1 -0
- package/dist/cli-bundle/chunks/history-2BA3T5AE.js +20 -0
- package/dist/cli-bundle/chunks/{history-FLZIP7PM.js → history-PKN67D5O.js} +10 -10
- package/dist/cli-bundle/chunks/history-redact-JBZNXHGR.js +19 -0
- package/dist/cli-bundle/chunks/{history-redact-SSZBAD76.js → history-redact-V4FOJGS7.js} +11 -11
- package/dist/cli-bundle/chunks/{history-repair-KC5ZOJRZ.js → history-repair-HIBCBIKK.js} +11 -11
- package/dist/cli-bundle/chunks/{history-repair-X7CO5JJZ.js → history-repair-SDMHR7JT.js} +11 -11
- package/dist/cli-bundle/chunks/init-HIVSNX3O.js +16 -0
- package/dist/cli-bundle/chunks/{init-6MRFVOLH.js → init-P56QYWJF.js} +8 -8
- package/dist/cli-bundle/chunks/{learnings-DCX55BNX.js → learnings-GDFRG5J7.js} +10 -10
- package/dist/cli-bundle/chunks/{learnings-A3CLB5CO.js → learnings-MIXSESLK.js} +10 -10
- package/dist/cli-bundle/chunks/{list-DBRECOG4.js → list-FGOWQMSM.js} +10 -10
- package/dist/cli-bundle/chunks/list-MUSQNMTD.js +20 -0
- package/dist/cli-bundle/chunks/{notes-42LIC3UE.js → notes-4XVSH2AZ.js} +10 -10
- package/dist/cli-bundle/chunks/{notes-P5O3VMQ3.js → notes-5UK3GEH2.js} +10 -10
- package/dist/cli-bundle/chunks/{plan-IGTE3WOJ.js → plan-DUMBV3QB.js} +13 -13
- package/dist/cli-bundle/chunks/{plan-HDQXEUKD.js → plan-SLMODHWX.js} +13 -13
- package/dist/cli-bundle/chunks/{register-list-query-EMNSIUO4.js → register-list-query-POCFZ5JW.js} +24 -22
- package/dist/cli-bundle/chunks/{register-list-query-EMNSIUO4.js.map → register-list-query-POCFZ5JW.js.map} +1 -1
- package/dist/cli-bundle/chunks/{register-list-query-AG3XKXWL.js → register-list-query-WST76HSQ.js} +23 -21
- package/dist/cli-bundle/chunks/{register-list-query-AG3XKXWL.js.map → register-list-query-WST76HSQ.js.map} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-M3YS6XDO.js → register-mutation-N5GSUZJV.js} +30 -30
- package/dist/cli-bundle/chunks/{register-mutation-RSMT6DPK.js → register-mutation-OA675SOU.js} +30 -30
- package/dist/cli-bundle/chunks/{register-operations-KYUOOOOD.js → register-operations-GNHBE5I3.js} +47 -34
- package/dist/cli-bundle/chunks/register-operations-GNHBE5I3.js.map +1 -0
- package/dist/cli-bundle/chunks/{register-operations-Q54JQZUN.js → register-operations-N3PMHXH5.js} +46 -33
- package/dist/cli-bundle/chunks/{register-operations-Q54JQZUN.js.map → register-operations-N3PMHXH5.js.map} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-DI6P7X75.js → register-setup-JIMPKJKV.js} +15 -15
- package/dist/cli-bundle/chunks/{register-setup-YJKIQRKA.js → register-setup-ZHJI2DI3.js} +15 -15
- package/dist/cli-bundle/chunks/restore-D4RGNX33.js +17 -0
- package/dist/cli-bundle/chunks/{restore-2XVTBLKQ.js → restore-PBL6T5TW.js} +11 -11
- package/dist/cli-bundle/chunks/{schema-V6BX65Q2.js → schema-2NOO6NGB.js} +8 -8
- package/dist/cli-bundle/chunks/{schema-LVXZWFGY.js → schema-EBI4NLUO.js} +8 -8
- package/dist/cli-bundle/chunks/{search-C7NPB7G7.js → search-JPUQTVCY.js} +10 -10
- package/dist/cli-bundle/chunks/{search-5A3BMFA7.js → search-OA6JX27A.js} +10 -10
- package/dist/cli-bundle/chunks/stats-CV54BAWY.js +202 -0
- package/dist/cli-bundle/chunks/stats-CV54BAWY.js.map +1 -0
- package/dist/cli-bundle/chunks/stats-XYB6KTFG.js +212 -0
- package/dist/cli-bundle/chunks/stats-XYB6KTFG.js.map +7 -0
- package/dist/cli-bundle/chunks/telemetry-LMJ7PFUO.js +251 -0
- package/dist/cli-bundle/chunks/telemetry-LMJ7PFUO.js.map +7 -0
- package/dist/cli-bundle/chunks/telemetry-NCMEPJNR.js +248 -0
- package/dist/cli-bundle/chunks/telemetry-NCMEPJNR.js.map +1 -0
- package/dist/cli-bundle/chunks/{test-RJ4ZF35X.js → test-MXX3E5JL.js} +13 -13
- package/dist/cli-bundle/chunks/{test-OFYONO3C.js → test-WBROAJIE.js} +13 -13
- package/dist/cli-bundle/chunks/{test-all-OJE6CIO7.js → test-all-N6NSRDTV.js} +13 -13
- package/dist/cli-bundle/chunks/{test-all-EEMKNTGU.js → test-all-NTOZ7C3M.js} +13 -13
- package/dist/cli-bundle/chunks/{test-runs-IPOPFOAF.js → test-runs-V6A7LPWZ.js} +5 -5
- package/dist/cli-bundle/chunks/{test-runs-L2M7WKH7.js → test-runs-WMVTQU3Y.js} +5 -5
- package/dist/cli-bundle/chunks/update-OSV3NAQB.js +25 -0
- package/dist/cli-bundle/chunks/update-YRXSUGYQ.js +21 -0
- package/dist/cli-bundle/chunks/{update-many-DYWGW57M.js → update-many-EM6OXYGO.js} +22 -22
- package/dist/cli-bundle/chunks/{update-many-322CCH2L.js → update-many-KGGEYABR.js} +22 -22
- package/dist/cli-bundle/chunks/{upgrade-2U727SAM.js → upgrade-BLI7XBAJ.js} +4 -4
- package/dist/cli-bundle/chunks/{upgrade-3W3RVSNM.js → upgrade-LXPOQKFV.js} +4 -4
- package/dist/cli-bundle/chunks/{validate-ZFVWPEEN.js → validate-AGOAUOVW.js} +15 -15
- package/dist/cli-bundle/chunks/{validate-GZ24NYHO.js → validate-L5J6A7YO.js} +15 -15
- package/dist/cli-bundle/main.js +42 -1451
- package/dist/cli-bundle/main.js.map +4 -4
- package/dist/core/history/history-diff.d.ts +47 -0
- package/dist/core/history/history-diff.js +152 -0
- package/dist/core/history/history-diff.js.map +1 -0
- package/dist/core/history/history-storage-stats.d.ts +31 -0
- package/dist/core/history/history-storage-stats.js +101 -0
- package/dist/core/history/history-storage-stats.js.map +1 -0
- package/dist/core/lock/lock-gc.d.ts +46 -0
- package/dist/core/lock/lock-gc.js +194 -0
- package/dist/core/lock/lock-gc.js.map +1 -0
- package/dist/core/shared/serial-queue.d.ts +12 -0
- package/dist/core/shared/serial-queue.js +43 -0
- package/dist/core/shared/serial-queue.js.map +1 -0
- package/dist/core/store/settings.d.ts +19 -1
- package/dist/core/store/settings.js +92 -7
- package/dist/core/store/settings.js.map +1 -1
- package/dist/core/telemetry/observability.js +3 -3
- package/dist/core/telemetry/observability.js.map +1 -1
- package/dist/core/telemetry/runtime.js +16 -4
- package/dist/core/telemetry/runtime.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +155 -37
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +2 -2
- package/dist/sdk/cli-contracts/enum-contracts.js +4 -2
- package/dist/sdk/cli-contracts/enum-contracts.js.map +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +10 -7
- package/dist/sdk/cli-contracts/tool-parameter-tables.js.map +1 -1
- package/dist/sdk/cli-contracts.d.ts +23 -0
- package/dist/sdk/cli-contracts.js +41 -7
- package/dist/sdk/cli-contracts.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -1
- package/docs/ARCHITECTURE.md +11 -1
- package/docs/CLAUDE_CODE_PLUGIN.md +2 -2
- package/docs/COMMANDS.md +33 -2
- package/docs/CONFIGURATION.md +9 -1
- package/docs/EXTENSIONS.md +7 -0
- package/docs/EXTENSION_AUTHOR_CONTRACTS.md +73 -0
- package/docs/ONBOARDING.md +165 -0
- package/docs/QUICKSTART.md +1 -1
- package/docs/README.md +12 -4
- package/docs/RELEASING.md +36 -1
- package/docs/TESTING.md +19 -1
- package/package.json +5 -2
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/README.md +7 -4
- package/plugins/pm-claude/agents/pm-coordinator.md +1 -1
- package/plugins/pm-claude/commands/pm-init.md +1 -1
- package/dist/cli-bundle/chunks/activity-P5BUMWP3.js +0 -16
- package/dist/cli-bundle/chunks/chunk-GNHVV4SY.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-V3JQ4IPL.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-WIKR7KFL.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-ZQ4F6EC4.js.map +0 -7
- package/dist/cli-bundle/chunks/close-RMHQ3KWH.js +0 -13
- package/dist/cli-bundle/chunks/contracts-CXSFM45B.js.map +0 -7
- package/dist/cli-bundle/chunks/create-ATONSAZW.js +0 -27
- package/dist/cli-bundle/chunks/gc-GT552UJH.js +0 -178
- package/dist/cli-bundle/chunks/gc-GT552UJH.js.map +0 -1
- package/dist/cli-bundle/chunks/gc-QNBRJG6H.js +0 -181
- package/dist/cli-bundle/chunks/gc-QNBRJG6H.js.map +0 -7
- package/dist/cli-bundle/chunks/health-5VPOJQ53.js.map +0 -1
- package/dist/cli-bundle/chunks/health-IT5DBPRJ.js.map +0 -7
- package/dist/cli-bundle/chunks/history-6FC4H6HI.js +0 -20
- package/dist/cli-bundle/chunks/history-redact-XKYEZDBL.js +0 -19
- package/dist/cli-bundle/chunks/init-KHX3FEEM.js +0 -16
- package/dist/cli-bundle/chunks/list-53LZ2AEO.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-KYUOOOOD.js.map +0 -1
- package/dist/cli-bundle/chunks/restore-JL5QIH7H.js +0 -17
- package/dist/cli-bundle/chunks/stats-FNLYVXTB.js +0 -120
- package/dist/cli-bundle/chunks/stats-FNLYVXTB.js.map +0 -1
- package/dist/cli-bundle/chunks/stats-Y2CBTZSL.js +0 -123
- package/dist/cli-bundle/chunks/stats-Y2CBTZSL.js.map +0 -7
- package/dist/cli-bundle/chunks/update-6YEHPVXH.js +0 -25
- package/dist/cli-bundle/chunks/update-PYLVHMRD.js +0 -21
- /package/dist/cli-bundle/chunks/{activity-P5BUMWP3.js.map → activity-CVXTCHYY.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{activity-3RWRRNOW.js.map → activity-JM6DTDND.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{aggregate-J35K7WZB.js.map → aggregate-GI7WSZPH.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{aggregate-OOAGKWRG.js.map → aggregate-VNAHUNNF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{append-5JE3YGXU.js.map → append-7H5N64R2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{append-PNN6UIBZ.js.map → append-WMKRXKCE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-D5X7N6FI.js.map → chunk-23YFZ3IG.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-FVXM3GKS.js.map → chunk-2HVRUSQA.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-SZQCER6S.js.map → chunk-2VBQ6ZXS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-A6PEXMYN.js.map → chunk-2VVCGBNV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-UOVLWB47.js.map → chunk-3BWOVLTH.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-5IRA6IYD.js.map → chunk-3FI4SEFT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-3OH4DAEB.js.map → chunk-3LB74A67.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-GFWHHMVV.js.map → chunk-3VAM3NNM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-4QCR4QW3.js.map → chunk-4LL6TK5B.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-TBQVUNVH.js.map → chunk-52MAOQ6J.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-5KB4X7YY.js.map → chunk-5FDATVEZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-GCAVRLGE.js.map → chunk-6ER6DXH5.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-26KTVXJW.js.map → chunk-6P6GGMOG.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-MISYRLEP.js.map → chunk-77GFDHGF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-HPH6X4ZZ.js.map → chunk-AO442XLN.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-ORTLYVA4.js.map → chunk-AY43SBIN.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-D3WUNETC.js.map → chunk-BQHUILZS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-KCMIYKTA.js.map → chunk-E5FJNAZA.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-OIQIXTNB.js.map → chunk-ECCMVXGM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-5PAOPUZX.js.map → chunk-FRAUKRU2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-JQI2HE55.js.map → chunk-GCOQW437.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-CWT53EYT.js.map → chunk-GGYWB3Y2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-ATBVS6WW.js.map → chunk-GU2ZJ2VV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-ASQM3IF5.js.map → chunk-GXSUPCBV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-SSFMJCPF.js.map → chunk-HZGUAH22.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-GPGAYASV.js.map → chunk-I2HSTRJF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-RQAWLSUU.js.map → chunk-I6BNFU5X.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-OUF3ARDX.js.map → chunk-IJGCZSIQ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-XX5LRINW.js.map → chunk-KAG77BY3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-BNL6S7QH.js.map → chunk-KJ33AOCY.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-4PJQSECJ.js.map → chunk-KJE2FOS4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-KBG47ZOY.js.map → chunk-LB3RUL2N.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-HZOS2UG5.js.map → chunk-LCM63IXU.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-VGO2T3TY.js.map → chunk-ME2YH6FN.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-ZRVSMOMR.js.map → chunk-MITFDD35.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-Y2AKE2HS.js.map → chunk-N7ACFE26.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-AXXKLVTW.js.map → chunk-NUZ5HKR4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-KK6YPGH3.js.map → chunk-OVQCEJPG.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-B3N4TKNL.js.map → chunk-OZ7NSQHL.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-TK633IVD.js.map → chunk-PDDFYW4X.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-DFIZGAI5.js.map → chunk-PIU6RSL3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-O5IBJE6S.js.map → chunk-PO3GSVFV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-N5YF3VFN.js.map → chunk-Q7SPNHLW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-RANZIID5.js.map → chunk-QO7EBBOM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-LDJK7SHN.js.map → chunk-R6UFP2GE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-CCUFLH4J.js.map → chunk-RWPSOIEE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-UFCG3SWR.js.map → chunk-RZN6VURO.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-KA24HME7.js.map → chunk-T3LC5LKB.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-NQP3ECKF.js.map → chunk-TCD3R2PF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-EHXPIKKP.js.map → chunk-TPH5XEVD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-T2ORWB6D.js.map → chunk-UBOPJT3S.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-S3CV7JGJ.js.map → chunk-UM2GBHS4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-G5BACLYH.js.map → chunk-W7XABV4H.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-K47WAYQV.js.map → chunk-WKY2IBOQ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-7K24QB23.js.map → chunk-WOPXVEZP.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-DFUSXJDE.js.map → chunk-WSMOSYRD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-CJDQB2ED.js.map → chunk-XLTD2HN3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-TQ3ZK6VD.js.map → chunk-XSJCLN2R.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-XG7RJYHV.js.map → chunk-XTGLRY2Y.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-JB4RGL4V.js.map → chunk-YL2DJGYS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{claim-AZLP7YFO.js.map → claim-4DBF6RVG.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{claim-ZFA7HKU4.js.map → claim-X2P4IB7J.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-RMHQ3KWH.js.map → close-3HDWDA3L.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-65DFYAVS.js.map → close-PTYFA45H.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-many-UKZSQGM7.js.map → close-many-7CGIDPTJ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-many-AKPZNSGI.js.map → close-many-ERFO6LHR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{comments-CJWXQI7X.js.map → comments-OONGABK5.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{comments-AGBNSJJH.js.map → comments-QKBYCWKD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{config-DDPYIA6J.js.map → config-6FXCQKIF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{config-5ON3ADRH.js.map → config-U4RZ4CQB.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{context-E6U3DWVM.js.map → context-FYYJGFWS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{context-ZOJEN5VT.js.map → context-YMJ3Z2HQ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{create-WYFKN2U4.js.map → create-I5L4EYYZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{create-ATONSAZW.js.map → create-JTZNCTQC.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{delete-ZXWC56WO.js.map → delete-7HPGI4GZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{delete-RONNH4F4.js.map → delete-VXUPH5RS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{deps-WII3O4HB.js.map → deps-JSRRWC3Z.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{deps-EHXIFOZR.js.map → deps-SCZIFK2Z.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{docs-PDREIMLS.js.map → docs-GJK6K4VP.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{docs-JMVCFNHH.js.map → docs-WW4N3MBM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{extension-G6UXSM5S.js.map → extension-J7IBMFTD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{extension-B4ROVWJD.js.map → extension-VDY2U33M.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{files-2PN7UM2D.js.map → files-ISZJGILQ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{files-MXYS5W4M.js.map → files-WKOD326U.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{get-WXMLRG7M.js.map → get-RV37HT3G.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{get-CINFEUIY.js.map → get-WXU6WI5P.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-6FC4H6HI.js.map → history-2BA3T5AE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-FLZIP7PM.js.map → history-PKN67D5O.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-redact-XKYEZDBL.js.map → history-redact-JBZNXHGR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-redact-SSZBAD76.js.map → history-redact-V4FOJGS7.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-repair-KC5ZOJRZ.js.map → history-repair-HIBCBIKK.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-repair-X7CO5JJZ.js.map → history-repair-SDMHR7JT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{init-KHX3FEEM.js.map → init-HIVSNX3O.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{init-6MRFVOLH.js.map → init-P56QYWJF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{learnings-DCX55BNX.js.map → learnings-GDFRG5J7.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{learnings-A3CLB5CO.js.map → learnings-MIXSESLK.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{list-DBRECOG4.js.map → list-FGOWQMSM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{list-53LZ2AEO.js.map → list-MUSQNMTD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{notes-42LIC3UE.js.map → notes-4XVSH2AZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{notes-P5O3VMQ3.js.map → notes-5UK3GEH2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{plan-IGTE3WOJ.js.map → plan-DUMBV3QB.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{plan-HDQXEUKD.js.map → plan-SLMODHWX.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{register-mutation-M3YS6XDO.js.map → register-mutation-N5GSUZJV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{register-mutation-RSMT6DPK.js.map → register-mutation-OA675SOU.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{register-setup-DI6P7X75.js.map → register-setup-JIMPKJKV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{register-setup-YJKIQRKA.js.map → register-setup-ZHJI2DI3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{restore-JL5QIH7H.js.map → restore-D4RGNX33.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{restore-2XVTBLKQ.js.map → restore-PBL6T5TW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{schema-V6BX65Q2.js.map → schema-2NOO6NGB.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{schema-LVXZWFGY.js.map → schema-EBI4NLUO.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{search-C7NPB7G7.js.map → search-JPUQTVCY.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{search-5A3BMFA7.js.map → search-OA6JX27A.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-RJ4ZF35X.js.map → test-MXX3E5JL.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-OFYONO3C.js.map → test-WBROAJIE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-all-OJE6CIO7.js.map → test-all-N6NSRDTV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-all-EEMKNTGU.js.map → test-all-NTOZ7C3M.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-runs-IPOPFOAF.js.map → test-runs-V6A7LPWZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-runs-L2M7WKH7.js.map → test-runs-WMVTQU3Y.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-6YEHPVXH.js.map → update-OSV3NAQB.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-PYLVHMRD.js.map → update-YRXSUGYQ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-many-DYWGW57M.js.map → update-many-EM6OXYGO.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-many-322CCH2L.js.map → update-many-KGGEYABR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{upgrade-2U727SAM.js.map → upgrade-BLI7XBAJ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{upgrade-3W3RVSNM.js.map → upgrade-LXPOQKFV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{validate-ZFVWPEEN.js.map → validate-AGOAUOVW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{validate-GZ24NYHO.js.map → validate-L5J6A7YO.js.map} +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { HistoryEntry } from "../../types/index.js";
|
|
2
|
+
export interface HistoryFieldChange {
|
|
3
|
+
field: string;
|
|
4
|
+
before: unknown;
|
|
5
|
+
after: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface HistoryDiffValueEntry {
|
|
8
|
+
/** 1-based position in the FULL stream (fullEntries index + 1). */
|
|
9
|
+
index: number;
|
|
10
|
+
ts: string;
|
|
11
|
+
op: string;
|
|
12
|
+
author: string;
|
|
13
|
+
/** Number of JSON-patch operations in this entry (entry.patch.length). */
|
|
14
|
+
patch_ops: number;
|
|
15
|
+
/** Unique field names touched by this entry, sorted by localeCompare. */
|
|
16
|
+
changed_fields: string[];
|
|
17
|
+
/** Per-field before/after values, sorted by field name (localeCompare). */
|
|
18
|
+
changes: HistoryFieldChange[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Map a JSON-Patch path string to a human-readable changed-field name.
|
|
22
|
+
*
|
|
23
|
+
* Rules (in priority order):
|
|
24
|
+
* - "/body" or "/body/…" → "body"
|
|
25
|
+
* - "/metadata/…" or "/front_matter/…"
|
|
26
|
+
* with a first segment → that decoded segment (e.g. "status")
|
|
27
|
+
* without a first segment (bare prefix) → "metadata"
|
|
28
|
+
* - "/" (root pointer, empty segment after strip) → "root"
|
|
29
|
+
* - anything else → decoded first path segment
|
|
30
|
+
*/
|
|
31
|
+
export declare function patchPathToChangedField(path: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Replay a full history entry stream and produce enriched diff entries with
|
|
34
|
+
* per-field before/after values.
|
|
35
|
+
*
|
|
36
|
+
* @param fullEntries The complete ordered history chain to replay.
|
|
37
|
+
* @param options.windowStartIndex 0-based index into fullEntries at which the
|
|
38
|
+
* DISPLAYED window begins. Entries before this index are still replayed (so
|
|
39
|
+
* state is accurate) but are not emitted. Defaults to 0.
|
|
40
|
+
* @param options.field When supplied, only entries that changed this field are
|
|
41
|
+
* emitted, and each emitted entry's `changes` and `changed_fields` are
|
|
42
|
+
* restricted to that single field.
|
|
43
|
+
*/
|
|
44
|
+
export declare function computeHistoryDiff(fullEntries: HistoryEntry[], options?: {
|
|
45
|
+
windowStartIndex?: number;
|
|
46
|
+
field?: string;
|
|
47
|
+
}): HistoryDiffValueEntry[];
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2b77be38-f27c-5a56-86de-9625777f7211")}catch(e){}}();
|
|
3
|
+
import { cloneEmptyReplayDocument, tryApplyReplayPatch } from "./replay.js";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// JSON-Pointer helpers (ported from src/cli/commands/history.ts so the
|
|
6
|
+
// orchestrator can replace the private copy there with an import from here).
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function decodeJsonPointerSegment(segment) {
|
|
9
|
+
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Map a JSON-Patch path string to a human-readable changed-field name.
|
|
13
|
+
*
|
|
14
|
+
* Rules (in priority order):
|
|
15
|
+
* - "/body" or "/body/…" → "body"
|
|
16
|
+
* - "/metadata/…" or "/front_matter/…"
|
|
17
|
+
* with a first segment → that decoded segment (e.g. "status")
|
|
18
|
+
* without a first segment (bare prefix) → "metadata"
|
|
19
|
+
* - "/" (root pointer, empty segment after strip) → "root"
|
|
20
|
+
* - anything else → decoded first path segment
|
|
21
|
+
*/
|
|
22
|
+
export function patchPathToChangedField(path) {
|
|
23
|
+
if (path === "/body" || path.startsWith("/body/"))
|
|
24
|
+
return "body";
|
|
25
|
+
if (path === "/metadata" ||
|
|
26
|
+
path.startsWith("/metadata/") ||
|
|
27
|
+
path === "/front_matter" ||
|
|
28
|
+
path.startsWith("/front_matter/")) {
|
|
29
|
+
const segment = path.replace(/^\/(?:metadata|front_matter)\/?/, "").split("/")[0];
|
|
30
|
+
if (!segment)
|
|
31
|
+
return "metadata";
|
|
32
|
+
return decodeJsonPointerSegment(segment);
|
|
33
|
+
}
|
|
34
|
+
const segment = path.replace(/^\//, "").split("/")[0];
|
|
35
|
+
return segment ? decodeJsonPointerSegment(segment) : "root";
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Field-value reader
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Extract the before/after value for a logical field from a replay document.
|
|
42
|
+
*
|
|
43
|
+
* - "body" → doc.body
|
|
44
|
+
* - "metadata" → doc.metadata (the whole object, for a bare /metadata replace)
|
|
45
|
+
* - "root" → the whole document (for a bare "/" patch op)
|
|
46
|
+
* - other → doc.metadata[field] (may be undefined when the field was absent)
|
|
47
|
+
*/
|
|
48
|
+
function readFieldValue(doc, field) {
|
|
49
|
+
if (field === "body")
|
|
50
|
+
return doc.body;
|
|
51
|
+
if (field === "metadata")
|
|
52
|
+
return doc.metadata;
|
|
53
|
+
if (field === "root")
|
|
54
|
+
return doc;
|
|
55
|
+
return doc.metadata[field];
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Main export
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* Replay a full history entry stream and produce enriched diff entries with
|
|
62
|
+
* per-field before/after values.
|
|
63
|
+
*
|
|
64
|
+
* @param fullEntries The complete ordered history chain to replay.
|
|
65
|
+
* @param options.windowStartIndex 0-based index into fullEntries at which the
|
|
66
|
+
* DISPLAYED window begins. Entries before this index are still replayed (so
|
|
67
|
+
* state is accurate) but are not emitted. Defaults to 0.
|
|
68
|
+
* @param options.field When supplied, only entries that changed this field are
|
|
69
|
+
* emitted, and each emitted entry's `changes` and `changed_fields` are
|
|
70
|
+
* restricted to that single field.
|
|
71
|
+
*/
|
|
72
|
+
export function computeHistoryDiff(fullEntries, options) {
|
|
73
|
+
const windowStartIndex = options?.windowStartIndex ?? 0;
|
|
74
|
+
const fieldFilter = options?.field;
|
|
75
|
+
if (fullEntries.length === 0 || windowStartIndex >= fullEntries.length) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
let current = cloneEmptyReplayDocument();
|
|
79
|
+
const result = [];
|
|
80
|
+
for (let p = 0; p < fullEntries.length; p += 1) {
|
|
81
|
+
const entry = fullEntries[p];
|
|
82
|
+
// Capture before state.
|
|
83
|
+
const beforeDoc = current;
|
|
84
|
+
// Attempt to apply patch.
|
|
85
|
+
const applied = tryApplyReplayPatch(current, entry.patch);
|
|
86
|
+
let afterDoc;
|
|
87
|
+
if (applied.ok) {
|
|
88
|
+
afterDoc = applied.document;
|
|
89
|
+
current = afterDoc;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Patch failed — use before state as after (cannot trust partial result).
|
|
93
|
+
// Do NOT advance current; keep prior replay state.
|
|
94
|
+
afterDoc = beforeDoc;
|
|
95
|
+
}
|
|
96
|
+
// Collect changed field names from patch paths (and from: paths for move/copy).
|
|
97
|
+
const changedFieldSet = new Set();
|
|
98
|
+
for (const patchOp of entry.patch) {
|
|
99
|
+
changedFieldSet.add(patchPathToChangedField(patchOp.path));
|
|
100
|
+
if (patchOp.from !== undefined) {
|
|
101
|
+
changedFieldSet.add(patchPathToChangedField(patchOp.from));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const sortedFields = [...changedFieldSet].sort((a, b) => a.localeCompare(b));
|
|
105
|
+
// Only emit entries in the display window.
|
|
106
|
+
if (p >= windowStartIndex) {
|
|
107
|
+
// Apply field filter: skip this entry entirely if it doesn't touch the
|
|
108
|
+
// requested field; otherwise narrow down to only that field.
|
|
109
|
+
if (fieldFilter !== undefined) {
|
|
110
|
+
if (!changedFieldSet.has(fieldFilter)) {
|
|
111
|
+
// This entry does not touch the requested field — skip it.
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// Emit only the filtered field.
|
|
115
|
+
const change = {
|
|
116
|
+
field: fieldFilter,
|
|
117
|
+
before: readFieldValue(beforeDoc, fieldFilter),
|
|
118
|
+
after: readFieldValue(afterDoc, fieldFilter),
|
|
119
|
+
};
|
|
120
|
+
result.push({
|
|
121
|
+
index: p + 1,
|
|
122
|
+
ts: entry.ts,
|
|
123
|
+
op: entry.op,
|
|
124
|
+
author: entry.author,
|
|
125
|
+
patch_ops: entry.patch.length,
|
|
126
|
+
changed_fields: [fieldFilter],
|
|
127
|
+
changes: [change],
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Emit all changed fields.
|
|
132
|
+
const changes = sortedFields.map((field) => ({
|
|
133
|
+
field,
|
|
134
|
+
before: readFieldValue(beforeDoc, field),
|
|
135
|
+
after: readFieldValue(afterDoc, field),
|
|
136
|
+
}));
|
|
137
|
+
result.push({
|
|
138
|
+
index: p + 1,
|
|
139
|
+
ts: entry.ts,
|
|
140
|
+
op: entry.op,
|
|
141
|
+
author: entry.author,
|
|
142
|
+
patch_ops: entry.patch.length,
|
|
143
|
+
changed_fields: sortedFields,
|
|
144
|
+
changes,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=history-diff.js.map
|
|
152
|
+
//# debugId=2b77be38-f27c-5a56-86de-9625777f7211
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-diff.js","sources":["core/history/history-diff.ts"],"sourceRoot":"/","sourcesContent":["import { cloneEmptyReplayDocument, tryApplyReplayPatch, type ReplayDocument } from \"./replay.js\";\nimport type { HistoryEntry } from \"../../types/index.js\";\n\nexport interface HistoryFieldChange {\n field: string;\n // before and after are the raw field values captured by replaying the history chain.\n // `undefined` is intentional: it signals that the field was absent in the document at\n // that point (e.g. after a JSON-Patch `remove` op). JSON serialization will omit\n // undefined values, which correctly communicates \"field did not exist\".\n before: unknown;\n after: unknown;\n}\n\nexport interface HistoryDiffValueEntry {\n /** 1-based position in the FULL stream (fullEntries index + 1). */\n index: number;\n ts: string;\n op: string;\n author: string;\n /** Number of JSON-patch operations in this entry (entry.patch.length). */\n patch_ops: number;\n /** Unique field names touched by this entry, sorted by localeCompare. */\n changed_fields: string[];\n /** Per-field before/after values, sorted by field name (localeCompare). */\n changes: HistoryFieldChange[];\n}\n\n// ---------------------------------------------------------------------------\n// JSON-Pointer helpers (ported from src/cli/commands/history.ts so the\n// orchestrator can replace the private copy there with an import from here).\n// ---------------------------------------------------------------------------\n\nfunction decodeJsonPointerSegment(segment: string): string {\n return segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\");\n}\n\n/**\n * Map a JSON-Patch path string to a human-readable changed-field name.\n *\n * Rules (in priority order):\n * - \"/body\" or \"/body/…\" → \"body\"\n * - \"/metadata/…\" or \"/front_matter/…\"\n * with a first segment → that decoded segment (e.g. \"status\")\n * without a first segment (bare prefix) → \"metadata\"\n * - \"/\" (root pointer, empty segment after strip) → \"root\"\n * - anything else → decoded first path segment\n */\nexport function patchPathToChangedField(path: string): string {\n if (path === \"/body\" || path.startsWith(\"/body/\")) return \"body\";\n if (\n path === \"/metadata\" ||\n path.startsWith(\"/metadata/\") ||\n path === \"/front_matter\" ||\n path.startsWith(\"/front_matter/\")\n ) {\n const segment = path.replace(/^\\/(?:metadata|front_matter)\\/?/, \"\").split(\"/\")[0];\n if (!segment) return \"metadata\";\n return decodeJsonPointerSegment(segment);\n }\n const segment = path.replace(/^\\//, \"\").split(\"/\")[0];\n return segment ? decodeJsonPointerSegment(segment) : \"root\";\n}\n\n// ---------------------------------------------------------------------------\n// Field-value reader\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the before/after value for a logical field from a replay document.\n *\n * - \"body\" → doc.body\n * - \"metadata\" → doc.metadata (the whole object, for a bare /metadata replace)\n * - \"root\" → the whole document (for a bare \"/\" patch op)\n * - other → doc.metadata[field] (may be undefined when the field was absent)\n */\nfunction readFieldValue(doc: ReplayDocument, field: string): unknown {\n if (field === \"body\") return doc.body;\n if (field === \"metadata\") return doc.metadata;\n if (field === \"root\") return doc;\n return doc.metadata[field];\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Replay a full history entry stream and produce enriched diff entries with\n * per-field before/after values.\n *\n * @param fullEntries The complete ordered history chain to replay.\n * @param options.windowStartIndex 0-based index into fullEntries at which the\n * DISPLAYED window begins. Entries before this index are still replayed (so\n * state is accurate) but are not emitted. Defaults to 0.\n * @param options.field When supplied, only entries that changed this field are\n * emitted, and each emitted entry's `changes` and `changed_fields` are\n * restricted to that single field.\n */\nexport function computeHistoryDiff(\n fullEntries: HistoryEntry[],\n options?: { windowStartIndex?: number; field?: string },\n): HistoryDiffValueEntry[] {\n const windowStartIndex = options?.windowStartIndex ?? 0;\n const fieldFilter = options?.field;\n\n if (fullEntries.length === 0 || windowStartIndex >= fullEntries.length) {\n return [];\n }\n\n let current = cloneEmptyReplayDocument();\n const result: HistoryDiffValueEntry[] = [];\n\n for (let p = 0; p < fullEntries.length; p += 1) {\n const entry = fullEntries[p];\n\n // Capture before state.\n const beforeDoc = current;\n\n // Attempt to apply patch.\n const applied = tryApplyReplayPatch(current, entry.patch);\n let afterDoc: ReplayDocument;\n if (applied.ok) {\n afterDoc = applied.document;\n current = afterDoc;\n } else {\n // Patch failed — use before state as after (cannot trust partial result).\n // Do NOT advance current; keep prior replay state.\n afterDoc = beforeDoc;\n }\n\n // Collect changed field names from patch paths (and from: paths for move/copy).\n const changedFieldSet = new Set<string>();\n for (const patchOp of entry.patch) {\n changedFieldSet.add(patchPathToChangedField(patchOp.path));\n if (patchOp.from !== undefined) {\n changedFieldSet.add(patchPathToChangedField(patchOp.from));\n }\n }\n const sortedFields = [...changedFieldSet].sort((a, b) => a.localeCompare(b));\n\n // Only emit entries in the display window.\n if (p >= windowStartIndex) {\n // Apply field filter: skip this entry entirely if it doesn't touch the\n // requested field; otherwise narrow down to only that field.\n if (fieldFilter !== undefined) {\n if (!changedFieldSet.has(fieldFilter)) {\n // This entry does not touch the requested field — skip it.\n continue;\n }\n // Emit only the filtered field.\n const change: HistoryFieldChange = {\n field: fieldFilter,\n before: readFieldValue(beforeDoc, fieldFilter),\n after: readFieldValue(afterDoc, fieldFilter),\n };\n result.push({\n index: p + 1,\n ts: entry.ts,\n op: entry.op,\n author: entry.author,\n patch_ops: entry.patch.length,\n changed_fields: [fieldFilter],\n changes: [change],\n });\n } else {\n // Emit all changed fields.\n const changes: HistoryFieldChange[] = sortedFields.map((field) => ({\n field,\n before: readFieldValue(beforeDoc, field),\n after: readFieldValue(afterDoc, field),\n }));\n result.push({\n index: p + 1,\n ts: entry.ts,\n op: entry.op,\n author: entry.author,\n patch_ops: entry.patch.length,\n changed_fields: sortedFields,\n changes,\n });\n }\n }\n }\n\n return result;\n}\n"],"names":[],"mappings":"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","debugId":"2b77be38-f27c-5a56-86de-9625777f7211"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure aggregate metrics for history streams (JSONL files).
|
|
3
|
+
* No filesystem access — callers read raw stream contents and pass them in.
|
|
4
|
+
* This module has no Node imports; Buffer is available as a global.
|
|
5
|
+
*/
|
|
6
|
+
export interface HistoryStreamStat {
|
|
7
|
+
id: string;
|
|
8
|
+
bytes: number;
|
|
9
|
+
lines: number;
|
|
10
|
+
oldest_ts: string | null;
|
|
11
|
+
newest_ts: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface HistoryStorageEntryRef {
|
|
14
|
+
id: string;
|
|
15
|
+
ts: string;
|
|
16
|
+
}
|
|
17
|
+
export interface HistoryStorageStats {
|
|
18
|
+
total_streams: number;
|
|
19
|
+
total_lines: number;
|
|
20
|
+
total_bytes: number;
|
|
21
|
+
largest_by_bytes: HistoryStreamStat[];
|
|
22
|
+
deepest_by_lines: HistoryStreamStat[];
|
|
23
|
+
oldest_entry: HistoryStorageEntryRef | null;
|
|
24
|
+
newest_entry: HistoryStorageEntryRef | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function computeHistoryStorageStats(streams: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
raw: string;
|
|
29
|
+
}>, options?: {
|
|
30
|
+
topN?: number;
|
|
31
|
+
}): HistoryStorageStats;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure aggregate metrics for history streams (JSONL files).
|
|
3
|
+
* No filesystem access — callers read raw stream contents and pass them in.
|
|
4
|
+
* This module has no Node imports; Buffer is available as a global.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="006b5d42-0ff6-57af-a598-4ca069ec316e")}catch(e){}}();
|
|
8
|
+
function parseStreamStat(id, raw) {
|
|
9
|
+
const bytes = Buffer.byteLength(raw, "utf8");
|
|
10
|
+
let lines = 0;
|
|
11
|
+
let oldestTs = null;
|
|
12
|
+
let newestTs = null;
|
|
13
|
+
let oldestMs = Number.POSITIVE_INFINITY;
|
|
14
|
+
let newestMs = Number.NEGATIVE_INFINITY;
|
|
15
|
+
for (const rawLine of raw.split(/\r?\n/)) {
|
|
16
|
+
const trimmed = rawLine.trim();
|
|
17
|
+
if (trimmed === "") {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
lines += 1;
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(trimmed);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (parsed === null ||
|
|
29
|
+
typeof parsed !== "object" ||
|
|
30
|
+
!("ts" in parsed) ||
|
|
31
|
+
typeof parsed["ts"] !== "string") {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const ts = parsed["ts"];
|
|
35
|
+
const ms = Date.parse(ts);
|
|
36
|
+
if (Number.isNaN(ms)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (ms < oldestMs || (ms === oldestMs && oldestTs !== null && ts < oldestTs)) {
|
|
40
|
+
oldestMs = ms;
|
|
41
|
+
oldestTs = ts;
|
|
42
|
+
}
|
|
43
|
+
if (ms > newestMs || (ms === newestMs && newestTs !== null && ts > newestTs)) {
|
|
44
|
+
newestMs = ms;
|
|
45
|
+
newestTs = ts;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { id, bytes, lines, oldest_ts: oldestTs, newest_ts: newestTs };
|
|
49
|
+
}
|
|
50
|
+
export function computeHistoryStorageStats(streams, options) {
|
|
51
|
+
const rawTopN = options?.topN ?? 5;
|
|
52
|
+
const topN = Math.max(0, Math.floor(rawTopN));
|
|
53
|
+
const stats = streams.map(({ id, raw }) => parseStreamStat(id, raw));
|
|
54
|
+
let totalLines = 0;
|
|
55
|
+
let totalBytes = 0;
|
|
56
|
+
let oldestEntry = null;
|
|
57
|
+
let newestEntry = null;
|
|
58
|
+
let globalOldestMs = Number.POSITIVE_INFINITY;
|
|
59
|
+
let globalNewestMs = Number.NEGATIVE_INFINITY;
|
|
60
|
+
for (const stat of stats) {
|
|
61
|
+
totalLines += stat.lines;
|
|
62
|
+
totalBytes += stat.bytes;
|
|
63
|
+
if (stat.oldest_ts !== null) {
|
|
64
|
+
const ms = Date.parse(stat.oldest_ts);
|
|
65
|
+
if (ms < globalOldestMs ||
|
|
66
|
+
(ms === globalOldestMs && oldestEntry !== null && stat.id < oldestEntry.id)) {
|
|
67
|
+
globalOldestMs = ms;
|
|
68
|
+
oldestEntry = { id: stat.id, ts: stat.oldest_ts };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (stat.newest_ts !== null) {
|
|
72
|
+
const ms = Date.parse(stat.newest_ts);
|
|
73
|
+
if (ms > globalNewestMs ||
|
|
74
|
+
(ms === globalNewestMs && newestEntry !== null && stat.id < newestEntry.id)) {
|
|
75
|
+
globalNewestMs = ms;
|
|
76
|
+
newestEntry = { id: stat.id, ts: stat.newest_ts };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const largestByBytes = topN === 0
|
|
81
|
+
? []
|
|
82
|
+
: [...stats]
|
|
83
|
+
.sort((a, b) => b.bytes - a.bytes || a.id.localeCompare(b.id))
|
|
84
|
+
.slice(0, topN);
|
|
85
|
+
const deepestByLines = topN === 0
|
|
86
|
+
? []
|
|
87
|
+
: [...stats]
|
|
88
|
+
.sort((a, b) => b.lines - a.lines || a.id.localeCompare(b.id))
|
|
89
|
+
.slice(0, topN);
|
|
90
|
+
return {
|
|
91
|
+
total_streams: streams.length,
|
|
92
|
+
total_lines: totalLines,
|
|
93
|
+
total_bytes: totalBytes,
|
|
94
|
+
largest_by_bytes: largestByBytes,
|
|
95
|
+
deepest_by_lines: deepestByLines,
|
|
96
|
+
oldest_entry: oldestEntry,
|
|
97
|
+
newest_entry: newestEntry,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=history-storage-stats.js.map
|
|
101
|
+
//# debugId=006b5d42-0ff6-57af-a598-4ca069ec316e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-storage-stats.js","sources":["core/history/history-storage-stats.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Pure aggregate metrics for history streams (JSONL files).\n * No filesystem access — callers read raw stream contents and pass them in.\n * This module has no Node imports; Buffer is available as a global.\n */\n\nexport interface HistoryStreamStat {\n id: string;\n bytes: number;\n lines: number; // non-empty JSONL entry count\n oldest_ts: string | null; // min parseable ts in this stream, else null\n newest_ts: string | null; // max parseable ts in this stream, else null\n}\n\nexport interface HistoryStorageEntryRef {\n id: string;\n ts: string;\n}\n\nexport interface HistoryStorageStats {\n total_streams: number;\n total_lines: number;\n total_bytes: number;\n largest_by_bytes: HistoryStreamStat[]; // top-N desc by bytes, tie-break id asc\n deepest_by_lines: HistoryStreamStat[]; // top-N desc by lines, tie-break id asc\n oldest_entry: HistoryStorageEntryRef | null; // global min ts across all streams\n newest_entry: HistoryStorageEntryRef | null; // global max ts across all streams\n}\n\nfunction parseStreamStat(id: string, raw: string): HistoryStreamStat {\n const bytes = Buffer.byteLength(raw, \"utf8\");\n\n let lines = 0;\n let oldestTs: string | null = null;\n let newestTs: string | null = null;\n let oldestMs = Number.POSITIVE_INFINITY;\n let newestMs = Number.NEGATIVE_INFINITY;\n\n for (const rawLine of raw.split(/\\r?\\n/)) {\n const trimmed = rawLine.trim();\n if (trimmed === \"\") {\n continue;\n }\n lines += 1;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n continue;\n }\n\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n !(\"ts\" in (parsed as object)) ||\n typeof (parsed as Record<string, unknown>)[\"ts\"] !== \"string\"\n ) {\n continue;\n }\n\n const ts = (parsed as Record<string, unknown>)[\"ts\"] as string;\n const ms = Date.parse(ts);\n if (Number.isNaN(ms)) {\n continue;\n }\n\n if (ms < oldestMs || (ms === oldestMs && oldestTs !== null && ts < oldestTs)) {\n oldestMs = ms;\n oldestTs = ts;\n }\n if (ms > newestMs || (ms === newestMs && newestTs !== null && ts > newestTs)) {\n newestMs = ms;\n newestTs = ts;\n }\n }\n\n return { id, bytes, lines, oldest_ts: oldestTs, newest_ts: newestTs };\n}\n\nexport function computeHistoryStorageStats(\n streams: Array<{ id: string; raw: string }>,\n options?: { topN?: number },\n): HistoryStorageStats {\n const rawTopN = options?.topN ?? 5;\n const topN = Math.max(0, Math.floor(rawTopN));\n\n const stats: HistoryStreamStat[] = streams.map(({ id, raw }) => parseStreamStat(id, raw));\n\n let totalLines = 0;\n let totalBytes = 0;\n let oldestEntry: HistoryStorageEntryRef | null = null;\n let newestEntry: HistoryStorageEntryRef | null = null;\n let globalOldestMs = Number.POSITIVE_INFINITY;\n let globalNewestMs = Number.NEGATIVE_INFINITY;\n\n for (const stat of stats) {\n totalLines += stat.lines;\n totalBytes += stat.bytes;\n\n if (stat.oldest_ts !== null) {\n const ms = Date.parse(stat.oldest_ts);\n if (\n ms < globalOldestMs ||\n (ms === globalOldestMs && oldestEntry !== null && stat.id < oldestEntry.id)\n ) {\n globalOldestMs = ms;\n oldestEntry = { id: stat.id, ts: stat.oldest_ts };\n }\n }\n\n if (stat.newest_ts !== null) {\n const ms = Date.parse(stat.newest_ts);\n if (\n ms > globalNewestMs ||\n (ms === globalNewestMs && newestEntry !== null && stat.id < newestEntry.id)\n ) {\n globalNewestMs = ms;\n newestEntry = { id: stat.id, ts: stat.newest_ts };\n }\n }\n }\n\n const largestByBytes =\n topN === 0\n ? []\n : [...stats]\n .sort((a, b) => b.bytes - a.bytes || a.id.localeCompare(b.id))\n .slice(0, topN);\n\n const deepestByLines =\n topN === 0\n ? []\n : [...stats]\n .sort((a, b) => b.lines - a.lines || a.id.localeCompare(b.id))\n .slice(0, topN);\n\n return {\n total_streams: streams.length,\n total_lines: totalLines,\n total_bytes: totalBytes,\n largest_by_bytes: largestByBytes,\n deepest_by_lines: deepestByLines,\n oldest_entry: oldestEntry,\n newest_entry: newestEntry,\n };\n}\n"],"names":[],"mappings":"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","debugId":"006b5d42-0ff6-57af-a598-4ca069ec316e"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lock-gc.ts — Stale lock file garbage collector.
|
|
3
|
+
*
|
|
4
|
+
* Safety-first policy: if a lock file cannot be read or parsed, it is RETAINED
|
|
5
|
+
* rather than deleted. We only remove locks whose staleness can be conclusively
|
|
6
|
+
* determined from the lock's own embedded `ttl_seconds` field.
|
|
7
|
+
*
|
|
8
|
+
* Staleness formula: (now_ms - Date.parse(created_at)) > ttl_seconds * 1000
|
|
9
|
+
* Zero ttl means any lock older than 0 ms is stale. Negative ttl means always stale.
|
|
10
|
+
*/
|
|
11
|
+
export interface StaleLockScanEntry {
|
|
12
|
+
/** e.g. "pm-abc1.lock" */
|
|
13
|
+
file: string;
|
|
14
|
+
/** parsed id, or null if unparseable */
|
|
15
|
+
id: string | null;
|
|
16
|
+
owner: string | null;
|
|
17
|
+
created_at: string | null;
|
|
18
|
+
ttl_seconds: number | null;
|
|
19
|
+
/** floor((now - created_at)/1000), null if unparseable */
|
|
20
|
+
age_seconds: number | null;
|
|
21
|
+
/** true only when classified expired */
|
|
22
|
+
stale: boolean;
|
|
23
|
+
reason: "expired" | "active" | "unparseable";
|
|
24
|
+
}
|
|
25
|
+
export interface LockGcHooks {
|
|
26
|
+
onRead?: (lockPath: string) => Promise<string[]> | string[];
|
|
27
|
+
onWrite?: (lockPath: string) => Promise<string[]> | string[];
|
|
28
|
+
}
|
|
29
|
+
export interface LockGcOptions {
|
|
30
|
+
dryRun: boolean;
|
|
31
|
+
/** epoch ms; defaults to Date.now() — injectable for deterministic tests */
|
|
32
|
+
now?: number;
|
|
33
|
+
hooks?: LockGcHooks;
|
|
34
|
+
}
|
|
35
|
+
export interface LockGcResult {
|
|
36
|
+
/** number of *.lock files examined */
|
|
37
|
+
scanned: number;
|
|
38
|
+
/** relative paths like "locks/pm-abc1.lock" (removed, or would-remove under dryRun) */
|
|
39
|
+
removed: string[];
|
|
40
|
+
/** relative paths retained (active OR unparseable — never delete what we can't reason about) */
|
|
41
|
+
retained: string[];
|
|
42
|
+
warnings: string[];
|
|
43
|
+
/** one per scanned *.lock file, sorted by file name asc */
|
|
44
|
+
entries: StaleLockScanEntry[];
|
|
45
|
+
}
|
|
46
|
+
export declare function runLockGc(pmRoot: string, options: LockGcOptions): Promise<LockGcResult>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lock-gc.ts — Stale lock file garbage collector.
|
|
3
|
+
*
|
|
4
|
+
* Safety-first policy: if a lock file cannot be read or parsed, it is RETAINED
|
|
5
|
+
* rather than deleted. We only remove locks whose staleness can be conclusively
|
|
6
|
+
* determined from the lock's own embedded `ttl_seconds` field.
|
|
7
|
+
*
|
|
8
|
+
* Staleness formula: (now_ms - Date.parse(created_at)) > ttl_seconds * 1000
|
|
9
|
+
* Zero ttl means any lock older than 0 ms is stale. Negative ttl means always stale.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1a412027-5c73-58d4-b696-4a909dde88a8")}catch(e){}}();
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
function parseLock(raw) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = JSON.parse(raw);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const candidate = parsed;
|
|
27
|
+
const { id, owner, created_at, ttl_seconds } = candidate;
|
|
28
|
+
if (typeof id !== "string" ||
|
|
29
|
+
typeof owner !== "string" ||
|
|
30
|
+
typeof created_at !== "string" ||
|
|
31
|
+
typeof ttl_seconds !== "number" ||
|
|
32
|
+
!Number.isFinite(ttl_seconds)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return { id, owner, created_at, ttl_seconds };
|
|
36
|
+
}
|
|
37
|
+
function isErrno(error, code) {
|
|
38
|
+
return (typeof error === "object" &&
|
|
39
|
+
error !== null &&
|
|
40
|
+
"code" in error &&
|
|
41
|
+
error.code === code);
|
|
42
|
+
}
|
|
43
|
+
export async function runLockGc(pmRoot, options) {
|
|
44
|
+
const { dryRun, hooks } = options;
|
|
45
|
+
const nowMs = options.now ?? Date.now();
|
|
46
|
+
const locksDir = path.join(pmRoot, "locks");
|
|
47
|
+
const result = {
|
|
48
|
+
scanned: 0,
|
|
49
|
+
removed: [],
|
|
50
|
+
retained: [],
|
|
51
|
+
warnings: [],
|
|
52
|
+
entries: [],
|
|
53
|
+
};
|
|
54
|
+
let dirEntries;
|
|
55
|
+
try {
|
|
56
|
+
const raw = await fs.readdir(locksDir);
|
|
57
|
+
dirEntries = raw.filter((f) => f.endsWith(".lock")).sort();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (isErrno(error, "ENOENT")) {
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
for (const file of dirEntries) {
|
|
66
|
+
result.scanned += 1;
|
|
67
|
+
const absPath = path.join(locksDir, file);
|
|
68
|
+
const relPath = `locks/${file}`;
|
|
69
|
+
// --- onRead hook ---
|
|
70
|
+
if (hooks?.onRead) {
|
|
71
|
+
const hookWarnings = await hooks.onRead(absPath);
|
|
72
|
+
result.warnings.push(...hookWarnings);
|
|
73
|
+
}
|
|
74
|
+
// --- read file ---
|
|
75
|
+
let raw;
|
|
76
|
+
try {
|
|
77
|
+
raw = await fs.readFile(absPath, "utf8");
|
|
78
|
+
}
|
|
79
|
+
catch (readError) {
|
|
80
|
+
if (isErrno(readError, "ENOENT")) {
|
|
81
|
+
// Disappeared between readdir and readFile — treat as a ghost; skip quietly.
|
|
82
|
+
result.scanned -= 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
result.warnings.push(`lock_unreadable:${file}`);
|
|
86
|
+
result.retained.push(relPath);
|
|
87
|
+
result.entries.push({
|
|
88
|
+
file,
|
|
89
|
+
id: null,
|
|
90
|
+
owner: null,
|
|
91
|
+
created_at: null,
|
|
92
|
+
ttl_seconds: null,
|
|
93
|
+
age_seconds: null,
|
|
94
|
+
stale: false,
|
|
95
|
+
reason: "unparseable",
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// --- parse ---
|
|
100
|
+
const lock = parseLock(raw);
|
|
101
|
+
if (lock === null) {
|
|
102
|
+
result.warnings.push(`lock_unparseable:${file}`);
|
|
103
|
+
result.retained.push(relPath);
|
|
104
|
+
result.entries.push({
|
|
105
|
+
file,
|
|
106
|
+
id: null,
|
|
107
|
+
owner: null,
|
|
108
|
+
created_at: null,
|
|
109
|
+
ttl_seconds: null,
|
|
110
|
+
age_seconds: null,
|
|
111
|
+
stale: false,
|
|
112
|
+
reason: "unparseable",
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// --- validate timestamp ---
|
|
117
|
+
const createdAtMs = Date.parse(lock.created_at);
|
|
118
|
+
if (!Number.isFinite(createdAtMs)) {
|
|
119
|
+
result.warnings.push(`lock_invalid_timestamp:${file}`);
|
|
120
|
+
result.retained.push(relPath);
|
|
121
|
+
result.entries.push({
|
|
122
|
+
file,
|
|
123
|
+
id: lock.id,
|
|
124
|
+
owner: lock.owner,
|
|
125
|
+
created_at: lock.created_at,
|
|
126
|
+
ttl_seconds: lock.ttl_seconds,
|
|
127
|
+
age_seconds: null,
|
|
128
|
+
stale: false,
|
|
129
|
+
reason: "unparseable",
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const ageMs = nowMs - createdAtMs;
|
|
134
|
+
const age_seconds = Math.floor(ageMs / 1000);
|
|
135
|
+
const stale = ageMs > lock.ttl_seconds * 1000;
|
|
136
|
+
if (!stale) {
|
|
137
|
+
result.retained.push(relPath);
|
|
138
|
+
result.entries.push({
|
|
139
|
+
file,
|
|
140
|
+
id: lock.id,
|
|
141
|
+
owner: lock.owner,
|
|
142
|
+
created_at: lock.created_at,
|
|
143
|
+
ttl_seconds: lock.ttl_seconds,
|
|
144
|
+
age_seconds,
|
|
145
|
+
stale: false,
|
|
146
|
+
reason: "active",
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// --- stale: remove or preview ---
|
|
151
|
+
if (!dryRun) {
|
|
152
|
+
try {
|
|
153
|
+
await fs.unlink(absPath);
|
|
154
|
+
}
|
|
155
|
+
catch (unlinkError) {
|
|
156
|
+
if (!isErrno(unlinkError, "ENOENT")) {
|
|
157
|
+
result.warnings.push(`lock_remove_failed:${file}`);
|
|
158
|
+
result.retained.push(relPath);
|
|
159
|
+
result.entries.push({
|
|
160
|
+
file,
|
|
161
|
+
id: lock.id,
|
|
162
|
+
owner: lock.owner,
|
|
163
|
+
created_at: lock.created_at,
|
|
164
|
+
ttl_seconds: lock.ttl_seconds,
|
|
165
|
+
age_seconds,
|
|
166
|
+
stale: true,
|
|
167
|
+
reason: "expired",
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// ENOENT on unlink = already gone (race); treat as removed
|
|
172
|
+
}
|
|
173
|
+
// onWrite hook after successful removal
|
|
174
|
+
if (hooks?.onWrite) {
|
|
175
|
+
const hookWarnings = await hooks.onWrite(absPath);
|
|
176
|
+
result.warnings.push(...hookWarnings);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
result.removed.push(relPath);
|
|
180
|
+
result.entries.push({
|
|
181
|
+
file,
|
|
182
|
+
id: lock.id,
|
|
183
|
+
owner: lock.owner,
|
|
184
|
+
created_at: lock.created_at,
|
|
185
|
+
ttl_seconds: lock.ttl_seconds,
|
|
186
|
+
age_seconds,
|
|
187
|
+
stale: true,
|
|
188
|
+
reason: "expired",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=lock-gc.js.map
|
|
194
|
+
//# debugId=1a412027-5c73-58d4-b696-4a909dde88a8
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-gc.js","sources":["core/lock/lock-gc.ts"],"sourceRoot":"/","sourcesContent":["/**\n * lock-gc.ts — Stale lock file garbage collector.\n *\n * Safety-first policy: if a lock file cannot be read or parsed, it is RETAINED\n * rather than deleted. We only remove locks whose staleness can be conclusively\n * determined from the lock's own embedded `ttl_seconds` field.\n *\n * Staleness formula: (now_ms - Date.parse(created_at)) > ttl_seconds * 1000\n * Zero ttl means any lock older than 0 ms is stale. Negative ttl means always stale.\n */\n\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface StaleLockScanEntry {\n /** e.g. \"pm-abc1.lock\" */\n file: string;\n /** parsed id, or null if unparseable */\n id: string | null;\n owner: string | null;\n created_at: string | null;\n ttl_seconds: number | null;\n /** floor((now - created_at)/1000), null if unparseable */\n age_seconds: number | null;\n /** true only when classified expired */\n stale: boolean;\n reason: \"expired\" | \"active\" | \"unparseable\";\n}\n\nexport interface LockGcHooks {\n onRead?: (lockPath: string) => Promise<string[]> | string[];\n onWrite?: (lockPath: string) => Promise<string[]> | string[];\n}\n\nexport interface LockGcOptions {\n dryRun: boolean;\n /** epoch ms; defaults to Date.now() — injectable for deterministic tests */\n now?: number;\n hooks?: LockGcHooks;\n}\n\nexport interface LockGcResult {\n /** number of *.lock files examined */\n scanned: number;\n /** relative paths like \"locks/pm-abc1.lock\" (removed, or would-remove under dryRun) */\n removed: string[];\n /** relative paths retained (active OR unparseable — never delete what we can't reason about) */\n retained: string[];\n warnings: string[];\n /** one per scanned *.lock file, sorted by file name asc */\n entries: StaleLockScanEntry[];\n}\n\ninterface ParsedLock {\n id: string;\n owner: string;\n created_at: string;\n ttl_seconds: number;\n}\n\nfunction parseLock(raw: string): ParsedLock | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw) as unknown;\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return null;\n }\n const candidate = parsed as Record<string, unknown>;\n const { id, owner, created_at, ttl_seconds } = candidate;\n if (\n typeof id !== \"string\" ||\n typeof owner !== \"string\" ||\n typeof created_at !== \"string\" ||\n typeof ttl_seconds !== \"number\" ||\n !Number.isFinite(ttl_seconds)\n ) {\n return null;\n }\n return { id, owner, created_at, ttl_seconds };\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as { code?: string }).code === code\n );\n}\n\nexport async function runLockGc(pmRoot: string, options: LockGcOptions): Promise<LockGcResult> {\n const { dryRun, hooks } = options;\n const nowMs = options.now ?? Date.now();\n const locksDir = path.join(pmRoot, \"locks\");\n\n const result: LockGcResult = {\n scanned: 0,\n removed: [],\n retained: [],\n warnings: [],\n entries: [],\n };\n\n let dirEntries: string[];\n try {\n const raw = await fs.readdir(locksDir);\n dirEntries = raw.filter((f) => f.endsWith(\".lock\")).sort();\n } catch (error: unknown) {\n if (isErrno(error, \"ENOENT\")) {\n return result;\n }\n throw error;\n }\n\n for (const file of dirEntries) {\n result.scanned += 1;\n const absPath = path.join(locksDir, file);\n const relPath = `locks/${file}`;\n\n // --- onRead hook ---\n if (hooks?.onRead) {\n const hookWarnings = await hooks.onRead(absPath);\n result.warnings.push(...hookWarnings);\n }\n\n // --- read file ---\n let raw: string;\n try {\n raw = await fs.readFile(absPath, \"utf8\");\n } catch (readError: unknown) {\n if (isErrno(readError, \"ENOENT\")) {\n // Disappeared between readdir and readFile — treat as a ghost; skip quietly.\n result.scanned -= 1;\n continue;\n }\n result.warnings.push(`lock_unreadable:${file}`);\n result.retained.push(relPath);\n result.entries.push({\n file,\n id: null,\n owner: null,\n created_at: null,\n ttl_seconds: null,\n age_seconds: null,\n stale: false,\n reason: \"unparseable\",\n });\n continue;\n }\n\n // --- parse ---\n const lock = parseLock(raw);\n if (lock === null) {\n result.warnings.push(`lock_unparseable:${file}`);\n result.retained.push(relPath);\n result.entries.push({\n file,\n id: null,\n owner: null,\n created_at: null,\n ttl_seconds: null,\n age_seconds: null,\n stale: false,\n reason: \"unparseable\",\n });\n continue;\n }\n\n // --- validate timestamp ---\n const createdAtMs = Date.parse(lock.created_at);\n if (!Number.isFinite(createdAtMs)) {\n result.warnings.push(`lock_invalid_timestamp:${file}`);\n result.retained.push(relPath);\n result.entries.push({\n file,\n id: lock.id,\n owner: lock.owner,\n created_at: lock.created_at,\n ttl_seconds: lock.ttl_seconds,\n age_seconds: null,\n stale: false,\n reason: \"unparseable\",\n });\n continue;\n }\n\n const ageMs = nowMs - createdAtMs;\n const age_seconds = Math.floor(ageMs / 1000);\n const stale = ageMs > lock.ttl_seconds * 1000;\n\n if (!stale) {\n result.retained.push(relPath);\n result.entries.push({\n file,\n id: lock.id,\n owner: lock.owner,\n created_at: lock.created_at,\n ttl_seconds: lock.ttl_seconds,\n age_seconds,\n stale: false,\n reason: \"active\",\n });\n continue;\n }\n\n // --- stale: remove or preview ---\n if (!dryRun) {\n try {\n await fs.unlink(absPath);\n } catch (unlinkError: unknown) {\n if (!isErrno(unlinkError, \"ENOENT\")) {\n result.warnings.push(`lock_remove_failed:${file}`);\n result.retained.push(relPath);\n result.entries.push({\n file,\n id: lock.id,\n owner: lock.owner,\n created_at: lock.created_at,\n ttl_seconds: lock.ttl_seconds,\n age_seconds,\n stale: true,\n reason: \"expired\",\n });\n continue;\n }\n // ENOENT on unlink = already gone (race); treat as removed\n }\n // onWrite hook after successful removal\n if (hooks?.onWrite) {\n const hookWarnings = await hooks.onWrite(absPath);\n result.warnings.push(...hookWarnings);\n }\n }\n\n result.removed.push(relPath);\n result.entries.push({\n file,\n id: lock.id,\n owner: lock.owner,\n created_at: lock.created_at,\n ttl_seconds: lock.ttl_seconds,\n age_seconds,\n stale: true,\n reason: \"expired\",\n });\n }\n\n return result;\n}\n"],"names":[],"mappings":"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","debugId":"1a412027-5c73-58d4-b696-4a909dde88a8"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SerialQueue {
|
|
2
|
+
/**
|
|
3
|
+
* Schedule `task` to run after every previously-enqueued task has settled.
|
|
4
|
+
* Returns a promise for this task's result (or rejection) so callers can
|
|
5
|
+
* await individual outcomes. A rejected task never wedges the queue: later
|
|
6
|
+
* tasks still run in order.
|
|
7
|
+
*/
|
|
8
|
+
enqueue<T>(task: () => Promise<T> | T): Promise<T>;
|
|
9
|
+
/** Resolves once the queue has fully drained (no pending tasks remain). */
|
|
10
|
+
idle(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createSerialQueue(): SerialQueue;
|