@nicnocquee/dataqueue 1.30.0 → 1.31.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/log-context.ts","../src/backends/postgres.ts","../src/queue.ts","../src/processor.ts","../src/db-util.ts","../src/backends/redis-scripts.ts","../src/backends/redis.ts","../src/handler-validation.ts","../src/index.ts"],"names":["JobEventType","FailureReason","AsyncLocalStorage","randomUUID","Worker","fs","parse","Pool","createRequire","validateHandlerSerializable"],"mappings":";;;;;;;;;;;;;;;;AA0FO,IAAK,YAAA,qBAAAA,aAAAA,KAAL;AACL,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,cAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AATA,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;AAoBL,IAAK,aAAA,qBAAAC,cAAAA,KAAL;AACL,EAAAA,eAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,eAAA,cAAA,CAAA,GAAe,eAAA;AACf,EAAAA,eAAA,WAAA,CAAA,GAAY,YAAA;AAHF,EAAA,OAAAA,cAAAA;AAAA,CAAA,EAAA,aAAA,IAAA,EAAA;AA2OL,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAGpC,WAAA,CACkB,IAAA,EACA,SAAA,EACA,OAAA,EACA,QAAA,EAChB;AACA,IAAA,KAAA,CAAM,YAAY,CAAA;AALF,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AANlB,IAAA,IAAA,CAAS,YAAA,GAAe,IAAA;AAStB,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AAAA;AAEhB;ACnWO,IAAM,UAAA,GAAa,IAAIC,6BAAA,EAE3B;AAEI,IAAM,aAAA,GAAgB,CAAC,OAAA,KAAqB;AACjD,EAAA,UAAA,CAAW,SAAA,CAAU,EAAE,OAAA,EAAS,CAAA;AAClC,CAAA;AAEO,IAAM,gBAAgB,MAAM;AACjC,EAAA,OAAO,WAAW,QAAA,EAAS;AAC7B,CAAA;AAEO,IAAM,GAAA,GAAM,CAAC,OAAA,KAAoB;AACtC,EAAA,MAAM,UAAU,aAAA,EAAc;AAC9B,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,OAAA,CAAQ,IAAI,OAAO,CAAA;AAAA;AAEvB,CAAA;;;ACNO,IAAM,kBAAN,MAA8C;AAAA,EACnD,YAAoB,IAAA,EAAY;AAAZ,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAa;AAAA,EAGjC,OAAA,GAAgB;AACd,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA;AACd;AAAA,EAIA,MAAM,cAAA,CACJ,KAAA,EACA,SAAA,EACA,QAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,yEAAA,CAAA;AAAA,QACA,CAAC,OAAO,SAAA,EAAW,QAAA,GAAW,KAAK,SAAA,CAAU,QAAQ,IAAI,IAAI;AAAA,OAC/D;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE5D,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,aAAa,KAAA,EAAoC;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,KAAA;AAAA,QACvB,CAAA,sJAAA,CAAA;AAAA,QACA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,OAAO,GAAA,CAAI,IAAA;AAAA,KACb,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,MAAA,CAAkD;AAAA,IACtD,OAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA,GAAc,CAAA;AAAA,IACd,QAAA,GAAW,CAAA;AAAA,IACX,KAAA,GAAQ,IAAA;AAAA,IACR,SAAA,GAAY,MAAA;AAAA,IACZ,kBAAA,GAAqB,KAAA;AAAA,IACrB,IAAA,GAAO,MAAA;AAAA,IACP,cAAA,GAAiB;AAAA,GACnB,EAA+C;AAC7C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,MAAA;AACJ,MAAA,MAAM,UAAA,GAAa,iBACf,CAAA,0EAAA,CAAA,GACA,EAAA;AAEJ,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,UACpB,CAAA;AAAA;AAAA;AAAA,WAAA,EAGG,UAAU;AAAA,uBAAA,CAAA;AAAA,UAEb;AAAA,YACE,OAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,QAAA;AAAA,YACA,KAAA;AAAA,YACA,SAAA,IAAa,IAAA;AAAA,YACb,kBAAA,IAAsB,KAAA;AAAA,YACtB,IAAA,IAAQ,IAAA;AAAA,YACR,cAAA,IAAkB;AAAA;AACpB,SACF;AAAA,OACF,MAAO;AACL,QAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,UACpB,CAAA;AAAA;AAAA;AAAA,WAAA,EAGG,UAAU;AAAA,uBAAA,CAAA;AAAA,UAEb;AAAA,YACE,OAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,QAAA;AAAA,YACA,SAAA,IAAa,IAAA;AAAA,YACb,kBAAA,IAAsB,KAAA;AAAA,YACtB,IAAA,IAAQ,IAAA;AAAA,YACR,cAAA,IAAkB;AAAA;AACpB,SACF;AAAA;AAIF,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,IAAK,cAAA,EAAgB;AAC9C,QAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,KAAA;AAAA,UAC5B,CAAA,mDAAA,CAAA;AAAA,UACA,CAAC,cAAc;AAAA,SACjB;AACA,QAAA,IAAI,QAAA,CAAS,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC5B,UAAA,GAAA;AAAA,YACE,6BAA6B,cAAc,CAAA,sBAAA,EAAyB,SAAS,IAAA,CAAK,CAAC,EAAE,EAAE,CAAA,yBAAA;AAAA,WACzF;AACA,UAAA,OAAO,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,EAAA;AAAA;AAE1B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,8EAA8E,cAAc,CAAA,CAAA;AAAA,SAC9F;AAAA;AAGF,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAAE,EAAA;AAC7B,MAAA,GAAA;AAAA,QACE,CAAA,UAAA,EAAa,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA,EAAA,EAAK,KAAA,GAAQ,CAAA,MAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,SAAA,EAAY,QAAQ,CAAA,cAAA,EAAiB,WAAW,CAAA,UAAA,EAAa,OAAO,UAAU,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,cAAA,GAAiB,CAAA,kBAAA,EAAqB,cAAc,MAAM,EAAE,CAAA;AAAA,OAC3Q;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,OAAA,cAA2B;AAAA,QACnD,OAAA;AAAA,QACA,OAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,OAAO,KAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,CAAE,CAAA;AAChC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OACJ,EAAA,EAC0C;AAC1C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,oyBAAA,CAAA;AAAA,QACA,CAAC,EAAE;AAAA,OACL;AAEA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAC5B,QAAA,GAAA,CAAI,CAAA,IAAA,EAAO,EAAE,CAAA,UAAA,CAAY,CAAA;AACzB,QAAA,OAAO,IAAA;AAAA;AAGT,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,EAAE,CAAA,CAAE,CAAA;AACrB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AACzB,MAAA,OAAO;AAAA,QACL,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACvC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,eAAA,CACJ,MAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,80BAAA,CAAA;AAAA,QACA,CAAC,MAAA,EAAQ,KAAA,EAAO,MAAM;AAAA,OACxB;AACA,MAAA,GAAA,CAAI,SAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,gBAAA,EAAmB,MAAM,CAAA,CAAE,CAAA;AAC1D,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,MAAM,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACtD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,UAAA,CACJ,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,4zBAAA,CAAA;AAAA,QACA,CAAC,OAAO,MAAM;AAAA,OAChB;AACA,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,WAAA,CAAa,CAAA;AAC5C,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI;AAAA,OAC1B,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,wBAAA,EAA2B,KAAK,CAAA,CAAE,CAAA;AACtC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CACJ,OAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ,CAAA,sxBAAA,CAAA;AACZ,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,MAAM,QAAkB,EAAC;AACzB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AACtC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AACtC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IACE,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,KACxB,QAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,IACpB,OAAA,CAAQ,KAAA,CAAM,GAAA,KAAQ,UACtB,OAAA,CAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,IACrB,OAAA,CAAQ,KAAA,CAAM,QAAQ,KAAA,CAAA,IACtB,OAAA,CAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,CAAA,EACvB;AACA,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AAOpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AAClC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAA,CAAG,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AAGF,QAAA,IAAI,OAAA,CAAQ,WAAW,KAAA,CAAA,EAAW;AAChC,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,QAAA,EAAU,CAAA,CAAE,CAAA;AAChC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA;AAC5B;AAEF,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,KAAA,IAAS,CAAA,OAAA,EAAU,KAAA,CAAM,IAAA,CAAK,OAAO,CAAC,CAAA,CAAA;AAAA;AAExC,MAAA,QAAA,GAAW,OAAO,MAAA,GAAS,CAAA;AAE3B,MAAA,KAAA,IAAS,4BAA4B,QAAA,EAAU,CAAA,CAAA;AAE/C,MAAA,IAAI,CAAC,SAAS,MAAA,EAAQ;AACpB,QAAA,KAAA,IAAS,YAAY,QAAQ,CAAA,CAAA;AAC7B,QAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AAAA,OAC3B,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA;AAEnB,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,KAAA,CAAO,CAAA;AACtC,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,KAAK,CAAA,CAAE,CAAA;AAClC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,cACJ,IAAA,EACA,IAAA,GAAqB,OACrB,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ,CAAA;AAAA,uBAAA,CAAA;AAEZ,MAAA,IAAI,SAAgB,EAAC;AACrB,MAAA,QAAQ,IAAA;AAAM,QACZ,KAAK,OAAA;AACH,UAAA,KAAA,IAAS,kBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,KAAA;AACH,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,KAAA;AACH,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,MAAA;AACH,UAAA,KAAA,IAAS,yBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF;AACE,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AAAA;AAElB,MAAA,KAAA,IAAS,8CAAA;AACT,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AACzB,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA;AAAA,QACE,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,cAAA,EAAiB,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,QAAA,EAAW,IAAI,CAAA,CAAA;AAAA,OACjF;AACA,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA;AAAA,QACE,CAAA,2BAAA,EAA8B,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,QAAA,EAAW,IAAI,MAAM,KAAK,CAAA;AAAA,OAC9E;AACA,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,YAAA,CACJ,QAAA,EACA,SAAA,GAAY,IACZ,OAAA,EACqC;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAE1B,MAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,MAAA,MAAM,MAAA,GAAgB,CAAC,QAAA,EAAU,SAAS,CAAA;AAC1C,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC1B,UAAA,aAAA,GAAgB,CAAA,uBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,SACrB,MAAO;AACL,UAAA,aAAA,GAAgB,CAAA,kBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA;AACrB;AAGF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAAA,EA0BI,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAOjB;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,gBAAA,CAAkB,CAAA;AACjD,MAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAG3B,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC1B,QAAA,MAAM,IAAA,CAAK,oBAAA;AAAA,UACT,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,YACxB,OAAO,GAAA,CAAI,EAAA;AAAA,YACX,SAAA,EAAA,YAAA;AAAA,WACF,CAAE;AAAA,SACJ;AAAA;AAGF,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI;AAAA,OAC1B,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,0BAAA,EAA6B,KAAK,CAAA,CAAE,CAAA;AACxC,MAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,YAAY,KAAA,EAA8B;AAC9C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,mEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC7C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,KAAA,EACA,aAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAaA;AAAA,UACE,KAAA;AAAA,UACA,KAAK,SAAA,CAAU;AAAA,YACb;AAAA,cACE,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,cACtC,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACpC,WACD,CAAA;AAAA,UACD,aAAA,IAAiB;AAAA;AACnB,OACF;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,wEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,QAAA,eAA4B;AAAA,QACpD,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,QACtC;AAAA,OACD,CAAA;AACD,MAAA,GAAA,CAAI,CAAA,WAAA,EAAc,KAAK,CAAA,CAAE,CAAA;AAAA,aAClB,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,EAAA,EAAK,GAAG,CAAA,CAAE,CAAA;AACxC,MAAA,MAAM,GAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,WAAW,KAAA,EAA8B;AAC7C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAKA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE/C,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,cAAA,CAAe,KAAA,EAAe,QAAA,EAAiC;AACnE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,oEAAA,CAAA;AAAA,QACA,CAAC,OAAO,QAAQ;AAAA,OAClB;AACA,MAAA,GAAA,CAAI,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,aAC9C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE1D,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,SAAS,KAAA,EAA8B;AAC3C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAUA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,wEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,SAAA,eAA2B;AACrD,MAAA,GAAA,CAAI,CAAA,YAAA,EAAe,KAAK,CAAA,CAAE,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,mBAAA,EAAsB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,UAAU,KAAA,EAA8B;AAC5C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC7C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,sBAAsB,OAAA,EAAuC;AACjE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ;AAAA;AAAA;AAAA,gCAAA,CAAA;AAIZ,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IAAW,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,EAAU;AAC5C,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AACpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,IAAS,gBAAgB,QAAA,EAAU,CAAA,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,IAAS,sBAAsB,QAAA,EAAU,CAAA,CAAA,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AACF;AAEF,MAAA,KAAA,IAAS,gBAAA;AACT,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,CAAA,KAAA,CAAO,CAAA;AACvC,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,CAAE,CAAA;AAC9C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAoC;AAC/D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,eAAyB,EAAC;AAChC,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AAEf,MAAA,IAAI,OAAA,CAAQ,YAAY,KAAA,CAAA,EAAW;AACjC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AAC5C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,MAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,CAAA,EAAW;AACrC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,gBAAA,EAAmB,QAAA,EAAU,CAAA,CAAE,CAAA;AACjD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,WAAW,CAAA;AAAA;AAEjC,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,UAAU,KAAA,CAAA,EAAW;AAC/B,QAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,UAAA,YAAA,CAAa,KAAK,CAAA,cAAA,CAAgB,CAAA;AAAA,SACpC,MAAO;AACL,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AAC3C,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA;AAC3B;AAEF,MAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,EAAW;AACnC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAE,CAAA;AAC/C,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,IAAI,CAAA;AAAA;AAEvC,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,CAAA,EAAW;AAC9B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AACzC,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAA,IAAQ,IAAI,CAAA;AAAA;AAGlC,MAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,QAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,QAAA;AAAA;AAGF,MAAA,YAAA,CAAa,KAAK,CAAA,kBAAA,CAAoB,CAAA;AACtC,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAEjB,MAAA,MAAM,KAAA,GAAQ;AAAA;AAAA,YAAA,EAEN,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC;AAAA,oBAAA,EACf,QAAQ,CAAA;AAAA,MAAA,CAAA;AAGxB,MAAA,MAAM,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAEhC,MAAA,MAAM,WAAgB,EAAC;AACvB,MAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,CAAA,EAAW,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAC9D,MAAA,IAAI,QAAQ,WAAA,KAAgB,KAAA,CAAA;AAC1B,QAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AACjC,MAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,KAAA,CAAA,EAAW,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAChE,MAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,KAAA,CAAA,EAAW,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAC1D,MAAA,IAAI,QAAQ,SAAA,KAAc,KAAA,CAAA;AACxB,QAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAC/B,MAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,KAAA,CAAA,EAAW,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAExD,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,KAAA,EAAA,QAAA,eAA4B,QAAQ,CAAA;AAC9D,MAAA,GAAA,CAAI,cAAc,KAAK,CAAA,EAAA,EAAK,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA,aAC/C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,kBAAA,CACJ,OAAA,GAAkC,MAAA,EAClC,OAAA,EACiB;AACjB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,eAAyB,EAAC;AAChC,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AAEf,MAAA,IAAI,OAAA,CAAQ,YAAY,KAAA,CAAA,EAAW;AACjC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AAC5C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,MAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,CAAA,EAAW;AACrC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,gBAAA,EAAmB,QAAA,EAAU,CAAA,CAAE,CAAA;AACjD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,WAAW,CAAA;AAAA;AAEjC,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,UAAU,KAAA,CAAA,EAAW;AAC/B,QAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,UAAA,YAAA,CAAa,KAAK,CAAA,cAAA,CAAgB,CAAA;AAAA,SACpC,MAAO;AACL,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AAC3C,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA;AAC3B;AAEF,MAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,EAAW;AACnC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAE,CAAA;AAC/C,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,IAAI,CAAA;AAAA;AAEvC,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,CAAA,EAAW;AAC9B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AACzC,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAA,IAAQ,IAAI,CAAA;AAAA;AAGlC,MAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,QAAA,GAAA,CAAI,CAAA,kCAAA,CAAoC,CAAA;AACxC,QAAA,OAAO,CAAA;AAAA;AAGT,MAAA,YAAA,CAAa,KAAK,CAAA,kBAAA,CAAoB,CAAA;AAEtC,MAAA,IAAI,KAAA,GAAQ;AAAA;AAAA,YAAA,EAEJ,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC;AAAA,gCAAA,CAAA;AAG/B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IAAW,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,EAAU;AAC5C,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AACpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,IAAS,gBAAgB,QAAA,EAAU,CAAA,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,IAAS,sBAAsB,QAAA,EAAU,CAAA,CAAA,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AACF;AAEF,MAAA,KAAA,IAAS,gBAAA;AAET,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,MAAM,WAAA,GAAc,OAAO,QAAA,IAAY,CAAA;AAEvC,MAAA,MAAM,WAAgB,EAAC;AACvB,MAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,CAAA,EAAW,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAC9D,MAAA,IAAI,QAAQ,WAAA,KAAgB,KAAA,CAAA;AAC1B,QAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AACjC,MAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,KAAA,CAAA,EAAW,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAChE,MAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,KAAA,CAAA,EAAW,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAC1D,MAAA,IAAI,QAAQ,SAAA,KAAc,KAAA,CAAA;AACxB,QAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAC/B,MAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,KAAA,CAAA,EAAW,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAExD,MAAA,KAAA,MAAW,GAAA,IAAO,OAAO,IAAA,EAAM;AAC7B,QAAA,MAAM,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,EAAA,EAAA,QAAA,eAAyB,QAAQ,CAAA;AAAA;AAGjE,MAAA,GAAA,CAAI,UAAU,WAAW,CAAA,eAAA,EAAkB,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AACrE,MAAA,OAAO,WAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,cAAA,CAAe,UAAA,GAAa,EAAA,EAAqB;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,UAAU;AAAA,OACb;AACA,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,SAAA,CAAW,CAAA;AACzC,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,mBAAA,CAAoB,UAAA,GAAa,EAAA,EAAqB;AAC1D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAKA,CAAC,UAAU;AAAA,OACb;AACA,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,eAAA,CAAiB,CAAA;AAC/C,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,CAAE,CAAA;AAChD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,gBAAA,CAAiB,wBAAA,GAA2B,EAAA,EAAqB;AACrE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AAAA,QAUA,CAAC,wBAAwB;AAAA,OAC3B;AACA,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,CAAA,WAAA,CAAa,CAAA;AAC7C,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,KAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,qBACZ,MAAA,EACe;AACf,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACzB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,SAAmB,EAAC;AAC1B,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,QAAA,MAAA,CAAO,KAAK,CAAA,EAAA,EAAK,QAAA,EAAU,MAAM,QAAA,EAAU,CAAA,GAAA,EAAM,UAAU,CAAA,CAAA,CAAG,CAAA;AAC9D,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,KAAA,CAAM,KAAA;AAAA,UACN,KAAA,CAAM,SAAA;AAAA,UACN,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAQ,CAAA,GAAI;AAAA,SACpD;AAAA;AAEF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,6DAAA,EAAgE,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,QACjF;AAAA,OACF;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,CAAE,CAAA;AAAA,KAElD,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,+BAAA,CACJ,MAAA,EACA,OAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,MAAA,MAAM,MAAA,GAAgB,CAAC,MAAM,CAAA;AAC7B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC1B,UAAA,aAAA,GAAgB,CAAA,uBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,SACrB,MAAO;AACL,UAAA,aAAA,GAAgB,CAAA,kBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA;AACrB;AAEF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,oEAAoE,aAAa,CAAA,CAAA;AAAA,QACjF;AAAA,OACF;AAAA,KACF,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AAEJ;AC3jCO,IAAM,cAAA,GAAiB,OAC5B,IAAA,EACA,KAAA,EACA,SAAA,EACA,QAAA,KAEA,IAAI,eAAA,CAAgB,IAAI,CAAA,CAAE,cAAA,CAAe,KAAA,EAAO,WAAW,QAAQ,CAAA;AA6L9D,IAAM,OAAA,GAAU,OACrB,IAAA,EACA,KAAA,EACA,OAAA,KAKkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,MAWA;AAAA,QACE,KAAA;AAAA,QACA,QAAQ,SAAA,IAAa,IAAA;AAAA,QACrB,QAAQ,WAAA,IAAe,IAAA;AAAA,QACvB,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,QAAQ;AAAA;AACjC,KACF;AACA,IAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,MAAA,GAAA;AAAA,QACE,OAAO,KAAK,CAAA,iFAAA;AAAA,OACd;AACA,MAAA;AAAA;AAEF,IAAA,MAAM,cAAA,CAAe,MAAM,KAAA,EAAA,SAAA,gBAA6B;AAAA,MACtD,SAAA,EAAW,OAAA,CAAQ,SAAA,EAAW,WAAA,EAAY,IAAK,IAAA;AAAA,MAC/C,WAAA,EAAa,QAAQ,WAAA,IAAe;AAAA,KACrC,CAAA;AACD,IAAA,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,eAAA,CAAiB,CAAA;AAAA,WAC1B,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AACrD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAMO,IAAM,cAAA,GAAiB,OAC5B,IAAA,EACA,KAAA,EACA,QAAA,KACkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,KAAA;AAAA,MACX,CAAA,qEAAA,CAAA;AAAA,MACA,CAAC,KAAA,EAAO,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC;AAAA,KAClC;AAAA,WACO,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,iCAAA,EAAoC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,GAE3D,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AASA,IAAM,cAAA,GAAiB,GAAA,GAAM,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAE5C,SAAS,mBAAmB,OAAA,EAAyB;AACnD,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,kBAAkB,CAAA;AAC9C,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,4BAA4B,OAAO,CAAA,iDAAA;AAAA,KACrC;AAAA;AAEF,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,CAAC,GAAG,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AACpB,EAAA,IAAI,EAAA;AACJ,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,GAAA;AACb,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,QAAQ,EAAA,GAAK,GAAA;AAClB,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AACvB,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAC5B,MAAA;AAAA,IACF;AACE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA;AAErD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,IAAK,KAAK,cAAA,EAAgB;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kBAAkB,OAAO,CAAA,4CAAA;AAAA,KAC3B;AAAA;AAEF,EAAA,OAAO,EAAA;AACT;AAWO,IAAM,eAAA,GAAkB,OAC7B,IAAA,EACA,KAAA,EACA,OAAA,KAC4B;AAC5B,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAK,CAAA,GAAA,EAAMC,iBAAA,EAAY,CAAA,CAAA;AAC7B,IAAA,IAAI,SAAA,GAAyB,IAAA;AAE7B,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,MAAM,EAAA,GAAK,kBAAA,CAAmB,OAAA,CAAQ,OAAO,CAAA;AAC7C,MAAA,SAAA,GAAY,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,KAAQ,EAAE,CAAA;AAAA;AAGtC,IAAA,MAAM,MAAA,CAAO,KAAA;AAAA,MACX,CAAA,gGAAA,CAAA;AAAA,MACA,CAAC,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,QAAQ,IAAI;AAAA,KAC9C;AAEA,IAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,EAAE,CAAA,SAAA,EAAY,KAAK,CAAA,CAAE,CAAA;AAC9C,IAAA,OAAO,EAAE,EAAA,EAAG;AAAA,WACL,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,0BAAA,EAA6B,KAAK,CAAA,CAAE,CAAA;AACxC,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAOO,IAAM,iBAAA,GAAoB,OAC/B,IAAA,EACA,OAAA,EACA,IAAA,KACkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAG1B,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,KAAA;AAAA,MAC5B,CAAA;AAAA;AAAA,uBAAA,CAAA;AAAA,MAGA,CAAC,SAAS,IAAA,IAAQ,IAAA,GAAO,KAAK,SAAA,CAAU,IAAI,IAAI,IAAI;AAAA,KACtD;AAEA,IAAA,IAAI,QAAA,CAAS,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAC9B,MAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,OAAO,CAAA,+BAAA,CAAiC,CAAA;AACzD,MAAA;AAAA;AAGF,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,MAAA;AAG/B,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA;AAAA;AAAA,6CAAA,CAAA;AAAA,QAGA,CAAC,KAAK;AAAA,OACR;AAAA;AAGF,IAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,OAAO,CAAA,SAAA,EAAY,KAAK,CAAA,CAAE,CAAA;AAAA,WAC9C,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,IAAA,GAAA,CAAI,CAAA,2BAAA,EAA8B,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACrD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAKO,IAAM,YAAA,GAAe,OAC1B,IAAA,EACA,OAAA,KACoC;AACpC,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B,CAAA,qKAAA,CAAA;AAAA,MACA,CAAC,OAAO;AAAA,KACV;AACA,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACrC,IAAA,OAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,WACb,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,wBAAA,EAA2B,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAClD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAMO,IAAM,wBAAA,GAA2B,OAAO,IAAA,KAAgC;AAC7E,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAG1B,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B,CAAA;AAAA;AAAA;AAAA,2BAAA;AAAA,KAIF;AAGA,IAAA,KAAA,MAAW,GAAA,IAAO,OAAO,IAAA,EAAM;AAC7B,MAAA,IAAI,GAAA,CAAI,UAAU,IAAA,EAAM;AACtB,QAAA,MAAM,MAAA,CAAO,KAAA;AAAA,UACX,CAAA;AAAA;AAAA,+CAAA,CAAA;AAAA,UAGA,CAAC,IAAI,MAAM;AAAA,SACb;AAAA;AACF;AAGF,IAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,IAAY,CAAA;AACjC,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,KAAK,CAAA,qBAAA,CAAuB,CAAA;AAAA;AAE7C,IAAA,OAAO,KAAA;AAAA,WACA,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,IAAA,GAAA,CAAI,CAAA,qCAAA,EAAwC,KAAK,CAAA,CAAE,CAAA;AACnD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AC9cA,SAAS,eAAe,OAAA,EAAoC;AAC1D,EAAA,IAAI,mBAAmB,eAAA,EAAiB;AACtC,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA;AAEzB,EAAA,OAAO,IAAA;AACT;AAMA,SAAS,iBAAA,CACP,OAAA,EACA,KAAA,EACA,OAAA,EAIY;AACZ,EAAA,MAAM,SAAA,GAAY,MAChB,IAAI,KAAA;AAAA,IACF;AAAA,GACF;AACF,EAAA,OAAO;AAAA,IACL,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,GAAA,EAAK,OAAU,SAAA,EAAmB,EAAA,KAAqC;AAErE,MAAA,OAAO,EAAA,EAAG;AAAA,KACZ;AAAA,IACA,SAAS,YAAY;AACnB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,WAAW,YAAY;AACrB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,aAAa,YAAY;AACvB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,cAAc,YAAY;AACxB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,WAAA,EAAa,OAAO,OAAA,KAAoB;AACtC,MAAA,IAAI,OAAA,GAAU,KAAK,OAAA,GAAU,GAAA;AAC3B,QAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AACtD,MAAA,MAAM,QAAQ,cAAA,CAAe,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA;AACzD,GACF;AACF;AAMA,SAAS,2BAAA,CAGP,SAAoC,OAAA,EAAuB;AAC3D,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AAIvC,IAAA,IACE,aAAA,CAAc,SAAS,OAAO,CAAA,IAC9B,CAAC,aAAA,CAAc,KAAA,CAAM,oBAAoB,CAAA,EACzC;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yBAAyB,OAAO,CAAA,uIAAA;AAAA,OAElC;AAAA;AAKF,IAAA,IAAI,aAAA,CAAc,QAAA,CAAS,eAAe,CAAA,EAAG;AAC3C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yBAAyB,OAAO,CAAA,8HAAA;AAAA,OAElC;AAAA;AAKF,IAAA,IAAI;AACF,MAAA,IAAI,QAAA,CAAS,YAAY,aAAa,CAAA;AAAA,aAC/B,UAAA,EAAY;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sBAAA,EAAyB,OAAO,CAAA,wBAAA,EAA2B,UAAA,YAAsB,QAAQ,UAAA,CAAW,OAAA,GAAU,MAAA,CAAO,UAAU,CAAC,CAAA,kHAAA;AAAA,OAElI;AAAA;AACF,WACO,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,MAAM,KAAA;AAAA;AAER,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uDAAA,EAA0D,OAAO,CAAA,GAAA,EAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACtF;AAAA;AAEJ;AAgBA,eAAe,kBAAA,CAIb,OAAA,EACA,OAAA,EACA,SAAA,EACA,OAAA,EACe;AAEf,EAAA,2BAAA,CAA4B,SAAS,OAAO,CAAA;AAE5C,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AAItC,IAAA,MAAM,UAAA,GAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAsFnB,IAAA,MAAM,MAAA,GAAS,IAAIC,qBAAA,CAAO,UAAA,EAAY;AAAA,MACpC,IAAA,EAAM,IAAA;AAAA,MACN,UAAA,EAAY;AAAA,QACV,WAAA,EAAa,QAAQ,QAAA,EAAS;AAAA,QAC9B,OAAA;AAAA,QACA;AAAA;AACF,KACD,CAAA;AAED,IAAA,IAAI,QAAA,GAAW,KAAA;AAEf,IAAA,MAAA,CAAO,EAAA,CAAG,SAAA,EAAW,CAAC,OAAA,KAA2C;AAC/D,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AAEX,MAAA,IAAI,OAAA,CAAQ,SAAS,SAAA,EAAW;AAC9B,QAAA,OAAA,EAAQ;AAAA,OACV,MAAA,IAAW,OAAA,CAAQ,IAAA,KAAS,SAAA,EAAW;AACrC,QAAA,MAAM,eAAe,IAAI,KAAA;AAAA,UACvB,uBAAuB,SAAS,CAAA,iCAAA;AAAA,SAClC;AAEA,QAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,QAAA,MAAA,CAAO,YAAY,CAAA;AAAA,OACrB,MAAA,IAAW,OAAA,CAAQ,IAAA,KAAS,OAAA,EAAS;AACnC,QAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,OAAO,CAAA;AAC7C,QAAA,KAAA,CAAM,KAAA,GAAQ,QAAQ,KAAA,CAAM,KAAA;AAC5B,QAAA,KAAA,CAAM,IAAA,GAAO,QAAQ,KAAA,CAAM,IAAA;AAC3B,QAAA,MAAA,CAAO,KAAK,CAAA;AAAA;AACd,KACD,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAC,KAAA,KAAU;AAC5B,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,MAAA,CAAO,KAAK,CAAA;AAAA,KACb,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,IAAA,KAAS;AAC1B,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,IAAI,SAAS,CAAA,EAAG;AACd,QAAA,QAAA,GAAW,IAAA;AACX,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,8BAAA,EAAiC,IAAI,EAAE,CAAC,CAAA;AAAA;AAC3D,KACD,CAAA;AAGD,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,QAAA,GAAW,IAAA;AACX,QAAA,MAAA,CACG,SAAA,EAAU,CACV,IAAA,CAAK,MAAM;AACV,UAAA,MAAM,eAAe,IAAI,KAAA;AAAA,YACvB,uBAAuB,SAAS,CAAA,iCAAA;AAAA,WAClC;AAEA,UAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,UAAA,MAAA,CAAO,YAAY,CAAA;AAAA,SACpB,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,SACX,CAAA;AAAA;AACL,KACF,EAAG,YAAY,GAAG,CAAA;AAAA,GACnB,CAAA;AACH;AAKA,SAAS,mBAAmB,QAAA,EAA8B;AACxD,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,IAAI,QAAA,CAAS,OAAA,EAAS,EAAA,IAAM,QAAA,CAAS,OAAA,GAAU,GAAA;AAC/C,EAAA,IAAI,QAAA,CAAS,OAAA,EAAS,EAAA,IAAM,QAAA,CAAS,UAAU,EAAA,GAAK,GAAA;AACpD,EAAA,IAAI,SAAS,KAAA,EAAO,EAAA,IAAM,QAAA,CAAS,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AACrD,EAAA,IAAI,SAAS,IAAA,EAAM,EAAA,IAAM,SAAS,IAAA,GAAO,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACxD,EAAA,IAAI,QAAA,CAAS,OAAO,EAAA,IAAM,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC9D,EAAA,IAAI,QAAA,CAAS,QAAQ,EAAA,IAAM,QAAA,CAAS,SAAS,EAAA,GAAK,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACjE,EAAA,IAAI,QAAA,CAAS,OAAO,EAAA,IAAM,QAAA,CAAS,QAAQ,GAAA,GAAM,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAChE,EAAA,IAAI,MAAM,CAAA,EAAG;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA;AAEF,EAAA,OAAO,IAAI,IAAA,CAAK,GAAA,GAAM,EAAE,CAAA;AAC1B;AAsDA,eAAe,qBAAA,CACb,MACA,QAAA,EACe;AACf,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AAChC,IAAA,MAAM,KAAA,GAAQ,SAAS,GAAG,CAAA;AAC1B,IAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,SAAA,EAAW;AAE5D,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,UAAA,IAAc,KAAA,CAAM,SAAS,MAAA,EAAQ;AAEtD,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,EAAE,GAAG,KAAA,EAAO,WAAW,IAAA,EAAK;AAAA,KAC9C,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,OAAA,IAAW,MAAM,OAAA,EAAS;AAElD,MAAA,MAAM,EAAA,GAAK,MAAM,YAAA,CAAa,IAAA,EAAM,MAAM,OAAO,CAAA;AACjD,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,QAAA,CAAS,GAAG,CAAA,GAAI;AAAA,UACd,GAAG,KAAA;AAAA,UACH,SAAA,EAAW,IAAA;AAAA,UACX,QAAQ,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,GAAG,MAAA;AAAO,SACxC;AAAA,OACF,MAAA,IAAW,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AAC1C,QAAA,QAAA,CAAS,GAAG,CAAA,GAAI;AAAA,UACd,GAAG,KAAA;AAAA,UACH,SAAA,EAAW,IAAA;AAAA,UACX,MAAA,EAAQ,EAAE,EAAA,EAAI,KAAA,EAAO,OAAO,iBAAA;AAAkB,SAChD;AAAA;AACF;AAEF;AAEJ;AAKA,SAAS,gBAAA,CACP,OAAA,EACA,IAAA,EACA,KAAA,EACA,UACA,OAAA,EAIY;AAIZ,EAAA,IAAI,WAAA,GAAc,CAAA;AAElB,EAAA,MAAM,GAAA,GAAkB;AAAA,IACtB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,IAEnB,GAAA,EAAK,OAAU,QAAA,EAAkB,EAAA,KAAqC;AAEpE,MAAA,MAAM,MAAA,GAAS,SAAS,QAAQ,CAAA;AAChC,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,WAAA,EAAa;AAC9D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,QAAQ,CAAA,8BAAA,EAAiC,KAAK,CAAA,CAAE,CAAA;AAC7D,QAAA,OAAO,MAAA,CAAO,MAAA;AAAA;AAIhB,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,EAAG;AAGxB,MAAA,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAE,WAAA,EAAa,MAAM,MAAA,EAAO;AACjD,MAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAE1C,MAAA,OAAO,MAAA;AAAA,KACT;AAAA,IAEA,OAAA,EAAS,OAAO,QAAA,KAA0C;AACxD,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,UAAA,CAAY,CAAA;AACpE,QAAA;AAAA;AAIF,MAAA,MAAM,aAAA,GAAgB,mBAAmB,QAAQ,CAAA;AAGjD,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,IAAA,EAAM,UAAA,EAAY,WAAW,KAAA,EAAM;AAGzD,MAAA,MAAM,IAAI,UAAA,CAAW,UAAA,EAAY,aAAA,EAAe,QAAW,QAAQ,CAAA;AAAA,KACrE;AAAA,IAEA,SAAA,EAAW,OAAO,IAAA,KAA8B;AAC9C,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,UAAA,CAAY,CAAA;AACpE,QAAA;AAAA;AAIF,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,IAAA,EAAM,MAAA,EAAQ,WAAW,KAAA,EAAM;AAGrD,MAAA,MAAM,IAAI,UAAA,CAAW,MAAA,EAAQ,IAAA,EAAM,QAAW,QAAQ,CAAA;AAAA,KACxD;AAAA,IAEA,WAAA,EAAa,OAAO,OAAA,KAAa;AAC/B,MAAA,MAAM,KAAA,GAAQ,MAAM,eAAA,CAAgB,IAAA,EAAM,OAAO,OAAO,CAAA;AACxD,MAAA,OAAO,KAAA;AAAA,KACT;AAAA,IAEA,YAAA,EAAc,OACZ,OAAA,KACgC;AAChC,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA;AAAA,UACE,CAAA,YAAA,EAAe,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,yBAAA;AAAA,SAC5D;AACA,QAAA,OAAO,MAAA,CAAO,MAAA;AAAA;AAIhB,MAAA,MAAM,EAAA,GAAK,MAAM,YAAA,CAAa,IAAA,EAAM,OAAO,CAAA;AAC3C,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,MAAM,MAAA,GAA6B;AAAA,UACjC,EAAA,EAAI,IAAA;AAAA,UACJ,QAAQ,EAAA,CAAG;AAAA,SACb;AACA,QAAA,QAAA,CAAS,OAAO,CAAA,GAAI;AAAA,UAClB,IAAA,EAAM,OAAA;AAAA,UACN,OAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX;AAAA,SACF;AACA,QAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA;AAET,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,MAAM,MAAA,GAA6B;AAAA,UACjC,EAAA,EAAI,KAAA;AAAA,UACJ,KAAA,EAAO;AAAA,SACT;AACA,QAAA,QAAA,CAAS,OAAO,CAAA,GAAI;AAAA,UAClB,IAAA,EAAM,OAAA;AAAA,UACN,OAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX;AAAA,SACF;AACA,QAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA;AAIT,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,MAAM,OAAA,EAAS,OAAA,EAAS,WAAW,KAAA,EAAM;AAC/D,MAAA,MAAM,IAAI,UAAA,CAAW,OAAA,EAAS,MAAA,EAAW,SAAS,QAAQ,CAAA;AAAA,KAC5D;AAAA,IAEA,WAAA,EAAa,OAAO,OAAA,KAAoB;AACtC,MAAA,IAAI,OAAA,GAAU,KAAK,OAAA,GAAU,GAAA;AAC3B,QAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AACtD,MAAA,MAAM,QAAQ,cAAA,CAAe,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA;AACzD,GACF;AAEA,EAAA,OAAO,GAAA;AACT;AAKA,eAAsB,sBAAA,CAIpB,OAAA,EACA,GAAA,EACA,WAAA,EACe;AACf,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AAEvC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,OAAA,CAAQ,+BAAA;AAAA,MACZ,CAAA,oCAAA,EAAuC,IAAI,OAAO,CAAA,CAAA;AAAA,MAClD,GAAA,CAAI;AAAA,KACN;AACA,IAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,MACZ,GAAA,CAAI,EAAA;AAAA,MACJ,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA;AAAA,MAAA,YAAA;AAAA,KAEhE;AACA,IAAA;AAAA;AAIF,EAAA,MAAM,WAAgC,EAAE,GAAI,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG;AAGhE,EAAA,MAAM,IAAA,GAAO,eAAe,OAAO,CAAA;AAGnC,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA;AAAA,IAAK,CAAC,CAAA,KACjD,CAAA,CAAE,UAAA,CAAW,SAAS;AAAA,GACxB;AACA,EAAA,IAAI,kBAAkB,IAAA,EAAM;AAC1B,IAAA,MAAM,qBAAA,CAAsB,MAAM,QAAQ,CAAA;AAE1C,IAAA,MAAM,cAAA,CAAe,IAAA,EAAM,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA;AAAA;AAI7C,EAAA,MAAM,SAAA,GAAY,IAAI,SAAA,IAAa,MAAA;AACnC,EAAA,MAAM,kBAAA,GAAqB,IAAI,kBAAA,IAAsB,KAAA;AACrD,EAAA,IAAI,SAAA;AACJ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,IAAI;AAGF,IAAA,IAAI,kBAAA,IAAsB,SAAA,IAAa,SAAA,GAAY,CAAA,EAAG;AACpD,MAAA,MAAM,mBAAmB,OAAA,EAAS,GAAA,CAAI,OAAA,EAAS,SAAA,EAAW,IAAI,OAAO,CAAA;AAAA,KACvE,MAAO;AAEL,MAAA,IAAI,iBAAA;AAGJ,MAAA,IAAI,aAAA;AASJ,MAAA,MAAM,UAAA,GAAa,CAAC,EAAA,KAAe;AACjC,QAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,QAAA,SAAA,GAAY,WAAW,MAAM;AAE3B,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAI;AACF,cAAA,MAAM,YAAY,iBAAA,EAAkB;AACpC,cAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,GAAY,CAAA,EAAG;AAElD,gBAAA,OAAA,CAAQ,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA,CAAE,MAAM,MAAM;AAAA,iBAAE,CAAA;AACzC,gBAAA,UAAA,CAAW,SAAS,CAAA;AACpB,gBAAA;AAAA;AACF,qBACO,aAAA,EAAe;AACtB,cAAA,GAAA;AAAA,gBACE,CAAA,iCAAA,EAAoC,GAAA,CAAI,EAAE,CAAA,EAAA,EAAK,aAAa,CAAA;AAAA,eAC9D;AAAA;AAEF;AAGF,UAAA,UAAA,CAAW,KAAA,EAAM;AACjB,UAAA,MAAM,YAAA,GAAe,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,EAAE,CAAA,GAAA,CAAK,CAAA;AAE7D,UAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,UAAA,IAAI,aAAA,EAAe;AACjB,YAAA,aAAA,CAAc,YAAY,CAAA;AAAA;AAC5B,WACC,EAAE,CAAA;AAAA,OACP;AAEA,MAAA,MAAM,UAAA,GAAa,SAAA,IAAa,IAAA,IAAQ,SAAA,GAAY,CAAA;AAGpD,MAAA,MAAM,UAAU,UAAA,GACZ;AAAA,QACE,OAAA,EAAS,CAAC,EAAA,KAAgB;AACxB,UAAA,MAAM,WAAW,EAAA,IAAM,SAAA;AACvB,UAAA,IAAI,QAAA,IAAY,IAAA,IAAQ,QAAA,GAAW,CAAA,EAAG;AACpC,YAAA,UAAA,CAAW,QAAQ,CAAA;AAEnB,YAAA,OAAA,CAAQ,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA,CAAE,MAAM,MAAM;AAAA,aAAE,CAAA;AAAA;AAC3C,SACF;AAAA,QACA,SAAA,EAAW,CAAC,QAAA,KAAgC;AAC1C,UAAA,iBAAA,GAAoB,QAAA;AAAA;AACtB,OACF,GACA;AAAA,QACE,SAAS,MAAM;AACb,UAAA,GAAA,CAAI,sDAAsD,CAAA;AAAA,SAC5D;AAAA,QACA,WAAW,MAAM;AACf,UAAA,GAAA,CAAI,wDAAwD,CAAA;AAAA;AAC9D,OACF;AAGJ,MAAA,MAAM,GAAA,GAAM,IAAA,GACR,gBAAA,CAAiB,OAAA,EAAS,MAAM,GAAA,CAAI,EAAA,EAAI,QAAA,EAAU,OAAO,CAAA,GACzD,iBAAA,CAAkB,OAAA,EAAS,GAAA,CAAI,IAAI,OAAO,CAAA;AAG9C,MAAA,IAAI,kBAAA,IAAsB,CAAC,UAAA,EAAY;AACrC,QAAA,GAAA;AAAA,UACE,CAAA,mDAAA,EAAsD,IAAI,EAAE,CAAA,4BAAA;AAAA,SAC9D;AAAA;AAGF,MAAA,MAAM,aAAa,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,QAAQ,GAAG,CAAA;AAE9D,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,UACjB,UAAA;AAAA,UACA,IAAI,OAAA,CAAe,CAAC,CAAA,EAAG,MAAA,KAAW;AAChC,YAAA,aAAA,GAAgB,MAAA;AAChB,YAAA,UAAA,CAAW,SAAU,CAAA;AAAA,WACtB;AAAA,SACF,CAAA;AAAA,OACH,MAAO;AACL,QAAA,MAAM,UAAA;AAAA;AACR;AAEF,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AAGrC,IAAA,MAAM,OAAA,CAAQ,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA;AAAA,WACzB,KAAA,EAAO;AACd,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AAGrC,IAAA,IAAI,iBAAiB,UAAA,EAAY;AAC/B,MAAA,IAAI,CAAC,IAAA,EAAM;AAGT,QAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,UACZ,GAAA,CAAI,EAAA;AAAA,UACJ,IAAI,KAAA;AAAA,YACF;AAAA,WACF;AAAA,UAAA,eAAA;AAAA,SAEF;AACA,QAAA;AAAA;AAEF,MAAA,GAAA;AAAA,QACE,CAAA,IAAA,EAAO,GAAA,CAAI,EAAE,CAAA,qBAAA,EAAwB,MAAM,IAAI,CAAA,YAAA,EAAe,KAAA,CAAM,SAAA,EAAW,aAAY,IAAK,MAAM,CAAA,UAAA,EAAa,KAAA,CAAM,WAAW,MAAM,CAAA;AAAA,OAC5I;AACA,MAAA,MAAM,OAAA,CAAQ,IAAA,EAAM,GAAA,CAAI,EAAA,EAAI;AAAA,QAC1B,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,aAAa,KAAA,CAAM,OAAA;AAAA,QACnB,UAAU,KAAA,CAAM;AAAA,OACjB,CAAA;AACD,MAAA;AAAA;AAIF,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,qBAAA,EAAwB,GAAA,CAAI,EAAE,KAAK,KAAK,CAAA;AACtD,IAAA,IAAI,aAAA,GAAA,eAAA;AACJ,IAAA,IACE,SACA,OAAO,KAAA,KAAU,YACjB,eAAA,IAAmB,KAAA,IAClB,MAA4C,aAAA,KAAA,SAAA,gBAE7C;AACA,MAAA,aAAA,GAAA,SAAA;AAAA;AAEF,IAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,MACZ,GAAA,CAAI,EAAA;AAAA,MACJ,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MACxD;AAAA,KACF;AAAA;AAEJ;AAKA,eAAsB,yBACpB,OAAA,EACA,QAAA,EACA,WACA,OAAA,EACA,WAAA,EACA,aACA,OAAA,EACiB;AACjB,EAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,YAAA;AAAA,IACzB,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,IAAI,CAAC,WAAA,IAAe,WAAA,IAAe,IAAA,CAAK,MAAA,EAAQ;AAE9C,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,IAAA,CAAK,IAAI,CAAC,GAAA,KAAQ,uBAAuB,OAAA,EAAS,GAAA,EAAK,WAAW,CAAC;AAAA,KACrE;AACA,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA;AAGd,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,IAAI,aAAa,IAAA,CAAK,MAAA,EAAQ,OAAO,OAAA,CAAQ,KAAK,MAAM,CAAA;AACxD,MAAA,OAAO,OAAA,GAAU,WAAA,IAAe,GAAA,GAAM,IAAA,CAAK,MAAA,EAAQ;AACjD,QAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAK,CAAA;AACtB,QAAA,OAAA,EAAA;AACA,QAAA,sBAAA,CAAuB,OAAA,EAAS,GAAA,EAAK,WAAW,CAAA,CAC7C,KAAK,MAAM;AACV,UAAA,OAAA,EAAA;AACA,UAAA,QAAA,EAAA;AACA,UAAA,IAAA,EAAK;AAAA,SACN,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,UAAA,OAAA,EAAA;AACA,UAAA,QAAA,EAAA;AACA,UAAA,IAAI,OAAA,EAAS;AACX,YAAA,OAAA,CAAQ,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA;AAE7D,UAAA,IAAA,EAAK;AAAA,SACN,CAAA;AAAA;AACL,KACF;AACA,IAAA,IAAA,EAAK;AAAA,GACN,CAAA;AACH;AASO,IAAM,kBAAkB,CAC7B,OAAA,EACA,QAAA,EACA,OAAA,GAA4B,EAAC,KACf;AACd,EAAA,MAAM;AAAA,IACJ,QAAA,GAAW,CAAA,OAAA,EAAU,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAAA,IAC/D,SAAA,GAAY,EAAA;AAAA,IACZ,YAAA,GAAe,GAAA;AAAA,IACf,UAAU,CAAC,KAAA,KAAiB,OAAA,CAAQ,KAAA,CAAM,wBAAwB,KAAK,CAAA;AAAA,IACvE,OAAA;AAAA,IACA,WAAA,GAAc;AAAA,GAChB,GAAI,OAAA;AAEJ,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,UAAA,GAAoC,IAAA;AACxC,EAAA,IAAI,mBAAA,GAA8C,IAAA;AAElD,EAAA,aAAA,CAAc,OAAA,CAAQ,WAAW,KAAK,CAAA;AAEtC,EAAA,MAAM,cAAc,YAA6B;AAC/C,IAAA,IAAI,CAAC,SAAS,OAAO,CAAA;AAErB,IAAA,GAAA;AAAA,MACE,CAAA,+BAAA,EAAkC,QAAQ,CAAA,EAAG,OAAA,GAAU,iBAAiB,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,QAAQ,IAAA,CAAK,GAAG,CAAA,GAAI,OAAO,KAAK,EAAE,CAAA;AAAA,KACrI;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,YAAY,MAAM,wBAAA;AAAA,QACtB,OAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,OAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,OAAO,SAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA;AAEnE,IAAA,OAAO,CAAA;AAAA,GACT;AAEA,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,mBAAmB,MAAM;AACvB,MAAA,IAAI,OAAA,EAAS;AAEb,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,IAAA;AAIV,MAAA,MAAM,YAAA,GAAe,CAAC,SAAA,KAAuB;AAC3C,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,UAAA,GAAa,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,SACjC,MAAO;AACL,UAAA,UAAA,GAAa,UAAA,CAAW,MAAM,YAAY,CAAA;AAAA;AAC5C,OACF;AAEA,MAAA,MAAM,OAAO,YAAY;AACvB,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,mBAAA,GAAsB,WAAA,EAAY;AAClC,QAAA,MAAM,YAAY,MAAM,mBAAA;AACxB,QAAA,mBAAA,GAAsB,IAAA;AAEtB,QAAA,YAAA,CAAa,cAAc,SAAS,CAAA;AAAA,OACtC;AAGA,MAAA,IAAA,EAAK;AAAA,KACP;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MAAM;AACV,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,YAAA,CAAa,UAAU,CAAA;AACvB,QAAA,UAAA,GAAa,IAAA;AAAA;AACf,KACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,YAAA,EAAc,OAAO,cAAA,GAAiB,GAAA,KAAU;AAC9C,MAAA,GAAA,CAAI,CAAA,mDAAA,EAAsD,QAAQ,CAAA,CAAE,CAAA;AACpE,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,YAAA,CAAa,UAAU,CAAA;AACvB,QAAA,UAAA,GAAa,IAAA;AAAA;AAGf,MAAA,IAAI,mBAAA,EAAqB;AACvB,QAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,UACjB,mBAAA,CAAoB,MAAM,MAAM;AAAA,WAAE,CAAA;AAAA,UAClC,IAAI,OAAA,CAAc,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,cAAc,CAAC;AAAA,SACnE,CAAA;AACD,QAAA,mBAAA,GAAsB,IAAA;AAAA;AAExB,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,QAAQ,CAAA,QAAA,CAAU,CAAA;AAAA,KACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,OAAO,YAAY;AACjB,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,MAAM,SAAA,GAAY,MAAM,WAAA,EAAY;AACpC,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,OAAO,SAAA;AAAA,KACT;AAAA,IACA,WAAW,MAAM;AAAA,GACnB;AACF,CAAA;ACj7BA,SAAS,cAAc,KAAA,EAAoC;AACzD,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AACnB,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,EAAG;AAC/B,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,OAAOC,mBAAA,CAAG,YAAA,CAAa,QAAA,EAAU,MAAM,CAAA;AAAA;AAEzC,EAAA,OAAO,KAAA;AACT;AAaO,IAAM,UAAA,GAAa,CACxB,MAAA,KACS;AACT,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,GAAA,GAAW,MAAA;AACf,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,OAAA;AAEJ,EAAA,IAAI,OAAO,gBAAA,EAAkB;AAC3B,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAA,CAAO,gBAAgB,CAAA;AAC3C,MAAA,UAAA,GAAa,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,aAAa,CAAA,IAAK,KAAA,CAAA;AACpD,MAAA,OAAA,GAAU,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA,IAAK,KAAA,CAAA;AAC7C,MAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,QAAA,GAAA,GAAM,EAAE,oBAAoB,KAAA,EAAM;AAAA;AACpC,aACO,CAAA,EAAG;AACV,MAAA,MAAM,MAAA,GAASC,wBAAA,CAAM,MAAA,CAAO,gBAAgB,CAAA;AAC5C,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,sBAAsB,CAAA;AACzD,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,UAAA,GAAa,MAAM,CAAC,CAAA;AAAA;AACtB;AAEF,MAAA,OAAA,GAAU,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GAAW,OAAO,OAAA,GAAU,MAAA;AAChE,MAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,QAAA,GAAA,GAAM,EAAE,oBAAoB,KAAA,EAAM;AAAA;AACpC;AACF;AAIF,EAAA,IAAI,OAAO,GAAA,EAAK;AACd,IAAA,IAAI,OAAO,MAAA,CAAO,GAAA,CAAI,EAAA,KAAO,QAAA,EAAU;AACrC,MAAA,QAAA,GAAW,OAAO,GAAA,CAAI,EAAA;AAAA,KACxB,MAAA,IAAW,OAAO,OAAA,CAAQ,GAAA,CAAI,kBAAkB,QAAA,EAAU;AACxD,MAAA,QAAA,GAAW,QAAQ,GAAA,CAAI,aAAA;AAAA,KACzB,MAAO;AACL,MAAA,QAAA,GAAW,MAAA;AAAA;AAEb,IAAA,MAAM,UACJ,OAAO,QAAA,KAAa,QAAA,GAAW,aAAA,CAAc,QAAQ,CAAA,GAAI,MAAA;AAC3D,IAAA,GAAA,GAAM;AAAA,MACJ,GAAG,GAAA;AAAA,MACH,GAAI,OAAA,GAAU,EAAE,EAAA,EAAI,OAAA,KAAY,EAAC;AAAA,MACjC,IAAA,EAAM,aAAA;AAAA,QACJ,OAAO,OAAO,GAAA,CAAI,IAAA,KAAS,WACvB,MAAA,CAAO,GAAA,CAAI,IAAA,GACX,OAAA,CAAQ,GAAA,CAAI;AAAA,OAClB;AAAA,MACA,GAAA,EAAK,aAAA;AAAA,QACH,OAAO,OAAO,GAAA,CAAI,GAAA,KAAQ,WACtB,MAAA,CAAO,GAAA,CAAI,GAAA,GACX,OAAA,CAAQ,GAAA,CAAI;AAAA,OAClB;AAAA,MACA,oBACE,MAAA,CAAO,GAAA,CAAI,uBAAuB,MAAA,GAC9B,MAAA,CAAO,IAAI,kBAAA,GACX;AAAA,KACR;AAAA;AAIF,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,MAAM,OAAA,GAAU;;AAAA;AAAA;AAAA;AAAA,eAAA,EAAsL,OAAO,CAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAC7M,IAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA;AAGtB,EAAA,MAAM,IAAA,GAAO,IAAIC,OAAA,CAAK;AAAA,IACpB,GAAG,MAAA;AAAA,IACH,GAAI,GAAA,GAAM,EAAE,GAAA,KAAQ;AAAC,GACtB,CAAA;AAED,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAA,CAAK,EAAA,CAAG,SAAA,EAAW,CAAC,MAAA,KAAW;AAC7B,MAAA,MAAA,CAAO,KAAA,CAAM,CAAA,mBAAA,EAAsB,UAAU,CAAA,CAAE,CAAA;AAAA,KAChD,CAAA;AAAA;AAGH,EAAA,OAAO,IAAA;AACT,CAAA;;;ACrFA,IAAM,WAAA,GAAc,kBAAA;AASb,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,2BAAA,EAoFD,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAkBnD,IAAM,qBAAA,GAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAiBX,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAgB7B,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuGhD,IAAM,mBAAA,GAAsB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuB5B,IAAM,eAAA,GAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAmDxB,IAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,yBAAA,EA6BL,WAAW,OAAO,WAAW,CAAA;AAAA;;AAAA;AAAA,CAAA;AAWjD,IAAM,iBAAA,GAAoB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AA4B1B,IAAM,kBAAA,GAAqB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuB3B,IAAM,yBAAA,GAA4B;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,iCAAA,EAqCN,WAAW,OAAO,WAAW,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAkBzD,IAAM,uBAAA,GAA0B;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;;;AC/cvC,SAAS,aAAa,GAAA,EAAuC;AAC3D,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA,EAAG;AACtC,IAAA,GAAA,CAAI,IAAI,CAAC,CAAC,CAAA,GAAI,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA;AAEzB,EAAA,OAAO,GAAA;AACT;AAGA,SAAS,eACP,CAAA,EAC0B;AAC1B,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KACf,CAAA,KAAM,UAAa,CAAA,KAAM,MAAA,IAAU,CAAA,KAAM,EAAA,GAAK,IAAA,GAAO,CAAA;AAEvD,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAyC;AAC1D,IAAA,MAAM,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnB,IAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,MAAA,CAAO,CAAC,CAAA;AAAA,GACrC;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAuC;AACzD,IAAA,MAAM,CAAA,GAAI,UAAU,CAAC,CAAA;AACrB,IAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,IAAI,KAAK,CAAC,CAAA;AAAA,GACvC;AAEA,EAAA,IAAI,eAAyD,EAAC;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,CAAA,CAAE,YAAA;AACd,IAAA,IAAI,GAAA,IAAO,QAAQ,IAAA,EAAM;AACvB,MAAA,YAAA,GAAe,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA;AAC/B,GACF,CAAA,MAAQ;AAAA;AAIR,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,CAAA,CAAE,IAAA;AACd,IAAA,IAAI,GAAA,IAAO,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAA,GAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA;AACvB,GACF,CAAA,MAAQ;AAAA;AAIR,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,OAAO,CAAA;AAAA,GAChC,CAAA,MAAQ;AACN,IAAA,OAAA,GAAU,CAAA,CAAE,OAAA;AAAA;AAGd,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,MAAA,CAAO,CAAA,CAAE,EAAE,CAAA;AAAA,IACf,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,OAAA;AAAA,IACA,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,IACvC,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,IACvC,QAAA,EAAU,UAAA,CAAW,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC/B,QAAA,EAAU,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC5B,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC3B,WAAA,EAAa,MAAA,CAAO,CAAA,CAAE,WAAW,CAAA;AAAA,IACjC,aAAA,EAAe,UAAA,CAAW,CAAA,CAAE,aAAa,CAAA;AAAA,IACzC,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC3B,OAAO,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,IAC/B,aAAA,EAAe,OAAA,CAAQ,CAAA,CAAE,aAAa,CAAA;AAAA,IACtC,YAAA;AAAA,IACA,SAAA,EAAW,SAAA,CAAU,CAAA,CAAE,SAAS,CAAA;AAAA,IAChC,kBAAA,EACE,CAAA,CAAE,kBAAA,KAAuB,MAAA,IAAU,EAAE,kBAAA,KAAuB,GAAA,GACxD,IAAA,GACA,CAAA,CAAE,kBAAA,KAAuB,OAAA,IAAW,CAAA,CAAE,kBAAA,KAAuB,MAC3D,KAAA,GACA,IAAA;AAAA,IACR,aAAA,EAAgB,OAAA,CAAQ,CAAA,CAAE,aAAa,CAAA,IAA8B,IAAA;AAAA,IACrE,WAAA,EAAa,UAAA,CAAW,CAAA,CAAE,WAAW,CAAA;AAAA,IACrC,SAAA,EAAW,UAAA,CAAW,CAAA,CAAE,SAAS,CAAA;AAAA,IACjC,aAAA,EAAe,UAAA,CAAW,CAAA,CAAE,aAAa,CAAA;AAAA,IACzC,YAAA,EAAc,UAAA,CAAW,CAAA,CAAE,YAAY,CAAA;AAAA,IACvC,eAAA,EAAiB,UAAA,CAAW,CAAA,CAAE,eAAe,CAAA;AAAA,IAC7C,IAAA;AAAA,IACA,cAAA,EAAgB,OAAA,CAAQ,CAAA,CAAE,cAAc,CAAA;AAAA,IACxC,QAAA,EAAU,SAAA,CAAU,CAAA,CAAE,QAAQ;AAAA,GAChC;AACF;AAEO,IAAM,eAAN,MAA2C;AAAA,EAIhD,YAAY,WAAA,EAAiD;AAE3D,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAWC,sBAAA,CAAc,2PAAe,CAAA;AAC9C,MAAA,OAAA,GAAU,SAAS,SAAS,CAAA;AAAA,KAC9B,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,IAAA,CAAK,MAAA,GAAS,YAAY,SAAA,IAAa,KAAA;AAEvC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,IAAA,CAAK,MAAA,GAAS,IAAI,OAAA,CAAQ,WAAA,CAAY,GAAA,EAAK;AAAA,QACzC,GAAI,YAAY,GAAA,GAAM,EAAE,KAAK,WAAA,CAAY,GAAA,KAAQ,EAAC;AAAA,QAClD,GAAI,YAAY,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,WAAA,CAAY,EAAA,EAAG,GAAI;AAAC,OAC9D,CAAA;AAAA,KACH,MAAO;AACL,MAAA,IAAA,CAAK,MAAA,GAAS,IAAI,OAAA,CAAQ;AAAA,QACxB,IAAA,EAAM,YAAY,IAAA,IAAQ,WAAA;AAAA,QAC1B,IAAA,EAAM,YAAY,IAAA,IAAQ,IAAA;AAAA,QAC1B,UAAU,WAAA,CAAY,QAAA;AAAA,QACtB,EAAA,EAAI,YAAY,EAAA,IAAM,CAAA;AAAA,QACtB,GAAI,YAAY,GAAA,GAAM,EAAE,KAAK,WAAA,CAAY,GAAA,KAAQ;AAAC,OACnD,CAAA;AAAA;AACH;AACF;AAAA,EAGA,SAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA;AACd,EAEQ,KAAA,GAAgB;AACtB,IAAA,OAAO,KAAK,GAAA,EAAI;AAAA;AAClB;AAAA,EAIA,MAAM,cAAA,CACJ,KAAA,EACA,SAAA,EACA,QAAA,EACe;AACf,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,YAAA,CAAc,CAAA;AACnE,MAAA,MAAM,KAAA,GAAQ,KAAK,SAAA,CAAU;AAAA,QAC3B,EAAA,EAAI,OAAA;AAAA,QACJ,KAAA;AAAA,QACA,SAAA;AAAA,QACA,SAAA,EAAW,KAAK,KAAA,EAAM;AAAA,QACtB,UAAU,QAAA,IAAY;AAAA,OACvB,CAAA;AACD,MAAA,MAAM,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,EAAG,KAAK,MAAM,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,aACvD,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE5D;AACF,EAEA,MAAM,aAAa,KAAA,EAAoC;AACrD,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MAC5B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,KAAc;AAC5B,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AACtB,MAAA,OAAO;AAAA,QACL,GAAG,CAAA;AAAA,QACH,SAAA,EAAW,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS;AAAA,OACjC;AAAA,KACD,CAAA;AAAA;AACH;AAAA,EAIA,MAAM,MAAA,CAAkD;AAAA,IACtD,OAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA,GAAc,CAAA;AAAA,IACd,QAAA,GAAW,CAAA;AAAA,IACX,KAAA,GAAQ,IAAA;AAAA,IACR,SAAA,GAAY,MAAA;AAAA,IACZ,kBAAA,GAAqB,KAAA;AAAA,IACrB,IAAA,GAAO,MAAA;AAAA,IACP,cAAA,GAAiB;AAAA,GACnB,EAA+C;AAC7C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,OAAA,GAAU,KAAA,GAAQ,KAAA,CAAM,OAAA,EAAQ,GAAI,CAAA;AAE1C,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,cAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,OAAA;AAAA,MACA,IAAA,CAAK,UAAU,OAAO,CAAA;AAAA,MACtB,WAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAQ,QAAA,EAAS;AAAA,MACjB,SAAA,KAAc,MAAA,GAAY,SAAA,CAAU,QAAA,EAAS,GAAI,MAAA;AAAA,MACjD,qBAAqB,MAAA,GAAS,OAAA;AAAA,MAC9B,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,MAAA;AAAA,MAC9B,cAAA,IAAkB,MAAA;AAAA,MAClB;AAAA,KACF;AAEA,IAAA,MAAM,KAAA,GAAQ,OAAO,MAAM,CAAA;AAC3B,IAAA,GAAA;AAAA,MACE,CAAA,UAAA,EAAa,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA,EAAA,EAAK,KAAA,GAAQ,CAAA,MAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,SAAA,EAAY,QAAQ,CAAA,cAAA,EAAiB,WAAW,CAAA,UAAA,EAAa,OAAO,UAAU,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,cAAA,GAAiB,CAAA,kBAAA,EAAqB,cAAc,MAAM,EAAE,CAAA;AAAA,KAC3Q;AACA,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,OAAA,cAA2B;AAAA,MACnD,OAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,OACJ,EAAA,EAC0C;AAC1C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAE,CAAA;AAChE,IAAA,IAAI,CAAC,IAAA,IAAQ,MAAA,CAAO,KAAK,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AAC3C,MAAA,GAAA,CAAI,CAAA,IAAA,EAAO,EAAE,CAAA,UAAA,CAAY,CAAA;AACzB,MAAA,OAAO,IAAA;AAAA;AAET,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,EAAE,CAAA,CAAE,CAAA;AACrB,IAAA,OAAO,eAA8B,IAAI,CAAA;AAAA;AAC3C,EAEA,MAAM,eAAA,CACJ,MAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,IAAA,CAAK,MAAM,CAAA,OAAA,EAAU,MAAM,CAAA,CAAE,CAAA;AACvE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAG9B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,GAAG,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C,EAEA,MAAM,UAAA,CACJ,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAA;AAAA,MAC5B,CAAA,EAAG,KAAK,MAAM,CAAA,GAAA,CAAA;AAAA,MACd,MAAA;AAAA,MACA,SAAS,KAAA,GAAQ;AAAA,KACnB;AACA,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC9B,IAAA,OAAO,IAAA,CAAK,cAA6B,GAAG,CAAA;AAAA;AAC9C,EAEA,MAAM,OAAA,CACJ,OAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,IAAI,YAAA;AAEJ,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,YAAA,GAAe,MAAM,KAAK,MAAA,CAAO,QAAA;AAAA,QAC/B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,QAAQ,OAAO,CAAA;AAAA,OACvC;AAAA,KACF,MAAO;AACL,MAAA,YAAA,GAAe,MAAM,KAAK,MAAA,CAAO,SAAA,CAAU,GAAG,IAAA,CAAK,MAAM,CAAA,GAAA,CAAA,EAAO,CAAA,EAAG,EAAE,CAAA;AAAA;AAGvE,IAAA,IAAI,YAAA,CAAa,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAGvC,IAAA,IAAI,SAAS,IAAA,IAAQ,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACnD,MAAA,YAAA,GAAe,MAAM,IAAA,CAAK,YAAA;AAAA,QACxB,YAAA;AAAA,QACA,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,OAAA,CAAQ,KAAK,IAAA,IAAQ;AAAA,OACvB;AAAA;AAIF,IAAA,IAAI,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,YAAY,CAAA;AAE/D,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,QAAA,IAAA,GAAO,KAAK,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,KAAa,QAAQ,QAAQ,CAAA;AAAA;AAE3D,MAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,QAAA,IAAA,GAAO,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,OAAA,CAAQ,KAAK,CAAA;AAAA;AAC/C;AAIF,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C,EAEA,MAAM,cACJ,IAAA,EACA,IAAA,GAAqB,OACrB,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,IAAA,CAAK,MAAM,CAAA,GAAA,CAAA,EAAO,CAAA,EAAG,EAAE,CAAA;AACrE,IAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEjC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,MAAM,IAAI,CAAA;AAC3D,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEnC,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,QAAQ,CAAA;AAC7D,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C;AAAA,EAIA,MAAM,YAAA,CACJ,QAAA,EACA,SAAA,GAAY,IACZ,OAAA,EACqC;AACrC,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,aAAA,GACJ,OAAA,KAAY,MAAA,GACR,MAAA,GACA,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GACnB,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,GACtB,OAAA;AAER,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,qBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,QAAA;AAAA,MACA,SAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAClC,MAAA,GAAA,CAAI,yBAAyB,CAAA;AAC7B,MAAA,OAAO,EAAC;AAAA;AAIV,IAAA,MAAM,OAAmC,EAAC;AAC1C,IAAA,IAAI,UAAoB,EAAC;AACzB,IAAA,KAAA,MAAW,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,QAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,UAAA,MAAM,CAAA,GAAI,aAAa,OAAO,CAAA;AAC9B,UAAA,IAAA,CAAK,IAAA,CAAK,cAAA,CAA8B,CAAC,CAAC,CAAA;AAAA;AAE5C,QAAA,OAAA,GAAU,EAAC;AAAA,OACb,MAAO;AACL,QAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA;AACnB;AAGF,IAAA,GAAA,CAAI,CAAA,MAAA,EAAS,IAAA,CAAK,MAAM,CAAA,gBAAA,CAAkB,CAAA;AAG1C,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,EAAA,EAAA,YAAA,kBAA2B;AAAA;AAG3D,IAAA,OAAO,IAAA;AAAA;AACT,EAEA,MAAM,YAAY,KAAA,EAA8B;AAC9C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,mBAAA,EAAqB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACtE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,IAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA;AAC9B,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,KAAA,EACA,aAAA,EACe;AACf,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,SAAA,GAAY,KAAK,SAAA,CAAU;AAAA,MAC/B;AAAA,QACE,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,QACtC,SAAA,EAAW,IAAI,IAAA,CAAK,GAAG,EAAE,WAAA;AAAY;AACvC,KACD,CAAA;AACD,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,eAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,KAAA;AAAA,MACA,SAAA;AAAA,MACA,aAAA,IAAiB,MAAA;AAAA,MACjB;AAAA,KACF;AACA,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,QAAA,eAA4B;AAAA,MACpD,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,MACtC;AAAA,KACD,CAAA;AACD,IAAA,GAAA,CAAI,CAAA,WAAA,EAAc,KAAK,CAAA,CAAE,CAAA;AAAA;AAC3B,EAEA,MAAM,WAAW,KAAA,EAA8B;AAC7C,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,MAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,kBAAA,EAAoB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACrE,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE/C;AACF;AAAA,EAIA,MAAM,cAAA,CAAe,KAAA,EAAe,QAAA,EAAiC;AACnE,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA;AAAA,QAC1B,UAAA;AAAA,QACA,SAAS,QAAA,EAAS;AAAA,QAClB,WAAA;AAAA,QACA,IAAI,QAAA;AAAS,OACf;AACA,MAAA,GAAA,CAAI,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,aAC9C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE1D;AACF;AAAA,EAIA,MAAM,SAAS,KAAA,EAA8B;AAC3C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,gBAAA,EAAkB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACnE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,SAAA,eAA2B;AACrD,IAAA,GAAA,CAAI,CAAA,YAAA,EAAe,KAAK,CAAA,CAAE,CAAA;AAAA;AAC5B,EAEA,MAAM,UAAU,KAAA,EAA8B;AAC5C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,iBAAA,EAAmB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACpE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,IAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA;AAC9B,EAEA,MAAM,sBAAsB,OAAA,EAAuC;AAEjE,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAE7B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,GAAA,GAAM,MAAM,IAAA,CAAK,YAAA,CAAa,GAAA,EAAK,OAAO,CAAA;AAAA;AAG5C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QAC/B,iBAAA;AAAA,QACA,CAAA;AAAA,QACA,IAAA,CAAK,MAAA;AAAA,QACL,EAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,MAAA,CAAO,MAAM,CAAA,KAAM,CAAA,EAAG,KAAA,EAAA;AAAA;AAG5B,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,KAAK,CAAA,KAAA,CAAO,CAAA;AAC7B,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAoC;AAC/D,IAAA,MAAM,EAAA,GAAK,CAAA,EAAG,IAAA,CAAK,MAAM,OAAO,KAAK,CAAA,CAAA;AACrC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,QAAQ,CAAA;AAClD,IAAA,IAAI,WAAW,SAAA,EAAW;AACxB,MAAA,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,yBAAA,EAA4B,MAAM,CAAA,gBAAA,CAAkB,CAAA;AACpE,MAAA;AAAA;AAGF,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,SAAmB,EAAC;AAC1B,IAAA,MAAM,WAAgB,EAAC;AAEvB,IAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,EAAW;AACjC,MAAA,MAAA,CAAO,KAAK,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,OAAO,CAAC,CAAA;AACtD,MAAA,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAAA;AAE7B,IAAA,IAAI,OAAA,CAAQ,gBAAgB,MAAA,EAAW;AACrC,MAAA,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,OAAA,CAAQ,WAAA,CAAY,UAAU,CAAA;AACzD,MAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AAAA;AAEjC,IAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,EAAY,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA;AACnD,MAAA,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAG5B,MAAA,MAAM,YAAY,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,WAAW,CAAA;AACxD,MAAA,MAAM,QAAQ,OAAA,CAAQ,QAAA,GAAW,IAAA,IAAQ,IAAA,GAAO,OAAO,SAAS,CAAA,CAAA;AAEhE,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,QAChC,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,CAAA;AAAA,QACd,MAAM,QAAA;AAAS,OACjB;AACA,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,CAAA,EAAS,KAAA,EAAO,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA;AACvE;AAEF,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW;AAC/B,MAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,EAAS,GAAA,CAAI,QAAA,EAAU,CAAA;AAAA,OACrC,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,OAAA,EAAS,OAAA,CAAQ,MAAM,OAAA,EAAQ,CAAE,UAAU,CAAA;AAAA;AAEzD,MAAA,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAAA;AAE3B,IAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,WAAA;AAAA,QACA,QAAQ,SAAA,KAAc,IAAA,GAAO,OAAA,CAAQ,SAAA,CAAU,UAAS,GAAI;AAAA,OAC9D;AACA,MAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAAA;AAE/B,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAE9B,MAAA,MAAM,cAAc,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,MAAM,CAAA;AACrD,MAAA,IAAI,WAAA,IAAe,gBAAgB,MAAA,EAAQ;AACzC,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA;AACtC,UAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,YAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,cAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA;AAAA,cACxB,MAAM,QAAA;AAAS,aACjB;AAAA;AACF,SACF,CAAA,MAAQ;AAAA;AAER;AAEF,MAAA,MAAM,IAAA,CAAK,OAAO,GAAA,CAAI,CAAA,EAAG,KAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,KAAA,CAAO,CAAA;AAEvD,MAAA,IAAI,OAAA,CAAQ,SAAS,IAAA,EAAM;AACzB,QAAA,KAAA,MAAW,GAAA,IAAO,QAAQ,IAAA,EAAM;AAC9B,UAAA,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA,EAAI,KAAA,CAAM,QAAA,EAAU,CAAA;AACnE,UAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,KAAA,CAAA,EAAS,GAAG,CAAA;AAAA;AAE/D,QAAA,MAAA,CAAO,KAAK,MAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,OAClD,MAAO;AACL,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA;AAE5B,MAAA,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAAA;AAG1B,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA;AAAA;AAGF,IAAA,MAAA,CAAO,IAAA,CAAK,WAAA,EAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AACvC,IAAA,MAAO,IAAA,CAAK,MAAA,CAAe,KAAA,CAAM,EAAA,EAAI,GAAG,MAAM,CAAA;AAE9C,IAAA,MAAM,IAAA,CAAK,cAAA,CAAe,KAAA,EAAA,QAAA,eAA4B,QAAQ,CAAA;AAC9D,IAAA,GAAA,CAAI,cAAc,KAAK,CAAA,EAAA,EAAK,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA;AACxD,EAEA,MAAM,kBAAA,CACJ,OAAA,EACA,OAAA,EACiB;AACjB,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAE7B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,GAAA,GAAM,MAAM,IAAA,CAAK,YAAA,CAAa,GAAA,EAAK,OAAO,CAAA;AAAA;AAG5C,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAO,CAAA;AACtC,MAAA,KAAA,EAAA;AAAA;AAGF,IAAA,GAAA,CAAI,CAAA,OAAA,EAAU,KAAK,CAAA,aAAA,CAAe,CAAA;AAClC,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,cAAA,CAAe,UAAA,GAAa,EAAA,EAAqB;AACrD,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,KAAU,UAAA,GAAa,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC5D,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,uBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL;AAAA,KACF;AACA,IAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAM,CAAA,SAAA,CAAW,CAAA;AAChC,IAAA,OAAO,OAAO,MAAM,CAAA;AAAA;AACtB,EAEA,MAAM,mBAAA,CAAoB,UAAA,GAAa,EAAA,EAAqB;AAK1D,IAAA,GAAA;AAAA,MACE,CAAA,wFAAA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA;AACT,EAEA,MAAM,gBAAA,CAAiB,wBAAA,GAA2B,EAAA,EAAqB;AACrE,IAAA,MAAM,QAAA,GAAW,2BAA2B,EAAA,GAAK,GAAA;AACjD,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,yBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,QAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAM,CAAA,WAAA,CAAa,CAAA;AACpC,IAAA,OAAO,OAAO,MAAM,CAAA;AAAA;AACtB;AAAA,EAIA,MAAM,+BAAA,CACJ,MAAA,EACA,OAAA,EACe;AACf,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AAEtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,OAAA,GAAU,CAAC,OAAO,CAAA;AACzD,MAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,MAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,QAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,CAAC,CAAA,CAAE,CAAA;AACpE,QAAA,KAAA,MAAW,EAAA,IAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAAA;AAE1C,MAAA,GAAA,GAAM,IAAI,MAAA,CAAO,CAAC,OAAe,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGlD,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA;AAAA,QACvB,eAAA;AAAA,QACA;AAAA,OACF;AAAA;AACF;AACF;AAAA,EAIA,MAAc,cACZ,GAAA,EACqC;AACrC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,QAAA,EAAS;AACtC,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,QAAA,CAAS,QAAQ,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAE,CAAA;AAAA;AAE5C,IAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,IAAA,MAAM,OAAmC,EAAC;AAC1C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,OAAA,EAAS;AACjC,QAAA,IACE,CAAC,GAAA,IACD,IAAA,IACA,OAAO,IAAA,KAAS,QAAA,IAChB,MAAA,CAAO,IAAA,CAAK,IAAc,CAAA,CAAE,MAAA,GAAS,CAAA,EACrC;AACA,UAAA,IAAA,CAAK,IAAA;AAAA,YACH,eAA8B,IAA8B;AAAA,WAC9D;AAAA;AACF;AACF;AAEF,IAAA,OAAO,IAAA;AAAA;AACT,EAEA,MAAc,YAAA,CACZ,YAAA,EACA,IAAA,EACA,IAAA,EACmB;AACnB,IAAA,MAAM,eAAe,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAC,CAAA;AAErD,IAAA,IAAI,SAAS,OAAA,EAAS;AAEpB,MAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,IAAI,CAAA;AAC3B,MAAA,MAAM,SAAmB,EAAC;AAC1B,MAAA,KAAA,MAAW,MAAM,YAAA,EAAc;AAC7B,QAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UAChC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,KAAA;AAAA,SACzB;AACA,QAAA,IACE,OAAA,CAAQ,MAAA,KAAW,MAAA,CAAO,IAAA,IAC1B,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,KAAc,MAAA,CAAO,GAAA,CAAI,CAAC,CAAC,CAAA,EAC1C;AACA,UAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA;AAChB;AAEF,MAAA,OAAO,MAAA;AAAA;AAGT,IAAA,IAAI,SAAS,KAAA,EAAO;AAElB,MAAA,IAAI,eAAe,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAC,CAAA;AACnD,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,MAAM,SAAS,IAAI,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAC,CAAA;AAC7C,QAAA,YAAA,GAAe,IAAI,GAAA;AAAA,UACjB,CAAC,GAAG,YAAY,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,MAAA,CAAO,GAAA,CAAI,EAAE,CAAC;AAAA,SACjD;AAAA;AAEF,MAAA,OAAO,CAAC,GAAG,YAAY,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,YAAA,CAAa,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAG9D,IAAA,IAAI,SAAS,KAAA,EAAO;AAElB,MAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,KAAA,MAAW,MAAM,UAAA,EAAY,KAAA,CAAM,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA;AAEnD,MAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,YAAA,CAAa,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGvD,IAAA,IAAI,SAAS,MAAA,EAAQ;AAEnB,MAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,KAAA,MAAW,MAAM,UAAA,EAAY,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA;AAErD,MAAA,OAAO,YAAA,CAAa,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,QAAQ,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAC,CAAA;AAAA;AAI7D,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,YAAA,EAAc,IAAA,EAAM,KAAK,CAAA;AAAA;AACpD,EAEQ,aAAA,CACN,MACA,KAAA,EAC4B;AAC5B,IAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,OAAA,EAAQ,KAAM,KAAA,CAAM,OAAA,EAAS,CAAA;AAAA;AAEjE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM;AACxB,MAAA,MAAM,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,OAAA,EAAQ;AAC1B,MAAA,IAAI,KAAA,CAAM,MAAM,EAAE,CAAA,GAAI,MAAM,EAAA,CAAG,OAAA,KAAY,OAAO,KAAA;AAClD,MAAA,IAAI,KAAA,CAAM,OAAO,EAAE,CAAA,IAAK,MAAM,GAAA,CAAI,OAAA,KAAY,OAAO,KAAA;AACrD,MAAA,IAAI,KAAA,CAAM,MAAM,EAAE,CAAA,GAAI,MAAM,EAAA,CAAG,OAAA,KAAY,OAAO,KAAA;AAClD,MAAA,IAAI,KAAA,CAAM,OAAO,EAAE,CAAA,IAAK,MAAM,GAAA,CAAI,OAAA,KAAY,OAAO,KAAA;AACrD,MAAA,IAAI,MAAM,EAAA,IAAM,CAAA,KAAM,MAAM,EAAA,CAAG,OAAA,IAAW,OAAO,KAAA;AACjD,MAAA,OAAO,IAAA;AAAA,KACR,CAAA;AAAA;AACH,EAEA,MAAc,YAAA,CACZ,GAAA,EACA,OAAA,EACmB;AACnB,IAAA,IAAI,MAAA,GAAS,GAAA;AAEb,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAM,UAAU,IAAI,GAAA;AAAA,QAClB,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,EAAQ,OAAA,CAAQ,OAAO,CAAA,CAAE;AAAA,OACpE;AACA,MAAA,MAAA,GAAS,OAAO,MAAA,CAAO,CAAC,OAAO,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGhD,IAAA,IAAI,QAAQ,IAAA,IAAQ,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AAClD,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,YAAA;AAAA,QAClB,MAAA;AAAA,QACA,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,OAAA,CAAQ,KAAK,IAAA,IAAQ;AAAA,OACvB;AAAA;AAIF,IAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,MAAA,IAAa,OAAA,CAAQ,KAAA,EAAO;AACnD,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAAc,MAAM,CAAA;AAC5C,MAAA,IAAI,QAAA,GAAW,IAAA;AACf,MAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,QAAA,QAAA,GAAW,SAAS,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,KAAa,QAAQ,QAAQ,CAAA;AAAA;AAEnE,MAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,QAAA,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,QAAA,EAAU,OAAA,CAAQ,KAAK,CAAA;AAAA;AAEvD,MAAA,MAAA,GAAS,SAAS,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,EAAA,CAAG,UAAU,CAAA;AAAA;AAG9C,IAAA,OAAO,MAAA;AAAA;AAEX,CAAA;;;ACzyBO,SAASC,4BAAAA,CAId,SACA,OAAA,EAC6C;AAC7C,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AACvC,IAAA,MAAM,SAAA,GAAY,OAAA,GAAU,CAAA,UAAA,EAAa,OAAO,CAAA,CAAA,CAAA,GAAM,SAAA;AAItD,IAAA,IACE,aAAA,CAAc,SAAS,OAAO,CAAA,IAC9B,CAAC,aAAA,CAAc,KAAA,CAAM,oBAAoB,CAAA,EACzC;AACA,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,eAAe,SAAS,CAAA,sIAAA;AAAA,OAE5B;AAAA;AAKF,IAAA,IAAI,aAAA,CAAc,QAAA,CAAS,eAAe,CAAA,EAAG;AAC3C,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,eAAe,SAAS,CAAA,6HAAA;AAAA,OAE5B;AAAA;AAKF,IAAA,IAAI;AACF,MAAA,IAAI,QAAA,CAAS,YAAY,aAAa,CAAA;AAAA,aAC/B,UAAA,EAAY;AACnB,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,CAAA,YAAA,EAAe,SAAS,CAAA,uBAAA,EAA0B,UAAA,YAAsB,QAAQ,UAAA,CAAW,OAAA,GAAU,MAAA,CAAO,UAAU,CAAC,CAAA,kHAAA;AAAA,OAE3H;AAAA;AAMF,IAAA,MAAM,sBACJ,uCAAA,CAAwC,IAAA,CAAK,aAAa,CAAA,IAC1D,qCAAA,CAAsC,KAAK,aAAa,CAAA;AAE1D,IAAA,IAAI,mBAAA,EAAqB;AAGvB,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,IAAA;AAAA;AAAA,QAChB,KAAA,EACE,wBAAwB,SAAS,CAAA,kNAAA;AAAA,OAGrC;AAAA;AAGF,IAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA,WACvB,KAAA,EAAO;AACd,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,KAAA;AAAA,MAChB,KAAA,EAAO,CAAA,wCAAA,EAA2C,OAAA,GAAU,CAAA,eAAA,EAAkB,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,EAAA,EAAK,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAC1J;AAAA;AAEJ;AAsBA,eAAsB,wBAAA,CAIpB,SACA,OAAA,EACsD;AAEtD,EAAA,MAAM,eAAA,GAAkBA,4BAAAA,CAA4B,OAAA,EAAS,OAAO,CAAA;AACpE,EAAA,IAAI,CAAC,gBAAgB,cAAA,EAAgB;AACnC,IAAA,OAAO,eAAA;AAAA;AAIT,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AACvC,IAAA,MAAM,SAAA,GAAY,IAAI,QAAA,CAAS,SAAA,GAAY,aAAa,CAAA,EAAE;AAI1D,IAAA,MAAM,cAAc,SAAA,CAAU,IAAI,IAAI,eAAA,GAAkB,MAAM,CAAA;AAC9D,IAAA,MAAM,iBAAiB,IAAI,OAAA;AAAA,MAAQ,CAAC,CAAA,EAAG,MAAA,KACrC,UAAA,CAAW,MAAM,MAAA,CAAO,IAAI,KAAA,CAAM,sBAAsB,CAAC,CAAA,EAAG,GAAG;AAAA,KACjE;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,WAAA,EAAa,cAAc,CAAC,CAAA;AAAA,aACzC,SAAA,EAAW;AAGlB,MAAA,IACE,SAAA,YAAqB,KAAA,IACrB,SAAA,CAAU,OAAA,KAAY,sBAAA,EACtB;AAEA,QAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA;AAChC;AAGF,IAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA,WACvB,KAAA,EAAO;AACd,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,KAAA;AAAA,MAChB,KAAA,EAAO,sCAAsC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACrG;AAAA;AAEJ;;;AC3IO,IAAM,YAAA,GAAe,CAC1B,MAAA,KACyB;AACzB,EAAA,MAAM,WAAA,GAAc,OAAO,OAAA,IAAW,UAAA;AACtC,EAAA,aAAA,CAAc,MAAA,CAAO,WAAW,KAAK,CAAA;AAErC,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,IAAA;AAEJ,EAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,IAAA,MAAM,QAAA,GAAW,MAAA;AACjB,IAAA,IAAA,GAAO,UAAA,CAAW,SAAS,cAAc,CAAA;AACzC,IAAA,OAAA,GAAU,IAAI,gBAAgB,IAAI,CAAA;AAAA,GACpC,MAAA,IAAW,gBAAgB,OAAA,EAAS;AAClC,IAAA,MAAM,cAAe,MAAA,CAA+B,WAAA;AAEpD,IAAA,OAAA,GAAU,IAAI,aAAa,WAAW,CAAA;AAAA,GACxC,MAAO;AACL,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,WAAW,CAAA,CAAE,CAAA;AAAA;AAGnD,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAEF,IAAA,OAAO,IAAA;AAAA,GACT;AAGA,EAAA,OAAO;AAAA;AAAA,IAEL,MAAA,EAAQ,cAAA;AAAA,MACN,CAAC,GAAA,KACC,OAAA,CAAQ,MAAA,CAAwB,GAAG,CAAA;AAAA,MACrC,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,MAAA,EAAQ,cAAA;AAAA,MACN,CAAC,EAAA,KAAe,OAAA,CAAQ,MAAA,CAAwB,EAAE,CAAA;AAAA,MAClD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,eAAA,EAAiB,cAAA;AAAA,MACf,CAAC,QAAgB,KAAA,EAAgB,MAAA,KAC/B,QAAQ,eAAA,CAAiC,MAAA,EAAQ,OAAO,MAAM,CAAA;AAAA,MAChE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,UAAA,EAAY,cAAA;AAAA,MACV,CAAC,KAAA,EAAgB,MAAA,KACf,OAAA,CAAQ,UAAA,CAA4B,OAAO,MAAM,CAAA;AAAA,MACnD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,MACP,CACE,SAQA,KAAA,EACA,MAAA,KACG,QAAQ,OAAA,CAAyB,OAAA,EAAS,OAAO,MAAM,CAAA;AAAA,MAC5D,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,QAAA,EAAU,CAAC,KAAA,KAAkB,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,IACnD,cAAA,EAAgB,CAAC,UAAA,KAAwB,OAAA,CAAQ,eAAe,UAAU,CAAA;AAAA,IAC1E,mBAAA,EAAqB,CAAC,UAAA,KACpB,OAAA,CAAQ,oBAAoB,UAAU,CAAA;AAAA,IACxC,SAAA,EAAW,cAAA;AAAA,MACT,CAAC,KAAA,KAAkB,OAAA,CAAQ,SAAA,CAAU,KAAK,CAAA;AAAA,MAC1C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,MACP,CACE,KAAA,EACA,OAAA,KACG,OAAA,CAAQ,OAAA,CAAQ,OAAO,OAA4C,CAAA;AAAA,MACxE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,kBAAA,EAAoB,cAAA;AAAA,MAClB,CACE,OAAA,EAaA,OAAA,KAEA,OAAA,CAAQ,kBAAA;AAAA,QACN,OAAA;AAAA,QACA;AAAA,OACF;AAAA,MACF,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,qBAAA,EAAuB,cAAA;AAAA,MACrB,CAAC,OAAA,KAOK,OAAA,CAAQ,qBAAA,CAAsB,OAAO,CAAA;AAAA,MAC3C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,gBAAA,EAAkB,cAAA;AAAA,MAChB,CAAC,wBAAA,KACC,OAAA,CAAQ,gBAAA,CAAiB,wBAAwB,CAAA;AAAA,MACnD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,IAAA,EAAgB,IAAA,GAAO,KAAA,EAAO,KAAA,EAAgB,MAAA,KAC7C,OAAA,CAAQ,aAAA,CAA+B,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAAA,MAClE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,iBAAiB,CACf,QAAA,EACA,YACG,eAAA,CAA4B,OAAA,EAAS,UAAU,OAAO,CAAA;AAAA;AAAA,IAG3D,YAAA,EAAc,cAAA;AAAA,MACZ,CAAC,KAAA,KAAkB,OAAA,CAAQ,YAAA,CAAa,KAAK,CAAA;AAAA,MAC7C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,WAAA,EAAa,cAAA;AAAA,MACX,CAAC,OAAA,KACC,eAAA,CAAgB,WAAA,EAAY,EAAG,MAAM,OAAO,CAAA;AAAA,MAC9C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,OAAA,EAAiB,IAAA,KAChB,kBAAkB,WAAA,EAAY,EAAG,SAAS,IAAI,CAAA;AAAA,MAChD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,QAAA,EAAU,cAAA;AAAA,MACR,CAAC,OAAA,KAAoB,YAAA,CAAa,WAAA,IAAe,OAAO,CAAA;AAAA,MACxD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,oBAAA,EAAsB,cAAA;AAAA,MACpB,MAAM,wBAAA,CAAyB,WAAA,EAAa,CAAA;AAAA,MAC5C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,SAAS,MAAM;AACb,MAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA;AAEF,MAAA,OAAQ,QAA4B,OAAA,EAAQ;AAAA,KAC9C;AAAA,IACA,gBAAgB,MAAM;AACpB,MAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA;AAEF,MAAA,OAAQ,QAAyB,SAAA,EAAU;AAAA;AAC7C,GACF;AACF;AAEA,IAAM,cAAA,GACJ,CAAI,EAAA,EAA2B,OAAA,KAC/B,IAAI,IAAA,KAAuD;AACzD,EAAA,aAAA,CAAc,OAAO,CAAA;AACrB,EAAA,OAAO,EAAA,CAAG,GAAG,IAAI,CAAA;AACnB,CAAA","file":"index.cjs","sourcesContent":["// Utility type for job type keys\nexport type JobType<PayloadMap> = keyof PayloadMap & string;\n\nexport interface JobOptions<PayloadMap, T extends JobType<PayloadMap>> {\n jobType: T;\n payload: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n /**\n * Timeout for this job in milliseconds. If not set, uses the processor default or unlimited.\n */\n timeoutMs?: number;\n /**\n * If true, the job will be forcefully terminated (using Worker Threads) when timeout is reached.\n * If false (default), the job will only receive an AbortSignal and must handle the abort gracefully.\n *\n * **⚠️ RUNTIME REQUIREMENTS**: This option requires **Node.js** and uses the `worker_threads` module.\n * It will **not work** in Bun or other runtimes that don't support Node.js worker threads.\n *\n * **IMPORTANT**: When `forceKillOnTimeout` is true, the handler must be serializable. This means:\n * - The handler should be a standalone function (not a closure over external variables)\n * - It should not capture variables from outer scopes that reference external dependencies\n * - It should not use 'this' context unless it's a bound method\n * - All dependencies must be importable in the worker thread context\n *\n * **Examples of serializable handlers:**\n * ```ts\n * // ✅ Good - standalone function\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * // ✅ Good - function that imports dependencies\n * const handler = async (payload, signal) => {\n * const { api } = await import('./api');\n * await api.call(payload);\n * };\n *\n * // ❌ Bad - closure over external variable\n * const db = getDatabase();\n * const handler = async (payload, signal) => {\n * await db.query(payload); // 'db' is captured from closure\n * };\n *\n * // ❌ Bad - uses 'this' context\n * class MyHandler {\n * async handle(payload, signal) {\n * await this.doSomething(payload); // 'this' won't work\n * }\n * }\n * ```\n *\n * If your handler doesn't meet these requirements, use `forceKillOnTimeout: false` (default)\n * and ensure your handler checks `signal.aborted` to exit gracefully.\n *\n * Note: forceKillOnTimeout requires timeoutMs to be set.\n */\n forceKillOnTimeout?: boolean;\n /**\n * Tags for this job. Used for grouping, searching, or batch operations.\n */\n tags?: string[];\n /**\n * Optional idempotency key. When provided, ensures that only one job exists for a given key.\n * If a job with the same idempotency key already exists, `addJob` returns the existing job's ID\n * instead of creating a duplicate.\n *\n * Useful for preventing duplicate jobs caused by retries, double-clicks, webhook replays,\n * or serverless function re-invocations.\n *\n * The key is unique across the entire `job_queue` table regardless of job status.\n * Once a key exists, it cannot be reused until the job is cleaned up (via `cleanupOldJobs`).\n */\n idempotencyKey?: string;\n}\n\n/**\n * Options for editing a pending job.\n * All fields are optional and only provided fields will be updated.\n * Note: jobType cannot be changed.\n * timeoutMs and tags can be set to null to clear them.\n */\nexport type EditJobOptions<PayloadMap, T extends JobType<PayloadMap>> = Partial<\n Omit<JobOptions<PayloadMap, T>, 'jobType'>\n> & {\n timeoutMs?: number | null;\n tags?: string[] | null;\n};\n\nexport enum JobEventType {\n Added = 'added',\n Processing = 'processing',\n Completed = 'completed',\n Failed = 'failed',\n Cancelled = 'cancelled',\n Retried = 'retried',\n Edited = 'edited',\n Prolonged = 'prolonged',\n Waiting = 'waiting',\n}\n\nexport interface JobEvent {\n id: number;\n jobId: number;\n eventType: JobEventType;\n createdAt: Date;\n metadata: any;\n}\n\nexport enum FailureReason {\n Timeout = 'timeout',\n HandlerError = 'handler_error',\n NoHandler = 'no_handler',\n}\n\nexport type JobStatus =\n | 'pending'\n | 'processing'\n | 'completed'\n | 'failed'\n | 'cancelled'\n | 'waiting';\n\nexport interface JobRecord<PayloadMap, T extends JobType<PayloadMap>> {\n id: number;\n jobType: T;\n payload: PayloadMap[T];\n status: JobStatus;\n createdAt: Date;\n updatedAt: Date;\n lockedAt: Date | null;\n lockedBy: string | null;\n attempts: number;\n maxAttempts: number;\n nextAttemptAt: Date | null;\n priority: number;\n runAt: Date;\n pendingReason?: string | null;\n errorHistory?: { message: string; timestamp: string }[];\n /**\n * Timeout for this job in milliseconds (null means no timeout).\n */\n timeoutMs?: number | null;\n /**\n * If true, the job will be forcefully terminated (using Worker Threads) when timeout is reached.\n * If false (default), the job will only receive an AbortSignal and must handle the abort gracefully.\n */\n forceKillOnTimeout?: boolean | null;\n /**\n * The reason for the last failure, if any.\n */\n failureReason?: FailureReason | null;\n /**\n * The time the job was completed, if completed.\n */\n completedAt: Date | null;\n /**\n * The time the job was first picked up for processing.\n */\n startedAt: Date | null;\n /**\n * The time the job was last retried.\n */\n lastRetriedAt: Date | null;\n /**\n * The time the job last failed.\n */\n lastFailedAt: Date | null;\n /**\n * The time the job was last cancelled.\n */\n lastCancelledAt: Date | null;\n /**\n * Tags for this job. Used for grouping, searching, or batch operations.\n */\n tags?: string[];\n /**\n * The idempotency key for this job, if one was provided when the job was created.\n */\n idempotencyKey?: string | null;\n /**\n * The time the job is waiting until (for time-based waits).\n */\n waitUntil?: Date | null;\n /**\n * The waitpoint token ID the job is waiting for (for token-based waits).\n */\n waitTokenId?: string | null;\n /**\n * Step data for the job. Stores completed step results for replay on re-invocation.\n */\n stepData?: Record<string, any>;\n /**\n * Progress percentage for the job (0-100), or null if no progress has been reported.\n * Updated by the handler via `ctx.setProgress(percent)`.\n */\n progress?: number | null;\n}\n\n/**\n * Callback registered via `onTimeout`. Invoked when the timeout fires, before the AbortSignal is triggered.\n * Return a number (ms) to extend the timeout, or return nothing to let the timeout proceed.\n */\nexport type OnTimeoutCallback = () => number | void | undefined;\n\n/**\n * Context object passed to job handlers as the third argument.\n * Provides mechanisms to extend the job's timeout while it's running,\n * as well as step tracking and wait capabilities.\n */\nexport interface JobContext {\n /**\n * Proactively reset the timeout deadline.\n * - If `ms` is provided, sets the deadline to `ms` milliseconds from now.\n * - If omitted, resets the deadline to the original `timeoutMs` from now (heartbeat-style).\n * - No-op if the job has no timeout set or if `forceKillOnTimeout` is true.\n */\n prolong: (ms?: number) => void;\n\n /**\n * Register a callback that is invoked when the timeout fires, **before** the AbortSignal is triggered.\n * - If the callback returns a number > 0, the timeout is reset to that many ms from now.\n * - If the callback returns `undefined`, `null`, `0`, or a negative number, the timeout proceeds normally.\n * - The callback may be invoked multiple times if the job keeps extending.\n * - Only one callback can be registered; subsequent calls replace the previous one.\n * - No-op if the job has no timeout set or if `forceKillOnTimeout` is true.\n */\n onTimeout: (callback: OnTimeoutCallback) => void;\n\n /**\n * Execute a named step with memoization. If the step was already completed\n * in a previous invocation (e.g., before a wait), the cached result is returned\n * without re-executing the function.\n *\n * Step names must be unique within a handler and stable across re-invocations.\n *\n * @param stepName - A unique identifier for this step.\n * @param fn - The function to execute. Its return value is cached.\n * @returns The result of the step (from cache or fresh execution).\n */\n run: <T>(stepName: string, fn: () => Promise<T>) => Promise<T>;\n\n /**\n * Wait for a specified duration before continuing execution.\n * The job will be paused and resumed after the duration elapses.\n *\n * When this is called, the handler throws a WaitSignal internally.\n * The job is set to 'waiting' status and will be re-invoked after the\n * specified duration. All steps completed via `ctx.run()` before this\n * call will be replayed from cache on re-invocation.\n *\n * @param duration - The duration to wait (e.g., `{ hours: 1 }`, `{ days: 7 }`).\n */\n waitFor: (duration: WaitDuration) => Promise<void>;\n\n /**\n * Wait until a specific date/time before continuing execution.\n * The job will be paused and resumed at (or after) the specified date.\n *\n * @param date - The date to wait until.\n */\n waitUntil: (date: Date) => Promise<void>;\n\n /**\n * Create a waitpoint token. The token can be completed externally\n * (by calling `jobQueue.completeToken()`) to resume a waiting job.\n *\n * Tokens can be created inside handlers or outside (via `jobQueue.createToken()`).\n *\n * @param options - Optional token configuration (timeout, tags).\n * @returns A token object with `id` that can be passed to `waitForToken()`.\n */\n createToken: (options?: CreateTokenOptions) => Promise<WaitToken>;\n\n /**\n * Wait for a waitpoint token to be completed by an external signal.\n * The job will be paused until `jobQueue.completeToken(tokenId, data)` is called\n * or the token times out.\n *\n * @param tokenId - The ID of the token to wait for.\n * @returns A result object indicating success or timeout.\n */\n waitForToken: <T = any>(tokenId: string) => Promise<WaitTokenResult<T>>;\n\n /**\n * Report progress for this job (0-100).\n * The value is persisted to the database and can be read by clients\n * via `getJob()` or the React SDK's `useJob()` hook.\n *\n * @param percent - Progress percentage (0-100). Values are rounded to the nearest integer.\n * @throws If percent is outside the 0-100 range.\n */\n setProgress: (percent: number) => Promise<void>;\n}\n\n/**\n * Duration specification for `ctx.waitFor()`.\n * At least one field must be provided. Fields are additive.\n */\nexport interface WaitDuration {\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n weeks?: number;\n months?: number;\n years?: number;\n}\n\n/**\n * Options for creating a waitpoint token.\n */\nexport interface CreateTokenOptions {\n /**\n * Maximum time to wait for the token to be completed.\n * Accepts a duration string like '10m', '1h', '24h', '7d'.\n * If not provided, the token has no timeout.\n */\n timeout?: string;\n /**\n * Tags to attach to the token for filtering.\n */\n tags?: string[];\n}\n\n/**\n * A waitpoint token returned by `ctx.createToken()`.\n */\nexport interface WaitToken {\n /** The unique token ID. */\n id: string;\n}\n\n/**\n * Result of `ctx.waitForToken()`.\n */\nexport type WaitTokenResult<T = any> =\n | { ok: true; output: T }\n | { ok: false; error: string };\n\n/**\n * Internal signal thrown by wait methods to pause handler execution.\n * This is not a real error -- the processor catches it and transitions the job to 'waiting' status.\n */\nexport class WaitSignal extends Error {\n readonly isWaitSignal = true;\n\n constructor(\n public readonly type: 'duration' | 'date' | 'token',\n public readonly waitUntil: Date | undefined,\n public readonly tokenId: string | undefined,\n public readonly stepData: Record<string, any>,\n ) {\n super('WaitSignal');\n this.name = 'WaitSignal';\n }\n}\n\n/**\n * Status of a waitpoint token.\n */\nexport type WaitpointStatus = 'waiting' | 'completed' | 'timed_out';\n\n/**\n * A waitpoint record from the database.\n */\nexport interface WaitpointRecord {\n id: string;\n jobId: number | null;\n status: WaitpointStatus;\n output: any;\n timeoutAt: Date | null;\n createdAt: Date;\n completedAt: Date | null;\n tags: string[] | null;\n}\n\nexport type JobHandler<PayloadMap, T extends keyof PayloadMap> = (\n payload: PayloadMap[T],\n signal: AbortSignal,\n ctx: JobContext,\n) => Promise<void>;\n\nexport type JobHandlers<PayloadMap> = {\n [K in keyof PayloadMap]: JobHandler<PayloadMap, K>;\n};\n\nexport interface ProcessorOptions {\n workerId?: string;\n /**\n * The number of jobs to process at a time.\n * - If not provided, the processor will process 10 jobs at a time.\n * - In serverless functions, it's better to process less jobs at a time since serverless functions are charged by the second and have a timeout.\n */\n batchSize?: number;\n /**\n * The maximum number of jobs to process in parallel per batch.\n * - If not provided, all jobs in the batch are processed in parallel.\n * - Set to 1 to process jobs sequentially.\n * - Set to a lower value to avoid resource exhaustion.\n */\n concurrency?: number;\n /**\n * The interval in milliseconds to poll for new jobs.\n * - If not provided, the processor will process jobs every 5 seconds when startInBackground is called.\n * - In serverless functions, it's better to leave this empty.\n * - If you call start instead of startInBackground, the pollInterval is ignored.\n */\n pollInterval?: number;\n onError?: (error: Error) => void;\n verbose?: boolean;\n /**\n * Only process jobs with this job type (string or array of strings). If omitted, all job types are processed.\n */\n jobType?: string | string[];\n}\n\nexport interface Processor {\n /**\n * Start the job processor in the background.\n * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs (as many as batchSize) as they become available.\n * - **You have to call the stop method to stop the processor.**\n * - Handlers are provided per-processor when calling createProcessor.\n * - In serverless functions, it's recommended to call start instead and await it to finish.\n */\n startInBackground: () => void;\n /**\n * Stop the job processor that runs in the background.\n * Does not wait for in-flight jobs to complete.\n */\n stop: () => void;\n /**\n * Stop the job processor and wait for all in-flight jobs to complete.\n * Useful for graceful shutdown (e.g., SIGTERM handling).\n * No new batches will be started after calling this method.\n *\n * @param timeoutMs - Maximum time to wait for in-flight jobs (default: 30000ms).\n * If jobs don't complete within this time, the promise resolves anyway.\n */\n stopAndDrain: (timeoutMs?: number) => Promise<void>;\n /**\n * Check if the job processor is running.\n */\n isRunning: () => boolean;\n /**\n * Start the job processor synchronously.\n * - This will process jobs (as many as batchSize) immediately and then stop. The pollInterval is ignored.\n * - In serverless functions, it's recommended to use this instead of startInBackground.\n * - Returns the number of jobs processed.\n */\n start: () => Promise<number>;\n}\n\nexport interface DatabaseSSLConfig {\n /**\n * CA certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n ca?: string;\n /**\n * Client certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n cert?: string;\n /**\n * Client private key as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n key?: string;\n /**\n * Whether to reject unauthorized certificates (default: true)\n */\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Configuration for PostgreSQL backend (default).\n * Backward-compatible: omitting `backend` defaults to 'postgres'.\n */\nexport interface PostgresJobQueueConfig {\n backend?: 'postgres';\n databaseConfig: {\n connectionString?: string;\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n password?: string;\n ssl?: DatabaseSSLConfig;\n };\n verbose?: boolean;\n}\n\n/**\n * TLS configuration for the Redis connection.\n */\nexport interface RedisTLSConfig {\n ca?: string;\n cert?: string;\n key?: string;\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Configuration for Redis backend.\n */\nexport interface RedisJobQueueConfig {\n backend: 'redis';\n redisConfig: {\n /** Redis URL (e.g. redis://localhost:6379) */\n url?: string;\n host?: string;\n port?: number;\n password?: string;\n /** Redis database number (default: 0) */\n db?: number;\n tls?: RedisTLSConfig;\n /**\n * Key prefix for all Redis keys (default: 'dq:').\n * Useful to namespace multiple queues in the same Redis instance.\n */\n keyPrefix?: string;\n };\n verbose?: boolean;\n}\n\n/**\n * Job queue configuration — discriminated union.\n * If `backend` is omitted, PostgreSQL is used.\n */\nexport type JobQueueConfig = PostgresJobQueueConfig | RedisJobQueueConfig;\n\n/** @deprecated Use JobQueueConfig instead. Alias kept for backward compat. */\nexport type JobQueueConfigLegacy = PostgresJobQueueConfig;\n\nexport type TagQueryMode = 'exact' | 'all' | 'any' | 'none';\n\nexport interface JobQueue<PayloadMap> {\n /**\n * Add a job to the job queue.\n */\n addJob: <T extends JobType<PayloadMap>>(\n job: JobOptions<PayloadMap, T>,\n ) => Promise<number>;\n /**\n * Get a job by its ID.\n */\n getJob: <T extends JobType<PayloadMap>>(\n id: number,\n ) => Promise<JobRecord<PayloadMap, T> | null>;\n /**\n * Get jobs by their status, with pagination.\n * - If no limit is provided, all jobs are returned.\n * - If no offset is provided, the first page is returned.\n * - The jobs are returned in descending order of createdAt.\n */\n getJobsByStatus: <T extends JobType<PayloadMap>>(\n status: JobStatus,\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get jobs by tag(s).\n * - Modes:\n * - 'exact': Jobs with exactly the same tags (no more, no less)\n * - 'all': Jobs that have all the given tags (can have more)\n * - 'any': Jobs that have at least one of the given tags\n * - 'none': Jobs that have none of the given tags\n * - Default mode is 'all'.\n */\n getJobsByTags: <T extends JobType<PayloadMap>>(\n tags: string[],\n mode?: TagQueryMode,\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get all jobs.\n */\n getAllJobs: <T extends JobType<PayloadMap>>(\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get jobs by filters, with pagination support.\n * - Use `cursor` for efficient keyset pagination (recommended for large datasets).\n * - Use `limit` and `offset` for traditional pagination.\n * - Do not combine `cursor` with `offset`.\n */\n getJobs: <T extends JobType<PayloadMap>>(\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n /** Cursor for keyset pagination. Only return jobs with id < cursor. */\n cursor?: number;\n },\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Retry a job given its ID.\n * - This will set the job status back to 'pending', clear the locked_at and locked_by, and allow it to be picked up by other workers.\n */\n retryJob: (jobId: number) => Promise<void>;\n /**\n * Cleanup jobs that are older than the specified number of days.\n */\n cleanupOldJobs: (daysToKeep?: number) => Promise<number>;\n /**\n * Cleanup job events that are older than the specified number of days.\n */\n cleanupOldJobEvents: (daysToKeep?: number) => Promise<number>;\n /**\n * Cancel a job given its ID.\n * - This will set the job status to 'cancelled' and clear the locked_at and locked_by.\n */\n cancelJob: (jobId: number) => Promise<void>;\n /**\n * Edit a pending job given its ID.\n * - Only works for jobs with status 'pending'. Silently fails for other statuses.\n * - All fields in EditJobOptions are optional - only provided fields will be updated.\n * - jobType cannot be changed.\n * - Records an 'edited' event with the updated fields in metadata.\n */\n editJob: <T extends JobType<PayloadMap>>(\n jobId: number,\n updates: EditJobOptions<PayloadMap, T>,\n ) => Promise<void>;\n /**\n * Edit all pending jobs that match the filters.\n * - Only works for jobs with status 'pending'. Non-pending jobs are not affected.\n * - All fields in EditJobOptions are optional - only provided fields will be updated.\n * - jobType cannot be changed.\n * - Records an 'edited' event with the updated fields in metadata for each affected job.\n * - Returns the number of jobs that were edited.\n * - The filters are:\n * - jobType: The job type to edit.\n * - priority: The priority of the job to edit.\n * - runAt: The time the job is scheduled to run at (now supports gt/gte/lt/lte/eq).\n * - tags: An object with 'values' (string[]) and 'mode' (TagQueryMode) for tag-based editing.\n */\n editAllPendingJobs: <T extends JobType<PayloadMap>>(\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }\n | undefined,\n updates: EditJobOptions<PayloadMap, T>,\n ) => Promise<number>;\n /**\n * Reclaim stuck jobs.\n * - If a process (e.g., API route or worker) crashes after marking a job as 'processing' but before completing it, the job can remain stuck in the 'processing' state indefinitely. This can happen if the process is killed or encounters an unhandled error after updating the job status but before marking it as 'completed' or 'failed'.\n * - This function will set the job status back to 'pending', clear the locked_at and locked_by, and allow it to be picked up by other workers.\n * - The default max processing time is 10 minutes.\n */\n reclaimStuckJobs: (maxProcessingTimeMinutes?: number) => Promise<number>;\n /**\n * Cancel all upcoming jobs that match the filters.\n * - If no filters are provided, all upcoming jobs are cancelled.\n * - If filters are provided, only jobs that match the filters are cancelled.\n * - The filters are:\n * - jobType: The job type to cancel.\n * - priority: The priority of the job to cancel.\n * - runAt: The time the job is scheduled to run at (now supports gt/gte/lt/lte/eq).\n * - tags: An object with 'values' (string[]) and 'mode' (TagQueryMode) for tag-based cancellation.\n */\n cancelAllUpcomingJobs: (filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }) => Promise<number>;\n /**\n * Create a job processor. Handlers must be provided per-processor.\n */\n createProcessor: (\n handlers: JobHandlers<PayloadMap>,\n options?: ProcessorOptions,\n ) => Processor;\n\n /**\n * Get the job events for a job.\n */\n getJobEvents: (jobId: number) => Promise<JobEvent[]>;\n\n /**\n * Create a waitpoint token.\n * Tokens can be completed externally to resume a waiting job.\n * Can be called outside of handlers (e.g., from an API route).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param options - Optional token configuration (timeout, tags).\n * @returns A token object with `id`.\n */\n createToken: (options?: CreateTokenOptions) => Promise<WaitToken>;\n\n /**\n * Complete a waitpoint token, resuming the associated waiting job.\n * Can be called from anywhere (API routes, external services, etc.).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param tokenId - The ID of the token to complete.\n * @param data - Optional data to pass to the waiting handler.\n */\n completeToken: (tokenId: string, data?: any) => Promise<void>;\n\n /**\n * Retrieve a waitpoint token by its ID.\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param tokenId - The ID of the token to retrieve.\n * @returns The token record, or null if not found.\n */\n getToken: (tokenId: string) => Promise<WaitpointRecord | null>;\n\n /**\n * Expire timed-out waitpoint tokens and resume their associated jobs.\n * Call this periodically (e.g., alongside `reclaimStuckJobs`).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @returns The number of tokens that were expired.\n */\n expireTimedOutTokens: () => Promise<number>;\n\n /**\n * Get the PostgreSQL database pool.\n * Throws if the backend is not PostgreSQL.\n */\n getPool: () => import('pg').Pool;\n /**\n * Get the Redis client instance (ioredis).\n * Throws if the backend is not Redis.\n */\n getRedisClient: () => unknown;\n}\n","import { AsyncLocalStorage } from 'async_hooks';\n\nexport const logStorage = new AsyncLocalStorage<{\n verbose: boolean;\n}>();\n\nexport const setLogContext = (verbose: boolean) => {\n logStorage.enterWith({ verbose });\n};\n\nexport const getLogContext = () => {\n return logStorage.getStore();\n};\n\nexport const log = (message: string) => {\n const context = getLogContext();\n if (context?.verbose) {\n console.log(message);\n }\n};\n","import { Pool } from 'pg';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n JobType,\n} from '../types.js';\nimport { QueueBackend, JobFilters, JobUpdates } from '../backend.js';\nimport { log } from '../log-context.js';\n\nexport class PostgresBackend implements QueueBackend {\n constructor(private pool: Pool) {}\n\n /** Expose the raw pool for advanced usage. */\n getPool(): Pool {\n return this.pool;\n }\n\n // ── Events ──────────────────────────────────────────────────────────\n\n async recordJobEvent(\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `INSERT INTO job_events (job_id, event_type, metadata) VALUES ($1, $2, $3)`,\n [jobId, eventType, metadata ? JSON.stringify(metadata) : null],\n );\n } catch (error) {\n log(`Error recording job event for job ${jobId}: ${error}`);\n // Do not throw, to avoid interfering with main job logic\n } finally {\n client.release();\n }\n }\n\n async getJobEvents(jobId: number): Promise<JobEvent[]> {\n const client = await this.pool.connect();\n try {\n const res = await client.query(\n `SELECT id, job_id AS \"jobId\", event_type AS \"eventType\", metadata, created_at AS \"createdAt\" FROM job_events WHERE job_id = $1 ORDER BY created_at ASC`,\n [jobId],\n );\n return res.rows as JobEvent[];\n } finally {\n client.release();\n }\n }\n\n // ── Job CRUD ──────────────────────────────────────────────────────────\n\n async addJob<PayloadMap, T extends JobType<PayloadMap>>({\n jobType,\n payload,\n maxAttempts = 3,\n priority = 0,\n runAt = null,\n timeoutMs = undefined,\n forceKillOnTimeout = false,\n tags = undefined,\n idempotencyKey = undefined,\n }: JobOptions<PayloadMap, T>): Promise<number> {\n const client = await this.pool.connect();\n try {\n let result;\n const onConflict = idempotencyKey\n ? `ON CONFLICT (idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING`\n : '';\n\n if (runAt) {\n result = await client.query(\n `INSERT INTO job_queue \n (job_type, payload, max_attempts, priority, run_at, timeout_ms, force_kill_on_timeout, tags, idempotency_key) \n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) \n ${onConflict}\n RETURNING id`,\n [\n jobType,\n payload,\n maxAttempts,\n priority,\n runAt,\n timeoutMs ?? null,\n forceKillOnTimeout ?? false,\n tags ?? null,\n idempotencyKey ?? null,\n ],\n );\n } else {\n result = await client.query(\n `INSERT INTO job_queue \n (job_type, payload, max_attempts, priority, timeout_ms, force_kill_on_timeout, tags, idempotency_key) \n VALUES ($1, $2, $3, $4, $5, $6, $7, $8) \n ${onConflict}\n RETURNING id`,\n [\n jobType,\n payload,\n maxAttempts,\n priority,\n timeoutMs ?? null,\n forceKillOnTimeout ?? false,\n tags ?? null,\n idempotencyKey ?? null,\n ],\n );\n }\n\n // If ON CONFLICT DO NOTHING was triggered, no rows are returned.\n if (result.rows.length === 0 && idempotencyKey) {\n const existing = await client.query(\n `SELECT id FROM job_queue WHERE idempotency_key = $1`,\n [idempotencyKey],\n );\n if (existing.rows.length > 0) {\n log(\n `Job with idempotency key \"${idempotencyKey}\" already exists (id: ${existing.rows[0].id}), returning existing job`,\n );\n return existing.rows[0].id;\n }\n throw new Error(\n `Failed to insert job and could not find existing job with idempotency key \"${idempotencyKey}\"`,\n );\n }\n\n const jobId = result.rows[0].id;\n log(\n `Added job ${jobId}: payload ${JSON.stringify(payload)}, ${runAt ? `runAt ${runAt.toISOString()}, ` : ''}priority ${priority}, maxAttempts ${maxAttempts}, jobType ${jobType}, tags ${JSON.stringify(tags)}${idempotencyKey ? `, idempotencyKey \"${idempotencyKey}\"` : ''}`,\n );\n await this.recordJobEvent(jobId, JobEventType.Added, {\n jobType,\n payload,\n tags,\n idempotencyKey,\n });\n return jobId;\n } catch (error) {\n log(`Error adding job: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJob<PayloadMap, T extends JobType<PayloadMap>>(\n id: number,\n ): Promise<JobRecord<PayloadMap, T> | null> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue WHERE id = $1`,\n [id],\n );\n\n if (result.rows.length === 0) {\n log(`Job ${id} not found`);\n return null;\n }\n\n log(`Found job ${id}`);\n const job = result.rows[0] as JobRecord<PayloadMap, T>;\n return {\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n };\n } catch (error) {\n log(`Error getting job ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobsByStatus<PayloadMap, T extends JobType<PayloadMap>>(\n status: string,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue WHERE status = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3`,\n [status, limit, offset],\n );\n log(`Found ${result.rows.length} jobs by status ${status}`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(`Error getting jobs by status ${status}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getAllJobs<PayloadMap, T extends JobType<PayloadMap>>(\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue ORDER BY created_at DESC LIMIT $1 OFFSET $2`,\n [limit, offset],\n );\n log(`Found ${result.rows.length} jobs (all)`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n }));\n } catch (error) {\n log(`Error getting all jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobs<PayloadMap, T extends JobType<PayloadMap>>(\n filters?: JobFilters,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n let query = `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue`;\n const params: any[] = [];\n const where: string[] = [];\n let paramIdx = 1;\n if (filters) {\n if (filters.jobType) {\n where.push(`job_type = $${paramIdx++}`);\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n where.push(`priority = $${paramIdx++}`);\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n where.push(`run_at = $${paramIdx++}`);\n params.push(filters.runAt);\n } else if (\n typeof filters.runAt === 'object' &&\n (filters.runAt.gt !== undefined ||\n filters.runAt.gte !== undefined ||\n filters.runAt.lt !== undefined ||\n filters.runAt.lte !== undefined ||\n filters.runAt.eq !== undefined)\n ) {\n const ops = filters.runAt as {\n gt?: Date;\n gte?: Date;\n lt?: Date;\n lte?: Date;\n eq?: Date;\n };\n if (ops.gt) {\n where.push(`run_at > $${paramIdx++}`);\n params.push(ops.gt);\n }\n if (ops.gte) {\n where.push(`run_at >= $${paramIdx++}`);\n params.push(ops.gte);\n }\n if (ops.lt) {\n where.push(`run_at < $${paramIdx++}`);\n params.push(ops.lt);\n }\n if (ops.lte) {\n where.push(`run_at <= $${paramIdx++}`);\n params.push(ops.lte);\n }\n if (ops.eq) {\n where.push(`run_at = $${paramIdx++}`);\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n where.push(`tags = $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'all':\n where.push(`tags @> $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'any':\n where.push(`tags && $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'none':\n where.push(`NOT (tags && $${paramIdx++})`);\n params.push(tagValues);\n break;\n default:\n where.push(`tags @> $${paramIdx++}`);\n params.push(tagValues);\n }\n }\n // Keyset pagination: use cursor (id < cursor) instead of OFFSET\n if (filters.cursor !== undefined) {\n where.push(`id < $${paramIdx++}`);\n params.push(filters.cursor);\n }\n }\n if (where.length > 0) {\n query += ` WHERE ${where.join(' AND ')}`;\n }\n paramIdx = params.length + 1;\n // Use ORDER BY id DESC for consistent keyset pagination\n query += ` ORDER BY id DESC LIMIT $${paramIdx++}`;\n // Only apply OFFSET when cursor is not used\n if (!filters?.cursor) {\n query += ` OFFSET $${paramIdx}`;\n params.push(limit, offset);\n } else {\n params.push(limit);\n }\n const result = await client.query(query, params);\n log(`Found ${result.rows.length} jobs`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(`Error getting jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobsByTags<PayloadMap, T extends JobType<PayloadMap>>(\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n let query = `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress\n FROM job_queue`;\n let params: any[] = [];\n switch (mode) {\n case 'exact':\n query += ' WHERE tags = $1';\n params = [tags];\n break;\n case 'all':\n query += ' WHERE tags @> $1';\n params = [tags];\n break;\n case 'any':\n query += ' WHERE tags && $1';\n params = [tags];\n break;\n case 'none':\n query += ' WHERE NOT (tags && $1)';\n params = [tags];\n break;\n default:\n query += ' WHERE tags @> $1';\n params = [tags];\n }\n query += ' ORDER BY created_at DESC LIMIT $2 OFFSET $3';\n params.push(limit, offset);\n const result = await client.query(query, params);\n log(\n `Found ${result.rows.length} jobs by tags ${JSON.stringify(tags)} (mode: ${mode})`,\n );\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(\n `Error getting jobs by tags ${JSON.stringify(tags)} (mode: ${mode}): ${error}`,\n );\n throw error;\n } finally {\n client.release();\n }\n }\n\n // ── Processing lifecycle ──────────────────────────────────────────────\n\n async getNextBatch<PayloadMap, T extends JobType<PayloadMap>>(\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n let jobTypeFilter = '';\n const params: any[] = [workerId, batchSize];\n if (jobType) {\n if (Array.isArray(jobType)) {\n jobTypeFilter = ` AND job_type = ANY($3)`;\n params.push(jobType);\n } else {\n jobTypeFilter = ` AND job_type = $3`;\n params.push(jobType);\n }\n }\n\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'processing', \n locked_at = NOW(), \n locked_by = $1,\n attempts = CASE WHEN status = 'waiting' THEN attempts ELSE attempts + 1 END,\n updated_at = NOW(),\n pending_reason = NULL,\n started_at = COALESCE(started_at, NOW()),\n last_retried_at = CASE WHEN status != 'waiting' AND attempts > 0 THEN NOW() ELSE last_retried_at END,\n wait_until = NULL\n WHERE id IN (\n SELECT id FROM job_queue\n WHERE (\n (\n (status = 'pending' OR (status = 'failed' AND next_attempt_at <= NOW()))\n AND (attempts < max_attempts)\n AND run_at <= NOW()\n )\n OR (\n status = 'waiting'\n AND wait_until IS NOT NULL\n AND wait_until <= NOW()\n AND wait_token_id IS NULL\n )\n )\n ${jobTypeFilter}\n ORDER BY priority DESC, created_at ASC\n LIMIT $2\n FOR UPDATE SKIP LOCKED\n )\n RETURNING id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress\n `,\n params,\n );\n\n log(`Found ${result.rows.length} jobs to process`);\n await client.query('COMMIT');\n\n // Batch-insert processing events in a single query\n if (result.rows.length > 0) {\n await this.recordJobEventsBatch(\n result.rows.map((row) => ({\n jobId: row.id,\n eventType: JobEventType.Processing,\n })),\n );\n }\n\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n }));\n } catch (error) {\n log(`Error getting next batch: ${error}`);\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n }\n\n async completeJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'completed', updated_at = NOW(), completed_at = NOW(),\n step_data = NULL, wait_until = NULL, wait_token_id = NULL\n WHERE id = $1 AND status = 'processing'\n `,\n [jobId],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be completed (not in processing state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Completed);\n log(`Completed job ${jobId}`);\n } catch (error) {\n log(`Error completing job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async failJob(\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'failed', \n updated_at = NOW(),\n next_attempt_at = CASE \n WHEN attempts < max_attempts THEN NOW() + (POWER(2, attempts) * INTERVAL '1 minute')\n ELSE NULL\n END,\n error_history = COALESCE(error_history, '[]'::jsonb) || $2::jsonb,\n failure_reason = $3,\n last_failed_at = NOW()\n WHERE id = $1 AND status IN ('processing', 'pending')\n `,\n [\n jobId,\n JSON.stringify([\n {\n message: error.message || String(error),\n timestamp: new Date().toISOString(),\n },\n ]),\n failureReason ?? null,\n ],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be failed (not in processing/pending state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Failed, {\n message: error.message || String(error),\n failureReason,\n });\n log(`Failed job ${jobId}`);\n } catch (err) {\n log(`Error failing job ${jobId}: ${err}`);\n throw err;\n } finally {\n client.release();\n }\n }\n\n async prolongJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `\n UPDATE job_queue\n SET locked_at = NOW(), updated_at = NOW()\n WHERE id = $1 AND status = 'processing'\n `,\n [jobId],\n );\n await this.recordJobEvent(jobId, JobEventType.Prolonged);\n log(`Prolonged job ${jobId}`);\n } catch (error) {\n log(`Error prolonging job ${jobId}: ${error}`);\n // Do not throw -- prolong is best-effort\n } finally {\n client.release();\n }\n }\n\n // ── Progress ──────────────────────────────────────────────────────────\n\n async updateProgress(jobId: number, progress: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `UPDATE job_queue SET progress = $2, updated_at = NOW() WHERE id = $1`,\n [jobId, progress],\n );\n log(`Updated progress for job ${jobId}: ${progress}%`);\n } catch (error) {\n log(`Error updating progress for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n } finally {\n client.release();\n }\n }\n\n // ── Job management ────────────────────────────────────────────────────\n\n async retryJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'pending', \n updated_at = NOW(),\n locked_at = NULL,\n locked_by = NULL,\n next_attempt_at = NOW(),\n last_retried_at = NOW()\n WHERE id = $1 AND status IN ('failed', 'processing')\n `,\n [jobId],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be retried (not in failed/processing state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Retried);\n log(`Retried job ${jobId}`);\n } catch (error) {\n log(`Error retrying job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cancelJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `\n UPDATE job_queue\n SET status = 'cancelled', updated_at = NOW(), last_cancelled_at = NOW(),\n wait_until = NULL, wait_token_id = NULL\n WHERE id = $1 AND status IN ('pending', 'waiting')\n `,\n [jobId],\n );\n await this.recordJobEvent(jobId, JobEventType.Cancelled);\n log(`Cancelled job ${jobId}`);\n } catch (error) {\n log(`Error cancelling job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cancelAllUpcomingJobs(filters?: JobFilters): Promise<number> {\n const client = await this.pool.connect();\n try {\n let query = `\n UPDATE job_queue\n SET status = 'cancelled', updated_at = NOW()\n WHERE status = 'pending'`;\n const params: any[] = [];\n let paramIdx = 1;\n if (filters) {\n if (filters.jobType) {\n query += ` AND job_type = $${paramIdx++}`;\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n query += ` AND priority = $${paramIdx++}`;\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(filters.runAt);\n } else if (typeof filters.runAt === 'object') {\n const ops = filters.runAt;\n if (ops.gt) {\n query += ` AND run_at > $${paramIdx++}`;\n params.push(ops.gt);\n }\n if (ops.gte) {\n query += ` AND run_at >= $${paramIdx++}`;\n params.push(ops.gte);\n }\n if (ops.lt) {\n query += ` AND run_at < $${paramIdx++}`;\n params.push(ops.lt);\n }\n if (ops.lte) {\n query += ` AND run_at <= $${paramIdx++}`;\n params.push(ops.lte);\n }\n if (ops.eq) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n query += ` AND tags = $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'all':\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'any':\n query += ` AND tags && $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'none':\n query += ` AND NOT (tags && $${paramIdx++})`;\n params.push(tagValues);\n break;\n default:\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n }\n }\n }\n query += '\\nRETURNING id';\n const result = await client.query(query, params);\n log(`Cancelled ${result.rowCount} jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cancelling upcoming jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async editJob(jobId: number, updates: JobUpdates): Promise<void> {\n const client = await this.pool.connect();\n try {\n const updateFields: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n\n if (updates.payload !== undefined) {\n updateFields.push(`payload = $${paramIdx++}`);\n params.push(updates.payload);\n }\n if (updates.maxAttempts !== undefined) {\n updateFields.push(`max_attempts = $${paramIdx++}`);\n params.push(updates.maxAttempts);\n }\n if (updates.priority !== undefined) {\n updateFields.push(`priority = $${paramIdx++}`);\n params.push(updates.priority);\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n updateFields.push(`run_at = NOW()`);\n } else {\n updateFields.push(`run_at = $${paramIdx++}`);\n params.push(updates.runAt);\n }\n }\n if (updates.timeoutMs !== undefined) {\n updateFields.push(`timeout_ms = $${paramIdx++}`);\n params.push(updates.timeoutMs ?? null);\n }\n if (updates.tags !== undefined) {\n updateFields.push(`tags = $${paramIdx++}`);\n params.push(updates.tags ?? null);\n }\n\n if (updateFields.length === 0) {\n log(`No fields to update for job ${jobId}`);\n return;\n }\n\n updateFields.push(`updated_at = NOW()`);\n params.push(jobId);\n\n const query = `\n UPDATE job_queue\n SET ${updateFields.join(', ')}\n WHERE id = $${paramIdx} AND status = 'pending'\n `;\n\n await client.query(query, params);\n\n const metadata: any = {};\n if (updates.payload !== undefined) metadata.payload = updates.payload;\n if (updates.maxAttempts !== undefined)\n metadata.maxAttempts = updates.maxAttempts;\n if (updates.priority !== undefined) metadata.priority = updates.priority;\n if (updates.runAt !== undefined) metadata.runAt = updates.runAt;\n if (updates.timeoutMs !== undefined)\n metadata.timeoutMs = updates.timeoutMs;\n if (updates.tags !== undefined) metadata.tags = updates.tags;\n\n await this.recordJobEvent(jobId, JobEventType.Edited, metadata);\n log(`Edited job ${jobId}: ${JSON.stringify(metadata)}`);\n } catch (error) {\n log(`Error editing job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async editAllPendingJobs(\n filters: JobFilters | undefined = undefined,\n updates: JobUpdates,\n ): Promise<number> {\n const client = await this.pool.connect();\n try {\n const updateFields: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n\n if (updates.payload !== undefined) {\n updateFields.push(`payload = $${paramIdx++}`);\n params.push(updates.payload);\n }\n if (updates.maxAttempts !== undefined) {\n updateFields.push(`max_attempts = $${paramIdx++}`);\n params.push(updates.maxAttempts);\n }\n if (updates.priority !== undefined) {\n updateFields.push(`priority = $${paramIdx++}`);\n params.push(updates.priority);\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n updateFields.push(`run_at = NOW()`);\n } else {\n updateFields.push(`run_at = $${paramIdx++}`);\n params.push(updates.runAt);\n }\n }\n if (updates.timeoutMs !== undefined) {\n updateFields.push(`timeout_ms = $${paramIdx++}`);\n params.push(updates.timeoutMs ?? null);\n }\n if (updates.tags !== undefined) {\n updateFields.push(`tags = $${paramIdx++}`);\n params.push(updates.tags ?? null);\n }\n\n if (updateFields.length === 0) {\n log(`No fields to update for batch edit`);\n return 0;\n }\n\n updateFields.push(`updated_at = NOW()`);\n\n let query = `\n UPDATE job_queue\n SET ${updateFields.join(', ')}\n WHERE status = 'pending'`;\n\n if (filters) {\n if (filters.jobType) {\n query += ` AND job_type = $${paramIdx++}`;\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n query += ` AND priority = $${paramIdx++}`;\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(filters.runAt);\n } else if (typeof filters.runAt === 'object') {\n const ops = filters.runAt;\n if (ops.gt) {\n query += ` AND run_at > $${paramIdx++}`;\n params.push(ops.gt);\n }\n if (ops.gte) {\n query += ` AND run_at >= $${paramIdx++}`;\n params.push(ops.gte);\n }\n if (ops.lt) {\n query += ` AND run_at < $${paramIdx++}`;\n params.push(ops.lt);\n }\n if (ops.lte) {\n query += ` AND run_at <= $${paramIdx++}`;\n params.push(ops.lte);\n }\n if (ops.eq) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n query += ` AND tags = $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'all':\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'any':\n query += ` AND tags && $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'none':\n query += ` AND NOT (tags && $${paramIdx++})`;\n params.push(tagValues);\n break;\n default:\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n }\n }\n }\n query += '\\nRETURNING id';\n\n const result = await client.query(query, params);\n const editedCount = result.rowCount || 0;\n\n const metadata: any = {};\n if (updates.payload !== undefined) metadata.payload = updates.payload;\n if (updates.maxAttempts !== undefined)\n metadata.maxAttempts = updates.maxAttempts;\n if (updates.priority !== undefined) metadata.priority = updates.priority;\n if (updates.runAt !== undefined) metadata.runAt = updates.runAt;\n if (updates.timeoutMs !== undefined)\n metadata.timeoutMs = updates.timeoutMs;\n if (updates.tags !== undefined) metadata.tags = updates.tags;\n\n for (const row of result.rows) {\n await this.recordJobEvent(row.id, JobEventType.Edited, metadata);\n }\n\n log(`Edited ${editedCount} pending jobs: ${JSON.stringify(metadata)}`);\n return editedCount;\n } catch (error) {\n log(`Error editing pending jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cleanupOldJobs(daysToKeep = 30): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n DELETE FROM job_queue\n WHERE status = 'completed'\n AND updated_at < NOW() - INTERVAL '1 day' * $1::int\n RETURNING id\n `,\n [daysToKeep],\n );\n log(`Deleted ${result.rowCount} old jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cleaning up old jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cleanupOldJobEvents(daysToKeep = 30): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n DELETE FROM job_events\n WHERE created_at < NOW() - INTERVAL '1 day' * $1::int\n RETURNING id\n `,\n [daysToKeep],\n );\n log(`Deleted ${result.rowCount} old job events`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cleaning up old job events: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async reclaimStuckJobs(maxProcessingTimeMinutes = 10): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'pending', locked_at = NULL, locked_by = NULL, updated_at = NOW()\n WHERE status = 'processing'\n AND locked_at < NOW() - GREATEST(\n INTERVAL '1 minute' * $1::int,\n INTERVAL '1 millisecond' * COALESCE(timeout_ms, 0)\n )\n RETURNING id\n `,\n [maxProcessingTimeMinutes],\n );\n log(`Reclaimed ${result.rowCount} stuck jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error reclaiming stuck jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n // ── Internal helpers ──────────────────────────────────────────────────\n\n /**\n * Batch-insert multiple job events in a single query.\n * More efficient than individual recordJobEvent calls.\n */\n private async recordJobEventsBatch(\n events: { jobId: number; eventType: JobEventType; metadata?: any }[],\n ): Promise<void> {\n if (events.length === 0) return;\n const client = await this.pool.connect();\n try {\n const values: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n for (const event of events) {\n values.push(`($${paramIdx++}, $${paramIdx++}, $${paramIdx++})`);\n params.push(\n event.jobId,\n event.eventType,\n event.metadata ? JSON.stringify(event.metadata) : null,\n );\n }\n await client.query(\n `INSERT INTO job_events (job_id, event_type, metadata) VALUES ${values.join(', ')}`,\n params,\n );\n } catch (error) {\n log(`Error recording batch job events: ${error}`);\n // Do not throw, to avoid interfering with main job logic\n } finally {\n client.release();\n }\n }\n\n async setPendingReasonForUnpickedJobs(\n reason: string,\n jobType?: string | string[],\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n let jobTypeFilter = '';\n const params: any[] = [reason];\n if (jobType) {\n if (Array.isArray(jobType)) {\n jobTypeFilter = ` AND job_type = ANY($2)`;\n params.push(jobType);\n } else {\n jobTypeFilter = ` AND job_type = $2`;\n params.push(jobType);\n }\n }\n await client.query(\n `UPDATE job_queue SET pending_reason = $1 WHERE status = 'pending'${jobTypeFilter}`,\n params,\n );\n } finally {\n client.release();\n }\n }\n}\n","/**\n * Backward-compatible re-exports.\n * All SQL logic has moved to backends/postgres.ts (PostgresBackend class).\n * These functions delegate to a temporary PostgresBackend instance so that\n * any existing internal callers continue to work.\n *\n * Wait-related functions (waitJob, updateStepData, createWaitpoint, etc.)\n * are PostgreSQL-only and use direct SQL queries.\n */\nimport { Pool } from 'pg';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n WaitpointRecord,\n} from './types.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport { randomUUID } from 'crypto';\nimport { log } from './log-context.js';\n\n/* Thin wrappers — every function creates a lightweight backend wrapper\n around the given pool and forwards the call. The class itself holds\n no mutable state so this is safe and cheap. */\n\nexport const recordJobEvent = async (\n pool: Pool,\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n): Promise<void> =>\n new PostgresBackend(pool).recordJobEvent(jobId, eventType, metadata);\n\nexport const addJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n job: JobOptions<PayloadMap, T>,\n): Promise<number> => new PostgresBackend(pool).addJob(job);\n\nexport const getJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n id: number,\n): Promise<JobRecord<PayloadMap, T> | null> =>\n new PostgresBackend(pool).getJob<PayloadMap, T>(id);\n\nexport const getJobsByStatus = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n status: string,\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobsByStatus<PayloadMap, T>(\n status,\n limit,\n offset,\n );\n\nexport const getNextBatch = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getNextBatch<PayloadMap, T>(\n workerId,\n batchSize,\n jobType,\n );\n\nexport const completeJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).completeJob(jobId);\n\nexport const prolongJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).prolongJob(jobId);\n\nexport const failJob = async (\n pool: Pool,\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n): Promise<void> =>\n new PostgresBackend(pool).failJob(jobId, error, failureReason);\n\nexport const retryJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).retryJob(jobId);\n\nexport const cleanupOldJobs = async (\n pool: Pool,\n daysToKeep = 30,\n): Promise<number> => new PostgresBackend(pool).cleanupOldJobs(daysToKeep);\n\nexport const cancelJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).cancelJob(jobId);\n\nexport const editJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n jobId: number,\n updates: {\n payload?: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n timeoutMs?: number | null;\n tags?: string[] | null;\n },\n): Promise<void> => new PostgresBackend(pool).editJob(jobId, updates);\n\nexport const editAllPendingJobs = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }\n | undefined,\n updates: {\n payload?: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n timeoutMs?: number;\n tags?: string[];\n },\n): Promise<number> =>\n new PostgresBackend(pool).editAllPendingJobs(filters, updates);\n\nexport const cancelAllUpcomingJobs = async (\n pool: Pool,\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n },\n): Promise<number> => new PostgresBackend(pool).cancelAllUpcomingJobs(filters);\n\nexport const getAllJobs = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getAllJobs<PayloadMap, T>(limit, offset);\n\nexport const setPendingReasonForUnpickedJobs = async (\n pool: Pool,\n reason: string,\n jobType?: string | string[],\n): Promise<void> =>\n new PostgresBackend(pool).setPendingReasonForUnpickedJobs(reason, jobType);\n\nexport const reclaimStuckJobs = async (\n pool: Pool,\n maxProcessingTimeMinutes = 10,\n): Promise<number> =>\n new PostgresBackend(pool).reclaimStuckJobs(maxProcessingTimeMinutes);\n\nexport const getJobEvents = async (\n pool: Pool,\n jobId: number,\n): Promise<JobEvent[]> => new PostgresBackend(pool).getJobEvents(jobId);\n\nexport const getJobsByTags = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobsByTags<PayloadMap, T>(\n tags,\n mode,\n limit,\n offset,\n );\n\nexport const getJobs = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n },\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobs<PayloadMap, T>(filters, limit, offset);\n\n// ── Progress ──────────────────────────────────────────────────────────────────\n\nexport const updateProgress = async (\n pool: Pool,\n jobId: number,\n progress: number,\n): Promise<void> => new PostgresBackend(pool).updateProgress(jobId, progress);\n\n// ── Wait support functions (PostgreSQL-only) ─────────────────────────────────\n\n/**\n * Transition a job to 'waiting' status with wait_until and/or wait_token_id.\n * Saves step_data so the handler can resume from where it left off.\n */\nexport const waitJob = async (\n pool: Pool,\n jobId: number,\n options: {\n waitUntil?: Date;\n waitTokenId?: string;\n stepData: Record<string, any>;\n },\n): Promise<void> => {\n const client = await pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'waiting',\n wait_until = $2,\n wait_token_id = $3,\n step_data = $4,\n locked_at = NULL,\n locked_by = NULL,\n updated_at = NOW()\n WHERE id = $1 AND status = 'processing'\n `,\n [\n jobId,\n options.waitUntil ?? null,\n options.waitTokenId ?? null,\n JSON.stringify(options.stepData),\n ],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be set to waiting (may have been reclaimed or is no longer processing)`,\n );\n return;\n }\n await recordJobEvent(pool, jobId, JobEventType.Waiting, {\n waitUntil: options.waitUntil?.toISOString() ?? null,\n waitTokenId: options.waitTokenId ?? null,\n });\n log(`Job ${jobId} set to waiting`);\n } catch (error) {\n log(`Error setting job ${jobId} to waiting: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Update step_data for a job. Called after each ctx.run() step completes\n * to persist intermediate progress.\n */\nexport const updateStepData = async (\n pool: Pool,\n jobId: number,\n stepData: Record<string, any>,\n): Promise<void> => {\n const client = await pool.connect();\n try {\n await client.query(\n `UPDATE job_queue SET step_data = $2, updated_at = NOW() WHERE id = $1`,\n [jobId, JSON.stringify(stepData)],\n );\n } catch (error) {\n log(`Error updating step_data for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n } finally {\n client.release();\n }\n};\n\n/**\n * Parse a timeout string like '10m', '1h', '24h', '7d' into milliseconds.\n */\n/**\n * Maximum allowed timeout in milliseconds (~365 days).\n * Prevents overflow to Infinity when computing Date offsets.\n */\nconst MAX_TIMEOUT_MS = 365 * 24 * 60 * 60 * 1000;\n\nfunction parseTimeoutString(timeout: string): number {\n const match = timeout.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(\n `Invalid timeout format: \"${timeout}\". Expected format like \"10m\", \"1h\", \"24h\", \"7d\".`,\n );\n }\n const value = parseInt(match[1], 10);\n const unit = match[2];\n let ms: number;\n switch (unit) {\n case 's':\n ms = value * 1000;\n break;\n case 'm':\n ms = value * 60 * 1000;\n break;\n case 'h':\n ms = value * 60 * 60 * 1000;\n break;\n case 'd':\n ms = value * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new Error(`Unknown timeout unit: \"${unit}\"`);\n }\n if (!Number.isFinite(ms) || ms > MAX_TIMEOUT_MS) {\n throw new Error(\n `Timeout value \"${timeout}\" is too large. Maximum allowed is 365 days.`,\n );\n }\n return ms;\n}\n\n/**\n * Create a waitpoint token in the database.\n * The token can be used to pause a job until an external signal completes it.\n *\n * @param pool - The database pool\n * @param jobId - The job ID to associate with the token (null if created outside a handler)\n * @param options - Optional timeout and tags\n * @returns The created waitpoint token\n */\nexport const createWaitpoint = async (\n pool: Pool,\n jobId: number | null,\n options?: { timeout?: string; tags?: string[] },\n): Promise<{ id: string }> => {\n const client = await pool.connect();\n try {\n const id = `wp_${randomUUID()}`;\n let timeoutAt: Date | null = null;\n\n if (options?.timeout) {\n const ms = parseTimeoutString(options.timeout);\n timeoutAt = new Date(Date.now() + ms);\n }\n\n await client.query(\n `INSERT INTO waitpoints (id, job_id, status, timeout_at, tags) VALUES ($1, $2, 'waiting', $3, $4)`,\n [id, jobId, timeoutAt, options?.tags ?? null],\n );\n\n log(`Created waitpoint ${id} for job ${jobId}`);\n return { id };\n } catch (error) {\n log(`Error creating waitpoint: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Complete a waitpoint token, optionally providing output data.\n * This also moves the associated job from 'waiting' back to 'pending' so\n * it gets picked up by the polling loop.\n */\nexport const completeWaitpoint = async (\n pool: Pool,\n tokenId: string,\n data?: any,\n): Promise<void> => {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n\n // Update the waitpoint\n const wpResult = await client.query(\n `UPDATE waitpoints SET status = 'completed', output = $2, completed_at = NOW()\n WHERE id = $1 AND status = 'waiting'\n RETURNING job_id`,\n [tokenId, data != null ? JSON.stringify(data) : null],\n );\n\n if (wpResult.rows.length === 0) {\n await client.query('ROLLBACK');\n log(`Waitpoint ${tokenId} not found or already completed`);\n return;\n }\n\n const jobId = wpResult.rows[0].job_id;\n\n // Move the associated job back to 'pending' so it gets picked up\n if (jobId != null) {\n await client.query(\n `UPDATE job_queue\n SET status = 'pending', wait_token_id = NULL, wait_until = NULL, updated_at = NOW()\n WHERE id = $1 AND status = 'waiting'`,\n [jobId],\n );\n }\n\n await client.query('COMMIT');\n log(`Completed waitpoint ${tokenId} for job ${jobId}`);\n } catch (error) {\n await client.query('ROLLBACK');\n log(`Error completing waitpoint ${tokenId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Retrieve a waitpoint token by its ID.\n */\nexport const getWaitpoint = async (\n pool: Pool,\n tokenId: string,\n): Promise<WaitpointRecord | null> => {\n const client = await pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_id AS \"jobId\", status, output, timeout_at AS \"timeoutAt\", created_at AS \"createdAt\", completed_at AS \"completedAt\", tags FROM waitpoints WHERE id = $1`,\n [tokenId],\n );\n if (result.rows.length === 0) return null;\n return result.rows[0] as WaitpointRecord;\n } catch (error) {\n log(`Error getting waitpoint ${tokenId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Expire timed-out waitpoint tokens and move their associated jobs back to 'pending'.\n * Should be called periodically (e.g., alongside reclaimStuckJobs).\n */\nexport const expireTimedOutWaitpoints = async (pool: Pool): Promise<number> => {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n\n // Find and expire timed-out waitpoints\n const result = await client.query(\n `UPDATE waitpoints\n SET status = 'timed_out'\n WHERE status = 'waiting' AND timeout_at IS NOT NULL AND timeout_at <= NOW()\n RETURNING id, job_id`,\n );\n\n // Move associated jobs back to 'pending'\n for (const row of result.rows) {\n if (row.job_id != null) {\n await client.query(\n `UPDATE job_queue\n SET status = 'pending', wait_token_id = NULL, wait_until = NULL, updated_at = NOW()\n WHERE id = $1 AND status = 'waiting'`,\n [row.job_id],\n );\n }\n }\n\n await client.query('COMMIT');\n const count = result.rowCount || 0;\n if (count > 0) {\n log(`Expired ${count} timed-out waitpoints`);\n }\n return count;\n } catch (error) {\n await client.query('ROLLBACK');\n log(`Error expiring timed-out waitpoints: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n","import { Worker } from 'worker_threads';\nimport { Pool } from 'pg';\nimport {\n JobRecord,\n ProcessorOptions,\n Processor,\n JobHandler,\n JobType,\n FailureReason,\n JobHandlers,\n JobContext,\n OnTimeoutCallback,\n WaitSignal,\n WaitDuration,\n WaitTokenResult,\n} from './types.js';\nimport { QueueBackend } from './backend.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport {\n waitJob,\n updateStepData,\n createWaitpoint,\n getWaitpoint,\n} from './queue.js';\nimport { log, setLogContext } from './log-context.js';\n\n/**\n * Try to extract the underlying pg Pool from a QueueBackend.\n * Returns null for non-PostgreSQL backends.\n */\nfunction tryExtractPool(backend: QueueBackend): Pool | null {\n if (backend instanceof PostgresBackend) {\n return backend.getPool();\n }\n return null;\n}\n\n/**\n * Build a JobContext without wait support (for non-PostgreSQL backends).\n * prolong/onTimeout work normally; wait-related methods throw helpful errors.\n */\nfunction buildBasicContext(\n backend: QueueBackend,\n jobId: number,\n baseCtx: {\n prolong: JobContext['prolong'];\n onTimeout: JobContext['onTimeout'];\n },\n): JobContext {\n const waitError = () =>\n new Error(\n 'Wait features (waitFor, waitUntil, createToken, waitForToken, ctx.run) are currently only supported with the PostgreSQL backend.',\n );\n return {\n prolong: baseCtx.prolong,\n onTimeout: baseCtx.onTimeout,\n run: async <T>(_stepName: string, fn: () => Promise<T>): Promise<T> => {\n // Without PostgreSQL, just execute the function directly (no persistence)\n return fn();\n },\n waitFor: async () => {\n throw waitError();\n },\n waitUntil: async () => {\n throw waitError();\n },\n createToken: async () => {\n throw waitError();\n },\n waitForToken: async () => {\n throw waitError();\n },\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n}\n\n/**\n * Validates that a handler can be serialized for worker thread execution.\n * Throws an error with helpful message if serialization fails.\n */\nfunction validateHandlerSerializable<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(handler: JobHandler<PayloadMap, T>, jobType: string): void {\n try {\n const handlerString = handler.toString();\n\n // Check for common patterns that indicate non-serializable handlers\n // 1. Arrow functions that capture 'this' (indicated by 'this' in the function body but not in parameters)\n if (\n handlerString.includes('this.') &&\n !handlerString.match(/\\([^)]*this[^)]*\\)/)\n ) {\n throw new Error(\n `Handler for job type \"${jobType}\" uses 'this' context which cannot be serialized. ` +\n `Use a regular function or avoid 'this' references when forceKillOnTimeout is enabled.`,\n );\n }\n\n // 2. Check if handler string looks like it might have closures\n // This is a heuristic - we can't perfectly detect closures, but we can warn about common patterns\n if (handlerString.includes('[native code]')) {\n throw new Error(\n `Handler for job type \"${jobType}\" contains native code which cannot be serialized. ` +\n `Ensure your handler is a plain function when forceKillOnTimeout is enabled.`,\n );\n }\n\n // 3. Try to create a function from the string to validate it's parseable\n // This will catch syntax errors early\n try {\n new Function('return ' + handlerString);\n } catch (parseError) {\n throw new Error(\n `Handler for job type \"${jobType}\" cannot be serialized: ${parseError instanceof Error ? parseError.message : String(parseError)}. ` +\n `When using forceKillOnTimeout, handlers must be serializable functions without closures over external variables.`,\n );\n }\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Failed to validate handler serialization for job type \"${jobType}\": ${String(error)}`,\n );\n }\n}\n\n/**\n * Run a handler in a worker thread for force-kill capability.\n *\n * **IMPORTANT**: The handler must be serializable for this to work. This means:\n * - The handler should be a standalone function or arrow function\n * - It should not capture variables from outer scopes (closures) that reference external dependencies\n * - It should not use 'this' context unless it's a bound method\n * - All dependencies must be importable in the worker thread context\n *\n * If your handler doesn't meet these requirements, use the default graceful shutdown\n * (forceKillOnTimeout: false) and ensure your handler checks signal.aborted.\n *\n * @throws {Error} If the handler cannot be serialized\n */\nasync function runHandlerInWorker<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n payload: PayloadMap[T],\n timeoutMs: number,\n jobType: string,\n): Promise<void> {\n // Validate handler can be serialized before attempting to run in worker\n validateHandlerSerializable(handler, jobType);\n\n return new Promise((resolve, reject) => {\n // Use inline worker code for better compatibility\n // Note: This requires the handler to be serializable (no closures with external dependencies)\n // Wrap in IIFE to allow return statements\n const workerCode = `\n (function() {\n const { parentPort, workerData } = require('worker_threads');\n const { handlerCode, payload, timeoutMs } = workerData;\n \n // Create an AbortController for the handler\n const controller = new AbortController();\n const signal = controller.signal;\n \n // Set up timeout\n const timeoutId = setTimeout(() => {\n controller.abort();\n parentPort.postMessage({ type: 'timeout' });\n }, timeoutMs);\n \n try {\n // Execute the handler\n // Note: This uses Function constructor which requires the handler to be serializable.\n // The handler should be validated before reaching this point.\n let handlerFn;\n try {\n // Wrap handlerCode in parentheses to ensure it's treated as an expression\n // This handles both arrow functions and regular functions\n const wrappedCode = handlerCode.trim().startsWith('async') || handlerCode.trim().startsWith('function')\n ? handlerCode\n : '(' + handlerCode + ')';\n handlerFn = new Function('return ' + wrappedCode)();\n } catch (parseError) {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: 'Handler cannot be deserialized in worker thread. ' +\n 'Ensure your handler is a standalone function without closures over external variables. ' +\n 'Original error: ' + (parseError instanceof Error ? parseError.message : String(parseError)),\n stack: parseError instanceof Error ? parseError.stack : undefined,\n name: 'SerializationError',\n },\n });\n return;\n }\n \n // Ensure handlerFn is actually a function\n if (typeof handlerFn !== 'function') {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: 'Handler deserialization did not produce a function. ' +\n 'Ensure your handler is a valid function when forceKillOnTimeout is enabled.',\n name: 'SerializationError',\n },\n });\n return;\n }\n \n handlerFn(payload, signal)\n .then(() => {\n clearTimeout(timeoutId);\n parentPort.postMessage({ type: 'success' });\n })\n .catch((error) => {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: error.message,\n stack: error.stack,\n name: error.name,\n },\n });\n });\n } catch (error) {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: error.message,\n stack: error.stack,\n name: error.name,\n },\n });\n }\n })();\n `;\n\n const worker = new Worker(workerCode, {\n eval: true,\n workerData: {\n handlerCode: handler.toString(),\n payload,\n timeoutMs,\n },\n });\n\n let resolved = false;\n\n worker.on('message', (message: { type: string; error?: any }) => {\n if (resolved) return;\n resolved = true;\n\n if (message.type === 'success') {\n resolve();\n } else if (message.type === 'timeout') {\n const timeoutError = new Error(\n `Job timed out after ${timeoutMs} ms and was forcefully terminated`,\n );\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n reject(timeoutError);\n } else if (message.type === 'error') {\n const error = new Error(message.error.message);\n error.stack = message.error.stack;\n error.name = message.error.name;\n reject(error);\n }\n });\n\n worker.on('error', (error) => {\n if (resolved) return;\n resolved = true;\n reject(error);\n });\n\n worker.on('exit', (code) => {\n if (resolved) return;\n if (code !== 0) {\n resolved = true;\n reject(new Error(`Worker stopped with exit code ${code}`));\n }\n });\n\n // Force terminate worker on timeout\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n worker\n .terminate()\n .then(() => {\n const timeoutError = new Error(\n `Job timed out after ${timeoutMs} ms and was forcefully terminated`,\n );\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n reject(timeoutError);\n })\n .catch((err) => {\n reject(err);\n });\n }\n }, timeoutMs + 100); // Small buffer to ensure timeout is handled\n });\n}\n\n/**\n * Convert a WaitDuration to a target Date.\n */\nfunction calculateWaitUntil(duration: WaitDuration): Date {\n const now = Date.now();\n let ms = 0;\n if (duration.seconds) ms += duration.seconds * 1000;\n if (duration.minutes) ms += duration.minutes * 60 * 1000;\n if (duration.hours) ms += duration.hours * 60 * 60 * 1000;\n if (duration.days) ms += duration.days * 24 * 60 * 60 * 1000;\n if (duration.weeks) ms += duration.weeks * 7 * 24 * 60 * 60 * 1000;\n if (duration.months) ms += duration.months * 30 * 24 * 60 * 60 * 1000;\n if (duration.years) ms += duration.years * 365 * 24 * 60 * 60 * 1000;\n if (ms <= 0) {\n throw new Error(\n 'waitFor duration must be positive. Provide at least one positive duration field.',\n );\n }\n return new Date(now + ms);\n}\n\n/**\n * Create a no-op JobContext for cases where prolong/onTimeout are not supported\n * (e.g. forceKillOnTimeout mode or no timeout set).\n */\nfunction createNoOpContext(\n backend: QueueBackend,\n jobId: number,\n reason: string,\n): JobContext {\n return {\n prolong: () => {\n log(`prolong() called but ignored: ${reason}`);\n },\n onTimeout: () => {\n log(`onTimeout() called but ignored: ${reason}`);\n },\n run: async <T>(_stepName: string, fn: () => Promise<T>): Promise<T> => {\n // In no-op context (forceKillOnTimeout), just execute the function directly\n return fn();\n },\n waitFor: async () => {\n throw new Error(\n `waitFor() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n waitUntil: async () => {\n throw new Error(\n `waitUntil() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n createToken: async () => {\n throw new Error(\n `createToken() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n waitForToken: async () => {\n throw new Error(\n `waitForToken() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n}\n\n/**\n * Pre-process stepData before handler re-invocation.\n * Marks pending waits as completed and fetches token outputs.\n */\nasync function resolveCompletedWaits(\n pool: Pool,\n stepData: Record<string, any>,\n): Promise<void> {\n for (const key of Object.keys(stepData)) {\n if (!key.startsWith('__wait_')) continue;\n const entry = stepData[key];\n if (!entry || typeof entry !== 'object' || entry.completed) continue;\n\n if (entry.type === 'duration' || entry.type === 'date') {\n // Time-based wait has elapsed (we got picked up, so it must have)\n stepData[key] = { ...entry, completed: true };\n } else if (entry.type === 'token' && entry.tokenId) {\n // Token-based wait -- fetch the waitpoint result\n const wp = await getWaitpoint(pool, entry.tokenId);\n if (wp && wp.status === 'completed') {\n stepData[key] = {\n ...entry,\n completed: true,\n result: { ok: true, output: wp.output },\n };\n } else if (wp && wp.status === 'timed_out') {\n stepData[key] = {\n ...entry,\n completed: true,\n result: { ok: false, error: 'Token timed out' },\n };\n }\n // If still waiting (shouldn't happen), leave as pending\n }\n }\n}\n\n/**\n * Build the extended JobContext with step tracking and wait support.\n */\nfunction buildWaitContext(\n backend: QueueBackend,\n pool: Pool,\n jobId: number,\n stepData: Record<string, any>,\n baseCtx: {\n prolong: JobContext['prolong'];\n onTimeout: JobContext['onTimeout'];\n },\n): JobContext {\n // Wait counter always starts at 0 per invocation.\n // The handler replays from the top each time, so the counter position\n // must match the order of waitFor/waitUntil/waitForToken calls in code.\n let waitCounter = 0;\n\n const ctx: JobContext = {\n prolong: baseCtx.prolong,\n onTimeout: baseCtx.onTimeout,\n\n run: async <T>(stepName: string, fn: () => Promise<T>): Promise<T> => {\n // Check if step was already completed in a previous invocation\n const cached = stepData[stepName];\n if (cached && typeof cached === 'object' && cached.__completed) {\n log(`Step \"${stepName}\" replayed from cache for job ${jobId}`);\n return cached.result as T;\n }\n\n // Execute the step\n const result = await fn();\n\n // Persist step result\n stepData[stepName] = { __completed: true, result };\n await updateStepData(pool, jobId, stepData);\n\n return result;\n },\n\n waitFor: async (duration: WaitDuration): Promise<void> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed (from a previous invocation)\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(`Wait \"${waitKey}\" already completed for job ${jobId}, skipping`);\n return;\n }\n\n // Calculate when to resume\n const waitUntilDate = calculateWaitUntil(duration);\n\n // Record this wait as pending in step data\n stepData[waitKey] = { type: 'duration', completed: false };\n\n // Throw WaitSignal to pause the handler\n throw new WaitSignal('duration', waitUntilDate, undefined, stepData);\n },\n\n waitUntil: async (date: Date): Promise<void> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(`Wait \"${waitKey}\" already completed for job ${jobId}, skipping`);\n return;\n }\n\n // Record this wait as pending\n stepData[waitKey] = { type: 'date', completed: false };\n\n // Throw WaitSignal to pause the handler\n throw new WaitSignal('date', date, undefined, stepData);\n },\n\n createToken: async (options?) => {\n const token = await createWaitpoint(pool, jobId, options);\n return token;\n },\n\n waitForToken: async <T = any>(\n tokenId: string,\n ): Promise<WaitTokenResult<T>> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(\n `Token wait \"${waitKey}\" already completed for job ${jobId}, returning cached result`,\n );\n return cached.result as WaitTokenResult<T>;\n }\n\n // Check if the token is already completed (e.g., completed while job was still processing)\n const wp = await getWaitpoint(pool, tokenId);\n if (wp && wp.status === 'completed') {\n const result: WaitTokenResult<T> = {\n ok: true,\n output: wp.output as T,\n };\n stepData[waitKey] = {\n type: 'token',\n tokenId,\n completed: true,\n result,\n };\n await updateStepData(pool, jobId, stepData);\n return result;\n }\n if (wp && wp.status === 'timed_out') {\n const result: WaitTokenResult<T> = {\n ok: false,\n error: 'Token timed out',\n };\n stepData[waitKey] = {\n type: 'token',\n tokenId,\n completed: true,\n result,\n };\n await updateStepData(pool, jobId, stepData);\n return result;\n }\n\n // Token not yet completed -- save pending state and throw WaitSignal\n stepData[waitKey] = { type: 'token', tokenId, completed: false };\n throw new WaitSignal('token', undefined, tokenId, stepData);\n },\n\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n\n return ctx;\n}\n\n/**\n * Process a single job using the provided handler map\n */\nexport async function processJobWithHandlers<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n backend: QueueBackend,\n job: JobRecord<PayloadMap, T>,\n jobHandlers: JobHandlers<PayloadMap>,\n): Promise<void> {\n const handler = jobHandlers[job.jobType];\n\n if (!handler) {\n await backend.setPendingReasonForUnpickedJobs(\n `No handler registered for job type: ${job.jobType}`,\n job.jobType,\n );\n await backend.failJob(\n job.id,\n new Error(`No handler registered for job type: ${job.jobType}`),\n FailureReason.NoHandler,\n );\n return;\n }\n\n // Load step data (may contain completed steps from previous invocations)\n const stepData: Record<string, any> = { ...(job.stepData || {}) };\n\n // Try to get pool for wait features (PostgreSQL-only)\n const pool = tryExtractPool(backend);\n\n // If resuming from a wait, resolve any pending wait entries\n const hasStepHistory = Object.keys(stepData).some((k) =>\n k.startsWith('__wait_'),\n );\n if (hasStepHistory && pool) {\n await resolveCompletedWaits(pool, stepData);\n // Persist the resolved step data\n await updateStepData(pool, job.id, stepData);\n }\n\n // Per-job timeout logic\n const timeoutMs = job.timeoutMs ?? undefined;\n const forceKillOnTimeout = job.forceKillOnTimeout ?? false;\n let timeoutId: NodeJS.Timeout | undefined;\n const controller = new AbortController();\n try {\n // If forceKillOnTimeout is true, run handler in a worker thread\n // Note: wait features are not available in forceKillOnTimeout mode\n if (forceKillOnTimeout && timeoutMs && timeoutMs > 0) {\n await runHandlerInWorker(handler, job.payload, timeoutMs, job.jobType);\n } else {\n // Build the JobContext for prolong/onTimeout support\n let onTimeoutCallback: OnTimeoutCallback | undefined;\n\n // Reference to the reject function of the timeout promise so we can re-arm it\n let timeoutReject: ((error: Error) => void) | undefined;\n\n /**\n * Arms (or re-arms) the timeout. When it fires:\n * 1. If an onTimeout callback is registered, call it first.\n * - If it returns a positive number, re-arm with that duration and update DB.\n * - Otherwise, proceed with abort.\n * 2. If no onTimeout callback, proceed with abort.\n */\n const armTimeout = (ms: number) => {\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n // Check if an onTimeout callback wants to extend\n if (onTimeoutCallback) {\n try {\n const extension = onTimeoutCallback();\n if (typeof extension === 'number' && extension > 0) {\n // Extend: re-arm timeout and update DB\n backend.prolongJob(job.id).catch(() => {});\n armTimeout(extension);\n return;\n }\n } catch (callbackError) {\n log(\n `onTimeout callback threw for job ${job.id}: ${callbackError}`,\n );\n // Treat as \"no extension\" and proceed with abort\n }\n }\n // No extension -- proceed with abort\n controller.abort();\n const timeoutError = new Error(`Job timed out after ${ms} ms`);\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n if (timeoutReject) {\n timeoutReject(timeoutError);\n }\n }, ms);\n };\n\n const hasTimeout = timeoutMs != null && timeoutMs > 0;\n\n // Build base prolong/onTimeout context\n const baseCtx = hasTimeout\n ? {\n prolong: (ms?: number) => {\n const duration = ms ?? timeoutMs;\n if (duration != null && duration > 0) {\n armTimeout(duration);\n // Update DB locked_at to prevent reclaimStuckJobs\n backend.prolongJob(job.id).catch(() => {});\n }\n },\n onTimeout: (callback: OnTimeoutCallback) => {\n onTimeoutCallback = callback;\n },\n }\n : {\n prolong: () => {\n log('prolong() called but ignored: job has no timeout set');\n },\n onTimeout: () => {\n log('onTimeout() called but ignored: job has no timeout set');\n },\n };\n\n // Build context: full wait support for PostgreSQL, basic for others\n const ctx = pool\n ? buildWaitContext(backend, pool, job.id, stepData, baseCtx)\n : buildBasicContext(backend, job.id, baseCtx);\n\n // If forceKillOnTimeout was set but timeoutMs was missing, warn\n if (forceKillOnTimeout && !hasTimeout) {\n log(\n `forceKillOnTimeout is set but no timeoutMs for job ${job.id}, running without force kill`,\n );\n }\n\n const jobPromise = handler(job.payload, controller.signal, ctx);\n\n if (hasTimeout) {\n await Promise.race([\n jobPromise,\n new Promise<never>((_, reject) => {\n timeoutReject = reject;\n armTimeout(timeoutMs!);\n }),\n ]);\n } else {\n await jobPromise;\n }\n }\n if (timeoutId) clearTimeout(timeoutId);\n\n // Job completed successfully -- complete via backend\n await backend.completeJob(job.id);\n } catch (error) {\n if (timeoutId) clearTimeout(timeoutId);\n\n // Check if this is a WaitSignal (not a real error)\n if (error instanceof WaitSignal) {\n if (!pool) {\n // Wait signals should never happen with non-PostgreSQL backends\n // since the context methods throw, but guard just in case\n await backend.failJob(\n job.id,\n new Error(\n 'WaitSignal received but wait features require the PostgreSQL backend.',\n ),\n FailureReason.HandlerError,\n );\n return;\n }\n log(\n `Job ${job.id} entering wait: type=${error.type}, waitUntil=${error.waitUntil?.toISOString() ?? 'none'}, tokenId=${error.tokenId ?? 'none'}`,\n );\n await waitJob(pool, job.id, {\n waitUntil: error.waitUntil,\n waitTokenId: error.tokenId,\n stepData: error.stepData,\n });\n return;\n }\n\n // Real error -- handle as failure\n console.error(`Error processing job ${job.id}:`, error);\n let failureReason = FailureReason.HandlerError;\n if (\n error &&\n typeof error === 'object' &&\n 'failureReason' in error &&\n (error as { failureReason?: FailureReason }).failureReason ===\n FailureReason.Timeout\n ) {\n failureReason = FailureReason.Timeout;\n }\n await backend.failJob(\n job.id,\n error instanceof Error ? error : new Error(String(error)),\n failureReason,\n );\n }\n}\n\n/**\n * Process a batch of jobs using the provided handler map and concurrency limit\n */\nexport async function processBatchWithHandlers<PayloadMap>(\n backend: QueueBackend,\n workerId: string,\n batchSize: number,\n jobType: string | string[] | undefined,\n jobHandlers: JobHandlers<PayloadMap>,\n concurrency?: number,\n onError?: (error: Error) => void,\n): Promise<number> {\n const jobs = await backend.getNextBatch<PayloadMap, JobType<PayloadMap>>(\n workerId,\n batchSize,\n jobType,\n );\n if (!concurrency || concurrency >= jobs.length) {\n // Default: all in parallel\n await Promise.all(\n jobs.map((job) => processJobWithHandlers(backend, job, jobHandlers)),\n );\n return jobs.length;\n }\n // Concurrency-limited pool\n let idx = 0;\n let running = 0;\n let finished = 0;\n return new Promise((resolve, reject) => {\n const next = () => {\n if (finished === jobs.length) return resolve(jobs.length);\n while (running < concurrency && idx < jobs.length) {\n const job = jobs[idx++];\n running++;\n processJobWithHandlers(backend, job, jobHandlers)\n .then(() => {\n running--;\n finished++;\n next();\n })\n .catch((err) => {\n running--;\n finished++;\n if (onError) {\n onError(err instanceof Error ? err : new Error(String(err)));\n }\n next();\n });\n }\n };\n next();\n });\n}\n\n/**\n * Start a job processor that continuously processes jobs\n * @param backend - The queue backend\n * @param handlers - The job handlers for this processor instance\n * @param options - The processor options. Leave pollInterval empty to run only once. Use jobType to filter jobs by type.\n * @returns {Processor} The processor instance\n */\nexport const createProcessor = <PayloadMap = any>(\n backend: QueueBackend,\n handlers: JobHandlers<PayloadMap>,\n options: ProcessorOptions = {},\n): Processor => {\n const {\n workerId = `worker-${Math.random().toString(36).substring(2, 9)}`,\n batchSize = 10,\n pollInterval = 5000,\n onError = (error: Error) => console.error('Job processor error:', error),\n jobType,\n concurrency = 3,\n } = options;\n\n let running = false;\n let intervalId: NodeJS.Timeout | null = null;\n let currentBatchPromise: Promise<number> | null = null;\n\n setLogContext(options.verbose ?? false);\n\n const processJobs = async (): Promise<number> => {\n if (!running) return 0;\n\n log(\n `Processing jobs with workerId: ${workerId}${jobType ? ` and jobType: ${Array.isArray(jobType) ? jobType.join(',') : jobType}` : ''}`,\n );\n\n try {\n const processed = await processBatchWithHandlers(\n backend,\n workerId,\n batchSize,\n jobType,\n handlers,\n concurrency,\n onError,\n );\n // Only process one batch in start; do not schedule next batch here\n return processed;\n } catch (error) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n return 0;\n };\n\n return {\n /**\n * Start the job processor in the background.\n * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs as they become available.\n * - You have to call the stop method to stop the processor.\n */\n startInBackground: () => {\n if (running) return;\n\n log(`Starting job processor with workerId: ${workerId}`);\n running = true;\n\n // Single serialized loop: process a batch, then either immediately\n // continue (if full batch was returned) or wait pollInterval.\n const scheduleNext = (immediate: boolean) => {\n if (!running) return;\n if (immediate) {\n intervalId = setTimeout(loop, 0);\n } else {\n intervalId = setTimeout(loop, pollInterval);\n }\n };\n\n const loop = async () => {\n if (!running) return;\n currentBatchPromise = processJobs();\n const processed = await currentBatchPromise;\n currentBatchPromise = null;\n // If we got a full batch, there may be more work — process immediately\n scheduleNext(processed === batchSize);\n };\n\n // Start the first iteration immediately\n loop();\n },\n /**\n * Stop the job processor that runs in the background.\n * Does not wait for in-flight jobs.\n */\n stop: () => {\n log(`Stopping job processor with workerId: ${workerId}`);\n running = false;\n if (intervalId) {\n clearTimeout(intervalId);\n intervalId = null;\n }\n },\n /**\n * Stop the job processor and wait for all in-flight jobs to complete.\n * Useful for graceful shutdown (e.g., SIGTERM handling).\n */\n stopAndDrain: async (drainTimeoutMs = 30000) => {\n log(`Stopping and draining job processor with workerId: ${workerId}`);\n running = false;\n if (intervalId) {\n clearTimeout(intervalId);\n intervalId = null;\n }\n // Wait for current batch to finish, with a timeout\n if (currentBatchPromise) {\n await Promise.race([\n currentBatchPromise.catch(() => {}),\n new Promise<void>((resolve) => setTimeout(resolve, drainTimeoutMs)),\n ]);\n currentBatchPromise = null;\n }\n log(`Job processor ${workerId} drained`);\n },\n /**\n * Start the job processor synchronously.\n * - This will process all jobs immediately and then stop.\n * - The pollInterval is ignored.\n */\n start: async () => {\n log(`Starting job processor with workerId: ${workerId}`);\n running = true;\n const processed = await processJobs();\n running = false;\n return processed;\n },\n isRunning: () => running,\n };\n};\n","import { Pool } from 'pg';\nimport { PostgresJobQueueConfig } from './types.js';\nimport { parse } from 'pg-connection-string';\nimport fs from 'fs';\n\n/**\n * Helper to load a PEM string or file. Only values starting with 'file://' are loaded from file.\n */\nfunction loadPemOrFile(value?: string): string | undefined {\n if (!value) return undefined;\n if (value.startsWith('file://')) {\n const filePath = value.slice(7);\n return fs.readFileSync(filePath, 'utf8');\n }\n return value;\n}\n\n/**\n * Create a database connection pool with flexible SSL certificate loading.\n *\n * SSL config example (for local file paths):\n * ssl: {\n * ca: process.env.PGSSLROOTCERT, // PEM string or 'file://...'\n * cert: process.env.PGSSLCERT, // optional, PEM string or 'file://...'\n * key: process.env.PGSSLKEY, // optional, PEM string or 'file://...'\n * rejectUnauthorized: true\n * }\n */\nexport const createPool = (\n config: PostgresJobQueueConfig['databaseConfig'],\n): Pool => {\n let searchPath: string | undefined;\n let ssl: any = undefined;\n let customCA: string | undefined;\n let sslmode: string | undefined;\n\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n searchPath = url.searchParams.get('search_path') || undefined;\n sslmode = url.searchParams.get('sslmode') || undefined;\n if (sslmode === 'no-verify') {\n ssl = { rejectUnauthorized: false };\n }\n } catch (e) {\n const parsed = parse(config.connectionString);\n if (parsed.options) {\n const match = parsed.options.match(/search_path=([^\\s]+)/);\n if (match) {\n searchPath = match[1];\n }\n }\n sslmode = typeof parsed.sslmode === 'string' ? parsed.sslmode : undefined;\n if (sslmode === 'no-verify') {\n ssl = { rejectUnauthorized: false };\n }\n }\n }\n\n // Flexible SSL loading: only support file:// for file loading\n if (config.ssl) {\n if (typeof config.ssl.ca === 'string') {\n customCA = config.ssl.ca;\n } else if (typeof process.env.PGSSLROOTCERT === 'string') {\n customCA = process.env.PGSSLROOTCERT;\n } else {\n customCA = undefined;\n }\n const caValue =\n typeof customCA === 'string' ? loadPemOrFile(customCA) : undefined;\n ssl = {\n ...ssl,\n ...(caValue ? { ca: caValue } : {}),\n cert: loadPemOrFile(\n typeof config.ssl.cert === 'string'\n ? config.ssl.cert\n : process.env.PGSSLCERT,\n ),\n key: loadPemOrFile(\n typeof config.ssl.key === 'string'\n ? config.ssl.key\n : process.env.PGSSLKEY,\n ),\n rejectUnauthorized:\n config.ssl.rejectUnauthorized !== undefined\n ? config.ssl.rejectUnauthorized\n : true,\n };\n }\n\n // Warn if both sslmode (any value) and a custom CA are set\n if (sslmode && customCA) {\n const warning = `\\n\\n\\x1b[33m**************************************************\\n\\u26A0\\uFE0F WARNING: SSL CONFIGURATION ISSUE\\n**************************************************\\nBoth sslmode ('${sslmode}') is set in the connection string\\nand a custom CA is provided (via config.ssl.ca or PGSSLROOTCERT).\\nThis combination may cause connection failures or unexpected behavior.\\n\\nRecommended: Remove sslmode from the connection string when using a custom CA.\\n**************************************************\\x1b[0m\\n`;\n console.warn(warning);\n }\n\n const pool = new Pool({\n ...config,\n ...(ssl ? { ssl } : {}),\n });\n\n if (searchPath) {\n pool.on('connect', (client) => {\n client.query(`SET search_path TO ${searchPath}`);\n });\n }\n\n return pool;\n};\n","/**\n * Lua scripts for atomic Redis operations.\n *\n * Key naming convention (all prefixed with the configurable keyPrefix, default \"dq:\"):\n * dq:id_seq – INCR counter for auto-increment IDs\n * dq:job:{id} – Hash with all job fields\n * dq:queue – Sorted Set of ready-to-process job IDs (score = priority composite)\n * dq:delayed – Sorted Set of future-scheduled job IDs (score = run_at ms)\n * dq:retry – Sorted Set of retry-waiting job IDs (score = next_attempt_at ms)\n * dq:status:{status} – Set of job IDs per status\n * dq:type:{jobType} – Set of job IDs per type\n * dq:tag:{tag} – Set of job IDs per tag\n * dq:job:{id}:tags – Set of tags for a specific job\n * dq:events:{id} – List of JSON event objects\n * dq:idempotency:{key} – String mapping idempotency key → job ID\n * dq:all – Sorted Set of all jobs (score = createdAt ms, for ordering)\n * dq:event_id_seq – INCR counter for event IDs\n */\n\n// ─── Score helpers ──────────────────────────────────────────────────────\n// For the ready queue we need: higher priority first, then earlier createdAt.\n// Score = priority * 1e15 + (1e15 - createdAtMs)\n// ZPOPMAX gives highest score → highest priority, earliest created.\nconst SCORE_RANGE = '1000000000000000'; // 1e15\n\n/**\n * ADD JOB\n * KEYS: [prefix]\n * ARGV: [jobType, payloadJson, maxAttempts, priority, runAtMs, timeoutMs,\n * forceKillOnTimeout, tagsJson, idempotencyKey, nowMs]\n * Returns: job ID (number)\n */\nexport const ADD_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobType = ARGV[1]\nlocal payloadJson = ARGV[2]\nlocal maxAttempts = tonumber(ARGV[3])\nlocal priority = tonumber(ARGV[4])\nlocal runAtMs = ARGV[5] -- \"0\" means now\nlocal timeoutMs = ARGV[6] -- \"null\" string if not set\nlocal forceKillOnTimeout = ARGV[7]\nlocal tagsJson = ARGV[8] -- \"null\" or JSON array string\nlocal idempotencyKey = ARGV[9] -- \"null\" string if not set\nlocal nowMs = tonumber(ARGV[10])\n\n-- Idempotency check\nif idempotencyKey ~= \"null\" then\n local existing = redis.call('GET', prefix .. 'idempotency:' .. idempotencyKey)\n if existing then\n return existing\n end\nend\n\n-- Generate ID\nlocal id = redis.call('INCR', prefix .. 'id_seq')\nlocal jobKey = prefix .. 'job:' .. id\nlocal runAt = runAtMs ~= \"0\" and tonumber(runAtMs) or nowMs\n\n-- Store the job hash\nredis.call('HMSET', jobKey,\n 'id', id,\n 'jobType', jobType,\n 'payload', payloadJson,\n 'status', 'pending',\n 'maxAttempts', maxAttempts,\n 'attempts', 0,\n 'priority', priority,\n 'runAt', runAt,\n 'timeoutMs', timeoutMs,\n 'forceKillOnTimeout', forceKillOnTimeout,\n 'createdAt', nowMs,\n 'updatedAt', nowMs,\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'nextAttemptAt', 'null',\n 'pendingReason', 'null',\n 'errorHistory', '[]',\n 'failureReason', 'null',\n 'completedAt', 'null',\n 'startedAt', 'null',\n 'lastRetriedAt', 'null',\n 'lastFailedAt', 'null',\n 'lastCancelledAt', 'null',\n 'tags', tagsJson,\n 'idempotencyKey', idempotencyKey\n)\n\n-- Status index\nredis.call('SADD', prefix .. 'status:pending', id)\n\n-- Type index\nredis.call('SADD', prefix .. 'type:' .. jobType, id)\n\n-- Tag indexes\nif tagsJson ~= \"null\" then\n local tags = cjson.decode(tagsJson)\n for _, tag in ipairs(tags) do\n redis.call('SADD', prefix .. 'tag:' .. tag, id)\n end\n -- Store tags for exact-match queries\n for _, tag in ipairs(tags) do\n redis.call('SADD', prefix .. 'job:' .. id .. ':tags', tag)\n end\nend\n\n-- Idempotency mapping\nif idempotencyKey ~= \"null\" then\n redis.call('SET', prefix .. 'idempotency:' .. idempotencyKey, id)\nend\n\n-- All-jobs sorted set (for ordering by createdAt)\nredis.call('ZADD', prefix .. 'all', nowMs, id)\n\n-- Queue or delayed\nif runAt <= nowMs then\n -- Ready now: add to queue with priority score\n local score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - nowMs)\n redis.call('ZADD', prefix .. 'queue', score, id)\nelse\n -- Future: add to delayed set\n redis.call('ZADD', prefix .. 'delayed', runAt, id)\nend\n\nreturn id\n`;\n\n/**\n * GET NEXT BATCH\n * Atomically: move ready delayed/retry jobs into queue, then pop N jobs.\n * KEYS: [prefix]\n * ARGV: [workerId, batchSize, nowMs, jobTypeFilter]\n * jobTypeFilter: \"null\" or a JSON array like [\"email\",\"sms\"] or a string like \"email\"\n * Returns: array of job field arrays (flat: [field1, val1, field2, val2, ...] per job)\n */\nexport const GET_NEXT_BATCH_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal workerId = ARGV[1]\nlocal batchSize = tonumber(ARGV[2])\nlocal nowMs = tonumber(ARGV[3])\nlocal jobTypeFilter = ARGV[4] -- \"null\" or JSON array or single string\n\n-- 1. Move ready delayed jobs into queue\nlocal delayed = redis.call('ZRANGEBYSCORE', prefix .. 'delayed', '-inf', nowMs, 'LIMIT', 0, 200)\nfor _, jobId in ipairs(delayed) do\n local jk = prefix .. 'job:' .. jobId\n local status = redis.call('HGET', jk, 'status')\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n if status == 'pending' and attempts < maxAttempts then\n local pri = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local ca = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = pri * ${SCORE_RANGE} + (${SCORE_RANGE} - ca)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n end\n redis.call('ZREM', prefix .. 'delayed', jobId)\nend\n\n-- 2. Move ready retry jobs into queue\nlocal retries = redis.call('ZRANGEBYSCORE', prefix .. 'retry', '-inf', nowMs, 'LIMIT', 0, 200)\nfor _, jobId in ipairs(retries) do\n local jk = prefix .. 'job:' .. jobId\n local status = redis.call('HGET', jk, 'status')\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n if status == 'failed' and attempts < maxAttempts then\n local pri = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local ca = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = pri * ${SCORE_RANGE} + (${SCORE_RANGE} - ca)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n redis.call('SREM', prefix .. 'status:failed', jobId)\n redis.call('SADD', prefix .. 'status:pending', jobId)\n redis.call('HMSET', jk, 'status', 'pending')\n end\n redis.call('ZREM', prefix .. 'retry', jobId)\nend\n\n-- 3. Parse job type filter\nlocal filterTypes = nil\nif jobTypeFilter ~= \"null\" then\n -- Could be a JSON array or a plain string\n local ok, decoded = pcall(cjson.decode, jobTypeFilter)\n if ok and type(decoded) == 'table' then\n filterTypes = {}\n for _, t in ipairs(decoded) do filterTypes[t] = true end\n else\n filterTypes = { [jobTypeFilter] = true }\n end\nend\n\n-- 4. Pop candidates from queue (highest score first)\n-- We pop more than batchSize because some may be filtered out\nlocal popCount = batchSize * 3\nlocal candidates = redis.call('ZPOPMAX', prefix .. 'queue', popCount)\n-- candidates: [member1, score1, member2, score2, ...]\n\nlocal results = {}\nlocal jobsClaimed = 0\nlocal putBack = {} -- {score, id} pairs to put back\n\nfor i = 1, #candidates, 2 do\n local jobId = candidates[i]\n local score = candidates[i + 1]\n local jk = prefix .. 'job:' .. jobId\n\n if jobsClaimed >= batchSize then\n -- We have enough; put the rest back\n table.insert(putBack, score)\n table.insert(putBack, jobId)\n else\n -- Check job type filter\n local jt = redis.call('HGET', jk, 'jobType')\n if filterTypes and not filterTypes[jt] then\n -- Doesn't match filter: put back\n table.insert(putBack, score)\n table.insert(putBack, jobId)\n else\n -- Check run_at\n local runAt = tonumber(redis.call('HGET', jk, 'runAt'))\n if runAt > nowMs then\n -- Not ready yet: move to delayed\n redis.call('ZADD', prefix .. 'delayed', runAt, jobId)\n else\n -- Claim this job\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local startedAt = redis.call('HGET', jk, 'startedAt')\n local lastRetriedAt = redis.call('HGET', jk, 'lastRetriedAt')\n if startedAt == 'null' then startedAt = nowMs end\n if attempts > 0 then lastRetriedAt = nowMs end\n\n redis.call('HMSET', jk,\n 'status', 'processing',\n 'lockedAt', nowMs,\n 'lockedBy', workerId,\n 'attempts', attempts + 1,\n 'updatedAt', nowMs,\n 'pendingReason', 'null',\n 'startedAt', startedAt,\n 'lastRetriedAt', lastRetriedAt\n )\n\n -- Update status sets\n redis.call('SREM', prefix .. 'status:pending', jobId)\n redis.call('SADD', prefix .. 'status:processing', jobId)\n\n -- Return job data as flat array\n local data = redis.call('HGETALL', jk)\n for _, v in ipairs(data) do\n table.insert(results, v)\n end\n -- Separator\n table.insert(results, '__JOB_SEP__')\n jobsClaimed = jobsClaimed + 1\n end\n end\n end\nend\n\n-- Put back jobs we didn't claim\nif #putBack > 0 then\n redis.call('ZADD', prefix .. 'queue', unpack(putBack))\nend\n\nreturn results\n`;\n\n/**\n * COMPLETE JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const COMPLETE_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nredis.call('HMSET', jk,\n 'status', 'completed',\n 'updatedAt', nowMs,\n 'completedAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:processing', jobId)\nredis.call('SADD', prefix .. 'status:completed', jobId)\n\nreturn 1\n`;\n\n/**\n * FAIL JOB\n * KEYS: [prefix]\n * ARGV: [jobId, errorJson, failureReason, nowMs]\n * errorJson: JSON array like [{\"message\":\"...\", \"timestamp\":\"...\"}]\n */\nexport const FAIL_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal errorJson = ARGV[2]\nlocal failureReason = ARGV[3]\nlocal nowMs = tonumber(ARGV[4])\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal attempts = tonumber(redis.call('HGET', jk, 'attempts'))\nlocal maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n\n-- Compute next_attempt_at: 2^attempts minutes from now\nlocal nextAttemptAt = 'null'\nif attempts < maxAttempts then\n local delayMs = math.pow(2, attempts) * 60000\n nextAttemptAt = nowMs + delayMs\nend\n\n-- Append to error_history\nlocal history = redis.call('HGET', jk, 'errorHistory') or '[]'\nlocal ok, arr = pcall(cjson.decode, history)\nif not ok then arr = {} end\nlocal newErrors = cjson.decode(errorJson)\nfor _, e in ipairs(newErrors) do\n table.insert(arr, e)\nend\n\nredis.call('HMSET', jk,\n 'status', 'failed',\n 'updatedAt', nowMs,\n 'nextAttemptAt', tostring(nextAttemptAt),\n 'errorHistory', cjson.encode(arr),\n 'failureReason', failureReason,\n 'lastFailedAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:processing', jobId)\nredis.call('SADD', prefix .. 'status:failed', jobId)\n\n-- Schedule retry if applicable\nif nextAttemptAt ~= 'null' then\n redis.call('ZADD', prefix .. 'retry', nextAttemptAt, jobId)\nend\n\nreturn 1\n`;\n\n/**\n * RETRY JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const RETRY_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = tonumber(ARGV[2])\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal oldStatus = redis.call('HGET', jk, 'status')\n\nredis.call('HMSET', jk,\n 'status', 'pending',\n 'updatedAt', nowMs,\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'nextAttemptAt', nowMs,\n 'lastRetriedAt', nowMs\n)\n\n-- Remove from old status, add to pending\nif oldStatus then\n redis.call('SREM', prefix .. 'status:' .. oldStatus, jobId)\nend\nredis.call('SADD', prefix .. 'status:pending', jobId)\n\n-- Remove from retry sorted set if present\nredis.call('ZREM', prefix .. 'retry', jobId)\n\n-- Add to queue (ready now)\nlocal priority = tonumber(redis.call('HGET', jk, 'priority') or '0')\nlocal createdAt = tonumber(redis.call('HGET', jk, 'createdAt'))\nlocal score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - createdAt)\nredis.call('ZADD', prefix .. 'queue', score, jobId)\n\nreturn 1\n`;\n\n/**\n * CANCEL JOB (only if pending)\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const CANCEL_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal status = redis.call('HGET', jk, 'status')\nif status ~= 'pending' then return 0 end\n\nredis.call('HMSET', jk,\n 'status', 'cancelled',\n 'updatedAt', nowMs,\n 'lastCancelledAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:pending', jobId)\nredis.call('SADD', prefix .. 'status:cancelled', jobId)\n-- Remove from queue / delayed\nredis.call('ZREM', prefix .. 'queue', jobId)\nredis.call('ZREM', prefix .. 'delayed', jobId)\n\nreturn 1\n`;\n\n/**\n * PROLONG JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const PROLONG_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal status = redis.call('HGET', jk, 'status')\nif status ~= 'processing' then return 0 end\n\nredis.call('HMSET', jk,\n 'lockedAt', nowMs,\n 'updatedAt', nowMs\n)\n\nreturn 1\n`;\n\n/**\n * RECLAIM STUCK JOBS\n * KEYS: [prefix]\n * ARGV: [maxAgeMs, nowMs]\n * Returns: count of reclaimed jobs\n */\nexport const RECLAIM_STUCK_JOBS_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal maxAgeMs = tonumber(ARGV[1])\nlocal nowMs = tonumber(ARGV[2])\n\nlocal processing = redis.call('SMEMBERS', prefix .. 'status:processing')\nlocal count = 0\n\nfor _, jobId in ipairs(processing) do\n local jk = prefix .. 'job:' .. jobId\n local lockedAt = redis.call('HGET', jk, 'lockedAt')\n if lockedAt and lockedAt ~= 'null' then\n local lockedAtNum = tonumber(lockedAt)\n if lockedAtNum then\n -- Use the greater of maxAgeMs and the job's own timeoutMs\n local jobMaxAge = maxAgeMs\n local timeoutMs = redis.call('HGET', jk, 'timeoutMs')\n if timeoutMs and timeoutMs ~= 'null' then\n local tMs = tonumber(timeoutMs)\n if tMs and tMs > jobMaxAge then\n jobMaxAge = tMs\n end\n end\n local cutoff = nowMs - jobMaxAge\n if lockedAtNum < cutoff then\n redis.call('HMSET', jk,\n 'status', 'pending',\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'updatedAt', nowMs\n )\n redis.call('SREM', prefix .. 'status:processing', jobId)\n redis.call('SADD', prefix .. 'status:pending', jobId)\n\n -- Re-add to queue\n local priority = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local createdAt = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - createdAt)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n\n count = count + 1\n end\n end\n end\nend\n\nreturn count\n`;\n\n/**\n * CLEANUP OLD JOBS\n * KEYS: [prefix]\n * ARGV: [cutoffMs]\n * Returns: count of deleted jobs\n */\nexport const CLEANUP_OLD_JOBS_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal cutoffMs = tonumber(ARGV[1])\n\nlocal completed = redis.call('SMEMBERS', prefix .. 'status:completed')\nlocal count = 0\n\nfor _, jobId in ipairs(completed) do\n local jk = prefix .. 'job:' .. jobId\n local updatedAt = tonumber(redis.call('HGET', jk, 'updatedAt'))\n if updatedAt and updatedAt < cutoffMs then\n -- Remove all indexes\n local jobType = redis.call('HGET', jk, 'jobType')\n local tagsJson = redis.call('HGET', jk, 'tags')\n local idempotencyKey = redis.call('HGET', jk, 'idempotencyKey')\n\n redis.call('DEL', jk)\n redis.call('SREM', prefix .. 'status:completed', jobId)\n redis.call('ZREM', prefix .. 'all', jobId)\n if jobType then\n redis.call('SREM', prefix .. 'type:' .. jobType, jobId)\n end\n if tagsJson and tagsJson ~= 'null' then\n local ok, tags = pcall(cjson.decode, tagsJson)\n if ok and type(tags) == 'table' then\n for _, tag in ipairs(tags) do\n redis.call('SREM', prefix .. 'tag:' .. tag, jobId)\n end\n end\n redis.call('DEL', prefix .. 'job:' .. jobId .. ':tags')\n end\n if idempotencyKey and idempotencyKey ~= 'null' then\n redis.call('DEL', prefix .. 'idempotency:' .. idempotencyKey)\n end\n -- Delete events\n redis.call('DEL', prefix .. 'events:' .. jobId)\n\n count = count + 1\n end\nend\n\nreturn count\n`;\n","import { createRequire } from 'module';\nimport type { Redis as RedisType } from 'ioredis';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n JobType,\n RedisJobQueueConfig,\n} from '../types.js';\nimport { QueueBackend, JobFilters, JobUpdates } from '../backend.js';\nimport { log } from '../log-context.js';\nimport {\n ADD_JOB_SCRIPT,\n GET_NEXT_BATCH_SCRIPT,\n COMPLETE_JOB_SCRIPT,\n FAIL_JOB_SCRIPT,\n RETRY_JOB_SCRIPT,\n CANCEL_JOB_SCRIPT,\n PROLONG_JOB_SCRIPT,\n RECLAIM_STUCK_JOBS_SCRIPT,\n CLEANUP_OLD_JOBS_SCRIPT,\n} from './redis-scripts.js';\n\n/** Helper: convert a Redis hash flat array [k,v,k,v,...] to a JS object */\nfunction hashToObject(arr: string[]): Record<string, string> {\n const obj: Record<string, string> = {};\n for (let i = 0; i < arr.length; i += 2) {\n obj[arr[i]] = arr[i + 1];\n }\n return obj;\n}\n\n/** Deserialise a Redis hash object into a JobRecord */\nfunction deserializeJob<PayloadMap, T extends JobType<PayloadMap>>(\n h: Record<string, string>,\n): JobRecord<PayloadMap, T> {\n const nullish = (v: string | undefined) =>\n v === undefined || v === 'null' || v === '' ? null : v;\n\n const numOrNull = (v: string | undefined): number | null => {\n const n = nullish(v);\n return n === null ? null : Number(n);\n };\n const dateOrNull = (v: string | undefined): Date | null => {\n const n = numOrNull(v);\n return n === null ? null : new Date(n);\n };\n\n let errorHistory: { message: string; timestamp: string }[] = [];\n try {\n const raw = h.errorHistory;\n if (raw && raw !== '[]') {\n errorHistory = JSON.parse(raw);\n }\n } catch {\n /* ignore */\n }\n\n let tags: string[] | undefined;\n try {\n const raw = h.tags;\n if (raw && raw !== 'null') {\n tags = JSON.parse(raw);\n }\n } catch {\n /* ignore */\n }\n\n let payload: any;\n try {\n payload = JSON.parse(h.payload);\n } catch {\n payload = h.payload;\n }\n\n return {\n id: Number(h.id),\n jobType: h.jobType as T,\n payload,\n status: h.status as any,\n createdAt: new Date(Number(h.createdAt)),\n updatedAt: new Date(Number(h.updatedAt)),\n lockedAt: dateOrNull(h.lockedAt),\n lockedBy: nullish(h.lockedBy) as string | null,\n attempts: Number(h.attempts),\n maxAttempts: Number(h.maxAttempts),\n nextAttemptAt: dateOrNull(h.nextAttemptAt),\n priority: Number(h.priority),\n runAt: new Date(Number(h.runAt)),\n pendingReason: nullish(h.pendingReason) as string | null | undefined,\n errorHistory,\n timeoutMs: numOrNull(h.timeoutMs),\n forceKillOnTimeout:\n h.forceKillOnTimeout === 'true' || h.forceKillOnTimeout === '1'\n ? true\n : h.forceKillOnTimeout === 'false' || h.forceKillOnTimeout === '0'\n ? false\n : null,\n failureReason: (nullish(h.failureReason) as FailureReason | null) ?? null,\n completedAt: dateOrNull(h.completedAt),\n startedAt: dateOrNull(h.startedAt),\n lastRetriedAt: dateOrNull(h.lastRetriedAt),\n lastFailedAt: dateOrNull(h.lastFailedAt),\n lastCancelledAt: dateOrNull(h.lastCancelledAt),\n tags,\n idempotencyKey: nullish(h.idempotencyKey) as string | null | undefined,\n progress: numOrNull(h.progress),\n };\n}\n\nexport class RedisBackend implements QueueBackend {\n private client: RedisType;\n private prefix: string;\n\n constructor(redisConfig: RedisJobQueueConfig['redisConfig']) {\n // Dynamically require ioredis to avoid hard dep\n let IORedis: any;\n try {\n const _require = createRequire(import.meta.url);\n IORedis = _require('ioredis');\n } catch {\n throw new Error(\n 'Redis backend requires the \"ioredis\" package. Install it with: npm install ioredis',\n );\n }\n\n this.prefix = redisConfig.keyPrefix ?? 'dq:';\n\n if (redisConfig.url) {\n this.client = new IORedis(redisConfig.url, {\n ...(redisConfig.tls ? { tls: redisConfig.tls } : {}),\n ...(redisConfig.db !== undefined ? { db: redisConfig.db } : {}),\n });\n } else {\n this.client = new IORedis({\n host: redisConfig.host ?? '127.0.0.1',\n port: redisConfig.port ?? 6379,\n password: redisConfig.password,\n db: redisConfig.db ?? 0,\n ...(redisConfig.tls ? { tls: redisConfig.tls } : {}),\n });\n }\n }\n\n /** Expose the raw ioredis client for advanced usage. */\n getClient(): RedisType {\n return this.client;\n }\n\n private nowMs(): number {\n return Date.now();\n }\n\n // ── Events ──────────────────────────────────────────────────────────\n\n async recordJobEvent(\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n ): Promise<void> {\n try {\n const eventId = await this.client.incr(`${this.prefix}event_id_seq`);\n const event = JSON.stringify({\n id: eventId,\n jobId,\n eventType,\n createdAt: this.nowMs(),\n metadata: metadata ?? null,\n });\n await this.client.rpush(`${this.prefix}events:${jobId}`, event);\n } catch (error) {\n log(`Error recording job event for job ${jobId}: ${error}`);\n // Do not throw\n }\n }\n\n async getJobEvents(jobId: number): Promise<JobEvent[]> {\n const raw = await this.client.lrange(\n `${this.prefix}events:${jobId}`,\n 0,\n -1,\n );\n return raw.map((r: string) => {\n const e = JSON.parse(r);\n return {\n ...e,\n createdAt: new Date(e.createdAt),\n };\n });\n }\n\n // ── Job CRUD ──────────────────────────────────────────────────────────\n\n async addJob<PayloadMap, T extends JobType<PayloadMap>>({\n jobType,\n payload,\n maxAttempts = 3,\n priority = 0,\n runAt = null,\n timeoutMs = undefined,\n forceKillOnTimeout = false,\n tags = undefined,\n idempotencyKey = undefined,\n }: JobOptions<PayloadMap, T>): Promise<number> {\n const now = this.nowMs();\n const runAtMs = runAt ? runAt.getTime() : 0;\n\n const result = (await this.client.eval(\n ADD_JOB_SCRIPT,\n 1,\n this.prefix,\n jobType,\n JSON.stringify(payload),\n maxAttempts,\n priority,\n runAtMs.toString(),\n timeoutMs !== undefined ? timeoutMs.toString() : 'null',\n forceKillOnTimeout ? 'true' : 'false',\n tags ? JSON.stringify(tags) : 'null',\n idempotencyKey ?? 'null',\n now,\n )) as number;\n\n const jobId = Number(result);\n log(\n `Added job ${jobId}: payload ${JSON.stringify(payload)}, ${runAt ? `runAt ${runAt.toISOString()}, ` : ''}priority ${priority}, maxAttempts ${maxAttempts}, jobType ${jobType}, tags ${JSON.stringify(tags)}${idempotencyKey ? `, idempotencyKey \"${idempotencyKey}\"` : ''}`,\n );\n await this.recordJobEvent(jobId, JobEventType.Added, {\n jobType,\n payload,\n tags,\n idempotencyKey,\n });\n return jobId;\n }\n\n async getJob<PayloadMap, T extends JobType<PayloadMap>>(\n id: number,\n ): Promise<JobRecord<PayloadMap, T> | null> {\n const data = await this.client.hgetall(`${this.prefix}job:${id}`);\n if (!data || Object.keys(data).length === 0) {\n log(`Job ${id} not found`);\n return null;\n }\n log(`Found job ${id}`);\n return deserializeJob<PayloadMap, T>(data);\n }\n\n async getJobsByStatus<PayloadMap, T extends JobType<PayloadMap>>(\n status: string,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const ids = await this.client.smembers(`${this.prefix}status:${status}`);\n if (ids.length === 0) return [];\n\n // Load all, sort by createdAt DESC, then paginate\n const jobs = await this.loadJobsByIds<PayloadMap, T>(ids);\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n async getAllJobs<PayloadMap, T extends JobType<PayloadMap>>(\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // All jobs sorted by createdAt DESC (the 'all' sorted set is scored by createdAt ms)\n const ids = await this.client.zrevrange(\n `${this.prefix}all`,\n offset,\n offset + limit - 1,\n );\n if (ids.length === 0) return [];\n return this.loadJobsByIds<PayloadMap, T>(ids);\n }\n\n async getJobs<PayloadMap, T extends JobType<PayloadMap>>(\n filters?: JobFilters,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // Start with all job IDs\n let candidateIds: string[];\n\n if (filters?.jobType) {\n candidateIds = await this.client.smembers(\n `${this.prefix}type:${filters.jobType}`,\n );\n } else {\n candidateIds = await this.client.zrevrange(`${this.prefix}all`, 0, -1);\n }\n\n if (candidateIds.length === 0) return [];\n\n // Apply tag filter via set operations\n if (filters?.tags && filters.tags.values.length > 0) {\n candidateIds = await this.filterByTags(\n candidateIds,\n filters.tags.values,\n filters.tags.mode || 'all',\n );\n }\n\n // Load and filter remaining criteria in-memory\n let jobs = await this.loadJobsByIds<PayloadMap, T>(candidateIds);\n\n if (filters) {\n if (filters.priority !== undefined) {\n jobs = jobs.filter((j) => j.priority === filters.priority);\n }\n if (filters.runAt) {\n jobs = this.filterByRunAt(jobs, filters.runAt);\n }\n }\n\n // Sort by createdAt DESC\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n async getJobsByTags<PayloadMap, T extends JobType<PayloadMap>>(\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // Start with all IDs\n const allIds = await this.client.zrevrange(`${this.prefix}all`, 0, -1);\n if (allIds.length === 0) return [];\n\n const filtered = await this.filterByTags(allIds, tags, mode);\n if (filtered.length === 0) return [];\n\n const jobs = await this.loadJobsByIds<PayloadMap, T>(filtered);\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n // ── Processing lifecycle ──────────────────────────────────────────────\n\n async getNextBatch<PayloadMap, T extends JobType<PayloadMap>>(\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const now = this.nowMs();\n const jobTypeFilter =\n jobType === undefined\n ? 'null'\n : Array.isArray(jobType)\n ? JSON.stringify(jobType)\n : jobType;\n\n const result = (await this.client.eval(\n GET_NEXT_BATCH_SCRIPT,\n 1,\n this.prefix,\n workerId,\n batchSize,\n now,\n jobTypeFilter,\n )) as string[];\n\n if (!result || result.length === 0) {\n log('Found 0 jobs to process');\n return [];\n }\n\n // Parse the flat result into jobs separated by __JOB_SEP__\n const jobs: JobRecord<PayloadMap, T>[] = [];\n let current: string[] = [];\n for (const item of result) {\n if (item === '__JOB_SEP__') {\n if (current.length > 0) {\n const h = hashToObject(current);\n jobs.push(deserializeJob<PayloadMap, T>(h));\n }\n current = [];\n } else {\n current.push(item);\n }\n }\n\n log(`Found ${jobs.length} jobs to process`);\n\n // Record processing events\n for (const job of jobs) {\n await this.recordJobEvent(job.id, JobEventType.Processing);\n }\n\n return jobs;\n }\n\n async completeJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(COMPLETE_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Completed);\n log(`Completed job ${jobId}`);\n }\n\n async failJob(\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n ): Promise<void> {\n const now = this.nowMs();\n const errorJson = JSON.stringify([\n {\n message: error.message || String(error),\n timestamp: new Date(now).toISOString(),\n },\n ]);\n await this.client.eval(\n FAIL_JOB_SCRIPT,\n 1,\n this.prefix,\n jobId,\n errorJson,\n failureReason ?? 'null',\n now,\n );\n await this.recordJobEvent(jobId, JobEventType.Failed, {\n message: error.message || String(error),\n failureReason,\n });\n log(`Failed job ${jobId}`);\n }\n\n async prolongJob(jobId: number): Promise<void> {\n try {\n const now = this.nowMs();\n await this.client.eval(PROLONG_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Prolonged);\n log(`Prolonged job ${jobId}`);\n } catch (error) {\n log(`Error prolonging job ${jobId}: ${error}`);\n // Best-effort, do not throw\n }\n }\n\n // ── Progress ──────────────────────────────────────────────────────────\n\n async updateProgress(jobId: number, progress: number): Promise<void> {\n try {\n const now = this.nowMs();\n await this.client.hset(\n `${this.prefix}job:${jobId}`,\n 'progress',\n progress.toString(),\n 'updatedAt',\n now.toString(),\n );\n log(`Updated progress for job ${jobId}: ${progress}%`);\n } catch (error) {\n log(`Error updating progress for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n }\n }\n\n // ── Job management ────────────────────────────────────────────────────\n\n async retryJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(RETRY_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Retried);\n log(`Retried job ${jobId}`);\n }\n\n async cancelJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(CANCEL_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Cancelled);\n log(`Cancelled job ${jobId}`);\n }\n\n async cancelAllUpcomingJobs(filters?: JobFilters): Promise<number> {\n // Get all pending IDs\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return 0;\n\n if (filters) {\n ids = await this.applyFilters(ids, filters);\n }\n\n const now = this.nowMs();\n let count = 0;\n for (const id of ids) {\n const result = await this.client.eval(\n CANCEL_JOB_SCRIPT,\n 1,\n this.prefix,\n id,\n now,\n );\n if (Number(result) === 1) count++;\n }\n\n log(`Cancelled ${count} jobs`);\n return count;\n }\n\n async editJob(jobId: number, updates: JobUpdates): Promise<void> {\n const jk = `${this.prefix}job:${jobId}`;\n const status = await this.client.hget(jk, 'status');\n if (status !== 'pending') {\n log(`Job ${jobId} is not pending (status: ${status}), skipping edit`);\n return;\n }\n\n const now = this.nowMs();\n const fields: string[] = [];\n const metadata: any = {};\n\n if (updates.payload !== undefined) {\n fields.push('payload', JSON.stringify(updates.payload));\n metadata.payload = updates.payload;\n }\n if (updates.maxAttempts !== undefined) {\n fields.push('maxAttempts', updates.maxAttempts.toString());\n metadata.maxAttempts = updates.maxAttempts;\n }\n if (updates.priority !== undefined) {\n fields.push('priority', updates.priority.toString());\n metadata.priority = updates.priority;\n\n // Recompute queue score\n const createdAt = await this.client.hget(jk, 'createdAt');\n const score = updates.priority * 1e15 + (1e15 - Number(createdAt));\n // Update score in queue if present\n const inQueue = await this.client.zscore(\n `${this.prefix}queue`,\n jobId.toString(),\n );\n if (inQueue !== null) {\n await this.client.zadd(`${this.prefix}queue`, score, jobId.toString());\n }\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n fields.push('runAt', now.toString());\n } else {\n fields.push('runAt', updates.runAt.getTime().toString());\n }\n metadata.runAt = updates.runAt;\n }\n if (updates.timeoutMs !== undefined) {\n fields.push(\n 'timeoutMs',\n updates.timeoutMs !== null ? updates.timeoutMs.toString() : 'null',\n );\n metadata.timeoutMs = updates.timeoutMs;\n }\n if (updates.tags !== undefined) {\n // Update tag indexes: remove old, add new\n const oldTagsJson = await this.client.hget(jk, 'tags');\n if (oldTagsJson && oldTagsJson !== 'null') {\n try {\n const oldTags = JSON.parse(oldTagsJson) as string[];\n for (const tag of oldTags) {\n await this.client.srem(\n `${this.prefix}tag:${tag}`,\n jobId.toString(),\n );\n }\n } catch {\n /* ignore */\n }\n }\n await this.client.del(`${this.prefix}job:${jobId}:tags`);\n\n if (updates.tags !== null) {\n for (const tag of updates.tags) {\n await this.client.sadd(`${this.prefix}tag:${tag}`, jobId.toString());\n await this.client.sadd(`${this.prefix}job:${jobId}:tags`, tag);\n }\n fields.push('tags', JSON.stringify(updates.tags));\n } else {\n fields.push('tags', 'null');\n }\n metadata.tags = updates.tags;\n }\n\n if (fields.length === 0) {\n log(`No fields to update for job ${jobId}`);\n return;\n }\n\n fields.push('updatedAt', now.toString());\n await (this.client as any).hmset(jk, ...fields);\n\n await this.recordJobEvent(jobId, JobEventType.Edited, metadata);\n log(`Edited job ${jobId}: ${JSON.stringify(metadata)}`);\n }\n\n async editAllPendingJobs(\n filters: JobFilters | undefined,\n updates: JobUpdates,\n ): Promise<number> {\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return 0;\n\n if (filters) {\n ids = await this.applyFilters(ids, filters);\n }\n\n let count = 0;\n for (const id of ids) {\n await this.editJob(Number(id), updates);\n count++;\n }\n\n log(`Edited ${count} pending jobs`);\n return count;\n }\n\n async cleanupOldJobs(daysToKeep = 30): Promise<number> {\n const cutoffMs = this.nowMs() - daysToKeep * 24 * 60 * 60 * 1000;\n const result = (await this.client.eval(\n CLEANUP_OLD_JOBS_SCRIPT,\n 1,\n this.prefix,\n cutoffMs,\n )) as number;\n log(`Deleted ${result} old jobs`);\n return Number(result);\n }\n\n async cleanupOldJobEvents(daysToKeep = 30): Promise<number> {\n // Redis events are stored per-job; cleaning up old events requires\n // iterating event lists and filtering by date. For now, we skip\n // events belonging to jobs that have been cleaned up (their keys are gone).\n // A full implementation would iterate all events:* keys.\n log(\n `cleanupOldJobEvents is a no-op for Redis backend (events are cleaned up with their jobs)`,\n );\n return 0;\n }\n\n async reclaimStuckJobs(maxProcessingTimeMinutes = 10): Promise<number> {\n const maxAgeMs = maxProcessingTimeMinutes * 60 * 1000;\n const now = this.nowMs();\n const result = (await this.client.eval(\n RECLAIM_STUCK_JOBS_SCRIPT,\n 1,\n this.prefix,\n maxAgeMs,\n now,\n )) as number;\n log(`Reclaimed ${result} stuck jobs`);\n return Number(result);\n }\n\n // ── Internal helpers ──────────────────────────────────────────────────\n\n async setPendingReasonForUnpickedJobs(\n reason: string,\n jobType?: string | string[],\n ): Promise<void> {\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return;\n\n if (jobType) {\n const types = Array.isArray(jobType) ? jobType : [jobType];\n const typeSet = new Set<string>();\n for (const t of types) {\n const typeIds = await this.client.smembers(`${this.prefix}type:${t}`);\n for (const id of typeIds) typeSet.add(id);\n }\n ids = ids.filter((id: string) => typeSet.has(id));\n }\n\n for (const id of ids) {\n await this.client.hset(\n `${this.prefix}job:${id}`,\n 'pendingReason',\n reason,\n );\n }\n }\n\n // ── Private helpers ───────────────────────────────────────────────────\n\n private async loadJobsByIds<PayloadMap, T extends JobType<PayloadMap>>(\n ids: string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const pipeline = this.client.pipeline();\n for (const id of ids) {\n pipeline.hgetall(`${this.prefix}job:${id}`);\n }\n const results = await pipeline.exec();\n const jobs: JobRecord<PayloadMap, T>[] = [];\n if (results) {\n for (const [err, data] of results) {\n if (\n !err &&\n data &&\n typeof data === 'object' &&\n Object.keys(data as object).length > 0\n ) {\n jobs.push(\n deserializeJob<PayloadMap, T>(data as Record<string, string>),\n );\n }\n }\n }\n return jobs;\n }\n\n private async filterByTags(\n candidateIds: string[],\n tags: string[],\n mode: TagQueryMode,\n ): Promise<string[]> {\n const candidateSet = new Set(candidateIds.map(String));\n\n if (mode === 'exact') {\n // Jobs whose tags set is exactly equal to the given tags\n const tagSet = new Set(tags);\n const result: string[] = [];\n for (const id of candidateIds) {\n const jobTags = await this.client.smembers(\n `${this.prefix}job:${id}:tags`,\n );\n if (\n jobTags.length === tagSet.size &&\n jobTags.every((t: string) => tagSet.has(t))\n ) {\n result.push(id);\n }\n }\n return result;\n }\n\n if (mode === 'all') {\n // Jobs that have ALL the given tags\n let intersection = new Set(candidateIds.map(String));\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n const tagSet = new Set(tagMembers.map(String));\n intersection = new Set(\n [...intersection].filter((id) => tagSet.has(id)),\n );\n }\n return [...intersection].filter((id) => candidateSet.has(id));\n }\n\n if (mode === 'any') {\n // Jobs that have at least ONE of the given tags\n const union = new Set<string>();\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n for (const id of tagMembers) union.add(String(id));\n }\n return [...union].filter((id) => candidateSet.has(id));\n }\n\n if (mode === 'none') {\n // Jobs that have NONE of the given tags\n const exclude = new Set<string>();\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n for (const id of tagMembers) exclude.add(String(id));\n }\n return candidateIds.filter((id) => !exclude.has(String(id)));\n }\n\n // Default: 'all'\n return this.filterByTags(candidateIds, tags, 'all');\n }\n\n private filterByRunAt<PayloadMap, T extends JobType<PayloadMap>>(\n jobs: JobRecord<PayloadMap, T>[],\n runAt: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date },\n ): JobRecord<PayloadMap, T>[] {\n if (runAt instanceof Date) {\n return jobs.filter((j) => j.runAt.getTime() === runAt.getTime());\n }\n return jobs.filter((j) => {\n const t = j.runAt.getTime();\n if (runAt.gt && !(t > runAt.gt.getTime())) return false;\n if (runAt.gte && !(t >= runAt.gte.getTime())) return false;\n if (runAt.lt && !(t < runAt.lt.getTime())) return false;\n if (runAt.lte && !(t <= runAt.lte.getTime())) return false;\n if (runAt.eq && t !== runAt.eq.getTime()) return false;\n return true;\n });\n }\n\n private async applyFilters(\n ids: string[],\n filters: JobFilters,\n ): Promise<string[]> {\n let result = ids;\n\n if (filters.jobType) {\n const typeIds = new Set(\n await this.client.smembers(`${this.prefix}type:${filters.jobType}`),\n );\n result = result.filter((id) => typeIds.has(id));\n }\n\n if (filters.tags && filters.tags.values.length > 0) {\n result = await this.filterByTags(\n result,\n filters.tags.values,\n filters.tags.mode || 'all',\n );\n }\n\n // For priority and runAt, we need to load job data\n if (filters.priority !== undefined || filters.runAt) {\n const jobs = await this.loadJobsByIds(result);\n let filtered = jobs;\n if (filters.priority !== undefined) {\n filtered = filtered.filter((j) => j.priority === filters.priority);\n }\n if (filters.runAt) {\n filtered = this.filterByRunAt(filtered, filters.runAt);\n }\n result = filtered.map((j) => j.id.toString());\n }\n\n return result;\n }\n}\n","import { JobHandler } from './types.js';\n\n/**\n * Validates that a job handler can be serialized for use with forceKillOnTimeout.\n *\n * This function checks if a handler can be safely serialized and executed in a worker thread.\n * Use this function during development to catch serialization issues early.\n *\n * @param handler - The job handler function to validate\n * @param jobType - Optional job type name for better error messages\n * @returns An object with `isSerializable` boolean and optional `error` message\n *\n * @example\n * ```ts\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * const result = validateHandlerSerializable(handler, 'myJob');\n * if (!result.isSerializable) {\n * console.error('Handler is not serializable:', result.error);\n * }\n * ```\n */\nexport function validateHandlerSerializable<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n jobType?: string,\n): { isSerializable: boolean; error?: string } {\n try {\n const handlerString = handler.toString();\n const typeLabel = jobType ? `job type \"${jobType}\"` : 'handler';\n\n // Check for common patterns that indicate non-serializable handlers\n // 1. Arrow functions that capture 'this' (indicated by 'this' in the function body but not in parameters)\n if (\n handlerString.includes('this.') &&\n !handlerString.match(/\\([^)]*this[^)]*\\)/)\n ) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} uses 'this' context which cannot be serialized. ` +\n `Use a regular function or avoid 'this' references when forceKillOnTimeout is enabled.`,\n };\n }\n\n // 2. Check if handler string looks like it might have closures\n // This is a heuristic - we can't perfectly detect closures, but we can warn about common patterns\n if (handlerString.includes('[native code]')) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} contains native code which cannot be serialized. ` +\n `Ensure your handler is a plain function when forceKillOnTimeout is enabled.`,\n };\n }\n\n // 3. Try to create a function from the string to validate it's parseable\n // This will catch syntax errors early\n try {\n new Function('return ' + handlerString);\n } catch (parseError) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} cannot be serialized: ${parseError instanceof Error ? parseError.message : String(parseError)}. ` +\n `When using forceKillOnTimeout, handlers must be serializable functions without closures over external variables.`,\n };\n }\n\n // 4. Check for common closure patterns (heuristic)\n // Look for variable references that might be from outer scope\n // This is not perfect but can catch some common issues\n const hasPotentialClosure =\n /const\\s+\\w+\\s*=\\s*[^;]+;\\s*async\\s*\\(/.test(handlerString) ||\n /let\\s+\\w+\\s*=\\s*[^;]+;\\s*async\\s*\\(/.test(handlerString);\n\n if (hasPotentialClosure) {\n // This is just a warning, not a hard error, since we can't be 100% sure\n // The actual serialization will fail at runtime if there's a real issue\n return {\n isSerializable: true, // Still serializable, but might have issues\n error:\n `Warning: Handler for ${typeLabel} may have closures over external variables. ` +\n `Test thoroughly with forceKillOnTimeout enabled. If the handler fails to execute in a worker thread, ` +\n `ensure all dependencies are imported within the handler function.`,\n };\n }\n\n return { isSerializable: true };\n } catch (error) {\n return {\n isSerializable: false,\n error: `Failed to validate handler serialization${jobType ? ` for job type \"${jobType}\"` : ''}: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Test if a handler can be serialized and executed in a worker thread.\n * This is a more thorough check that actually attempts to serialize and deserialize the handler.\n *\n * @param handler - The job handler function to test\n * @param jobType - Optional job type name for better error messages\n * @returns Promise that resolves to validation result\n *\n * @example\n * ```ts\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * const result = await testHandlerSerialization(handler, 'myJob');\n * if (!result.isSerializable) {\n * console.error('Handler failed serialization test:', result.error);\n * }\n * ```\n */\nexport async function testHandlerSerialization<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n jobType?: string,\n): Promise<{ isSerializable: boolean; error?: string }> {\n // First do the basic validation\n const basicValidation = validateHandlerSerializable(handler, jobType);\n if (!basicValidation.isSerializable) {\n return basicValidation;\n }\n\n // Then try to actually serialize and deserialize in a worker-like context\n try {\n const handlerString = handler.toString();\n const handlerFn = new Function('return ' + handlerString)();\n\n // Try to call it with dummy parameters to see if it executes\n // We use a very short timeout to avoid hanging\n const testPromise = handlerFn({}, new AbortController().signal);\n const timeoutPromise = new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Handler test timeout')), 100),\n );\n\n try {\n await Promise.race([testPromise, timeoutPromise]);\n } catch (execError) {\n // Execution errors are OK - we just want to know if it can be deserialized\n // The actual job execution will handle real errors\n if (\n execError instanceof Error &&\n execError.message === 'Handler test timeout'\n ) {\n // Handler is taking too long, but that's OK for serialization test\n return { isSerializable: true };\n }\n }\n\n return { isSerializable: true };\n } catch (error) {\n return {\n isSerializable: false,\n error: `Handler failed serialization test: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","import {\n createWaitpoint,\n completeWaitpoint,\n getWaitpoint,\n expireTimedOutWaitpoints,\n} from './queue.js';\nimport { createProcessor } from './processor.js';\nimport {\n JobQueueConfig,\n JobQueue,\n JobOptions,\n ProcessorOptions,\n JobHandlers,\n JobType,\n PostgresJobQueueConfig,\n RedisJobQueueConfig,\n} from './types.js';\nimport { QueueBackend } from './backend.js';\nimport { setLogContext } from './log-context.js';\nimport { createPool } from './db-util.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport { RedisBackend } from './backends/redis.js';\n\n/**\n * Initialize the job queue system.\n *\n * Defaults to PostgreSQL when `backend` is omitted.\n */\nexport const initJobQueue = <PayloadMap = any>(\n config: JobQueueConfig,\n): JobQueue<PayloadMap> => {\n const backendType = config.backend ?? 'postgres';\n setLogContext(config.verbose ?? false);\n\n let backend: QueueBackend;\n let pool: import('pg').Pool | undefined;\n\n if (backendType === 'postgres') {\n const pgConfig = config as PostgresJobQueueConfig;\n pool = createPool(pgConfig.databaseConfig);\n backend = new PostgresBackend(pool);\n } else if (backendType === 'redis') {\n const redisConfig = (config as RedisJobQueueConfig).redisConfig;\n // RedisBackend constructor will throw if ioredis is not installed\n backend = new RedisBackend(redisConfig);\n } else {\n throw new Error(`Unknown backend: ${backendType}`);\n }\n\n const requirePool = () => {\n if (!pool) {\n throw new Error(\n 'Wait/Token features require the PostgreSQL backend. Configure with backend: \"postgres\" to use these features.',\n );\n }\n return pool;\n };\n\n // Return the job queue API\n return {\n // Job queue operations\n addJob: withLogContext(\n (job: JobOptions<PayloadMap, any>) =>\n backend.addJob<PayloadMap, any>(job),\n config.verbose ?? false,\n ),\n getJob: withLogContext(\n (id: number) => backend.getJob<PayloadMap, any>(id),\n config.verbose ?? false,\n ),\n getJobsByStatus: withLogContext(\n (status: string, limit?: number, offset?: number) =>\n backend.getJobsByStatus<PayloadMap, any>(status, limit, offset),\n config.verbose ?? false,\n ),\n getAllJobs: withLogContext(\n (limit?: number, offset?: number) =>\n backend.getAllJobs<PayloadMap, any>(limit, offset),\n config.verbose ?? false,\n ),\n getJobs: withLogContext(\n (\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };\n },\n limit?: number,\n offset?: number,\n ) => backend.getJobs<PayloadMap, any>(filters, limit, offset),\n config.verbose ?? false,\n ),\n retryJob: (jobId: number) => backend.retryJob(jobId),\n cleanupOldJobs: (daysToKeep?: number) => backend.cleanupOldJobs(daysToKeep),\n cleanupOldJobEvents: (daysToKeep?: number) =>\n backend.cleanupOldJobEvents(daysToKeep),\n cancelJob: withLogContext(\n (jobId: number) => backend.cancelJob(jobId),\n config.verbose ?? false,\n ),\n editJob: withLogContext(\n <T extends JobType<PayloadMap>>(\n jobId: number,\n updates: import('./types.js').EditJobOptions<PayloadMap, T>,\n ) => backend.editJob(jobId, updates as import('./backend.js').JobUpdates),\n config.verbose ?? false,\n ),\n editAllPendingJobs: withLogContext(\n <T extends JobType<PayloadMap>>(\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: {\n values: string[];\n mode?: import('./types.js').TagQueryMode;\n };\n }\n | undefined,\n updates: import('./types.js').EditJobOptions<PayloadMap, T>,\n ) =>\n backend.editAllPendingJobs(\n filters,\n updates as import('./backend.js').JobUpdates,\n ),\n config.verbose ?? false,\n ),\n cancelAllUpcomingJobs: withLogContext(\n (filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };\n }) => backend.cancelAllUpcomingJobs(filters),\n config.verbose ?? false,\n ),\n reclaimStuckJobs: withLogContext(\n (maxProcessingTimeMinutes?: number) =>\n backend.reclaimStuckJobs(maxProcessingTimeMinutes),\n config.verbose ?? false,\n ),\n getJobsByTags: withLogContext(\n (tags: string[], mode = 'all', limit?: number, offset?: number) =>\n backend.getJobsByTags<PayloadMap, any>(tags, mode, limit, offset),\n config.verbose ?? false,\n ),\n\n // Job processing\n createProcessor: (\n handlers: JobHandlers<PayloadMap>,\n options?: ProcessorOptions,\n ) => createProcessor<PayloadMap>(backend, handlers, options),\n\n // Job events\n getJobEvents: withLogContext(\n (jobId: number) => backend.getJobEvents(jobId),\n config.verbose ?? false,\n ),\n\n // Wait / Token support (PostgreSQL-only for now)\n createToken: withLogContext(\n (options?: import('./types.js').CreateTokenOptions) =>\n createWaitpoint(requirePool(), null, options),\n config.verbose ?? false,\n ),\n completeToken: withLogContext(\n (tokenId: string, data?: any) =>\n completeWaitpoint(requirePool(), tokenId, data),\n config.verbose ?? false,\n ),\n getToken: withLogContext(\n (tokenId: string) => getWaitpoint(requirePool(), tokenId),\n config.verbose ?? false,\n ),\n expireTimedOutTokens: withLogContext(\n () => expireTimedOutWaitpoints(requirePool()),\n config.verbose ?? false,\n ),\n\n // Advanced access\n getPool: () => {\n if (backendType !== 'postgres') {\n throw new Error(\n 'getPool() is only available with the PostgreSQL backend.',\n );\n }\n return (backend as PostgresBackend).getPool();\n },\n getRedisClient: () => {\n if (backendType !== 'redis') {\n throw new Error(\n 'getRedisClient() is only available with the Redis backend.',\n );\n }\n return (backend as RedisBackend).getClient();\n },\n };\n};\n\nconst withLogContext =\n <T>(fn: (...args: any[]) => T, verbose: boolean) =>\n (...args: Parameters<typeof fn>): ReturnType<typeof fn> => {\n setLogContext(verbose);\n return fn(...args);\n };\n\nexport * from './types.js';\nexport { QueueBackend } from './backend.js';\nexport { PostgresBackend } from './backends/postgres.js';\nexport {\n validateHandlerSerializable,\n testHandlerSerialization,\n} from './handler-validation.js';\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/log-context.ts","../src/backends/postgres.ts","../src/queue.ts","../src/processor.ts","../src/db-util.ts","../src/backends/redis-scripts.ts","../src/backends/redis.ts","../src/cron.ts","../src/handler-validation.ts","../src/index.ts"],"names":["JobEventType","FailureReason","AsyncLocalStorage","randomUUID","Worker","fs","parse","Pool","createRequire","Cron","validateHandlerSerializable","nextRunAt"],"mappings":";;;;;;;;;;;;;;;;;AA0FO,IAAK,YAAA,qBAAAA,aAAAA,KAAL;AACL,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,cAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AATA,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;AAoBL,IAAK,aAAA,qBAAAC,cAAAA,KAAL;AACL,EAAAA,eAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,eAAA,cAAA,CAAA,GAAe,eAAA;AACf,EAAAA,eAAA,WAAA,CAAA,GAAY,YAAA;AAHF,EAAA,OAAAA,cAAAA;AAAA,CAAA,EAAA,aAAA,IAAA,EAAA;AA2OL,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAGpC,WAAA,CACkB,IAAA,EACA,SAAA,EACA,OAAA,EACA,QAAA,EAChB;AACA,IAAA,KAAA,CAAM,YAAY,CAAA;AALF,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AANlB,IAAA,IAAA,CAAS,YAAA,GAAe,IAAA;AAStB,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AAAA;AAEhB;ACnWO,IAAM,UAAA,GAAa,IAAIC,6BAAA,EAE3B;AAEI,IAAM,aAAA,GAAgB,CAAC,OAAA,KAAqB;AACjD,EAAA,UAAA,CAAW,SAAA,CAAU,EAAE,OAAA,EAAS,CAAA;AAClC,CAAA;AAEO,IAAM,gBAAgB,MAAM;AACjC,EAAA,OAAO,WAAW,QAAA,EAAS;AAC7B,CAAA;AAEO,IAAM,GAAA,GAAM,CAAC,OAAA,KAAoB;AACtC,EAAA,MAAM,UAAU,aAAA,EAAc;AAC9B,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,OAAA,CAAQ,IAAI,OAAO,CAAA;AAAA;AAEvB,CAAA;;;ACEO,IAAM,kBAAN,MAA8C;AAAA,EACnD,YAAoB,IAAA,EAAY;AAAZ,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAa;AAAA,EAGjC,OAAA,GAAgB;AACd,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA;AACd;AAAA,EAIA,MAAM,cAAA,CACJ,KAAA,EACA,SAAA,EACA,QAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,yEAAA,CAAA;AAAA,QACA,CAAC,OAAO,SAAA,EAAW,QAAA,GAAW,KAAK,SAAA,CAAU,QAAQ,IAAI,IAAI;AAAA,OAC/D;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE5D,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,aAAa,KAAA,EAAoC;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,KAAA;AAAA,QACvB,CAAA,sJAAA,CAAA;AAAA,QACA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,OAAO,GAAA,CAAI,IAAA;AAAA,KACb,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,MAAA,CAAkD;AAAA,IACtD,OAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA,GAAc,CAAA;AAAA,IACd,QAAA,GAAW,CAAA;AAAA,IACX,KAAA,GAAQ,IAAA;AAAA,IACR,SAAA,GAAY,MAAA;AAAA,IACZ,kBAAA,GAAqB,KAAA;AAAA,IACrB,IAAA,GAAO,MAAA;AAAA,IACP,cAAA,GAAiB;AAAA,GACnB,EAA+C;AAC7C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,MAAA;AACJ,MAAA,MAAM,UAAA,GAAa,iBACf,CAAA,0EAAA,CAAA,GACA,EAAA;AAEJ,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,UACpB,CAAA;AAAA;AAAA;AAAA,WAAA,EAGG,UAAU;AAAA,uBAAA,CAAA;AAAA,UAEb;AAAA,YACE,OAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,QAAA;AAAA,YACA,KAAA;AAAA,YACA,SAAA,IAAa,IAAA;AAAA,YACb,kBAAA,IAAsB,KAAA;AAAA,YACtB,IAAA,IAAQ,IAAA;AAAA,YACR,cAAA,IAAkB;AAAA;AACpB,SACF;AAAA,OACF,MAAO;AACL,QAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,UACpB,CAAA;AAAA;AAAA;AAAA,WAAA,EAGG,UAAU;AAAA,uBAAA,CAAA;AAAA,UAEb;AAAA,YACE,OAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,QAAA;AAAA,YACA,SAAA,IAAa,IAAA;AAAA,YACb,kBAAA,IAAsB,KAAA;AAAA,YACtB,IAAA,IAAQ,IAAA;AAAA,YACR,cAAA,IAAkB;AAAA;AACpB,SACF;AAAA;AAIF,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,IAAK,cAAA,EAAgB;AAC9C,QAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,KAAA;AAAA,UAC5B,CAAA,mDAAA,CAAA;AAAA,UACA,CAAC,cAAc;AAAA,SACjB;AACA,QAAA,IAAI,QAAA,CAAS,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC5B,UAAA,GAAA;AAAA,YACE,6BAA6B,cAAc,CAAA,sBAAA,EAAyB,SAAS,IAAA,CAAK,CAAC,EAAE,EAAE,CAAA,yBAAA;AAAA,WACzF;AACA,UAAA,OAAO,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,EAAA;AAAA;AAE1B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,8EAA8E,cAAc,CAAA,CAAA;AAAA,SAC9F;AAAA;AAGF,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAAE,EAAA;AAC7B,MAAA,GAAA;AAAA,QACE,CAAA,UAAA,EAAa,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA,EAAA,EAAK,KAAA,GAAQ,CAAA,MAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,SAAA,EAAY,QAAQ,CAAA,cAAA,EAAiB,WAAW,CAAA,UAAA,EAAa,OAAO,UAAU,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,cAAA,GAAiB,CAAA,kBAAA,EAAqB,cAAc,MAAM,EAAE,CAAA;AAAA,OAC3Q;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,OAAA,cAA2B;AAAA,QACnD,OAAA;AAAA,QACA,OAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,OAAO,KAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,CAAE,CAAA;AAChC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OACJ,EAAA,EAC0C;AAC1C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,oyBAAA,CAAA;AAAA,QACA,CAAC,EAAE;AAAA,OACL;AAEA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAC5B,QAAA,GAAA,CAAI,CAAA,IAAA,EAAO,EAAE,CAAA,UAAA,CAAY,CAAA;AACzB,QAAA,OAAO,IAAA;AAAA;AAGT,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,EAAE,CAAA,CAAE,CAAA;AACrB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AACzB,MAAA,OAAO;AAAA,QACL,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACvC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,eAAA,CACJ,MAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,80BAAA,CAAA;AAAA,QACA,CAAC,MAAA,EAAQ,KAAA,EAAO,MAAM;AAAA,OACxB;AACA,MAAA,GAAA,CAAI,SAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,gBAAA,EAAmB,MAAM,CAAA,CAAE,CAAA;AAC1D,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,MAAM,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACtD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,UAAA,CACJ,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA,4zBAAA,CAAA;AAAA,QACA,CAAC,OAAO,MAAM;AAAA,OAChB;AACA,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,WAAA,CAAa,CAAA;AAC5C,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI;AAAA,OAC1B,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,wBAAA,EAA2B,KAAK,CAAA,CAAE,CAAA;AACtC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CACJ,OAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ,CAAA,sxBAAA,CAAA;AACZ,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,MAAM,QAAkB,EAAC;AACzB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AACtC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AACtC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IACE,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,KACxB,QAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,IACpB,OAAA,CAAQ,KAAA,CAAM,GAAA,KAAQ,UACtB,OAAA,CAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,IACrB,OAAA,CAAQ,KAAA,CAAM,QAAQ,KAAA,CAAA,IACtB,OAAA,CAAQ,KAAA,CAAM,EAAA,KAAO,KAAA,CAAA,CAAA,EACvB;AACA,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AAOpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AACpC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AAClC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAA,CAAG,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,QAAA,EAAU,CAAA,CAAE,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AAGF,QAAA,IAAI,OAAA,CAAQ,WAAW,KAAA,CAAA,EAAW;AAChC,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,QAAA,EAAU,CAAA,CAAE,CAAA;AAChC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA;AAC5B;AAEF,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,KAAA,IAAS,CAAA,OAAA,EAAU,KAAA,CAAM,IAAA,CAAK,OAAO,CAAC,CAAA,CAAA;AAAA;AAExC,MAAA,QAAA,GAAW,OAAO,MAAA,GAAS,CAAA;AAE3B,MAAA,KAAA,IAAS,4BAA4B,QAAA,EAAU,CAAA,CAAA;AAE/C,MAAA,IAAI,CAAC,SAAS,MAAA,EAAQ;AACpB,QAAA,KAAA,IAAS,YAAY,QAAQ,CAAA,CAAA;AAC7B,QAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AAAA,OAC3B,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA;AAEnB,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,KAAA,CAAO,CAAA;AACtC,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,KAAK,CAAA,CAAE,CAAA;AAClC,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,cACJ,IAAA,EACA,IAAA,GAAqB,OACrB,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ,CAAA;AAAA,uBAAA,CAAA;AAEZ,MAAA,IAAI,SAAgB,EAAC;AACrB,MAAA,QAAQ,IAAA;AAAM,QACZ,KAAK,OAAA;AACH,UAAA,KAAA,IAAS,kBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,KAAA;AACH,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,KAAA;AACH,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF,KAAK,MAAA;AACH,UAAA,KAAA,IAAS,yBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AACd,UAAA;AAAA,QACF;AACE,UAAA,KAAA,IAAS,mBAAA;AACT,UAAA,MAAA,GAAS,CAAC,IAAI,CAAA;AAAA;AAElB,MAAA,KAAA,IAAS,8CAAA;AACT,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AACzB,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA;AAAA,QACE,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,cAAA,EAAiB,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,QAAA,EAAW,IAAI,CAAA,CAAA;AAAA,OACjF;AACA,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI,kBAAA;AAAA,QACxB,eAAe,GAAA,CAAI;AAAA,OACrB,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA;AAAA,QACE,CAAA,2BAAA,EAA8B,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,QAAA,EAAW,IAAI,MAAM,KAAK,CAAA;AAAA,OAC9E;AACA,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,YAAA,CACJ,QAAA,EACA,SAAA,GAAY,IACZ,OAAA,EACqC;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAE1B,MAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,MAAA,MAAM,MAAA,GAAgB,CAAC,QAAA,EAAU,SAAS,CAAA;AAC1C,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC1B,UAAA,aAAA,GAAgB,CAAA,uBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,SACrB,MAAO;AACL,UAAA,aAAA,GAAgB,CAAA,kBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA;AACrB;AAGF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAAA,EA0BI,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAOjB;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,gBAAA,CAAkB,CAAA;AACjD,MAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAG3B,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC1B,QAAA,MAAM,IAAA,CAAK,oBAAA;AAAA,UACT,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,YACxB,OAAO,GAAA,CAAI,EAAA;AAAA,YACX,SAAA,EAAA,YAAA;AAAA,WACF,CAAE;AAAA,SACJ;AAAA;AAGF,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC/B,GAAG,GAAA;AAAA,QACH,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,oBAAoB,GAAA,CAAI;AAAA,OAC1B,CAAE,CAAA;AAAA,aACK,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,0BAAA,EAA6B,KAAK,CAAA,CAAE,CAAA;AACxC,MAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,YAAY,KAAA,EAA8B;AAC9C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,mEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC7C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,KAAA,EACA,aAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAaA;AAAA,UACE,KAAA;AAAA,UACA,KAAK,SAAA,CAAU;AAAA,YACb;AAAA,cACE,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,cACtC,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACpC,WACD,CAAA;AAAA,UACD,aAAA,IAAiB;AAAA;AACnB,OACF;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,wEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,QAAA,eAA4B;AAAA,QACpD,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,QACtC;AAAA,OACD,CAAA;AACD,MAAA,GAAA,CAAI,CAAA,WAAA,EAAc,KAAK,CAAA,CAAE,CAAA;AAAA,aAClB,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,EAAA,EAAK,GAAG,CAAA,CAAE,CAAA;AACxC,MAAA,MAAM,GAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,WAAW,KAAA,EAA8B;AAC7C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAKA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE/C,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,cAAA,CAAe,KAAA,EAAe,QAAA,EAAiC;AACnE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,oEAAA,CAAA;AAAA,QACA,CAAC,OAAO,QAAQ;AAAA,OAClB;AACA,MAAA,GAAA,CAAI,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,aAC9C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,KAE1D,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,SAAS,KAAA,EAA8B;AAC3C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAUA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,QAAA,GAAA;AAAA,UACE,OAAO,KAAK,CAAA,wEAAA;AAAA,SACd;AAAA;AAEF,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,SAAA,eAA2B;AACrD,MAAA,GAAA,CAAI,CAAA,YAAA,EAAe,KAAK,CAAA,CAAE,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,mBAAA,EAAsB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,UAAU,KAAA,EAA8B;AAC5C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,KAAK;AAAA,OACR;AACA,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC7C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,sBAAsB,OAAA,EAAuC;AACjE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ;AAAA;AAAA;AAAA,gCAAA,CAAA;AAIZ,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IAAW,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,EAAU;AAC5C,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AACpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,IAAS,gBAAgB,QAAA,EAAU,CAAA,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,IAAS,sBAAsB,QAAA,EAAU,CAAA,CAAA,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AACF;AAEF,MAAA,KAAA,IAAS,gBAAA;AACT,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,CAAA,KAAA,CAAO,CAAA;AACvC,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,CAAE,CAAA;AAC9C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAoC;AAC/D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,eAAyB,EAAC;AAChC,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AAEf,MAAA,IAAI,OAAA,CAAQ,YAAY,KAAA,CAAA,EAAW;AACjC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AAC5C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,MAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,CAAA,EAAW;AACrC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,gBAAA,EAAmB,QAAA,EAAU,CAAA,CAAE,CAAA;AACjD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,WAAW,CAAA;AAAA;AAEjC,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,UAAU,KAAA,CAAA,EAAW;AAC/B,QAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,UAAA,YAAA,CAAa,KAAK,CAAA,cAAA,CAAgB,CAAA;AAAA,SACpC,MAAO;AACL,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AAC3C,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA;AAC3B;AAEF,MAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,EAAW;AACnC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAE,CAAA;AAC/C,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,IAAI,CAAA;AAAA;AAEvC,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,CAAA,EAAW;AAC9B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AACzC,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAA,IAAQ,IAAI,CAAA;AAAA;AAGlC,MAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,QAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,QAAA;AAAA;AAGF,MAAA,YAAA,CAAa,KAAK,CAAA,kBAAA,CAAoB,CAAA;AACtC,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAEjB,MAAA,MAAM,KAAA,GAAQ;AAAA;AAAA,YAAA,EAEN,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC;AAAA,oBAAA,EACf,QAAQ,CAAA;AAAA,MAAA,CAAA;AAGxB,MAAA,MAAM,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAEhC,MAAA,MAAM,WAAgB,EAAC;AACvB,MAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,CAAA,EAAW,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAC9D,MAAA,IAAI,QAAQ,WAAA,KAAgB,KAAA,CAAA;AAC1B,QAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AACjC,MAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,KAAA,CAAA,EAAW,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAChE,MAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,KAAA,CAAA,EAAW,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAC1D,MAAA,IAAI,QAAQ,SAAA,KAAc,KAAA,CAAA;AACxB,QAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAC/B,MAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,KAAA,CAAA,EAAW,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAExD,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,KAAA,EAAA,QAAA,eAA4B,QAAQ,CAAA;AAC9D,MAAA,GAAA,CAAI,cAAc,KAAK,CAAA,EAAA,EAAK,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA,aAC/C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,kBAAA,CACJ,OAAA,GAAkC,MAAA,EAClC,OAAA,EACiB;AACjB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,eAAyB,EAAC;AAChC,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AAEf,MAAA,IAAI,OAAA,CAAQ,YAAY,KAAA,CAAA,EAAW;AACjC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AAC5C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,MAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,CAAA,EAAW;AACrC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,gBAAA,EAAmB,QAAA,EAAU,CAAA,CAAE,CAAA;AACjD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,WAAW,CAAA;AAAA;AAEjC,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,UAAU,KAAA,CAAA,EAAW;AAC/B,QAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,UAAA,YAAA,CAAa,KAAK,CAAA,cAAA,CAAgB,CAAA;AAAA,SACpC,MAAO;AACL,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,UAAA,EAAa,QAAA,EAAU,CAAA,CAAE,CAAA;AAC3C,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA;AAC3B;AAEF,MAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,EAAW;AACnC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAE,CAAA;AAC/C,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,IAAI,CAAA;AAAA;AAEvC,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,CAAA,EAAW;AAC9B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AACzC,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAA,IAAQ,IAAI,CAAA;AAAA;AAGlC,MAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,QAAA,GAAA,CAAI,CAAA,kCAAA,CAAoC,CAAA;AACxC,QAAA,OAAO,CAAA;AAAA;AAGT,MAAA,YAAA,CAAa,KAAK,CAAA,kBAAA,CAAoB,CAAA;AAEtC,MAAA,IAAI,KAAA,GAAQ;AAAA;AAAA,YAAA,EAEJ,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC;AAAA,gCAAA,CAAA;AAG/B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,QAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,UAAA,KAAA,IAAS,oBAAoB,QAAA,EAAU,CAAA,CAAA;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,QAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,UAAA,IAAI,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACjC,YAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,YAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAC3B,MAAA,IAAW,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,EAAU;AAC5C,YAAA,MAAM,MAAM,OAAA,CAAQ,KAAA;AACpB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AAEpB,YAAA,IAAI,IAAI,GAAA,EAAK;AACX,cAAA,KAAA,IAAS,mBAAmB,QAAA,EAAU,CAAA,CAAA;AACtC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA;AAErB,YAAA,IAAI,IAAI,EAAA,EAAI;AACV,cAAA,KAAA,IAAS,kBAAkB,QAAA,EAAU,CAAA,CAAA;AACrC,cAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA;AACpB;AACF;AAEF,QAAA,IACE,OAAA,CAAQ,QACR,OAAA,CAAQ,IAAA,CAAK,UACb,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAC7B;AACA,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,IAAQ,KAAA;AAClC,UAAA,MAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,MAAA;AAC/B,UAAA,QAAQ,IAAA;AAAM,YACZ,KAAK,OAAA;AACH,cAAA,KAAA,IAAS,gBAAgB,QAAA,EAAU,CAAA,CAAA;AACnC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,KAAA;AACH,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF,KAAK,MAAA;AACH,cAAA,KAAA,IAAS,sBAAsB,QAAA,EAAU,CAAA,CAAA,CAAA;AACzC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AACrB,cAAA;AAAA,YACF;AACE,cAAA,KAAA,IAAS,iBAAiB,QAAA,EAAU,CAAA,CAAA;AACpC,cAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AACzB;AACF;AAEF,MAAA,KAAA,IAAS,gBAAA;AAET,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,MAAM,WAAA,GAAc,OAAO,QAAA,IAAY,CAAA;AAEvC,MAAA,MAAM,WAAgB,EAAC;AACvB,MAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,CAAA,EAAW,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAC9D,MAAA,IAAI,QAAQ,WAAA,KAAgB,KAAA,CAAA;AAC1B,QAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AACjC,MAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,KAAA,CAAA,EAAW,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAChE,MAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,KAAA,CAAA,EAAW,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAC1D,MAAA,IAAI,QAAQ,SAAA,KAAc,KAAA,CAAA;AACxB,QAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAC/B,MAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,KAAA,CAAA,EAAW,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAExD,MAAA,KAAA,MAAW,GAAA,IAAO,OAAO,IAAA,EAAM;AAC7B,QAAA,MAAM,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,EAAA,EAAA,QAAA,eAAyB,QAAQ,CAAA;AAAA;AAGjE,MAAA,GAAA,CAAI,UAAU,WAAW,CAAA,eAAA,EAAkB,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AACrE,MAAA,OAAO,WAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,cAAA,CAAe,UAAA,GAAa,EAAA,EAAqB;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAMA,CAAC,UAAU;AAAA,OACb;AACA,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,SAAA,CAAW,CAAA;AACzC,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,mBAAA,CAAoB,UAAA,GAAa,EAAA,EAAqB;AAC1D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,QAKA,CAAC,UAAU;AAAA,OACb;AACA,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,eAAA,CAAiB,CAAA;AAC/C,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,CAAE,CAAA;AAChD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF,EAEA,MAAM,gBAAA,CAAiB,wBAAA,GAA2B,EAAA,EAAqB;AACrE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AAAA,QAUA,CAAC,wBAAwB;AAAA,OAC3B;AACA,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,CAAA,WAAA,CAAa,CAAA;AAC7C,MAAA,OAAO,OAAO,QAAA,IAAY,CAAA;AAAA,aACnB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,KAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,qBACZ,MAAA,EACe;AACf,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACzB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,SAAmB,EAAC;AAC1B,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AACf,MAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,QAAA,MAAA,CAAO,KAAK,CAAA,EAAA,EAAK,QAAA,EAAU,MAAM,QAAA,EAAU,CAAA,GAAA,EAAM,UAAU,CAAA,CAAA,CAAG,CAAA;AAC9D,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,KAAA,CAAM,KAAA;AAAA,UACN,KAAA,CAAM,SAAA;AAAA,UACN,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAQ,CAAA,GAAI;AAAA,SACpD;AAAA;AAEF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,6DAAA,EAAgE,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,QACjF;AAAA,OACF;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,CAAE,CAAA;AAAA,KAElD,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA;AAAA,EAKA,MAAM,gBAAgB,KAAA,EAA2C;AAC/D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,CAAA;AAAA,QAMA;AAAA,UACE,KAAA,CAAM,YAAA;AAAA,UACN,KAAA,CAAM,cAAA;AAAA,UACN,KAAA,CAAM,OAAA;AAAA,UACN,KAAA,CAAM,OAAA;AAAA,UACN,KAAA,CAAM,WAAA;AAAA,UACN,KAAA,CAAM,QAAA;AAAA,UACN,KAAA,CAAM,SAAA;AAAA,UACN,KAAA,CAAM,kBAAA;AAAA,UACN,MAAM,IAAA,IAAQ,IAAA;AAAA,UACd,KAAA,CAAM,QAAA;AAAA,UACN,KAAA,CAAM,YAAA;AAAA,UACN,KAAA,CAAM;AAAA;AACR,OACF;AACA,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAAE,EAAA;AAC1B,MAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,EAAE,CAAA,GAAA,EAAM,KAAA,CAAM,YAAY,CAAA,CAAA,CAAG,CAAA;AACxD,MAAA,OAAO,EAAA;AAAA,aACA,KAAA,EAAY;AAEnB,MAAA,IAAI,KAAA,EAAO,SAAS,OAAA,EAAS;AAC3B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,yBAAA,EAA4B,MAAM,YAAY,CAAA,gBAAA;AAAA,SAChD;AAAA;AAEF,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,gBAAgB,EAAA,EAAgD;AACpE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAAA,CAAA;AAAA,QASA,CAAC,EAAE;AAAA,OACL;AACA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACrC,MAAA,OAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,aACb,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACjD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,sBACJ,IAAA,EACoC;AACpC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAAA,CAAA;AAAA,QASA,CAAC,IAAI;AAAA,OACP;AACA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACrC,MAAA,OAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,aACb,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qCAAA,EAAwC,IAAI,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AAC7D,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,kBACJ,MAAA,EAC+B;AAC/B,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,GAAQ,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAA,CAAA;AASZ,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAA,IAAS,CAAA,kBAAA,CAAA;AACT,QAAA,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA;AAEpB,MAAA,KAAA,IAAS,CAAA,wBAAA,CAAA;AACT,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAO,MAAM,CAAA;AAC/C,MAAA,OAAO,MAAA,CAAO,IAAA;AAAA,aACP,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,8BAAA,EAAiC,KAAK,CAAA,CAAE,CAAA;AAC5C,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,mBAAmB,EAAA,EAA2B;AAClD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA,CAAM,CAAA,wCAAA,CAAA,EAA4C,CAAC,EAAE,CAAC,CAAA;AACnE,MAAA,GAAA,CAAI,CAAA,sBAAA,EAAyB,EAAE,CAAA,CAAE,CAAA;AAAA,aAC1B,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAClD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,kBAAkB,EAAA,EAA2B;AACjD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,6EAAA,CAAA;AAAA,QACA,CAAC,EAAE;AAAA,OACL;AACA,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,CAAE,CAAA;AAAA,aACzB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACjD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,mBAAmB,EAAA,EAA2B;AAClD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA,6EAAA,CAAA;AAAA,QACA,CAAC,EAAE;AAAA,OACL;AACA,MAAA,GAAA,CAAI,CAAA,sBAAA,EAAyB,EAAE,CAAA,CAAE,CAAA;AAAA,aAC1B,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAClD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAGA,MAAM,gBAAA,CACJ,EAAA,EACA,OAAA,EACA,SAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,eAAyB,EAAC;AAChC,MAAA,MAAM,SAAgB,EAAC;AACvB,MAAA,IAAI,QAAA,GAAW,CAAA;AAEf,MAAA,IAAI,OAAA,CAAQ,mBAAmB,KAAA,CAAA,EAAW;AACxC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,mBAAA,EAAsB,QAAA,EAAU,CAAA,CAAE,CAAA;AACpD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,cAAc,CAAA;AAAA;AAEpC,MAAA,IAAI,OAAA,CAAQ,YAAY,KAAA,CAAA,EAAW;AACjC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,WAAA,EAAc,QAAA,EAAU,CAAA,CAAE,CAAA;AAC5C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,OAAO,CAAA;AAAA;AAE7B,MAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,CAAA,EAAW;AACrC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,gBAAA,EAAmB,QAAA,EAAU,CAAA,CAAE,CAAA;AACjD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,WAAW,CAAA;AAAA;AAEjC,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,EAAW;AACnC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,cAAA,EAAiB,QAAA,EAAU,CAAA,CAAE,CAAA;AAC/C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,SAAS,CAAA;AAAA;AAE/B,MAAA,IAAI,OAAA,CAAQ,uBAAuB,KAAA,CAAA,EAAW;AAC5C,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,yBAAA,EAA4B,QAAA,EAAU,CAAA,CAAE,CAAA;AAC1D,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,kBAAkB,CAAA;AAAA;AAExC,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,CAAA,EAAW;AAC9B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,QAAA,EAAW,QAAA,EAAU,CAAA,CAAE,CAAA;AACzC,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,IAAI,CAAA;AAAA;AAE1B,MAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,CAAA,EAAW;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,YAAA,EAAe,QAAA,EAAU,CAAA,CAAE,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA;AAE9B,MAAA,IAAI,OAAA,CAAQ,iBAAiB,KAAA,CAAA,EAAW;AACtC,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,iBAAA,EAAoB,QAAA,EAAU,CAAA,CAAE,CAAA;AAClD,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,YAAY,CAAA;AAAA;AAElC,MAAA,IAAI,cAAc,KAAA,CAAA,EAAW;AAC3B,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,eAAA,EAAkB,QAAA,EAAU,CAAA,CAAE,CAAA;AAChD,QAAA,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA;AAGvB,MAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,QAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,EAAE,CAAA,CAAE,CAAA;AACjD,QAAA;AAAA;AAGF,MAAA,YAAA,CAAa,KAAK,CAAA,kBAAA,CAAoB,CAAA;AACtC,MAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAEd,MAAA,MAAM,QAAQ,CAAA,0BAAA,EAA6B,YAAA,CAAa,KAAK,IAAI,CAAC,gBAAgB,QAAQ,CAAA,CAAA;AAC1F,MAAA,MAAM,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAChC,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,CAAE,CAAA;AAAA,aACzB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,EAAE,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACjD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAA,GAAqD;AACzD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAAA;AAAA,OAcF;AACA,MAAA,GAAA,CAAI,CAAA,MAAA,EAAS,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,mBAAA,CAAqB,CAAA;AACpD,MAAA,OAAO,MAAA,CAAO,IAAA;AAAA,aACP,KAAA,EAAY;AAEnB,MAAA,IAAI,KAAA,EAAO,SAAS,OAAA,EAAS;AAC3B,QAAA,GAAA,CAAI,4DAA4D,CAAA;AAChE,QAAA,OAAO,EAAC;AAAA;AAEV,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,CAAE,CAAA;AAChD,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,8BAAA,CACJ,EAAA,EACA,cAAA,EACA,WACA,SAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAA,CAAA;AAAA,QAMA,CAAC,EAAA,EAAI,cAAA,EAAgB,SAAA,EAAW,SAAS;AAAA,OAC3C;AACA,MAAA,GAAA;AAAA,QACE,CAAA,sBAAA,EAAyB,EAAE,CAAA,YAAA,EAAe,SAAS,eAAe,SAAA,EAAW,WAAA,MAAiB,MAAM,CAAA;AAAA,OACtG;AAAA,aACO,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,6BAAA,EAAgC,EAAE,CAAA,gBAAA,EAAmB,KAAK,CAAA,CAAE,CAAA;AAChE,MAAA,MAAM,KAAA;AAAA,KACR,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AACF;AAAA,EAIA,MAAM,+BAAA,CACJ,MAAA,EACA,OAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,EAAQ;AACvC,IAAA,IAAI;AACF,MAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,MAAA,MAAM,MAAA,GAAgB,CAAC,MAAM,CAAA;AAC7B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC1B,UAAA,aAAA,GAAgB,CAAA,uBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,SACrB,MAAO;AACL,UAAA,aAAA,GAAgB,CAAA,kBAAA,CAAA;AAChB,UAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA;AACrB;AAEF,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,oEAAoE,aAAa,CAAA,CAAA;AAAA,QACjF;AAAA,OACF;AAAA,KACF,SAAE;AACA,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AACjB;AAEJ;ACr4CO,IAAM,cAAA,GAAiB,OAC5B,IAAA,EACA,KAAA,EACA,SAAA,EACA,QAAA,KAEA,IAAI,eAAA,CAAgB,IAAI,CAAA,CAAE,cAAA,CAAe,KAAA,EAAO,WAAW,QAAQ,CAAA;AA6L9D,IAAM,OAAA,GAAU,OACrB,IAAA,EACA,KAAA,EACA,OAAA,KAKkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,MAWA;AAAA,QACE,KAAA;AAAA,QACA,QAAQ,SAAA,IAAa,IAAA;AAAA,QACrB,QAAQ,WAAA,IAAe,IAAA;AAAA,QACvB,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,QAAQ;AAAA;AACjC,KACF;AACA,IAAA,IAAI,MAAA,CAAO,aAAa,CAAA,EAAG;AACzB,MAAA,GAAA;AAAA,QACE,OAAO,KAAK,CAAA,iFAAA;AAAA,OACd;AACA,MAAA;AAAA;AAEF,IAAA,MAAM,cAAA,CAAe,MAAM,KAAA,EAAA,SAAA,gBAA6B;AAAA,MACtD,SAAA,EAAW,OAAA,CAAQ,SAAA,EAAW,WAAA,EAAY,IAAK,IAAA;AAAA,MAC/C,WAAA,EAAa,QAAQ,WAAA,IAAe;AAAA,KACrC,CAAA;AACD,IAAA,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,eAAA,CAAiB,CAAA;AAAA,WAC1B,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAK,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AACrD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAMO,IAAM,cAAA,GAAiB,OAC5B,IAAA,EACA,KAAA,EACA,QAAA,KACkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,KAAA;AAAA,MACX,CAAA,qEAAA,CAAA;AAAA,MACA,CAAC,KAAA,EAAO,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC;AAAA,KAClC;AAAA,WACO,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,iCAAA,EAAoC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,GAE3D,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AASA,IAAM,cAAA,GAAiB,GAAA,GAAM,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAE5C,SAAS,mBAAmB,OAAA,EAAyB;AACnD,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,kBAAkB,CAAA;AAC9C,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,4BAA4B,OAAO,CAAA,iDAAA;AAAA,KACrC;AAAA;AAEF,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,CAAC,GAAG,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AACpB,EAAA,IAAI,EAAA;AACJ,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,GAAA;AACb,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,QAAQ,EAAA,GAAK,GAAA;AAClB,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AACvB,MAAA;AAAA,IACF,KAAK,GAAA;AACH,MAAA,EAAA,GAAK,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAC5B,MAAA;AAAA,IACF;AACE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA;AAErD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,IAAK,KAAK,cAAA,EAAgB;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kBAAkB,OAAO,CAAA,4CAAA;AAAA,KAC3B;AAAA;AAEF,EAAA,OAAO,EAAA;AACT;AAWO,IAAM,eAAA,GAAkB,OAC7B,IAAA,EACA,KAAA,EACA,OAAA,KAC4B;AAC5B,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAK,CAAA,GAAA,EAAMC,iBAAA,EAAY,CAAA,CAAA;AAC7B,IAAA,IAAI,SAAA,GAAyB,IAAA;AAE7B,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,MAAM,EAAA,GAAK,kBAAA,CAAmB,OAAA,CAAQ,OAAO,CAAA;AAC7C,MAAA,SAAA,GAAY,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,KAAQ,EAAE,CAAA;AAAA;AAGtC,IAAA,MAAM,MAAA,CAAO,KAAA;AAAA,MACX,CAAA,gGAAA,CAAA;AAAA,MACA,CAAC,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,QAAQ,IAAI;AAAA,KAC9C;AAEA,IAAA,GAAA,CAAI,CAAA,kBAAA,EAAqB,EAAE,CAAA,SAAA,EAAY,KAAK,CAAA,CAAE,CAAA;AAC9C,IAAA,OAAO,EAAE,EAAA,EAAG;AAAA,WACL,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,0BAAA,EAA6B,KAAK,CAAA,CAAE,CAAA;AACxC,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAOO,IAAM,iBAAA,GAAoB,OAC/B,IAAA,EACA,OAAA,EACA,IAAA,KACkB;AAClB,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAG1B,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,KAAA;AAAA,MAC5B,CAAA;AAAA;AAAA,uBAAA,CAAA;AAAA,MAGA,CAAC,SAAS,IAAA,IAAQ,IAAA,GAAO,KAAK,SAAA,CAAU,IAAI,IAAI,IAAI;AAAA,KACtD;AAEA,IAAA,IAAI,QAAA,CAAS,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAC9B,MAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,MAAA,GAAA,CAAI,CAAA,UAAA,EAAa,OAAO,CAAA,+BAAA,CAAiC,CAAA;AACzD,MAAA;AAAA;AAGF,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,MAAA;AAG/B,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAM,MAAA,CAAO,KAAA;AAAA,QACX,CAAA;AAAA;AAAA,6CAAA,CAAA;AAAA,QAGA,CAAC,KAAK;AAAA,OACR;AAAA;AAGF,IAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,OAAO,CAAA,SAAA,EAAY,KAAK,CAAA,CAAE,CAAA;AAAA,WAC9C,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,IAAA,GAAA,CAAI,CAAA,2BAAA,EAA8B,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AACrD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAKO,IAAM,YAAA,GAAe,OAC1B,IAAA,EACA,OAAA,KACoC;AACpC,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B,CAAA,qKAAA,CAAA;AAAA,MACA,CAAC,OAAO;AAAA,KACV;AACA,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACrC,IAAA,OAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,WACb,KAAA,EAAO;AACd,IAAA,GAAA,CAAI,CAAA,wBAAA,EAA2B,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAClD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AAMO,IAAM,wBAAA,GAA2B,OAAO,IAAA,KAAgC;AAC7E,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAG1B,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,MAC1B,CAAA;AAAA;AAAA;AAAA,2BAAA;AAAA,KAIF;AAGA,IAAA,KAAA,MAAW,GAAA,IAAO,OAAO,IAAA,EAAM;AAC7B,MAAA,IAAI,GAAA,CAAI,UAAU,IAAA,EAAM;AACtB,QAAA,MAAM,MAAA,CAAO,KAAA;AAAA,UACX,CAAA;AAAA;AAAA,+CAAA,CAAA;AAAA,UAGA,CAAC,IAAI,MAAM;AAAA,SACb;AAAA;AACF;AAGF,IAAA,MAAM,MAAA,CAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,IAAY,CAAA;AACjC,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA,GAAA,CAAI,CAAA,QAAA,EAAW,KAAK,CAAA,qBAAA,CAAuB,CAAA;AAAA;AAE7C,IAAA,OAAO,KAAA;AAAA,WACA,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,CAAO,MAAM,UAAU,CAAA;AAC7B,IAAA,GAAA,CAAI,CAAA,qCAAA,EAAwC,KAAK,CAAA,CAAE,CAAA;AACnD,IAAA,MAAM,KAAA;AAAA,GACR,SAAE;AACA,IAAA,MAAA,CAAO,OAAA,EAAQ;AAAA;AAEnB,CAAA;AC9cA,SAAS,eAAe,OAAA,EAAoC;AAC1D,EAAA,IAAI,mBAAmB,eAAA,EAAiB;AACtC,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA;AAEzB,EAAA,OAAO,IAAA;AACT;AAMA,SAAS,iBAAA,CACP,OAAA,EACA,KAAA,EACA,OAAA,EAIY;AACZ,EAAA,MAAM,SAAA,GAAY,MAChB,IAAI,KAAA;AAAA,IACF;AAAA,GACF;AACF,EAAA,OAAO;AAAA,IACL,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,GAAA,EAAK,OAAU,SAAA,EAAmB,EAAA,KAAqC;AAErE,MAAA,OAAO,EAAA,EAAG;AAAA,KACZ;AAAA,IACA,SAAS,YAAY;AACnB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,WAAW,YAAY;AACrB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,aAAa,YAAY;AACvB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,cAAc,YAAY;AACxB,MAAA,MAAM,SAAA,EAAU;AAAA,KAClB;AAAA,IACA,WAAA,EAAa,OAAO,OAAA,KAAoB;AACtC,MAAA,IAAI,OAAA,GAAU,KAAK,OAAA,GAAU,GAAA;AAC3B,QAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AACtD,MAAA,MAAM,QAAQ,cAAA,CAAe,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA;AACzD,GACF;AACF;AAMA,SAAS,2BAAA,CAGP,SAAoC,OAAA,EAAuB;AAC3D,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AAIvC,IAAA,IACE,aAAA,CAAc,SAAS,OAAO,CAAA,IAC9B,CAAC,aAAA,CAAc,KAAA,CAAM,oBAAoB,CAAA,EACzC;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yBAAyB,OAAO,CAAA,uIAAA;AAAA,OAElC;AAAA;AAKF,IAAA,IAAI,aAAA,CAAc,QAAA,CAAS,eAAe,CAAA,EAAG;AAC3C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yBAAyB,OAAO,CAAA,8HAAA;AAAA,OAElC;AAAA;AAKF,IAAA,IAAI;AACF,MAAA,IAAI,QAAA,CAAS,YAAY,aAAa,CAAA;AAAA,aAC/B,UAAA,EAAY;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sBAAA,EAAyB,OAAO,CAAA,wBAAA,EAA2B,UAAA,YAAsB,QAAQ,UAAA,CAAW,OAAA,GAAU,MAAA,CAAO,UAAU,CAAC,CAAA,kHAAA;AAAA,OAElI;AAAA;AACF,WACO,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,MAAM,KAAA;AAAA;AAER,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uDAAA,EAA0D,OAAO,CAAA,GAAA,EAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACtF;AAAA;AAEJ;AAgBA,eAAe,kBAAA,CAIb,OAAA,EACA,OAAA,EACA,SAAA,EACA,OAAA,EACe;AAEf,EAAA,2BAAA,CAA4B,SAAS,OAAO,CAAA;AAE5C,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AAItC,IAAA,MAAM,UAAA,GAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAsFnB,IAAA,MAAM,MAAA,GAAS,IAAIC,qBAAA,CAAO,UAAA,EAAY;AAAA,MACpC,IAAA,EAAM,IAAA;AAAA,MACN,UAAA,EAAY;AAAA,QACV,WAAA,EAAa,QAAQ,QAAA,EAAS;AAAA,QAC9B,OAAA;AAAA,QACA;AAAA;AACF,KACD,CAAA;AAED,IAAA,IAAI,QAAA,GAAW,KAAA;AAEf,IAAA,MAAA,CAAO,EAAA,CAAG,SAAA,EAAW,CAAC,OAAA,KAA2C;AAC/D,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AAEX,MAAA,IAAI,OAAA,CAAQ,SAAS,SAAA,EAAW;AAC9B,QAAA,OAAA,EAAQ;AAAA,OACV,MAAA,IAAW,OAAA,CAAQ,IAAA,KAAS,SAAA,EAAW;AACrC,QAAA,MAAM,eAAe,IAAI,KAAA;AAAA,UACvB,uBAAuB,SAAS,CAAA,iCAAA;AAAA,SAClC;AAEA,QAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,QAAA,MAAA,CAAO,YAAY,CAAA;AAAA,OACrB,MAAA,IAAW,OAAA,CAAQ,IAAA,KAAS,OAAA,EAAS;AACnC,QAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,OAAO,CAAA;AAC7C,QAAA,KAAA,CAAM,KAAA,GAAQ,QAAQ,KAAA,CAAM,KAAA;AAC5B,QAAA,KAAA,CAAM,IAAA,GAAO,QAAQ,KAAA,CAAM,IAAA;AAC3B,QAAA,MAAA,CAAO,KAAK,CAAA;AAAA;AACd,KACD,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAC,KAAA,KAAU;AAC5B,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,MAAA,CAAO,KAAK,CAAA;AAAA,KACb,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,IAAA,KAAS;AAC1B,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,IAAI,SAAS,CAAA,EAAG;AACd,QAAA,QAAA,GAAW,IAAA;AACX,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,8BAAA,EAAiC,IAAI,EAAE,CAAC,CAAA;AAAA;AAC3D,KACD,CAAA;AAGD,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,QAAA,GAAW,IAAA;AACX,QAAA,MAAA,CACG,SAAA,EAAU,CACV,IAAA,CAAK,MAAM;AACV,UAAA,MAAM,eAAe,IAAI,KAAA;AAAA,YACvB,uBAAuB,SAAS,CAAA,iCAAA;AAAA,WAClC;AAEA,UAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,UAAA,MAAA,CAAO,YAAY,CAAA;AAAA,SACpB,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,SACX,CAAA;AAAA;AACL,KACF,EAAG,YAAY,GAAG,CAAA;AAAA,GACnB,CAAA;AACH;AAKA,SAAS,mBAAmB,QAAA,EAA8B;AACxD,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,IAAI,QAAA,CAAS,OAAA,EAAS,EAAA,IAAM,QAAA,CAAS,OAAA,GAAU,GAAA;AAC/C,EAAA,IAAI,QAAA,CAAS,OAAA,EAAS,EAAA,IAAM,QAAA,CAAS,UAAU,EAAA,GAAK,GAAA;AACpD,EAAA,IAAI,SAAS,KAAA,EAAO,EAAA,IAAM,QAAA,CAAS,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AACrD,EAAA,IAAI,SAAS,IAAA,EAAM,EAAA,IAAM,SAAS,IAAA,GAAO,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACxD,EAAA,IAAI,QAAA,CAAS,OAAO,EAAA,IAAM,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC9D,EAAA,IAAI,QAAA,CAAS,QAAQ,EAAA,IAAM,QAAA,CAAS,SAAS,EAAA,GAAK,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACjE,EAAA,IAAI,QAAA,CAAS,OAAO,EAAA,IAAM,QAAA,CAAS,QAAQ,GAAA,GAAM,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAChE,EAAA,IAAI,MAAM,CAAA,EAAG;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA;AAEF,EAAA,OAAO,IAAI,IAAA,CAAK,GAAA,GAAM,EAAE,CAAA;AAC1B;AAsDA,eAAe,qBAAA,CACb,MACA,QAAA,EACe;AACf,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AAChC,IAAA,MAAM,KAAA,GAAQ,SAAS,GAAG,CAAA;AAC1B,IAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,SAAA,EAAW;AAE5D,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,UAAA,IAAc,KAAA,CAAM,SAAS,MAAA,EAAQ;AAEtD,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,EAAE,GAAG,KAAA,EAAO,WAAW,IAAA,EAAK;AAAA,KAC9C,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,OAAA,IAAW,MAAM,OAAA,EAAS;AAElD,MAAA,MAAM,EAAA,GAAK,MAAM,YAAA,CAAa,IAAA,EAAM,MAAM,OAAO,CAAA;AACjD,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,QAAA,CAAS,GAAG,CAAA,GAAI;AAAA,UACd,GAAG,KAAA;AAAA,UACH,SAAA,EAAW,IAAA;AAAA,UACX,QAAQ,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,GAAG,MAAA;AAAO,SACxC;AAAA,OACF,MAAA,IAAW,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AAC1C,QAAA,QAAA,CAAS,GAAG,CAAA,GAAI;AAAA,UACd,GAAG,KAAA;AAAA,UACH,SAAA,EAAW,IAAA;AAAA,UACX,MAAA,EAAQ,EAAE,EAAA,EAAI,KAAA,EAAO,OAAO,iBAAA;AAAkB,SAChD;AAAA;AACF;AAEF;AAEJ;AAKA,SAAS,gBAAA,CACP,OAAA,EACA,IAAA,EACA,KAAA,EACA,UACA,OAAA,EAIY;AAIZ,EAAA,IAAI,WAAA,GAAc,CAAA;AAElB,EAAA,MAAM,GAAA,GAAkB;AAAA,IACtB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,IAEnB,GAAA,EAAK,OAAU,QAAA,EAAkB,EAAA,KAAqC;AAEpE,MAAA,MAAM,MAAA,GAAS,SAAS,QAAQ,CAAA;AAChC,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,WAAA,EAAa;AAC9D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,QAAQ,CAAA,8BAAA,EAAiC,KAAK,CAAA,CAAE,CAAA;AAC7D,QAAA,OAAO,MAAA,CAAO,MAAA;AAAA;AAIhB,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,EAAG;AAGxB,MAAA,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAE,WAAA,EAAa,MAAM,MAAA,EAAO;AACjD,MAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAE1C,MAAA,OAAO,MAAA;AAAA,KACT;AAAA,IAEA,OAAA,EAAS,OAAO,QAAA,KAA0C;AACxD,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,UAAA,CAAY,CAAA;AACpE,QAAA;AAAA;AAIF,MAAA,MAAM,aAAA,GAAgB,mBAAmB,QAAQ,CAAA;AAGjD,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,IAAA,EAAM,UAAA,EAAY,WAAW,KAAA,EAAM;AAGzD,MAAA,MAAM,IAAI,UAAA,CAAW,UAAA,EAAY,aAAA,EAAe,QAAW,QAAQ,CAAA;AAAA,KACrE;AAAA,IAEA,SAAA,EAAW,OAAO,IAAA,KAA8B;AAC9C,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA,CAAI,CAAA,MAAA,EAAS,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,UAAA,CAAY,CAAA;AACpE,QAAA;AAAA;AAIF,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,IAAA,EAAM,MAAA,EAAQ,WAAW,KAAA,EAAM;AAGrD,MAAA,MAAM,IAAI,UAAA,CAAW,MAAA,EAAQ,IAAA,EAAM,QAAW,QAAQ,CAAA;AAAA,KACxD;AAAA,IAEA,WAAA,EAAa,OAAO,OAAA,KAAa;AAC/B,MAAA,MAAM,KAAA,GAAQ,MAAM,eAAA,CAAgB,IAAA,EAAM,OAAO,OAAO,CAAA;AACxD,MAAA,OAAO,KAAA;AAAA,KACT;AAAA,IAEA,YAAA,EAAc,OACZ,OAAA,KACgC;AAChC,MAAA,MAAM,OAAA,GAAU,UAAU,WAAA,EAAa,CAAA,CAAA;AAGvC,MAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,SAAA,EAAW;AAC5D,QAAA,GAAA;AAAA,UACE,CAAA,YAAA,EAAe,OAAO,CAAA,4BAAA,EAA+B,KAAK,CAAA,yBAAA;AAAA,SAC5D;AACA,QAAA,OAAO,MAAA,CAAO,MAAA;AAAA;AAIhB,MAAA,MAAM,EAAA,GAAK,MAAM,YAAA,CAAa,IAAA,EAAM,OAAO,CAAA;AAC3C,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,MAAM,MAAA,GAA6B;AAAA,UACjC,EAAA,EAAI,IAAA;AAAA,UACJ,QAAQ,EAAA,CAAG;AAAA,SACb;AACA,QAAA,QAAA,CAAS,OAAO,CAAA,GAAI;AAAA,UAClB,IAAA,EAAM,OAAA;AAAA,UACN,OAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX;AAAA,SACF;AACA,QAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA;AAET,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,MAAA,KAAW,WAAA,EAAa;AACnC,QAAA,MAAM,MAAA,GAA6B;AAAA,UACjC,EAAA,EAAI,KAAA;AAAA,UACJ,KAAA,EAAO;AAAA,SACT;AACA,QAAA,QAAA,CAAS,OAAO,CAAA,GAAI;AAAA,UAClB,IAAA,EAAM,OAAA;AAAA,UACN,OAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX;AAAA,SACF;AACA,QAAA,MAAM,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA;AAIT,MAAA,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,MAAM,OAAA,EAAS,OAAA,EAAS,WAAW,KAAA,EAAM;AAC/D,MAAA,MAAM,IAAI,UAAA,CAAW,OAAA,EAAS,MAAA,EAAW,SAAS,QAAQ,CAAA;AAAA,KAC5D;AAAA,IAEA,WAAA,EAAa,OAAO,OAAA,KAAoB;AACtC,MAAA,IAAI,OAAA,GAAU,KAAK,OAAA,GAAU,GAAA;AAC3B,QAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AACtD,MAAA,MAAM,QAAQ,cAAA,CAAe,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA;AACzD,GACF;AAEA,EAAA,OAAO,GAAA;AACT;AAKA,eAAsB,sBAAA,CAIpB,OAAA,EACA,GAAA,EACA,WAAA,EACe;AACf,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AAEvC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,OAAA,CAAQ,+BAAA;AAAA,MACZ,CAAA,oCAAA,EAAuC,IAAI,OAAO,CAAA,CAAA;AAAA,MAClD,GAAA,CAAI;AAAA,KACN;AACA,IAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,MACZ,GAAA,CAAI,EAAA;AAAA,MACJ,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA;AAAA,MAAA,YAAA;AAAA,KAEhE;AACA,IAAA;AAAA;AAIF,EAAA,MAAM,WAAgC,EAAE,GAAI,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG;AAGhE,EAAA,MAAM,IAAA,GAAO,eAAe,OAAO,CAAA;AAGnC,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA;AAAA,IAAK,CAAC,CAAA,KACjD,CAAA,CAAE,UAAA,CAAW,SAAS;AAAA,GACxB;AACA,EAAA,IAAI,kBAAkB,IAAA,EAAM;AAC1B,IAAA,MAAM,qBAAA,CAAsB,MAAM,QAAQ,CAAA;AAE1C,IAAA,MAAM,cAAA,CAAe,IAAA,EAAM,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA;AAAA;AAI7C,EAAA,MAAM,SAAA,GAAY,IAAI,SAAA,IAAa,MAAA;AACnC,EAAA,MAAM,kBAAA,GAAqB,IAAI,kBAAA,IAAsB,KAAA;AACrD,EAAA,IAAI,SAAA;AACJ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,IAAI;AAGF,IAAA,IAAI,kBAAA,IAAsB,SAAA,IAAa,SAAA,GAAY,CAAA,EAAG;AACpD,MAAA,MAAM,mBAAmB,OAAA,EAAS,GAAA,CAAI,OAAA,EAAS,SAAA,EAAW,IAAI,OAAO,CAAA;AAAA,KACvE,MAAO;AAEL,MAAA,IAAI,iBAAA;AAGJ,MAAA,IAAI,aAAA;AASJ,MAAA,MAAM,UAAA,GAAa,CAAC,EAAA,KAAe;AACjC,QAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,QAAA,SAAA,GAAY,WAAW,MAAM;AAE3B,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAI;AACF,cAAA,MAAM,YAAY,iBAAA,EAAkB;AACpC,cAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,GAAY,CAAA,EAAG;AAElD,gBAAA,OAAA,CAAQ,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA,CAAE,MAAM,MAAM;AAAA,iBAAE,CAAA;AACzC,gBAAA,UAAA,CAAW,SAAS,CAAA;AACpB,gBAAA;AAAA;AACF,qBACO,aAAA,EAAe;AACtB,cAAA,GAAA;AAAA,gBACE,CAAA,iCAAA,EAAoC,GAAA,CAAI,EAAE,CAAA,EAAA,EAAK,aAAa,CAAA;AAAA,eAC9D;AAAA;AAEF;AAGF,UAAA,UAAA,CAAW,KAAA,EAAM;AACjB,UAAA,MAAM,YAAA,GAAe,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,EAAE,CAAA,GAAA,CAAK,CAAA;AAE7D,UAAA,YAAA,CAAa,aAAA,GAAA,SAAA;AACb,UAAA,IAAI,aAAA,EAAe;AACjB,YAAA,aAAA,CAAc,YAAY,CAAA;AAAA;AAC5B,WACC,EAAE,CAAA;AAAA,OACP;AAEA,MAAA,MAAM,UAAA,GAAa,SAAA,IAAa,IAAA,IAAQ,SAAA,GAAY,CAAA;AAGpD,MAAA,MAAM,UAAU,UAAA,GACZ;AAAA,QACE,OAAA,EAAS,CAAC,EAAA,KAAgB;AACxB,UAAA,MAAM,WAAW,EAAA,IAAM,SAAA;AACvB,UAAA,IAAI,QAAA,IAAY,IAAA,IAAQ,QAAA,GAAW,CAAA,EAAG;AACpC,YAAA,UAAA,CAAW,QAAQ,CAAA;AAEnB,YAAA,OAAA,CAAQ,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA,CAAE,MAAM,MAAM;AAAA,aAAE,CAAA;AAAA;AAC3C,SACF;AAAA,QACA,SAAA,EAAW,CAAC,QAAA,KAAgC;AAC1C,UAAA,iBAAA,GAAoB,QAAA;AAAA;AACtB,OACF,GACA;AAAA,QACE,SAAS,MAAM;AACb,UAAA,GAAA,CAAI,sDAAsD,CAAA;AAAA,SAC5D;AAAA,QACA,WAAW,MAAM;AACf,UAAA,GAAA,CAAI,wDAAwD,CAAA;AAAA;AAC9D,OACF;AAGJ,MAAA,MAAM,GAAA,GAAM,IAAA,GACR,gBAAA,CAAiB,OAAA,EAAS,MAAM,GAAA,CAAI,EAAA,EAAI,QAAA,EAAU,OAAO,CAAA,GACzD,iBAAA,CAAkB,OAAA,EAAS,GAAA,CAAI,IAAI,OAAO,CAAA;AAG9C,MAAA,IAAI,kBAAA,IAAsB,CAAC,UAAA,EAAY;AACrC,QAAA,GAAA;AAAA,UACE,CAAA,mDAAA,EAAsD,IAAI,EAAE,CAAA,4BAAA;AAAA,SAC9D;AAAA;AAGF,MAAA,MAAM,aAAa,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,QAAQ,GAAG,CAAA;AAE9D,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,UACjB,UAAA;AAAA,UACA,IAAI,OAAA,CAAe,CAAC,CAAA,EAAG,MAAA,KAAW;AAChC,YAAA,aAAA,GAAgB,MAAA;AAChB,YAAA,UAAA,CAAW,SAAU,CAAA;AAAA,WACtB;AAAA,SACF,CAAA;AAAA,OACH,MAAO;AACL,QAAA,MAAM,UAAA;AAAA;AACR;AAEF,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AAGrC,IAAA,MAAM,OAAA,CAAQ,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA;AAAA,WACzB,KAAA,EAAO;AACd,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AAGrC,IAAA,IAAI,iBAAiB,UAAA,EAAY;AAC/B,MAAA,IAAI,CAAC,IAAA,EAAM;AAGT,QAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,UACZ,GAAA,CAAI,EAAA;AAAA,UACJ,IAAI,KAAA;AAAA,YACF;AAAA,WACF;AAAA,UAAA,eAAA;AAAA,SAEF;AACA,QAAA;AAAA;AAEF,MAAA,GAAA;AAAA,QACE,CAAA,IAAA,EAAO,GAAA,CAAI,EAAE,CAAA,qBAAA,EAAwB,MAAM,IAAI,CAAA,YAAA,EAAe,KAAA,CAAM,SAAA,EAAW,aAAY,IAAK,MAAM,CAAA,UAAA,EAAa,KAAA,CAAM,WAAW,MAAM,CAAA;AAAA,OAC5I;AACA,MAAA,MAAM,OAAA,CAAQ,IAAA,EAAM,GAAA,CAAI,EAAA,EAAI;AAAA,QAC1B,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,aAAa,KAAA,CAAM,OAAA;AAAA,QACnB,UAAU,KAAA,CAAM;AAAA,OACjB,CAAA;AACD,MAAA;AAAA;AAIF,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,qBAAA,EAAwB,GAAA,CAAI,EAAE,KAAK,KAAK,CAAA;AACtD,IAAA,IAAI,aAAA,GAAA,eAAA;AACJ,IAAA,IACE,SACA,OAAO,KAAA,KAAU,YACjB,eAAA,IAAmB,KAAA,IAClB,MAA4C,aAAA,KAAA,SAAA,gBAE7C;AACA,MAAA,aAAA,GAAA,SAAA;AAAA;AAEF,IAAA,MAAM,OAAA,CAAQ,OAAA;AAAA,MACZ,GAAA,CAAI,EAAA;AAAA,MACJ,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MACxD;AAAA,KACF;AAAA;AAEJ;AAKA,eAAsB,yBACpB,OAAA,EACA,QAAA,EACA,WACA,OAAA,EACA,WAAA,EACA,aACA,OAAA,EACiB;AACjB,EAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,YAAA;AAAA,IACzB,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,IAAI,CAAC,WAAA,IAAe,WAAA,IAAe,IAAA,CAAK,MAAA,EAAQ;AAE9C,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,IAAA,CAAK,IAAI,CAAC,GAAA,KAAQ,uBAAuB,OAAA,EAAS,GAAA,EAAK,WAAW,CAAC;AAAA,KACrE;AACA,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA;AAGd,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,IAAI,aAAa,IAAA,CAAK,MAAA,EAAQ,OAAO,OAAA,CAAQ,KAAK,MAAM,CAAA;AACxD,MAAA,OAAO,OAAA,GAAU,WAAA,IAAe,GAAA,GAAM,IAAA,CAAK,MAAA,EAAQ;AACjD,QAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAK,CAAA;AACtB,QAAA,OAAA,EAAA;AACA,QAAA,sBAAA,CAAuB,OAAA,EAAS,GAAA,EAAK,WAAW,CAAA,CAC7C,KAAK,MAAM;AACV,UAAA,OAAA,EAAA;AACA,UAAA,QAAA,EAAA;AACA,UAAA,IAAA,EAAK;AAAA,SACN,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,UAAA,OAAA,EAAA;AACA,UAAA,QAAA,EAAA;AACA,UAAA,IAAI,OAAA,EAAS;AACX,YAAA,OAAA,CAAQ,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA;AAE7D,UAAA,IAAA,EAAK;AAAA,SACN,CAAA;AAAA;AACL,KACF;AACA,IAAA,IAAA,EAAK;AAAA,GACN,CAAA;AACH;AAUO,IAAM,kBAAkB,CAC7B,OAAA,EACA,UACA,OAAA,GAA4B,IAC5B,aAAA,KACc;AACd,EAAA,MAAM;AAAA,IACJ,QAAA,GAAW,CAAA,OAAA,EAAU,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAAA,IAC/D,SAAA,GAAY,EAAA;AAAA,IACZ,YAAA,GAAe,GAAA;AAAA,IACf,UAAU,CAAC,KAAA,KAAiB,OAAA,CAAQ,KAAA,CAAM,wBAAwB,KAAK,CAAA;AAAA,IACvE,OAAA;AAAA,IACA,WAAA,GAAc;AAAA,GAChB,GAAI,OAAA;AAEJ,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,UAAA,GAAoC,IAAA;AACxC,EAAA,IAAI,mBAAA,GAA8C,IAAA;AAElD,EAAA,aAAA,CAAc,OAAA,CAAQ,WAAW,KAAK,CAAA;AAEtC,EAAA,MAAM,cAAc,YAA6B;AAC/C,IAAA,IAAI,CAAC,SAAS,OAAO,CAAA;AAGrB,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IAAI;AACF,QAAA,MAAM,aAAA,EAAc;AAAA,eACb,SAAA,EAAW;AAClB,QAAA,GAAA,CAAI,CAAA,0BAAA,EAA6B,SAAS,CAAA,CAAE,CAAA;AAC5C,QAAA,IAAI,OAAA,EAAS;AACX,UAAA,OAAA;AAAA,YACE,qBAAqB,KAAA,GACjB,SAAA,GACA,IAAI,KAAA,CAAM,MAAA,CAAO,SAAS,CAAC;AAAA,WACjC;AAAA;AACF;AACF;AAGF,IAAA,GAAA;AAAA,MACE,CAAA,+BAAA,EAAkC,QAAQ,CAAA,EAAG,OAAA,GAAU,iBAAiB,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,QAAQ,IAAA,CAAK,GAAG,CAAA,GAAI,OAAO,KAAK,EAAE,CAAA;AAAA,KACrI;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,YAAY,MAAM,wBAAA;AAAA,QACtB,OAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,OAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,OAAO,SAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA;AAEnE,IAAA,OAAO,CAAA;AAAA,GACT;AAEA,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,mBAAmB,MAAM;AACvB,MAAA,IAAI,OAAA,EAAS;AAEb,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,IAAA;AAIV,MAAA,MAAM,YAAA,GAAe,CAAC,SAAA,KAAuB;AAC3C,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,UAAA,GAAa,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,SACjC,MAAO;AACL,UAAA,UAAA,GAAa,UAAA,CAAW,MAAM,YAAY,CAAA;AAAA;AAC5C,OACF;AAEA,MAAA,MAAM,OAAO,YAAY;AACvB,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,mBAAA,GAAsB,WAAA,EAAY;AAClC,QAAA,MAAM,YAAY,MAAM,mBAAA;AACxB,QAAA,mBAAA,GAAsB,IAAA;AAEtB,QAAA,YAAA,CAAa,cAAc,SAAS,CAAA;AAAA,OACtC;AAGA,MAAA,IAAA,EAAK;AAAA,KACP;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MAAM;AACV,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,YAAA,CAAa,UAAU,CAAA;AACvB,QAAA,UAAA,GAAa,IAAA;AAAA;AACf,KACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,YAAA,EAAc,OAAO,cAAA,GAAiB,GAAA,KAAU;AAC9C,MAAA,GAAA,CAAI,CAAA,mDAAA,EAAsD,QAAQ,CAAA,CAAE,CAAA;AACpE,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,YAAA,CAAa,UAAU,CAAA;AACvB,QAAA,UAAA,GAAa,IAAA;AAAA;AAGf,MAAA,IAAI,mBAAA,EAAqB;AACvB,QAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,UACjB,mBAAA,CAAoB,MAAM,MAAM;AAAA,WAAE,CAAA;AAAA,UAClC,IAAI,OAAA,CAAc,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,cAAc,CAAC;AAAA,SACnE,CAAA;AACD,QAAA,mBAAA,GAAsB,IAAA;AAAA;AAExB,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,QAAQ,CAAA,QAAA,CAAU,CAAA;AAAA,KACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,OAAO,YAAY;AACjB,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,QAAQ,CAAA,CAAE,CAAA;AACvD,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,MAAM,SAAA,GAAY,MAAM,WAAA,EAAY;AACpC,MAAA,OAAA,GAAU,KAAA;AACV,MAAA,OAAO,SAAA;AAAA,KACT;AAAA,IACA,WAAW,MAAM;AAAA,GACnB;AACF,CAAA;ACn8BA,SAAS,cAAc,KAAA,EAAoC;AACzD,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AACnB,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,EAAG;AAC/B,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,OAAOC,mBAAA,CAAG,YAAA,CAAa,QAAA,EAAU,MAAM,CAAA;AAAA;AAEzC,EAAA,OAAO,KAAA;AACT;AAaO,IAAM,UAAA,GAAa,CACxB,MAAA,KACS;AACT,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,GAAA,GAAW,MAAA;AACf,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,OAAA;AAEJ,EAAA,IAAI,OAAO,gBAAA,EAAkB;AAC3B,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAA,CAAO,gBAAgB,CAAA;AAC3C,MAAA,UAAA,GAAa,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,aAAa,CAAA,IAAK,KAAA,CAAA;AACpD,MAAA,OAAA,GAAU,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA,IAAK,KAAA,CAAA;AAC7C,MAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,QAAA,GAAA,GAAM,EAAE,oBAAoB,KAAA,EAAM;AAAA;AACpC,aACO,CAAA,EAAG;AACV,MAAA,MAAM,MAAA,GAASC,wBAAA,CAAM,MAAA,CAAO,gBAAgB,CAAA;AAC5C,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,sBAAsB,CAAA;AACzD,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,UAAA,GAAa,MAAM,CAAC,CAAA;AAAA;AACtB;AAEF,MAAA,OAAA,GAAU,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GAAW,OAAO,OAAA,GAAU,MAAA;AAChE,MAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,QAAA,GAAA,GAAM,EAAE,oBAAoB,KAAA,EAAM;AAAA;AACpC;AACF;AAIF,EAAA,IAAI,OAAO,GAAA,EAAK;AACd,IAAA,IAAI,OAAO,MAAA,CAAO,GAAA,CAAI,EAAA,KAAO,QAAA,EAAU;AACrC,MAAA,QAAA,GAAW,OAAO,GAAA,CAAI,EAAA;AAAA,KACxB,MAAA,IAAW,OAAO,OAAA,CAAQ,GAAA,CAAI,kBAAkB,QAAA,EAAU;AACxD,MAAA,QAAA,GAAW,QAAQ,GAAA,CAAI,aAAA;AAAA,KACzB,MAAO;AACL,MAAA,QAAA,GAAW,MAAA;AAAA;AAEb,IAAA,MAAM,UACJ,OAAO,QAAA,KAAa,QAAA,GAAW,aAAA,CAAc,QAAQ,CAAA,GAAI,MAAA;AAC3D,IAAA,GAAA,GAAM;AAAA,MACJ,GAAG,GAAA;AAAA,MACH,GAAI,OAAA,GAAU,EAAE,EAAA,EAAI,OAAA,KAAY,EAAC;AAAA,MACjC,IAAA,EAAM,aAAA;AAAA,QACJ,OAAO,OAAO,GAAA,CAAI,IAAA,KAAS,WACvB,MAAA,CAAO,GAAA,CAAI,IAAA,GACX,OAAA,CAAQ,GAAA,CAAI;AAAA,OAClB;AAAA,MACA,GAAA,EAAK,aAAA;AAAA,QACH,OAAO,OAAO,GAAA,CAAI,GAAA,KAAQ,WACtB,MAAA,CAAO,GAAA,CAAI,GAAA,GACX,OAAA,CAAQ,GAAA,CAAI;AAAA,OAClB;AAAA,MACA,oBACE,MAAA,CAAO,GAAA,CAAI,uBAAuB,MAAA,GAC9B,MAAA,CAAO,IAAI,kBAAA,GACX;AAAA,KACR;AAAA;AAIF,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,MAAM,OAAA,GAAU;;AAAA;AAAA;AAAA;AAAA,eAAA,EAAsL,OAAO,CAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAC7M,IAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA;AAGtB,EAAA,MAAM,IAAA,GAAO,IAAIC,OAAA,CAAK;AAAA,IACpB,GAAG,MAAA;AAAA,IACH,GAAI,GAAA,GAAM,EAAE,GAAA,KAAQ;AAAC,GACtB,CAAA;AAED,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAA,CAAK,EAAA,CAAG,SAAA,EAAW,CAAC,MAAA,KAAW;AAC7B,MAAA,MAAA,CAAO,KAAA,CAAM,CAAA,mBAAA,EAAsB,UAAU,CAAA,CAAE,CAAA;AAAA,KAChD,CAAA;AAAA;AAGH,EAAA,OAAO,IAAA;AACT,CAAA;;;ACrFA,IAAM,WAAA,GAAc,kBAAA;AASb,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,2BAAA,EAoFD,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAkBnD,IAAM,qBAAA,GAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAiBX,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAgB7B,WAAW,OAAO,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuGhD,IAAM,mBAAA,GAAsB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuB5B,IAAM,eAAA,GAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAmDxB,IAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,yBAAA,EA6BL,WAAW,OAAO,WAAW,CAAA;AAAA;;AAAA;AAAA,CAAA;AAWjD,IAAM,iBAAA,GAAoB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AA4B1B,IAAM,kBAAA,GAAqB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAuB3B,IAAM,yBAAA,GAA4B;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,iCAAA,EAqCN,WAAW,OAAO,WAAW,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAkBzD,IAAM,uBAAA,GAA0B;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;;;ACvcvC,SAAS,aAAa,GAAA,EAAuC;AAC3D,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA,EAAG;AACtC,IAAA,GAAA,CAAI,IAAI,CAAC,CAAC,CAAA,GAAI,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA;AAEzB,EAAA,OAAO,GAAA;AACT;AAGA,SAAS,eACP,CAAA,EAC0B;AAC1B,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KACf,CAAA,KAAM,UAAa,CAAA,KAAM,MAAA,IAAU,CAAA,KAAM,EAAA,GAAK,IAAA,GAAO,CAAA;AAEvD,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAyC;AAC1D,IAAA,MAAM,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnB,IAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,MAAA,CAAO,CAAC,CAAA;AAAA,GACrC;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAuC;AACzD,IAAA,MAAM,CAAA,GAAI,UAAU,CAAC,CAAA;AACrB,IAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,IAAI,KAAK,CAAC,CAAA;AAAA,GACvC;AAEA,EAAA,IAAI,eAAyD,EAAC;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,CAAA,CAAE,YAAA;AACd,IAAA,IAAI,GAAA,IAAO,QAAQ,IAAA,EAAM;AACvB,MAAA,YAAA,GAAe,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA;AAC/B,GACF,CAAA,MAAQ;AAAA;AAIR,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,CAAA,CAAE,IAAA;AACd,IAAA,IAAI,GAAA,IAAO,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAA,GAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA;AACvB,GACF,CAAA,MAAQ;AAAA;AAIR,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,OAAO,CAAA;AAAA,GAChC,CAAA,MAAQ;AACN,IAAA,OAAA,GAAU,CAAA,CAAE,OAAA;AAAA;AAGd,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,MAAA,CAAO,CAAA,CAAE,EAAE,CAAA;AAAA,IACf,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,OAAA;AAAA,IACA,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,IACvC,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,IACvC,QAAA,EAAU,UAAA,CAAW,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC/B,QAAA,EAAU,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC5B,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC3B,WAAA,EAAa,MAAA,CAAO,CAAA,CAAE,WAAW,CAAA;AAAA,IACjC,aAAA,EAAe,UAAA,CAAW,CAAA,CAAE,aAAa,CAAA;AAAA,IACzC,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC3B,OAAO,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,IAC/B,aAAA,EAAe,OAAA,CAAQ,CAAA,CAAE,aAAa,CAAA;AAAA,IACtC,YAAA;AAAA,IACA,SAAA,EAAW,SAAA,CAAU,CAAA,CAAE,SAAS,CAAA;AAAA,IAChC,kBAAA,EACE,CAAA,CAAE,kBAAA,KAAuB,MAAA,IAAU,EAAE,kBAAA,KAAuB,GAAA,GACxD,IAAA,GACA,CAAA,CAAE,kBAAA,KAAuB,OAAA,IAAW,CAAA,CAAE,kBAAA,KAAuB,MAC3D,KAAA,GACA,IAAA;AAAA,IACR,aAAA,EAAgB,OAAA,CAAQ,CAAA,CAAE,aAAa,CAAA,IAA8B,IAAA;AAAA,IACrE,WAAA,EAAa,UAAA,CAAW,CAAA,CAAE,WAAW,CAAA;AAAA,IACrC,SAAA,EAAW,UAAA,CAAW,CAAA,CAAE,SAAS,CAAA;AAAA,IACjC,aAAA,EAAe,UAAA,CAAW,CAAA,CAAE,aAAa,CAAA;AAAA,IACzC,YAAA,EAAc,UAAA,CAAW,CAAA,CAAE,YAAY,CAAA;AAAA,IACvC,eAAA,EAAiB,UAAA,CAAW,CAAA,CAAE,eAAe,CAAA;AAAA,IAC7C,IAAA;AAAA,IACA,cAAA,EAAgB,OAAA,CAAQ,CAAA,CAAE,cAAc,CAAA;AAAA,IACxC,QAAA,EAAU,SAAA,CAAU,CAAA,CAAE,QAAQ;AAAA,GAChC;AACF;AAEO,IAAM,eAAN,MAA2C;AAAA,EAIhD,YAAY,WAAA,EAAiD;AAE3D,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAWC,sBAAA,CAAc,2PAAe,CAAA;AAC9C,MAAA,OAAA,GAAU,SAAS,SAAS,CAAA;AAAA,KAC9B,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,IAAA,CAAK,MAAA,GAAS,YAAY,SAAA,IAAa,KAAA;AAEvC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,IAAA,CAAK,MAAA,GAAS,IAAI,OAAA,CAAQ,WAAA,CAAY,GAAA,EAAK;AAAA,QACzC,GAAI,YAAY,GAAA,GAAM,EAAE,KAAK,WAAA,CAAY,GAAA,KAAQ,EAAC;AAAA,QAClD,GAAI,YAAY,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,WAAA,CAAY,EAAA,EAAG,GAAI;AAAC,OAC9D,CAAA;AAAA,KACH,MAAO;AACL,MAAA,IAAA,CAAK,MAAA,GAAS,IAAI,OAAA,CAAQ;AAAA,QACxB,IAAA,EAAM,YAAY,IAAA,IAAQ,WAAA;AAAA,QAC1B,IAAA,EAAM,YAAY,IAAA,IAAQ,IAAA;AAAA,QAC1B,UAAU,WAAA,CAAY,QAAA;AAAA,QACtB,EAAA,EAAI,YAAY,EAAA,IAAM,CAAA;AAAA,QACtB,GAAI,YAAY,GAAA,GAAM,EAAE,KAAK,WAAA,CAAY,GAAA,KAAQ;AAAC,OACnD,CAAA;AAAA;AACH;AACF;AAAA,EAGA,SAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA;AACd,EAEQ,KAAA,GAAgB;AACtB,IAAA,OAAO,KAAK,GAAA,EAAI;AAAA;AAClB;AAAA,EAIA,MAAM,cAAA,CACJ,KAAA,EACA,SAAA,EACA,QAAA,EACe;AACf,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,YAAA,CAAc,CAAA;AACnE,MAAA,MAAM,KAAA,GAAQ,KAAK,SAAA,CAAU;AAAA,QAC3B,EAAA,EAAI,OAAA;AAAA,QACJ,KAAA;AAAA,QACA,SAAA;AAAA,QACA,SAAA,EAAW,KAAK,KAAA,EAAM;AAAA,QACtB,UAAU,QAAA,IAAY;AAAA,OACvB,CAAA;AACD,MAAA,MAAM,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,EAAG,KAAK,MAAM,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,aACvD,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,kCAAA,EAAqC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE5D;AACF,EAEA,MAAM,aAAa,KAAA,EAAoC;AACrD,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MAC5B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,KAAc;AAC5B,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AACtB,MAAA,OAAO;AAAA,QACL,GAAG,CAAA;AAAA,QACH,SAAA,EAAW,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS;AAAA,OACjC;AAAA,KACD,CAAA;AAAA;AACH;AAAA,EAIA,MAAM,MAAA,CAAkD;AAAA,IACtD,OAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA,GAAc,CAAA;AAAA,IACd,QAAA,GAAW,CAAA;AAAA,IACX,KAAA,GAAQ,IAAA;AAAA,IACR,SAAA,GAAY,MAAA;AAAA,IACZ,kBAAA,GAAqB,KAAA;AAAA,IACrB,IAAA,GAAO,MAAA;AAAA,IACP,cAAA,GAAiB;AAAA,GACnB,EAA+C;AAC7C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,OAAA,GAAU,KAAA,GAAQ,KAAA,CAAM,OAAA,EAAQ,GAAI,CAAA;AAE1C,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,cAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,OAAA;AAAA,MACA,IAAA,CAAK,UAAU,OAAO,CAAA;AAAA,MACtB,WAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAQ,QAAA,EAAS;AAAA,MACjB,SAAA,KAAc,MAAA,GAAY,SAAA,CAAU,QAAA,EAAS,GAAI,MAAA;AAAA,MACjD,qBAAqB,MAAA,GAAS,OAAA;AAAA,MAC9B,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,MAAA;AAAA,MAC9B,cAAA,IAAkB,MAAA;AAAA,MAClB;AAAA,KACF;AAEA,IAAA,MAAM,KAAA,GAAQ,OAAO,MAAM,CAAA;AAC3B,IAAA,GAAA;AAAA,MACE,CAAA,UAAA,EAAa,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA,EAAA,EAAK,KAAA,GAAQ,CAAA,MAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,SAAA,EAAY,QAAQ,CAAA,cAAA,EAAiB,WAAW,CAAA,UAAA,EAAa,OAAO,UAAU,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,cAAA,GAAiB,CAAA,kBAAA,EAAqB,cAAc,MAAM,EAAE,CAAA;AAAA,KAC3Q;AACA,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,OAAA,cAA2B;AAAA,MACnD,OAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,OACJ,EAAA,EAC0C;AAC1C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAE,CAAA;AAChE,IAAA,IAAI,CAAC,IAAA,IAAQ,MAAA,CAAO,KAAK,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AAC3C,MAAA,GAAA,CAAI,CAAA,IAAA,EAAO,EAAE,CAAA,UAAA,CAAY,CAAA;AACzB,MAAA,OAAO,IAAA;AAAA;AAET,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,EAAE,CAAA,CAAE,CAAA;AACrB,IAAA,OAAO,eAA8B,IAAI,CAAA;AAAA;AAC3C,EAEA,MAAM,eAAA,CACJ,MAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AACrC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,IAAA,CAAK,MAAM,CAAA,OAAA,EAAU,MAAM,CAAA,CAAE,CAAA;AACvE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAG9B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,GAAG,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C,EAEA,MAAM,UAAA,CACJ,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAA;AAAA,MAC5B,CAAA,EAAG,KAAK,MAAM,CAAA,GAAA,CAAA;AAAA,MACd,MAAA;AAAA,MACA,SAAS,KAAA,GAAQ;AAAA,KACnB;AACA,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC9B,IAAA,OAAO,IAAA,CAAK,cAA6B,GAAG,CAAA;AAAA;AAC9C,EAEA,MAAM,OAAA,CACJ,OAAA,EACA,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,IAAI,YAAA;AAEJ,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,YAAA,GAAe,MAAM,KAAK,MAAA,CAAO,QAAA;AAAA,QAC/B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,QAAQ,OAAO,CAAA;AAAA,OACvC;AAAA,KACF,MAAO;AACL,MAAA,YAAA,GAAe,MAAM,KAAK,MAAA,CAAO,SAAA,CAAU,GAAG,IAAA,CAAK,MAAM,CAAA,GAAA,CAAA,EAAO,CAAA,EAAG,EAAE,CAAA;AAAA;AAGvE,IAAA,IAAI,YAAA,CAAa,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAGvC,IAAA,IAAI,SAAS,IAAA,IAAQ,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACnD,MAAA,YAAA,GAAe,MAAM,IAAA,CAAK,YAAA;AAAA,QACxB,YAAA;AAAA,QACA,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,OAAA,CAAQ,KAAK,IAAA,IAAQ;AAAA,OACvB;AAAA;AAIF,IAAA,IAAI,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,YAAY,CAAA;AAE/D,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,QAAA,IAAA,GAAO,KAAK,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,KAAa,QAAQ,QAAQ,CAAA;AAAA;AAE3D,MAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,QAAA,IAAA,GAAO,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,OAAA,CAAQ,KAAK,CAAA;AAAA;AAC/C;AAIF,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C,EAEA,MAAM,cACJ,IAAA,EACA,IAAA,GAAqB,OACrB,KAAA,GAAQ,GAAA,EACR,SAAS,CAAA,EAC4B;AAErC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,IAAA,CAAK,MAAM,CAAA,GAAA,CAAA,EAAO,CAAA,EAAG,EAAE,CAAA;AACrE,IAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEjC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,MAAM,IAAI,CAAA;AAC3D,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEnC,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAA6B,QAAQ,CAAA;AAC7D,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACjE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA;AAC1C;AAAA,EAIA,MAAM,YAAA,CACJ,QAAA,EACA,SAAA,GAAY,IACZ,OAAA,EACqC;AACrC,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,aAAA,GACJ,OAAA,KAAY,MAAA,GACR,MAAA,GACA,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GACnB,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,GACtB,OAAA;AAER,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,qBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,QAAA;AAAA,MACA,SAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAClC,MAAA,GAAA,CAAI,yBAAyB,CAAA;AAC7B,MAAA,OAAO,EAAC;AAAA;AAIV,IAAA,MAAM,OAAmC,EAAC;AAC1C,IAAA,IAAI,UAAoB,EAAC;AACzB,IAAA,KAAA,MAAW,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,QAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,UAAA,MAAM,CAAA,GAAI,aAAa,OAAO,CAAA;AAC9B,UAAA,IAAA,CAAK,IAAA,CAAK,cAAA,CAA8B,CAAC,CAAC,CAAA;AAAA;AAE5C,QAAA,OAAA,GAAU,EAAC;AAAA,OACb,MAAO;AACL,QAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA;AACnB;AAGF,IAAA,GAAA,CAAI,CAAA,MAAA,EAAS,IAAA,CAAK,MAAM,CAAA,gBAAA,CAAkB,CAAA;AAG1C,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,EAAA,EAAA,YAAA,kBAA2B;AAAA;AAG3D,IAAA,OAAO,IAAA;AAAA;AACT,EAEA,MAAM,YAAY,KAAA,EAA8B;AAC9C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,mBAAA,EAAqB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACtE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,IAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA;AAC9B,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,KAAA,EACA,aAAA,EACe;AACf,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,SAAA,GAAY,KAAK,SAAA,CAAU;AAAA,MAC/B;AAAA,QACE,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,QACtC,SAAA,EAAW,IAAI,IAAA,CAAK,GAAG,EAAE,WAAA;AAAY;AACvC,KACD,CAAA;AACD,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,eAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,KAAA;AAAA,MACA,SAAA;AAAA,MACA,aAAA,IAAiB,MAAA;AAAA,MACjB;AAAA,KACF;AACA,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,QAAA,eAA4B;AAAA,MACpD,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAAA,MACtC;AAAA,KACD,CAAA;AACD,IAAA,GAAA,CAAI,CAAA,WAAA,EAAc,KAAK,CAAA,CAAE,CAAA;AAAA;AAC3B,EAEA,MAAM,WAAW,KAAA,EAA8B;AAC7C,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,MAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,kBAAA,EAAoB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACrE,MAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,MAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,aACrB,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE/C;AACF;AAAA,EAIA,MAAM,cAAA,CAAe,KAAA,EAAe,QAAA,EAAiC;AACnE,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA;AAAA,QAC1B,UAAA;AAAA,QACA,SAAS,QAAA,EAAS;AAAA,QAClB,WAAA;AAAA,QACA,IAAI,QAAA;AAAS,OACf;AACA,MAAA,GAAA,CAAI,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,aAC9C,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,CAAA,gCAAA,EAAmC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA;AAE1D;AACF;AAAA,EAIA,MAAM,SAAS,KAAA,EAA8B;AAC3C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,gBAAA,EAAkB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACnE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,SAAA,eAA2B;AACrD,IAAA,GAAA,CAAI,CAAA,YAAA,EAAe,KAAK,CAAA,CAAE,CAAA;AAAA;AAC5B,EAEA,MAAM,UAAU,KAAA,EAA8B;AAC5C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,iBAAA,EAAmB,GAAG,IAAA,CAAK,MAAA,EAAQ,OAAO,GAAG,CAAA;AACpE,IAAA,MAAM,IAAA,CAAK,eAAe,KAAA,EAAA,WAAA,iBAA6B;AACvD,IAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AAAA;AAC9B,EAEA,MAAM,sBAAsB,OAAA,EAAuC;AAEjE,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAE7B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,GAAA,GAAM,MAAM,IAAA,CAAK,YAAA,CAAa,GAAA,EAAK,OAAO,CAAA;AAAA;AAG5C,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QAC/B,iBAAA;AAAA,QACA,CAAA;AAAA,QACA,IAAA,CAAK,MAAA;AAAA,QACL,EAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,MAAA,CAAO,MAAM,CAAA,KAAM,CAAA,EAAG,KAAA,EAAA;AAAA;AAG5B,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,KAAK,CAAA,KAAA,CAAO,CAAA;AAC7B,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAoC;AAC/D,IAAA,MAAM,EAAA,GAAK,CAAA,EAAG,IAAA,CAAK,MAAM,OAAO,KAAK,CAAA,CAAA;AACrC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,QAAQ,CAAA;AAClD,IAAA,IAAI,WAAW,SAAA,EAAW;AACxB,MAAA,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,yBAAA,EAA4B,MAAM,CAAA,gBAAA,CAAkB,CAAA;AACpE,MAAA;AAAA;AAGF,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,SAAmB,EAAC;AAC1B,IAAA,MAAM,WAAgB,EAAC;AAEvB,IAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,EAAW;AACjC,MAAA,MAAA,CAAO,KAAK,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,OAAO,CAAC,CAAA;AACtD,MAAA,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAAA;AAE7B,IAAA,IAAI,OAAA,CAAQ,gBAAgB,MAAA,EAAW;AACrC,MAAA,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,OAAA,CAAQ,WAAA,CAAY,UAAU,CAAA;AACzD,MAAA,QAAA,CAAS,cAAc,OAAA,CAAQ,WAAA;AAAA;AAEjC,IAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,EAAY,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA;AACnD,MAAA,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAG5B,MAAA,MAAM,YAAY,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,WAAW,CAAA;AACxD,MAAA,MAAM,QAAQ,OAAA,CAAQ,QAAA,GAAW,IAAA,IAAQ,IAAA,GAAO,OAAO,SAAS,CAAA,CAAA;AAEhE,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,QAChC,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,CAAA;AAAA,QACd,MAAM,QAAA;AAAS,OACjB;AACA,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,CAAA,EAAS,KAAA,EAAO,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA;AACvE;AAEF,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW;AAC/B,MAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,EAAS,GAAA,CAAI,QAAA,EAAU,CAAA;AAAA,OACrC,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,OAAA,EAAS,OAAA,CAAQ,MAAM,OAAA,EAAQ,CAAE,UAAU,CAAA;AAAA;AAEzD,MAAA,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAAA;AAE3B,IAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,WAAA;AAAA,QACA,QAAQ,SAAA,KAAc,IAAA,GAAO,OAAA,CAAQ,SAAA,CAAU,UAAS,GAAI;AAAA,OAC9D;AACA,MAAA,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAAA;AAE/B,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAE9B,MAAA,MAAM,cAAc,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAI,MAAM,CAAA;AACrD,MAAA,IAAI,WAAA,IAAe,gBAAgB,MAAA,EAAQ;AACzC,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA;AACtC,UAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,YAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,cAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA;AAAA,cACxB,MAAM,QAAA;AAAS,aACjB;AAAA;AACF,SACF,CAAA,MAAQ;AAAA;AAER;AAEF,MAAA,MAAM,IAAA,CAAK,OAAO,GAAA,CAAI,CAAA,EAAG,KAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,KAAA,CAAO,CAAA;AAEvD,MAAA,IAAI,OAAA,CAAQ,SAAS,IAAA,EAAM;AACzB,QAAA,KAAA,MAAW,GAAA,IAAO,QAAQ,IAAA,EAAM;AAC9B,UAAA,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA,EAAI,KAAA,CAAM,QAAA,EAAU,CAAA;AACnE,UAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,IAAA,EAAO,KAAK,CAAA,KAAA,CAAA,EAAS,GAAG,CAAA;AAAA;AAE/D,QAAA,MAAA,CAAO,KAAK,MAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,OAClD,MAAO;AACL,QAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA;AAE5B,MAAA,QAAA,CAAS,OAAO,OAAA,CAAQ,IAAA;AAAA;AAG1B,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAE,CAAA;AAC1C,MAAA;AAAA;AAGF,IAAA,MAAA,CAAO,IAAA,CAAK,WAAA,EAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AACvC,IAAA,MAAO,IAAA,CAAK,MAAA,CAAe,KAAA,CAAM,EAAA,EAAI,GAAG,MAAM,CAAA;AAE9C,IAAA,MAAM,IAAA,CAAK,cAAA,CAAe,KAAA,EAAA,QAAA,eAA4B,QAAQ,CAAA;AAC9D,IAAA,GAAA,CAAI,cAAc,KAAK,CAAA,EAAA,EAAK,KAAK,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA;AACxD,EAEA,MAAM,kBAAA,CACJ,OAAA,EACA,OAAA,EACiB;AACjB,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAE7B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,GAAA,GAAM,MAAM,IAAA,CAAK,YAAA,CAAa,GAAA,EAAK,OAAO,CAAA;AAAA;AAG5C,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAO,CAAA;AACtC,MAAA,KAAA,EAAA;AAAA;AAGF,IAAA,GAAA,CAAI,CAAA,OAAA,EAAU,KAAK,CAAA,aAAA,CAAe,CAAA;AAClC,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAM,cAAA,CAAe,UAAA,GAAa,EAAA,EAAqB;AACrD,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,KAAU,UAAA,GAAa,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC5D,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,uBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL;AAAA,KACF;AACA,IAAA,GAAA,CAAI,CAAA,QAAA,EAAW,MAAM,CAAA,SAAA,CAAW,CAAA;AAChC,IAAA,OAAO,OAAO,MAAM,CAAA;AAAA;AACtB,EAEA,MAAM,mBAAA,CAAoB,UAAA,GAAa,EAAA,EAAqB;AAK1D,IAAA,GAAA;AAAA,MACE,CAAA,wFAAA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA;AACT,EAEA,MAAM,gBAAA,CAAiB,wBAAA,GAA2B,EAAA,EAAqB;AACrE,IAAA,MAAM,QAAA,GAAW,2BAA2B,EAAA,GAAK,GAAA;AACjD,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,MAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAChC,yBAAA;AAAA,MACA,CAAA;AAAA,MACA,IAAA,CAAK,MAAA;AAAA,MACL,QAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,CAAA,UAAA,EAAa,MAAM,CAAA,WAAA,CAAa,CAAA;AACpC,IAAA,OAAO,OAAO,MAAM,CAAA;AAAA;AACtB;AAAA,EAIA,MAAM,+BAAA,CACJ,MAAA,EACA,OAAA,EACe;AACf,IAAA,IAAI,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,cAAA,CAAgB,CAAA;AACnE,IAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AAEtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,OAAA,GAAU,CAAC,OAAO,CAAA;AACzD,MAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,MAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,QAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,CAAC,CAAA,CAAE,CAAA;AACpE,QAAA,KAAA,MAAW,EAAA,IAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAAA;AAE1C,MAAA,GAAA,GAAM,IAAI,MAAA,CAAO,CAAC,OAAe,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGlD,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA;AAAA,QACvB,eAAA;AAAA,QACA;AAAA,OACF;AAAA;AACF;AACF;AAAA,EAIA,MAAc,cACZ,GAAA,EACqC;AACrC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,QAAA,EAAS;AACtC,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,QAAA,CAAS,QAAQ,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAE,CAAA;AAAA;AAE5C,IAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,IAAA,MAAM,OAAmC,EAAC;AAC1C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,OAAA,EAAS;AACjC,QAAA,IACE,CAAC,GAAA,IACD,IAAA,IACA,OAAO,IAAA,KAAS,QAAA,IAChB,MAAA,CAAO,IAAA,CAAK,IAAc,CAAA,CAAE,MAAA,GAAS,CAAA,EACrC;AACA,UAAA,IAAA,CAAK,IAAA;AAAA,YACH,eAA8B,IAA8B;AAAA,WAC9D;AAAA;AACF;AACF;AAEF,IAAA,OAAO,IAAA;AAAA;AACT,EAEA,MAAc,YAAA,CACZ,YAAA,EACA,IAAA,EACA,IAAA,EACmB;AACnB,IAAA,MAAM,eAAe,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAC,CAAA;AAErD,IAAA,IAAI,SAAS,OAAA,EAAS;AAEpB,MAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,IAAI,CAAA;AAC3B,MAAA,MAAM,SAAmB,EAAC;AAC1B,MAAA,KAAA,MAAW,MAAM,YAAA,EAAc;AAC7B,QAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UAChC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,KAAA;AAAA,SACzB;AACA,QAAA,IACE,OAAA,CAAQ,MAAA,KAAW,MAAA,CAAO,IAAA,IAC1B,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,KAAc,MAAA,CAAO,GAAA,CAAI,CAAC,CAAC,CAAA,EAC1C;AACA,UAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA;AAChB;AAEF,MAAA,OAAO,MAAA;AAAA;AAGT,IAAA,IAAI,SAAS,KAAA,EAAO;AAElB,MAAA,IAAI,eAAe,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAC,CAAA;AACnD,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,MAAM,SAAS,IAAI,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAC,CAAA;AAC7C,QAAA,YAAA,GAAe,IAAI,GAAA;AAAA,UACjB,CAAC,GAAG,YAAY,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,MAAA,CAAO,GAAA,CAAI,EAAE,CAAC;AAAA,SACjD;AAAA;AAEF,MAAA,OAAO,CAAC,GAAG,YAAY,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,YAAA,CAAa,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAG9D,IAAA,IAAI,SAAS,KAAA,EAAO;AAElB,MAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,KAAA,MAAW,MAAM,UAAA,EAAY,KAAA,CAAM,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA;AAEnD,MAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAO,YAAA,CAAa,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGvD,IAAA,IAAI,SAAS,MAAA,EAAQ;AAEnB,MAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,UACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,IAAA,EAAO,GAAG,CAAA;AAAA,SAC1B;AACA,QAAA,KAAA,MAAW,MAAM,UAAA,EAAY,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA;AAErD,MAAA,OAAO,YAAA,CAAa,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,QAAQ,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAC,CAAA;AAAA;AAI7D,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,YAAA,EAAc,IAAA,EAAM,KAAK,CAAA;AAAA;AACpD,EAEQ,aAAA,CACN,MACA,KAAA,EAC4B;AAC5B,IAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,OAAA,EAAQ,KAAM,KAAA,CAAM,OAAA,EAAS,CAAA;AAAA;AAEjE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM;AACxB,MAAA,MAAM,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,OAAA,EAAQ;AAC1B,MAAA,IAAI,KAAA,CAAM,MAAM,EAAE,CAAA,GAAI,MAAM,EAAA,CAAG,OAAA,KAAY,OAAO,KAAA;AAClD,MAAA,IAAI,KAAA,CAAM,OAAO,EAAE,CAAA,IAAK,MAAM,GAAA,CAAI,OAAA,KAAY,OAAO,KAAA;AACrD,MAAA,IAAI,KAAA,CAAM,MAAM,EAAE,CAAA,GAAI,MAAM,EAAA,CAAG,OAAA,KAAY,OAAO,KAAA;AAClD,MAAA,IAAI,KAAA,CAAM,OAAO,EAAE,CAAA,IAAK,MAAM,GAAA,CAAI,OAAA,KAAY,OAAO,KAAA;AACrD,MAAA,IAAI,MAAM,EAAA,IAAM,CAAA,KAAM,MAAM,EAAA,CAAG,OAAA,IAAW,OAAO,KAAA;AACjD,MAAA,OAAO,IAAA;AAAA,KACR,CAAA;AAAA;AACH;AAAA;AAAA,EAKA,MAAM,gBAAgB,KAAA,EAA2C;AAC/D,IAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA;AAAA,MACnC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,UAAA,EAAa,MAAM,YAAY,CAAA;AAAA,KAC/C;AACA,IAAA,IAAI,eAAe,IAAA,EAAM;AACvB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,yBAAA,EAA4B,MAAM,YAAY,CAAA,gBAAA;AAAA,OAChD;AAAA;AAGF,IAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,WAAA,CAAa,CAAA;AAC7D,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,MAAM,QAAQ,EAAE,CAAA,CAAA;AAEpC,IAAA,MAAM,MAAA,GAAmB;AAAA,MACvB,IAAA;AAAA,MACA,GAAG,QAAA,EAAS;AAAA,MACZ,cAAA;AAAA,MACA,KAAA,CAAM,YAAA;AAAA,MACN,gBAAA;AAAA,MACA,KAAA,CAAM,cAAA;AAAA,MACN,SAAA;AAAA,MACA,KAAA,CAAM,OAAA;AAAA,MACN,SAAA;AAAA,MACA,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,OAAO,CAAA;AAAA,MAC5B,aAAA;AAAA,MACA,KAAA,CAAM,YAAY,QAAA,EAAS;AAAA,MAC3B,UAAA;AAAA,MACA,KAAA,CAAM,SAAS,QAAA,EAAS;AAAA,MACxB,WAAA;AAAA,MACA,MAAM,SAAA,KAAc,IAAA,GAAO,KAAA,CAAM,SAAA,CAAU,UAAS,GAAI,MAAA;AAAA,MACxD,oBAAA;AAAA,MACA,KAAA,CAAM,qBAAqB,MAAA,GAAS,OAAA;AAAA,MACpC,MAAA;AAAA,MACA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAAA,MAC1C,UAAA;AAAA,MACA,KAAA,CAAM,QAAA;AAAA,MACN,cAAA;AAAA,MACA,KAAA,CAAM,eAAe,MAAA,GAAS,OAAA;AAAA,MAC9B,QAAA;AAAA,MACA,QAAA;AAAA,MACA,gBAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAM,SAAA,GAAY,KAAA,CAAM,UAAU,OAAA,EAAQ,CAAE,UAAS,GAAI,MAAA;AAAA,MACzD,WAAA;AAAA,MACA,IAAI,QAAA,EAAS;AAAA,MACb,WAAA;AAAA,MACA,IAAI,QAAA;AAAS,KACf;AAEA,IAAA,MAAO,IAAA,CAAK,MAAA,CAAe,KAAA,CAAM,GAAA,EAAK,GAAG,MAAM,CAAA;AAC/C,IAAA,MAAM,KAAK,MAAA,CAAO,GAAA;AAAA,MAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,UAAA,EAAa,MAAM,YAAY,CAAA,CAAA;AAAA,MAC7C,GAAG,QAAA;AAAS,KACd;AACA,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,CAAA,EAAS,EAAA,CAAG,QAAA,EAAU,CAAA;AAC3D,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB,EAAA,CAAG,QAAA,EAAU,CAAA;AAExE,IAAA,IAAI,MAAM,SAAA,EAAW;AACnB,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA;AAAA,QACd,KAAA,CAAM,UAAU,OAAA,EAAQ;AAAA,QACxB,GAAG,QAAA;AAAS,OACd;AAAA;AAGF,IAAA,GAAA,CAAI,CAAA,oBAAA,EAAuB,EAAE,CAAA,GAAA,EAAM,KAAA,CAAM,YAAY,CAAA,CAAA,CAAG,CAAA;AACxD,IAAA,OAAO,EAAA;AAAA;AACT;AAAA,EAGA,MAAM,gBAAgB,EAAA,EAAgD;AACpE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAE,CAAA;AACjE,IAAA,IAAI,CAAC,QAAQ,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,KAAW,GAAG,OAAO,IAAA;AACpD,IAAA,OAAO,IAAA,CAAK,wBAAwB,IAAI,CAAA;AAAA;AAC1C;AAAA,EAGA,MAAM,sBACJ,IAAA,EACoC;AACpC,IAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,IAAA,CAAK,MAAM,CAAA,UAAA,EAAa,IAAI,CAAA,CAAE,CAAA;AAClE,IAAA,IAAI,EAAA,KAAO,MAAM,OAAO,IAAA;AACxB,IAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA;AACxC;AAAA,EAGA,MAAM,kBACJ,MAAA,EAC+B;AAC/B,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,GAAA,GAAM,MAAM,KAAK,MAAA,CAAO,QAAA,CAAS,GAAG,IAAA,CAAK,MAAM,CAAA,YAAA,EAAe,MAAM,CAAA,CAAE,CAAA;AAAA,KACxE,MAAO;AACL,MAAA,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,CAAO,CAAA;AAAA;AAExD,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAE9B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,QAAA,EAAS;AACtC,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,QAAA,CAAS,QAAQ,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAE,CAAA;AAAA;AAE7C,IAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,IAAA,MAAM,YAAkC,EAAC;AACzC,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,OAAA,EAAS;AACjC,QAAA,IACE,CAAC,GAAA,IACD,IAAA,IACA,OAAO,IAAA,KAAS,QAAA,IAChB,MAAA,CAAO,IAAA,CAAK,IAAc,CAAA,CAAE,MAAA,GAAS,CAAA,EACrC;AACA,UAAA,SAAA,CAAU,IAAA;AAAA,YACR,IAAA,CAAK,wBAAwB,IAA8B;AAAA,WAC7D;AAAA;AACF;AACF;AAEF,IAAA,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,CAAU,OAAA,EAAQ,GAAI,CAAA,CAAE,SAAA,CAAU,OAAA,EAAS,CAAA;AACtE,IAAA,OAAO,SAAA;AAAA;AACT;AAAA,EAGA,MAAM,mBAAmB,EAAA,EAA2B;AAClD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAE,CAAA;AACjE,IAAA,IAAI,CAAC,IAAA,IAAQ,MAAA,CAAO,KAAK,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AAE7C,IAAA,MAAM,OAAO,IAAA,CAAK,YAAA;AAClB,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AAEpB,IAAA,MAAM,IAAA,CAAK,OAAO,GAAA,CAAI,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAE,CAAA;AAChD,IAAA,MAAM,IAAA,CAAK,OAAO,GAAA,CAAI,CAAA,EAAG,KAAK,MAAM,CAAA,UAAA,EAAa,IAAI,CAAA,CAAE,CAAA;AACvD,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,CAAA,EAAS,EAAA,CAAG,QAAA,EAAU,CAAA;AAC3D,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,YAAA,EAAe,MAAM,CAAA,CAAA;AAAA,MACnC,GAAG,QAAA;AAAS,KACd;AACA,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA,EAAY,EAAA,CAAG,QAAA,EAAU,CAAA;AAC9D,IAAA,GAAA,CAAI,CAAA,sBAAA,EAAyB,EAAE,CAAA,CAAE,CAAA;AAAA;AACnC;AAAA,EAGA,MAAM,kBAAkB,EAAA,EAA2B;AACjD,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAA;AAAA,MACxB,QAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAI,QAAA;AAAS,KACf;AACA,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB,EAAA,CAAG,QAAA,EAAU,CAAA;AACxE,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB,EAAA,CAAG,QAAA,EAAU,CAAA;AACxE,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA,EAAY,EAAA,CAAG,QAAA,EAAU,CAAA;AAC9D,IAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,CAAE,CAAA;AAAA;AAClC;AAAA,EAGA,MAAM,mBAAmB,EAAA,EAA2B;AAClD,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAA;AAAA,MACxB,QAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAI,QAAA;AAAS,KACf;AACA,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB,EAAA,CAAG,QAAA,EAAU,CAAA;AACxE,IAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB,EAAA,CAAG,QAAA,EAAU,CAAA;AAExE,IAAA,MAAM,SAAA,GAAY,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAClC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAA;AAAA,MACxB;AAAA,KACF;AACA,IAAA,IAAI,SAAA,IAAa,cAAc,MAAA,EAAQ;AACrC,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA;AAAA,QACd,OAAO,SAAS,CAAA;AAAA,QAChB,GAAG,QAAA;AAAS,OACd;AAAA;AAEF,IAAA,GAAA,CAAI,CAAA,sBAAA,EAAyB,EAAE,CAAA,CAAE,CAAA;AAAA;AACnC;AAAA,EAGA,MAAM,gBAAA,CACJ,EAAA,EACA,OAAA,EACA,SAAA,EACe;AACf,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,SAAmB,EAAC;AAE1B,IAAA,IAAI,OAAA,CAAQ,mBAAmB,MAAA,EAAW;AACxC,MAAA,MAAA,CAAO,IAAA,CAAK,gBAAA,EAAkB,OAAA,CAAQ,cAAc,CAAA;AAAA;AAEtD,IAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,EAAW;AACjC,MAAA,MAAA,CAAO,KAAK,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA;AAExD,IAAA,IAAI,OAAA,CAAQ,gBAAgB,MAAA,EAAW;AACrC,MAAA,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,OAAA,CAAQ,WAAA,CAAY,UAAU,CAAA;AAAA;AAE3D,IAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,EAAY,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA;AAAA;AAErD,IAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,WAAA;AAAA,QACA,QAAQ,SAAA,KAAc,IAAA,GAAO,OAAA,CAAQ,SAAA,CAAU,UAAS,GAAI;AAAA,OAC9D;AAAA;AAEF,IAAA,IAAI,OAAA,CAAQ,uBAAuB,MAAA,EAAW;AAC5C,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,oBAAA;AAAA,QACA,OAAA,CAAQ,qBAAqB,MAAA,GAAS;AAAA,OACxC;AAAA;AAEF,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAC9B,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,MAAA;AAAA,QACA,QAAQ,IAAA,KAAS,IAAA,GAAO,KAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,GAAI;AAAA,OACzD;AAAA;AAEF,IAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,EAAY,OAAA,CAAQ,QAAQ,CAAA;AAAA;AAE1C,IAAA,IAAI,OAAA,CAAQ,iBAAiB,MAAA,EAAW;AACtC,MAAA,MAAA,CAAO,IAAA,CAAK,cAAA,EAAgB,OAAA,CAAQ,YAAA,GAAe,SAAS,OAAO,CAAA;AAAA;AAErE,IAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,MAAA,MAAM,MAAM,SAAA,KAAc,IAAA,GAAO,UAAU,OAAA,EAAQ,CAAE,UAAS,GAAI,MAAA;AAClE,MAAA,MAAA,CAAO,IAAA,CAAK,aAAa,GAAG,CAAA;AAC5B,MAAA,IAAI,cAAc,IAAA,EAAM;AACtB,QAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,UAChB,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA;AAAA,UACd,UAAU,OAAA,EAAQ;AAAA,UAClB,GAAG,QAAA;AAAS,SACd;AAAA,OACF,MAAO;AACL,QAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA,EAAY,EAAA,CAAG,QAAA,EAAU,CAAA;AAAA;AAChE;AAGF,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,GAAA,CAAI,CAAA,sCAAA,EAAyC,EAAE,CAAA,CAAE,CAAA;AACjD,MAAA;AAAA;AAGF,IAAA,MAAA,CAAO,IAAA,CAAK,WAAA,EAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AACvC,IAAA,MAAO,IAAA,CAAK,MAAA,CAAe,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAA,EAAI,GAAG,MAAM,CAAA;AACtE,IAAA,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,CAAE,CAAA;AAAA;AAClC;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAA,GAAqD;AACzD,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,EAAM;AACvB,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,aAAA;AAAA,MAC5B,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA;AAAA,MACd,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AACpB,MAAA,GAAA,CAAI,4BAA4B,CAAA;AAChC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,YAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAE,CAAA;AACjE,MAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,CAAK,IAAI,EAAE,MAAA,GAAS,CAAA,IAAK,IAAA,CAAK,MAAA,KAAW,QAAA,EAAU;AACpE,QAAA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB,IAAI,CAAC,CAAA;AAAA;AACnD;AAEF,IAAA,GAAA,CAAI,CAAA,MAAA,EAAS,SAAA,CAAU,MAAM,CAAA,mBAAA,CAAqB,CAAA;AAClD,IAAA,OAAO,SAAA;AAAA;AACT;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,8BAAA,CACJ,EAAA,EACA,cAAA,EACA,WACA,SAAA,EACe;AACf,IAAA,MAAM,MAAA,GAAmB;AAAA,MACvB,gBAAA;AAAA,MACA,cAAA,CAAe,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,MAClC,WAAA;AAAA,MACA,UAAU,QAAA,EAAS;AAAA,MACnB,WAAA;AAAA,MACA,SAAA,GAAY,SAAA,CAAU,OAAA,EAAQ,CAAE,UAAS,GAAI,MAAA;AAAA,MAC7C,WAAA;AAAA,MACA,IAAA,CAAK,KAAA,EAAM,CAAE,QAAA;AAAS,KACxB;AAEA,IAAA,MAAO,IAAA,CAAK,MAAA,CAAe,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,KAAA,EAAQ,EAAE,CAAA,CAAA,EAAI,GAAG,MAAM,CAAA;AAEtE,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,QAChB,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA;AAAA,QACd,UAAU,OAAA,EAAQ;AAAA,QAClB,GAAG,QAAA;AAAS,OACd;AAAA,KACF,MAAO;AACL,MAAA,MAAM,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,EAAG,KAAK,MAAM,CAAA,QAAA,CAAA,EAAY,EAAA,CAAG,QAAA,EAAU,CAAA;AAAA;AAGhE,IAAA,GAAA;AAAA,MACE,CAAA,sBAAA,EAAyB,EAAE,CAAA,YAAA,EAAe,SAAS,eAAe,SAAA,EAAW,WAAA,MAAiB,MAAM,CAAA;AAAA,KACtG;AAAA;AACF;AAAA,EAGQ,wBACN,CAAA,EACoB;AACpB,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KACf,CAAA,KAAM,UAAa,CAAA,KAAM,MAAA,IAAU,CAAA,KAAM,EAAA,GAAK,IAAA,GAAO,CAAA;AACvD,IAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAyC;AAC1D,MAAA,MAAM,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnB,MAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,MAAA,CAAO,CAAC,CAAA;AAAA,KACrC;AACA,IAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAuC;AACzD,MAAA,MAAM,CAAA,GAAI,UAAU,CAAC,CAAA;AACrB,MAAA,OAAO,CAAA,KAAM,IAAA,GAAO,IAAA,GAAO,IAAI,KAAK,CAAC,CAAA;AAAA,KACvC;AAEA,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,OAAO,CAAA;AAAA,KAChC,CAAA,MAAQ;AACN,MAAA,OAAA,GAAU,CAAA,CAAE,OAAA;AAAA;AAGd,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,CAAA,CAAE,IAAA;AACd,MAAA,IAAI,GAAA,IAAO,QAAQ,MAAA,EAAQ;AACzB,QAAA,IAAA,GAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA;AACvB,KACF,CAAA,MAAQ;AAAA;AAIR,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,MAAA,CAAO,CAAA,CAAE,EAAE,CAAA;AAAA,MACf,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,OAAA;AAAA,MACA,WAAA,EAAa,MAAA,CAAO,CAAA,CAAE,WAAW,CAAA;AAAA,MACjC,QAAA,EAAU,MAAA,CAAO,CAAA,CAAE,QAAQ,CAAA;AAAA,MAC3B,SAAA,EAAW,SAAA,CAAU,CAAA,CAAE,SAAS,CAAA;AAAA,MAChC,kBAAA,EAAoB,EAAE,kBAAA,KAAuB,MAAA;AAAA,MAC7C,IAAA;AAAA,MACA,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,YAAA,EAAc,EAAE,YAAA,KAAiB,MAAA;AAAA,MACjC,QAAQ,CAAA,CAAE,MAAA;AAAA,MACV,cAAA,EAAgB,UAAA,CAAW,CAAA,CAAE,cAAc,CAAA;AAAA,MAC3C,SAAA,EAAW,SAAA,CAAU,CAAA,CAAE,SAAS,CAAA;AAAA,MAChC,SAAA,EAAW,UAAA,CAAW,CAAA,CAAE,SAAS,CAAA;AAAA,MACjC,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,MACvC,WAAW,IAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,SAAS,CAAC;AAAA,KACzC;AAAA;AACF;AAAA,EAIA,MAAc,YAAA,CACZ,GAAA,EACA,OAAA,EACmB;AACnB,IAAA,IAAI,MAAA,GAAS,GAAA;AAEb,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAM,UAAU,IAAI,GAAA;AAAA,QAClB,MAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,CAAA,EAAG,KAAK,MAAM,CAAA,KAAA,EAAQ,OAAA,CAAQ,OAAO,CAAA,CAAE;AAAA,OACpE;AACA,MAAA,MAAA,GAAS,OAAO,MAAA,CAAO,CAAC,OAAO,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA;AAGhD,IAAA,IAAI,QAAQ,IAAA,IAAQ,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AAClD,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,YAAA;AAAA,QAClB,MAAA;AAAA,QACA,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,OAAA,CAAQ,KAAK,IAAA,IAAQ;AAAA,OACvB;AAAA;AAIF,IAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,MAAA,IAAa,OAAA,CAAQ,KAAA,EAAO;AACnD,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,aAAA,CAAc,MAAM,CAAA;AAC5C,MAAA,IAAI,QAAA,GAAW,IAAA;AACf,MAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,QAAA,QAAA,GAAW,SAAS,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,KAAa,QAAQ,QAAQ,CAAA;AAAA;AAEnE,MAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,QAAA,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,QAAA,EAAU,OAAA,CAAQ,KAAK,CAAA;AAAA;AAEvD,MAAA,MAAA,GAAS,SAAS,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,EAAA,CAAG,UAAU,CAAA;AAAA;AAG9C,IAAA,OAAO,MAAA;AAAA;AAEX,CAAA;AC1rCO,SAAS,sBACd,cAAA,EACA,QAAA,GAAmB,KAAA,EACnB,KAAA,EACA,WAAwBC,WAAA,EACX;AACb,EAAA,MAAM,OAAO,IAAI,QAAA,CAAS,cAAA,EAAgB,EAAE,UAAU,CAAA;AACtD,EAAA,MAAM,OAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,oBAAS,IAAI,MAAM,CAAA;AAC7C,EAAA,OAAO,IAAA,IAAQ,IAAA;AACjB;AASO,SAAS,sBAAA,CACd,cAAA,EACA,QAAA,GAAwBA,WAAA,EACf;AACT,EAAA,IAAI;AACF,IAAA,IAAI,SAAS,cAAc,CAAA;AAC3B,IAAA,OAAO,IAAA;AAAA,GACT,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA;AAEX;;;ACfO,SAASC,4BAAAA,CAId,SACA,OAAA,EAC6C;AAC7C,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AACvC,IAAA,MAAM,SAAA,GAAY,OAAA,GAAU,CAAA,UAAA,EAAa,OAAO,CAAA,CAAA,CAAA,GAAM,SAAA;AAItD,IAAA,IACE,aAAA,CAAc,SAAS,OAAO,CAAA,IAC9B,CAAC,aAAA,CAAc,KAAA,CAAM,oBAAoB,CAAA,EACzC;AACA,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,eAAe,SAAS,CAAA,sIAAA;AAAA,OAE5B;AAAA;AAKF,IAAA,IAAI,aAAA,CAAc,QAAA,CAAS,eAAe,CAAA,EAAG;AAC3C,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,eAAe,SAAS,CAAA,6HAAA;AAAA,OAE5B;AAAA;AAKF,IAAA,IAAI;AACF,MAAA,IAAI,QAAA,CAAS,YAAY,aAAa,CAAA;AAAA,aAC/B,UAAA,EAAY;AACnB,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,KAAA;AAAA,QAChB,KAAA,EACE,CAAA,YAAA,EAAe,SAAS,CAAA,uBAAA,EAA0B,UAAA,YAAsB,QAAQ,UAAA,CAAW,OAAA,GAAU,MAAA,CAAO,UAAU,CAAC,CAAA,kHAAA;AAAA,OAE3H;AAAA;AAMF,IAAA,MAAM,sBACJ,uCAAA,CAAwC,IAAA,CAAK,aAAa,CAAA,IAC1D,qCAAA,CAAsC,KAAK,aAAa,CAAA;AAE1D,IAAA,IAAI,mBAAA,EAAqB;AAGvB,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,IAAA;AAAA;AAAA,QAChB,KAAA,EACE,wBAAwB,SAAS,CAAA,kNAAA;AAAA,OAGrC;AAAA;AAGF,IAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA,WACvB,KAAA,EAAO;AACd,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,KAAA;AAAA,MAChB,KAAA,EAAO,CAAA,wCAAA,EAA2C,OAAA,GAAU,CAAA,eAAA,EAAkB,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,EAAA,EAAK,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAC1J;AAAA;AAEJ;AAsBA,eAAsB,wBAAA,CAIpB,SACA,OAAA,EACsD;AAEtD,EAAA,MAAM,eAAA,GAAkBA,4BAAAA,CAA4B,OAAA,EAAS,OAAO,CAAA;AACpE,EAAA,IAAI,CAAC,gBAAgB,cAAA,EAAgB;AACnC,IAAA,OAAO,eAAA;AAAA;AAIT,EAAA,IAAI;AACF,IAAA,MAAM,aAAA,GAAgB,QAAQ,QAAA,EAAS;AACvC,IAAA,MAAM,SAAA,GAAY,IAAI,QAAA,CAAS,SAAA,GAAY,aAAa,CAAA,EAAE;AAI1D,IAAA,MAAM,cAAc,SAAA,CAAU,IAAI,IAAI,eAAA,GAAkB,MAAM,CAAA;AAC9D,IAAA,MAAM,iBAAiB,IAAI,OAAA;AAAA,MAAQ,CAAC,CAAA,EAAG,MAAA,KACrC,UAAA,CAAW,MAAM,MAAA,CAAO,IAAI,KAAA,CAAM,sBAAsB,CAAC,CAAA,EAAG,GAAG;AAAA,KACjE;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,WAAA,EAAa,cAAc,CAAC,CAAA;AAAA,aACzC,SAAA,EAAW;AAGlB,MAAA,IACE,SAAA,YAAqB,KAAA,IACrB,SAAA,CAAU,OAAA,KAAY,sBAAA,EACtB;AAEA,QAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA;AAChC;AAGF,IAAA,OAAO,EAAE,gBAAgB,IAAA,EAAK;AAAA,WACvB,KAAA,EAAO;AACd,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,KAAA;AAAA,MAChB,KAAA,EAAO,sCAAsC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACrG;AAAA;AAEJ;;;ACvIO,IAAM,YAAA,GAAe,CAC1B,MAAA,KACyB;AACzB,EAAA,MAAM,WAAA,GAAc,OAAO,OAAA,IAAW,UAAA;AACtC,EAAA,aAAA,CAAc,MAAA,CAAO,WAAW,KAAK,CAAA;AAErC,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,IAAA;AAEJ,EAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,IAAA,MAAM,QAAA,GAAW,MAAA;AACjB,IAAA,IAAA,GAAO,UAAA,CAAW,SAAS,cAAc,CAAA;AACzC,IAAA,OAAA,GAAU,IAAI,gBAAgB,IAAI,CAAA;AAAA,GACpC,MAAA,IAAW,gBAAgB,OAAA,EAAS;AAClC,IAAA,MAAM,cAAe,MAAA,CAA+B,WAAA;AAEpD,IAAA,OAAA,GAAU,IAAI,aAAa,WAAW,CAAA;AAAA,GACxC,MAAO;AACL,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,WAAW,CAAA,CAAE,CAAA;AAAA;AAGnD,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAEF,IAAA,OAAO,IAAA;AAAA,GACT;AAKA,EAAA,MAAM,yBAAyB,YAA6B;AAC1D,IAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,mBAAA,EAAoB;AACvD,IAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,IAAA,KAAA,MAAW,YAAY,YAAA,EAAc;AAEnC,MAAA,IAAI,CAAC,QAAA,CAAS,YAAA,IAAgB,QAAA,CAAS,cAAc,IAAA,EAAM;AACzD,QAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAA,CAAO,SAAS,SAAS,CAAA;AACvD,QAAA,IACE,OAAA,KACC,QAAQ,MAAA,KAAW,SAAA,IAClB,QAAQ,MAAA,KAAW,YAAA,IACnB,OAAA,CAAQ,MAAA,KAAW,SAAA,CAAA,EACrB;AAEA,UAAA,MAAMC,UAAAA,GAAY,qBAAA;AAAA,YAChB,QAAA,CAAS,cAAA;AAAA,YACT,QAAA,CAAS;AAAA,WACX;AACA,UAAA,MAAM,OAAA,CAAQ,8BAAA;AAAA,YACZ,QAAA,CAAS,EAAA;AAAA,gCACL,IAAA,EAAK;AAAA,YACT,QAAA,CAAS,SAAA;AAAA,YACTA;AAAA,WACF;AACA,UAAA;AAAA;AACF;AAIF,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,MAAA,CAAiB;AAAA,QAC3C,SAAS,QAAA,CAAS,OAAA;AAAA,QAClB,SAAS,QAAA,CAAS,OAAA;AAAA,QAClB,aAAa,QAAA,CAAS,WAAA;AAAA,QACtB,UAAU,QAAA,CAAS,QAAA;AAAA,QACnB,SAAA,EAAW,SAAS,SAAA,IAAa,MAAA;AAAA,QACjC,oBAAoB,QAAA,CAAS,kBAAA;AAAA,QAC7B,MAAM,QAAA,CAAS;AAAA,OAChB,CAAA;AAGD,MAAA,MAAM,SAAA,GAAY,qBAAA;AAAA,QAChB,QAAA,CAAS,cAAA;AAAA,QACT,QAAA,CAAS;AAAA,OACX;AACA,MAAA,MAAM,OAAA,CAAQ,8BAAA;AAAA,QACZ,QAAA,CAAS,EAAA;AAAA,4BACL,IAAA,EAAK;AAAA,QACT,KAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,KAAA,EAAA;AAAA;AAGF,IAAA,OAAO,KAAA;AAAA,GACT;AAGA,EAAA,OAAO;AAAA;AAAA,IAEL,MAAA,EAAQ,cAAA;AAAA,MACN,CAAC,GAAA,KACC,OAAA,CAAQ,MAAA,CAAwB,GAAG,CAAA;AAAA,MACrC,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,MAAA,EAAQ,cAAA;AAAA,MACN,CAAC,EAAA,KAAe,OAAA,CAAQ,MAAA,CAAwB,EAAE,CAAA;AAAA,MAClD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,eAAA,EAAiB,cAAA;AAAA,MACf,CAAC,QAAgB,KAAA,EAAgB,MAAA,KAC/B,QAAQ,eAAA,CAAiC,MAAA,EAAQ,OAAO,MAAM,CAAA;AAAA,MAChE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,UAAA,EAAY,cAAA;AAAA,MACV,CAAC,KAAA,EAAgB,MAAA,KACf,OAAA,CAAQ,UAAA,CAA4B,OAAO,MAAM,CAAA;AAAA,MACnD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,MACP,CACE,SAQA,KAAA,EACA,MAAA,KACG,QAAQ,OAAA,CAAyB,OAAA,EAAS,OAAO,MAAM,CAAA;AAAA,MAC5D,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,QAAA,EAAU,CAAC,KAAA,KAAkB,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,IACnD,cAAA,EAAgB,CAAC,UAAA,KAAwB,OAAA,CAAQ,eAAe,UAAU,CAAA;AAAA,IAC1E,mBAAA,EAAqB,CAAC,UAAA,KACpB,OAAA,CAAQ,oBAAoB,UAAU,CAAA;AAAA,IACxC,SAAA,EAAW,cAAA;AAAA,MACT,CAAC,KAAA,KAAkB,OAAA,CAAQ,SAAA,CAAU,KAAK,CAAA;AAAA,MAC1C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,MACP,CACE,KAAA,EACA,OAAA,KACG,OAAA,CAAQ,OAAA,CAAQ,OAAO,OAA4C,CAAA;AAAA,MACxE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,kBAAA,EAAoB,cAAA;AAAA,MAClB,CACE,OAAA,EAaA,OAAA,KAEA,OAAA,CAAQ,kBAAA;AAAA,QACN,OAAA;AAAA,QACA;AAAA,OACF;AAAA,MACF,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,qBAAA,EAAuB,cAAA;AAAA,MACrB,CAAC,OAAA,KAOK,OAAA,CAAQ,qBAAA,CAAsB,OAAO,CAAA;AAAA,MAC3C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,gBAAA,EAAkB,cAAA;AAAA,MAChB,CAAC,wBAAA,KACC,OAAA,CAAQ,gBAAA,CAAiB,wBAAwB,CAAA;AAAA,MACnD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,IAAA,EAAgB,IAAA,GAAO,KAAA,EAAO,KAAA,EAAgB,MAAA,KAC7C,OAAA,CAAQ,aAAA,CAA+B,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAAA,MAClE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,eAAA,EAAiB,CACf,QAAA,EACA,OAAA,KAEA,gBAA4B,OAAA,EAAS,QAAA,EAAU,SAAS,YAAY;AAClE,MAAA,MAAM,sBAAA,EAAuB;AAAA,KAC9B,CAAA;AAAA;AAAA,IAGH,YAAA,EAAc,cAAA;AAAA,MACZ,CAAC,KAAA,KAAkB,OAAA,CAAQ,YAAA,CAAa,KAAK,CAAA;AAAA,MAC7C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,WAAA,EAAa,cAAA;AAAA,MACX,CAAC,OAAA,KACC,eAAA,CAAgB,WAAA,EAAY,EAAG,MAAM,OAAO,CAAA;AAAA,MAC9C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,OAAA,EAAiB,IAAA,KAChB,kBAAkB,WAAA,EAAY,EAAG,SAAS,IAAI,CAAA;AAAA,MAChD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,QAAA,EAAU,cAAA;AAAA,MACR,CAAC,OAAA,KAAoB,YAAA,CAAa,WAAA,IAAe,OAAO,CAAA;AAAA,MACxD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,oBAAA,EAAsB,cAAA;AAAA,MACpB,MAAM,wBAAA,CAAyB,WAAA,EAAa,CAAA;AAAA,MAC5C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,UAAA,EAAY,cAAA;AAAA,MACV,CACE,OAAA,KACG;AACH,QAAA,IAAI,CAAC,sBAAA,CAAuB,OAAA,CAAQ,cAAc,CAAA,EAAG;AACnD,UAAA,OAAO,OAAA,CAAQ,MAAA;AAAA,YACb,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,OAAA,CAAQ,cAAc,CAAA,CAAA,CAAG;AAAA,WAClE;AAAA;AAEF,QAAA,MAAM,SAAA,GAAY,qBAAA;AAAA,UAChB,OAAA,CAAQ,cAAA;AAAA,UACR,QAAQ,QAAA,IAAY;AAAA,SACtB;AACA,QAAA,MAAM,KAAA,GAA2B;AAAA,UAC/B,cAAc,OAAA,CAAQ,YAAA;AAAA,UACtB,gBAAgB,OAAA,CAAQ,cAAA;AAAA,UACxB,SAAS,OAAA,CAAQ,OAAA;AAAA,UACjB,SAAS,OAAA,CAAQ,OAAA;AAAA,UACjB,WAAA,EAAa,QAAQ,WAAA,IAAe,CAAA;AAAA,UACpC,QAAA,EAAU,QAAQ,QAAA,IAAY,CAAA;AAAA,UAC9B,SAAA,EAAW,QAAQ,SAAA,IAAa,IAAA;AAAA,UAChC,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,KAAA;AAAA,UAClD,MAAM,OAAA,CAAQ,IAAA;AAAA,UACd,QAAA,EAAU,QAAQ,QAAA,IAAY,KAAA;AAAA,UAC9B,YAAA,EAAc,QAAQ,YAAA,IAAgB,KAAA;AAAA,UACtC;AAAA,SACF;AACA,QAAA,OAAO,OAAA,CAAQ,gBAAgB,KAAK,CAAA;AAAA,OACtC;AAAA,MACA,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,UAAA,EAAY,cAAA;AAAA,MACV,CAAC,EAAA,KAAe,OAAA,CAAQ,eAAA,CAAgB,EAAE,CAAA;AAAA,MAC1C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,gBAAA,EAAkB,cAAA;AAAA,MAChB,CAAC,IAAA,KAAiB,OAAA,CAAQ,qBAAA,CAAsB,IAAI,CAAA;AAAA,MACpD,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,YAAA,EAAc,cAAA;AAAA,MACZ,CAAC,MAAA,KAAgC,OAAA,CAAQ,iBAAA,CAAkB,MAAM,CAAA;AAAA,MACjE,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,EAAA,KAAe,OAAA,CAAQ,kBAAA,CAAmB,EAAE,CAAA;AAAA,MAC7C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,YAAA,EAAc,cAAA;AAAA,MACZ,CAAC,EAAA,KAAe,OAAA,CAAQ,iBAAA,CAAkB,EAAE,CAAA;AAAA,MAC5C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,MACb,CAAC,EAAA,KAAe,OAAA,CAAQ,kBAAA,CAAmB,EAAE,CAAA;AAAA,MAC7C,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,WAAA,EAAa,cAAA;AAAA,MACX,OAAO,IAAY,OAAA,KAAqC;AACtD,QAAA,IACE,QAAQ,cAAA,KAAmB,MAAA,IAC3B,CAAC,sBAAA,CAAuB,OAAA,CAAQ,cAAc,CAAA,EAC9C;AACA,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,0BAAA,EAA6B,QAAQ,cAAc,CAAA,CAAA;AAAA,WACrD;AAAA;AAEF,QAAA,IAAI,SAAA;AACJ,QAAA,IACE,OAAA,CAAQ,cAAA,KAAmB,MAAA,IAC3B,OAAA,CAAQ,aAAa,MAAA,EACrB;AACA,UAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,eAAA,CAAgB,EAAE,CAAA;AACjD,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,cAAA,IAAkB,QAAA,EAAU,cAAA,IAAkB,EAAA;AACnE,UAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,QAAA,IAAY,QAAA,EAAU,QAAA,IAAY,KAAA;AACrD,UAAA,SAAA,GAAY,qBAAA,CAAsB,MAAM,EAAE,CAAA;AAAA;AAE5C,QAAA,MAAM,OAAA,CAAQ,gBAAA,CAAiB,EAAA,EAAI,OAAA,EAAS,SAAS,CAAA;AAAA,OACvD;AAAA,MACA,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA,IACA,kBAAA,EAAoB,cAAA;AAAA,MAClB,MAAM,sBAAA,EAAuB;AAAA,MAC7B,OAAO,OAAA,IAAW;AAAA,KACpB;AAAA;AAAA,IAGA,SAAS,MAAM;AACb,MAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA;AAEF,MAAA,OAAQ,QAA4B,OAAA,EAAQ;AAAA,KAC9C;AAAA,IACA,gBAAgB,MAAM;AACpB,MAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA;AAEF,MAAA,OAAQ,QAAyB,SAAA,EAAU;AAAA;AAC7C,GACF;AACF;AAEA,IAAM,cAAA,GACJ,CAAI,EAAA,EAA2B,OAAA,KAC/B,IAAI,IAAA,KAAuD;AACzD,EAAA,aAAA,CAAc,OAAO,CAAA;AACrB,EAAA,OAAO,EAAA,CAAG,GAAG,IAAI,CAAA;AACnB,CAAA","file":"index.cjs","sourcesContent":["// Utility type for job type keys\nexport type JobType<PayloadMap> = keyof PayloadMap & string;\n\nexport interface JobOptions<PayloadMap, T extends JobType<PayloadMap>> {\n jobType: T;\n payload: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n /**\n * Timeout for this job in milliseconds. If not set, uses the processor default or unlimited.\n */\n timeoutMs?: number;\n /**\n * If true, the job will be forcefully terminated (using Worker Threads) when timeout is reached.\n * If false (default), the job will only receive an AbortSignal and must handle the abort gracefully.\n *\n * **⚠️ RUNTIME REQUIREMENTS**: This option requires **Node.js** and uses the `worker_threads` module.\n * It will **not work** in Bun or other runtimes that don't support Node.js worker threads.\n *\n * **IMPORTANT**: When `forceKillOnTimeout` is true, the handler must be serializable. This means:\n * - The handler should be a standalone function (not a closure over external variables)\n * - It should not capture variables from outer scopes that reference external dependencies\n * - It should not use 'this' context unless it's a bound method\n * - All dependencies must be importable in the worker thread context\n *\n * **Examples of serializable handlers:**\n * ```ts\n * // ✅ Good - standalone function\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * // ✅ Good - function that imports dependencies\n * const handler = async (payload, signal) => {\n * const { api } = await import('./api');\n * await api.call(payload);\n * };\n *\n * // ❌ Bad - closure over external variable\n * const db = getDatabase();\n * const handler = async (payload, signal) => {\n * await db.query(payload); // 'db' is captured from closure\n * };\n *\n * // ❌ Bad - uses 'this' context\n * class MyHandler {\n * async handle(payload, signal) {\n * await this.doSomething(payload); // 'this' won't work\n * }\n * }\n * ```\n *\n * If your handler doesn't meet these requirements, use `forceKillOnTimeout: false` (default)\n * and ensure your handler checks `signal.aborted` to exit gracefully.\n *\n * Note: forceKillOnTimeout requires timeoutMs to be set.\n */\n forceKillOnTimeout?: boolean;\n /**\n * Tags for this job. Used for grouping, searching, or batch operations.\n */\n tags?: string[];\n /**\n * Optional idempotency key. When provided, ensures that only one job exists for a given key.\n * If a job with the same idempotency key already exists, `addJob` returns the existing job's ID\n * instead of creating a duplicate.\n *\n * Useful for preventing duplicate jobs caused by retries, double-clicks, webhook replays,\n * or serverless function re-invocations.\n *\n * The key is unique across the entire `job_queue` table regardless of job status.\n * Once a key exists, it cannot be reused until the job is cleaned up (via `cleanupOldJobs`).\n */\n idempotencyKey?: string;\n}\n\n/**\n * Options for editing a pending job.\n * All fields are optional and only provided fields will be updated.\n * Note: jobType cannot be changed.\n * timeoutMs and tags can be set to null to clear them.\n */\nexport type EditJobOptions<PayloadMap, T extends JobType<PayloadMap>> = Partial<\n Omit<JobOptions<PayloadMap, T>, 'jobType'>\n> & {\n timeoutMs?: number | null;\n tags?: string[] | null;\n};\n\nexport enum JobEventType {\n Added = 'added',\n Processing = 'processing',\n Completed = 'completed',\n Failed = 'failed',\n Cancelled = 'cancelled',\n Retried = 'retried',\n Edited = 'edited',\n Prolonged = 'prolonged',\n Waiting = 'waiting',\n}\n\nexport interface JobEvent {\n id: number;\n jobId: number;\n eventType: JobEventType;\n createdAt: Date;\n metadata: any;\n}\n\nexport enum FailureReason {\n Timeout = 'timeout',\n HandlerError = 'handler_error',\n NoHandler = 'no_handler',\n}\n\nexport type JobStatus =\n | 'pending'\n | 'processing'\n | 'completed'\n | 'failed'\n | 'cancelled'\n | 'waiting';\n\nexport interface JobRecord<PayloadMap, T extends JobType<PayloadMap>> {\n id: number;\n jobType: T;\n payload: PayloadMap[T];\n status: JobStatus;\n createdAt: Date;\n updatedAt: Date;\n lockedAt: Date | null;\n lockedBy: string | null;\n attempts: number;\n maxAttempts: number;\n nextAttemptAt: Date | null;\n priority: number;\n runAt: Date;\n pendingReason?: string | null;\n errorHistory?: { message: string; timestamp: string }[];\n /**\n * Timeout for this job in milliseconds (null means no timeout).\n */\n timeoutMs?: number | null;\n /**\n * If true, the job will be forcefully terminated (using Worker Threads) when timeout is reached.\n * If false (default), the job will only receive an AbortSignal and must handle the abort gracefully.\n */\n forceKillOnTimeout?: boolean | null;\n /**\n * The reason for the last failure, if any.\n */\n failureReason?: FailureReason | null;\n /**\n * The time the job was completed, if completed.\n */\n completedAt: Date | null;\n /**\n * The time the job was first picked up for processing.\n */\n startedAt: Date | null;\n /**\n * The time the job was last retried.\n */\n lastRetriedAt: Date | null;\n /**\n * The time the job last failed.\n */\n lastFailedAt: Date | null;\n /**\n * The time the job was last cancelled.\n */\n lastCancelledAt: Date | null;\n /**\n * Tags for this job. Used for grouping, searching, or batch operations.\n */\n tags?: string[];\n /**\n * The idempotency key for this job, if one was provided when the job was created.\n */\n idempotencyKey?: string | null;\n /**\n * The time the job is waiting until (for time-based waits).\n */\n waitUntil?: Date | null;\n /**\n * The waitpoint token ID the job is waiting for (for token-based waits).\n */\n waitTokenId?: string | null;\n /**\n * Step data for the job. Stores completed step results for replay on re-invocation.\n */\n stepData?: Record<string, any>;\n /**\n * Progress percentage for the job (0-100), or null if no progress has been reported.\n * Updated by the handler via `ctx.setProgress(percent)`.\n */\n progress?: number | null;\n}\n\n/**\n * Callback registered via `onTimeout`. Invoked when the timeout fires, before the AbortSignal is triggered.\n * Return a number (ms) to extend the timeout, or return nothing to let the timeout proceed.\n */\nexport type OnTimeoutCallback = () => number | void | undefined;\n\n/**\n * Context object passed to job handlers as the third argument.\n * Provides mechanisms to extend the job's timeout while it's running,\n * as well as step tracking and wait capabilities.\n */\nexport interface JobContext {\n /**\n * Proactively reset the timeout deadline.\n * - If `ms` is provided, sets the deadline to `ms` milliseconds from now.\n * - If omitted, resets the deadline to the original `timeoutMs` from now (heartbeat-style).\n * - No-op if the job has no timeout set or if `forceKillOnTimeout` is true.\n */\n prolong: (ms?: number) => void;\n\n /**\n * Register a callback that is invoked when the timeout fires, **before** the AbortSignal is triggered.\n * - If the callback returns a number > 0, the timeout is reset to that many ms from now.\n * - If the callback returns `undefined`, `null`, `0`, or a negative number, the timeout proceeds normally.\n * - The callback may be invoked multiple times if the job keeps extending.\n * - Only one callback can be registered; subsequent calls replace the previous one.\n * - No-op if the job has no timeout set or if `forceKillOnTimeout` is true.\n */\n onTimeout: (callback: OnTimeoutCallback) => void;\n\n /**\n * Execute a named step with memoization. If the step was already completed\n * in a previous invocation (e.g., before a wait), the cached result is returned\n * without re-executing the function.\n *\n * Step names must be unique within a handler and stable across re-invocations.\n *\n * @param stepName - A unique identifier for this step.\n * @param fn - The function to execute. Its return value is cached.\n * @returns The result of the step (from cache or fresh execution).\n */\n run: <T>(stepName: string, fn: () => Promise<T>) => Promise<T>;\n\n /**\n * Wait for a specified duration before continuing execution.\n * The job will be paused and resumed after the duration elapses.\n *\n * When this is called, the handler throws a WaitSignal internally.\n * The job is set to 'waiting' status and will be re-invoked after the\n * specified duration. All steps completed via `ctx.run()` before this\n * call will be replayed from cache on re-invocation.\n *\n * @param duration - The duration to wait (e.g., `{ hours: 1 }`, `{ days: 7 }`).\n */\n waitFor: (duration: WaitDuration) => Promise<void>;\n\n /**\n * Wait until a specific date/time before continuing execution.\n * The job will be paused and resumed at (or after) the specified date.\n *\n * @param date - The date to wait until.\n */\n waitUntil: (date: Date) => Promise<void>;\n\n /**\n * Create a waitpoint token. The token can be completed externally\n * (by calling `jobQueue.completeToken()`) to resume a waiting job.\n *\n * Tokens can be created inside handlers or outside (via `jobQueue.createToken()`).\n *\n * @param options - Optional token configuration (timeout, tags).\n * @returns A token object with `id` that can be passed to `waitForToken()`.\n */\n createToken: (options?: CreateTokenOptions) => Promise<WaitToken>;\n\n /**\n * Wait for a waitpoint token to be completed by an external signal.\n * The job will be paused until `jobQueue.completeToken(tokenId, data)` is called\n * or the token times out.\n *\n * @param tokenId - The ID of the token to wait for.\n * @returns A result object indicating success or timeout.\n */\n waitForToken: <T = any>(tokenId: string) => Promise<WaitTokenResult<T>>;\n\n /**\n * Report progress for this job (0-100).\n * The value is persisted to the database and can be read by clients\n * via `getJob()` or the React SDK's `useJob()` hook.\n *\n * @param percent - Progress percentage (0-100). Values are rounded to the nearest integer.\n * @throws If percent is outside the 0-100 range.\n */\n setProgress: (percent: number) => Promise<void>;\n}\n\n/**\n * Duration specification for `ctx.waitFor()`.\n * At least one field must be provided. Fields are additive.\n */\nexport interface WaitDuration {\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n weeks?: number;\n months?: number;\n years?: number;\n}\n\n/**\n * Options for creating a waitpoint token.\n */\nexport interface CreateTokenOptions {\n /**\n * Maximum time to wait for the token to be completed.\n * Accepts a duration string like '10m', '1h', '24h', '7d'.\n * If not provided, the token has no timeout.\n */\n timeout?: string;\n /**\n * Tags to attach to the token for filtering.\n */\n tags?: string[];\n}\n\n/**\n * A waitpoint token returned by `ctx.createToken()`.\n */\nexport interface WaitToken {\n /** The unique token ID. */\n id: string;\n}\n\n/**\n * Result of `ctx.waitForToken()`.\n */\nexport type WaitTokenResult<T = any> =\n | { ok: true; output: T }\n | { ok: false; error: string };\n\n/**\n * Internal signal thrown by wait methods to pause handler execution.\n * This is not a real error -- the processor catches it and transitions the job to 'waiting' status.\n */\nexport class WaitSignal extends Error {\n readonly isWaitSignal = true;\n\n constructor(\n public readonly type: 'duration' | 'date' | 'token',\n public readonly waitUntil: Date | undefined,\n public readonly tokenId: string | undefined,\n public readonly stepData: Record<string, any>,\n ) {\n super('WaitSignal');\n this.name = 'WaitSignal';\n }\n}\n\n/**\n * Status of a waitpoint token.\n */\nexport type WaitpointStatus = 'waiting' | 'completed' | 'timed_out';\n\n/**\n * A waitpoint record from the database.\n */\nexport interface WaitpointRecord {\n id: string;\n jobId: number | null;\n status: WaitpointStatus;\n output: any;\n timeoutAt: Date | null;\n createdAt: Date;\n completedAt: Date | null;\n tags: string[] | null;\n}\n\nexport type JobHandler<PayloadMap, T extends keyof PayloadMap> = (\n payload: PayloadMap[T],\n signal: AbortSignal,\n ctx: JobContext,\n) => Promise<void>;\n\nexport type JobHandlers<PayloadMap> = {\n [K in keyof PayloadMap]: JobHandler<PayloadMap, K>;\n};\n\nexport interface ProcessorOptions {\n workerId?: string;\n /**\n * The number of jobs to process at a time.\n * - If not provided, the processor will process 10 jobs at a time.\n * - In serverless functions, it's better to process less jobs at a time since serverless functions are charged by the second and have a timeout.\n */\n batchSize?: number;\n /**\n * The maximum number of jobs to process in parallel per batch.\n * - If not provided, all jobs in the batch are processed in parallel.\n * - Set to 1 to process jobs sequentially.\n * - Set to a lower value to avoid resource exhaustion.\n */\n concurrency?: number;\n /**\n * The interval in milliseconds to poll for new jobs.\n * - If not provided, the processor will process jobs every 5 seconds when startInBackground is called.\n * - In serverless functions, it's better to leave this empty.\n * - If you call start instead of startInBackground, the pollInterval is ignored.\n */\n pollInterval?: number;\n onError?: (error: Error) => void;\n verbose?: boolean;\n /**\n * Only process jobs with this job type (string or array of strings). If omitted, all job types are processed.\n */\n jobType?: string | string[];\n}\n\nexport interface Processor {\n /**\n * Start the job processor in the background.\n * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs (as many as batchSize) as they become available.\n * - **You have to call the stop method to stop the processor.**\n * - Handlers are provided per-processor when calling createProcessor.\n * - In serverless functions, it's recommended to call start instead and await it to finish.\n */\n startInBackground: () => void;\n /**\n * Stop the job processor that runs in the background.\n * Does not wait for in-flight jobs to complete.\n */\n stop: () => void;\n /**\n * Stop the job processor and wait for all in-flight jobs to complete.\n * Useful for graceful shutdown (e.g., SIGTERM handling).\n * No new batches will be started after calling this method.\n *\n * @param timeoutMs - Maximum time to wait for in-flight jobs (default: 30000ms).\n * If jobs don't complete within this time, the promise resolves anyway.\n */\n stopAndDrain: (timeoutMs?: number) => Promise<void>;\n /**\n * Check if the job processor is running.\n */\n isRunning: () => boolean;\n /**\n * Start the job processor synchronously.\n * - This will process jobs (as many as batchSize) immediately and then stop. The pollInterval is ignored.\n * - In serverless functions, it's recommended to use this instead of startInBackground.\n * - Returns the number of jobs processed.\n */\n start: () => Promise<number>;\n}\n\nexport interface DatabaseSSLConfig {\n /**\n * CA certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n ca?: string;\n /**\n * Client certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n cert?: string;\n /**\n * Client private key as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.\n */\n key?: string;\n /**\n * Whether to reject unauthorized certificates (default: true)\n */\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Configuration for PostgreSQL backend (default).\n * Backward-compatible: omitting `backend` defaults to 'postgres'.\n */\nexport interface PostgresJobQueueConfig {\n backend?: 'postgres';\n databaseConfig: {\n connectionString?: string;\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n password?: string;\n ssl?: DatabaseSSLConfig;\n };\n verbose?: boolean;\n}\n\n/**\n * TLS configuration for the Redis connection.\n */\nexport interface RedisTLSConfig {\n ca?: string;\n cert?: string;\n key?: string;\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Configuration for Redis backend.\n */\nexport interface RedisJobQueueConfig {\n backend: 'redis';\n redisConfig: {\n /** Redis URL (e.g. redis://localhost:6379) */\n url?: string;\n host?: string;\n port?: number;\n password?: string;\n /** Redis database number (default: 0) */\n db?: number;\n tls?: RedisTLSConfig;\n /**\n * Key prefix for all Redis keys (default: 'dq:').\n * Useful to namespace multiple queues in the same Redis instance.\n */\n keyPrefix?: string;\n };\n verbose?: boolean;\n}\n\n/**\n * Job queue configuration — discriminated union.\n * If `backend` is omitted, PostgreSQL is used.\n */\nexport type JobQueueConfig = PostgresJobQueueConfig | RedisJobQueueConfig;\n\n/** @deprecated Use JobQueueConfig instead. Alias kept for backward compat. */\nexport type JobQueueConfigLegacy = PostgresJobQueueConfig;\n\nexport type TagQueryMode = 'exact' | 'all' | 'any' | 'none';\n\n// ── Cron schedule types ──────────────────────────────────────────────\n\n/**\n * Status of a cron schedule.\n */\nexport type CronScheduleStatus = 'active' | 'paused';\n\n/**\n * Options for creating a recurring cron schedule.\n * Each schedule defines a recurring job that is automatically enqueued\n * when its cron expression matches.\n */\nexport interface CronScheduleOptions<\n PayloadMap,\n T extends JobType<PayloadMap>,\n> {\n /** Unique human-readable name for the schedule. */\n scheduleName: string;\n /** Standard cron expression (5 fields, e.g. \"0 * * * *\"). */\n cronExpression: string;\n /** Job type from the PayloadMap. */\n jobType: T;\n /** Payload for each job instance. */\n payload: PayloadMap[T];\n /** Maximum retry attempts for each job instance (default: 3). */\n maxAttempts?: number;\n /** Priority for each job instance (default: 0). */\n priority?: number;\n /** Timeout in milliseconds for each job instance. */\n timeoutMs?: number;\n /** Whether to force-kill the job on timeout (default: false). */\n forceKillOnTimeout?: boolean;\n /** Tags for each job instance. */\n tags?: string[];\n /** IANA timezone string for cron evaluation (default: \"UTC\"). */\n timezone?: string;\n /**\n * Whether to allow overlapping job instances (default: false).\n * When false, a new job will not be enqueued if the previous instance\n * is still pending, processing, or waiting.\n */\n allowOverlap?: boolean;\n}\n\n/**\n * A persisted cron schedule record.\n */\nexport interface CronScheduleRecord {\n id: number;\n scheduleName: string;\n cronExpression: string;\n jobType: string;\n payload: any;\n maxAttempts: number;\n priority: number;\n timeoutMs: number | null;\n forceKillOnTimeout: boolean;\n tags: string[] | undefined;\n timezone: string;\n allowOverlap: boolean;\n status: CronScheduleStatus;\n lastEnqueuedAt: Date | null;\n lastJobId: number | null;\n nextRunAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/**\n * Options for editing an existing cron schedule.\n * All fields are optional; only provided fields are updated.\n */\nexport interface EditCronScheduleOptions {\n cronExpression?: string;\n payload?: any;\n maxAttempts?: number;\n priority?: number;\n timeoutMs?: number | null;\n forceKillOnTimeout?: boolean;\n tags?: string[] | null;\n timezone?: string;\n allowOverlap?: boolean;\n}\n\nexport interface JobQueue<PayloadMap> {\n /**\n * Add a job to the job queue.\n */\n addJob: <T extends JobType<PayloadMap>>(\n job: JobOptions<PayloadMap, T>,\n ) => Promise<number>;\n /**\n * Get a job by its ID.\n */\n getJob: <T extends JobType<PayloadMap>>(\n id: number,\n ) => Promise<JobRecord<PayloadMap, T> | null>;\n /**\n * Get jobs by their status, with pagination.\n * - If no limit is provided, all jobs are returned.\n * - If no offset is provided, the first page is returned.\n * - The jobs are returned in descending order of createdAt.\n */\n getJobsByStatus: <T extends JobType<PayloadMap>>(\n status: JobStatus,\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get jobs by tag(s).\n * - Modes:\n * - 'exact': Jobs with exactly the same tags (no more, no less)\n * - 'all': Jobs that have all the given tags (can have more)\n * - 'any': Jobs that have at least one of the given tags\n * - 'none': Jobs that have none of the given tags\n * - Default mode is 'all'.\n */\n getJobsByTags: <T extends JobType<PayloadMap>>(\n tags: string[],\n mode?: TagQueryMode,\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get all jobs.\n */\n getAllJobs: <T extends JobType<PayloadMap>>(\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Get jobs by filters, with pagination support.\n * - Use `cursor` for efficient keyset pagination (recommended for large datasets).\n * - Use `limit` and `offset` for traditional pagination.\n * - Do not combine `cursor` with `offset`.\n */\n getJobs: <T extends JobType<PayloadMap>>(\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n /** Cursor for keyset pagination. Only return jobs with id < cursor. */\n cursor?: number;\n },\n limit?: number,\n offset?: number,\n ) => Promise<JobRecord<PayloadMap, T>[]>;\n /**\n * Retry a job given its ID.\n * - This will set the job status back to 'pending', clear the locked_at and locked_by, and allow it to be picked up by other workers.\n */\n retryJob: (jobId: number) => Promise<void>;\n /**\n * Cleanup jobs that are older than the specified number of days.\n */\n cleanupOldJobs: (daysToKeep?: number) => Promise<number>;\n /**\n * Cleanup job events that are older than the specified number of days.\n */\n cleanupOldJobEvents: (daysToKeep?: number) => Promise<number>;\n /**\n * Cancel a job given its ID.\n * - This will set the job status to 'cancelled' and clear the locked_at and locked_by.\n */\n cancelJob: (jobId: number) => Promise<void>;\n /**\n * Edit a pending job given its ID.\n * - Only works for jobs with status 'pending'. Silently fails for other statuses.\n * - All fields in EditJobOptions are optional - only provided fields will be updated.\n * - jobType cannot be changed.\n * - Records an 'edited' event with the updated fields in metadata.\n */\n editJob: <T extends JobType<PayloadMap>>(\n jobId: number,\n updates: EditJobOptions<PayloadMap, T>,\n ) => Promise<void>;\n /**\n * Edit all pending jobs that match the filters.\n * - Only works for jobs with status 'pending'. Non-pending jobs are not affected.\n * - All fields in EditJobOptions are optional - only provided fields will be updated.\n * - jobType cannot be changed.\n * - Records an 'edited' event with the updated fields in metadata for each affected job.\n * - Returns the number of jobs that were edited.\n * - The filters are:\n * - jobType: The job type to edit.\n * - priority: The priority of the job to edit.\n * - runAt: The time the job is scheduled to run at (now supports gt/gte/lt/lte/eq).\n * - tags: An object with 'values' (string[]) and 'mode' (TagQueryMode) for tag-based editing.\n */\n editAllPendingJobs: <T extends JobType<PayloadMap>>(\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }\n | undefined,\n updates: EditJobOptions<PayloadMap, T>,\n ) => Promise<number>;\n /**\n * Reclaim stuck jobs.\n * - If a process (e.g., API route or worker) crashes after marking a job as 'processing' but before completing it, the job can remain stuck in the 'processing' state indefinitely. This can happen if the process is killed or encounters an unhandled error after updating the job status but before marking it as 'completed' or 'failed'.\n * - This function will set the job status back to 'pending', clear the locked_at and locked_by, and allow it to be picked up by other workers.\n * - The default max processing time is 10 minutes.\n */\n reclaimStuckJobs: (maxProcessingTimeMinutes?: number) => Promise<number>;\n /**\n * Cancel all upcoming jobs that match the filters.\n * - If no filters are provided, all upcoming jobs are cancelled.\n * - If filters are provided, only jobs that match the filters are cancelled.\n * - The filters are:\n * - jobType: The job type to cancel.\n * - priority: The priority of the job to cancel.\n * - runAt: The time the job is scheduled to run at (now supports gt/gte/lt/lte/eq).\n * - tags: An object with 'values' (string[]) and 'mode' (TagQueryMode) for tag-based cancellation.\n */\n cancelAllUpcomingJobs: (filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }) => Promise<number>;\n /**\n * Create a job processor. Handlers must be provided per-processor.\n */\n createProcessor: (\n handlers: JobHandlers<PayloadMap>,\n options?: ProcessorOptions,\n ) => Processor;\n\n /**\n * Get the job events for a job.\n */\n getJobEvents: (jobId: number) => Promise<JobEvent[]>;\n\n /**\n * Create a waitpoint token.\n * Tokens can be completed externally to resume a waiting job.\n * Can be called outside of handlers (e.g., from an API route).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param options - Optional token configuration (timeout, tags).\n * @returns A token object with `id`.\n */\n createToken: (options?: CreateTokenOptions) => Promise<WaitToken>;\n\n /**\n * Complete a waitpoint token, resuming the associated waiting job.\n * Can be called from anywhere (API routes, external services, etc.).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param tokenId - The ID of the token to complete.\n * @param data - Optional data to pass to the waiting handler.\n */\n completeToken: (tokenId: string, data?: any) => Promise<void>;\n\n /**\n * Retrieve a waitpoint token by its ID.\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @param tokenId - The ID of the token to retrieve.\n * @returns The token record, or null if not found.\n */\n getToken: (tokenId: string) => Promise<WaitpointRecord | null>;\n\n /**\n * Expire timed-out waitpoint tokens and resume their associated jobs.\n * Call this periodically (e.g., alongside `reclaimStuckJobs`).\n *\n * **PostgreSQL backend only.** Throws if the backend is Redis.\n *\n * @returns The number of tokens that were expired.\n */\n expireTimedOutTokens: () => Promise<number>;\n\n // ── Cron schedule operations ────────────────────────────────────────\n\n /**\n * Add a recurring cron schedule. The processor automatically enqueues\n * due cron jobs before each batch, so no manual triggering is needed.\n *\n * @returns The ID of the created schedule.\n * @throws If the cron expression is invalid or the schedule name is already taken.\n */\n addCronJob: <T extends JobType<PayloadMap>>(\n options: CronScheduleOptions<PayloadMap, T>,\n ) => Promise<number>;\n\n /**\n * Get a cron schedule by its ID.\n */\n getCronJob: (id: number) => Promise<CronScheduleRecord | null>;\n\n /**\n * Get a cron schedule by its unique name.\n */\n getCronJobByName: (name: string) => Promise<CronScheduleRecord | null>;\n\n /**\n * List all cron schedules, optionally filtered by status.\n */\n listCronJobs: (status?: CronScheduleStatus) => Promise<CronScheduleRecord[]>;\n\n /**\n * Remove a cron schedule by its ID. Does not cancel any already-enqueued jobs.\n */\n removeCronJob: (id: number) => Promise<void>;\n\n /**\n * Pause a cron schedule. Paused schedules are skipped by `enqueueDueCronJobs()`.\n */\n pauseCronJob: (id: number) => Promise<void>;\n\n /**\n * Resume a paused cron schedule.\n */\n resumeCronJob: (id: number) => Promise<void>;\n\n /**\n * Edit an existing cron schedule. Only provided fields are updated.\n * If `cronExpression` or `timezone` changes, `nextRunAt` is recalculated.\n */\n editCronJob: (id: number, updates: EditCronScheduleOptions) => Promise<void>;\n\n /**\n * Check all active cron schedules and enqueue jobs for any whose\n * `nextRunAt` has passed. When `allowOverlap` is false (the default),\n * a new job is not enqueued if the previous instance is still\n * pending, processing, or waiting.\n *\n * **Note:** The processor calls this automatically before each batch,\n * so you typically do not need to call it yourself. It is exposed for\n * manual use in tests or one-off scripts.\n *\n * @returns The number of jobs that were enqueued.\n */\n enqueueDueCronJobs: () => Promise<number>;\n\n // ── Advanced access ───────────────────────────────────────────────────\n\n /**\n * Get the PostgreSQL database pool.\n * Throws if the backend is not PostgreSQL.\n */\n getPool: () => import('pg').Pool;\n /**\n * Get the Redis client instance (ioredis).\n * Throws if the backend is not Redis.\n */\n getRedisClient: () => unknown;\n}\n","import { AsyncLocalStorage } from 'async_hooks';\n\nexport const logStorage = new AsyncLocalStorage<{\n verbose: boolean;\n}>();\n\nexport const setLogContext = (verbose: boolean) => {\n logStorage.enterWith({ verbose });\n};\n\nexport const getLogContext = () => {\n return logStorage.getStore();\n};\n\nexport const log = (message: string) => {\n const context = getLogContext();\n if (context?.verbose) {\n console.log(message);\n }\n};\n","import { Pool } from 'pg';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n JobType,\n CronScheduleRecord,\n CronScheduleStatus,\n EditCronScheduleOptions,\n} from '../types.js';\nimport {\n QueueBackend,\n JobFilters,\n JobUpdates,\n CronScheduleInput,\n} from '../backend.js';\nimport { log } from '../log-context.js';\n\nexport class PostgresBackend implements QueueBackend {\n constructor(private pool: Pool) {}\n\n /** Expose the raw pool for advanced usage. */\n getPool(): Pool {\n return this.pool;\n }\n\n // ── Events ──────────────────────────────────────────────────────────\n\n async recordJobEvent(\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `INSERT INTO job_events (job_id, event_type, metadata) VALUES ($1, $2, $3)`,\n [jobId, eventType, metadata ? JSON.stringify(metadata) : null],\n );\n } catch (error) {\n log(`Error recording job event for job ${jobId}: ${error}`);\n // Do not throw, to avoid interfering with main job logic\n } finally {\n client.release();\n }\n }\n\n async getJobEvents(jobId: number): Promise<JobEvent[]> {\n const client = await this.pool.connect();\n try {\n const res = await client.query(\n `SELECT id, job_id AS \"jobId\", event_type AS \"eventType\", metadata, created_at AS \"createdAt\" FROM job_events WHERE job_id = $1 ORDER BY created_at ASC`,\n [jobId],\n );\n return res.rows as JobEvent[];\n } finally {\n client.release();\n }\n }\n\n // ── Job CRUD ──────────────────────────────────────────────────────────\n\n async addJob<PayloadMap, T extends JobType<PayloadMap>>({\n jobType,\n payload,\n maxAttempts = 3,\n priority = 0,\n runAt = null,\n timeoutMs = undefined,\n forceKillOnTimeout = false,\n tags = undefined,\n idempotencyKey = undefined,\n }: JobOptions<PayloadMap, T>): Promise<number> {\n const client = await this.pool.connect();\n try {\n let result;\n const onConflict = idempotencyKey\n ? `ON CONFLICT (idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING`\n : '';\n\n if (runAt) {\n result = await client.query(\n `INSERT INTO job_queue \n (job_type, payload, max_attempts, priority, run_at, timeout_ms, force_kill_on_timeout, tags, idempotency_key) \n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) \n ${onConflict}\n RETURNING id`,\n [\n jobType,\n payload,\n maxAttempts,\n priority,\n runAt,\n timeoutMs ?? null,\n forceKillOnTimeout ?? false,\n tags ?? null,\n idempotencyKey ?? null,\n ],\n );\n } else {\n result = await client.query(\n `INSERT INTO job_queue \n (job_type, payload, max_attempts, priority, timeout_ms, force_kill_on_timeout, tags, idempotency_key) \n VALUES ($1, $2, $3, $4, $5, $6, $7, $8) \n ${onConflict}\n RETURNING id`,\n [\n jobType,\n payload,\n maxAttempts,\n priority,\n timeoutMs ?? null,\n forceKillOnTimeout ?? false,\n tags ?? null,\n idempotencyKey ?? null,\n ],\n );\n }\n\n // If ON CONFLICT DO NOTHING was triggered, no rows are returned.\n if (result.rows.length === 0 && idempotencyKey) {\n const existing = await client.query(\n `SELECT id FROM job_queue WHERE idempotency_key = $1`,\n [idempotencyKey],\n );\n if (existing.rows.length > 0) {\n log(\n `Job with idempotency key \"${idempotencyKey}\" already exists (id: ${existing.rows[0].id}), returning existing job`,\n );\n return existing.rows[0].id;\n }\n throw new Error(\n `Failed to insert job and could not find existing job with idempotency key \"${idempotencyKey}\"`,\n );\n }\n\n const jobId = result.rows[0].id;\n log(\n `Added job ${jobId}: payload ${JSON.stringify(payload)}, ${runAt ? `runAt ${runAt.toISOString()}, ` : ''}priority ${priority}, maxAttempts ${maxAttempts}, jobType ${jobType}, tags ${JSON.stringify(tags)}${idempotencyKey ? `, idempotencyKey \"${idempotencyKey}\"` : ''}`,\n );\n await this.recordJobEvent(jobId, JobEventType.Added, {\n jobType,\n payload,\n tags,\n idempotencyKey,\n });\n return jobId;\n } catch (error) {\n log(`Error adding job: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJob<PayloadMap, T extends JobType<PayloadMap>>(\n id: number,\n ): Promise<JobRecord<PayloadMap, T> | null> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue WHERE id = $1`,\n [id],\n );\n\n if (result.rows.length === 0) {\n log(`Job ${id} not found`);\n return null;\n }\n\n log(`Found job ${id}`);\n const job = result.rows[0] as JobRecord<PayloadMap, T>;\n return {\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n };\n } catch (error) {\n log(`Error getting job ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobsByStatus<PayloadMap, T extends JobType<PayloadMap>>(\n status: string,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue WHERE status = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3`,\n [status, limit, offset],\n );\n log(`Found ${result.rows.length} jobs by status ${status}`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(`Error getting jobs by status ${status}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getAllJobs<PayloadMap, T extends JobType<PayloadMap>>(\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue ORDER BY created_at DESC LIMIT $1 OFFSET $2`,\n [limit, offset],\n );\n log(`Found ${result.rows.length} jobs (all)`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n }));\n } catch (error) {\n log(`Error getting all jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobs<PayloadMap, T extends JobType<PayloadMap>>(\n filters?: JobFilters,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n let query = `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress FROM job_queue`;\n const params: any[] = [];\n const where: string[] = [];\n let paramIdx = 1;\n if (filters) {\n if (filters.jobType) {\n where.push(`job_type = $${paramIdx++}`);\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n where.push(`priority = $${paramIdx++}`);\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n where.push(`run_at = $${paramIdx++}`);\n params.push(filters.runAt);\n } else if (\n typeof filters.runAt === 'object' &&\n (filters.runAt.gt !== undefined ||\n filters.runAt.gte !== undefined ||\n filters.runAt.lt !== undefined ||\n filters.runAt.lte !== undefined ||\n filters.runAt.eq !== undefined)\n ) {\n const ops = filters.runAt as {\n gt?: Date;\n gte?: Date;\n lt?: Date;\n lte?: Date;\n eq?: Date;\n };\n if (ops.gt) {\n where.push(`run_at > $${paramIdx++}`);\n params.push(ops.gt);\n }\n if (ops.gte) {\n where.push(`run_at >= $${paramIdx++}`);\n params.push(ops.gte);\n }\n if (ops.lt) {\n where.push(`run_at < $${paramIdx++}`);\n params.push(ops.lt);\n }\n if (ops.lte) {\n where.push(`run_at <= $${paramIdx++}`);\n params.push(ops.lte);\n }\n if (ops.eq) {\n where.push(`run_at = $${paramIdx++}`);\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n where.push(`tags = $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'all':\n where.push(`tags @> $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'any':\n where.push(`tags && $${paramIdx++}`);\n params.push(tagValues);\n break;\n case 'none':\n where.push(`NOT (tags && $${paramIdx++})`);\n params.push(tagValues);\n break;\n default:\n where.push(`tags @> $${paramIdx++}`);\n params.push(tagValues);\n }\n }\n // Keyset pagination: use cursor (id < cursor) instead of OFFSET\n if (filters.cursor !== undefined) {\n where.push(`id < $${paramIdx++}`);\n params.push(filters.cursor);\n }\n }\n if (where.length > 0) {\n query += ` WHERE ${where.join(' AND ')}`;\n }\n paramIdx = params.length + 1;\n // Use ORDER BY id DESC for consistent keyset pagination\n query += ` ORDER BY id DESC LIMIT $${paramIdx++}`;\n // Only apply OFFSET when cursor is not used\n if (!filters?.cursor) {\n query += ` OFFSET $${paramIdx}`;\n params.push(limit, offset);\n } else {\n params.push(limit);\n }\n const result = await client.query(query, params);\n log(`Found ${result.rows.length} jobs`);\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(`Error getting jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getJobsByTags<PayloadMap, T extends JobType<PayloadMap>>(\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n let query = `SELECT id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_failed_at AS \"lastFailedAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", tags, idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress\n FROM job_queue`;\n let params: any[] = [];\n switch (mode) {\n case 'exact':\n query += ' WHERE tags = $1';\n params = [tags];\n break;\n case 'all':\n query += ' WHERE tags @> $1';\n params = [tags];\n break;\n case 'any':\n query += ' WHERE tags && $1';\n params = [tags];\n break;\n case 'none':\n query += ' WHERE NOT (tags && $1)';\n params = [tags];\n break;\n default:\n query += ' WHERE tags @> $1';\n params = [tags];\n }\n query += ' ORDER BY created_at DESC LIMIT $2 OFFSET $3';\n params.push(limit, offset);\n const result = await client.query(query, params);\n log(\n `Found ${result.rows.length} jobs by tags ${JSON.stringify(tags)} (mode: ${mode})`,\n );\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n failureReason: job.failureReason,\n }));\n } catch (error) {\n log(\n `Error getting jobs by tags ${JSON.stringify(tags)} (mode: ${mode}): ${error}`,\n );\n throw error;\n } finally {\n client.release();\n }\n }\n\n // ── Processing lifecycle ──────────────────────────────────────────────\n\n async getNextBatch<PayloadMap, T extends JobType<PayloadMap>>(\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n let jobTypeFilter = '';\n const params: any[] = [workerId, batchSize];\n if (jobType) {\n if (Array.isArray(jobType)) {\n jobTypeFilter = ` AND job_type = ANY($3)`;\n params.push(jobType);\n } else {\n jobTypeFilter = ` AND job_type = $3`;\n params.push(jobType);\n }\n }\n\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'processing', \n locked_at = NOW(), \n locked_by = $1,\n attempts = CASE WHEN status = 'waiting' THEN attempts ELSE attempts + 1 END,\n updated_at = NOW(),\n pending_reason = NULL,\n started_at = COALESCE(started_at, NOW()),\n last_retried_at = CASE WHEN status != 'waiting' AND attempts > 0 THEN NOW() ELSE last_retried_at END,\n wait_until = NULL\n WHERE id IN (\n SELECT id FROM job_queue\n WHERE (\n (\n (status = 'pending' OR (status = 'failed' AND next_attempt_at <= NOW()))\n AND (attempts < max_attempts)\n AND run_at <= NOW()\n )\n OR (\n status = 'waiting'\n AND wait_until IS NOT NULL\n AND wait_until <= NOW()\n AND wait_token_id IS NULL\n )\n )\n ${jobTypeFilter}\n ORDER BY priority DESC, created_at ASC\n LIMIT $2\n FOR UPDATE SKIP LOCKED\n )\n RETURNING id, job_type AS \"jobType\", payload, status, max_attempts AS \"maxAttempts\", attempts, priority, run_at AS \"runAt\", timeout_ms AS \"timeoutMs\", force_kill_on_timeout AS \"forceKillOnTimeout\", created_at AS \"createdAt\", updated_at AS \"updatedAt\", started_at AS \"startedAt\", completed_at AS \"completedAt\", last_failed_at AS \"lastFailedAt\", locked_at AS \"lockedAt\", locked_by AS \"lockedBy\", error_history AS \"errorHistory\", failure_reason AS \"failureReason\", next_attempt_at AS \"nextAttemptAt\", last_retried_at AS \"lastRetriedAt\", last_cancelled_at AS \"lastCancelledAt\", pending_reason AS \"pendingReason\", idempotency_key AS \"idempotencyKey\", wait_until AS \"waitUntil\", wait_token_id AS \"waitTokenId\", step_data AS \"stepData\", progress\n `,\n params,\n );\n\n log(`Found ${result.rows.length} jobs to process`);\n await client.query('COMMIT');\n\n // Batch-insert processing events in a single query\n if (result.rows.length > 0) {\n await this.recordJobEventsBatch(\n result.rows.map((row) => ({\n jobId: row.id,\n eventType: JobEventType.Processing,\n })),\n );\n }\n\n return result.rows.map((job) => ({\n ...job,\n payload: job.payload,\n timeoutMs: job.timeoutMs,\n forceKillOnTimeout: job.forceKillOnTimeout,\n }));\n } catch (error) {\n log(`Error getting next batch: ${error}`);\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n }\n\n async completeJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'completed', updated_at = NOW(), completed_at = NOW(),\n step_data = NULL, wait_until = NULL, wait_token_id = NULL\n WHERE id = $1 AND status = 'processing'\n `,\n [jobId],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be completed (not in processing state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Completed);\n log(`Completed job ${jobId}`);\n } catch (error) {\n log(`Error completing job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async failJob(\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'failed', \n updated_at = NOW(),\n next_attempt_at = CASE \n WHEN attempts < max_attempts THEN NOW() + (POWER(2, attempts) * INTERVAL '1 minute')\n ELSE NULL\n END,\n error_history = COALESCE(error_history, '[]'::jsonb) || $2::jsonb,\n failure_reason = $3,\n last_failed_at = NOW()\n WHERE id = $1 AND status IN ('processing', 'pending')\n `,\n [\n jobId,\n JSON.stringify([\n {\n message: error.message || String(error),\n timestamp: new Date().toISOString(),\n },\n ]),\n failureReason ?? null,\n ],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be failed (not in processing/pending state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Failed, {\n message: error.message || String(error),\n failureReason,\n });\n log(`Failed job ${jobId}`);\n } catch (err) {\n log(`Error failing job ${jobId}: ${err}`);\n throw err;\n } finally {\n client.release();\n }\n }\n\n async prolongJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `\n UPDATE job_queue\n SET locked_at = NOW(), updated_at = NOW()\n WHERE id = $1 AND status = 'processing'\n `,\n [jobId],\n );\n await this.recordJobEvent(jobId, JobEventType.Prolonged);\n log(`Prolonged job ${jobId}`);\n } catch (error) {\n log(`Error prolonging job ${jobId}: ${error}`);\n // Do not throw -- prolong is best-effort\n } finally {\n client.release();\n }\n }\n\n // ── Progress ──────────────────────────────────────────────────────────\n\n async updateProgress(jobId: number, progress: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `UPDATE job_queue SET progress = $2, updated_at = NOW() WHERE id = $1`,\n [jobId, progress],\n );\n log(`Updated progress for job ${jobId}: ${progress}%`);\n } catch (error) {\n log(`Error updating progress for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n } finally {\n client.release();\n }\n }\n\n // ── Job management ────────────────────────────────────────────────────\n\n async retryJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'pending', \n updated_at = NOW(),\n locked_at = NULL,\n locked_by = NULL,\n next_attempt_at = NOW(),\n last_retried_at = NOW()\n WHERE id = $1 AND status IN ('failed', 'processing')\n `,\n [jobId],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be retried (not in failed/processing state or does not exist)`,\n );\n }\n await this.recordJobEvent(jobId, JobEventType.Retried);\n log(`Retried job ${jobId}`);\n } catch (error) {\n log(`Error retrying job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cancelJob(jobId: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `\n UPDATE job_queue\n SET status = 'cancelled', updated_at = NOW(), last_cancelled_at = NOW(),\n wait_until = NULL, wait_token_id = NULL\n WHERE id = $1 AND status IN ('pending', 'waiting')\n `,\n [jobId],\n );\n await this.recordJobEvent(jobId, JobEventType.Cancelled);\n log(`Cancelled job ${jobId}`);\n } catch (error) {\n log(`Error cancelling job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cancelAllUpcomingJobs(filters?: JobFilters): Promise<number> {\n const client = await this.pool.connect();\n try {\n let query = `\n UPDATE job_queue\n SET status = 'cancelled', updated_at = NOW()\n WHERE status = 'pending'`;\n const params: any[] = [];\n let paramIdx = 1;\n if (filters) {\n if (filters.jobType) {\n query += ` AND job_type = $${paramIdx++}`;\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n query += ` AND priority = $${paramIdx++}`;\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(filters.runAt);\n } else if (typeof filters.runAt === 'object') {\n const ops = filters.runAt;\n if (ops.gt) {\n query += ` AND run_at > $${paramIdx++}`;\n params.push(ops.gt);\n }\n if (ops.gte) {\n query += ` AND run_at >= $${paramIdx++}`;\n params.push(ops.gte);\n }\n if (ops.lt) {\n query += ` AND run_at < $${paramIdx++}`;\n params.push(ops.lt);\n }\n if (ops.lte) {\n query += ` AND run_at <= $${paramIdx++}`;\n params.push(ops.lte);\n }\n if (ops.eq) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n query += ` AND tags = $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'all':\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'any':\n query += ` AND tags && $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'none':\n query += ` AND NOT (tags && $${paramIdx++})`;\n params.push(tagValues);\n break;\n default:\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n }\n }\n }\n query += '\\nRETURNING id';\n const result = await client.query(query, params);\n log(`Cancelled ${result.rowCount} jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cancelling upcoming jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async editJob(jobId: number, updates: JobUpdates): Promise<void> {\n const client = await this.pool.connect();\n try {\n const updateFields: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n\n if (updates.payload !== undefined) {\n updateFields.push(`payload = $${paramIdx++}`);\n params.push(updates.payload);\n }\n if (updates.maxAttempts !== undefined) {\n updateFields.push(`max_attempts = $${paramIdx++}`);\n params.push(updates.maxAttempts);\n }\n if (updates.priority !== undefined) {\n updateFields.push(`priority = $${paramIdx++}`);\n params.push(updates.priority);\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n updateFields.push(`run_at = NOW()`);\n } else {\n updateFields.push(`run_at = $${paramIdx++}`);\n params.push(updates.runAt);\n }\n }\n if (updates.timeoutMs !== undefined) {\n updateFields.push(`timeout_ms = $${paramIdx++}`);\n params.push(updates.timeoutMs ?? null);\n }\n if (updates.tags !== undefined) {\n updateFields.push(`tags = $${paramIdx++}`);\n params.push(updates.tags ?? null);\n }\n\n if (updateFields.length === 0) {\n log(`No fields to update for job ${jobId}`);\n return;\n }\n\n updateFields.push(`updated_at = NOW()`);\n params.push(jobId);\n\n const query = `\n UPDATE job_queue\n SET ${updateFields.join(', ')}\n WHERE id = $${paramIdx} AND status = 'pending'\n `;\n\n await client.query(query, params);\n\n const metadata: any = {};\n if (updates.payload !== undefined) metadata.payload = updates.payload;\n if (updates.maxAttempts !== undefined)\n metadata.maxAttempts = updates.maxAttempts;\n if (updates.priority !== undefined) metadata.priority = updates.priority;\n if (updates.runAt !== undefined) metadata.runAt = updates.runAt;\n if (updates.timeoutMs !== undefined)\n metadata.timeoutMs = updates.timeoutMs;\n if (updates.tags !== undefined) metadata.tags = updates.tags;\n\n await this.recordJobEvent(jobId, JobEventType.Edited, metadata);\n log(`Edited job ${jobId}: ${JSON.stringify(metadata)}`);\n } catch (error) {\n log(`Error editing job ${jobId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async editAllPendingJobs(\n filters: JobFilters | undefined = undefined,\n updates: JobUpdates,\n ): Promise<number> {\n const client = await this.pool.connect();\n try {\n const updateFields: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n\n if (updates.payload !== undefined) {\n updateFields.push(`payload = $${paramIdx++}`);\n params.push(updates.payload);\n }\n if (updates.maxAttempts !== undefined) {\n updateFields.push(`max_attempts = $${paramIdx++}`);\n params.push(updates.maxAttempts);\n }\n if (updates.priority !== undefined) {\n updateFields.push(`priority = $${paramIdx++}`);\n params.push(updates.priority);\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n updateFields.push(`run_at = NOW()`);\n } else {\n updateFields.push(`run_at = $${paramIdx++}`);\n params.push(updates.runAt);\n }\n }\n if (updates.timeoutMs !== undefined) {\n updateFields.push(`timeout_ms = $${paramIdx++}`);\n params.push(updates.timeoutMs ?? null);\n }\n if (updates.tags !== undefined) {\n updateFields.push(`tags = $${paramIdx++}`);\n params.push(updates.tags ?? null);\n }\n\n if (updateFields.length === 0) {\n log(`No fields to update for batch edit`);\n return 0;\n }\n\n updateFields.push(`updated_at = NOW()`);\n\n let query = `\n UPDATE job_queue\n SET ${updateFields.join(', ')}\n WHERE status = 'pending'`;\n\n if (filters) {\n if (filters.jobType) {\n query += ` AND job_type = $${paramIdx++}`;\n params.push(filters.jobType);\n }\n if (filters.priority !== undefined) {\n query += ` AND priority = $${paramIdx++}`;\n params.push(filters.priority);\n }\n if (filters.runAt) {\n if (filters.runAt instanceof Date) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(filters.runAt);\n } else if (typeof filters.runAt === 'object') {\n const ops = filters.runAt;\n if (ops.gt) {\n query += ` AND run_at > $${paramIdx++}`;\n params.push(ops.gt);\n }\n if (ops.gte) {\n query += ` AND run_at >= $${paramIdx++}`;\n params.push(ops.gte);\n }\n if (ops.lt) {\n query += ` AND run_at < $${paramIdx++}`;\n params.push(ops.lt);\n }\n if (ops.lte) {\n query += ` AND run_at <= $${paramIdx++}`;\n params.push(ops.lte);\n }\n if (ops.eq) {\n query += ` AND run_at = $${paramIdx++}`;\n params.push(ops.eq);\n }\n }\n }\n if (\n filters.tags &&\n filters.tags.values &&\n filters.tags.values.length > 0\n ) {\n const mode = filters.tags.mode || 'all';\n const tagValues = filters.tags.values;\n switch (mode) {\n case 'exact':\n query += ` AND tags = $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'all':\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'any':\n query += ` AND tags && $${paramIdx++}`;\n params.push(tagValues);\n break;\n case 'none':\n query += ` AND NOT (tags && $${paramIdx++})`;\n params.push(tagValues);\n break;\n default:\n query += ` AND tags @> $${paramIdx++}`;\n params.push(tagValues);\n }\n }\n }\n query += '\\nRETURNING id';\n\n const result = await client.query(query, params);\n const editedCount = result.rowCount || 0;\n\n const metadata: any = {};\n if (updates.payload !== undefined) metadata.payload = updates.payload;\n if (updates.maxAttempts !== undefined)\n metadata.maxAttempts = updates.maxAttempts;\n if (updates.priority !== undefined) metadata.priority = updates.priority;\n if (updates.runAt !== undefined) metadata.runAt = updates.runAt;\n if (updates.timeoutMs !== undefined)\n metadata.timeoutMs = updates.timeoutMs;\n if (updates.tags !== undefined) metadata.tags = updates.tags;\n\n for (const row of result.rows) {\n await this.recordJobEvent(row.id, JobEventType.Edited, metadata);\n }\n\n log(`Edited ${editedCount} pending jobs: ${JSON.stringify(metadata)}`);\n return editedCount;\n } catch (error) {\n log(`Error editing pending jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cleanupOldJobs(daysToKeep = 30): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n DELETE FROM job_queue\n WHERE status = 'completed'\n AND updated_at < NOW() - INTERVAL '1 day' * $1::int\n RETURNING id\n `,\n [daysToKeep],\n );\n log(`Deleted ${result.rowCount} old jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cleaning up old jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async cleanupOldJobEvents(daysToKeep = 30): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n DELETE FROM job_events\n WHERE created_at < NOW() - INTERVAL '1 day' * $1::int\n RETURNING id\n `,\n [daysToKeep],\n );\n log(`Deleted ${result.rowCount} old job events`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error cleaning up old job events: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async reclaimStuckJobs(maxProcessingTimeMinutes = 10): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'pending', locked_at = NULL, locked_by = NULL, updated_at = NOW()\n WHERE status = 'processing'\n AND locked_at < NOW() - GREATEST(\n INTERVAL '1 minute' * $1::int,\n INTERVAL '1 millisecond' * COALESCE(timeout_ms, 0)\n )\n RETURNING id\n `,\n [maxProcessingTimeMinutes],\n );\n log(`Reclaimed ${result.rowCount} stuck jobs`);\n return result.rowCount || 0;\n } catch (error) {\n log(`Error reclaiming stuck jobs: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n // ── Internal helpers ──────────────────────────────────────────────────\n\n /**\n * Batch-insert multiple job events in a single query.\n * More efficient than individual recordJobEvent calls.\n */\n private async recordJobEventsBatch(\n events: { jobId: number; eventType: JobEventType; metadata?: any }[],\n ): Promise<void> {\n if (events.length === 0) return;\n const client = await this.pool.connect();\n try {\n const values: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n for (const event of events) {\n values.push(`($${paramIdx++}, $${paramIdx++}, $${paramIdx++})`);\n params.push(\n event.jobId,\n event.eventType,\n event.metadata ? JSON.stringify(event.metadata) : null,\n );\n }\n await client.query(\n `INSERT INTO job_events (job_id, event_type, metadata) VALUES ${values.join(', ')}`,\n params,\n );\n } catch (error) {\n log(`Error recording batch job events: ${error}`);\n // Do not throw, to avoid interfering with main job logic\n } finally {\n client.release();\n }\n }\n\n // ── Cron schedules ──────────────────────────────────────────────────\n\n /** Create a cron schedule and return its ID. */\n async addCronSchedule(input: CronScheduleInput): Promise<number> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `INSERT INTO cron_schedules\n (schedule_name, cron_expression, job_type, payload, max_attempts,\n priority, timeout_ms, force_kill_on_timeout, tags, timezone,\n allow_overlap, next_run_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)\n RETURNING id`,\n [\n input.scheduleName,\n input.cronExpression,\n input.jobType,\n input.payload,\n input.maxAttempts,\n input.priority,\n input.timeoutMs,\n input.forceKillOnTimeout,\n input.tags ?? null,\n input.timezone,\n input.allowOverlap,\n input.nextRunAt,\n ],\n );\n const id = result.rows[0].id;\n log(`Added cron schedule ${id}: \"${input.scheduleName}\"`);\n return id;\n } catch (error: any) {\n // Unique constraint violation on schedule_name\n if (error?.code === '23505') {\n throw new Error(\n `Cron schedule with name \"${input.scheduleName}\" already exists`,\n );\n }\n log(`Error adding cron schedule: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Get a cron schedule by ID. */\n async getCronSchedule(id: number): Promise<CronScheduleRecord | null> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, schedule_name AS \"scheduleName\", cron_expression AS \"cronExpression\",\n job_type AS \"jobType\", payload, max_attempts AS \"maxAttempts\",\n priority, timeout_ms AS \"timeoutMs\",\n force_kill_on_timeout AS \"forceKillOnTimeout\", tags,\n timezone, allow_overlap AS \"allowOverlap\", status,\n last_enqueued_at AS \"lastEnqueuedAt\", last_job_id AS \"lastJobId\",\n next_run_at AS \"nextRunAt\",\n created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n FROM cron_schedules WHERE id = $1`,\n [id],\n );\n if (result.rows.length === 0) return null;\n return result.rows[0] as CronScheduleRecord;\n } catch (error) {\n log(`Error getting cron schedule ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Get a cron schedule by its unique name. */\n async getCronScheduleByName(\n name: string,\n ): Promise<CronScheduleRecord | null> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, schedule_name AS \"scheduleName\", cron_expression AS \"cronExpression\",\n job_type AS \"jobType\", payload, max_attempts AS \"maxAttempts\",\n priority, timeout_ms AS \"timeoutMs\",\n force_kill_on_timeout AS \"forceKillOnTimeout\", tags,\n timezone, allow_overlap AS \"allowOverlap\", status,\n last_enqueued_at AS \"lastEnqueuedAt\", last_job_id AS \"lastJobId\",\n next_run_at AS \"nextRunAt\",\n created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n FROM cron_schedules WHERE schedule_name = $1`,\n [name],\n );\n if (result.rows.length === 0) return null;\n return result.rows[0] as CronScheduleRecord;\n } catch (error) {\n log(`Error getting cron schedule by name \"${name}\": ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** List cron schedules, optionally filtered by status. */\n async listCronSchedules(\n status?: CronScheduleStatus,\n ): Promise<CronScheduleRecord[]> {\n const client = await this.pool.connect();\n try {\n let query = `SELECT id, schedule_name AS \"scheduleName\", cron_expression AS \"cronExpression\",\n job_type AS \"jobType\", payload, max_attempts AS \"maxAttempts\",\n priority, timeout_ms AS \"timeoutMs\",\n force_kill_on_timeout AS \"forceKillOnTimeout\", tags,\n timezone, allow_overlap AS \"allowOverlap\", status,\n last_enqueued_at AS \"lastEnqueuedAt\", last_job_id AS \"lastJobId\",\n next_run_at AS \"nextRunAt\",\n created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n FROM cron_schedules`;\n const params: any[] = [];\n if (status) {\n query += ` WHERE status = $1`;\n params.push(status);\n }\n query += ` ORDER BY created_at ASC`;\n const result = await client.query(query, params);\n return result.rows as CronScheduleRecord[];\n } catch (error) {\n log(`Error listing cron schedules: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Delete a cron schedule by ID. */\n async removeCronSchedule(id: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(`DELETE FROM cron_schedules WHERE id = $1`, [id]);\n log(`Removed cron schedule ${id}`);\n } catch (error) {\n log(`Error removing cron schedule ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Pause a cron schedule. */\n async pauseCronSchedule(id: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `UPDATE cron_schedules SET status = 'paused', updated_at = NOW() WHERE id = $1`,\n [id],\n );\n log(`Paused cron schedule ${id}`);\n } catch (error) {\n log(`Error pausing cron schedule ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Resume a paused cron schedule. */\n async resumeCronSchedule(id: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `UPDATE cron_schedules SET status = 'active', updated_at = NOW() WHERE id = $1`,\n [id],\n );\n log(`Resumed cron schedule ${id}`);\n } catch (error) {\n log(`Error resuming cron schedule ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /** Edit a cron schedule. */\n async editCronSchedule(\n id: number,\n updates: EditCronScheduleOptions,\n nextRunAt?: Date | null,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n const updateFields: string[] = [];\n const params: any[] = [];\n let paramIdx = 1;\n\n if (updates.cronExpression !== undefined) {\n updateFields.push(`cron_expression = $${paramIdx++}`);\n params.push(updates.cronExpression);\n }\n if (updates.payload !== undefined) {\n updateFields.push(`payload = $${paramIdx++}`);\n params.push(updates.payload);\n }\n if (updates.maxAttempts !== undefined) {\n updateFields.push(`max_attempts = $${paramIdx++}`);\n params.push(updates.maxAttempts);\n }\n if (updates.priority !== undefined) {\n updateFields.push(`priority = $${paramIdx++}`);\n params.push(updates.priority);\n }\n if (updates.timeoutMs !== undefined) {\n updateFields.push(`timeout_ms = $${paramIdx++}`);\n params.push(updates.timeoutMs);\n }\n if (updates.forceKillOnTimeout !== undefined) {\n updateFields.push(`force_kill_on_timeout = $${paramIdx++}`);\n params.push(updates.forceKillOnTimeout);\n }\n if (updates.tags !== undefined) {\n updateFields.push(`tags = $${paramIdx++}`);\n params.push(updates.tags);\n }\n if (updates.timezone !== undefined) {\n updateFields.push(`timezone = $${paramIdx++}`);\n params.push(updates.timezone);\n }\n if (updates.allowOverlap !== undefined) {\n updateFields.push(`allow_overlap = $${paramIdx++}`);\n params.push(updates.allowOverlap);\n }\n if (nextRunAt !== undefined) {\n updateFields.push(`next_run_at = $${paramIdx++}`);\n params.push(nextRunAt);\n }\n\n if (updateFields.length === 0) {\n log(`No fields to update for cron schedule ${id}`);\n return;\n }\n\n updateFields.push(`updated_at = NOW()`);\n params.push(id);\n\n const query = `UPDATE cron_schedules SET ${updateFields.join(', ')} WHERE id = $${paramIdx}`;\n await client.query(query, params);\n log(`Edited cron schedule ${id}`);\n } catch (error) {\n log(`Error editing cron schedule ${id}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /**\n * Atomically fetch all active cron schedules whose nextRunAt <= NOW().\n * Uses FOR UPDATE SKIP LOCKED to prevent duplicate enqueuing across workers.\n */\n async getDueCronSchedules(): Promise<CronScheduleRecord[]> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(\n `SELECT id, schedule_name AS \"scheduleName\", cron_expression AS \"cronExpression\",\n job_type AS \"jobType\", payload, max_attempts AS \"maxAttempts\",\n priority, timeout_ms AS \"timeoutMs\",\n force_kill_on_timeout AS \"forceKillOnTimeout\", tags,\n timezone, allow_overlap AS \"allowOverlap\", status,\n last_enqueued_at AS \"lastEnqueuedAt\", last_job_id AS \"lastJobId\",\n next_run_at AS \"nextRunAt\",\n created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n FROM cron_schedules\n WHERE status = 'active'\n AND next_run_at IS NOT NULL\n AND next_run_at <= NOW()\n ORDER BY next_run_at ASC\n FOR UPDATE SKIP LOCKED`,\n );\n log(`Found ${result.rows.length} due cron schedules`);\n return result.rows as CronScheduleRecord[];\n } catch (error: any) {\n // 42P01 = undefined_table — cron migration hasn't been run yet\n if (error?.code === '42P01') {\n log('cron_schedules table does not exist, skipping cron enqueue');\n return [];\n }\n log(`Error getting due cron schedules: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n /**\n * Update a cron schedule after a job has been enqueued.\n * Sets lastEnqueuedAt, lastJobId, and advances nextRunAt.\n */\n async updateCronScheduleAfterEnqueue(\n id: number,\n lastEnqueuedAt: Date,\n lastJobId: number,\n nextRunAt: Date | null,\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\n `UPDATE cron_schedules\n SET last_enqueued_at = $2,\n last_job_id = $3,\n next_run_at = $4,\n updated_at = NOW()\n WHERE id = $1`,\n [id, lastEnqueuedAt, lastJobId, nextRunAt],\n );\n log(\n `Updated cron schedule ${id}: lastJobId=${lastJobId}, nextRunAt=${nextRunAt?.toISOString() ?? 'null'}`,\n );\n } catch (error) {\n log(`Error updating cron schedule ${id} after enqueue: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n }\n\n // ── Internal helpers ──────────────────────────────────────────────────\n\n async setPendingReasonForUnpickedJobs(\n reason: string,\n jobType?: string | string[],\n ): Promise<void> {\n const client = await this.pool.connect();\n try {\n let jobTypeFilter = '';\n const params: any[] = [reason];\n if (jobType) {\n if (Array.isArray(jobType)) {\n jobTypeFilter = ` AND job_type = ANY($2)`;\n params.push(jobType);\n } else {\n jobTypeFilter = ` AND job_type = $2`;\n params.push(jobType);\n }\n }\n await client.query(\n `UPDATE job_queue SET pending_reason = $1 WHERE status = 'pending'${jobTypeFilter}`,\n params,\n );\n } finally {\n client.release();\n }\n }\n}\n","/**\n * Backward-compatible re-exports.\n * All SQL logic has moved to backends/postgres.ts (PostgresBackend class).\n * These functions delegate to a temporary PostgresBackend instance so that\n * any existing internal callers continue to work.\n *\n * Wait-related functions (waitJob, updateStepData, createWaitpoint, etc.)\n * are PostgreSQL-only and use direct SQL queries.\n */\nimport { Pool } from 'pg';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n WaitpointRecord,\n} from './types.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport { randomUUID } from 'crypto';\nimport { log } from './log-context.js';\n\n/* Thin wrappers — every function creates a lightweight backend wrapper\n around the given pool and forwards the call. The class itself holds\n no mutable state so this is safe and cheap. */\n\nexport const recordJobEvent = async (\n pool: Pool,\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n): Promise<void> =>\n new PostgresBackend(pool).recordJobEvent(jobId, eventType, metadata);\n\nexport const addJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n job: JobOptions<PayloadMap, T>,\n): Promise<number> => new PostgresBackend(pool).addJob(job);\n\nexport const getJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n id: number,\n): Promise<JobRecord<PayloadMap, T> | null> =>\n new PostgresBackend(pool).getJob<PayloadMap, T>(id);\n\nexport const getJobsByStatus = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n status: string,\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobsByStatus<PayloadMap, T>(\n status,\n limit,\n offset,\n );\n\nexport const getNextBatch = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getNextBatch<PayloadMap, T>(\n workerId,\n batchSize,\n jobType,\n );\n\nexport const completeJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).completeJob(jobId);\n\nexport const prolongJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).prolongJob(jobId);\n\nexport const failJob = async (\n pool: Pool,\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n): Promise<void> =>\n new PostgresBackend(pool).failJob(jobId, error, failureReason);\n\nexport const retryJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).retryJob(jobId);\n\nexport const cleanupOldJobs = async (\n pool: Pool,\n daysToKeep = 30,\n): Promise<number> => new PostgresBackend(pool).cleanupOldJobs(daysToKeep);\n\nexport const cancelJob = async (pool: Pool, jobId: number): Promise<void> =>\n new PostgresBackend(pool).cancelJob(jobId);\n\nexport const editJob = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n jobId: number,\n updates: {\n payload?: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n timeoutMs?: number | null;\n tags?: string[] | null;\n },\n): Promise<void> => new PostgresBackend(pool).editJob(jobId, updates);\n\nexport const editAllPendingJobs = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n }\n | undefined,\n updates: {\n payload?: PayloadMap[T];\n maxAttempts?: number;\n priority?: number;\n runAt?: Date | null;\n timeoutMs?: number;\n tags?: string[];\n },\n): Promise<number> =>\n new PostgresBackend(pool).editAllPendingJobs(filters, updates);\n\nexport const cancelAllUpcomingJobs = async (\n pool: Pool,\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n },\n): Promise<number> => new PostgresBackend(pool).cancelAllUpcomingJobs(filters);\n\nexport const getAllJobs = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getAllJobs<PayloadMap, T>(limit, offset);\n\nexport const setPendingReasonForUnpickedJobs = async (\n pool: Pool,\n reason: string,\n jobType?: string | string[],\n): Promise<void> =>\n new PostgresBackend(pool).setPendingReasonForUnpickedJobs(reason, jobType);\n\nexport const reclaimStuckJobs = async (\n pool: Pool,\n maxProcessingTimeMinutes = 10,\n): Promise<number> =>\n new PostgresBackend(pool).reclaimStuckJobs(maxProcessingTimeMinutes);\n\nexport const getJobEvents = async (\n pool: Pool,\n jobId: number,\n): Promise<JobEvent[]> => new PostgresBackend(pool).getJobEvents(jobId);\n\nexport const getJobsByTags = async <\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n pool: Pool,\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobsByTags<PayloadMap, T>(\n tags,\n mode,\n limit,\n offset,\n );\n\nexport const getJobs = async <PayloadMap, T extends keyof PayloadMap & string>(\n pool: Pool,\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: TagQueryMode };\n },\n limit = 100,\n offset = 0,\n): Promise<JobRecord<PayloadMap, T>[]> =>\n new PostgresBackend(pool).getJobs<PayloadMap, T>(filters, limit, offset);\n\n// ── Progress ──────────────────────────────────────────────────────────────────\n\nexport const updateProgress = async (\n pool: Pool,\n jobId: number,\n progress: number,\n): Promise<void> => new PostgresBackend(pool).updateProgress(jobId, progress);\n\n// ── Wait support functions (PostgreSQL-only) ─────────────────────────────────\n\n/**\n * Transition a job to 'waiting' status with wait_until and/or wait_token_id.\n * Saves step_data so the handler can resume from where it left off.\n */\nexport const waitJob = async (\n pool: Pool,\n jobId: number,\n options: {\n waitUntil?: Date;\n waitTokenId?: string;\n stepData: Record<string, any>;\n },\n): Promise<void> => {\n const client = await pool.connect();\n try {\n const result = await client.query(\n `\n UPDATE job_queue\n SET status = 'waiting',\n wait_until = $2,\n wait_token_id = $3,\n step_data = $4,\n locked_at = NULL,\n locked_by = NULL,\n updated_at = NOW()\n WHERE id = $1 AND status = 'processing'\n `,\n [\n jobId,\n options.waitUntil ?? null,\n options.waitTokenId ?? null,\n JSON.stringify(options.stepData),\n ],\n );\n if (result.rowCount === 0) {\n log(\n `Job ${jobId} could not be set to waiting (may have been reclaimed or is no longer processing)`,\n );\n return;\n }\n await recordJobEvent(pool, jobId, JobEventType.Waiting, {\n waitUntil: options.waitUntil?.toISOString() ?? null,\n waitTokenId: options.waitTokenId ?? null,\n });\n log(`Job ${jobId} set to waiting`);\n } catch (error) {\n log(`Error setting job ${jobId} to waiting: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Update step_data for a job. Called after each ctx.run() step completes\n * to persist intermediate progress.\n */\nexport const updateStepData = async (\n pool: Pool,\n jobId: number,\n stepData: Record<string, any>,\n): Promise<void> => {\n const client = await pool.connect();\n try {\n await client.query(\n `UPDATE job_queue SET step_data = $2, updated_at = NOW() WHERE id = $1`,\n [jobId, JSON.stringify(stepData)],\n );\n } catch (error) {\n log(`Error updating step_data for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n } finally {\n client.release();\n }\n};\n\n/**\n * Parse a timeout string like '10m', '1h', '24h', '7d' into milliseconds.\n */\n/**\n * Maximum allowed timeout in milliseconds (~365 days).\n * Prevents overflow to Infinity when computing Date offsets.\n */\nconst MAX_TIMEOUT_MS = 365 * 24 * 60 * 60 * 1000;\n\nfunction parseTimeoutString(timeout: string): number {\n const match = timeout.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(\n `Invalid timeout format: \"${timeout}\". Expected format like \"10m\", \"1h\", \"24h\", \"7d\".`,\n );\n }\n const value = parseInt(match[1], 10);\n const unit = match[2];\n let ms: number;\n switch (unit) {\n case 's':\n ms = value * 1000;\n break;\n case 'm':\n ms = value * 60 * 1000;\n break;\n case 'h':\n ms = value * 60 * 60 * 1000;\n break;\n case 'd':\n ms = value * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new Error(`Unknown timeout unit: \"${unit}\"`);\n }\n if (!Number.isFinite(ms) || ms > MAX_TIMEOUT_MS) {\n throw new Error(\n `Timeout value \"${timeout}\" is too large. Maximum allowed is 365 days.`,\n );\n }\n return ms;\n}\n\n/**\n * Create a waitpoint token in the database.\n * The token can be used to pause a job until an external signal completes it.\n *\n * @param pool - The database pool\n * @param jobId - The job ID to associate with the token (null if created outside a handler)\n * @param options - Optional timeout and tags\n * @returns The created waitpoint token\n */\nexport const createWaitpoint = async (\n pool: Pool,\n jobId: number | null,\n options?: { timeout?: string; tags?: string[] },\n): Promise<{ id: string }> => {\n const client = await pool.connect();\n try {\n const id = `wp_${randomUUID()}`;\n let timeoutAt: Date | null = null;\n\n if (options?.timeout) {\n const ms = parseTimeoutString(options.timeout);\n timeoutAt = new Date(Date.now() + ms);\n }\n\n await client.query(\n `INSERT INTO waitpoints (id, job_id, status, timeout_at, tags) VALUES ($1, $2, 'waiting', $3, $4)`,\n [id, jobId, timeoutAt, options?.tags ?? null],\n );\n\n log(`Created waitpoint ${id} for job ${jobId}`);\n return { id };\n } catch (error) {\n log(`Error creating waitpoint: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Complete a waitpoint token, optionally providing output data.\n * This also moves the associated job from 'waiting' back to 'pending' so\n * it gets picked up by the polling loop.\n */\nexport const completeWaitpoint = async (\n pool: Pool,\n tokenId: string,\n data?: any,\n): Promise<void> => {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n\n // Update the waitpoint\n const wpResult = await client.query(\n `UPDATE waitpoints SET status = 'completed', output = $2, completed_at = NOW()\n WHERE id = $1 AND status = 'waiting'\n RETURNING job_id`,\n [tokenId, data != null ? JSON.stringify(data) : null],\n );\n\n if (wpResult.rows.length === 0) {\n await client.query('ROLLBACK');\n log(`Waitpoint ${tokenId} not found or already completed`);\n return;\n }\n\n const jobId = wpResult.rows[0].job_id;\n\n // Move the associated job back to 'pending' so it gets picked up\n if (jobId != null) {\n await client.query(\n `UPDATE job_queue\n SET status = 'pending', wait_token_id = NULL, wait_until = NULL, updated_at = NOW()\n WHERE id = $1 AND status = 'waiting'`,\n [jobId],\n );\n }\n\n await client.query('COMMIT');\n log(`Completed waitpoint ${tokenId} for job ${jobId}`);\n } catch (error) {\n await client.query('ROLLBACK');\n log(`Error completing waitpoint ${tokenId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Retrieve a waitpoint token by its ID.\n */\nexport const getWaitpoint = async (\n pool: Pool,\n tokenId: string,\n): Promise<WaitpointRecord | null> => {\n const client = await pool.connect();\n try {\n const result = await client.query(\n `SELECT id, job_id AS \"jobId\", status, output, timeout_at AS \"timeoutAt\", created_at AS \"createdAt\", completed_at AS \"completedAt\", tags FROM waitpoints WHERE id = $1`,\n [tokenId],\n );\n if (result.rows.length === 0) return null;\n return result.rows[0] as WaitpointRecord;\n } catch (error) {\n log(`Error getting waitpoint ${tokenId}: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n\n/**\n * Expire timed-out waitpoint tokens and move their associated jobs back to 'pending'.\n * Should be called periodically (e.g., alongside reclaimStuckJobs).\n */\nexport const expireTimedOutWaitpoints = async (pool: Pool): Promise<number> => {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n\n // Find and expire timed-out waitpoints\n const result = await client.query(\n `UPDATE waitpoints\n SET status = 'timed_out'\n WHERE status = 'waiting' AND timeout_at IS NOT NULL AND timeout_at <= NOW()\n RETURNING id, job_id`,\n );\n\n // Move associated jobs back to 'pending'\n for (const row of result.rows) {\n if (row.job_id != null) {\n await client.query(\n `UPDATE job_queue\n SET status = 'pending', wait_token_id = NULL, wait_until = NULL, updated_at = NOW()\n WHERE id = $1 AND status = 'waiting'`,\n [row.job_id],\n );\n }\n }\n\n await client.query('COMMIT');\n const count = result.rowCount || 0;\n if (count > 0) {\n log(`Expired ${count} timed-out waitpoints`);\n }\n return count;\n } catch (error) {\n await client.query('ROLLBACK');\n log(`Error expiring timed-out waitpoints: ${error}`);\n throw error;\n } finally {\n client.release();\n }\n};\n","import { Worker } from 'worker_threads';\nimport { Pool } from 'pg';\nimport {\n JobRecord,\n ProcessorOptions,\n Processor,\n JobHandler,\n JobType,\n FailureReason,\n JobHandlers,\n JobContext,\n OnTimeoutCallback,\n WaitSignal,\n WaitDuration,\n WaitTokenResult,\n} from './types.js';\nimport { QueueBackend } from './backend.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport {\n waitJob,\n updateStepData,\n createWaitpoint,\n getWaitpoint,\n} from './queue.js';\nimport { log, setLogContext } from './log-context.js';\n\n/**\n * Try to extract the underlying pg Pool from a QueueBackend.\n * Returns null for non-PostgreSQL backends.\n */\nfunction tryExtractPool(backend: QueueBackend): Pool | null {\n if (backend instanceof PostgresBackend) {\n return backend.getPool();\n }\n return null;\n}\n\n/**\n * Build a JobContext without wait support (for non-PostgreSQL backends).\n * prolong/onTimeout work normally; wait-related methods throw helpful errors.\n */\nfunction buildBasicContext(\n backend: QueueBackend,\n jobId: number,\n baseCtx: {\n prolong: JobContext['prolong'];\n onTimeout: JobContext['onTimeout'];\n },\n): JobContext {\n const waitError = () =>\n new Error(\n 'Wait features (waitFor, waitUntil, createToken, waitForToken, ctx.run) are currently only supported with the PostgreSQL backend.',\n );\n return {\n prolong: baseCtx.prolong,\n onTimeout: baseCtx.onTimeout,\n run: async <T>(_stepName: string, fn: () => Promise<T>): Promise<T> => {\n // Without PostgreSQL, just execute the function directly (no persistence)\n return fn();\n },\n waitFor: async () => {\n throw waitError();\n },\n waitUntil: async () => {\n throw waitError();\n },\n createToken: async () => {\n throw waitError();\n },\n waitForToken: async () => {\n throw waitError();\n },\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n}\n\n/**\n * Validates that a handler can be serialized for worker thread execution.\n * Throws an error with helpful message if serialization fails.\n */\nfunction validateHandlerSerializable<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(handler: JobHandler<PayloadMap, T>, jobType: string): void {\n try {\n const handlerString = handler.toString();\n\n // Check for common patterns that indicate non-serializable handlers\n // 1. Arrow functions that capture 'this' (indicated by 'this' in the function body but not in parameters)\n if (\n handlerString.includes('this.') &&\n !handlerString.match(/\\([^)]*this[^)]*\\)/)\n ) {\n throw new Error(\n `Handler for job type \"${jobType}\" uses 'this' context which cannot be serialized. ` +\n `Use a regular function or avoid 'this' references when forceKillOnTimeout is enabled.`,\n );\n }\n\n // 2. Check if handler string looks like it might have closures\n // This is a heuristic - we can't perfectly detect closures, but we can warn about common patterns\n if (handlerString.includes('[native code]')) {\n throw new Error(\n `Handler for job type \"${jobType}\" contains native code which cannot be serialized. ` +\n `Ensure your handler is a plain function when forceKillOnTimeout is enabled.`,\n );\n }\n\n // 3. Try to create a function from the string to validate it's parseable\n // This will catch syntax errors early\n try {\n new Function('return ' + handlerString);\n } catch (parseError) {\n throw new Error(\n `Handler for job type \"${jobType}\" cannot be serialized: ${parseError instanceof Error ? parseError.message : String(parseError)}. ` +\n `When using forceKillOnTimeout, handlers must be serializable functions without closures over external variables.`,\n );\n }\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Failed to validate handler serialization for job type \"${jobType}\": ${String(error)}`,\n );\n }\n}\n\n/**\n * Run a handler in a worker thread for force-kill capability.\n *\n * **IMPORTANT**: The handler must be serializable for this to work. This means:\n * - The handler should be a standalone function or arrow function\n * - It should not capture variables from outer scopes (closures) that reference external dependencies\n * - It should not use 'this' context unless it's a bound method\n * - All dependencies must be importable in the worker thread context\n *\n * If your handler doesn't meet these requirements, use the default graceful shutdown\n * (forceKillOnTimeout: false) and ensure your handler checks signal.aborted.\n *\n * @throws {Error} If the handler cannot be serialized\n */\nasync function runHandlerInWorker<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n payload: PayloadMap[T],\n timeoutMs: number,\n jobType: string,\n): Promise<void> {\n // Validate handler can be serialized before attempting to run in worker\n validateHandlerSerializable(handler, jobType);\n\n return new Promise((resolve, reject) => {\n // Use inline worker code for better compatibility\n // Note: This requires the handler to be serializable (no closures with external dependencies)\n // Wrap in IIFE to allow return statements\n const workerCode = `\n (function() {\n const { parentPort, workerData } = require('worker_threads');\n const { handlerCode, payload, timeoutMs } = workerData;\n \n // Create an AbortController for the handler\n const controller = new AbortController();\n const signal = controller.signal;\n \n // Set up timeout\n const timeoutId = setTimeout(() => {\n controller.abort();\n parentPort.postMessage({ type: 'timeout' });\n }, timeoutMs);\n \n try {\n // Execute the handler\n // Note: This uses Function constructor which requires the handler to be serializable.\n // The handler should be validated before reaching this point.\n let handlerFn;\n try {\n // Wrap handlerCode in parentheses to ensure it's treated as an expression\n // This handles both arrow functions and regular functions\n const wrappedCode = handlerCode.trim().startsWith('async') || handlerCode.trim().startsWith('function')\n ? handlerCode\n : '(' + handlerCode + ')';\n handlerFn = new Function('return ' + wrappedCode)();\n } catch (parseError) {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: 'Handler cannot be deserialized in worker thread. ' +\n 'Ensure your handler is a standalone function without closures over external variables. ' +\n 'Original error: ' + (parseError instanceof Error ? parseError.message : String(parseError)),\n stack: parseError instanceof Error ? parseError.stack : undefined,\n name: 'SerializationError',\n },\n });\n return;\n }\n \n // Ensure handlerFn is actually a function\n if (typeof handlerFn !== 'function') {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: 'Handler deserialization did not produce a function. ' +\n 'Ensure your handler is a valid function when forceKillOnTimeout is enabled.',\n name: 'SerializationError',\n },\n });\n return;\n }\n \n handlerFn(payload, signal)\n .then(() => {\n clearTimeout(timeoutId);\n parentPort.postMessage({ type: 'success' });\n })\n .catch((error) => {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: error.message,\n stack: error.stack,\n name: error.name,\n },\n });\n });\n } catch (error) {\n clearTimeout(timeoutId);\n parentPort.postMessage({\n type: 'error',\n error: {\n message: error.message,\n stack: error.stack,\n name: error.name,\n },\n });\n }\n })();\n `;\n\n const worker = new Worker(workerCode, {\n eval: true,\n workerData: {\n handlerCode: handler.toString(),\n payload,\n timeoutMs,\n },\n });\n\n let resolved = false;\n\n worker.on('message', (message: { type: string; error?: any }) => {\n if (resolved) return;\n resolved = true;\n\n if (message.type === 'success') {\n resolve();\n } else if (message.type === 'timeout') {\n const timeoutError = new Error(\n `Job timed out after ${timeoutMs} ms and was forcefully terminated`,\n );\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n reject(timeoutError);\n } else if (message.type === 'error') {\n const error = new Error(message.error.message);\n error.stack = message.error.stack;\n error.name = message.error.name;\n reject(error);\n }\n });\n\n worker.on('error', (error) => {\n if (resolved) return;\n resolved = true;\n reject(error);\n });\n\n worker.on('exit', (code) => {\n if (resolved) return;\n if (code !== 0) {\n resolved = true;\n reject(new Error(`Worker stopped with exit code ${code}`));\n }\n });\n\n // Force terminate worker on timeout\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n worker\n .terminate()\n .then(() => {\n const timeoutError = new Error(\n `Job timed out after ${timeoutMs} ms and was forcefully terminated`,\n );\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n reject(timeoutError);\n })\n .catch((err) => {\n reject(err);\n });\n }\n }, timeoutMs + 100); // Small buffer to ensure timeout is handled\n });\n}\n\n/**\n * Convert a WaitDuration to a target Date.\n */\nfunction calculateWaitUntil(duration: WaitDuration): Date {\n const now = Date.now();\n let ms = 0;\n if (duration.seconds) ms += duration.seconds * 1000;\n if (duration.minutes) ms += duration.minutes * 60 * 1000;\n if (duration.hours) ms += duration.hours * 60 * 60 * 1000;\n if (duration.days) ms += duration.days * 24 * 60 * 60 * 1000;\n if (duration.weeks) ms += duration.weeks * 7 * 24 * 60 * 60 * 1000;\n if (duration.months) ms += duration.months * 30 * 24 * 60 * 60 * 1000;\n if (duration.years) ms += duration.years * 365 * 24 * 60 * 60 * 1000;\n if (ms <= 0) {\n throw new Error(\n 'waitFor duration must be positive. Provide at least one positive duration field.',\n );\n }\n return new Date(now + ms);\n}\n\n/**\n * Create a no-op JobContext for cases where prolong/onTimeout are not supported\n * (e.g. forceKillOnTimeout mode or no timeout set).\n */\nfunction createNoOpContext(\n backend: QueueBackend,\n jobId: number,\n reason: string,\n): JobContext {\n return {\n prolong: () => {\n log(`prolong() called but ignored: ${reason}`);\n },\n onTimeout: () => {\n log(`onTimeout() called but ignored: ${reason}`);\n },\n run: async <T>(_stepName: string, fn: () => Promise<T>): Promise<T> => {\n // In no-op context (forceKillOnTimeout), just execute the function directly\n return fn();\n },\n waitFor: async () => {\n throw new Error(\n `waitFor() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n waitUntil: async () => {\n throw new Error(\n `waitUntil() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n createToken: async () => {\n throw new Error(\n `createToken() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n waitForToken: async () => {\n throw new Error(\n `waitForToken() is not supported when forceKillOnTimeout is enabled. ${reason}`,\n );\n },\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n}\n\n/**\n * Pre-process stepData before handler re-invocation.\n * Marks pending waits as completed and fetches token outputs.\n */\nasync function resolveCompletedWaits(\n pool: Pool,\n stepData: Record<string, any>,\n): Promise<void> {\n for (const key of Object.keys(stepData)) {\n if (!key.startsWith('__wait_')) continue;\n const entry = stepData[key];\n if (!entry || typeof entry !== 'object' || entry.completed) continue;\n\n if (entry.type === 'duration' || entry.type === 'date') {\n // Time-based wait has elapsed (we got picked up, so it must have)\n stepData[key] = { ...entry, completed: true };\n } else if (entry.type === 'token' && entry.tokenId) {\n // Token-based wait -- fetch the waitpoint result\n const wp = await getWaitpoint(pool, entry.tokenId);\n if (wp && wp.status === 'completed') {\n stepData[key] = {\n ...entry,\n completed: true,\n result: { ok: true, output: wp.output },\n };\n } else if (wp && wp.status === 'timed_out') {\n stepData[key] = {\n ...entry,\n completed: true,\n result: { ok: false, error: 'Token timed out' },\n };\n }\n // If still waiting (shouldn't happen), leave as pending\n }\n }\n}\n\n/**\n * Build the extended JobContext with step tracking and wait support.\n */\nfunction buildWaitContext(\n backend: QueueBackend,\n pool: Pool,\n jobId: number,\n stepData: Record<string, any>,\n baseCtx: {\n prolong: JobContext['prolong'];\n onTimeout: JobContext['onTimeout'];\n },\n): JobContext {\n // Wait counter always starts at 0 per invocation.\n // The handler replays from the top each time, so the counter position\n // must match the order of waitFor/waitUntil/waitForToken calls in code.\n let waitCounter = 0;\n\n const ctx: JobContext = {\n prolong: baseCtx.prolong,\n onTimeout: baseCtx.onTimeout,\n\n run: async <T>(stepName: string, fn: () => Promise<T>): Promise<T> => {\n // Check if step was already completed in a previous invocation\n const cached = stepData[stepName];\n if (cached && typeof cached === 'object' && cached.__completed) {\n log(`Step \"${stepName}\" replayed from cache for job ${jobId}`);\n return cached.result as T;\n }\n\n // Execute the step\n const result = await fn();\n\n // Persist step result\n stepData[stepName] = { __completed: true, result };\n await updateStepData(pool, jobId, stepData);\n\n return result;\n },\n\n waitFor: async (duration: WaitDuration): Promise<void> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed (from a previous invocation)\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(`Wait \"${waitKey}\" already completed for job ${jobId}, skipping`);\n return;\n }\n\n // Calculate when to resume\n const waitUntilDate = calculateWaitUntil(duration);\n\n // Record this wait as pending in step data\n stepData[waitKey] = { type: 'duration', completed: false };\n\n // Throw WaitSignal to pause the handler\n throw new WaitSignal('duration', waitUntilDate, undefined, stepData);\n },\n\n waitUntil: async (date: Date): Promise<void> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(`Wait \"${waitKey}\" already completed for job ${jobId}, skipping`);\n return;\n }\n\n // Record this wait as pending\n stepData[waitKey] = { type: 'date', completed: false };\n\n // Throw WaitSignal to pause the handler\n throw new WaitSignal('date', date, undefined, stepData);\n },\n\n createToken: async (options?) => {\n const token = await createWaitpoint(pool, jobId, options);\n return token;\n },\n\n waitForToken: async <T = any>(\n tokenId: string,\n ): Promise<WaitTokenResult<T>> => {\n const waitKey = `__wait_${waitCounter++}`;\n\n // Check if this wait was already completed\n const cached = stepData[waitKey];\n if (cached && typeof cached === 'object' && cached.completed) {\n log(\n `Token wait \"${waitKey}\" already completed for job ${jobId}, returning cached result`,\n );\n return cached.result as WaitTokenResult<T>;\n }\n\n // Check if the token is already completed (e.g., completed while job was still processing)\n const wp = await getWaitpoint(pool, tokenId);\n if (wp && wp.status === 'completed') {\n const result: WaitTokenResult<T> = {\n ok: true,\n output: wp.output as T,\n };\n stepData[waitKey] = {\n type: 'token',\n tokenId,\n completed: true,\n result,\n };\n await updateStepData(pool, jobId, stepData);\n return result;\n }\n if (wp && wp.status === 'timed_out') {\n const result: WaitTokenResult<T> = {\n ok: false,\n error: 'Token timed out',\n };\n stepData[waitKey] = {\n type: 'token',\n tokenId,\n completed: true,\n result,\n };\n await updateStepData(pool, jobId, stepData);\n return result;\n }\n\n // Token not yet completed -- save pending state and throw WaitSignal\n stepData[waitKey] = { type: 'token', tokenId, completed: false };\n throw new WaitSignal('token', undefined, tokenId, stepData);\n },\n\n setProgress: async (percent: number) => {\n if (percent < 0 || percent > 100)\n throw new Error('Progress must be between 0 and 100');\n await backend.updateProgress(jobId, Math.round(percent));\n },\n };\n\n return ctx;\n}\n\n/**\n * Process a single job using the provided handler map\n */\nexport async function processJobWithHandlers<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n backend: QueueBackend,\n job: JobRecord<PayloadMap, T>,\n jobHandlers: JobHandlers<PayloadMap>,\n): Promise<void> {\n const handler = jobHandlers[job.jobType];\n\n if (!handler) {\n await backend.setPendingReasonForUnpickedJobs(\n `No handler registered for job type: ${job.jobType}`,\n job.jobType,\n );\n await backend.failJob(\n job.id,\n new Error(`No handler registered for job type: ${job.jobType}`),\n FailureReason.NoHandler,\n );\n return;\n }\n\n // Load step data (may contain completed steps from previous invocations)\n const stepData: Record<string, any> = { ...(job.stepData || {}) };\n\n // Try to get pool for wait features (PostgreSQL-only)\n const pool = tryExtractPool(backend);\n\n // If resuming from a wait, resolve any pending wait entries\n const hasStepHistory = Object.keys(stepData).some((k) =>\n k.startsWith('__wait_'),\n );\n if (hasStepHistory && pool) {\n await resolveCompletedWaits(pool, stepData);\n // Persist the resolved step data\n await updateStepData(pool, job.id, stepData);\n }\n\n // Per-job timeout logic\n const timeoutMs = job.timeoutMs ?? undefined;\n const forceKillOnTimeout = job.forceKillOnTimeout ?? false;\n let timeoutId: NodeJS.Timeout | undefined;\n const controller = new AbortController();\n try {\n // If forceKillOnTimeout is true, run handler in a worker thread\n // Note: wait features are not available in forceKillOnTimeout mode\n if (forceKillOnTimeout && timeoutMs && timeoutMs > 0) {\n await runHandlerInWorker(handler, job.payload, timeoutMs, job.jobType);\n } else {\n // Build the JobContext for prolong/onTimeout support\n let onTimeoutCallback: OnTimeoutCallback | undefined;\n\n // Reference to the reject function of the timeout promise so we can re-arm it\n let timeoutReject: ((error: Error) => void) | undefined;\n\n /**\n * Arms (or re-arms) the timeout. When it fires:\n * 1. If an onTimeout callback is registered, call it first.\n * - If it returns a positive number, re-arm with that duration and update DB.\n * - Otherwise, proceed with abort.\n * 2. If no onTimeout callback, proceed with abort.\n */\n const armTimeout = (ms: number) => {\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n // Check if an onTimeout callback wants to extend\n if (onTimeoutCallback) {\n try {\n const extension = onTimeoutCallback();\n if (typeof extension === 'number' && extension > 0) {\n // Extend: re-arm timeout and update DB\n backend.prolongJob(job.id).catch(() => {});\n armTimeout(extension);\n return;\n }\n } catch (callbackError) {\n log(\n `onTimeout callback threw for job ${job.id}: ${callbackError}`,\n );\n // Treat as \"no extension\" and proceed with abort\n }\n }\n // No extension -- proceed with abort\n controller.abort();\n const timeoutError = new Error(`Job timed out after ${ms} ms`);\n // @ts-ignore\n timeoutError.failureReason = FailureReason.Timeout;\n if (timeoutReject) {\n timeoutReject(timeoutError);\n }\n }, ms);\n };\n\n const hasTimeout = timeoutMs != null && timeoutMs > 0;\n\n // Build base prolong/onTimeout context\n const baseCtx = hasTimeout\n ? {\n prolong: (ms?: number) => {\n const duration = ms ?? timeoutMs;\n if (duration != null && duration > 0) {\n armTimeout(duration);\n // Update DB locked_at to prevent reclaimStuckJobs\n backend.prolongJob(job.id).catch(() => {});\n }\n },\n onTimeout: (callback: OnTimeoutCallback) => {\n onTimeoutCallback = callback;\n },\n }\n : {\n prolong: () => {\n log('prolong() called but ignored: job has no timeout set');\n },\n onTimeout: () => {\n log('onTimeout() called but ignored: job has no timeout set');\n },\n };\n\n // Build context: full wait support for PostgreSQL, basic for others\n const ctx = pool\n ? buildWaitContext(backend, pool, job.id, stepData, baseCtx)\n : buildBasicContext(backend, job.id, baseCtx);\n\n // If forceKillOnTimeout was set but timeoutMs was missing, warn\n if (forceKillOnTimeout && !hasTimeout) {\n log(\n `forceKillOnTimeout is set but no timeoutMs for job ${job.id}, running without force kill`,\n );\n }\n\n const jobPromise = handler(job.payload, controller.signal, ctx);\n\n if (hasTimeout) {\n await Promise.race([\n jobPromise,\n new Promise<never>((_, reject) => {\n timeoutReject = reject;\n armTimeout(timeoutMs!);\n }),\n ]);\n } else {\n await jobPromise;\n }\n }\n if (timeoutId) clearTimeout(timeoutId);\n\n // Job completed successfully -- complete via backend\n await backend.completeJob(job.id);\n } catch (error) {\n if (timeoutId) clearTimeout(timeoutId);\n\n // Check if this is a WaitSignal (not a real error)\n if (error instanceof WaitSignal) {\n if (!pool) {\n // Wait signals should never happen with non-PostgreSQL backends\n // since the context methods throw, but guard just in case\n await backend.failJob(\n job.id,\n new Error(\n 'WaitSignal received but wait features require the PostgreSQL backend.',\n ),\n FailureReason.HandlerError,\n );\n return;\n }\n log(\n `Job ${job.id} entering wait: type=${error.type}, waitUntil=${error.waitUntil?.toISOString() ?? 'none'}, tokenId=${error.tokenId ?? 'none'}`,\n );\n await waitJob(pool, job.id, {\n waitUntil: error.waitUntil,\n waitTokenId: error.tokenId,\n stepData: error.stepData,\n });\n return;\n }\n\n // Real error -- handle as failure\n console.error(`Error processing job ${job.id}:`, error);\n let failureReason = FailureReason.HandlerError;\n if (\n error &&\n typeof error === 'object' &&\n 'failureReason' in error &&\n (error as { failureReason?: FailureReason }).failureReason ===\n FailureReason.Timeout\n ) {\n failureReason = FailureReason.Timeout;\n }\n await backend.failJob(\n job.id,\n error instanceof Error ? error : new Error(String(error)),\n failureReason,\n );\n }\n}\n\n/**\n * Process a batch of jobs using the provided handler map and concurrency limit\n */\nexport async function processBatchWithHandlers<PayloadMap>(\n backend: QueueBackend,\n workerId: string,\n batchSize: number,\n jobType: string | string[] | undefined,\n jobHandlers: JobHandlers<PayloadMap>,\n concurrency?: number,\n onError?: (error: Error) => void,\n): Promise<number> {\n const jobs = await backend.getNextBatch<PayloadMap, JobType<PayloadMap>>(\n workerId,\n batchSize,\n jobType,\n );\n if (!concurrency || concurrency >= jobs.length) {\n // Default: all in parallel\n await Promise.all(\n jobs.map((job) => processJobWithHandlers(backend, job, jobHandlers)),\n );\n return jobs.length;\n }\n // Concurrency-limited pool\n let idx = 0;\n let running = 0;\n let finished = 0;\n return new Promise((resolve, reject) => {\n const next = () => {\n if (finished === jobs.length) return resolve(jobs.length);\n while (running < concurrency && idx < jobs.length) {\n const job = jobs[idx++];\n running++;\n processJobWithHandlers(backend, job, jobHandlers)\n .then(() => {\n running--;\n finished++;\n next();\n })\n .catch((err) => {\n running--;\n finished++;\n if (onError) {\n onError(err instanceof Error ? err : new Error(String(err)));\n }\n next();\n });\n }\n };\n next();\n });\n}\n\n/**\n * Start a job processor that continuously processes jobs.\n * @param backend - The queue backend.\n * @param handlers - The job handlers for this processor instance.\n * @param options - The processor options. Leave pollInterval empty to run only once. Use jobType to filter jobs by type.\n * @param onBeforeBatch - Optional callback invoked before each batch. Used internally to enqueue due cron jobs.\n * @returns {Processor} The processor instance.\n */\nexport const createProcessor = <PayloadMap = any>(\n backend: QueueBackend,\n handlers: JobHandlers<PayloadMap>,\n options: ProcessorOptions = {},\n onBeforeBatch?: () => Promise<void>,\n): Processor => {\n const {\n workerId = `worker-${Math.random().toString(36).substring(2, 9)}`,\n batchSize = 10,\n pollInterval = 5000,\n onError = (error: Error) => console.error('Job processor error:', error),\n jobType,\n concurrency = 3,\n } = options;\n\n let running = false;\n let intervalId: NodeJS.Timeout | null = null;\n let currentBatchPromise: Promise<number> | null = null;\n\n setLogContext(options.verbose ?? false);\n\n const processJobs = async (): Promise<number> => {\n if (!running) return 0;\n\n // Run pre-batch hook (e.g. enqueue due cron jobs) before processing\n if (onBeforeBatch) {\n try {\n await onBeforeBatch();\n } catch (hookError) {\n log(`onBeforeBatch hook error: ${hookError}`);\n if (onError) {\n onError(\n hookError instanceof Error\n ? hookError\n : new Error(String(hookError)),\n );\n }\n }\n }\n\n log(\n `Processing jobs with workerId: ${workerId}${jobType ? ` and jobType: ${Array.isArray(jobType) ? jobType.join(',') : jobType}` : ''}`,\n );\n\n try {\n const processed = await processBatchWithHandlers(\n backend,\n workerId,\n batchSize,\n jobType,\n handlers,\n concurrency,\n onError,\n );\n // Only process one batch in start; do not schedule next batch here\n return processed;\n } catch (error) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n return 0;\n };\n\n return {\n /**\n * Start the job processor in the background.\n * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs as they become available.\n * - You have to call the stop method to stop the processor.\n */\n startInBackground: () => {\n if (running) return;\n\n log(`Starting job processor with workerId: ${workerId}`);\n running = true;\n\n // Single serialized loop: process a batch, then either immediately\n // continue (if full batch was returned) or wait pollInterval.\n const scheduleNext = (immediate: boolean) => {\n if (!running) return;\n if (immediate) {\n intervalId = setTimeout(loop, 0);\n } else {\n intervalId = setTimeout(loop, pollInterval);\n }\n };\n\n const loop = async () => {\n if (!running) return;\n currentBatchPromise = processJobs();\n const processed = await currentBatchPromise;\n currentBatchPromise = null;\n // If we got a full batch, there may be more work — process immediately\n scheduleNext(processed === batchSize);\n };\n\n // Start the first iteration immediately\n loop();\n },\n /**\n * Stop the job processor that runs in the background.\n * Does not wait for in-flight jobs.\n */\n stop: () => {\n log(`Stopping job processor with workerId: ${workerId}`);\n running = false;\n if (intervalId) {\n clearTimeout(intervalId);\n intervalId = null;\n }\n },\n /**\n * Stop the job processor and wait for all in-flight jobs to complete.\n * Useful for graceful shutdown (e.g., SIGTERM handling).\n */\n stopAndDrain: async (drainTimeoutMs = 30000) => {\n log(`Stopping and draining job processor with workerId: ${workerId}`);\n running = false;\n if (intervalId) {\n clearTimeout(intervalId);\n intervalId = null;\n }\n // Wait for current batch to finish, with a timeout\n if (currentBatchPromise) {\n await Promise.race([\n currentBatchPromise.catch(() => {}),\n new Promise<void>((resolve) => setTimeout(resolve, drainTimeoutMs)),\n ]);\n currentBatchPromise = null;\n }\n log(`Job processor ${workerId} drained`);\n },\n /**\n * Start the job processor synchronously.\n * - This will process all jobs immediately and then stop.\n * - The pollInterval is ignored.\n */\n start: async () => {\n log(`Starting job processor with workerId: ${workerId}`);\n running = true;\n const processed = await processJobs();\n running = false;\n return processed;\n },\n isRunning: () => running,\n };\n};\n","import { Pool } from 'pg';\nimport { PostgresJobQueueConfig } from './types.js';\nimport { parse } from 'pg-connection-string';\nimport fs from 'fs';\n\n/**\n * Helper to load a PEM string or file. Only values starting with 'file://' are loaded from file.\n */\nfunction loadPemOrFile(value?: string): string | undefined {\n if (!value) return undefined;\n if (value.startsWith('file://')) {\n const filePath = value.slice(7);\n return fs.readFileSync(filePath, 'utf8');\n }\n return value;\n}\n\n/**\n * Create a database connection pool with flexible SSL certificate loading.\n *\n * SSL config example (for local file paths):\n * ssl: {\n * ca: process.env.PGSSLROOTCERT, // PEM string or 'file://...'\n * cert: process.env.PGSSLCERT, // optional, PEM string or 'file://...'\n * key: process.env.PGSSLKEY, // optional, PEM string or 'file://...'\n * rejectUnauthorized: true\n * }\n */\nexport const createPool = (\n config: PostgresJobQueueConfig['databaseConfig'],\n): Pool => {\n let searchPath: string | undefined;\n let ssl: any = undefined;\n let customCA: string | undefined;\n let sslmode: string | undefined;\n\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n searchPath = url.searchParams.get('search_path') || undefined;\n sslmode = url.searchParams.get('sslmode') || undefined;\n if (sslmode === 'no-verify') {\n ssl = { rejectUnauthorized: false };\n }\n } catch (e) {\n const parsed = parse(config.connectionString);\n if (parsed.options) {\n const match = parsed.options.match(/search_path=([^\\s]+)/);\n if (match) {\n searchPath = match[1];\n }\n }\n sslmode = typeof parsed.sslmode === 'string' ? parsed.sslmode : undefined;\n if (sslmode === 'no-verify') {\n ssl = { rejectUnauthorized: false };\n }\n }\n }\n\n // Flexible SSL loading: only support file:// for file loading\n if (config.ssl) {\n if (typeof config.ssl.ca === 'string') {\n customCA = config.ssl.ca;\n } else if (typeof process.env.PGSSLROOTCERT === 'string') {\n customCA = process.env.PGSSLROOTCERT;\n } else {\n customCA = undefined;\n }\n const caValue =\n typeof customCA === 'string' ? loadPemOrFile(customCA) : undefined;\n ssl = {\n ...ssl,\n ...(caValue ? { ca: caValue } : {}),\n cert: loadPemOrFile(\n typeof config.ssl.cert === 'string'\n ? config.ssl.cert\n : process.env.PGSSLCERT,\n ),\n key: loadPemOrFile(\n typeof config.ssl.key === 'string'\n ? config.ssl.key\n : process.env.PGSSLKEY,\n ),\n rejectUnauthorized:\n config.ssl.rejectUnauthorized !== undefined\n ? config.ssl.rejectUnauthorized\n : true,\n };\n }\n\n // Warn if both sslmode (any value) and a custom CA are set\n if (sslmode && customCA) {\n const warning = `\\n\\n\\x1b[33m**************************************************\\n\\u26A0\\uFE0F WARNING: SSL CONFIGURATION ISSUE\\n**************************************************\\nBoth sslmode ('${sslmode}') is set in the connection string\\nand a custom CA is provided (via config.ssl.ca or PGSSLROOTCERT).\\nThis combination may cause connection failures or unexpected behavior.\\n\\nRecommended: Remove sslmode from the connection string when using a custom CA.\\n**************************************************\\x1b[0m\\n`;\n console.warn(warning);\n }\n\n const pool = new Pool({\n ...config,\n ...(ssl ? { ssl } : {}),\n });\n\n if (searchPath) {\n pool.on('connect', (client) => {\n client.query(`SET search_path TO ${searchPath}`);\n });\n }\n\n return pool;\n};\n","/**\n * Lua scripts for atomic Redis operations.\n *\n * Key naming convention (all prefixed with the configurable keyPrefix, default \"dq:\"):\n * dq:id_seq – INCR counter for auto-increment IDs\n * dq:job:{id} – Hash with all job fields\n * dq:queue – Sorted Set of ready-to-process job IDs (score = priority composite)\n * dq:delayed – Sorted Set of future-scheduled job IDs (score = run_at ms)\n * dq:retry – Sorted Set of retry-waiting job IDs (score = next_attempt_at ms)\n * dq:status:{status} – Set of job IDs per status\n * dq:type:{jobType} – Set of job IDs per type\n * dq:tag:{tag} – Set of job IDs per tag\n * dq:job:{id}:tags – Set of tags for a specific job\n * dq:events:{id} – List of JSON event objects\n * dq:idempotency:{key} – String mapping idempotency key → job ID\n * dq:all – Sorted Set of all jobs (score = createdAt ms, for ordering)\n * dq:event_id_seq – INCR counter for event IDs\n */\n\n// ─── Score helpers ──────────────────────────────────────────────────────\n// For the ready queue we need: higher priority first, then earlier createdAt.\n// Score = priority * 1e15 + (1e15 - createdAtMs)\n// ZPOPMAX gives highest score → highest priority, earliest created.\nconst SCORE_RANGE = '1000000000000000'; // 1e15\n\n/**\n * ADD JOB\n * KEYS: [prefix]\n * ARGV: [jobType, payloadJson, maxAttempts, priority, runAtMs, timeoutMs,\n * forceKillOnTimeout, tagsJson, idempotencyKey, nowMs]\n * Returns: job ID (number)\n */\nexport const ADD_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobType = ARGV[1]\nlocal payloadJson = ARGV[2]\nlocal maxAttempts = tonumber(ARGV[3])\nlocal priority = tonumber(ARGV[4])\nlocal runAtMs = ARGV[5] -- \"0\" means now\nlocal timeoutMs = ARGV[6] -- \"null\" string if not set\nlocal forceKillOnTimeout = ARGV[7]\nlocal tagsJson = ARGV[8] -- \"null\" or JSON array string\nlocal idempotencyKey = ARGV[9] -- \"null\" string if not set\nlocal nowMs = tonumber(ARGV[10])\n\n-- Idempotency check\nif idempotencyKey ~= \"null\" then\n local existing = redis.call('GET', prefix .. 'idempotency:' .. idempotencyKey)\n if existing then\n return existing\n end\nend\n\n-- Generate ID\nlocal id = redis.call('INCR', prefix .. 'id_seq')\nlocal jobKey = prefix .. 'job:' .. id\nlocal runAt = runAtMs ~= \"0\" and tonumber(runAtMs) or nowMs\n\n-- Store the job hash\nredis.call('HMSET', jobKey,\n 'id', id,\n 'jobType', jobType,\n 'payload', payloadJson,\n 'status', 'pending',\n 'maxAttempts', maxAttempts,\n 'attempts', 0,\n 'priority', priority,\n 'runAt', runAt,\n 'timeoutMs', timeoutMs,\n 'forceKillOnTimeout', forceKillOnTimeout,\n 'createdAt', nowMs,\n 'updatedAt', nowMs,\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'nextAttemptAt', 'null',\n 'pendingReason', 'null',\n 'errorHistory', '[]',\n 'failureReason', 'null',\n 'completedAt', 'null',\n 'startedAt', 'null',\n 'lastRetriedAt', 'null',\n 'lastFailedAt', 'null',\n 'lastCancelledAt', 'null',\n 'tags', tagsJson,\n 'idempotencyKey', idempotencyKey\n)\n\n-- Status index\nredis.call('SADD', prefix .. 'status:pending', id)\n\n-- Type index\nredis.call('SADD', prefix .. 'type:' .. jobType, id)\n\n-- Tag indexes\nif tagsJson ~= \"null\" then\n local tags = cjson.decode(tagsJson)\n for _, tag in ipairs(tags) do\n redis.call('SADD', prefix .. 'tag:' .. tag, id)\n end\n -- Store tags for exact-match queries\n for _, tag in ipairs(tags) do\n redis.call('SADD', prefix .. 'job:' .. id .. ':tags', tag)\n end\nend\n\n-- Idempotency mapping\nif idempotencyKey ~= \"null\" then\n redis.call('SET', prefix .. 'idempotency:' .. idempotencyKey, id)\nend\n\n-- All-jobs sorted set (for ordering by createdAt)\nredis.call('ZADD', prefix .. 'all', nowMs, id)\n\n-- Queue or delayed\nif runAt <= nowMs then\n -- Ready now: add to queue with priority score\n local score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - nowMs)\n redis.call('ZADD', prefix .. 'queue', score, id)\nelse\n -- Future: add to delayed set\n redis.call('ZADD', prefix .. 'delayed', runAt, id)\nend\n\nreturn id\n`;\n\n/**\n * GET NEXT BATCH\n * Atomically: move ready delayed/retry jobs into queue, then pop N jobs.\n * KEYS: [prefix]\n * ARGV: [workerId, batchSize, nowMs, jobTypeFilter]\n * jobTypeFilter: \"null\" or a JSON array like [\"email\",\"sms\"] or a string like \"email\"\n * Returns: array of job field arrays (flat: [field1, val1, field2, val2, ...] per job)\n */\nexport const GET_NEXT_BATCH_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal workerId = ARGV[1]\nlocal batchSize = tonumber(ARGV[2])\nlocal nowMs = tonumber(ARGV[3])\nlocal jobTypeFilter = ARGV[4] -- \"null\" or JSON array or single string\n\n-- 1. Move ready delayed jobs into queue\nlocal delayed = redis.call('ZRANGEBYSCORE', prefix .. 'delayed', '-inf', nowMs, 'LIMIT', 0, 200)\nfor _, jobId in ipairs(delayed) do\n local jk = prefix .. 'job:' .. jobId\n local status = redis.call('HGET', jk, 'status')\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n if status == 'pending' and attempts < maxAttempts then\n local pri = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local ca = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = pri * ${SCORE_RANGE} + (${SCORE_RANGE} - ca)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n end\n redis.call('ZREM', prefix .. 'delayed', jobId)\nend\n\n-- 2. Move ready retry jobs into queue\nlocal retries = redis.call('ZRANGEBYSCORE', prefix .. 'retry', '-inf', nowMs, 'LIMIT', 0, 200)\nfor _, jobId in ipairs(retries) do\n local jk = prefix .. 'job:' .. jobId\n local status = redis.call('HGET', jk, 'status')\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n if status == 'failed' and attempts < maxAttempts then\n local pri = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local ca = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = pri * ${SCORE_RANGE} + (${SCORE_RANGE} - ca)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n redis.call('SREM', prefix .. 'status:failed', jobId)\n redis.call('SADD', prefix .. 'status:pending', jobId)\n redis.call('HMSET', jk, 'status', 'pending')\n end\n redis.call('ZREM', prefix .. 'retry', jobId)\nend\n\n-- 3. Parse job type filter\nlocal filterTypes = nil\nif jobTypeFilter ~= \"null\" then\n -- Could be a JSON array or a plain string\n local ok, decoded = pcall(cjson.decode, jobTypeFilter)\n if ok and type(decoded) == 'table' then\n filterTypes = {}\n for _, t in ipairs(decoded) do filterTypes[t] = true end\n else\n filterTypes = { [jobTypeFilter] = true }\n end\nend\n\n-- 4. Pop candidates from queue (highest score first)\n-- We pop more than batchSize because some may be filtered out\nlocal popCount = batchSize * 3\nlocal candidates = redis.call('ZPOPMAX', prefix .. 'queue', popCount)\n-- candidates: [member1, score1, member2, score2, ...]\n\nlocal results = {}\nlocal jobsClaimed = 0\nlocal putBack = {} -- {score, id} pairs to put back\n\nfor i = 1, #candidates, 2 do\n local jobId = candidates[i]\n local score = candidates[i + 1]\n local jk = prefix .. 'job:' .. jobId\n\n if jobsClaimed >= batchSize then\n -- We have enough; put the rest back\n table.insert(putBack, score)\n table.insert(putBack, jobId)\n else\n -- Check job type filter\n local jt = redis.call('HGET', jk, 'jobType')\n if filterTypes and not filterTypes[jt] then\n -- Doesn't match filter: put back\n table.insert(putBack, score)\n table.insert(putBack, jobId)\n else\n -- Check run_at\n local runAt = tonumber(redis.call('HGET', jk, 'runAt'))\n if runAt > nowMs then\n -- Not ready yet: move to delayed\n redis.call('ZADD', prefix .. 'delayed', runAt, jobId)\n else\n -- Claim this job\n local attempts = tonumber(redis.call('HGET', jk, 'attempts'))\n local startedAt = redis.call('HGET', jk, 'startedAt')\n local lastRetriedAt = redis.call('HGET', jk, 'lastRetriedAt')\n if startedAt == 'null' then startedAt = nowMs end\n if attempts > 0 then lastRetriedAt = nowMs end\n\n redis.call('HMSET', jk,\n 'status', 'processing',\n 'lockedAt', nowMs,\n 'lockedBy', workerId,\n 'attempts', attempts + 1,\n 'updatedAt', nowMs,\n 'pendingReason', 'null',\n 'startedAt', startedAt,\n 'lastRetriedAt', lastRetriedAt\n )\n\n -- Update status sets\n redis.call('SREM', prefix .. 'status:pending', jobId)\n redis.call('SADD', prefix .. 'status:processing', jobId)\n\n -- Return job data as flat array\n local data = redis.call('HGETALL', jk)\n for _, v in ipairs(data) do\n table.insert(results, v)\n end\n -- Separator\n table.insert(results, '__JOB_SEP__')\n jobsClaimed = jobsClaimed + 1\n end\n end\n end\nend\n\n-- Put back jobs we didn't claim\nif #putBack > 0 then\n redis.call('ZADD', prefix .. 'queue', unpack(putBack))\nend\n\nreturn results\n`;\n\n/**\n * COMPLETE JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const COMPLETE_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nredis.call('HMSET', jk,\n 'status', 'completed',\n 'updatedAt', nowMs,\n 'completedAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:processing', jobId)\nredis.call('SADD', prefix .. 'status:completed', jobId)\n\nreturn 1\n`;\n\n/**\n * FAIL JOB\n * KEYS: [prefix]\n * ARGV: [jobId, errorJson, failureReason, nowMs]\n * errorJson: JSON array like [{\"message\":\"...\", \"timestamp\":\"...\"}]\n */\nexport const FAIL_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal errorJson = ARGV[2]\nlocal failureReason = ARGV[3]\nlocal nowMs = tonumber(ARGV[4])\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal attempts = tonumber(redis.call('HGET', jk, 'attempts'))\nlocal maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))\n\n-- Compute next_attempt_at: 2^attempts minutes from now\nlocal nextAttemptAt = 'null'\nif attempts < maxAttempts then\n local delayMs = math.pow(2, attempts) * 60000\n nextAttemptAt = nowMs + delayMs\nend\n\n-- Append to error_history\nlocal history = redis.call('HGET', jk, 'errorHistory') or '[]'\nlocal ok, arr = pcall(cjson.decode, history)\nif not ok then arr = {} end\nlocal newErrors = cjson.decode(errorJson)\nfor _, e in ipairs(newErrors) do\n table.insert(arr, e)\nend\n\nredis.call('HMSET', jk,\n 'status', 'failed',\n 'updatedAt', nowMs,\n 'nextAttemptAt', tostring(nextAttemptAt),\n 'errorHistory', cjson.encode(arr),\n 'failureReason', failureReason,\n 'lastFailedAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:processing', jobId)\nredis.call('SADD', prefix .. 'status:failed', jobId)\n\n-- Schedule retry if applicable\nif nextAttemptAt ~= 'null' then\n redis.call('ZADD', prefix .. 'retry', nextAttemptAt, jobId)\nend\n\nreturn 1\n`;\n\n/**\n * RETRY JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const RETRY_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = tonumber(ARGV[2])\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal oldStatus = redis.call('HGET', jk, 'status')\n\nredis.call('HMSET', jk,\n 'status', 'pending',\n 'updatedAt', nowMs,\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'nextAttemptAt', nowMs,\n 'lastRetriedAt', nowMs\n)\n\n-- Remove from old status, add to pending\nif oldStatus then\n redis.call('SREM', prefix .. 'status:' .. oldStatus, jobId)\nend\nredis.call('SADD', prefix .. 'status:pending', jobId)\n\n-- Remove from retry sorted set if present\nredis.call('ZREM', prefix .. 'retry', jobId)\n\n-- Add to queue (ready now)\nlocal priority = tonumber(redis.call('HGET', jk, 'priority') or '0')\nlocal createdAt = tonumber(redis.call('HGET', jk, 'createdAt'))\nlocal score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - createdAt)\nredis.call('ZADD', prefix .. 'queue', score, jobId)\n\nreturn 1\n`;\n\n/**\n * CANCEL JOB (only if pending)\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const CANCEL_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal status = redis.call('HGET', jk, 'status')\nif status ~= 'pending' then return 0 end\n\nredis.call('HMSET', jk,\n 'status', 'cancelled',\n 'updatedAt', nowMs,\n 'lastCancelledAt', nowMs\n)\nredis.call('SREM', prefix .. 'status:pending', jobId)\nredis.call('SADD', prefix .. 'status:cancelled', jobId)\n-- Remove from queue / delayed\nredis.call('ZREM', prefix .. 'queue', jobId)\nredis.call('ZREM', prefix .. 'delayed', jobId)\n\nreturn 1\n`;\n\n/**\n * PROLONG JOB\n * KEYS: [prefix]\n * ARGV: [jobId, nowMs]\n */\nexport const PROLONG_JOB_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal jobId = ARGV[1]\nlocal nowMs = ARGV[2]\nlocal jk = prefix .. 'job:' .. jobId\n\nlocal status = redis.call('HGET', jk, 'status')\nif status ~= 'processing' then return 0 end\n\nredis.call('HMSET', jk,\n 'lockedAt', nowMs,\n 'updatedAt', nowMs\n)\n\nreturn 1\n`;\n\n/**\n * RECLAIM STUCK JOBS\n * KEYS: [prefix]\n * ARGV: [maxAgeMs, nowMs]\n * Returns: count of reclaimed jobs\n */\nexport const RECLAIM_STUCK_JOBS_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal maxAgeMs = tonumber(ARGV[1])\nlocal nowMs = tonumber(ARGV[2])\n\nlocal processing = redis.call('SMEMBERS', prefix .. 'status:processing')\nlocal count = 0\n\nfor _, jobId in ipairs(processing) do\n local jk = prefix .. 'job:' .. jobId\n local lockedAt = redis.call('HGET', jk, 'lockedAt')\n if lockedAt and lockedAt ~= 'null' then\n local lockedAtNum = tonumber(lockedAt)\n if lockedAtNum then\n -- Use the greater of maxAgeMs and the job's own timeoutMs\n local jobMaxAge = maxAgeMs\n local timeoutMs = redis.call('HGET', jk, 'timeoutMs')\n if timeoutMs and timeoutMs ~= 'null' then\n local tMs = tonumber(timeoutMs)\n if tMs and tMs > jobMaxAge then\n jobMaxAge = tMs\n end\n end\n local cutoff = nowMs - jobMaxAge\n if lockedAtNum < cutoff then\n redis.call('HMSET', jk,\n 'status', 'pending',\n 'lockedAt', 'null',\n 'lockedBy', 'null',\n 'updatedAt', nowMs\n )\n redis.call('SREM', prefix .. 'status:processing', jobId)\n redis.call('SADD', prefix .. 'status:pending', jobId)\n\n -- Re-add to queue\n local priority = tonumber(redis.call('HGET', jk, 'priority') or '0')\n local createdAt = tonumber(redis.call('HGET', jk, 'createdAt'))\n local score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - createdAt)\n redis.call('ZADD', prefix .. 'queue', score, jobId)\n\n count = count + 1\n end\n end\n end\nend\n\nreturn count\n`;\n\n/**\n * CLEANUP OLD JOBS\n * KEYS: [prefix]\n * ARGV: [cutoffMs]\n * Returns: count of deleted jobs\n */\nexport const CLEANUP_OLD_JOBS_SCRIPT = `\nlocal prefix = KEYS[1]\nlocal cutoffMs = tonumber(ARGV[1])\n\nlocal completed = redis.call('SMEMBERS', prefix .. 'status:completed')\nlocal count = 0\n\nfor _, jobId in ipairs(completed) do\n local jk = prefix .. 'job:' .. jobId\n local updatedAt = tonumber(redis.call('HGET', jk, 'updatedAt'))\n if updatedAt and updatedAt < cutoffMs then\n -- Remove all indexes\n local jobType = redis.call('HGET', jk, 'jobType')\n local tagsJson = redis.call('HGET', jk, 'tags')\n local idempotencyKey = redis.call('HGET', jk, 'idempotencyKey')\n\n redis.call('DEL', jk)\n redis.call('SREM', prefix .. 'status:completed', jobId)\n redis.call('ZREM', prefix .. 'all', jobId)\n if jobType then\n redis.call('SREM', prefix .. 'type:' .. jobType, jobId)\n end\n if tagsJson and tagsJson ~= 'null' then\n local ok, tags = pcall(cjson.decode, tagsJson)\n if ok and type(tags) == 'table' then\n for _, tag in ipairs(tags) do\n redis.call('SREM', prefix .. 'tag:' .. tag, jobId)\n end\n end\n redis.call('DEL', prefix .. 'job:' .. jobId .. ':tags')\n end\n if idempotencyKey and idempotencyKey ~= 'null' then\n redis.call('DEL', prefix .. 'idempotency:' .. idempotencyKey)\n end\n -- Delete events\n redis.call('DEL', prefix .. 'events:' .. jobId)\n\n count = count + 1\n end\nend\n\nreturn count\n`;\n","import { createRequire } from 'module';\nimport type { Redis as RedisType } from 'ioredis';\nimport {\n JobOptions,\n JobRecord,\n FailureReason,\n JobEvent,\n JobEventType,\n TagQueryMode,\n JobType,\n RedisJobQueueConfig,\n CronScheduleRecord,\n CronScheduleStatus,\n EditCronScheduleOptions,\n} from '../types.js';\nimport {\n QueueBackend,\n JobFilters,\n JobUpdates,\n CronScheduleInput,\n} from '../backend.js';\nimport { log } from '../log-context.js';\nimport {\n ADD_JOB_SCRIPT,\n GET_NEXT_BATCH_SCRIPT,\n COMPLETE_JOB_SCRIPT,\n FAIL_JOB_SCRIPT,\n RETRY_JOB_SCRIPT,\n CANCEL_JOB_SCRIPT,\n PROLONG_JOB_SCRIPT,\n RECLAIM_STUCK_JOBS_SCRIPT,\n CLEANUP_OLD_JOBS_SCRIPT,\n} from './redis-scripts.js';\n\n/** Helper: convert a Redis hash flat array [k,v,k,v,...] to a JS object */\nfunction hashToObject(arr: string[]): Record<string, string> {\n const obj: Record<string, string> = {};\n for (let i = 0; i < arr.length; i += 2) {\n obj[arr[i]] = arr[i + 1];\n }\n return obj;\n}\n\n/** Deserialise a Redis hash object into a JobRecord */\nfunction deserializeJob<PayloadMap, T extends JobType<PayloadMap>>(\n h: Record<string, string>,\n): JobRecord<PayloadMap, T> {\n const nullish = (v: string | undefined) =>\n v === undefined || v === 'null' || v === '' ? null : v;\n\n const numOrNull = (v: string | undefined): number | null => {\n const n = nullish(v);\n return n === null ? null : Number(n);\n };\n const dateOrNull = (v: string | undefined): Date | null => {\n const n = numOrNull(v);\n return n === null ? null : new Date(n);\n };\n\n let errorHistory: { message: string; timestamp: string }[] = [];\n try {\n const raw = h.errorHistory;\n if (raw && raw !== '[]') {\n errorHistory = JSON.parse(raw);\n }\n } catch {\n /* ignore */\n }\n\n let tags: string[] | undefined;\n try {\n const raw = h.tags;\n if (raw && raw !== 'null') {\n tags = JSON.parse(raw);\n }\n } catch {\n /* ignore */\n }\n\n let payload: any;\n try {\n payload = JSON.parse(h.payload);\n } catch {\n payload = h.payload;\n }\n\n return {\n id: Number(h.id),\n jobType: h.jobType as T,\n payload,\n status: h.status as any,\n createdAt: new Date(Number(h.createdAt)),\n updatedAt: new Date(Number(h.updatedAt)),\n lockedAt: dateOrNull(h.lockedAt),\n lockedBy: nullish(h.lockedBy) as string | null,\n attempts: Number(h.attempts),\n maxAttempts: Number(h.maxAttempts),\n nextAttemptAt: dateOrNull(h.nextAttemptAt),\n priority: Number(h.priority),\n runAt: new Date(Number(h.runAt)),\n pendingReason: nullish(h.pendingReason) as string | null | undefined,\n errorHistory,\n timeoutMs: numOrNull(h.timeoutMs),\n forceKillOnTimeout:\n h.forceKillOnTimeout === 'true' || h.forceKillOnTimeout === '1'\n ? true\n : h.forceKillOnTimeout === 'false' || h.forceKillOnTimeout === '0'\n ? false\n : null,\n failureReason: (nullish(h.failureReason) as FailureReason | null) ?? null,\n completedAt: dateOrNull(h.completedAt),\n startedAt: dateOrNull(h.startedAt),\n lastRetriedAt: dateOrNull(h.lastRetriedAt),\n lastFailedAt: dateOrNull(h.lastFailedAt),\n lastCancelledAt: dateOrNull(h.lastCancelledAt),\n tags,\n idempotencyKey: nullish(h.idempotencyKey) as string | null | undefined,\n progress: numOrNull(h.progress),\n };\n}\n\nexport class RedisBackend implements QueueBackend {\n private client: RedisType;\n private prefix: string;\n\n constructor(redisConfig: RedisJobQueueConfig['redisConfig']) {\n // Dynamically require ioredis to avoid hard dep\n let IORedis: any;\n try {\n const _require = createRequire(import.meta.url);\n IORedis = _require('ioredis');\n } catch {\n throw new Error(\n 'Redis backend requires the \"ioredis\" package. Install it with: npm install ioredis',\n );\n }\n\n this.prefix = redisConfig.keyPrefix ?? 'dq:';\n\n if (redisConfig.url) {\n this.client = new IORedis(redisConfig.url, {\n ...(redisConfig.tls ? { tls: redisConfig.tls } : {}),\n ...(redisConfig.db !== undefined ? { db: redisConfig.db } : {}),\n });\n } else {\n this.client = new IORedis({\n host: redisConfig.host ?? '127.0.0.1',\n port: redisConfig.port ?? 6379,\n password: redisConfig.password,\n db: redisConfig.db ?? 0,\n ...(redisConfig.tls ? { tls: redisConfig.tls } : {}),\n });\n }\n }\n\n /** Expose the raw ioredis client for advanced usage. */\n getClient(): RedisType {\n return this.client;\n }\n\n private nowMs(): number {\n return Date.now();\n }\n\n // ── Events ──────────────────────────────────────────────────────────\n\n async recordJobEvent(\n jobId: number,\n eventType: JobEventType,\n metadata?: any,\n ): Promise<void> {\n try {\n const eventId = await this.client.incr(`${this.prefix}event_id_seq`);\n const event = JSON.stringify({\n id: eventId,\n jobId,\n eventType,\n createdAt: this.nowMs(),\n metadata: metadata ?? null,\n });\n await this.client.rpush(`${this.prefix}events:${jobId}`, event);\n } catch (error) {\n log(`Error recording job event for job ${jobId}: ${error}`);\n // Do not throw\n }\n }\n\n async getJobEvents(jobId: number): Promise<JobEvent[]> {\n const raw = await this.client.lrange(\n `${this.prefix}events:${jobId}`,\n 0,\n -1,\n );\n return raw.map((r: string) => {\n const e = JSON.parse(r);\n return {\n ...e,\n createdAt: new Date(e.createdAt),\n };\n });\n }\n\n // ── Job CRUD ──────────────────────────────────────────────────────────\n\n async addJob<PayloadMap, T extends JobType<PayloadMap>>({\n jobType,\n payload,\n maxAttempts = 3,\n priority = 0,\n runAt = null,\n timeoutMs = undefined,\n forceKillOnTimeout = false,\n tags = undefined,\n idempotencyKey = undefined,\n }: JobOptions<PayloadMap, T>): Promise<number> {\n const now = this.nowMs();\n const runAtMs = runAt ? runAt.getTime() : 0;\n\n const result = (await this.client.eval(\n ADD_JOB_SCRIPT,\n 1,\n this.prefix,\n jobType,\n JSON.stringify(payload),\n maxAttempts,\n priority,\n runAtMs.toString(),\n timeoutMs !== undefined ? timeoutMs.toString() : 'null',\n forceKillOnTimeout ? 'true' : 'false',\n tags ? JSON.stringify(tags) : 'null',\n idempotencyKey ?? 'null',\n now,\n )) as number;\n\n const jobId = Number(result);\n log(\n `Added job ${jobId}: payload ${JSON.stringify(payload)}, ${runAt ? `runAt ${runAt.toISOString()}, ` : ''}priority ${priority}, maxAttempts ${maxAttempts}, jobType ${jobType}, tags ${JSON.stringify(tags)}${idempotencyKey ? `, idempotencyKey \"${idempotencyKey}\"` : ''}`,\n );\n await this.recordJobEvent(jobId, JobEventType.Added, {\n jobType,\n payload,\n tags,\n idempotencyKey,\n });\n return jobId;\n }\n\n async getJob<PayloadMap, T extends JobType<PayloadMap>>(\n id: number,\n ): Promise<JobRecord<PayloadMap, T> | null> {\n const data = await this.client.hgetall(`${this.prefix}job:${id}`);\n if (!data || Object.keys(data).length === 0) {\n log(`Job ${id} not found`);\n return null;\n }\n log(`Found job ${id}`);\n return deserializeJob<PayloadMap, T>(data);\n }\n\n async getJobsByStatus<PayloadMap, T extends JobType<PayloadMap>>(\n status: string,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const ids = await this.client.smembers(`${this.prefix}status:${status}`);\n if (ids.length === 0) return [];\n\n // Load all, sort by createdAt DESC, then paginate\n const jobs = await this.loadJobsByIds<PayloadMap, T>(ids);\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n async getAllJobs<PayloadMap, T extends JobType<PayloadMap>>(\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // All jobs sorted by createdAt DESC (the 'all' sorted set is scored by createdAt ms)\n const ids = await this.client.zrevrange(\n `${this.prefix}all`,\n offset,\n offset + limit - 1,\n );\n if (ids.length === 0) return [];\n return this.loadJobsByIds<PayloadMap, T>(ids);\n }\n\n async getJobs<PayloadMap, T extends JobType<PayloadMap>>(\n filters?: JobFilters,\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // Start with all job IDs\n let candidateIds: string[];\n\n if (filters?.jobType) {\n candidateIds = await this.client.smembers(\n `${this.prefix}type:${filters.jobType}`,\n );\n } else {\n candidateIds = await this.client.zrevrange(`${this.prefix}all`, 0, -1);\n }\n\n if (candidateIds.length === 0) return [];\n\n // Apply tag filter via set operations\n if (filters?.tags && filters.tags.values.length > 0) {\n candidateIds = await this.filterByTags(\n candidateIds,\n filters.tags.values,\n filters.tags.mode || 'all',\n );\n }\n\n // Load and filter remaining criteria in-memory\n let jobs = await this.loadJobsByIds<PayloadMap, T>(candidateIds);\n\n if (filters) {\n if (filters.priority !== undefined) {\n jobs = jobs.filter((j) => j.priority === filters.priority);\n }\n if (filters.runAt) {\n jobs = this.filterByRunAt(jobs, filters.runAt);\n }\n }\n\n // Sort by createdAt DESC\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n async getJobsByTags<PayloadMap, T extends JobType<PayloadMap>>(\n tags: string[],\n mode: TagQueryMode = 'all',\n limit = 100,\n offset = 0,\n ): Promise<JobRecord<PayloadMap, T>[]> {\n // Start with all IDs\n const allIds = await this.client.zrevrange(`${this.prefix}all`, 0, -1);\n if (allIds.length === 0) return [];\n\n const filtered = await this.filterByTags(allIds, tags, mode);\n if (filtered.length === 0) return [];\n\n const jobs = await this.loadJobsByIds<PayloadMap, T>(filtered);\n jobs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return jobs.slice(offset, offset + limit);\n }\n\n // ── Processing lifecycle ──────────────────────────────────────────────\n\n async getNextBatch<PayloadMap, T extends JobType<PayloadMap>>(\n workerId: string,\n batchSize = 10,\n jobType?: string | string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const now = this.nowMs();\n const jobTypeFilter =\n jobType === undefined\n ? 'null'\n : Array.isArray(jobType)\n ? JSON.stringify(jobType)\n : jobType;\n\n const result = (await this.client.eval(\n GET_NEXT_BATCH_SCRIPT,\n 1,\n this.prefix,\n workerId,\n batchSize,\n now,\n jobTypeFilter,\n )) as string[];\n\n if (!result || result.length === 0) {\n log('Found 0 jobs to process');\n return [];\n }\n\n // Parse the flat result into jobs separated by __JOB_SEP__\n const jobs: JobRecord<PayloadMap, T>[] = [];\n let current: string[] = [];\n for (const item of result) {\n if (item === '__JOB_SEP__') {\n if (current.length > 0) {\n const h = hashToObject(current);\n jobs.push(deserializeJob<PayloadMap, T>(h));\n }\n current = [];\n } else {\n current.push(item);\n }\n }\n\n log(`Found ${jobs.length} jobs to process`);\n\n // Record processing events\n for (const job of jobs) {\n await this.recordJobEvent(job.id, JobEventType.Processing);\n }\n\n return jobs;\n }\n\n async completeJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(COMPLETE_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Completed);\n log(`Completed job ${jobId}`);\n }\n\n async failJob(\n jobId: number,\n error: Error,\n failureReason?: FailureReason,\n ): Promise<void> {\n const now = this.nowMs();\n const errorJson = JSON.stringify([\n {\n message: error.message || String(error),\n timestamp: new Date(now).toISOString(),\n },\n ]);\n await this.client.eval(\n FAIL_JOB_SCRIPT,\n 1,\n this.prefix,\n jobId,\n errorJson,\n failureReason ?? 'null',\n now,\n );\n await this.recordJobEvent(jobId, JobEventType.Failed, {\n message: error.message || String(error),\n failureReason,\n });\n log(`Failed job ${jobId}`);\n }\n\n async prolongJob(jobId: number): Promise<void> {\n try {\n const now = this.nowMs();\n await this.client.eval(PROLONG_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Prolonged);\n log(`Prolonged job ${jobId}`);\n } catch (error) {\n log(`Error prolonging job ${jobId}: ${error}`);\n // Best-effort, do not throw\n }\n }\n\n // ── Progress ──────────────────────────────────────────────────────────\n\n async updateProgress(jobId: number, progress: number): Promise<void> {\n try {\n const now = this.nowMs();\n await this.client.hset(\n `${this.prefix}job:${jobId}`,\n 'progress',\n progress.toString(),\n 'updatedAt',\n now.toString(),\n );\n log(`Updated progress for job ${jobId}: ${progress}%`);\n } catch (error) {\n log(`Error updating progress for job ${jobId}: ${error}`);\n // Best-effort: do not throw to avoid killing the running handler\n }\n }\n\n // ── Job management ────────────────────────────────────────────────────\n\n async retryJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(RETRY_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Retried);\n log(`Retried job ${jobId}`);\n }\n\n async cancelJob(jobId: number): Promise<void> {\n const now = this.nowMs();\n await this.client.eval(CANCEL_JOB_SCRIPT, 1, this.prefix, jobId, now);\n await this.recordJobEvent(jobId, JobEventType.Cancelled);\n log(`Cancelled job ${jobId}`);\n }\n\n async cancelAllUpcomingJobs(filters?: JobFilters): Promise<number> {\n // Get all pending IDs\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return 0;\n\n if (filters) {\n ids = await this.applyFilters(ids, filters);\n }\n\n const now = this.nowMs();\n let count = 0;\n for (const id of ids) {\n const result = await this.client.eval(\n CANCEL_JOB_SCRIPT,\n 1,\n this.prefix,\n id,\n now,\n );\n if (Number(result) === 1) count++;\n }\n\n log(`Cancelled ${count} jobs`);\n return count;\n }\n\n async editJob(jobId: number, updates: JobUpdates): Promise<void> {\n const jk = `${this.prefix}job:${jobId}`;\n const status = await this.client.hget(jk, 'status');\n if (status !== 'pending') {\n log(`Job ${jobId} is not pending (status: ${status}), skipping edit`);\n return;\n }\n\n const now = this.nowMs();\n const fields: string[] = [];\n const metadata: any = {};\n\n if (updates.payload !== undefined) {\n fields.push('payload', JSON.stringify(updates.payload));\n metadata.payload = updates.payload;\n }\n if (updates.maxAttempts !== undefined) {\n fields.push('maxAttempts', updates.maxAttempts.toString());\n metadata.maxAttempts = updates.maxAttempts;\n }\n if (updates.priority !== undefined) {\n fields.push('priority', updates.priority.toString());\n metadata.priority = updates.priority;\n\n // Recompute queue score\n const createdAt = await this.client.hget(jk, 'createdAt');\n const score = updates.priority * 1e15 + (1e15 - Number(createdAt));\n // Update score in queue if present\n const inQueue = await this.client.zscore(\n `${this.prefix}queue`,\n jobId.toString(),\n );\n if (inQueue !== null) {\n await this.client.zadd(`${this.prefix}queue`, score, jobId.toString());\n }\n }\n if (updates.runAt !== undefined) {\n if (updates.runAt === null) {\n fields.push('runAt', now.toString());\n } else {\n fields.push('runAt', updates.runAt.getTime().toString());\n }\n metadata.runAt = updates.runAt;\n }\n if (updates.timeoutMs !== undefined) {\n fields.push(\n 'timeoutMs',\n updates.timeoutMs !== null ? updates.timeoutMs.toString() : 'null',\n );\n metadata.timeoutMs = updates.timeoutMs;\n }\n if (updates.tags !== undefined) {\n // Update tag indexes: remove old, add new\n const oldTagsJson = await this.client.hget(jk, 'tags');\n if (oldTagsJson && oldTagsJson !== 'null') {\n try {\n const oldTags = JSON.parse(oldTagsJson) as string[];\n for (const tag of oldTags) {\n await this.client.srem(\n `${this.prefix}tag:${tag}`,\n jobId.toString(),\n );\n }\n } catch {\n /* ignore */\n }\n }\n await this.client.del(`${this.prefix}job:${jobId}:tags`);\n\n if (updates.tags !== null) {\n for (const tag of updates.tags) {\n await this.client.sadd(`${this.prefix}tag:${tag}`, jobId.toString());\n await this.client.sadd(`${this.prefix}job:${jobId}:tags`, tag);\n }\n fields.push('tags', JSON.stringify(updates.tags));\n } else {\n fields.push('tags', 'null');\n }\n metadata.tags = updates.tags;\n }\n\n if (fields.length === 0) {\n log(`No fields to update for job ${jobId}`);\n return;\n }\n\n fields.push('updatedAt', now.toString());\n await (this.client as any).hmset(jk, ...fields);\n\n await this.recordJobEvent(jobId, JobEventType.Edited, metadata);\n log(`Edited job ${jobId}: ${JSON.stringify(metadata)}`);\n }\n\n async editAllPendingJobs(\n filters: JobFilters | undefined,\n updates: JobUpdates,\n ): Promise<number> {\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return 0;\n\n if (filters) {\n ids = await this.applyFilters(ids, filters);\n }\n\n let count = 0;\n for (const id of ids) {\n await this.editJob(Number(id), updates);\n count++;\n }\n\n log(`Edited ${count} pending jobs`);\n return count;\n }\n\n async cleanupOldJobs(daysToKeep = 30): Promise<number> {\n const cutoffMs = this.nowMs() - daysToKeep * 24 * 60 * 60 * 1000;\n const result = (await this.client.eval(\n CLEANUP_OLD_JOBS_SCRIPT,\n 1,\n this.prefix,\n cutoffMs,\n )) as number;\n log(`Deleted ${result} old jobs`);\n return Number(result);\n }\n\n async cleanupOldJobEvents(daysToKeep = 30): Promise<number> {\n // Redis events are stored per-job; cleaning up old events requires\n // iterating event lists and filtering by date. For now, we skip\n // events belonging to jobs that have been cleaned up (their keys are gone).\n // A full implementation would iterate all events:* keys.\n log(\n `cleanupOldJobEvents is a no-op for Redis backend (events are cleaned up with their jobs)`,\n );\n return 0;\n }\n\n async reclaimStuckJobs(maxProcessingTimeMinutes = 10): Promise<number> {\n const maxAgeMs = maxProcessingTimeMinutes * 60 * 1000;\n const now = this.nowMs();\n const result = (await this.client.eval(\n RECLAIM_STUCK_JOBS_SCRIPT,\n 1,\n this.prefix,\n maxAgeMs,\n now,\n )) as number;\n log(`Reclaimed ${result} stuck jobs`);\n return Number(result);\n }\n\n // ── Internal helpers ──────────────────────────────────────────────────\n\n async setPendingReasonForUnpickedJobs(\n reason: string,\n jobType?: string | string[],\n ): Promise<void> {\n let ids = await this.client.smembers(`${this.prefix}status:pending`);\n if (ids.length === 0) return;\n\n if (jobType) {\n const types = Array.isArray(jobType) ? jobType : [jobType];\n const typeSet = new Set<string>();\n for (const t of types) {\n const typeIds = await this.client.smembers(`${this.prefix}type:${t}`);\n for (const id of typeIds) typeSet.add(id);\n }\n ids = ids.filter((id: string) => typeSet.has(id));\n }\n\n for (const id of ids) {\n await this.client.hset(\n `${this.prefix}job:${id}`,\n 'pendingReason',\n reason,\n );\n }\n }\n\n // ── Private helpers ───────────────────────────────────────────────────\n\n private async loadJobsByIds<PayloadMap, T extends JobType<PayloadMap>>(\n ids: string[],\n ): Promise<JobRecord<PayloadMap, T>[]> {\n const pipeline = this.client.pipeline();\n for (const id of ids) {\n pipeline.hgetall(`${this.prefix}job:${id}`);\n }\n const results = await pipeline.exec();\n const jobs: JobRecord<PayloadMap, T>[] = [];\n if (results) {\n for (const [err, data] of results) {\n if (\n !err &&\n data &&\n typeof data === 'object' &&\n Object.keys(data as object).length > 0\n ) {\n jobs.push(\n deserializeJob<PayloadMap, T>(data as Record<string, string>),\n );\n }\n }\n }\n return jobs;\n }\n\n private async filterByTags(\n candidateIds: string[],\n tags: string[],\n mode: TagQueryMode,\n ): Promise<string[]> {\n const candidateSet = new Set(candidateIds.map(String));\n\n if (mode === 'exact') {\n // Jobs whose tags set is exactly equal to the given tags\n const tagSet = new Set(tags);\n const result: string[] = [];\n for (const id of candidateIds) {\n const jobTags = await this.client.smembers(\n `${this.prefix}job:${id}:tags`,\n );\n if (\n jobTags.length === tagSet.size &&\n jobTags.every((t: string) => tagSet.has(t))\n ) {\n result.push(id);\n }\n }\n return result;\n }\n\n if (mode === 'all') {\n // Jobs that have ALL the given tags\n let intersection = new Set(candidateIds.map(String));\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n const tagSet = new Set(tagMembers.map(String));\n intersection = new Set(\n [...intersection].filter((id) => tagSet.has(id)),\n );\n }\n return [...intersection].filter((id) => candidateSet.has(id));\n }\n\n if (mode === 'any') {\n // Jobs that have at least ONE of the given tags\n const union = new Set<string>();\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n for (const id of tagMembers) union.add(String(id));\n }\n return [...union].filter((id) => candidateSet.has(id));\n }\n\n if (mode === 'none') {\n // Jobs that have NONE of the given tags\n const exclude = new Set<string>();\n for (const tag of tags) {\n const tagMembers = await this.client.smembers(\n `${this.prefix}tag:${tag}`,\n );\n for (const id of tagMembers) exclude.add(String(id));\n }\n return candidateIds.filter((id) => !exclude.has(String(id)));\n }\n\n // Default: 'all'\n return this.filterByTags(candidateIds, tags, 'all');\n }\n\n private filterByRunAt<PayloadMap, T extends JobType<PayloadMap>>(\n jobs: JobRecord<PayloadMap, T>[],\n runAt: Date | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date },\n ): JobRecord<PayloadMap, T>[] {\n if (runAt instanceof Date) {\n return jobs.filter((j) => j.runAt.getTime() === runAt.getTime());\n }\n return jobs.filter((j) => {\n const t = j.runAt.getTime();\n if (runAt.gt && !(t > runAt.gt.getTime())) return false;\n if (runAt.gte && !(t >= runAt.gte.getTime())) return false;\n if (runAt.lt && !(t < runAt.lt.getTime())) return false;\n if (runAt.lte && !(t <= runAt.lte.getTime())) return false;\n if (runAt.eq && t !== runAt.eq.getTime()) return false;\n return true;\n });\n }\n\n // ── Cron schedules ──────────────────────────────────────────────────\n\n /** Create a cron schedule and return its ID. */\n async addCronSchedule(input: CronScheduleInput): Promise<number> {\n const existingId = await this.client.get(\n `${this.prefix}cron_name:${input.scheduleName}`,\n );\n if (existingId !== null) {\n throw new Error(\n `Cron schedule with name \"${input.scheduleName}\" already exists`,\n );\n }\n\n const id = await this.client.incr(`${this.prefix}cron_id_seq`);\n const now = this.nowMs();\n const key = `${this.prefix}cron:${id}`;\n\n const fields: string[] = [\n 'id',\n id.toString(),\n 'scheduleName',\n input.scheduleName,\n 'cronExpression',\n input.cronExpression,\n 'jobType',\n input.jobType,\n 'payload',\n JSON.stringify(input.payload),\n 'maxAttempts',\n input.maxAttempts.toString(),\n 'priority',\n input.priority.toString(),\n 'timeoutMs',\n input.timeoutMs !== null ? input.timeoutMs.toString() : 'null',\n 'forceKillOnTimeout',\n input.forceKillOnTimeout ? 'true' : 'false',\n 'tags',\n input.tags ? JSON.stringify(input.tags) : 'null',\n 'timezone',\n input.timezone,\n 'allowOverlap',\n input.allowOverlap ? 'true' : 'false',\n 'status',\n 'active',\n 'lastEnqueuedAt',\n 'null',\n 'lastJobId',\n 'null',\n 'nextRunAt',\n input.nextRunAt ? input.nextRunAt.getTime().toString() : 'null',\n 'createdAt',\n now.toString(),\n 'updatedAt',\n now.toString(),\n ];\n\n await (this.client as any).hmset(key, ...fields);\n await this.client.set(\n `${this.prefix}cron_name:${input.scheduleName}`,\n id.toString(),\n );\n await this.client.sadd(`${this.prefix}crons`, id.toString());\n await this.client.sadd(`${this.prefix}cron_status:active`, id.toString());\n\n if (input.nextRunAt) {\n await this.client.zadd(\n `${this.prefix}cron_due`,\n input.nextRunAt.getTime(),\n id.toString(),\n );\n }\n\n log(`Added cron schedule ${id}: \"${input.scheduleName}\"`);\n return id;\n }\n\n /** Get a cron schedule by ID. */\n async getCronSchedule(id: number): Promise<CronScheduleRecord | null> {\n const data = await this.client.hgetall(`${this.prefix}cron:${id}`);\n if (!data || Object.keys(data).length === 0) return null;\n return this.deserializeCronSchedule(data);\n }\n\n /** Get a cron schedule by its unique name. */\n async getCronScheduleByName(\n name: string,\n ): Promise<CronScheduleRecord | null> {\n const id = await this.client.get(`${this.prefix}cron_name:${name}`);\n if (id === null) return null;\n return this.getCronSchedule(Number(id));\n }\n\n /** List cron schedules, optionally filtered by status. */\n async listCronSchedules(\n status?: CronScheduleStatus,\n ): Promise<CronScheduleRecord[]> {\n let ids: string[];\n if (status) {\n ids = await this.client.smembers(`${this.prefix}cron_status:${status}`);\n } else {\n ids = await this.client.smembers(`${this.prefix}crons`);\n }\n if (ids.length === 0) return [];\n\n const pipeline = this.client.pipeline();\n for (const id of ids) {\n pipeline.hgetall(`${this.prefix}cron:${id}`);\n }\n const results = await pipeline.exec();\n const schedules: CronScheduleRecord[] = [];\n if (results) {\n for (const [err, data] of results) {\n if (\n !err &&\n data &&\n typeof data === 'object' &&\n Object.keys(data as object).length > 0\n ) {\n schedules.push(\n this.deserializeCronSchedule(data as Record<string, string>),\n );\n }\n }\n }\n schedules.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());\n return schedules;\n }\n\n /** Delete a cron schedule by ID. */\n async removeCronSchedule(id: number): Promise<void> {\n const data = await this.client.hgetall(`${this.prefix}cron:${id}`);\n if (!data || Object.keys(data).length === 0) return;\n\n const name = data.scheduleName;\n const status = data.status;\n\n await this.client.del(`${this.prefix}cron:${id}`);\n await this.client.del(`${this.prefix}cron_name:${name}`);\n await this.client.srem(`${this.prefix}crons`, id.toString());\n await this.client.srem(\n `${this.prefix}cron_status:${status}`,\n id.toString(),\n );\n await this.client.zrem(`${this.prefix}cron_due`, id.toString());\n log(`Removed cron schedule ${id}`);\n }\n\n /** Pause a cron schedule. */\n async pauseCronSchedule(id: number): Promise<void> {\n const now = this.nowMs();\n await this.client.hset(\n `${this.prefix}cron:${id}`,\n 'status',\n 'paused',\n 'updatedAt',\n now.toString(),\n );\n await this.client.srem(`${this.prefix}cron_status:active`, id.toString());\n await this.client.sadd(`${this.prefix}cron_status:paused`, id.toString());\n await this.client.zrem(`${this.prefix}cron_due`, id.toString());\n log(`Paused cron schedule ${id}`);\n }\n\n /** Resume a paused cron schedule. */\n async resumeCronSchedule(id: number): Promise<void> {\n const now = this.nowMs();\n await this.client.hset(\n `${this.prefix}cron:${id}`,\n 'status',\n 'active',\n 'updatedAt',\n now.toString(),\n );\n await this.client.srem(`${this.prefix}cron_status:paused`, id.toString());\n await this.client.sadd(`${this.prefix}cron_status:active`, id.toString());\n\n const nextRunAt = await this.client.hget(\n `${this.prefix}cron:${id}`,\n 'nextRunAt',\n );\n if (nextRunAt && nextRunAt !== 'null') {\n await this.client.zadd(\n `${this.prefix}cron_due`,\n Number(nextRunAt),\n id.toString(),\n );\n }\n log(`Resumed cron schedule ${id}`);\n }\n\n /** Edit a cron schedule. */\n async editCronSchedule(\n id: number,\n updates: EditCronScheduleOptions,\n nextRunAt?: Date | null,\n ): Promise<void> {\n const now = this.nowMs();\n const fields: string[] = [];\n\n if (updates.cronExpression !== undefined) {\n fields.push('cronExpression', updates.cronExpression);\n }\n if (updates.payload !== undefined) {\n fields.push('payload', JSON.stringify(updates.payload));\n }\n if (updates.maxAttempts !== undefined) {\n fields.push('maxAttempts', updates.maxAttempts.toString());\n }\n if (updates.priority !== undefined) {\n fields.push('priority', updates.priority.toString());\n }\n if (updates.timeoutMs !== undefined) {\n fields.push(\n 'timeoutMs',\n updates.timeoutMs !== null ? updates.timeoutMs.toString() : 'null',\n );\n }\n if (updates.forceKillOnTimeout !== undefined) {\n fields.push(\n 'forceKillOnTimeout',\n updates.forceKillOnTimeout ? 'true' : 'false',\n );\n }\n if (updates.tags !== undefined) {\n fields.push(\n 'tags',\n updates.tags !== null ? JSON.stringify(updates.tags) : 'null',\n );\n }\n if (updates.timezone !== undefined) {\n fields.push('timezone', updates.timezone);\n }\n if (updates.allowOverlap !== undefined) {\n fields.push('allowOverlap', updates.allowOverlap ? 'true' : 'false');\n }\n if (nextRunAt !== undefined) {\n const val = nextRunAt !== null ? nextRunAt.getTime().toString() : 'null';\n fields.push('nextRunAt', val);\n if (nextRunAt !== null) {\n await this.client.zadd(\n `${this.prefix}cron_due`,\n nextRunAt.getTime(),\n id.toString(),\n );\n } else {\n await this.client.zrem(`${this.prefix}cron_due`, id.toString());\n }\n }\n\n if (fields.length === 0) {\n log(`No fields to update for cron schedule ${id}`);\n return;\n }\n\n fields.push('updatedAt', now.toString());\n await (this.client as any).hmset(`${this.prefix}cron:${id}`, ...fields);\n log(`Edited cron schedule ${id}`);\n }\n\n /**\n * Fetch all active cron schedules whose nextRunAt <= now.\n * Uses a sorted set (cron_due) for efficient range query.\n */\n async getDueCronSchedules(): Promise<CronScheduleRecord[]> {\n const now = this.nowMs();\n const ids = await this.client.zrangebyscore(\n `${this.prefix}cron_due`,\n 0,\n now,\n );\n if (ids.length === 0) {\n log('Found 0 due cron schedules');\n return [];\n }\n\n const schedules: CronScheduleRecord[] = [];\n for (const id of ids) {\n const data = await this.client.hgetall(`${this.prefix}cron:${id}`);\n if (data && Object.keys(data).length > 0 && data.status === 'active') {\n schedules.push(this.deserializeCronSchedule(data));\n }\n }\n log(`Found ${schedules.length} due cron schedules`);\n return schedules;\n }\n\n /**\n * Update a cron schedule after a job has been enqueued.\n * Sets lastEnqueuedAt, lastJobId, and advances nextRunAt.\n */\n async updateCronScheduleAfterEnqueue(\n id: number,\n lastEnqueuedAt: Date,\n lastJobId: number,\n nextRunAt: Date | null,\n ): Promise<void> {\n const fields: string[] = [\n 'lastEnqueuedAt',\n lastEnqueuedAt.getTime().toString(),\n 'lastJobId',\n lastJobId.toString(),\n 'nextRunAt',\n nextRunAt ? nextRunAt.getTime().toString() : 'null',\n 'updatedAt',\n this.nowMs().toString(),\n ];\n\n await (this.client as any).hmset(`${this.prefix}cron:${id}`, ...fields);\n\n if (nextRunAt) {\n await this.client.zadd(\n `${this.prefix}cron_due`,\n nextRunAt.getTime(),\n id.toString(),\n );\n } else {\n await this.client.zrem(`${this.prefix}cron_due`, id.toString());\n }\n\n log(\n `Updated cron schedule ${id}: lastJobId=${lastJobId}, nextRunAt=${nextRunAt?.toISOString() ?? 'null'}`,\n );\n }\n\n /** Deserialize a Redis hash into a CronScheduleRecord. */\n private deserializeCronSchedule(\n h: Record<string, string>,\n ): CronScheduleRecord {\n const nullish = (v: string | undefined) =>\n v === undefined || v === 'null' || v === '' ? null : v;\n const numOrNull = (v: string | undefined): number | null => {\n const n = nullish(v);\n return n === null ? null : Number(n);\n };\n const dateOrNull = (v: string | undefined): Date | null => {\n const n = numOrNull(v);\n return n === null ? null : new Date(n);\n };\n\n let payload: any;\n try {\n payload = JSON.parse(h.payload);\n } catch {\n payload = h.payload;\n }\n\n let tags: string[] | undefined;\n try {\n const raw = h.tags;\n if (raw && raw !== 'null') {\n tags = JSON.parse(raw);\n }\n } catch {\n /* ignore */\n }\n\n return {\n id: Number(h.id),\n scheduleName: h.scheduleName,\n cronExpression: h.cronExpression,\n jobType: h.jobType,\n payload,\n maxAttempts: Number(h.maxAttempts),\n priority: Number(h.priority),\n timeoutMs: numOrNull(h.timeoutMs),\n forceKillOnTimeout: h.forceKillOnTimeout === 'true',\n tags,\n timezone: h.timezone,\n allowOverlap: h.allowOverlap === 'true',\n status: h.status as CronScheduleStatus,\n lastEnqueuedAt: dateOrNull(h.lastEnqueuedAt),\n lastJobId: numOrNull(h.lastJobId),\n nextRunAt: dateOrNull(h.nextRunAt),\n createdAt: new Date(Number(h.createdAt)),\n updatedAt: new Date(Number(h.updatedAt)),\n };\n }\n\n // ── Private helpers (filters) ─────────────────────────────────────────\n\n private async applyFilters(\n ids: string[],\n filters: JobFilters,\n ): Promise<string[]> {\n let result = ids;\n\n if (filters.jobType) {\n const typeIds = new Set(\n await this.client.smembers(`${this.prefix}type:${filters.jobType}`),\n );\n result = result.filter((id) => typeIds.has(id));\n }\n\n if (filters.tags && filters.tags.values.length > 0) {\n result = await this.filterByTags(\n result,\n filters.tags.values,\n filters.tags.mode || 'all',\n );\n }\n\n // For priority and runAt, we need to load job data\n if (filters.priority !== undefined || filters.runAt) {\n const jobs = await this.loadJobsByIds(result);\n let filtered = jobs;\n if (filters.priority !== undefined) {\n filtered = filtered.filter((j) => j.priority === filters.priority);\n }\n if (filters.runAt) {\n filtered = this.filterByRunAt(filtered, filters.runAt);\n }\n result = filtered.map((j) => j.id.toString());\n }\n\n return result;\n }\n}\n","import { Cron } from 'croner';\n\n/**\n * Calculate the next occurrence of a cron expression after a given date.\n *\n * @param cronExpression - A standard cron expression (5 fields, e.g. \"0 * * * *\").\n * @param timezone - IANA timezone string (default: \"UTC\").\n * @param after - The reference date to compute the next run from (default: now).\n * @param CronImpl - Cron class for dependency injection (default: croner's Cron).\n * @returns The next occurrence as a Date, or null if the expression will never fire again.\n */\nexport function getNextCronOccurrence(\n cronExpression: string,\n timezone: string = 'UTC',\n after?: Date,\n CronImpl: typeof Cron = Cron,\n): Date | null {\n const cron = new CronImpl(cronExpression, { timezone });\n const next = cron.nextRun(after ?? new Date());\n return next ?? null;\n}\n\n/**\n * Validate whether a string is a syntactically correct cron expression.\n *\n * @param cronExpression - The cron expression to validate.\n * @param CronImpl - Cron class for dependency injection (default: croner's Cron).\n * @returns True if the expression is valid, false otherwise.\n */\nexport function validateCronExpression(\n cronExpression: string,\n CronImpl: typeof Cron = Cron,\n): boolean {\n try {\n new CronImpl(cronExpression);\n return true;\n } catch {\n return false;\n }\n}\n","import { JobHandler } from './types.js';\n\n/**\n * Validates that a job handler can be serialized for use with forceKillOnTimeout.\n *\n * This function checks if a handler can be safely serialized and executed in a worker thread.\n * Use this function during development to catch serialization issues early.\n *\n * @param handler - The job handler function to validate\n * @param jobType - Optional job type name for better error messages\n * @returns An object with `isSerializable` boolean and optional `error` message\n *\n * @example\n * ```ts\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * const result = validateHandlerSerializable(handler, 'myJob');\n * if (!result.isSerializable) {\n * console.error('Handler is not serializable:', result.error);\n * }\n * ```\n */\nexport function validateHandlerSerializable<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n jobType?: string,\n): { isSerializable: boolean; error?: string } {\n try {\n const handlerString = handler.toString();\n const typeLabel = jobType ? `job type \"${jobType}\"` : 'handler';\n\n // Check for common patterns that indicate non-serializable handlers\n // 1. Arrow functions that capture 'this' (indicated by 'this' in the function body but not in parameters)\n if (\n handlerString.includes('this.') &&\n !handlerString.match(/\\([^)]*this[^)]*\\)/)\n ) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} uses 'this' context which cannot be serialized. ` +\n `Use a regular function or avoid 'this' references when forceKillOnTimeout is enabled.`,\n };\n }\n\n // 2. Check if handler string looks like it might have closures\n // This is a heuristic - we can't perfectly detect closures, but we can warn about common patterns\n if (handlerString.includes('[native code]')) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} contains native code which cannot be serialized. ` +\n `Ensure your handler is a plain function when forceKillOnTimeout is enabled.`,\n };\n }\n\n // 3. Try to create a function from the string to validate it's parseable\n // This will catch syntax errors early\n try {\n new Function('return ' + handlerString);\n } catch (parseError) {\n return {\n isSerializable: false,\n error:\n `Handler for ${typeLabel} cannot be serialized: ${parseError instanceof Error ? parseError.message : String(parseError)}. ` +\n `When using forceKillOnTimeout, handlers must be serializable functions without closures over external variables.`,\n };\n }\n\n // 4. Check for common closure patterns (heuristic)\n // Look for variable references that might be from outer scope\n // This is not perfect but can catch some common issues\n const hasPotentialClosure =\n /const\\s+\\w+\\s*=\\s*[^;]+;\\s*async\\s*\\(/.test(handlerString) ||\n /let\\s+\\w+\\s*=\\s*[^;]+;\\s*async\\s*\\(/.test(handlerString);\n\n if (hasPotentialClosure) {\n // This is just a warning, not a hard error, since we can't be 100% sure\n // The actual serialization will fail at runtime if there's a real issue\n return {\n isSerializable: true, // Still serializable, but might have issues\n error:\n `Warning: Handler for ${typeLabel} may have closures over external variables. ` +\n `Test thoroughly with forceKillOnTimeout enabled. If the handler fails to execute in a worker thread, ` +\n `ensure all dependencies are imported within the handler function.`,\n };\n }\n\n return { isSerializable: true };\n } catch (error) {\n return {\n isSerializable: false,\n error: `Failed to validate handler serialization${jobType ? ` for job type \"${jobType}\"` : ''}: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Test if a handler can be serialized and executed in a worker thread.\n * This is a more thorough check that actually attempts to serialize and deserialize the handler.\n *\n * @param handler - The job handler function to test\n * @param jobType - Optional job type name for better error messages\n * @returns Promise that resolves to validation result\n *\n * @example\n * ```ts\n * const handler = async (payload, signal) => {\n * await doSomething(payload);\n * };\n *\n * const result = await testHandlerSerialization(handler, 'myJob');\n * if (!result.isSerializable) {\n * console.error('Handler failed serialization test:', result.error);\n * }\n * ```\n */\nexport async function testHandlerSerialization<\n PayloadMap,\n T extends keyof PayloadMap & string,\n>(\n handler: JobHandler<PayloadMap, T>,\n jobType?: string,\n): Promise<{ isSerializable: boolean; error?: string }> {\n // First do the basic validation\n const basicValidation = validateHandlerSerializable(handler, jobType);\n if (!basicValidation.isSerializable) {\n return basicValidation;\n }\n\n // Then try to actually serialize and deserialize in a worker-like context\n try {\n const handlerString = handler.toString();\n const handlerFn = new Function('return ' + handlerString)();\n\n // Try to call it with dummy parameters to see if it executes\n // We use a very short timeout to avoid hanging\n const testPromise = handlerFn({}, new AbortController().signal);\n const timeoutPromise = new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Handler test timeout')), 100),\n );\n\n try {\n await Promise.race([testPromise, timeoutPromise]);\n } catch (execError) {\n // Execution errors are OK - we just want to know if it can be deserialized\n // The actual job execution will handle real errors\n if (\n execError instanceof Error &&\n execError.message === 'Handler test timeout'\n ) {\n // Handler is taking too long, but that's OK for serialization test\n return { isSerializable: true };\n }\n }\n\n return { isSerializable: true };\n } catch (error) {\n return {\n isSerializable: false,\n error: `Handler failed serialization test: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","import {\n createWaitpoint,\n completeWaitpoint,\n getWaitpoint,\n expireTimedOutWaitpoints,\n} from './queue.js';\nimport { createProcessor } from './processor.js';\nimport {\n JobQueueConfig,\n JobQueue,\n JobOptions,\n ProcessorOptions,\n JobHandlers,\n JobType,\n PostgresJobQueueConfig,\n RedisJobQueueConfig,\n CronScheduleOptions,\n CronScheduleStatus,\n EditCronScheduleOptions,\n} from './types.js';\nimport { QueueBackend, CronScheduleInput } from './backend.js';\nimport { setLogContext } from './log-context.js';\nimport { createPool } from './db-util.js';\nimport { PostgresBackend } from './backends/postgres.js';\nimport { RedisBackend } from './backends/redis.js';\nimport { getNextCronOccurrence, validateCronExpression } from './cron.js';\n\n/**\n * Initialize the job queue system.\n *\n * Defaults to PostgreSQL when `backend` is omitted.\n */\nexport const initJobQueue = <PayloadMap = any>(\n config: JobQueueConfig,\n): JobQueue<PayloadMap> => {\n const backendType = config.backend ?? 'postgres';\n setLogContext(config.verbose ?? false);\n\n let backend: QueueBackend;\n let pool: import('pg').Pool | undefined;\n\n if (backendType === 'postgres') {\n const pgConfig = config as PostgresJobQueueConfig;\n pool = createPool(pgConfig.databaseConfig);\n backend = new PostgresBackend(pool);\n } else if (backendType === 'redis') {\n const redisConfig = (config as RedisJobQueueConfig).redisConfig;\n // RedisBackend constructor will throw if ioredis is not installed\n backend = new RedisBackend(redisConfig);\n } else {\n throw new Error(`Unknown backend: ${backendType}`);\n }\n\n const requirePool = () => {\n if (!pool) {\n throw new Error(\n 'Wait/Token features require the PostgreSQL backend. Configure with backend: \"postgres\" to use these features.',\n );\n }\n return pool;\n };\n\n /**\n * Enqueue due cron jobs. Shared by the public API and the processor hook.\n */\n const enqueueDueCronJobsImpl = async (): Promise<number> => {\n const dueSchedules = await backend.getDueCronSchedules();\n let count = 0;\n\n for (const schedule of dueSchedules) {\n // Overlap check: skip if allowOverlap is false and last job is still active\n if (!schedule.allowOverlap && schedule.lastJobId !== null) {\n const lastJob = await backend.getJob(schedule.lastJobId);\n if (\n lastJob &&\n (lastJob.status === 'pending' ||\n lastJob.status === 'processing' ||\n lastJob.status === 'waiting')\n ) {\n // Still active — advance nextRunAt but don't enqueue\n const nextRunAt = getNextCronOccurrence(\n schedule.cronExpression,\n schedule.timezone,\n );\n await backend.updateCronScheduleAfterEnqueue(\n schedule.id,\n new Date(),\n schedule.lastJobId,\n nextRunAt,\n );\n continue;\n }\n }\n\n // Enqueue a new job instance\n const jobId = await backend.addJob<any, any>({\n jobType: schedule.jobType,\n payload: schedule.payload,\n maxAttempts: schedule.maxAttempts,\n priority: schedule.priority,\n timeoutMs: schedule.timeoutMs ?? undefined,\n forceKillOnTimeout: schedule.forceKillOnTimeout,\n tags: schedule.tags,\n });\n\n // Advance to next occurrence\n const nextRunAt = getNextCronOccurrence(\n schedule.cronExpression,\n schedule.timezone,\n );\n await backend.updateCronScheduleAfterEnqueue(\n schedule.id,\n new Date(),\n jobId,\n nextRunAt,\n );\n count++;\n }\n\n return count;\n };\n\n // Return the job queue API\n return {\n // Job queue operations\n addJob: withLogContext(\n (job: JobOptions<PayloadMap, any>) =>\n backend.addJob<PayloadMap, any>(job),\n config.verbose ?? false,\n ),\n getJob: withLogContext(\n (id: number) => backend.getJob<PayloadMap, any>(id),\n config.verbose ?? false,\n ),\n getJobsByStatus: withLogContext(\n (status: string, limit?: number, offset?: number) =>\n backend.getJobsByStatus<PayloadMap, any>(status, limit, offset),\n config.verbose ?? false,\n ),\n getAllJobs: withLogContext(\n (limit?: number, offset?: number) =>\n backend.getAllJobs<PayloadMap, any>(limit, offset),\n config.verbose ?? false,\n ),\n getJobs: withLogContext(\n (\n filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };\n },\n limit?: number,\n offset?: number,\n ) => backend.getJobs<PayloadMap, any>(filters, limit, offset),\n config.verbose ?? false,\n ),\n retryJob: (jobId: number) => backend.retryJob(jobId),\n cleanupOldJobs: (daysToKeep?: number) => backend.cleanupOldJobs(daysToKeep),\n cleanupOldJobEvents: (daysToKeep?: number) =>\n backend.cleanupOldJobEvents(daysToKeep),\n cancelJob: withLogContext(\n (jobId: number) => backend.cancelJob(jobId),\n config.verbose ?? false,\n ),\n editJob: withLogContext(\n <T extends JobType<PayloadMap>>(\n jobId: number,\n updates: import('./types.js').EditJobOptions<PayloadMap, T>,\n ) => backend.editJob(jobId, updates as import('./backend.js').JobUpdates),\n config.verbose ?? false,\n ),\n editAllPendingJobs: withLogContext(\n <T extends JobType<PayloadMap>>(\n filters:\n | {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: {\n values: string[];\n mode?: import('./types.js').TagQueryMode;\n };\n }\n | undefined,\n updates: import('./types.js').EditJobOptions<PayloadMap, T>,\n ) =>\n backend.editAllPendingJobs(\n filters,\n updates as import('./backend.js').JobUpdates,\n ),\n config.verbose ?? false,\n ),\n cancelAllUpcomingJobs: withLogContext(\n (filters?: {\n jobType?: string;\n priority?: number;\n runAt?:\n | Date\n | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };\n tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };\n }) => backend.cancelAllUpcomingJobs(filters),\n config.verbose ?? false,\n ),\n reclaimStuckJobs: withLogContext(\n (maxProcessingTimeMinutes?: number) =>\n backend.reclaimStuckJobs(maxProcessingTimeMinutes),\n config.verbose ?? false,\n ),\n getJobsByTags: withLogContext(\n (tags: string[], mode = 'all', limit?: number, offset?: number) =>\n backend.getJobsByTags<PayloadMap, any>(tags, mode, limit, offset),\n config.verbose ?? false,\n ),\n\n // Job processing — automatically enqueues due cron jobs before each batch\n createProcessor: (\n handlers: JobHandlers<PayloadMap>,\n options?: ProcessorOptions,\n ) =>\n createProcessor<PayloadMap>(backend, handlers, options, async () => {\n await enqueueDueCronJobsImpl();\n }),\n\n // Job events\n getJobEvents: withLogContext(\n (jobId: number) => backend.getJobEvents(jobId),\n config.verbose ?? false,\n ),\n\n // Wait / Token support (PostgreSQL-only for now)\n createToken: withLogContext(\n (options?: import('./types.js').CreateTokenOptions) =>\n createWaitpoint(requirePool(), null, options),\n config.verbose ?? false,\n ),\n completeToken: withLogContext(\n (tokenId: string, data?: any) =>\n completeWaitpoint(requirePool(), tokenId, data),\n config.verbose ?? false,\n ),\n getToken: withLogContext(\n (tokenId: string) => getWaitpoint(requirePool(), tokenId),\n config.verbose ?? false,\n ),\n expireTimedOutTokens: withLogContext(\n () => expireTimedOutWaitpoints(requirePool()),\n config.verbose ?? false,\n ),\n\n // Cron schedule operations\n addCronJob: withLogContext(\n <T extends JobType<PayloadMap>>(\n options: CronScheduleOptions<PayloadMap, T>,\n ) => {\n if (!validateCronExpression(options.cronExpression)) {\n return Promise.reject(\n new Error(`Invalid cron expression: \"${options.cronExpression}\"`),\n );\n }\n const nextRunAt = getNextCronOccurrence(\n options.cronExpression,\n options.timezone ?? 'UTC',\n );\n const input: CronScheduleInput = {\n scheduleName: options.scheduleName,\n cronExpression: options.cronExpression,\n jobType: options.jobType as string,\n payload: options.payload,\n maxAttempts: options.maxAttempts ?? 3,\n priority: options.priority ?? 0,\n timeoutMs: options.timeoutMs ?? null,\n forceKillOnTimeout: options.forceKillOnTimeout ?? false,\n tags: options.tags,\n timezone: options.timezone ?? 'UTC',\n allowOverlap: options.allowOverlap ?? false,\n nextRunAt,\n };\n return backend.addCronSchedule(input);\n },\n config.verbose ?? false,\n ),\n getCronJob: withLogContext(\n (id: number) => backend.getCronSchedule(id),\n config.verbose ?? false,\n ),\n getCronJobByName: withLogContext(\n (name: string) => backend.getCronScheduleByName(name),\n config.verbose ?? false,\n ),\n listCronJobs: withLogContext(\n (status?: CronScheduleStatus) => backend.listCronSchedules(status),\n config.verbose ?? false,\n ),\n removeCronJob: withLogContext(\n (id: number) => backend.removeCronSchedule(id),\n config.verbose ?? false,\n ),\n pauseCronJob: withLogContext(\n (id: number) => backend.pauseCronSchedule(id),\n config.verbose ?? false,\n ),\n resumeCronJob: withLogContext(\n (id: number) => backend.resumeCronSchedule(id),\n config.verbose ?? false,\n ),\n editCronJob: withLogContext(\n async (id: number, updates: EditCronScheduleOptions) => {\n if (\n updates.cronExpression !== undefined &&\n !validateCronExpression(updates.cronExpression)\n ) {\n throw new Error(\n `Invalid cron expression: \"${updates.cronExpression}\"`,\n );\n }\n let nextRunAt: Date | null | undefined;\n if (\n updates.cronExpression !== undefined ||\n updates.timezone !== undefined\n ) {\n const existing = await backend.getCronSchedule(id);\n const expr = updates.cronExpression ?? existing?.cronExpression ?? '';\n const tz = updates.timezone ?? existing?.timezone ?? 'UTC';\n nextRunAt = getNextCronOccurrence(expr, tz);\n }\n await backend.editCronSchedule(id, updates, nextRunAt);\n },\n config.verbose ?? false,\n ),\n enqueueDueCronJobs: withLogContext(\n () => enqueueDueCronJobsImpl(),\n config.verbose ?? false,\n ),\n\n // Advanced access\n getPool: () => {\n if (backendType !== 'postgres') {\n throw new Error(\n 'getPool() is only available with the PostgreSQL backend.',\n );\n }\n return (backend as PostgresBackend).getPool();\n },\n getRedisClient: () => {\n if (backendType !== 'redis') {\n throw new Error(\n 'getRedisClient() is only available with the Redis backend.',\n );\n }\n return (backend as RedisBackend).getClient();\n },\n };\n};\n\nconst withLogContext =\n <T>(fn: (...args: any[]) => T, verbose: boolean) =>\n (...args: Parameters<typeof fn>): ReturnType<typeof fn> => {\n setLogContext(verbose);\n return fn(...args);\n };\n\nexport * from './types.js';\nexport { QueueBackend, CronScheduleInput } from './backend.js';\nexport { PostgresBackend } from './backends/postgres.js';\nexport {\n validateHandlerSerializable,\n testHandlerSerialization,\n} from './handler-validation.js';\nexport { getNextCronOccurrence, validateCronExpression } from './cron.js';\n"]}