@marktiderman/genesis-notifications 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +251 -2
- package/dist/blocks.d.ts +10 -0
- package/dist/blocks.js +35 -0
- package/dist/blocks.js.map +1 -0
- package/dist/chunk-2BG336II.js +124 -0
- package/dist/chunk-2BG336II.js.map +1 -0
- package/dist/chunk-7PHA3WVG.js +51 -0
- package/dist/chunk-7PHA3WVG.js.map +1 -0
- package/dist/chunk-EWZB35V6.js +113 -0
- package/dist/chunk-EWZB35V6.js.map +1 -0
- package/dist/chunk-UXNWI7OS.js +118 -0
- package/dist/chunk-UXNWI7OS.js.map +1 -0
- package/dist/email-render-CID_pXDy.d.ts +53 -0
- package/dist/email.d.ts +46 -0
- package/dist/email.js +64 -0
- package/dist/email.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +151 -0
- package/dist/index.js.map +1 -0
- package/dist/native.d.ts +92 -0
- package/dist/native.js +170 -0
- package/dist/native.js.map +1 -0
- package/dist/react.d.ts +98 -0
- package/dist/react.js +4 -0
- package/dist/react.js.map +1 -0
- package/dist/runner.d.ts +174 -0
- package/dist/runner.js +3 -0
- package/dist/runner.js.map +1 -0
- package/dist/server.d.ts +658 -0
- package/dist/server.js +995 -0
- package/dist/server.js.map +1 -0
- package/dist/supabase.d.ts +118 -0
- package/dist/supabase.js +3 -0
- package/dist/supabase.js.map +1 -0
- package/dist/types-BilguCPW.d.ts +332 -0
- package/dist/web.d.ts +79 -0
- package/dist/web.js +233 -0
- package/dist/web.js.map +1 -0
- package/functions/genesis-notifications/index.ts +60 -0
- package/functions/genesis-notifications/notifications.ts +18 -0
- package/migrations/0001_genesis_notifications.sql +2057 -0
- package/migrations/tests/0001_genesis_notifications.test.sql +473 -0
- package/package.json +191 -3
- package/src/blocks.ts +42 -0
- package/src/core/index.ts +43 -0
- package/src/core/registry.ts +354 -0
- package/src/core/types.ts +90 -0
- package/src/core/validate.ts +73 -0
- package/src/email/index.tsx +142 -0
- package/src/index.ts +6 -0
- package/src/native/index.tsx +283 -0
- package/src/react/index.tsx +226 -0
- package/src/runner/index.ts +299 -0
- package/src/server/crypto.ts +44 -0
- package/src/server/dns.ts +106 -0
- package/src/server/email-render.ts +90 -0
- package/src/server/errors.ts +61 -0
- package/src/server/expo.ts +133 -0
- package/src/server/handler.ts +127 -0
- package/src/server/index.ts +57 -0
- package/src/server/notify.ts +100 -0
- package/src/server/rate.ts +47 -0
- package/src/server/resend.ts +143 -0
- package/src/server/runner.ts +524 -0
- package/src/server/secret-links.ts +53 -0
- package/src/server/store.ts +150 -0
- package/src/server/unsubscribe.ts +78 -0
- package/src/server/webhook.ts +55 -0
- package/src/supabase/index.ts +210 -0
- package/src/web/index.tsx +428 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/store.ts","../src/server/notify.ts","../src/server/email-render.ts","../src/server/errors.ts","../src/server/dns.ts","../src/server/expo.ts","../src/server/secret-links.ts","../src/server/crypto.ts","../src/server/unsubscribe.ts","../src/server/runner.ts","../src/server/webhook.ts","../src/server/handler.ts","../src/server/rate.ts","../src/server/resend.ts"],"names":["emailHash","json"],"mappings":";;;;AAiBO,IAAM,sBAAA,GAAN,cAAqC,KAAA,CAAM;AAAA,EAChD,WAAA,CACW,WACA,IAAA,EACT;AACA,IAAA,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,OAAA,EAAU,IAAA,GAAO,KAAK,IAAI,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAE,CAAA;AAH7C,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAGT,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AAAA,EACd;AAAA,EALW,SAAA;AAAA,EACA,IAAA;AAKb;AA+FO,SAAS,0BAA0B,MAAA,EAAsC;AAC9E,EAAA,MAAM,IAAA,GAAO,OAAU,EAAA,EAAY,IAAA,KAA8C;AAC/E,IAAA,MAAM,EAAE,MAAM,KAAA,EAAM,GAAI,MAAM,MAAA,CAAO,GAAA,CAAI,IAAI,IAAI,CAAA;AACjD,IAAA,IAAI,OAAO,MAAM,IAAI,uBAAuB,EAAA,EAAI,KAAA,CAAM,QAAQ,IAAI,CAAA;AAClE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,CAAC,OAAA,EAAS,KAAA,EAAO,YAAA,EAAc,cAAc,CAAA,KAClD,IAAA,CAAmB,6BAAA,EAA+B,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,KAAA,EAAO,eAAA,EAAiB,YAAA,EAAc,cAAA,EAAgB,WAAA,EAAa,CAAA,CAAE,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,IAAQ,EAAE,CAAA;AAAA,IACjL,MAAM,aAAA,CAAc,EAAA,EAAI,OAAA,EAAS,UAAU,IAAA,EAAM;AAC/C,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAyE,sCAAA,EAAwC,EAAE,IAAA,EAAM,EAAA,EAAI,OAAA,EAAS,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,MAAA,EAAQ,MAAM,CAAA;AACtM,MAAA,MAAM,GAAA,GAAM,OAAO,CAAC,CAAA;AACpB,MAAA,OAAO,GAAA,EAAK,QAAA,IAAY,GAAA,CAAI,aAAA,GAAgB,EAAE,QAAA,EAAU,GAAA,CAAI,QAAA,EAAU,IAAA,EAAM,GAAA,CAAI,aAAA,EAAc,GAAI,IAAA;AAAA,IACpG,CAAA;AAAA,IACA,QAAQ,CAAC,EAAA,EAAI,OAAA,EAAS,OAAA,KAAY,KAAc,8BAAA,EAAgC,EAAE,IAAA,EAAM,EAAA,EAAI,SAAS,OAAA,EAAS,SAAA,EAAW,SAAS,CAAA,CAAE,KAAK,OAAO,CAAA;AAAA,IAChJ,kBAAkB,CAAC,EAAA,EAAI,OAAA,EAAS,UAAA,KAC9B,KAAc,0CAAA,EAA4C,EAAE,IAAA,EAAM,EAAA,EAAI,SAAS,OAAA,EAAS,aAAA,EAAe,YAAY,CAAA,CAAE,KAAK,OAAO,CAAA;AAAA,IACnI,MAAM,WAAA,CAAY,UAAA,EAAY,OAAA,EAAS;AACrC,MAAA,MAAM,KAAK,oCAAA,EAAsC,EAAE,YAAY,UAAA,EAAY,SAAA,EAAW,SAAS,CAAA;AAAA,IACjG,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,KAAA,KAAU,IAAA,CAA0C,wCAAwC,EAAE,OAAA,EAAS,KAAA,EAAO,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,IAAK,EAAE,CAAA;AAAA,IACnJ,MAAM,eAAe,OAAA,EAAS;AAC5B,MAAA,MAAM,IAAA,CAAK,uCAAA,EAAyC,EAAE,SAAA,EAAW,SAAS,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,YAAY,CAAC,UAAA,EAAY,KAAA,EAAO,SAAA,KAAc,KAAc,mCAAA,EAAqC,EAAE,aAAA,EAAe,UAAA,EAAY,SAAS,KAAA,EAAO,WAAA,EAAa,WAAW,CAAA,CAAE,KAAK,OAAO,CAAA;AAAA,IACpL,WAAA,EAAa,CAAC,MAAA,EAAQA,UAAAA,EAAW,UAAU,QAAA,KACzC,IAAA,CAAc,qCAAqC,EAAE,SAAA,EAAW,QAAQ,YAAA,EAAcA,UAAAA,EAAW,YAAY,QAAA,EAAU,WAAA,EAAa,UAAU,CAAA,CAAE,KAAK,OAAO,CAAA;AAAA,IAC9J,SAAA,EAAW,CAAC,IAAA,KAAS,IAAA,CAAa,oCAAoC,EAAE,MAAA,EAAQ,MAAM;AAAA,GACxF;AACF;;;AC7HA,eAAsB,MAAA,CAAyB,MAAA,EAAmB,KAAA,EAAuB,OAAA,GAAyB,EAAC,EAA4B;AAC7I,EAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,QAAA,EAAU,IAAA,CAAK,MAAM,IAAI,CAAA;AAC3C,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,IAAa,CAAA,IAAK,CAAC,CAAA,CAAE,UAAA,EAAY,MAAM,IAAI,KAAA,CAAM,CAAA,aAAA,EAAgB,KAAA,CAAM,IAAI,CAAA,uBAAA,CAAyB,CAAA;AAEzH,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,IAAa,KAAA,CAAM,UAAA,CAAW,WAAW,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,gDAAgD,CAAA;AACjI,EAAA,IAAI,CAAC,KAAA,CAAM,cAAA,IAAkB,KAAA,CAAM,cAAA,CAAe,SAAS,GAAA,EAAK,MAAM,IAAI,KAAA,CAAM,+DAA+D,CAAA;AAC/I,EAAA,eAAA,CAAgB,MAAM,IAAI,CAAA;AAC1B,EAAA,IAAI,KAAA,CAAM,KAAA,KAAU,MAAA,KAAc,CAAC,OAAO,SAAA,CAAU,KAAA,CAAM,KAAK,CAAA,IAAK,MAAM,KAAA,GAAQ,CAAA,CAAA,EAAI,MAAM,IAAI,MAAM,4CAA4C,CAAA;AAClJ,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,KAAU,MAAM,MAAA,CAAO,IAAI,gBAAA,EAAkB;AAAA,IACzD,QAAQ,KAAA,CAAM,IAAA;AAAA,IACd,YAAA,EAAc,gBAAA,CAAiB,KAAA,CAAM,UAAU,CAAA;AAAA,IAC/C,MAAA,EAAQ,KAAA,CAAM,IAAA,IAAQ,EAAC;AAAA,IACvB,mBAAmB,KAAA,CAAM,cAAA;AAAA,IACzB,YAAA,EAAc,MAAM,SAAA,IAAa,IAAA;AAAA,IACjC,cAAA,EAAgB,MAAM,WAAA,IAAe,IAAA;AAAA,IACrC,OAAA,EAAS,MAAM,KAAA,IAAS,IAAA;AAAA,IACxB,WAAA,EAAa,MAAM,QAAA,IAAY,IAAA;AAAA,IAC/B,OAAA,EAAS,MAAM,KAAA,IAAS,IAAA;AAAA,IACxB,QAAA,EAAU,MAAM,MAAA,IAAU;AAAA,GAC3B,CAAA;AACD,EAAA,IAAI,OAAO,MAAM,IAAI,uBAAuB,gBAAA,EAAkB,KAAA,CAAM,QAAQ,IAAI,CAAA;AAChF,EAAA,OAAA,CAAS,IAAA,IAA0F,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACjH,gBAAgB,CAAA,CAAE,eAAA;AAAA,IAClB,cAAc,CAAA,CAAE,aAAA;AAAA,IAChB,SAAS,CAAA,CAAE;AAAA,GACb,CAAE,CAAA;AACJ;AAwBA,eAAsB,kBAAA,CAAmB,QAAmB,KAAA,EAAsE;AAChI,EAAA,IAAI,CAAC,KAAA,CAAM,IAAA,EAAM,MAAM,IAAI,MAAM,wCAAwC,CAAA;AACzE,EAAA,IAAI,CAAC,KAAA,CAAM,cAAA,IAAkB,KAAA,CAAM,cAAA,CAAe,SAAS,GAAA,EAAK,MAAM,IAAI,KAAA,CAAM,2EAA2E,CAAA;AAC3J,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,KAAU,MAAM,MAAA,CAAO,IAAI,8BAAA,EAAgC;AAAA,IACvE,QAAQ,KAAA,CAAM,IAAA;AAAA,IACd,mBAAmB,KAAA,CAAM,cAAA;AAAA,IACzB,WAAA,EAAa,MAAM,QAAA,IAAY,IAAA;AAAA,IAC/B,cAAc,KAAA,CAAM,UAAA,GAAa,gBAAA,CAAiB,KAAA,CAAM,UAAU,CAAA,GAAI,IAAA;AAAA,IACtE,SAAA,EAAW,MAAM,OAAA,IAAW;AAAA,GAC7B,CAAA;AACD,EAAA,IAAI,OAAO,MAAM,IAAI,uBAAuB,8BAAA,EAAgC,KAAA,CAAM,QAAQ,IAAI,CAAA;AAC9F,EAAA,MAAM,MAAO,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA,GAAI,IAAA;AAC7C,EAAA,OAAO,EAAE,SAAA,EAAW,MAAA,CAAO,GAAA,EAAK,SAAA,IAAa,CAAC,CAAA,EAAG,QAAA,EAAU,MAAA,CAAO,GAAA,EAAK,QAAA,IAAY,CAAC,CAAA,EAAE;AACxF;AAQA,eAAsB,qBAAA,CAAsB,QAAmB,QAAA,EAAiD;AAC9G,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,MAAA,CAAO,GAAA,CAAI,kCAAA,EAAoC,EAAE,MAAA,EAAQ,QAAA,CAAS,UAAA,IAAc,CAAA;AAC9G,EAAA,IAAI,OAAO,MAAM,IAAI,uBAAuB,kCAAA,EAAoC,KAAA,CAAM,QAAQ,IAAI,CAAA;AAClG,EAAA,OAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AACzB;;;AC3DO,SAAS,WAAW,IAAA,EAAsB;AAC/C,EAAA,OAAO,KAAK,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,OAAA,CAAQ,MAAM,MAAM,CAAA,CAAE,QAAQ,IAAA,EAAM,MAAM,EAAE,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CAAE,OAAA,CAAQ,MAAM,OAAO,CAAA;AAC9H;AAGO,SAAS,UAAA,CAAW,IAAA,EAAc,GAAA,GAAM,GAAA,EAAa;AAE1D,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,qCAAA,EAAuC,GAAG,EAAE,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,IAAA,EAAK;AAChG,EAAA,OAAO,IAAA,CAAK,MAAA,GAAS,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,EAAG,GAAA,GAAM,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,IAAA;AAC5D;AAGO,SAAS,cAAc,KAAA,EAAiC;AAE7D,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,IAAU,GAAA,IAAO,mEAAA,CAAoE,IAAA,CAAK,KAAK,CAAA,IAAK,CAAC,uBAAA,CAAwB,KAAK,KAAK,CAAA;AACnL;AAGO,SAAS,YAAY,IAAA,EAAsB;AAChD,EAAA,OAAO,WAAW,IAAA,CAAK,OAAA,CAAQ,mBAAA,EAAqB,EAAE,GAAG,GAAG,CAAA;AAC9D;AAGO,IAAM,oBAAA,GAAsC,CAAC,GAAA,KAAQ;AAC1D,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,KAAA;AACvB,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,IAAA,CAAK,IAAA,IAAQ,EAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,UAAA,IAAc,GAAA,CAAI,GAAA;AACnC,EAAA,MAAM,MAAA,GAAS,OAAO,GAAA,CAAI,KAAA,CAAM,UAAU,QAAA,IAAY,qBAAA,CAAsB,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA,GAAI,GAAA,CAAI,MAAM,KAAA,GAAQ,SAAA;AACtH,EAAA,MAAM,IAAA,GAAO,IAAI,KAAA,CAAM,IAAA,GAAO,WAAW,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,EAAA;AAC3D,EAAA,MAAM,CAAA,GAAI,qFAAA;AACV,EAAA,MAAM,IAAA,GACJ,+KAEC,IAAA,GAAO,CAAA,UAAA,EAAa,CAAC,CAAA,uBAAA,EAA0B,MAAM,CAAA,EAAA,EAAK,IAAI,CAAA,IAAA,CAAA,GAAS,EAAA,CAAA,GACxE,aAAa,CAAC,CAAA,iCAAA,EAAoC,WAAW,KAAK,CAAC,UAClE,IAAA,GAAO,CAAA,UAAA,EAAa,CAAC,CAAA,uBAAA,EAA0B,UAAA,CAAW,IAAI,CAAC,CAAA,IAAA,CAAA,GAAS,EAAA,CAAA,IACxE,OAAO,CAAA,UAAA,EAAa,CAAC,cAAc,UAAA,CAAW,IAAI,CAAC,CAAA,yCAAA,EAA4C,MAAM,CAAA,qFAAA,CAAA,GAA0F,OAC/L,GAAA,CAAI,cAAA,GAAiB,aAAa,CAAC,CAAA,wCAAA,EAA2C,WAAW,GAAA,CAAI,cAAc,CAAC,CAAA,8DAAA,CAAA,GAAmE,EAAA,CAAA,GAChL,CAAA,oBAAA,CAAA;AACF,EAAA,MAAM,OAAO,CAAC,IAAA,EAAM,OAAO,IAAA,EAAM,IAAA,IAAQ,IAAI,GAAA,CAAI,cAAA,GAAiB,gBAAgB,GAAA,CAAI,cAAc,KAAK,EAAE,CAAA,CAAE,OAAO,OAAO,CAAA,CAAE,KAAK,MAAM,CAAA;AACxI,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,IAAA,EAAM,IAAA,EAAK;AACtC;AAGO,SAAS,cAAc,KAAA,EAAwB;AACpD,EAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,UAAU,OAAO,IAAA,CAAK,UAAU,KAAK,CAAA;AAC5E,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,KAAA,CAAM,GAAA,CAAI,aAAa,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAA;AACvE,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAgC,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,CAAC,CAAA,KAAM,CAAA,KAAM,MAAS,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA,KAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAE,CAAA;AACnJ,EAAA,OAAO,CAAA,CAAA,EAAI,QAAQ,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,MAAM,CAAA,EAAG,IAAA,CAAK,UAAU,CAAC,CAAC,IAAI,aAAA,CAAc,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAA;AAC1F;;;ACpFO,IAAM,SAAA,GAAN,cAAwB,KAAA,CAAM;AAAA,EAC1B,IAAA,GAAO,WAAA;AAAA,EACP,SAAA;AAAA,EACA,iBAAA;AAAA,EACA,MAAA;AAAA,EACT,WAAA,CAAY,OAAA,EAAiB,OAAA,GAAgF,EAAC,EAAG;AAC/G,IAAA,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA;AAC3B,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,KAAA;AACtC,IAAA,IAAA,CAAK,oBAAoB,OAAA,CAAQ,iBAAA;AACjC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AAAA,EACxB;AACF;AAGO,SAAS,kBAAkB,MAAA,EAAyB;AACzD,EAAA,OAAO,WAAW,GAAA,IAAO,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,OAAO,MAAA,IAAU,GAAA;AACzE;AAGO,SAAS,kBAAkB,MAAA,EAAmC,QAAA,GAAW,IAAI,GAAA,GAAM,IAAA,CAAK,KAAI,EAAW;AAC5G,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAc,IAAA,CAAK,GAAA,CAAI,OAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAA;AAC5D,EAAA,MAAM,KAAA,GAAQ,QAAQ,IAAA,EAAK;AAC3B,EAAA,IAAI,KAAA,IAAS,QAAQ,IAAA,CAAK,KAAK,GAAG,OAAO,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAC5D,EAAA,IAAI,KAAA,IAAS,kFAAA,CAAmF,IAAA,CAAK,KAAK,CAAA,EAAG;AAC3G,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC3B,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,EAAG,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAA,CAAM,EAAA,GAAK,GAAA,IAAO,GAAI,CAAC,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,QAAA;AACT;AAGO,IAAM,kBAAA,GAAqB;AASlC,eAAsB,UAAA,CAAW,IAAA,EAAgB,GAAA,EAAa,IAAA,EAAmB,MAAA,EAAyC;AACxH,EAAA,MAAM,CAAA,GAAI,KAAK,KAAA,IAAS,KAAA;AACxB,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,IAAA,CAAK,aAAa,kBAAkB,CAAA;AACxE,EAAA,MAAM,QAAA,GAAW,SAAS,WAAA,CAAY,GAAA,CAAI,CAAC,MAAA,EAAQ,OAAO,CAAC,CAAA,GAAI,OAAA;AAC/D,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,EAAE,GAAA,EAAK,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,UAAU,CAAA;AAAA,EACnD,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAQ,KAAA,EAAoC,IAAA;AAClD,IAAA,MAAM,IAAI,SAAA,CAAU,IAAA,KAAS,cAAA,IAAkB,IAAA,KAAS,YAAA,GAAe,WAAA,GAAc,eAAA,EAAiB,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,EAC3H;AACF;AAGO,SAAS,QAAQ,QAAA,EAA0B;AAChD,EAAA,QAAA,CAAS,IAAA,EAAM,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,EAAC,CAAC,CAAA;AACxC;;;ACvBA,IAAM,MAAA,GAAS,sDAAA;AAGR,IAAM,qBAAA,GAAwB;AAQ9B,SAAS,qBAAA,CAAsB,QAAgB,KAAA,EAA6D;AACjH,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA,EAAG,qBAAqB,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,KAAA,EAAO,CAAA,6BAAA,EAAgC,KAAK,CAAA,CAAA,EAAG;AACnH;AAGO,SAAS,gBAAA,CAAiB,QAAgB,QAAA,EAA2D;AAC1G,EAAA,MAAM,IAAI,MAAA,CAAO,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAChD,EAAA,OAAO,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM;AAC1B,IAAA,MAAM,IAAI,CAAA,EAAG,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC5C,IAAA,OAAO,CAAC,CAAC,CAAA,KAAM,CAAA,KAAM,KAAK,CAAA,CAAE,QAAA,CAAS,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAAA;AAAA,EAC9C,CAAC,CAAA;AACH;AAEA,eAAe,GAAA,CAAI,SAA6B,IAAA,EAAiC;AAC/E,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,YAAY,sCAAsC,CAAA;AAC9E,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AACjC,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,OAAA,EAAS,GAAA,CAAI,QAAA,EAAS,EAAG,EAAE,OAAA,EAAS,EAAE,MAAA,EAAQ,sBAAA,IAA0B,CAAA;AAC1G,EAAA,IAAI,CAAC,QAAA,CAAS,EAAA,EAAI,OAAO,EAAC;AAC1B,EAAA,MAAMC,KAAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,EAAA,OAAA,CAAQA,KAAAA,CAAK,MAAA,IAAU,EAAC,EACrB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,EAAA,IAAM,OAAO,CAAA,CAAE,IAAA,KAAS,QAAQ,CAAA,CAEzD,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,CAAM,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA,CAAE,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAC,CAAA;AACnE;AASA,eAAsB,iBAAA,CAAkB,QAAgB,OAAA,EAAmD;AACzG,EAAA,IAAI,CAAC,OAAO,IAAA,CAAK,MAAM,GAAG,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAChF,EAAA,IAAI,OAAO,OAAA,EAAS,KAAA,KAAU,QAAA,IAAY,CAAC,gBAAA,CAAiB,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA,EAAG,MAAM,IAAI,MAAM,uDAAuD,CAAA;AACxJ,EAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,eAAe,CAAA,IAAK,OAAA,CAAQ,eAAA,CAAgB,MAAA,KAAW,CAAA,EAAG;AACnF,IAAA,MAAM,IAAI,MAAM,mFAAmF,CAAA;AAAA,EACrG;AACA,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,MAAA,EAAQ,OAAA,CAAQ,eAAe,CAAA;AACjE,EAAA,IAAI,QAAA,EAAU,OAAO,EAAE,SAAA,EAAW,OAAO,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,QAAA,EAAU,IAAA,EAAM,IAAI,KAAA,EAAM;AAC1G,EAAA,MAAM,OAAA,GAAU,QAAQ,UAAA,IAAc,eAAA;AACtC,EAAA,MAAM,KAAA,GAAQ,qBAAA,CAAsB,MAAA,EAAQ,OAAA,CAAQ,KAAK,CAAA;AACzD,EAAA,MAAM,CAAC,OAAO,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IAClD,GAAA,CAAI,OAAA,EAAS,KAAA,CAAM,IAAI,CAAA;AAAA,IACvB,IAAI,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAE,CAAA;AAAA,IAChD,GAAA,CAAI,SAAS,CAAA,EAAG,OAAA,CAAQ,gBAAgB,QAAQ,CAAA,YAAA,EAAe,MAAM,CAAA,CAAE,CAAA;AAAA,IACvE,GAAA,CAAI,OAAA,EAAS,CAAA,OAAA,EAAU,MAAM,CAAA,CAAE;AAAA,GAChC,CAAA;AACD,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,SAAA,EAAW,MAAM,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,IAAA,EAAK,KAAM,KAAA,CAAM,KAAK,CAAA;AAAA,IACrD,KAAK,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA,KAAM,aAAa,IAAA,CAAK,CAAC,CAAA,IAAK,CAAA,CAAE,aAAY,CAAE,QAAA,CAAS,CAAA,QAAA,EAAW,OAAO,EAAE,CAAC,CAAA;AAAA,IAC3F,IAAA,EAAM,KAAK,IAAA,CAAK,CAAC,MAAM,+BAAA,CAAgC,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,IAC9D,KAAA,EAAO,MAAM,IAAA,CAAK,CAAC,MAAM,cAAA,CAAe,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,IAC/C,QAAA,EAAU;AAAA,GACZ;AACA,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,EAAA,EAAI,MAAA,CAAO,SAAA,IAAa,MAAA,CAAO,GAAA,IAAO,MAAA,CAAO,IAAA,IAAQ,MAAA,CAAO,KAAA,EAAM;AACxF;;;AC9FO,IAAM,aAAA,GAAgB;AACtB,IAAM,iBAAA,GAAoB;AAE1B,IAAM,eAAA,GAAkB;AAExB,IAAM,mBAAA,GAAsB;AAgD5B,SAAS,cAAc,KAAA,EAA8D;AAC1F,EAAA,MAAM,IAAA,GAAO,OAAO,OAAA,EAAS,KAAA;AAC7B,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,IAAY,oBAAoB,IAAA,CAAK,IAAI,IAAI,IAAA,GAAO,SAAA;AAC7E;AAGO,SAAS,gBAAgB,KAAA,EAAyB;AACvD,EAAA,OAAO,KAAA,YAAiB,SAAA,IAAa,KAAA,CAAM,MAAA,KAAW,MAAA,IAAa,KAAA,CAAM,MAAA,IAAU,GAAA,IAAO,KAAA,CAAM,MAAA,GAAS,GAAA,IAAO,KAAA,CAAM,MAAA,KAAW,GAAA;AACnI;AAOO,SAAS,gBAAA,CAAiB,OAAA,GAAuB,EAAC,EAAiB;AACxE,EAAA,MAAM,OAAA,GAAU;AAAA,IACd,MAAA,EAAQ,kBAAA;AAAA,IACR,cAAA,EAAgB,kBAAA;AAAA,IAChB,GAAI,OAAA,CAAQ,WAAA,GAAc,EAAE,aAAA,EAAe,UAAU,OAAA,CAAQ,WAAW,CAAA,CAAA,EAAG,GAAI;AAAC,GAClF;AACA,EAAA,eAAe,IAAA,CAAK,GAAA,EAAa,IAAA,EAAe,MAAA,EAAwC;AACtF,IAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,OAAA,EAAS,KAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,MAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,IAAK,MAAM,CAAA;AAC/G,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAEhB,MAAA,MAAM,UAAA,GAAa,QAAA,CAAS,MAAA,KAAW,GAAA,GAAM,iBAAA,CAAkB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA,EAAG,EAAE,CAAA,GAAI,MAAA;AAC1G,MAAA,OAAA,CAAQ,QAAQ,CAAA;AAChB,MAAA,MAAM,IAAI,SAAA,CAAU,CAAA,wBAAA,EAA2B,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI;AAAA,QAChE,SAAA,EAAW,iBAAA,CAAkB,QAAA,CAAS,MAAM,CAAA;AAAA,QAC5C,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,GAAI,UAAA,KAAe,MAAA,GAAY,EAAE,iBAAA,EAAmB,UAAA,KAAe;AAAC,OACrE,CAAA;AAAA,IACH;AACA,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,SAAA,CAAU,mDAAA,EAAqD,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,IAC9F;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA;AAAA,IACN,MAAM,IAAA,CAAK,QAAA,EAAU,EAAE,MAAA,EAAO,GAAI,EAAC,EAAG;AACpC,MAAA,IAAI,QAAA,CAAS,SAAS,eAAA,EAAiB,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,eAAe,CAAA,SAAA,CAAW,CAAA;AAC9G,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AACnC,MAAA,MAAMA,QAAQ,MAAM,IAAA,CAAK,QAAQ,OAAA,IAAW,aAAA,EAAe,UAAU,MAAM,CAAA;AAC3E,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQA,KAAAA,EAAM,IAAI,CAAA,GAAKA,KAAAA,CAAK,OAA8E,EAAC;AAEjI,MAAA,OAAO,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAkB;AACxC,QAAA,MAAM,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnB,QAAA,IAAI,CAAA,EAAG,WAAW,IAAA,IAAQ,OAAO,EAAE,EAAA,KAAO,QAAA,IAAY,EAAE,EAAA,CAAG,MAAA,GAAS,KAAK,CAAA,CAAE,EAAA,CAAG,UAAU,GAAA,EAAK,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,EAAA,EAAI,CAAA,CAAE,EAAA,EAAG;AAC7H,QAAA,IAAI,CAAA,EAAG,MAAA,KAAW,OAAA,EAAS,OAAO,EAAE,QAAQ,OAAA,EAAS,IAAA,EAAM,aAAA,CAAc,CAAC,CAAA,EAAE;AAC5E,QAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,UAAA,EAAW;AAAA,MAC7C,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,MAAM,QAAA,CAAS,GAAA,EAAK,EAAE,MAAA,EAAO,GAAI,EAAC,EAAG;AACnC,MAAA,IAAI,GAAA,CAAI,SAAS,mBAAA,EAAqB,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,mBAAmB,CAAA,IAAA,CAAM,CAAA;AAC5G,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC9B,MAAA,MAAMA,KAAAA,GAAQ,MAAM,IAAA,CAAK,OAAA,CAAQ,eAAe,iBAAA,EAAmB,EAAE,GAAA,EAAI,EAAG,MAAM,CAAA;AAClF,MAAA,MAAM,IAAA,GAAOA,KAAAA,EAAM,IAAA,IAAQ,OAAOA,MAAK,IAAA,KAAS,QAAA,IAAY,CAAC,KAAA,CAAM,QAAQA,KAAAA,CAAK,IAAI,CAAA,GAAKA,KAAAA,CAAK,OAA8E,EAAC;AAC7K,MAAA,MAAM,MAAmC,EAAC;AAC1C,MAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,QAAA,MAAM,CAAA,GAAI,KAAK,EAAE,CAAA;AACjB,QAAA,IAAI,CAAA,EAAG,WAAW,IAAA,EAAM,GAAA,CAAI,EAAE,CAAA,GAAI,EAAE,QAAQ,IAAA,EAAK;AAAA,aAAA,IACxC,CAAA,EAAG,MAAA,KAAW,OAAA,EAAS,GAAA,CAAI,EAAE,CAAA,GAAI,EAAE,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,aAAA,CAAc,CAAC,CAAA,EAAE;AAAA,MACtF;AACA,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACF;AACF;;;AC9GO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACzC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AASA,eAAsB,iBAAA,CAAkB,KAAA,EAAoB,IAAA,EAAc,GAAA,EAAqC;AAC7G,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,OAAO,IAAI,CAAA,GAAI,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAChF,EAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,eAAA,CAAgB,CAAA,gCAAA,EAAmC,IAAI,CAAA,CAAE,CAAA;AAC/E,EAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,IAAI,IAAI,IAAI,CAAA;AAAA,EACpB,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,eAAA,CAAgB,CAAA,yBAAA,EAA4B,IAAI,CAAA,aAAA,CAAe,CAAA;AAAA,EAC3E;AACA,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,QAAA,KAAa,WAAA,IAAe,IAAI,QAAA,KAAa,WAAA;AAC/D,EAAA,IAAI,GAAA,CAAI,QAAA,KAAa,QAAA,IAAY,EAAE,KAAA,IAAS,GAAA,CAAI,QAAA,KAAa,OAAA,CAAA,EAAU,MAAM,IAAI,eAAA,CAAgB,CAAA,yBAAA,EAA4B,IAAI,CAAA,aAAA,CAAe,CAAA;AAChJ,EAAA,OAAO,IAAI,QAAA,EAAS;AACtB;AAGO,IAAM,uBAAA,GAA0B;;;ACnDvC,IAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEzB,SAAS,UAAU,KAAA,EAA2B;AACnD,EAAA,IAAI,CAAA,GAAI,EAAA;AACR,EAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,CAAA,IAAK,MAAA,CAAO,aAAa,CAAC,CAAA;AACjD,EAAA,OAAO,IAAA,CAAK,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC1E;AAEO,SAAS,cAAc,IAAA,EAA0B;AACtD,EAAA,MAAM,CAAA,GAAI,KAAK,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AACnD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,CAAA,GAAI,GAAA,CAAI,MAAA,CAAA,CAAQ,IAAK,CAAA,CAAE,MAAA,GAAS,CAAA,IAAM,CAAC,CAAC,CAAA;AACzD,EAAA,OAAO,UAAA,CAAW,KAAK,GAAA,EAAK,CAAC,MAAM,CAAA,CAAE,UAAA,CAAW,CAAC,CAAC,CAAA;AACpD;AAEO,SAAS,WAAW,IAAA,EAA0B;AACnD,EAAA,MAAM,GAAA,GAAM,KAAK,IAAI,CAAA;AACrB,EAAA,OAAO,UAAA,CAAW,KAAK,GAAA,EAAK,CAAC,MAAM,CAAA,CAAE,UAAA,CAAW,CAAC,CAAC,CAAA;AACpD;AAQA,eAAsB,UAAA,CAAW,KAA0B,OAAA,EAAsC;AAC/F,EAAA,MAAM,MAAM,OAAO,GAAA,KAAQ,WAAW,OAAA,CAAQ,MAAA,CAAO,GAAG,CAAA,GAAI,GAAA;AAC5D,EAAA,MAAM,IAAI,MAAM,MAAA,CAAO,MAAA,CAAO,SAAA,CAAU,OAAO,GAAA,EAAqB,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAM,SAAA,EAAU,EAAG,KAAA,EAAO,CAAC,MAAM,CAAC,CAAA;AACtH,EAAA,OAAO,IAAI,UAAA,CAAW,MAAM,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,CAAA,EAAG,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAC,CAAA;AACpF;AAEA,eAAsB,UAAU,IAAA,EAA+B;AAC7D,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW,MAAM,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA;AACzF,EAAA,OAAO,CAAC,GAAG,MAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AACxE;AAGO,SAAS,eAAA,CAAgB,GAAe,CAAA,EAAwB;AACrE,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ,OAAO,KAAA;AAClC,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,MAAA,EAAQ,CAAA,EAAA,EAAK,IAAA,IAAQ,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,CAAA;AACtD,EAAA,OAAO,IAAA,KAAS,CAAA;AAClB;;;ACtBO,SAAS,UAAU,KAAA,EAAgC;AACxD,EAAA,OAAO,SAAA,CAAU,KAAA,CAAM,IAAA,EAAK,CAAE,aAAa,CAAA;AAC7C;AAGA,SAAS,YAAY,MAAA,EAAgB;AACnC,EAAA,IAAI,OAAO,WAAW,QAAA,IAAY,MAAA,CAAO,SAAS,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,uDAAuD,CAAA;AAC/H;AAGA,eAAsB,sBAAA,CAAuB,QAAgB,MAAA,EAA4C;AACvG,EAAA,WAAA,CAAY,MAAM,CAAA;AAClB,EAAA,IAAI,MAAA,CAAO,SAAA,KAAc,MAAA,IAAa,CAAC,gBAAA,CAAiB,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,gFAAgF,CAAA;AAChL,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAI,WAAA,EAAY,CAAE,OAAO,IAAA,CAAK,SAAA,CAAU,EAAE,CAAA,EAAG,MAAA,CAAO,MAAA,EAAQ,GAAG,MAAA,CAAO,SAAA,EAAW,CAAA,EAAG,MAAA,CAAO,QAAA,EAAU,CAAA,EAAG,MAAA,CAAO,QAAA,EAAU,CAAA,EAAG,CAAA,EAAG,CAAC,CAAC,CAAA;AAC3J,EAAA,MAAM,GAAA,GAAM,UAAU,MAAM,UAAA,CAAW,QAAQ,CAAA,YAAA,EAAe,OAAO,EAAE,CAAC,CAAA;AACxE,EAAA,OAAO,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AAC1B;AAGA,eAAsB,sBAAA,CAAuB,QAAgB,KAAA,EAAkD;AAC7G,EAAA,WAAA,CAAY,MAAM,CAAA;AAClB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,GAAS,KAAM,OAAO,IAAA;AAC7D,EAAA,MAAM,CAAC,OAAA,EAAS,GAAA,EAAK,KAAK,CAAA,GAAI,KAAA,CAAM,MAAM,GAAG,CAAA;AAC7C,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,GAAA,IAAO,KAAA,KAAU,QAAW,OAAO,IAAA;AACpD,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AACF,IAAA,KAAA,GAAQ,cAAc,GAAG,CAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,WAAW,MAAM,UAAA,CAAW,MAAA,EAAQ,CAAA,YAAA,EAAe,OAAO,CAAA,CAAE,CAAA;AAClE,EAAA,IAAI,CAAC,eAAA,CAAgB,KAAA,EAAO,QAAQ,GAAG,OAAO,IAAA;AAC9C,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAI,WAAA,GAAc,MAAA,CAAO,aAAA,CAAc,OAAO,CAAC,CAAC,CAAA;AACvE,IAAA,IAAI,IAAI,CAAA,KAAM,CAAA,IAAK,OAAO,GAAA,CAAI,CAAA,KAAM,UAAU,OAAO,IAAA;AACrD,IAAA,MAAM,IAAA,GAAO,OAAO,GAAA,CAAI,CAAA,KAAM,QAAA,IAAY,gBAAA,CAAiB,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,KAAA,CAAA;AACjF,IAAA,IAAI,OAAO,GAAA,CAAI,CAAA,KAAM,QAAA,IAAY,CAAC,MAAM,OAAO,IAAA;AAC/C,IAAA,OAAO;AAAA,MACL,UAAU,GAAA,CAAI,CAAA;AAAA,MACd,GAAI,OAAO,GAAA,CAAI,CAAA,KAAM,QAAA,GAAW,EAAE,MAAA,EAAQ,GAAA,CAAI,CAAA,EAAE,GAAI,EAAC;AAAA,MACrD,GAAI,IAAA,GAAO,EAAE,SAAA,EAAW,IAAA,KAAS,EAAC;AAAA,MAClC,GAAI,OAAO,GAAA,CAAI,CAAA,KAAM,QAAA,GAAW,EAAE,QAAA,EAAU,GAAA,CAAI,CAAA,EAAE,GAAI;AAAC,KACzD;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGO,SAAS,kBAAA,CAAmB,KAAa,KAAA,EAAuC;AACrF,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,GAAG,CAAA;AACxB,EAAA,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,kBAAA,EAAoB,CAAA,CAAA,EAAI,IAAA,CAAK,QAAA,EAAU,CAAA,CAAA,CAAA;AAAA,IACvC,uBAAA,EAAyB;AAAA,GAC3B;AACF;;;AC2BA,SAAS,QAAA,CAAS,SAAqB,UAAA,EAA+C;AACpF,EAAA,MAAM,KAAK,UAAA,GAAa,EAAE,WAAA,EAAa,UAAA,KAAe,EAAC;AACvD,EAAA,QAAQ,QAAQ,MAAA;AAAQ,IACtB,KAAK,MAAA;AACH,MAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,WAAA,EAAa,cAAc,IAAA,EAAK;AAAA,IAC3D,KAAK,UAAA;AACH,MAAA,OAAO,EAAE,QAAQ,UAAA,EAAW;AAAA,IAC9B,KAAK,SAAA;AACH,MAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,MAAA,EAAQ,gBAAA,CAAiB,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA,GAAI,OAAA,CAAQ,MAAA,GAAS,UAAA,EAAW;AAAA,IAC1G,KAAK,QAAA;AACH,MAAA,OAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,OAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,EAAA,EAAG;AAAA,IACzD,KAAK,OAAA;AACH,MAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,QAAQ,KAAA,EAAO,QAAA,EAAU,OAAA,CAAQ,OAAA,CAAQ,aAAY,EAAG,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAS,GAAG,EAAA,EAAG;AAAA;AAE/H;AAGO,SAAS,QAAQ,GAAA,EAAwC;AAC9D,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,MAAA,GAAS,KAAM,OAAO,IAAA;AACzD,EAAA,IAAI,CAAC,eAAe,IAAA,CAAK,GAAG,KAAK,2BAAA,CAA4B,IAAA,CAAK,GAAG,CAAA,EAAG,OAAO,IAAA;AAC/E,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,WAAA,GAAyB;AAChC,EAAA,OAAO,EAAE,OAAA,EAAS,CAAA,EAAG,MAAM,CAAA,EAAG,QAAA,EAAU,GAAG,OAAA,EAAS,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,UAAU,CAAA,EAAG,MAAA,EAAQ,GAAG,KAAA,EAAO,CAAA,EAAG,YAAY,CAAA,EAAE;AACtH;AAEA,SAAS,SAAA,CAAU,GAAc,CAAA,EAAyB;AACxD,EAAA,MAAM,GAAA,GAAM,EAAE,GAAG,CAAA,EAAE;AACnB,EAAA,KAAA,MAAW,CAAA,IAAK,OAAO,IAAA,CAAK,GAAG,GAA0B,GAAA,CAAI,CAAC,CAAA,IAAK,CAAA,CAAE,CAAC,CAAA;AACtE,EAAA,OAAO,GAAA;AACT;AAOO,SAAS,yBAAyB,OAAA,EAAwD;AAC/F,EAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI,OAAA;AAC5B,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc;AACzB,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,IACjB,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA;AACA,EAAA,MAAM,KAAA,GAAQ,QAAQ,SAAA,IAAa,GAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,QAAQ,YAAA,IAAgB,GAAA;AACtC,EAAA,MAAM,MAAM,MAAM,OAAA,CAAQ,GAAA,IAAM,IAAK,KAAK,GAAA,EAAI;AAC9C,EAAA,IAAI,QAAQ,WAAA,KAAgB,MAAA,IAAa,EAAE,OAAA,CAAQ,WAAA,IAAe,QAAQ,GAAA,CAAA,EAAO;AAC/E,IAAA,MAAM,IAAI,MAAM,8GAA8G,CAAA;AAAA,EAChI;AACA,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA;AACtB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA,CAAE,SAAS,KAAA,KAAU,MAAA,IAAa,CAAC,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA,CAAE,aAAA,IAAiB,CAAC,QAAA,CAAS,UAAA,CAAW,GAAG,CAAC,CAAA;AACpL,IAAA,IAAI,gBAAA,IAAoB,CAAC,KAAA,CAAM,WAAA,EAAa;AAC1C,MAAA,MAAM,IAAI,MAAM,2HAA2H,CAAA;AAAA,IAC7I;AACA,IAAA,KAAA,MAAW,CAAC,KAAK,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA,EAAG;AACrD,MAAA,IAAI,CAAA,CAAE,UAAA,IAAc,CAAC,KAAA,CAAM,WAAA,GAAc,GAAG,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,+BAAA,EAAkC,GAAG,CAAA,iDAAA,CAAmD,CAAA;AAAA,IACzJ;AAAA,EACF;AACA,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAE5C,EAAA,MAAM,MAAA,GAAS,OAAO,GAAA,EAAU,OAAA,KAAwB;AACtD,IAAA,MAAM,UAAA,GAAa,WAAA,CAAY,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AACzC,IAAA,WAAA,CAAY,MAAA,CAAO,IAAI,EAAE,CAAA;AACzB,IAAA,OAAO,KAAA,CAAM,OAAO,GAAA,CAAI,EAAA,EAAI,IAAI,QAAA,EAAU,QAAA,CAAS,OAAA,EAAS,UAAU,CAAC,CAAA;AAAA,EACzE,CAAA;AACA,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,MAAA;AAAA,IACA,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,WAAA,EAAa,CAAC,GAAA,KAAa,CAAA,EAAG,IAAI,OAAO,CAAA,KAAA,EAAQ,IAAI,EAAE,CAAA;AAAA,GACzD;AAIA,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAC1C,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAE1C,EAAA,IAAI,QAAA,GAA6B,QAAQ,OAAA,EAAQ;AACjD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,IAAA,GACvB,YAAA,CAAkB;AAAA,IAChB,GAAG,MAAA;AAAA,IACH,KAAA,EAAO,CAAC,GAAA,KAAQ,CAAA,KAAA,EAAQ,IAAI,EAAE,CAAA,CAAA;AAAA,IAC9B,KAAA,EAAO,OAAO,EAAE,KAAA,EAAM,KAAM;AAC1B,MAAA,MAAM,YAAY,GAAA,EAAI;AACtB,MAAA,MAAM,IAAA,GAAQ,MAAM,KAAA,CAAM,KAAA,CAAM,QAAQ,KAAA,EAAO,KAAA,EAAO,OAAA,CAAQ,WAAA,IAAe,CAAC,CAAA;AAC9E,MAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,OAAA,EAAS,MAAA,IAAU,GAAG,eAAe,CAAA;AAC5D,QAAA,IAAI,MAAA,GAAS,CAAA,IAAK,MAAA,GAAS,CAAA,GAAI,eAAA,EAAiB;AAC9C,UAAA,KAAA,EAAA;AACA,UAAA,MAAA,GAAS,CAAA;AAAA,QACX;AACA,QAAA,MAAA,IAAU,CAAA;AACV,QAAA,SAAA,CAAU,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,KAAK,CAAA;AAC3B,QAAA,SAAA,CAAU,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,SAAA,GAAY,QAAQ,GAAI,CAAA;AAAA,MAChD;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAEA,KAAA,EAAO,CAAC,GAAA,KAAQ,CAAA,KAAA,EAAQ,UAAU,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,IAAK,CAAC,CAAA,CAAA;AAAA,IAClD,IAAA,EAAM,CAAC,IAAA,EAAM,EAAE,QAAO,KAAM;AAC1B,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,GAAG,KAAK,GAAA,CAAI,CAAC,CAAA,KAAM,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,IAAK,QAAQ,CAAC,CAAA;AAC7E,MAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,QAAA,SAAA,CAAU,MAAA,CAAO,EAAE,EAAE,CAAA;AACrB,QAAA,SAAA,CAAU,MAAA,CAAO,EAAE,EAAE,CAAA;AAAA,MACvB;AACA,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,CAAS,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAM,QAAA,EAAU,QAAA,EAAU,MAAM,CAAC,CAAA;AACzF,MAAA,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,MAAM,MAAS,CAAA;AACrC,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACZ,CAAA,GACD,IAAA;AAGJ,EAAA,SAAS,WAAW,MAAA,EAA8E;AAChG,IAAA,MAAM,MAAuC,EAAC;AAC9C,IAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,IAAU,EAAE,CAAA,EAAG;AACvD,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAa,OAAO,KAAA,KAAU,QAAA,IAAY,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAI,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AAAA,IACrG;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,SAAS,WAAA,CAAY,KAAU,KAAA,EAA4B;AACzD,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,aAAA,IAAiB,GAAA,CAAI,YAAA,KAAiB,SAAA;AACzD,IAAA,MAAM,WAAW,QAAA,CAAS,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,IAAI,IAAI,CAAA;AACnD,IAAA,MAAM,IAAA,GAAO,MAAA,GAAS,EAAE,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,QAAA,CAAS,aAAA,CAAc,IAAA,EAAK,GAAI,QAAA;AACnG,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA;AAEhC,IAAA,MAAM,SAAS,EAAE,GAAG,UAAA,CAAW,QAAA,CAAS,MAAM,GAAG,CAAA,EAAG,GAAI,MAAA,GAAS,EAAC,GAAI,UAAA,CAAW,QAAA,CAAS,IAAA,EAAM,OAAO,CAAA,EAAG;AAC1G,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,GAAI,KAAK,IAAA,GAAO,EAAE,MAAM,IAAA,CAAK,IAAA,KAAS,EAAC;AAAA,MACvC,IAAA,EAAM,EAAE,GAAG,MAAA,EAAQ,eAAA,EAAiB,GAAA,CAAI,eAAA,EAAiB,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,GAAI,GAAA,GAAM,EAAE,KAAI,GAAI,EAAC,EAAI,GAAI,GAAA,CAAI,UAAA,GAAa,EAAE,UAAA,EAAY,GAAA,CAAI,UAAA,EAAW,GAAI,EAAC,EAAG;AAAA,MAC5J,GAAI,CAAC,MAAA,IAAU,QAAA,CAAS,IAAA,EAAM,iBAAiB,EAAE,cAAA,EAAgB,IAAA,EAAK,GAAI,EAAC;AAAA,MAC3E,KAAA,EAAO,SAAA;AAAA,MACP,GAAI,GAAA,CAAI,KAAA,KAAU,IAAA,IAAQ,GAAA,CAAI,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,GAAA,CAAI,KAAA,EAAM,GAAI,EAAC;AAAA,MAC5E,WAAW,GAAA,CAAI,QAAA;AAAA,MACf,GAAI,IAAI,YAAA,GAAe,EAAE,YAAY,GAAA,CAAI,YAAA,KAAiB,EAAC;AAAA,MAC3D,GAAI,IAAI,UAAA,GAAa,EAAE,UAAU,GAAA,CAAI,UAAA,KAAe,EAAC;AAAA,MACrD,aAAa,GAAA,CAAI,YAAA,IAAgB,IAAI,eAAA,EAAiB,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,MACjE,MAAM,GAAA,CAAI,YAAA,IAAgB,IAAI,eAAA,EAAiB,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,MAC1D,QAAA,EAAU;AAAA,KACZ;AAAA,EACF;AAEA,EAAA,eAAe,QAAA,CAAS,IAAA,EAAsB,QAAA,EAAwB,QAAA,EAAkB,MAAA,EAA4C;AAElI,IAAA,MAAM,SAAA,GAAY,WAAW,kBAAA,GAAqB,GAAA;AAClD,IAAA,MAAM,eAAA,GAAkB,MAAM,IAAI,SAAA,CAAU,mBAAA,EAAqB,EAAE,SAAA,EAAW,IAAA,EAAM,iBAAA,EAAmB,CAAA,EAAG,CAAA;AAC1G,IAAA,MAAM,QAAiE,EAAC;AACxE,IAAA,MAAM,UAAgC,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAS,QAAA,CAAS,IAAI,GAAA,CAAI,IAAI,CAAA,GAAI,IAAA,GAAO,EAAE,EAAA,EAAI,KAAA,EAAO,WAAW,KAAA,EAAO,KAAA,EAAO,gBAAiB,CAAA;AAChJ,IAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,EAAK,CAAA,KAAM;AACvB,MAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,EAAG;AAChB,MAAA,KAAA,MAAW,KAAK,GAAA,CAAI,OAAA,IAAW,EAAC,EAAG,KAAA,CAAM,KAAK,EAAE,GAAA,EAAK,GAAG,MAAA,EAAQ,CAAA,CAAE,IAAI,OAAA,EAAS,WAAA,CAAY,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,IAC5G,CAAC,CAAA;AACD,IAAA,MAAM,OAAA,GAAsC,IAAI,KAAA,CAAM,KAAA,CAAM,MAAM,CAAA;AAClE,IAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,KAAA,CAAM,MAAA,EAAQ,SAAS,eAAA,EAAiB;AAClE,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,QAAQ,eAAe,CAAA;AACxD,MAAA,IAAI,GAAA,KAAQ,SAAA,EAAW;AACrB,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA,GAAI,eAAA,EAAgB;AAC5E,QAAA;AAAA,MACF;AACA,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,CAAA,EAAG,EAAE,QAAQ,CAAA;AACvE,QAAA,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAO,QAAQ,KAAA,GAAQ,CAAC,IAAI,CAAE,CAAA;AAAA,MAChD,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,IAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AAE9C,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,YAAA,IAAI,GAAA,KAAQ,SAAA,EAAW;AACrB,cAAA,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA,GAAI,eAAA,EAAgB;AACrC,cAAA;AAAA,YACF;AACA,YAAA,IAAI;AACF,cAAA,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA,GAAA,CAAK,MAAM,SAAS,IAAA,CAAK,CAAC,KAAA,CAAM,CAAC,CAAA,CAAG,OAAO,GAAG,EAAE,MAAA,EAAQ,CAAA,EAAG,CAAC,KAAK,EAAE,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,UAAA,EAAW;AAAA,YACxH,SAAS,GAAA,EAAK;AACZ,cAAA,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA,GAAI,GAAA,YAAe,SAAA,GAAY,GAAA,GAAM,IAAI,SAAA,CAAU,aAAA,EAAe,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,YACxG;AAAA,UACF;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAM,CAAA,GAAI,KAAA,YAAiB,SAAA,GAAY,KAAA,GAAQ,IAAI,UAAU,aAAA,EAAe,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAC/F,UAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA,GAAI,CAAA;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,IAAA,CAAK,GAAA,CAAI,OAAO,GAAA,EAAK,CAAA,KAAM;AACzB,QAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,EAAG;AAChB,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO,EAAE,GAAG,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAE,EAAE,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,CAAA,CAAE,QAAQ,CAAC,CAAA;AACrF,QAAA,MAAM,IAAA,GAAwB,IAAA,CAC3B,MAAA,CAAO,CAAC,MAAM,EAAE,CAAA,CAAE,CAAA,YAAa,SAAA,CAAU,EACzC,GAAA,CAAI,CAAC,EAAE,CAAA,EAAG,GAAE,KAAM;AACjB,UAAA,MAAM,MAAA,GAAS,CAAA;AACf,UAAA,OAAO,MAAA,CAAO,MAAA,KAAW,IAAA,GACrB,EAAE,SAAA,EAAW,EAAE,MAAA,EAAQ,SAAA,EAAW,MAAA,CAAO,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAiB,OAAO,IAAA,EAAK,GAClF,EAAE,SAAA,EAAW,CAAA,CAAE,MAAA,EAAQ,SAAA,EAAW,IAAA,EAAM,MAAA,EAAQ,QAAA,EAAmB,KAAA,EAAO,MAAA,CAAO,IAAA,EAAK;AAAA,QAC5F,CAAC,CAAA;AACH,QAAA,IAAI,KAAK,MAAA,EAAQ;AACf,UAAA,IAAI;AACF,YAAA,MAAM,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,EAAA,EAAI,IAAI,CAAA;AAAA,UACtC,CAAA,CAAA,MAAQ;AACN,YAAA,GAAA,CAAI,CAAA,eAAA,EAAkB,GAAA,CAAI,EAAE,CAAA,iBAAA,CAAmB,CAAA;AAAA,UACjD;AAAA,QACF;AACA,QAAA,IAAI,KAAK,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,MAAM,CAAA,EAAG;AACzC,UAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAE,EAAA,EAAI,IAAA,EAAK;AACxB,UAAA;AAAA,QACF;AACA,QAAA,MAAM,SAAA,GAAY,KAAK,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,CAAA,YAAa,SAAS,CAAA,EAAG,CAAA;AAC9D,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,SAAA,CAAU,SAAS,CAAA;AAChC,UAAA;AAAA,QACF;AACA,QAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AACrC,QAAA,IAAI,MAAM,MAAA,IAAU,KAAA,CAAM,KAAA,CAAM,CAAC,MAAM,CAAA,KAAM,qBAAqB,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAE,EAAA,EAAI,IAAA,EAAM,SAAS,uBAAA,EAAwB;AAAA,aAAA,IACtH,KAAA,CAAM,QAAA,CAAS,qBAAqB,CAAA,UAAW,CAAC,CAAA,GAAI,EAAE,EAAA,EAAI,OAAO,SAAA,EAAW,IAAA,EAAM,iBAAA,EAAmB,EAAA,EAAI,OAAO,2BAAA,EAA4B;AAAA,aAAA,IAC5I,CAAC,KAAA,CAAM,MAAA,EAAQ,OAAA,CAAQ,CAAC,IAAI,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,YAAA,EAAa;AAAA,qBAC1D,CAAC,CAAA,GAAI,EAAE,EAAA,EAAI,KAAA,EAAO,WAAW,KAAA,EAAO,KAAA,EAAO,CAAA,MAAA,EAAS,KAAA,CAAM,KAAK,CAAC,CAAA,KAAM,MAAM,qBAAqB,CAAA,IAAK,SAAS,CAAA,CAAA,EAAG;AAAA,MACjI,CAAC;AAAA,KACH;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAGA,EAAA,MAAM,WAAA,GAAc,QAChB,YAAA,CAAkB;AAAA,IAChB,GAAG,MAAA;AAAA,IACH,KAAA,EAAO,CAAC,GAAA,KAAQ,CAAA,MAAA,EAAS,IAAI,EAAE,CAAA,CAAA;AAAA,IAC/B,KAAA,EAAO,CAAC,EAAE,KAAA,EAAM,KAAM,KAAA,CAAM,KAAA,CAAM,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,OAAA,CAAQ,WAAA,IAAe,CAAC,CAAA;AAAA,IACjF,IAAA,EAAM,OAAO,CAAC,GAAG,GAAG,EAAE,MAAA,EAAO,KAAM,CAAC,MAAM,SAAA,CAAU,GAAA,EAAM,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,IACxE,SAAA,EAAW;AAAA,GACZ,CAAA,GACD,IAAA;AAEJ,EAAA,SAAS,WAAA,CAAY,KAAU,CAAA,EAAyB;AACtD,IAAA,MAAM,KAAA,GAAQ,EAAE,SAAA,IAAa,eAAA;AAC7B,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,aAAA,GAAgB,CAAA,CAAE,OAAA,CAAQ,gBAAiB,CAAA,CAAE,OAAA,CAAQ,KAAA,IAAS,CAAA,CAAE,OAAA,CAAQ,aAAA;AAE3F,IAAA,MAAM,GAAA,GAAM,IAAI,MAAA,EAAQ,eAAA,IAAmB,IAAI,MAAA,CAAO,MAAA,IAAU,CAAC,gBAAA,CAAiB,GAAA,CAAI,MAAA,CAAO,QAAQ,CAAC,CAAA,CAAE,OAAA,CAAQ,aAAA,EAAe,CAAA,CAAE,OAAA,CAAQ,KAAK,CAAC,CAAA,GAAI,GAAA,CAAI,MAAA,CAAO,MAAA,GAAS,IAAA;AACvK,IAAA,MAAM,SAAS,GAAA,IAAO,MAAA;AACtB,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,KAAA,GAAQ,MAAM,MAAM,QAAA,GAAY,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA,GAAe,IAAA;AAC5F,IAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,CAAI,MAAA,EAAQ,SAAA,IAAa,SAAA,IAAa,CAAA,CAAE,OAAO,CAAA,IAAK,WAAA,CAAY,CAAA,CAAE,OAAO,CAAA;AAClG,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,EAAA,EAAK,KAAK,IAAI,MAAM,CAAA,CAAA,CAAA;AAAA,EACpC;AAEA,EAAA,eAAe,MAAA,CAAO,KAAU,CAAA,EAAyC;AACvE,IAAA,MAAM,KAAK,GAAA,CAAI,KAAA;AACf,IAAA,IAAI,CAAC,aAAA,CAAc,EAAE,GAAG,MAAM,IAAI,UAAU,kBAAkB,CAAA;AAC9D,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA;AAChC,IAAA,IAAI,cAAA,GAAgC,IAAA;AACpC,IAAA,IAAI,OAAA;AAEJ,IAAA,IAAI,CAAC,GAAA,CAAI,aAAA,IAAiB,CAAC,GAAA,CAAI,QAAA,IAAY,EAAE,WAAA,EAAa;AACxD,MAAA,MAAM,KAAA,GAAQ,MAAM,sBAAA,CAAuB,CAAA,CAAE,YAAY,MAAA,EAAQ;AAAA,QAC/D,GAAI,GAAA,CAAI,OAAA,GAAU,EAAE,MAAA,EAAQ,GAAA,CAAI,OAAA,EAAQ,GAAI,EAAE,SAAA,EAAW,MAAM,SAAA,CAAU,EAAE,CAAA,EAAE;AAAA,QAC7E,UAAU,GAAA,CAAI,QAAA;AAAA,QACd,GAAI,IAAI,SAAA,GAAY,EAAE,UAAU,GAAA,CAAI,SAAA,KAAc;AAAC,OACpD,CAAA;AACD,MAAA,OAAA,GAAU,kBAAA,CAAmB,CAAA,CAAE,WAAA,CAAY,GAAA,EAAK,KAAK,CAAA;AACrD,MAAA,cAAA,GAAiB,OAAA,CAAQ,kBAAkB,CAAA,CAAG,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,IAC3D;AACA,IAAA,MAAM,OAAO,QAAA,CAAS,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,IAAI,IAAI,CAAA;AAC/C,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA;AAC7B,IAAA,MAAM,GAAA,GAAM,IAAA,IAAQ,OAAA,CAAQ,MAAA,GAAS,IAAI,GAAA,CAAI,IAAA,EAAM,IAAI,GAAA,CAAI,QAAQ,MAAM,CAAA,CAAE,MAAM,CAAA,CAAE,UAAS,GAAI,IAAA;AAChG,IAAA,MAAM,WAAW,CAAA,CAAE,SAAA,GAAY,IAAI,IAAI,CAAA,IAAK,EAAE,eAAA,IAAmB,oBAAA;AACjE,IAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS;AAAA,MAC7B,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,IAAA;AAAA,MACA,WAAW,EAAE,KAAA,EAAO,EAAA,EAAI,IAAA,EAAM,IAAI,cAAA,EAAe;AAAA,MACjD,KAAA,EAAQ,GAAA,CAAI,KAAA,IAAS,EAAC;AAAA,MACtB,UAAU,GAAA,CAAI,SAAA;AAAA,MACd,eAAe,GAAA,CAAI,aAAA;AAAA,MACnB,UAAA,EAAY,CAAA,CAAE,UAAA,GAAa,uBAAA,GAA0B,IAAA;AAAA,MACrD,cAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,MAAM,OAAA,GAAU,IAAI,MAAA,EAAQ,QAAA;AAC5B,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,WAAA,CAAY,GAAA,EAAK,CAAC,CAAA;AAAA,MACxB,EAAA,EAAI,CAAC,EAAE,CAAA;AAAA,MACP,GAAI,cAAc,OAAO,CAAA,GAAI,EAAE,QAAA,EAAU,OAAA,KAAY,EAAC;AAAA,MACtD,OAAA,EAAS,WAAW,OAAA,CAAQ,OAAO,KAAK,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA,IAAK,cAAA;AAAA,MAClE,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,MAC7B,IAAA,EAAM;AAAA,QACJ,EAAE,MAAM,UAAA,EAAY,KAAA,EAAO,IAAI,QAAA,CAAS,OAAA,CAAQ,iBAAA,EAAmB,GAAG,CAAA,EAAE;AAAA,QACxE,EAAE,IAAA,EAAM,QAAA,EAAU,OAAO,GAAA,CAAI,aAAA,GAAgB,kBAAkB,OAAA;AAAQ;AACzE,KACF;AAAA,EACF;AAEA,EAAA,eAAe,SAAA,CAAU,GAAA,EAAU,CAAA,EAAiB,MAAA,EAA0C;AAC5F,IAAA,IAAI;AACF,MAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,GAAA,CAAI,IAAI,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,OAAO,cAAA,EAAe;AACzF,MAAA,IAAI,WAAW,GAAA,CAAI,QAAA;AACnB,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,IAAI,CAAC,GAAA,CAAI,aAAA,IAAkB,MAAM,SAAA,CAAU,cAAc,QAAQ,CAAC,CAAA,KAAO,GAAA,CAAI,aAAA,EAAe;AAE1F,UAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,OAAO,wBAAA,EAAyB;AAAA,QACxE;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,GAAA,EAAK,CAAC,CAAA;AACjC,QAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,aAAA,CAAc,KAAK,CAAC,CAAA;AACjD,QAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,aAAA,CAAc,IAAI,EAAA,EAAI,GAAA,CAAI,QAAA,EAAU,KAAA,EAAO,IAAI,CAAA;AACxE,QAAA,IAAI,CAAC,MAAM,OAAO,EAAE,IAAI,KAAA,EAAO,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,YAAA,EAAa;AACpE,QAAA,QAAA,GAAW,IAAA,CAAK,QAAA;AAAA,MAClB;AACA,MAAA,IAAI,OAAO,QAAA,CAAS,IAAA;AACpB,MAAA,IAAI,OAAO,QAAA,CAAS,IAAA;AACpB,MAAA,IAAI,QAAA,CAAS,IAAA,CAAK,GAAA,CAAI,IAAI,EAAE,UAAA,EAAY;AACtC,QAAA,MAAM,IAAA,GAAO,MAAM,iBAAA,CAAkB,CAAA,CAAE,eAAe,EAAC,EAAG,IAAI,IAAA,EAAM;AAAA,UAClE,MAAM,GAAA,CAAI,IAAA;AAAA,UACV,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,gBAAgB,GAAA,CAAI,eAAA;AAAA,UACpB,UAAU,GAAA,CAAI;AAAA,SACf,CAAA;AACD,QAAA,IAAA,GAAO,KAAK,KAAA,CAAM,uBAAuB,EAAE,IAAA,CAAK,UAAA,CAAW,IAAI,CAAC,CAAA;AAChE,QAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,uBAAuB,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,MACtD;AACA,MAAA,MAAM,QAAA,GAAW,CAAA,CAAE,WAAA,GAAc,GAAG,KAAK,CAAA,CAAE,QAAA;AAC3C,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,IAAA;AAAA,QAC5B;AAAA,UACE,MAAM,QAAA,CAAS,IAAA;AAAA,UACf,IAAI,QAAA,CAAS,EAAA;AAAA,UACb,GAAI,SAAS,QAAA,GAAW,EAAE,SAAS,QAAA,CAAS,QAAA,KAAa,EAAC;AAAA,UAC1D,SAAS,QAAA,CAAS,OAAA;AAAA,UAClB,IAAA;AAAA,UACA,IAAA;AAAA,UACA,GAAI,SAAS,OAAA,GAAU,EAAE,SAAS,QAAA,CAAS,OAAA,KAAY,EAAC;AAAA,UACxD,GAAI,SAAS,IAAA,GAAO,EAAE,MAAM,QAAA,CAAS,IAAA,KAAS,EAAC;AAAA,UAC/C,cAAA,EAAgB,CAAA,qBAAA,EAAwB,GAAA,CAAI,EAAE,CAAA;AAAA,SAChD;AAAA,QACA,EAAE,MAAA;AAAO,OACX;AACA,MAAA,IAAI,OAAO,EAAA,EAAI;AACb,QAAA,WAAA,CAAY,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,MAAA,CAAO,EAAE,CAAA;AACjC,QAAA,IAAI;AACF,UAAA,MAAM,MAAM,gBAAA,CAAiB,GAAA,CAAI,IAAI,GAAA,CAAI,QAAA,EAAU,OAAO,EAAE,CAAA;AAAA,QAC9D,CAAA,CAAA,MAAQ;AACN,UAAA,GAAA,CAAI,CAAA,cAAA,EAAiB,GAAA,CAAI,EAAE,CAAA,iDAAA,CAAmD,CAAA;AAAA,QAChF;AAAA,MACF;AACA,MAAA,OAAO,MAAA,CAAO,WAAA,GAAc,EAAE,EAAA,EAAI,IAAA,EAAM,cAAc,IAAA,EAAK,GAAI,EAAE,EAAA,EAAI,IAAA,EAAK;AAAA,IAC5E,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,KAAA,YAAiB,SAAA,IAAa,KAAA,CAAM,OAAA,KAAY,kBAAA,SAA2B,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,YAAA,EAAa;AACjH,MAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,iBAAA,EAAmB,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,KAAA,EAAO,yBAAA,EAA0B;AACvI,MAAA,OAAO,UAAU,KAAK,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,MAAM,OAAO,YAAgC;AAC3C,IAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAAA,EACjD,CAAA;AAGA,EAAA,eAAe,KAAA,CAAM,GAA6D,MAAA,EAA0C;AAC1H,IAAA,MAAM,SAAS,OAAA,CAAQ,WAAA;AACvB,IAAA,IAAI,WAAW,MAAA,EAAW,OAAO,EAAE,GAAA,CAAI,EAAE,QAAQ,CAAA;AACjD,IAAA,MAAM,UAAU,GAAA,EAAI;AACpB,IAAA,IAAI,QAAQ,WAAA,EAAY;AACxB,IAAA,OAAO,MAAA,IAAU,KAAI,GAAI,OAAA,CAAA,IAAY,QAAQ,GAAA,IAAQ,CAAC,QAAQ,OAAA,EAAS;AACrE,MAAA,MAAM,SAAS,MAAM,CAAA,CAAE,GAAA,CAAI,EAAE,QAAQ,CAAA;AACrC,MAAA,KAAA,GAAQ,SAAA,CAAU,OAAO,MAAM,CAAA;AAC/B,MAAA,IAAI,MAAA,CAAO,UAAU,KAAA,EAAO;AAAA,IAC9B;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,GAAA,CAAI,EAAE,MAAA,EAAO,GAAI,EAAC,EAAG;AACzB,MAAA,MAAM,CAAC,MAAM,IAAI,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,UAAA,GAAa,KAAA,CAAM,YAAY,MAAM,CAAA,GAAI,MAAM,WAAA,GAAc,KAAA,CAAM,aAAa,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACvI,MAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AAAA,IAC7B,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,EAAE,MAAA,EAAO,GAAI,EAAC,KAAO,UAAA,GAAa,KAAA,CAAM,UAAA,EAAY,MAAM,CAAA,GAAI,IAAA,EAAK;AAAA,IAC7E,QAAA,EAAU,CAAC,EAAE,MAAA,EAAO,GAAI,EAAC,KAAO,WAAA,GAAc,KAAA,CAAM,WAAA,EAAa,MAAM,CAAA,GAAI,IAAA,EAAK;AAAA,IAChF,MAAM,cAAc,EAAE,MAAA,EAAQ,WAAW,GAAA,EAAO,GAAI,EAAC,EAAG;AACtD,MAAA,MAAM,QAAA,GAAW,QAAQ,IAAA,EAAM,QAAA;AAC/B,MAAA,IAAI,CAAC,QAAA,EAAU,OAAO,EAAE,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,EAAE;AAC/C,MAAA,MAAM,OAAA,GAAU,KAAK,GAAA,EAAI;AACzB,MAAA,IAAI,OAAA,GAAU,CAAA;AACd,MAAA,IAAI,OAAA,GAAU,CAAA;AACd,MAAA,WAAS;AACP,QAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,aAAA,CAAc,mBAAmB,CAAA;AACzD,QAAA,IAAI,CAAC,IAAI,MAAA,EAAQ;AACjB,QAAA,OAAA,IAAW,GAAA,CAAI,MAAA;AACf,QAAA,IAAI,QAAA;AACJ,QAAA,IAAI;AACF,UAAA,QAAA,GAAW,MAAM,QAAA,CAAS,QAAA,CAAS,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAA,EAAG,EAAE,MAAA,EAAQ,CAAA;AAAA,QAC5E,SAAS,KAAA,EAAO;AACd,UAAA,GAAA,CAAI,yBAAyB,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,SAAS,CAAA,CAAE,CAAA;AACjF,UAAA;AAAA,QACF;AACA,QAAA,MAAM,OAAA,GAAU,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAA,KAAM;AACjC,UAAA,MAAM,CAAA,GAAI,QAAA,CAAS,CAAA,CAAE,SAAS,CAAA;AAC9B,UAAA,IAAI,CAAC,CAAA,EAAG,OAAO,EAAC;AAChB,UAAA,OAAO,CAAC,EAAE,EAAA,EAAI,EAAE,EAAA,EAAI,MAAA,EAAQ,EAAE,MAAA,KAAW,IAAA,GAAQ,WAAA,GAAyB,QAAA,EAAoB,OAAO,CAAA,CAAE,MAAA,KAAW,UAAU,CAAA,CAAE,IAAA,GAAO,MAAM,CAAA;AAAA,QAC7I,CAAC,CAAA;AACD,QAAA,IAAI,OAAA,CAAQ,KAAK,CAAC,CAAA,KAAM,EAAE,KAAA,KAAU,oBAAoB,CAAA,EAAG,GAAA,CAAI,kFAAkF,CAAA;AACjJ,QAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,MAAM,KAAA,CAAM,eAAe,OAAO,CAAA;AACtD,QAAA,OAAA,IAAW,OAAA,CAAQ,MAAA;AACnB,QAAA,IAAI,IAAI,MAAA,GAAS,mBAAA,IAAuB,KAAK,GAAA,EAAI,GAAI,UAAU,QAAA,EAAU;AAAA,MAC3E;AACA,MAAA,OAAO,EAAE,SAAS,OAAA,EAAQ;AAAA,IAC5B;AAAA,GACF;AACF;;;ACngBO,IAAM,yBAAA,GAA4B;AAGzC,eAAsB,oBAAoB,MAAA,EAAgB,OAAA,EAAkB,MAAc,GAAA,GAAM,IAAA,CAAK,KAAI,EAAqB;AAC5H,EAAA,MAAM,KAAK,OAAA,CAAQ,GAAA,CAAI,SAAS,CAAA,IAAK,OAAA,CAAQ,IAAI,YAAY,CAAA;AAC7D,EAAA,MAAM,YAAY,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,IAAK,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAClF,EAAA,MAAM,aAAa,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,IAAK,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AACnF,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,EAAA,IAAM,CAAC,SAAA,IAAa,CAAC,UAAA,IAAc,CAAC,YAAA,CAAa,IAAA,CAAK,SAAS,GAAG,OAAO,KAAA;AACzF,EAAA,IAAI,IAAA,CAAK,IAAI,GAAA,GAAM,GAAA,GAAO,OAAO,SAAS,CAAC,CAAA,GAAI,yBAAA,EAA2B,OAAO,KAAA;AACjF,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,UAAA,CAAW,OAAO,UAAA,CAAW,QAAQ,IAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,GAAI,MAAM,CAAA;AAAA,EACzE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,GAAA,EAAK,CAAA,EAAG,EAAE,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA;AACnE,EAAA,KAAA,MAAW,IAAA,IAAQ,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA,EAAG;AACxC,IAAA,MAAM,CAAC,OAAA,EAAS,GAAG,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACrC,IAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,CAAC,GAAA,EAAK;AAC9B,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,WAAW,GAAG,CAAA;AAAA,IACxB,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AACA,IAAA,IAAI,eAAA,CAAgB,KAAA,EAAO,QAAQ,CAAA,EAAG,OAAO,IAAA;AAAA,EAC/C;AACA,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,aAAa,OAAA,EAAoH;AAC/I,EAAA,MAAM,CAAA,GAAI,OAAA;AACV,EAAA,MAAM,EAAA,GAAK,GAAG,IAAA,EAAM,QAAA;AACpB,EAAA,IAAI,OAAO,OAAO,QAAA,IAAY,CAAC,MAAM,EAAA,CAAG,MAAA,GAAS,KAAK,OAAO,IAAA;AAC7D,EAAA,QAAQ,GAAG,IAAA;AAAM,IACf,KAAK,iBAAA;AACH,MAAA,OAAO,EAAE,UAAA,EAAY,EAAA,EAAI,KAAA,EAAO,WAAA,EAAa,WAAW,KAAA,EAAM;AAAA,IAChE,KAAK,eAAA;AAEH,MAAA,OAAO,EAAE,UAAA,EAAY,EAAA,EAAI,KAAA,EAAO,SAAA,EAAW,WAAW,CAAA,CAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,KAAS,WAAA,EAAY;AAAA,IAC7F,KAAK,kBAAA;AACH,MAAA,OAAO,EAAE,UAAA,EAAY,EAAA,EAAI,KAAA,EAAO,YAAA,EAAc,WAAW,IAAA,EAAK;AAAA,IAChE;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;;;ACnBA,IAAM,IAAA,GAAO;AAAA,EACX,KAAA,EAAO,aAAA;AAAA,EACP,OAAA,EAAS,4BAAA;AAAA,EACT,MAAA,EAAQ,aAAA;AAAA,EACR,IAAA,EAAM,2DAAA;AAAA,EACN,OAAA,EAAS;AACX,CAAA;AAEA,SAAS,IAAA,CAAK,KAAA,EAAe,IAAA,EAAc,MAAA,GAAS,GAAA,EAAe;AACjE,EAAA,MAAM,OAAO,CAAA,4KAAA,EAA+K,UAAA,CAAW,KAAK,CAAC,uHAAuH,IAAI,CAAA,cAAA,CAAA;AACxU,EAAA,OAAO,IAAI,QAAA,CAAS,IAAA,EAAM,EAAE,QAAQ,OAAA,EAAS,EAAE,cAAA,EAAgB,0BAAA,EAA4B,eAAA,EAAiB,UAAA,EAAY,iBAAA,EAAmB,aAAA,IAAiB,CAAA;AAC9J;AAEA,SAAS,IAAA,CAAK,IAAA,EAAe,MAAA,GAAS,GAAA,EAAe;AACnD,EAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,EAAG,EAAE,MAAA,EAAQ,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,eAAA,EAAiB,UAAA,IAAc,CAAA;AACpI;AAOO,SAAS,2BAA2B,OAAA,EAA+E;AACxH,EAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,IAAc,OAAA,CAAQ,UAAA,CAAW,SAAS,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,uEAAuE,CAAA;AAClJ,EAAA,MAAM,OAAO,EAAE,GAAG,IAAA,EAAM,GAAG,QAAQ,IAAA,EAAK;AACxC,EAAA,MAAM,GAAA,GAAM,IAAI,WAAA,EAAY;AAC5B,EAAA,MAAM,UAAA,GAAa,CAAC,OAAA,KAAqB;AACvC,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA,IAAK,EAAA;AACvD,IAAA,MAAM,KAAA,GAAQ,OAAO,UAAA,CAAW,SAAS,IAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,GAAI,EAAA;AAC/D,IAAA,OAAO,eAAA,CAAgB,IAAI,MAAA,CAAO,KAAK,GAAG,GAAA,CAAI,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAC,CAAA;AAAA,EAC1E,CAAA;AACA,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc;AACzB,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,IACjB,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,OAAO,OAAA,KAAY;AACxB,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,QAAA,CAAS,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAC5C,IAAA,IAAI;AACF,MAAA,IAAI,KAAK,QAAA,CAAS,MAAM,KAAK,IAAA,CAAK,QAAA,CAAS,WAAW,CAAA,EAAG;AACvD,QAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAQ,OAAO,KAAK,EAAE,KAAA,EAAO,oBAAA,EAAqB,EAAG,GAAG,CAAA;AAC/E,QAAA,IAAI,CAAC,UAAA,CAAW,OAAO,CAAA,EAAG,OAAO,KAAK,EAAE,KAAA,EAAO,cAAA,EAAe,EAAG,GAAG,CAAA;AACpE,QAAA,IAAI,IAAA,CAAK,SAAS,MAAM,CAAA,SAAU,IAAA,CAAK,EAAE,IAAI,IAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,MAAA,CAAO,IAAI,EAAE,MAAA,EAAQ,QAAQ,MAAA,EAAQ,GAAI,CAAA;AAC9G,QAAA,OAAO,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,MAAA,CAAO,aAAA,CAAc,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,GAAI,CAAA;AAAA,MAC/F;AAEA,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,kBAAkB,CAAA,EAAG;AACrC,QAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAQ,OAAO,KAAK,EAAE,KAAA,EAAO,oBAAA,EAAqB,EAAG,GAAG,CAAA;AAC/E,QAAA,IAAI,CAAC,QAAQ,mBAAA,EAAqB,OAAO,KAAK,EAAE,KAAA,EAAO,gBAAA,EAAiB,EAAG,GAAG,CAAA;AAC9E,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,IAAA,EAAK;AAChC,QAAA,IAAI,KAAK,MAAA,GAAS,KAAA,IAAW,CAAE,MAAM,mBAAA,CAAoB,QAAQ,mBAAA,EAAqB,OAAA,CAAQ,OAAA,EAAS,IAAI,GAAI,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,eAAA,IAAmB,GAAG,CAAA;AAC1J,QAAA,IAAI,OAAA;AACJ,QAAA,IAAI;AACF,UAAA,OAAA,GAAU,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QAC3B,CAAA,CAAA,MAAQ;AACN,UAAA,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,UAAA,IAAc,GAAG,CAAA;AAAA,QACxC;AACA,QAAA,MAAM,KAAA,GAAQ,aAAa,OAAO,CAAA;AAClC,QAAA,IAAI,CAAC,OAAO,OAAO,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,CAAA;AACnD,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,KAAA,CAAM,UAAA,CAAW,MAAM,UAAA,EAAY,KAAA,CAAM,KAAA,EAAO,KAAA,CAAM,SAAS,CAAA;AAC1F,QAAA,OAAO,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,EAAM,MAAM,CAAA;AAAA,MAChC;AAEA,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,cAAc,CAAA,EAAG;AACjC,QAAA,IAAI,CAAC,OAAA,CAAQ,iBAAA,EAAmB,OAAO,IAAA,CAAK,IAAA,CAAK,KAAA,EAAO,CAAA,GAAA,EAAM,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC,QAAQ,GAAG,CAAA;AACjG,QAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA,IAAK,EAAA;AAC3C,QAAA,MAAM,MAAA,GAAS,MAAM,sBAAA,CAAuB,OAAA,CAAQ,mBAAmB,KAAK,CAAA;AAC5E,QAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,IAAA,CAAK,IAAA,CAAK,KAAA,EAAO,CAAA,GAAA,EAAM,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC,CAAA,IAAA,CAAA,EAAQ,GAAG,CAAA;AAC9E,QAAA,IAAI,OAAA,CAAQ,WAAW,KAAA,EAAO;AAE5B,UAAA,MAAM,MAAA,GAAS,CAAA,GAAA,EAAM,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAC9C,UAAA,OAAO,IAAA;AAAA,YACL,IAAA,CAAK,KAAA;AAAA,YACL,CAAA,8BAAA,EAAiC,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC,CAAA,iCAAA,EAAoC,UAAA,CAAW,MAAM,CAAC,CAAA,iIAAA,EAAoI,UAAA,CAAW,IAAA,CAAK,MAAM,CAAC,CAAA,gBAAA;AAAA,WAC5Q;AAAA,QACF;AACA,QAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAQ,OAAO,KAAK,EAAE,KAAA,EAAO,oBAAA,EAAqB,EAAG,GAAG,CAAA;AAC/E,QAAA,MAAM,QAAQ,KAAA,CAAM,WAAA,CAAY,MAAA,CAAO,MAAA,IAAU,MAAM,MAAA,CAAO,MAAA,GAAS,IAAA,GAAQ,MAAA,CAAO,aAAa,IAAA,EAAO,MAAA,CAAO,QAAA,EAAU,MAAA,CAAO,YAAY,IAAI,CAAA;AAClJ,QAAA,OAAO,IAAA,CAAK,KAAK,KAAA,EAAO,CAAA,GAAA,EAAM,WAAW,IAAA,CAAK,IAAI,CAAC,CAAA,IAAA,CAAM,CAAA;AAAA,MAC3D;AAEA,MAAA,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,IAAe,GAAG,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,GAAA,CAAI,0BAA0B,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,IAAA,GAAO,OAAO,CAAA,IAAA,EAAO,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,KAAA,CAAM,EAAE,CAAA,CAAE,CAAC,CAAC,CAAA,CAAE,CAAA;AAChH,MAAA,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,IAAkB,GAAG,CAAA;AAAA,IAC5C;AAAA,EACF,CAAA;AACF;;;AC9GA,SAAS,YAAA,CAAa,IAAY,MAAA,EAAqC;AACrE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,MAAA,EAAQ,SAAS,OAAO,MAAA,CAAO,OAAO,MAAA,IAAU,IAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AACxE,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,MAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC5C,MAAA,OAAA,EAAQ;AAAA,IACV,GAAG,EAAE,CAAA;AACL,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,MAAA,CAAO,MAAA,EAAQ,MAAA,IAAU,IAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAAA,IAC/C,CAAA;AACA,IAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,EAC3D,CAAC,CAAA;AACH;AAGO,SAAS,kBAAkB,OAAA,EAA0C;AAC1E,EAAA,IAAI,EAAE,OAAA,CAAQ,SAAA,GAAY,CAAA,CAAA,IAAM,CAAC,MAAA,CAAO,QAAA,CAAS,OAAA,CAAQ,SAAS,CAAA,EAAG,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAC7I,EAAA,MAAM,GAAA,GAAM,MAAO,OAAA,CAAQ,SAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,IAAA,CAAK,GAAA;AAChC,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,YAAA;AAC/B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO;AAAA,IACL,MAAM,QAAQ,MAAA,EAAQ;AACpB,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,IAAO,IAAI,CAAA;AAC/B,MAAA,IAAA,GAAO,EAAA,GAAK,GAAA;AACZ,MAAA,MAAM,IAAA,GAAO,KAAK,GAAA,EAAI;AACtB,MAAA,IAAI,IAAA,GAAO,CAAA,EAAG,MAAM,KAAA,CAAM,MAAM,MAAM,CAAA;AAAA,IACxC;AAAA,GACF;AACF;;;ACEO,IAAM,UAAA,GAAa;AAGnB,IAAM,yBAAA,GAA4B;AAiBlC,SAAS,eAAe,OAAA,EAAuC;AACpE,EAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,IAC7B,SAAA,EAAW,QAAQ,SAAA,IAAa,yBAAA;AAAA,IAChC,GAAI,QAAQ,GAAA,GAAM,EAAE,KAAK,OAAA,CAAQ,GAAA,KAAQ,EAAC;AAAA,IAC1C,GAAI,QAAQ,KAAA,GAAQ,EAAE,OAAO,OAAA,CAAQ,KAAA,KAAU;AAAC,GACjD,CAAA;AACD,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,MAAM,IAAA,CAAK,OAAA,EAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAG;AACnC,MAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,EAAQ,MAAM,IAAI,UAAU,+BAA+B,CAAA;AACxE,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA,MAC3B,CAAA,CAAA,MAAQ;AACN,QAAA,MAAM,IAAI,SAAA,CAAU,WAAA,EAAa,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,MACtD;AACA,MAAA,MAAM,WAAW,MAAM,UAAA;AAAA,QACrB,OAAA;AAAA,QACA,QAAQ,QAAA,IAAY,UAAA;AAAA,QACpB;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,OAAA,CAAQ,MAAM,CAAA,CAAA;AAAA,YACvC,cAAA,EAAgB,kBAAA;AAAA,YAChB,mBAAmB,OAAA,CAAQ;AAAA,WAC7B;AAAA,UACA,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,YACnB,MAAM,OAAA,CAAQ,IAAA;AAAA,YACd,IAAI,OAAA,CAAQ,EAAA;AAAA,YACZ,SAAS,OAAA,CAAQ,OAAA;AAAA,YACjB,MAAM,OAAA,CAAQ,IAAA;AAAA,YACd,MAAM,OAAA,CAAQ,IAAA;AAAA,YACd,GAAI,QAAQ,OAAA,GAAU,EAAE,UAAU,OAAA,CAAQ,OAAA,KAAY,EAAC;AAAA,YACvD,GAAI,OAAA,CAAQ,OAAA,IAAW,MAAA,CAAO,KAAK,OAAA,CAAQ,OAAO,CAAA,CAAE,MAAA,GAAS,EAAE,OAAA,EAAS,OAAA,CAAQ,OAAA,KAAY,EAAC;AAAA,YAC7F,GAAI,QAAQ,IAAA,EAAM,MAAA,GAAS,EAAE,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAK,GAAI;AAAC,WACtD;AAAA,SACH;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,SAAS,EAAA,EAAI;AACf,QAAA,IAAI,EAAA,GAAoB,IAAA;AACxB,QAAA,IAAI;AACF,UAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,UAAA,IAAI,OAAO,KAAK,EAAA,KAAO,QAAA,IAAY,KAAK,EAAA,CAAG,MAAA,IAAU,GAAA,EAAK,EAAA,GAAK,IAAA,CAAK,EAAA;AAAA,QACtE,CAAA,CAAA,MAAQ;AAAA,QAER;AACA,QAAA,OAAO,EAAE,EAAA,EAAG;AAAA,MACd;AAEA,MAAA,IAAI,IAAA;AACJ,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,QAAA,IAAI,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,IAAY,gBAAA,CAAiB,KAAK,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,MACrF,CAAA,CAAA,MAAQ;AACN,QAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,MAClB;AACA,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,IAAA,KAAS,4BAAA,EAA8B;AAEpE,QAAA,MAAM,IAAI,UAAU,sIAAA,EAAwI;AAAA,UAC1J,SAAA,EAAW,KAAA;AAAA,UACX,MAAA,EAAQ;AAAA,SACT,CAAA;AAAA,MACH;AACA,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,IAAA,KAAS,gCAAA,EAAkC;AACxE,QAAA,MAAM,IAAI,SAAA,CAAU,sDAAA,EAAwD,EAAE,SAAA,EAAW,MAAM,iBAAA,EAAmB,CAAA,EAAG,MAAA,EAAQ,GAAA,EAAK,CAAA;AAAA,MACpI;AACA,MAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,QAAA,CAAS,MAAM,CAAA;AACnD,MAAA,MAAM,IAAI,SAAA,CAAU,CAAA,qBAAA,EAAwB,QAAA,CAAS,MAAM,CAAA,EAAG,IAAA,GAAO,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAA,EAAI;AAAA,QACxF,SAAA;AAAA,QACA,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,GAAI,QAAA,CAAS,MAAA,KAAW,OAAO,QAAA,CAAS,MAAA,KAAW,MAAM,EAAE,iBAAA,EAAmB,iBAAA,CAAkB,QAAA,CAAS,QAAQ,GAAA,CAAI,aAAa,GAAG,EAAE,CAAA,KAAM;AAAC,OAC/I,CAAA;AAAA,IACH;AAAA,GACF;AACF","file":"server.js","sourcesContent":["/**\n * The database half, through the service role: what the runner and the\n * handler call. `supabaseNotificationStore(admin)` wraps migration 0001's\n * functions; tests (and an app with another queue) pass their own.\n */\nimport type { Channel, NotificationTypeRow } from \"../core/registry\";\n\ntype RpcResult = { data: unknown; error: { message?: string; code?: string | null } | null };\n\n/** The part of a Supabase client the store uses, signed in with the service role (`SUPABASE_SECRET_KEY`). */\nexport interface RpcClient {\n // Loose on purpose: a client typed by an app's Database narrows `rpc`'s names to its own.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n rpc: (fn: any, args?: any) => PromiseLike<RpcResult>;\n}\n\n/** A database refusal, with its SQLSTATE; never the row it refused. */\nexport class NotificationStoreError extends Error {\n constructor(\n readonly operation: string,\n readonly code: string | null,\n ) {\n super(`${operation} failed${code ? ` (${code})` : \"\"}`);\n this.name = \"NotificationStoreError\";\n }\n}\n\nexport interface ClaimedDevice {\n id: string;\n token: string;\n platform: string;\n}\n\nexport interface SenderIdentity {\n from_name: string | null;\n reply_to: string | null;\n domain: string | null;\n domain_verified: boolean;\n}\n\n/** One claimed send job, as genesis_notifications_claim hands it over. */\nexport interface ClaimedJob {\n id: string;\n claim_id: string;\n notification_id: string;\n channel: Exclude<Channel, \"in_app\">;\n attempt: number;\n deferrals: number;\n redelivery: boolean;\n type: string;\n category: string;\n tenant_id: string | null;\n user_id: string | null;\n email: string | null;\n recipient_name: string | null;\n data: Record<string, unknown>;\n thread_key: string | null;\n collapse_key: string | null;\n badge: number | null;\n brand: Record<string, unknown> | null;\n transactional: boolean;\n /** Preferences can't turn it off, so its email has no unsubscribe link. */\n critical: boolean;\n push_privacy: \"default\" | \"generic\";\n ios_category: string | null;\n hide_previews: boolean;\n devices: ClaimedDevice[] | null;\n sender: SenderIdentity | null;\n rendered: RenderedEmail | null;\n rendered_hash: string | null;\n secret: string | null;\n}\n\n/** An email as first rendered: what every retry sends. */\nexport interface RenderedEmail {\n from: string;\n to: string[];\n reply_to?: string;\n subject: string;\n html: string;\n text: string;\n headers?: Record<string, string>;\n tags?: { name: string; value: string }[];\n}\n\nexport type SettleOutcome =\n | { status: \"sent\"; provider_id?: string | null }\n | { status: \"skipped\"; reason: string }\n | { status: \"filtered\" }\n | { status: \"failed\"; error: string; provider_id?: string | null }\n | { status: \"retry\"; error: string; retry_at: string; counted: boolean; provider_id?: string | null };\n\nexport interface PushTicketRow {\n device_id: string;\n ticket_id: string | null;\n status: \"sent\" | \"failed\";\n error: string | null;\n}\n\nexport interface NotificationStore {\n /** `maxAttempts`: a job whose lease ran out that many times fails (`lease_expired`) instead of coming back. */\n claim(channel: \"push\" | \"email\", limit: number, leaseSeconds: number, maxAttempts?: number): Promise<ClaimedJob[]>;\n storeRendered(id: string, claimId: string, rendered: RenderedEmail, hash: string): Promise<{ rendered: RenderedEmail; hash: string } | null>;\n settle(id: string, claimId: string, outcome: SettleOutcome): Promise<boolean>;\n /** Keeps the provider's id the moment a send returns, before it settles, so an early webhook finds the job. False when the claim is gone. */\n recordProviderId(id: string, claimId: string, providerId: string): Promise<boolean>;\n pushTickets(deliveryId: string, tickets: PushTicketRow[]): Promise<void>;\n receiptsClaim(limit: number): Promise<{ id: number; ticket_id: string }[]>;\n receiptsSettle(results: { id: number; status: \"delivered\" | \"failed\"; error: string | null }[]): Promise<void>;\n emailEvent(providerId: string, event: \"delivered\" | \"bounced\" | \"complained\", permanent: boolean): Promise<boolean>;\n /** For a login (`userId`), or someone with none by the SHA-256 of their address; for one tenant's mail (`tenantId`), or mail sent for none. */\n unsubscribe(userId: string | null, emailHash: string | null, category: string, tenantId: string | null): Promise<boolean>;\n syncTypes(rows: NotificationTypeRow[]): Promise<number>;\n}\n\n/**\n * The store over migration 0001's functions.\n *\n * @stability Beta\n */\nexport function supabaseNotificationStore(client: RpcClient): NotificationStore {\n const call = async <T>(fn: string, args: Record<string, unknown>): Promise<T> => {\n const { data, error } = await client.rpc(fn, args);\n if (error) throw new NotificationStoreError(fn, error.code ?? null);\n return data as T;\n };\n return {\n claim: (channel, limit, leaseSeconds, maxAttempts = 8) =>\n call<ClaimedJob[]>(\"genesis_notifications_claim\", { p_channel: channel, p_limit: limit, p_lease_seconds: leaseSeconds, p_max_attempts: maxAttempts }).then((rows) => rows ?? []),\n async storeRendered(id, claimId, rendered, hash) {\n const rows = await call<{ rendered: RenderedEmail | null; rendered_hash: string | null }[]>(\"genesis_notifications_store_rendered\", { p_id: id, p_claim: claimId, p_rendered: rendered, p_hash: hash });\n const row = rows?.[0];\n return row?.rendered && row.rendered_hash ? { rendered: row.rendered, hash: row.rendered_hash } : null;\n },\n settle: (id, claimId, outcome) => call<boolean>(\"genesis_notifications_settle\", { p_id: id, p_claim: claimId, p_outcome: outcome }).then(Boolean),\n recordProviderId: (id, claimId, providerId) =>\n call<boolean>(\"genesis_notifications_record_provider_id\", { p_id: id, p_claim: claimId, p_provider_id: providerId }).then(Boolean),\n async pushTickets(deliveryId, tickets) {\n await call(\"genesis_notifications_push_tickets\", { p_delivery: deliveryId, p_tickets: tickets });\n },\n receiptsClaim: (limit) => call<{ id: number; ticket_id: string }[]>(\"genesis_notifications_receipts_claim\", { p_limit: limit }).then((r) => r ?? []),\n async receiptsSettle(results) {\n await call(\"genesis_notifications_receipts_settle\", { p_results: results });\n },\n emailEvent: (providerId, event, permanent) => call<boolean>(\"genesis_notifications_email_event\", { p_provider_id: providerId, p_event: event, p_permanent: permanent }).then(Boolean),\n unsubscribe: (userId, emailHash, category, tenantId) =>\n call<boolean>(\"genesis_notifications_unsubscribe\", { p_user_id: userId, p_email_hash: emailHash, p_category: category, p_tenant_id: tenantId }).then(Boolean),\n syncTypes: (rows) => call<number>(\"genesis_notifications_sync_types\", { p_rows: rows }),\n };\n}\n","/**\n * `notify()`: what a domain calls, from its server. It checks the input\n * (the registry knows the type, the recipients are well formed, `data`\n * holds no secret) and calls `genesis_notify`, which decides channels from\n * preferences and queues the sends. A domain's own SQL can call\n * `genesis_notify` directly with the same arguments.\n */\nimport type { NotificationRegistry } from \"../core/registry\";\nimport type { NotifyInput, NotifyResult, Recipient } from \"../core/types\";\nimport { assertNoSecrets, recipientsToJson } from \"../core/validate\";\nimport { NotificationStoreError, type RpcClient } from \"./store\";\n\nexport interface NotifyOptions {\n /** Checks the type against the registry before the database does. */\n registry?: NotificationRegistry;\n}\n\n/**\n * Queue a notification for each recipient. Resolves one result per recipient\n * that exists (a deleted login is left out); the same idempotency key again\n * (for the same type and tenant) resolves `created: false` and sends nothing more.\n *\n * @stability Beta\n */\nexport async function notify<K extends string>(client: RpcClient, input: NotifyInput<K>, options: NotifyOptions = {}): Promise<NotifyResult[]> {\n const t = options.registry?.type(input.type);\n if (input.secret !== undefined && t && !t.secretLink) throw new Error(`notify: type ${input.type} carries no secret link`);\n // A one-time secret is one person's: never copied onto another recipient's email.\n if (input.secret !== undefined && input.recipients.length !== 1) throw new Error(\"notify: a secret goes to exactly one recipient\");\n if (!input.idempotencyKey || input.idempotencyKey.length > 200) throw new Error(\"notify: an idempotency key of 1 to 200 characters is required\");\n assertNoSecrets(input.data);\n if (input.badge !== undefined && (!Number.isInteger(input.badge) || input.badge < 0)) throw new Error(\"notify: badge is a whole number, 0 or more\");\n const { data, error } = await client.rpc(\"genesis_notify\", {\n p_type: input.type,\n p_recipients: recipientsToJson(input.recipients),\n p_data: input.data ?? {},\n p_idempotency_key: input.idempotencyKey,\n p_thread_key: input.threadKey ?? null,\n p_collapse_key: input.collapseKey ?? null,\n p_badge: input.badge ?? null,\n p_tenant_id: input.tenantId ?? null,\n p_brand: input.brand ?? null,\n p_secret: input.secret ?? null,\n });\n if (error) throw new NotificationStoreError(\"genesis_notify\", error.code ?? null);\n return ((data as { notification_id: string; recipient_key: string; created: boolean }[] | null) ?? []).map((r) => ({\n notificationId: r.notification_id,\n recipientKey: r.recipient_key,\n created: r.created,\n }));\n}\n\nexport interface CancelInput {\n /** The type the notifications were made with: a key is a domain's, per type. */\n type: string;\n /** The key the notifications were made with. */\n idempotencyKey: string;\n /** The tenant they were made for. Default: those made with no tenant (never another tenant's). */\n tenantId?: string | null;\n /** Only these recipients (as `notify()` takes them). Default: everyone the key reached. */\n recipients?: readonly Recipient[];\n /** Archive the feed rows too. Default false. */\n archive?: boolean;\n}\n\n/**\n * Stop what hasn't gone out for a type, tenant and idempotency key (a release\n * rolled back, an event undone): every pending send job is marked cancelled\n * and never claimed. A job already sent is left alone; one being sent right\n * now ends cancelled unless that attempt goes through (it is never retried).\n * With `archive`, the feed rows are archived too.\n *\n * @stability Beta\n */\nexport async function cancelNotification(client: RpcClient, input: CancelInput): Promise<{ cancelled: number; archived: number }> {\n if (!input.type) throw new Error(\"cancelNotification: a type is required\");\n if (!input.idempotencyKey || input.idempotencyKey.length > 200) throw new Error(\"cancelNotification: an idempotency key of 1 to 200 characters is required\");\n const { data, error } = await client.rpc(\"genesis_notifications_cancel\", {\n p_type: input.type,\n p_idempotency_key: input.idempotencyKey,\n p_tenant_id: input.tenantId ?? null,\n p_recipients: input.recipients ? recipientsToJson(input.recipients) : null,\n p_archive: input.archive ?? false,\n });\n if (error) throw new NotificationStoreError(\"genesis_notifications_cancel\", error.code ?? null);\n const row = (Array.isArray(data) ? data[0] : data) as { cancelled?: number; archived?: number } | undefined;\n return { cancelled: Number(row?.cancelled ?? 0), archived: Number(row?.archived ?? 0) };\n}\n\n/**\n * Mirror the registry into `notification_types` (run it on deploy, or the\n * server's first request): types left out are retired, never deleted.\n *\n * @stability Beta\n */\nexport async function syncNotificationTypes(client: RpcClient, registry: NotificationRegistry): Promise<number> {\n const { data, error } = await client.rpc(\"genesis_notifications_sync_types\", { p_rows: registry.toTypeRows() });\n if (error) throw new NotificationStoreError(\"genesis_notifications_sync_types\", error.code ?? null);\n return Number(data ?? 0);\n}\n","/**\n * What an email says, and the checks every one passes: subjects and names on\n * one line, addresses well formed, every value escaped in the default body.\n */\nimport type { RenderedText } from \"../core/registry\";\nimport type { NotificationBrand } from \"../core/types\";\n\nexport interface EmailContent {\n subject: string;\n html: string;\n text: string;\n}\n\nexport interface EmailTemplateContext {\n type: string;\n category: string;\n /** The notification's `data`. */\n data: Record<string, unknown>;\n /** What the registry's render says (title, body, url): a starting point. */\n text: RenderedText;\n recipient: { email: string; name: string | null };\n /** The caller's brand over the tenant's default. */\n brand: NotificationBrand;\n tenantId: string | null;\n transactional: boolean;\n /**\n * Put this where the secret link goes (`href={secretLink}`): the runner\n * swaps in the real link at send time, so the stored render never holds it.\n * Null when the type has none.\n */\n secretLink: string | null;\n /** The signed one-click unsubscribe link, for the footer. Null for transactional mail. */\n unsubscribeUrl: string | null;\n /** The app's absolute link for `text.url`, when `appUrl` is set. */\n url: string | null;\n}\n\n/** A type's email: React Email (`@marktiderman/genesis-notifications/email`), or any HTML. */\nexport type EmailTemplate = (context: EmailTemplateContext) => EmailContent | Promise<EmailContent>;\n\nexport function escapeHtml(text: string): string {\n return text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\").replace(/'/g, \"'\");\n}\n\n/** A header value on one line: control characters and line separators become spaces; cut to `max`. */\nexport function headerSafe(text: string, max = 200): string {\n // eslint-disable-next-line no-control-regex\n const flat = text.replace(/[\\u0000-\\u001f\\u007f\\u2028\\u2029]+/g, \" \").replace(/\\s+/g, \" \").trim();\n return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;\n}\n\n/** A bare address safe as a header value. */\nexport function isSafeAddress(value: unknown): value is string {\n // eslint-disable-next-line no-control-regex\n return typeof value === \"string\" && value.length <= 254 && /^[^\\s@<>,;:\"()[\\]\\\\]+@[^\\s@<>,;:\"()[\\]\\\\]+\\.[^\\s@<>,;:\"()[\\]\\\\]+$/.test(value) && !/[\\u0000-\\u001f\\u007f]/.test(value);\n}\n\n/** A display name with nothing that could break out of the From header (or need quoting): no quotes, brackets, \":\", \";\", \"@\", \",\" or \"\\\\\". */\nexport function displayName(name: string): string {\n return headerSafe(name.replace(/[\"<>()[\\]:;@,\\\\]/g, \"\"), 100);\n}\n\n/** The default email when a type has no template: its title, body and a button, every value escaped. */\nexport const defaultEmailTemplate: EmailTemplate = (ctx) => {\n const title = ctx.text.title;\n const body = ctx.text.body ?? \"\";\n const link = ctx.secretLink ?? ctx.url;\n const accent = typeof ctx.brand.color === \"string\" && /^#[0-9a-fA-F]{3,8}$/.test(ctx.brand.color) ? ctx.brand.color : \"#111827\";\n const from = ctx.brand.name ? escapeHtml(ctx.brand.name) : \"\";\n const p = \"font-family:sans-serif;font-size:15px;line-height:1.5;margin:0 0 16px;color:#111827\";\n const html =\n `<!doctype html><html><body style=\"margin:0;padding:24px;background:#f9fafb\">` +\n `<div style=\"max-width:560px;margin:0 auto;background:#ffffff;border-radius:8px;padding:24px\">` +\n (from ? `<p style=\"${p};font-weight:600;color:${accent}\">${from}</p>` : \"\") +\n `<p style=\"${p};font-size:18px;font-weight:600\">${escapeHtml(title)}</p>` +\n (body ? `<p style=\"${p};white-space:pre-wrap\">${escapeHtml(body)}</p>` : \"\") +\n (link ? `<p style=\"${p}\"><a href=\"${escapeHtml(link)}\" style=\"display:inline-block;background:${accent};color:#ffffff;padding:10px 16px;border-radius:6px;text-decoration:none\">Open</a></p>` : \"\") +\n (ctx.unsubscribeUrl ? `<p style=\"${p};font-size:12px;color:#6b7280\"><a href=\"${escapeHtml(ctx.unsubscribeUrl)}\" style=\"color:#6b7280\">Unsubscribe</a> from these emails.</p>` : \"\") +\n `</div></body></html>`;\n const text = [from, title, body, link ?? \"\", ctx.unsubscribeUrl ? `Unsubscribe: ${ctx.unsubscribeUrl}` : \"\"].filter(Boolean).join(\"\\n\\n\");\n return { subject: title, html, text };\n};\n\n/** JSON with sorted keys: the same bytes however a database reordered them. */\nexport function canonicalJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>).filter(([, v]) => v !== undefined).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(\",\")}}`;\n}\n","/**\n * A send failure with a message that is safe to store and log (a status and\n * a provider's error code, never content, an address or a secret). The\n * runner turns it into a retry or a failure.\n */\nexport class SendError extends Error {\n override name = \"SendError\";\n readonly retryable: boolean;\n readonly retryAfterSeconds: number | undefined;\n readonly status: number | undefined;\n constructor(message: string, options: { retryable?: boolean; retryAfterSeconds?: number; status?: number } = {}) {\n super(message.slice(0, 500));\n this.retryable = options.retryable ?? false;\n this.retryAfterSeconds = options.retryAfterSeconds;\n this.status = options.status;\n }\n}\n\n/** Whether an HTTP status is worth retrying unchanged: 408, 425, 429 and 5xx. */\nexport function isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n/** Seconds from a Retry-After header (delta-seconds or an HTTP date), else `fallback`. 1 s to a day. */\nexport function retryAfterSeconds(header: string | null | undefined, fallback = 60, now = Date.now()): number {\n const clamp = (s: number) => Math.min(86_400, Math.max(1, s));\n const value = header?.trim();\n if (value && /^\\d+$/.test(value)) return clamp(Number(value));\n if (value && /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$/.test(value)) {\n const at = Date.parse(value);\n if (Number.isFinite(at)) return clamp(Math.ceil((at - now) / 1000));\n }\n return fallback;\n}\n\n/** Every request gives up after this long. */\nexport const REQUEST_TIMEOUT_MS = 10_000;\n\nexport interface HttpDeps {\n /** Default the global `fetch`. */\n fetch?: typeof fetch;\n timeoutMs?: number;\n}\n\n/** `fetch` with a timeout, and the caller's abort signal too. */\nexport async function timedFetch(deps: HttpDeps, url: string, init: RequestInit, signal?: AbortSignal): Promise<Response> {\n const f = deps.fetch ?? fetch;\n const timeout = AbortSignal.timeout(deps.timeoutMs ?? REQUEST_TIMEOUT_MS);\n const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;\n try {\n return await f(url, { ...init, signal: combined });\n } catch (error) {\n const name = (error as { name?: string } | null)?.name;\n throw new SendError(name === \"TimeoutError\" || name === \"AbortError\" ? \"Timed out\" : \"Network error\", { retryable: true });\n }\n}\n\n/** Reads and drops a body we won't use (some runtimes leak a connection otherwise). */\nexport function discard(response: Response): void {\n response.body?.cancel().catch(() => {});\n}\n","/**\n * Is a sender domain the tenant's, and ready to send from? An ownership TXT\n * record holding the tenant's own token (so one tenant can't verify another's\n * domain), SPF on Resend's return-path host, DKIM at Resend's selector, and a\n * DMARC record, read through DNS over HTTPS so it runs anywhere `fetch` does.\n * The app's own shared sending domains (and anything under them) are never a\n * tenant's.\n */\nimport { timedFetch, type HttpDeps } from \"./errors\";\n\nexport interface DomainCheck {\n /** `_genesis-notifications.<domain>` holds `genesis-notifications-verify=<token>`. */\n ownership: boolean;\n spf: boolean;\n dkim: boolean;\n dmarc: boolean;\n /** The domain is (or is under) one of the app's own shared domains: never a tenant's. */\n reserved: boolean;\n /** Owned, all three records, and not reserved. */\n ok: boolean;\n}\n\nexport interface DomainCheckOptions extends HttpDeps {\n /** The sender's `domain_token` (from `notification_senders`; made when the domain was set). */\n token: string;\n /** The app's shared sending domains (`email.domains`): a tenant may never verify one, or one under it. */\n reservedDomains: readonly string[];\n /** Default Cloudflare's JSON resolver. */\n resolver?: string;\n /** Resend's DKIM selector. Default `resend`. */\n dkimSelector?: string;\n /** Where the SPF record lives. Default `send.<domain>` (Resend's return path). */\n spfHost?: string;\n /** What the SPF record includes. Default `amazonses.com` (Resend's). */\n spfInclude?: string;\n}\n\nconst DOMAIN = /^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/;\n\n/** The ownership record's host. */\nexport const SENDER_OWNERSHIP_HOST = \"_genesis-notifications\";\n\n/**\n * The TXT record a tenant adds to prove `domain` is theirs: show it beside\n * Resend's SPF and DKIM records in your sender settings.\n *\n * @stability Beta\n */\nexport function senderOwnershipRecord(domain: string, token: string): { type: \"TXT\"; name: string; value: string } {\n return { type: \"TXT\", name: `${SENDER_OWNERSHIP_HOST}.${domain}`, value: `genesis-notifications-verify=${token}` };\n}\n\n/** Whether `domain` is one of `reserved`, or under one. */\nexport function isReservedDomain(domain: string, reserved: readonly (string | undefined | null)[]): boolean {\n const d = domain.toLowerCase().replace(/\\.$/, \"\");\n return reserved.some((r) => {\n const x = r?.toLowerCase().replace(/\\.$/, \"\");\n return !!x && (d === x || d.endsWith(`.${x}`));\n });\n}\n\nasync function txt(options: DomainCheckOptions, name: string): Promise<string[]> {\n const url = new URL(options.resolver ?? \"https://cloudflare-dns.com/dns-query\");\n url.searchParams.set(\"name\", name);\n url.searchParams.set(\"type\", \"TXT\");\n const response = await timedFetch(options, url.toString(), { headers: { accept: \"application/dns-json\" } });\n if (!response.ok) return [];\n const json = (await response.json()) as { Answer?: { type?: number; data?: string }[] };\n return (json.Answer ?? [])\n .filter((a) => a.type === 16 && typeof a.data === \"string\")\n // A TXT answer comes quoted, maybe in several strings: \"v=spf1 \" \"include:…\"\n .map((a) => a.data!.replace(/\"\\s*\"/g, \"\").replace(/^\"|\"$/g, \"\"));\n}\n\n/**\n * Checks ownership, SPF, DKIM and DMARC for `domain`. Record the result with\n * `genesis_notifications_set_sender_dns(tenant, domain, token, ok ? 'verified' : 'failed', result)`\n * with the same token, so a check made before the tenant changed its domain changes nothing.\n *\n * @stability Beta\n */\nexport async function checkSenderDomain(domain: string, options: DomainCheckOptions): Promise<DomainCheck> {\n if (!DOMAIN.test(domain)) throw new Error(\"checkSenderDomain: not a domain name\");\n if (typeof options?.token !== \"string\" || !/^[0-9a-f]{32}$/.test(options.token)) throw new Error(\"checkSenderDomain: token is the sender's domain_token\");\n if (!Array.isArray(options.reservedDomains) || options.reservedDomains.length === 0) {\n throw new Error(\"checkSenderDomain: reservedDomains (the app's shared sending domains) is required\");\n }\n const reserved = isReservedDomain(domain, options.reservedDomains);\n if (reserved) return { ownership: false, spf: false, dkim: false, dmarc: false, reserved: true, ok: false };\n const include = options.spfInclude ?? \"amazonses.com\";\n const proof = senderOwnershipRecord(domain, options.token);\n const [owner, spf, dkim, dmarc] = await Promise.all([\n txt(options, proof.name),\n txt(options, options.spfHost ?? `send.${domain}`),\n txt(options, `${options.dkimSelector ?? \"resend\"}._domainkey.${domain}`),\n txt(options, `_dmarc.${domain}`),\n ]);\n const result = {\n ownership: owner.some((r) => r.trim() === proof.value),\n spf: spf.some((r) => /^v=spf1\\b/i.test(r) && r.toLowerCase().includes(`include:${include}`)),\n dkim: dkim.some((r) => /(^|;)\\s*p=[A-Za-z0-9+/=]{20,}/.test(r)),\n dmarc: dmarc.some((r) => /^v=DMARC1\\b/i.test(r)),\n reserved: false,\n };\n return { ...result, ok: result.ownership && result.spf && result.dkim && result.dmarc };\n}\n","/**\n * Expo's push service: https://docs.expo.dev/push-notifications/sending-notifications/\n *\n * POST /--/api/v2/push/send up to 100 messages; one ticket each, in order\n * POST /--/api/v2/push/getReceipts up to 1000 ticket ids; a receipt for each one ready\n *\n * Nothing here logs; an error names a status or Expo's error code, never a\n * token or a message.\n */\nimport { SendError, discard, isRetryableStatus, retryAfterSeconds, timedFetch, type HttpDeps } from \"./errors\";\n\nexport const EXPO_SEND_URL = \"https://exp.host/--/api/v2/push/send\";\nexport const EXPO_RECEIPTS_URL = \"https://exp.host/--/api/v2/push/getReceipts\";\n/** Expo takes up to 100 messages per send. */\nexport const EXPO_SEND_BATCH = 100;\n/** And up to 1000 ticket ids per receipts request. */\nexport const EXPO_RECEIPTS_BATCH = 1000;\n\n/** One message for Expo. */\nexport interface PushMessage {\n to: string;\n title?: string;\n subtitle?: string;\n body?: string;\n /** Reaches the app (`notification.request.content.data`): ids and the link, never content. */\n data?: Record<string, string | number | boolean | null>;\n sound?: \"default\" | null;\n badge?: number;\n /** Android: the notification channel (one per category). */\n channelId?: string;\n /** The device category whose actions it shows (Reply, Mark as read). */\n categoryId?: string;\n /** iOS: groups notifications on the lock screen (a conversation). */\n threadId?: string;\n /** Replaces an earlier one with the same id, in transit (and on screen on iOS). */\n collapseId?: string;\n /** Android: replaces one already on screen with the same tag. */\n tag?: string;\n ttl?: number;\n priority?: \"default\" | \"normal\" | \"high\";\n /** iOS: lets the app's Notification Service Extension change it (rich push). */\n mutableContent?: boolean;\n}\n\nexport type PushTicket = { status: \"ok\"; id: string } | { status: \"error\"; code: string };\nexport type PushReceipt = { status: \"ok\" } | { status: \"error\"; code: string };\n\n/** Sends pushes and reads receipts; `expoPushProvider` is the built-in one. */\nexport interface PushProvider {\n readonly name: string;\n /** At most `EXPO_SEND_BATCH` messages; one ticket per message, in order. Throws a `SendError` when the request failed. */\n send(messages: readonly PushMessage[], options?: { signal?: AbortSignal }): Promise<PushTicket[]>;\n /** Receipts for at most `EXPO_RECEIPTS_BATCH` tickets; ids with none yet are left out. */\n receipts(ids: readonly string[], options?: { signal?: AbortSignal }): Promise<Record<string, PushReceipt>>;\n}\n\nexport interface ExpoOptions extends HttpDeps {\n /** Expo's access token, for \"Enhanced Security for Push Notifications\" (turn it on). */\n accessToken?: string;\n sendUrl?: string;\n receiptsUrl?: string;\n}\n\n/** An Expo error code (`DeviceNotRegistered`), safe to store; anything else is `Unknown`. */\nexport function expoErrorCode(value: { details?: { error?: unknown } } | undefined): string {\n const code = value?.details?.error;\n return typeof code === \"string\" && /^[A-Za-z_]{1,64}$/.test(code) ? code : \"Unknown\";\n}\n\n/** Expo refused the request itself (a 4xx other than 429): resend its messages one by one. */\nexport function refusedOutright(error: unknown): boolean {\n return error instanceof SendError && error.status !== undefined && error.status >= 400 && error.status < 500 && error.status !== 429;\n}\n\n/**\n * Expo's push service as a `PushProvider`.\n *\n * @stability Beta\n */\nexport function expoPushProvider(options: ExpoOptions = {}): PushProvider {\n const headers = {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n ...(options.accessToken ? { authorization: `Bearer ${options.accessToken}` } : {}),\n };\n async function post(url: string, body: unknown, signal?: AbortSignal): Promise<unknown> {\n const response = await timedFetch(options, url, { method: \"POST\", headers, body: JSON.stringify(body) }, signal);\n if (!response.ok) {\n // The body of an error is never read: it can echo the request back.\n const retryAfter = response.status === 429 ? retryAfterSeconds(response.headers.get(\"retry-after\"), 30) : undefined;\n discard(response);\n throw new SendError(`Expo push answered HTTP ${response.status}`, {\n retryable: isRetryableStatus(response.status),\n status: response.status,\n ...(retryAfter !== undefined ? { retryAfterSeconds: retryAfter } : {}),\n });\n }\n try {\n return await response.json();\n } catch {\n throw new SendError(\"Expo push answered with something that isn't JSON\", { retryable: true });\n }\n }\n return {\n name: \"expo\",\n async send(messages, { signal } = {}) {\n if (messages.length > EXPO_SEND_BATCH) throw new Error(`expoPushProvider: at most ${EXPO_SEND_BATCH} messages`);\n if (messages.length === 0) return [];\n const json = (await post(options.sendUrl ?? EXPO_SEND_URL, messages, signal)) as { data?: unknown };\n const tickets = Array.isArray(json?.data) ? (json.data as { status?: string; id?: unknown; details?: { error?: unknown } }[]) : [];\n // A short or malformed answer: the messages without a ticket failed, never \"sent\".\n return messages.map((_, i): PushTicket => {\n const t = tickets[i];\n if (t?.status === \"ok\" && typeof t.id === \"string\" && t.id.length > 0 && t.id.length <= 128) return { status: \"ok\", id: t.id };\n if (t?.status === \"error\") return { status: \"error\", code: expoErrorCode(t) };\n return { status: \"error\", code: \"NoTicket\" };\n });\n },\n async receipts(ids, { signal } = {}) {\n if (ids.length > EXPO_RECEIPTS_BATCH) throw new Error(`expoPushProvider: at most ${EXPO_RECEIPTS_BATCH} ids`);\n if (ids.length === 0) return {};\n const json = (await post(options.receiptsUrl ?? EXPO_RECEIPTS_URL, { ids }, signal)) as { data?: unknown };\n const data = json?.data && typeof json.data === \"object\" && !Array.isArray(json.data) ? (json.data as Record<string, { status?: string; details?: { error?: unknown } }>) : {};\n const out: Record<string, PushReceipt> = {};\n for (const id of ids) {\n const r = data[id];\n if (r?.status === \"ok\") out[id] = { status: \"ok\" };\n else if (r?.status === \"error\") out[id] = { status: \"error\", code: expoErrorCode(r) };\n }\n return out;\n },\n };\n}\n","/**\n * Secret links: a link whose query is a credential (a one-time sign-in, an\n * invite, a response's edit link). They never go in `notifications.data` or\n * a push. The domain stores a reference (`{ response_id }`) and, for a token\n * that exists only hashed, passes the plaintext as `notify({ secret })`; the\n * runner builds the link at send time, for the email only.\n */\n\n/** What a secret link is built from. */\nexport interface SecretLinkRef {\n /** The notification's `data` (references, never secrets). */\n data: Record<string, unknown>;\n /** The plaintext from `notify({ secret })`, decrypted for this send only. */\n secret: string | null;\n notificationId: string;\n tenantId: string | null;\n}\n\n/** Type key → how its link is built. A builder signs the link with its own key, or puts `secret` in it. */\nexport type SecretLinks = Readonly<Record<string, (ref: SecretLinkRef) => string | Promise<string>>>;\n\n/** No builder for a type whose emails need one, or a builder that gave back something that isn't an https link. */\nexport class SecretLinkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SecretLinkError\";\n }\n}\n\n/**\n * The secret link for one notification, built server-side at send time.\n * Throws a `SecretLinkError` when there is no builder for `type` or it gives\n * something other than an `https:` URL (`http:` only for localhost).\n *\n * @stability Beta\n */\nexport async function resolveSecretLink(links: SecretLinks, type: string, ref: SecretLinkRef): Promise<string> {\n const build = Object.prototype.hasOwnProperty.call(links, type) ? links[type] : undefined;\n if (!build) throw new SecretLinkError(`No secret link builder for type ${type}`);\n const link = await build(ref);\n let url: URL;\n try {\n url = new URL(link);\n } catch {\n throw new SecretLinkError(`The secret link for type ${type} is not a URL`);\n }\n const local = url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\";\n if (url.protocol !== \"https:\" && !(local && url.protocol === \"http:\")) throw new SecretLinkError(`The secret link for type ${type} is not https`);\n return url.toString();\n}\n\n/** What a template puts where the secret link goes; the runner swaps it in at send time, so the stored render never holds it. */\nexport const SECRET_LINK_PLACEHOLDER = \"https://genesis-secret-link.invalid/\";\n","/** Small WebCrypto helpers (Node 20+, Deno, browsers, workers). */\nconst encoder = new TextEncoder();\n\nexport function base64url(bytes: Uint8Array): string {\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nexport function fromBase64url(text: string): Uint8Array {\n const s = text.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const bin = atob(s + \"=\".repeat((4 - (s.length % 4)) % 4));\n return Uint8Array.from(bin, (c) => c.charCodeAt(0));\n}\n\nexport function fromBase64(text: string): Uint8Array {\n const bin = atob(text);\n return Uint8Array.from(bin, (c) => c.charCodeAt(0));\n}\n\nexport function toBase64(bytes: Uint8Array): string {\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s);\n}\n\nexport async function hmacSha256(key: Uint8Array | string, message: string): Promise<Uint8Array> {\n const raw = typeof key === \"string\" ? encoder.encode(key) : key;\n const k = await crypto.subtle.importKey(\"raw\", raw as BufferSource, { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n return new Uint8Array(await crypto.subtle.sign(\"HMAC\", k, encoder.encode(message)));\n}\n\nexport async function sha256Hex(text: string): Promise<string> {\n const digest = new Uint8Array(await crypto.subtle.digest(\"SHA-256\", encoder.encode(text)));\n return [...digest].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Equal bytes, in time that doesn't depend on where they differ. */\nexport function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i]! ^ b[i]!;\n return diff === 0;\n}\n","/**\n * One-click unsubscribe (RFC 8058) for mail that isn't transactional: the\n * `List-Unsubscribe` and `List-Unsubscribe-Post` headers, and a signed token\n * that names who, which category and which tenant, so the endpoint needs no\n * sign-in. The token never carries an address: someone with no login is named\n * by the SHA-256 of their lower-cased address (what the suppression list keeps).\n * The unsubscribe is for the tenant the email came from, not every tenant.\n */\nimport { base64url, fromBase64url, hmacSha256, sha256Hex, timingSafeEqual } from \"./crypto\";\n\nexport interface UnsubscribeClaims {\n /** The login, when the recipient has one. */\n userId?: string;\n /** SHA-256 (hex) of the lower-cased, trimmed address, when they have none (`emailHash`). */\n emailHash?: string;\n category: string;\n /** The tenant the email came from; the unsubscribe holds for it only. */\n tenantId?: string;\n}\n\n/** What the suppression list keys an address by: SHA-256 of it lower-cased and trimmed (as `genesis_notifications_email_hash`). */\nexport function emailHash(email: string): Promise<string> {\n return sha256Hex(email.trim().toLowerCase());\n}\n\n/** The secret must be at least 32 characters. */\nfunction checkSecret(secret: string) {\n if (typeof secret !== \"string\" || secret.length < 32) throw new Error(\"The unsubscribe secret must be at least 32 characters\");\n}\n\n/** A signed, URL-safe token for `claims`. */\nexport async function createUnsubscribeToken(secret: string, claims: UnsubscribeClaims): Promise<string> {\n checkSecret(secret);\n if (claims.emailHash !== undefined && !/^[0-9a-f]{64}$/.test(claims.emailHash)) throw new Error(\"createUnsubscribeToken: emailHash is a SHA-256 hex digest (emailHash(address))\");\n const payload = base64url(new TextEncoder().encode(JSON.stringify({ u: claims.userId, h: claims.emailHash, c: claims.category, t: claims.tenantId, v: 2 })));\n const sig = base64url(await hmacSha256(secret, `unsubscribe.${payload}`));\n return `${payload}.${sig}`;\n}\n\n/** The claims of a token this secret signed, or null for anything else. */\nexport async function verifyUnsubscribeToken(secret: string, token: string): Promise<UnsubscribeClaims | null> {\n checkSecret(secret);\n if (typeof token !== \"string\" || token.length > 2000) return null;\n const [payload, sig, extra] = token.split(\".\");\n if (!payload || !sig || extra !== undefined) return null;\n let given: Uint8Array;\n try {\n given = fromBase64url(sig);\n } catch {\n return null;\n }\n const expected = await hmacSha256(secret, `unsubscribe.${payload}`);\n if (!timingSafeEqual(given, expected)) return null;\n try {\n const raw = JSON.parse(new TextDecoder().decode(fromBase64url(payload))) as { u?: unknown; h?: unknown; c?: unknown; t?: unknown; v?: unknown };\n if (raw.v !== 2 || typeof raw.c !== \"string\") return null;\n const hash = typeof raw.h === \"string\" && /^[0-9a-f]{64}$/.test(raw.h) ? raw.h : undefined;\n if (typeof raw.u !== \"string\" && !hash) return null;\n return {\n category: raw.c,\n ...(typeof raw.u === \"string\" ? { userId: raw.u } : {}),\n ...(hash ? { emailHash: hash } : {}),\n ...(typeof raw.t === \"string\" ? { tenantId: raw.t } : {}),\n };\n } catch {\n return null;\n }\n}\n\n/** The RFC 8058 headers for one email. `url` is your unsubscribe endpoint; the token goes in its query. */\nexport function unsubscribeHeaders(url: string, token: string): Record<string, string> {\n const link = new URL(url);\n link.searchParams.set(\"t\", token);\n return {\n \"List-Unsubscribe\": `<${link.toString()}>`,\n \"List-Unsubscribe-Post\": \"List-Unsubscribe=One-Click\",\n };\n}\n","/**\n * `createNotificationRunner`: sends what `notify()` queued, on the shared\n * runner (`@marktiderman/genesis-notifications/runner`). Run `run()` every\n * minute and `checkReceipts()` every 15 minutes (pg_cron with pg_net calling\n * the handler, or any scheduler).\n *\n * Push: the claimed jobs' devices in Expo batches of 100 (a job's devices\n * never split across two), each batch sent, its tickets recorded and its jobs\n * settled on its own, and no batch started once its lease is about to run\n * out (those jobs come straight back), with `threadId`,\n * `categoryId`, `badge`, a `collapseId` (the collapse key, else the\n * notification id, so a resend after a timeout replaces rather than repeats),\n * and neutral text when the person hides previews or the type says so. The\n * payload's `data` is the link, the notification id and the type, never the\n * notification's own `data`. Tickets are recorded per device; a\n * `DeviceNotRegistered` forgets the device.\n *\n * Email: rendered once at first claim and stored, so every retry sends the\n * same bytes with the same idempotency key. From the tenant's verified\n * domain, or the app's shared one with the tenant's name and reply-to;\n * transactional and other mail from separate domains; RFC 8058 unsubscribe\n * headers on everything that isn't transactional; a secret link swapped in\n * at send time. The provider's id is kept the moment a send returns, before\n * the job settles, so a webhook that comes first finds it.\n *\n * Links (a type's `render().url`) must be paths on the app's own origin\n * (`/messages/42`): anything else (another host, `//host`, `javascript:`) is\n * left out of the push and the email.\n */\nimport type { NotificationRegistry } from \"../core/registry\";\nimport type { NotificationBrand } from \"../core/types\";\nimport { createRunner, failureOf, type JobOutcome, type JobResult, type RunReport } from \"../runner/index\";\nimport { canonicalJson, defaultEmailTemplate, displayName, escapeHtml, headerSafe, isSafeAddress, type EmailTemplate } from \"./email-render\";\nimport { REQUEST_TIMEOUT_MS, SendError } from \"./errors\";\nimport { isReservedDomain } from \"./dns\";\nimport { EXPO_RECEIPTS_BATCH, EXPO_SEND_BATCH, refusedOutright, type PushMessage, type PushProvider, type PushTicket } from \"./expo\";\nimport type { EmailProvider } from \"./resend\";\nimport { SECRET_LINK_PLACEHOLDER, resolveSecretLink, type SecretLinks } from \"./secret-links\";\nimport { sha256Hex } from \"./crypto\";\nimport type { ClaimedJob, NotificationStore, PushTicketRow, RenderedEmail, SettleOutcome } from \"./store\";\nimport { createUnsubscribeToken, emailHash, unsubscribeHeaders } from \"./unsubscribe\";\n\nexport interface EmailOptions {\n provider: EmailProvider;\n /** The from name when a tenant has none (\"Example\"). */\n appName: string;\n /**\n * The app's shared sending domains, verified in Resend: transactional mail\n * and other mail go from separate ones, so one's reputation never holds up\n * the other. `other` defaults to `transactional`.\n */\n domains: { transactional: string; other?: string };\n /** The address's local part. Default `notifications`. */\n localPart?: string;\n /** Type key → template. A type without one gets `defaultEmailTemplate`. */\n templates?: Readonly<Record<string, EmailTemplate>>;\n defaultTemplate?: EmailTemplate;\n /** How each secret-link type's link is built (`resolveSecretLink`). */\n secretLinks?: SecretLinks;\n /** Required when any type sends email that isn't transactional. */\n unsubscribe?: { url: string; secret: string };\n /**\n * The provider for one job. Default the app's `provider`. The seam for a\n * tenant-owned key later (read from Vault by id); not built in v1.\n */\n providerFor?: (job: ClaimedJob) => EmailProvider;\n}\n\nexport interface NotificationRunnerOptions {\n store: NotificationStore;\n registry: NotificationRegistry;\n push?: { provider: PushProvider };\n email?: EmailOptions;\n /** The app's origin, for absolute links in emails (`https://app.example.com`). */\n appUrl?: string;\n /** Jobs claimed per channel per run. Default 100. */\n batchSize?: number;\n /** How long a claim holds a job. Default 300 seconds. */\n leaseSeconds?: number;\n /**\n * How long one `run()` may take (your function's wall-clock limit, less a\n * margin). With it, a run claims again while the last claim was full, and\n * stops claiming once the time left is under the lease. Without it, a run\n * claims once per channel. At least `leaseSeconds`.\n */\n runBudgetMs?: number;\n maxAttempts?: number;\n maxDeferrals?: number;\n /** Safe lines only (ids, statuses, error codes). */\n log?: (message: string) => void;\n now?: () => number;\n}\n\nexport interface NotificationRunner {\n /** Sends what is due on every configured channel. */\n run(options?: { signal?: AbortSignal }): Promise<{ push: RunReport | null; email: RunReport | null }>;\n runPush(options?: { signal?: AbortSignal }): Promise<RunReport>;\n runEmail(options?: { signal?: AbortSignal }): Promise<RunReport>;\n /** Asks Expo for the receipts that are due and records them. */\n checkReceipts(options?: { signal?: AbortSignal; budgetMs?: number }): Promise<{ checked: number; settled: number }>;\n}\n\ntype Job = ClaimedJob & { attempt: number };\n\nfunction settleOf(outcome: JobOutcome, providerId: string | undefined): SettleOutcome {\n const id = providerId ? { provider_id: providerId } : {};\n switch (outcome.status) {\n case \"sent\":\n return { status: \"sent\", provider_id: providerId ?? null };\n case \"filtered\":\n return { status: \"filtered\" };\n case \"skipped\":\n return { status: \"skipped\", reason: /^[a-z_]{1,64}$/.test(outcome.reason) ? outcome.reason : \"not_sent\" };\n case \"failed\":\n return { status: \"failed\", error: outcome.error, ...id };\n case \"retry\":\n return { status: \"retry\", error: outcome.error, retry_at: outcome.retryAt.toISOString(), counted: outcome.counted, ...id };\n }\n}\n\n/** A link a type rendered, if it is a path on the app's own origin (`/x`, never `//host` or `/\\host`); else null. */\nexport function appPath(url: string | undefined): string | null {\n if (typeof url !== \"string\" || url.length > 2000) return null;\n if (!/^\\/(?![/\\\\])/.test(url) || /[\\u0000-\\u001f\\u007f\\s\\\\]/.test(url)) return null;\n return url;\n}\n\nfunction emptyReport(): RunReport {\n return { claimed: 0, sent: 0, filtered: 0, skipped: 0, retrying: 0, deferred: 0, failed: 0, stale: 0, unrecorded: 0 };\n}\n\nfunction addReport(a: RunReport, b: RunReport): RunReport {\n const out = { ...a };\n for (const k of Object.keys(out) as (keyof RunReport)[]) out[k] += b[k];\n return out;\n}\n\n/**\n * The runner.\n *\n * @stability Beta\n */\nexport function createNotificationRunner(options: NotificationRunnerOptions): NotificationRunner {\n const { store, registry } = options;\n const log = (m: string) => {\n try {\n options.log?.(m);\n } catch {\n // ignored\n }\n };\n const batch = options.batchSize ?? 100;\n const lease = options.leaseSeconds ?? 300;\n const now = () => options.now?.() ?? Date.now();\n if (options.runBudgetMs !== undefined && !(options.runBudgetMs >= lease * 1000)) {\n throw new Error(\"createNotificationRunner: runBudgetMs must be at least leaseSeconds (a run would claim jobs it can't finish)\");\n }\n const email = options.email;\n if (email) {\n const needsUnsubscribe = Object.keys(registry.types).some((key) => registry.type(key).channels.email !== undefined && !registry.type(key).transactional && !registry.isCritical(key));\n if (needsUnsubscribe && !email.unsubscribe) {\n throw new Error(\"createNotificationRunner: a type sends email that isn't transactional, so email.unsubscribe ({ url, secret }) is required\");\n }\n for (const [key, t] of Object.entries(registry.types)) {\n if (t.secretLink && !email.secretLinks?.[key]) throw new Error(`createNotificationRunner: type ${key} needs a secret link builder in email.secretLinks`);\n }\n }\n const providerIds = new Map<string, string>();\n\n const record = async (job: Job, outcome: JobOutcome) => {\n const providerId = providerIds.get(job.id);\n providerIds.delete(job.id);\n return store.settle(job.id, job.claim_id, settleOf(outcome, providerId));\n };\n const common = {\n record,\n now: options.now,\n log: options.log,\n maxAttempts: options.maxAttempts,\n maxDeferrals: options.maxDeferrals,\n recordLabel: (job: Job) => `${job.channel} job ${job.id}`,\n };\n\n // Push ---------------------------------------------------------------------\n // Each claimed job's Expo batch (its devices never split), and when its lease runs out.\n const pushChunk = new Map<string, number>();\n const leaseEnds = new Map<string, number>();\n // The batches go one after another (createRunner starts every group at once), so the lease check before each means something.\n let pushTurn: Promise<unknown> = Promise.resolve();\n const pushRunner = options.push\n ? createRunner<Job>({\n ...common,\n label: (job) => `Push ${job.id}`,\n claim: async ({ limit }) => {\n const claimedAt = now();\n const jobs = (await store.claim(\"push\", limit, lease, options.maxAttempts ?? 8)) as Job[];\n let chunk = 0;\n let filled = 0;\n for (const job of jobs) {\n const n = Math.min(job.devices?.length ?? 0, EXPO_SEND_BATCH);\n if (filled > 0 && filled + n > EXPO_SEND_BATCH) {\n chunk++;\n filled = 0;\n }\n filled += n;\n pushChunk.set(job.id, chunk);\n leaseEnds.set(job.id, claimedAt + lease * 1000);\n }\n return jobs;\n },\n // One group per Expo batch: each is sent, recorded and settled on its own.\n group: (job) => `push:${pushChunk.get(job.id) ?? 0}`,\n send: (jobs, { signal }) => {\n const deadline = Math.min(...jobs.map((j) => leaseEnds.get(j.id) ?? Infinity));\n for (const j of jobs) {\n pushChunk.delete(j.id);\n leaseEnds.delete(j.id);\n }\n const mine = pushTurn.then(() => sendPush(jobs, options.push!.provider, deadline, signal));\n pushTurn = mine.catch(() => undefined);\n return mine;\n },\n batchSize: batch,\n })\n : null;\n\n /** Only strings and finite numbers, so a type can't put an object or a function in a push. */\n function flatValues(values: Record<string, unknown> | undefined): Record<string, string | number> {\n const out: Record<string, string | number> = {};\n for (const [key, value] of Object.entries(values ?? {})) {\n if (typeof value === \"string\" || (typeof value === \"number\" && Number.isFinite(value))) out[key] = value;\n }\n return out;\n }\n\n function pushMessage(job: Job, token: string): PushMessage {\n const hidden = job.hide_previews || job.push_privacy === \"generic\";\n const rendered = registry.render(job.type, job.data);\n const text = hidden ? { title: registry.hiddenPreview.title, body: registry.hiddenPreview.body } : rendered;\n const url = appPath(rendered.url);\n // The type's push extras: ids always, display values and rich push only with previews shown.\n const extras = { ...flatValues(rendered.push?.ids), ...(hidden ? {} : flatValues(rendered.push?.display)) };\n return {\n to: token,\n title: text.title,\n ...(text.body ? { body: text.body } : {}),\n data: { ...extras, notification_id: job.notification_id, type: job.type, ...(url ? { url } : {}), ...(job.thread_key ? { thread_key: job.thread_key } : {}) },\n ...(!hidden && rendered.push?.mutableContent ? { mutableContent: true } : {}),\n sound: \"default\",\n ...(job.badge !== null && job.badge !== undefined ? { badge: job.badge } : {}),\n channelId: job.category,\n ...(job.ios_category ? { categoryId: job.ios_category } : {}),\n ...(job.thread_key ? { threadId: job.thread_key } : {}),\n collapseId: (job.collapse_key ?? job.notification_id).slice(0, 64),\n tag: (job.collapse_key ?? job.notification_id).slice(0, 64),\n priority: \"high\",\n };\n }\n\n async function sendPush(jobs: readonly Job[], provider: PushProvider, deadline: number, signal?: AbortSignal): Promise<JobResult[]> {\n // A request started after this may outlive the lease, and another runner would send the same push again.\n const lastStart = deadline - REQUEST_TIMEOUT_MS - 5_000;\n const leaseRunningOut = () => new SendError(\"lease_running_out\", { retryable: true, retryAfterSeconds: 1 });\n const slots: { job: number; device: string; message: PushMessage }[] = [];\n const results: (JobResult | null)[] = jobs.map((job) => (registry.has(job.type) ? null : { ok: false, retryable: false, error: \"unknown_type\" }));\n jobs.forEach((job, j) => {\n if (results[j]) return;\n for (const d of job.devices ?? []) slots.push({ job: j, device: d.id, message: pushMessage(job, d.token) });\n });\n const tickets: (PushTicket | SendError)[] = new Array(slots.length);\n for (let start = 0; start < slots.length; start += EXPO_SEND_BATCH) {\n const chunk = slots.slice(start, start + EXPO_SEND_BATCH);\n if (now() > lastStart) {\n for (let i = 0; i < chunk.length; i++) tickets[start + i] = leaseRunningOut();\n continue;\n }\n try {\n const got = await provider.send(chunk.map((s) => s.message), { signal });\n got.forEach((t, i) => (tickets[start + i] = t));\n } catch (error) {\n if (refusedOutright(error) && chunk.length > 1) {\n // One bad message can't hold up the rest: each on its own.\n for (let i = 0; i < chunk.length; i++) {\n if (now() > lastStart) {\n tickets[start + i] = leaseRunningOut();\n continue;\n }\n try {\n tickets[start + i] = (await provider.send([chunk[i]!.message], { signal }))[0] ?? { status: \"error\", code: \"NoTicket\" };\n } catch (one) {\n tickets[start + i] = one instanceof SendError ? one : new SendError(\"Push failed\", { retryable: true });\n }\n }\n } else {\n const e = error instanceof SendError ? error : new SendError(\"Push failed\", { retryable: true });\n for (let i = 0; i < chunk.length; i++) tickets[start + i] = e;\n }\n }\n }\n // Per job: its tickets recorded, and one result.\n await Promise.all(\n jobs.map(async (job, j) => {\n if (results[j]) return;\n const mine = slots.map((s, i) => ({ s, t: tickets[i]! })).filter((x) => x.s.job === j);\n const rows: PushTicketRow[] = mine\n .filter((x) => !(x.t instanceof SendError))\n .map(({ s, t }) => {\n const ticket = t as PushTicket;\n return ticket.status === \"ok\"\n ? { device_id: s.device, ticket_id: ticket.id, status: \"sent\" as const, error: null }\n : { device_id: s.device, ticket_id: null, status: \"failed\" as const, error: ticket.code };\n });\n if (rows.length) {\n try {\n await store.pushTickets(job.id, rows);\n } catch {\n log(`Recording push ${job.id}'s tickets failed`);\n }\n }\n if (rows.some((r) => r.status === \"sent\")) {\n results[j] = { ok: true };\n return;\n }\n const httpError = mine.find((x) => x.t instanceof SendError)?.t as SendError | undefined;\n if (httpError) {\n results[j] = failureOf(httpError);\n return;\n }\n const codes = rows.map((r) => r.error);\n if (codes.length && codes.every((c) => c === \"DeviceNotRegistered\")) results[j] = { ok: true, notSent: \"device_not_registered\" };\n else if (codes.includes(\"MessageRateExceeded\")) results[j] = { ok: false, retryable: true, retryAfterSeconds: 60, error: \"Expo: MessageRateExceeded\" };\n else if (!codes.length) results[j] = { ok: true, notSent: \"no_devices\" };\n else results[j] = { ok: false, retryable: false, error: `Expo: ${codes.find((c) => c !== \"DeviceNotRegistered\") ?? \"Unknown\"}` };\n }),\n );\n return results as JobResult[];\n }\n\n // Email --------------------------------------------------------------------\n const emailRunner = email\n ? createRunner<Job>({\n ...common,\n label: (job) => `Email ${job.id}`,\n claim: ({ limit }) => store.claim(\"email\", limit, lease, options.maxAttempts ?? 8) as Promise<Job[]>,\n send: async ([job], { signal }) => [await sendEmail(job!, email, signal)],\n batchSize: batch,\n })\n : null;\n\n function fromAddress(job: Job, e: EmailOptions): string {\n const local = e.localPart ?? \"notifications\";\n const shared = job.transactional ? e.domains.transactional : (e.domains.other ?? e.domains.transactional);\n // A tenant's verified domain, unless it is (or is under) one of the app's own: never a tenant's to send as.\n const own = job.sender?.domain_verified && job.sender.domain && !isReservedDomain(job.sender.domain, [e.domains.transactional, e.domains.other]) ? job.sender.domain : null;\n const domain = own ?? shared;\n const brandName = typeof job.brand?.[\"name\"] === \"string\" ? (job.brand[\"name\"] as string) : null;\n const name = displayName(job.sender?.from_name ?? brandName ?? e.appName) || displayName(e.appName);\n return `${name} <${local}@${domain}>`;\n }\n\n async function render(job: Job, e: EmailOptions): Promise<RenderedEmail> {\n const to = job.email;\n if (!isSafeAddress(to)) throw new SendError(\"no_valid_address\");\n const t = registry.type(job.type);\n let unsubscribeUrl: string | null = null;\n let headers: Record<string, string> | undefined;\n // Transactional mail has no unsubscribe, and nor does a critical type's (preferences can't turn it off).\n if (!job.transactional && !job.critical && e.unsubscribe) {\n const token = await createUnsubscribeToken(e.unsubscribe.secret, {\n ...(job.user_id ? { userId: job.user_id } : { emailHash: await emailHash(to) }),\n category: job.category,\n ...(job.tenant_id ? { tenantId: job.tenant_id } : {}),\n });\n headers = unsubscribeHeaders(e.unsubscribe.url, token);\n unsubscribeUrl = headers[\"List-Unsubscribe\"]!.slice(1, -1);\n }\n const text = registry.render(job.type, job.data);\n const path = appPath(text.url);\n const url = path && options.appUrl ? new URL(path, new URL(options.appUrl).origin).toString() : null;\n const template = e.templates?.[job.type] ?? e.defaultTemplate ?? defaultEmailTemplate;\n const content = await template({\n type: job.type,\n category: job.category,\n data: job.data,\n text,\n recipient: { email: to, name: job.recipient_name },\n brand: (job.brand ?? {}) as NotificationBrand,\n tenantId: job.tenant_id,\n transactional: job.transactional,\n secretLink: t.secretLink ? SECRET_LINK_PLACEHOLDER : null,\n unsubscribeUrl,\n url,\n });\n const replyTo = job.sender?.reply_to;\n return {\n from: fromAddress(job, e),\n to: [to],\n ...(isSafeAddress(replyTo) ? { reply_to: replyTo } : {}),\n subject: headerSafe(content.subject) || headerSafe(text.title) || \"Notification\",\n html: content.html,\n text: content.text,\n ...(headers ? { headers } : {}),\n tags: [\n { name: \"category\", value: job.category.replace(/[^A-Za-z0-9_-]/g, \"_\") },\n { name: \"stream\", value: job.transactional ? \"transactional\" : \"other\" },\n ],\n };\n }\n\n async function sendEmail(job: Job, e: EmailOptions, signal?: AbortSignal): Promise<JobResult> {\n try {\n if (!registry.has(job.type)) return { ok: false, retryable: false, error: \"unknown_type\" };\n let rendered = job.rendered;\n if (rendered) {\n if (!job.rendered_hash || (await sha256Hex(canonicalJson(rendered))) !== job.rendered_hash) {\n // Never send something other than the first render under its key.\n return { ok: false, retryable: false, error: \"rendered_email_changed\" };\n }\n } else {\n const fresh = await render(job, e);\n const hash = await sha256Hex(canonicalJson(fresh));\n const kept = await store.storeRendered(job.id, job.claim_id, fresh, hash);\n if (!kept) return { ok: false, retryable: true, error: \"claim_lost\" };\n rendered = kept.rendered;\n }\n let html = rendered.html;\n let text = rendered.text;\n if (registry.type(job.type).secretLink) {\n const link = await resolveSecretLink(e.secretLinks ?? {}, job.type, {\n data: job.data,\n secret: job.secret,\n notificationId: job.notification_id,\n tenantId: job.tenant_id,\n });\n html = html.split(SECRET_LINK_PLACEHOLDER).join(escapeHtml(link));\n text = text.split(SECRET_LINK_PLACEHOLDER).join(link);\n }\n const provider = e.providerFor?.(job) ?? e.provider;\n const result = await provider.send(\n {\n from: rendered.from,\n to: rendered.to,\n ...(rendered.reply_to ? { replyTo: rendered.reply_to } : {}),\n subject: rendered.subject,\n html,\n text,\n ...(rendered.headers ? { headers: rendered.headers } : {}),\n ...(rendered.tags ? { tags: rendered.tags } : {}),\n idempotencyKey: `genesis-notification-${job.id}`,\n },\n { signal },\n );\n if (result.id) {\n providerIds.set(job.id, result.id);\n try {\n await store.recordProviderId(job.id, job.claim_id, result.id);\n } catch {\n log(`Keeping email ${job.id}'s provider id failed; it is kept when it settles`);\n }\n }\n return result.alreadySent ? { ok: true, alreadyThere: true } : { ok: true };\n } catch (error) {\n if (error instanceof SendError && error.message === \"no_valid_address\") return { ok: true, notSent: \"no_address\" };\n if (error instanceof Error && error.name === \"SecretLinkError\") return { ok: false, retryable: false, error: \"secret_link_unavailable\" };\n return failureOf(error);\n }\n }\n\n const none = async (): Promise<RunReport> => {\n throw new Error(\"This channel isn't configured\");\n };\n\n // One claim, or (with runBudgetMs) claims while they come back full and the time left covers a lease.\n async function drain(r: { run(o: { signal?: AbortSignal }): Promise<RunReport> }, signal?: AbortSignal): Promise<RunReport> {\n const budget = options.runBudgetMs;\n if (budget === undefined) return r.run({ signal });\n const started = now();\n let total = emptyReport();\n while (budget - (now() - started) >= lease * 1000 && !signal?.aborted) {\n const report = await r.run({ signal });\n total = addReport(total, report);\n if (report.claimed < batch) break;\n }\n return total;\n }\n\n return {\n async run({ signal } = {}) {\n const [push, mail] = await Promise.all([pushRunner ? drain(pushRunner, signal) : null, emailRunner ? drain(emailRunner, signal) : null]);\n return { push, email: mail };\n },\n runPush: ({ signal } = {}) => (pushRunner ? drain(pushRunner, signal) : none()),\n runEmail: ({ signal } = {}) => (emailRunner ? drain(emailRunner, signal) : none()),\n async checkReceipts({ signal, budgetMs = 60_000 } = {}) {\n const provider = options.push?.provider;\n if (!provider) return { checked: 0, settled: 0 };\n const started = Date.now();\n let checked = 0;\n let settled = 0;\n for (;;) {\n const due = await store.receiptsClaim(EXPO_RECEIPTS_BATCH);\n if (!due.length) break;\n checked += due.length;\n let receipts;\n try {\n receipts = await provider.receipts(due.map((d) => d.ticket_id), { signal });\n } catch (error) {\n log(`Push receipts failed: ${error instanceof Error ? error.message : \"unknown\"}`);\n break;\n }\n const results = due.flatMap((d) => {\n const r = receipts[d.ticket_id];\n if (!r) return [];\n return [{ id: d.id, status: r.status === \"ok\" ? (\"delivered\" as const) : (\"failed\" as const), error: r.status === \"error\" ? r.code : null }];\n });\n if (results.some((r) => r.error === \"InvalidCredentials\")) log(\"Push receipts: InvalidCredentials (the APNs or FCM credentials in EAS are wrong)\");\n if (results.length) await store.receiptsSettle(results);\n settled += results.length;\n if (due.length < EXPO_RECEIPTS_BATCH || Date.now() - started > budgetMs) break;\n }\n return { checked, settled };\n },\n };\n}\n","/**\n * Resend's webhooks are signed the Standard Webhooks way (Svix): headers\n * `svix-id`, `svix-timestamp` and `svix-signature` (`v1,<base64 HMAC-SHA256\n * of \"id.timestamp.body\">`, with the base64 secret after `whsec_`).\n */\nimport { fromBase64, hmacSha256, timingSafeEqual } from \"./crypto\";\n\n/** How far a webhook's timestamp may be from now: five minutes, as Svix checks. */\nexport const WEBHOOK_TOLERANCE_SECONDS = 300;\n\n/** Whether `body` was signed with `secret`; false for anything stale, unsigned or malformed. */\nexport async function verifyResendWebhook(secret: string, headers: Headers, body: string, now = Date.now()): Promise<boolean> {\n const id = headers.get(\"svix-id\") ?? headers.get(\"webhook-id\");\n const timestamp = headers.get(\"svix-timestamp\") ?? headers.get(\"webhook-timestamp\");\n const signatures = headers.get(\"svix-signature\") ?? headers.get(\"webhook-signature\");\n if (!secret || !id || !timestamp || !signatures || !/^\\d{1,12}$/.test(timestamp)) return false;\n if (Math.abs(now / 1000 - Number(timestamp)) > WEBHOOK_TOLERANCE_SECONDS) return false;\n let key: Uint8Array;\n try {\n key = fromBase64(secret.startsWith(\"whsec_\") ? secret.slice(6) : secret);\n } catch {\n return false;\n }\n const expected = await hmacSha256(key, `${id}.${timestamp}.${body}`);\n for (const part of signatures.split(\" \")) {\n const [version, sig] = part.split(\",\");\n if (version !== \"v1\" || !sig) continue;\n let given: Uint8Array;\n try {\n given = fromBase64(sig);\n } catch {\n continue;\n }\n if (timingSafeEqual(given, expected)) return true;\n }\n return false;\n}\n\n/** What the notifier does with an email event; null for the events it doesn't use. */\nexport function emailEventOf(payload: unknown): { providerId: string; event: \"delivered\" | \"bounced\" | \"complained\"; permanent: boolean } | null {\n const p = payload as { type?: unknown; data?: { email_id?: unknown; bounce?: { type?: unknown } } } | null;\n const id = p?.data?.email_id;\n if (typeof id !== \"string\" || !id || id.length > 200) return null;\n switch (p?.type) {\n case \"email.delivered\":\n return { providerId: id, event: \"delivered\", permanent: false };\n case \"email.bounced\":\n // Only what Resend names \"Permanent\" is a hard bounce; anything else (Transient, Undetermined, none) doesn't suppress.\n return { providerId: id, event: \"bounced\", permanent: p.data?.bounce?.type === \"Permanent\" };\n case \"email.complained\":\n return { providerId: id, event: \"complained\", permanent: true };\n default:\n return null;\n }\n}\n","/**\n * `createNotificationsHandler`: the notifier's HTTP surface, as a\n * `(Request) => Promise<Response>` for an Edge Function, a worker or any\n * server with Fetch API requests. Matched by the path's last segments, so\n * it mounts under any prefix:\n *\n * POST …/run send what is due (Authorization: Bearer <cronSecret>)\n * POST …/receipts check push receipts (Authorization: Bearer <cronSecret>)\n * POST …/webhooks/resend bounces, complaints, deliveries (Svix-signed)\n * GET …/unsubscribe?t=… a page with one button (GET never unsubscribes: link scanners follow links)\n * POST …/unsubscribe?t=… unsubscribes; RFC 8058 one-click posts `List-Unsubscribe=One-Click` here\n *\n * Answers carry counts and statuses only.\n */\nimport { timingSafeEqual } from \"./crypto\";\nimport { escapeHtml } from \"./email-render\";\nimport type { NotificationRunner } from \"./runner\";\nimport type { NotificationStore } from \"./store\";\nimport { verifyUnsubscribeToken } from \"./unsubscribe\";\nimport { emailEventOf, verifyResendWebhook } from \"./webhook\";\n\nexport interface NotificationsHandlerOptions {\n runner: NotificationRunner;\n store: NotificationStore;\n /** The bearer secret a scheduler sends to /run and /receipts (32 characters or more). */\n cronSecret: string;\n /** The unsubscribe signing secret (the runner's `email.unsubscribe.secret`). */\n unsubscribeSecret?: string;\n /** Resend's webhook signing secret (`whsec_…`). */\n resendWebhookSecret?: string;\n /** Words on the unsubscribe page. */\n copy?: Partial<{ title: string; confirm: string; button: string; done: string; invalid: string }>;\n log?: (message: string) => void;\n}\n\nconst COPY = {\n title: \"Unsubscribe\",\n confirm: \"Stop getting these emails?\",\n button: \"Unsubscribe\",\n done: \"You're unsubscribed. You won't get these emails any more.\",\n invalid: \"This link isn't valid any more.\",\n};\n\nfunction page(title: string, body: string, status = 200): Response {\n const html = `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"robots\" content=\"noindex\"><title>${escapeHtml(title)}</title></head><body style=\"font-family:sans-serif;max-width:32rem;margin:4rem auto;padding:0 1rem;line-height:1.5\">${body}</body></html>`;\n return new Response(html, { status, headers: { \"content-type\": \"text/html; charset=utf-8\", \"cache-control\": \"no-store\", \"referrer-policy\": \"no-referrer\" } });\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), { status, headers: { \"content-type\": \"application/json\", \"cache-control\": \"no-store\" } });\n}\n\n/**\n * The handler.\n *\n * @stability Beta\n */\nexport function createNotificationsHandler(options: NotificationsHandlerOptions): (request: Request) => Promise<Response> {\n if (!options.cronSecret || options.cronSecret.length < 32) throw new Error(\"createNotificationsHandler: cronSecret must be at least 32 characters\");\n const copy = { ...COPY, ...options.copy };\n const enc = new TextEncoder();\n const authorized = (request: Request) => {\n const header = request.headers.get(\"authorization\") ?? \"\";\n const given = header.startsWith(\"Bearer \") ? header.slice(7) : \"\";\n return timingSafeEqual(enc.encode(given), enc.encode(options.cronSecret));\n };\n const log = (m: string) => {\n try {\n options.log?.(m);\n } catch {\n // ignored\n }\n };\n\n return async (request) => {\n const url = new URL(request.url);\n const path = url.pathname.replace(/\\/+$/, \"\");\n try {\n if (path.endsWith(\"/run\") || path.endsWith(\"/receipts\")) {\n if (request.method !== \"POST\") return json({ error: \"method_not_allowed\" }, 405);\n if (!authorized(request)) return json({ error: \"unauthorized\" }, 401);\n if (path.endsWith(\"/run\")) return json({ ok: true, ...(await options.runner.run({ signal: request.signal })) });\n return json({ ok: true, ...(await options.runner.checkReceipts({ signal: request.signal })) });\n }\n\n if (path.endsWith(\"/webhooks/resend\")) {\n if (request.method !== \"POST\") return json({ error: \"method_not_allowed\" }, 405);\n if (!options.resendWebhookSecret) return json({ error: \"not_configured\" }, 404);\n const body = await request.text();\n if (body.length > 256_000 || !(await verifyResendWebhook(options.resendWebhookSecret, request.headers, body))) return json({ error: \"bad_signature\" }, 401);\n let payload: unknown;\n try {\n payload = JSON.parse(body);\n } catch {\n return json({ error: \"bad_json\" }, 400);\n }\n const event = emailEventOf(payload);\n if (!event) return json({ ok: true, ignored: true });\n const ours = await options.store.emailEvent(event.providerId, event.event, event.permanent);\n return json({ ok: true, ours });\n }\n\n if (path.endsWith(\"/unsubscribe\")) {\n if (!options.unsubscribeSecret) return page(copy.title, `<p>${escapeHtml(copy.invalid)}</p>`, 404);\n const token = url.searchParams.get(\"t\") ?? \"\";\n const claims = await verifyUnsubscribeToken(options.unsubscribeSecret, token);\n if (!claims) return page(copy.title, `<p>${escapeHtml(copy.invalid)}</p>`, 400);\n if (request.method === \"GET\") {\n // Relative: behind a gateway (Supabase's /functions/v1) the function doesn't see its public path.\n const action = `?t=${encodeURIComponent(token)}`;\n return page(\n copy.title,\n `<h1 style=\"font-size:1.25rem\">${escapeHtml(copy.confirm)}</h1><form method=\"post\" action=\"${escapeHtml(action)}\"><input type=\"hidden\" name=\"List-Unsubscribe\" value=\"One-Click\"><button type=\"submit\" style=\"font-size:1rem;padding:.5rem 1rem\">${escapeHtml(copy.button)}</button></form>`,\n );\n }\n if (request.method !== \"POST\") return json({ error: \"method_not_allowed\" }, 405);\n await options.store.unsubscribe(claims.userId ?? null, claims.userId ? null : (claims.emailHash ?? null), claims.category, claims.tenantId ?? null);\n return page(copy.title, `<p>${escapeHtml(copy.done)}</p>`);\n }\n\n return json({ error: \"not_found\" }, 404);\n } catch (error) {\n log(`notifications handler: ${error instanceof Error ? error.name : \"error\"} on ${path.split(\"/\").slice(-1)[0]}`);\n return json({ error: \"server_error\" }, 500);\n }\n };\n}\n","/**\n * A request pacer: at most `perSecond` requests start in any second, spaced\n * evenly, however many callers wait at once. Each provider instance has its\n * own; two runners running at once each keep to it, so set it for that.\n */\nexport interface RateLimiter {\n /** Resolves when the caller may start its request; rejects if `signal` aborts first. */\n acquire(signal?: AbortSignal): Promise<void>;\n}\n\nexport interface RateLimiterOptions {\n perSecond: number;\n now?: () => number;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n}\n\nfunction defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason ?? new Error(\"Aborted\"));\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/** @stability Beta */\nexport function createRateLimiter(options: RateLimiterOptions): RateLimiter {\n if (!(options.perSecond > 0) || !Number.isFinite(options.perSecond)) throw new Error(\"createRateLimiter: perSecond must be a positive number\");\n const gap = 1000 / options.perSecond;\n const now = options.now ?? Date.now;\n const sleep = options.sleep ?? defaultSleep;\n let next = 0;\n return {\n async acquire(signal) {\n const at = Math.max(now(), next);\n next = at + gap;\n const wait = at - now();\n if (wait > 0) await sleep(wait, signal);\n },\n };\n}\n","/**\n * Resend's REST API as an `EmailProvider`. One key per app (`RESEND_API_KEY`);\n * each tenant gets its own sender identity (`senderFor`), not its own key.\n *\n * Retries are byte-identical (the runner stores the first render), and every\n * send carries an idempotency key, so Resend sends once:\n * - 409 `invalid_idempotent_request`: the key was used with another body. The\n * stored render never changes, so the body differed in what is built at send\n * time: a secret link builder that isn't deterministic (it must give the same\n * link for the same notification every time). The job fails loudly\n * (`idempotency_body_changed`); it is never counted as sent.\n * - 409 `concurrent_idempotent_requests`: another attempt is in flight: retry soon.\n *\n * Requests are paced (`perSecond`, default 5) under Resend's default limit of\n * 10 requests a second per team, which every key of the team shares.\n */\nimport { SendError, discard, isRetryableStatus, retryAfterSeconds, timedFetch, type HttpDeps } from \"./errors\";\nimport { createRateLimiter } from \"./rate\";\n\n/** One email, ready to send. Addresses and headers are already checked. */\nexport interface EmailMessage {\n from: string;\n to: string[];\n replyTo?: string;\n subject: string;\n html: string;\n text: string;\n /** Extra headers (`List-Unsubscribe`, `List-Unsubscribe-Post`). */\n headers?: Record<string, string>;\n /** Provider tags: name and value are letters, digits, \"_\" or \"-\". */\n tags?: { name: string; value: string }[];\n /** Stable across retries of one send job. */\n idempotencyKey: string;\n}\n\nexport interface EmailSendResult {\n /** The provider's id for the email (what its webhooks name). */\n id: string | null;\n /** The provider says it was sent before (the same idempotency key). */\n alreadySent?: boolean;\n}\n\n/** Sends one email. Throws a `SendError` on failure. */\nexport interface EmailProvider {\n readonly name: string;\n send(message: EmailMessage, options?: { signal?: AbortSignal }): Promise<EmailSendResult>;\n}\n\nexport const RESEND_API = \"https://api.resend.com/emails\";\n\n/** Requests a second the provider starts by default: half Resend's default team limit (10). */\nexport const RESEND_DEFAULT_PER_SECOND = 5;\n\nexport interface ResendOptions extends HttpDeps {\n apiKey: string;\n endpoint?: string;\n /** Requests started per second, at most. Default `RESEND_DEFAULT_PER_SECOND` (5). Raise it only if Resend raised your team's limit. */\n perSecond?: number;\n /** For tests: the clock and the wait the pacer uses. */\n now?: () => number;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n}\n\n/**\n * Resend as an `EmailProvider`.\n *\n * @stability Beta\n */\nexport function resendProvider(options: ResendOptions): EmailProvider {\n const pace = createRateLimiter({\n perSecond: options.perSecond ?? RESEND_DEFAULT_PER_SECOND,\n ...(options.now ? { now: options.now } : {}),\n ...(options.sleep ? { sleep: options.sleep } : {}),\n });\n return {\n name: \"resend\",\n async send(message, { signal } = {}) {\n if (!options.apiKey) throw new SendError(\"The Resend API key is not set\");\n try {\n await pace.acquire(signal);\n } catch {\n throw new SendError(\"Timed out\", { retryable: true });\n }\n const response = await timedFetch(\n options,\n options.endpoint ?? RESEND_API,\n {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${options.apiKey}`,\n \"content-type\": \"application/json\",\n \"idempotency-key\": message.idempotencyKey,\n },\n body: JSON.stringify({\n from: message.from,\n to: message.to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n ...(message.replyTo ? { reply_to: message.replyTo } : {}),\n ...(message.headers && Object.keys(message.headers).length ? { headers: message.headers } : {}),\n ...(message.tags?.length ? { tags: message.tags } : {}),\n }),\n },\n signal,\n );\n if (response.ok) {\n let id: string | null = null;\n try {\n const body = (await response.json()) as { id?: unknown };\n if (typeof body.id === \"string\" && body.id.length <= 200) id = body.id;\n } catch {\n // Sent; the id is only for the webhooks.\n }\n return { id };\n }\n // Resend's error `name` is safe to keep; its message may echo input.\n let name: string | undefined;\n try {\n const body = (await response.json()) as { name?: unknown };\n if (typeof body.name === \"string\" && /^[a-z_]{1,64}$/.test(body.name)) name = body.name;\n } catch {\n discard(response);\n }\n if (response.status === 409 && name === \"invalid_idempotent_request\") {\n // Never \"sent\": the body changed under the same key, so something built at send time isn't deterministic.\n throw new SendError(\"idempotency_body_changed: Resend answered 409 (invalid_idempotent_request); a secret link builder must give the same link every time\", {\n retryable: false,\n status: 409,\n });\n }\n if (response.status === 409 && name === \"concurrent_idempotent_requests\") {\n throw new SendError(\"Resend answered 409 (concurrent_idempotent_requests)\", { retryable: true, retryAfterSeconds: 5, status: 409 });\n }\n const retryable = isRetryableStatus(response.status);\n throw new SendError(`Resend answered HTTP ${response.status}${name ? ` (${name})` : \"\"}`, {\n retryable,\n status: response.status,\n ...(response.status === 429 || response.status === 503 ? { retryAfterSeconds: retryAfterSeconds(response.headers.get(\"retry-after\"), 30) } : {}),\n });\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { n as NotificationRow, b as ChannelChoices, a as Channel } from './types-BilguCPW.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@marktiderman/genesis-notifications/supabase`: the migration this package
|
|
5
|
+
* ships (`migrations/0001_genesis_notifications.sql`, with its pgTAP test),
|
|
6
|
+
* and `notificationsClient(supabase)`, the browser and phone side over it:
|
|
7
|
+
* the person's feed, unread count, marks and preferences under row level
|
|
8
|
+
* security, the realtime nudge on `user:<id>`, and the admin views.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
declare const NOTIFICATIONS_PACKAGE = "@marktiderman/genesis-notifications";
|
|
14
|
+
/** The migrations to copy, in order, with their tests. */
|
|
15
|
+
declare const NOTIFICATIONS_MIGRATIONS: readonly [{
|
|
16
|
+
readonly package: "@marktiderman/genesis-notifications";
|
|
17
|
+
readonly file: "migrations/0001_genesis_notifications.sql";
|
|
18
|
+
readonly test: "migrations/tests/0001_genesis_notifications.test.sql";
|
|
19
|
+
}];
|
|
20
|
+
type Result = {
|
|
21
|
+
data: unknown;
|
|
22
|
+
error: {
|
|
23
|
+
message?: string;
|
|
24
|
+
code?: string | null;
|
|
25
|
+
} | null;
|
|
26
|
+
count?: number | null;
|
|
27
|
+
};
|
|
28
|
+
/** The parts of a Supabase client used here; a `SupabaseClient` fits, typed by your Database or not. */
|
|
29
|
+
interface NotificationsSupabaseLike {
|
|
30
|
+
rpc: (fn: any, args?: any) => PromiseLike<Result>;
|
|
31
|
+
from(table: string): unknown;
|
|
32
|
+
channel(name: string, options?: {
|
|
33
|
+
config?: {
|
|
34
|
+
private?: boolean;
|
|
35
|
+
};
|
|
36
|
+
}): unknown;
|
|
37
|
+
removeChannel(channel: unknown): unknown;
|
|
38
|
+
realtime?: {
|
|
39
|
+
setAuth?: (token?: string | null) => unknown;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** A database refusal, with its SQLSTATE. */
|
|
43
|
+
declare class NotificationsClientError extends Error {
|
|
44
|
+
readonly operation: string;
|
|
45
|
+
readonly code: string | null;
|
|
46
|
+
constructor(operation: string, code: string | null);
|
|
47
|
+
}
|
|
48
|
+
type FeedFilter = "all" | "unread" | "archived";
|
|
49
|
+
/** One row of the admin's delivery log: no content, a masked address. */
|
|
50
|
+
interface DeliveryLogRow {
|
|
51
|
+
id: string;
|
|
52
|
+
notification_id: string;
|
|
53
|
+
tenant_id: string | null;
|
|
54
|
+
type: string;
|
|
55
|
+
channel: "push" | "email";
|
|
56
|
+
status: string;
|
|
57
|
+
attempts: number;
|
|
58
|
+
error: string | null;
|
|
59
|
+
skip_reason: string | null;
|
|
60
|
+
recipient_masked: string | null;
|
|
61
|
+
created_at: string;
|
|
62
|
+
sent_at: string | null;
|
|
63
|
+
}
|
|
64
|
+
interface SuppressionRow {
|
|
65
|
+
email_hash: string;
|
|
66
|
+
category: string;
|
|
67
|
+
reason: "bounce" | "complaint" | "unsubscribe" | "manual";
|
|
68
|
+
email_masked: string | null;
|
|
69
|
+
created_at: string;
|
|
70
|
+
}
|
|
71
|
+
interface NotificationsClient {
|
|
72
|
+
feed(options?: {
|
|
73
|
+
filter?: FeedFilter;
|
|
74
|
+
limit?: number;
|
|
75
|
+
before?: string;
|
|
76
|
+
}): Promise<NotificationRow[]>;
|
|
77
|
+
unreadCount(): Promise<number>;
|
|
78
|
+
mark(ids: readonly string[], state: "seen" | "read" | "unread" | "archived" | "unarchived"): Promise<number>;
|
|
79
|
+
markAll(state: "seen" | "read"): Promise<number>;
|
|
80
|
+
/** The signed-in person's choices (category → channel → on); `{}` before they chose. */
|
|
81
|
+
choices(userId: string): Promise<ChannelChoices>;
|
|
82
|
+
/** One category and channel; `null` returns it to the default. Resolves the person's choices after. */
|
|
83
|
+
setChoice(category: string, channel: Channel, on: boolean | null): Promise<ChannelChoices>;
|
|
84
|
+
/** Calls `onChange` whenever the person's feed changes (a broadcast on `user:<id>`). Returns the stop. */
|
|
85
|
+
subscribe(userId: string, onChange: () => void): () => void;
|
|
86
|
+
/** Admin: the delivery log (row level security shows an admin their tenants'). */
|
|
87
|
+
deliveryLog(options?: {
|
|
88
|
+
tenantId?: string | null;
|
|
89
|
+
status?: string;
|
|
90
|
+
limit?: number;
|
|
91
|
+
}): Promise<DeliveryLogRow[]>;
|
|
92
|
+
/** Admin: the suppression list (platform admins). */
|
|
93
|
+
suppressions(options?: {
|
|
94
|
+
limit?: number;
|
|
95
|
+
}): Promise<SuppressionRow[]>;
|
|
96
|
+
removeSuppression(emailHash: string, category?: string): Promise<boolean>;
|
|
97
|
+
/** Admin: a test notification to yourself, on every channel you have. */
|
|
98
|
+
sendTest(tenantId?: string | null): Promise<string | null>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A preferences row as category → channel → on: `channels`, with the push
|
|
102
|
+
* and email of a category switched off as a whole (migration 0005's
|
|
103
|
+
* `categories`) filled in where no channel was chosen, as the server reads it.
|
|
104
|
+
*/
|
|
105
|
+
declare function choicesOf(row: {
|
|
106
|
+
channels?: unknown;
|
|
107
|
+
categories?: unknown;
|
|
108
|
+
} | null): ChannelChoices;
|
|
109
|
+
declare const FEED_COLUMNS = "id, type, category, tenant_id, data, thread_key, created_at, seen_at, read_at, archived_at";
|
|
110
|
+
/**
|
|
111
|
+
* The browser and phone side of the notifier, under the signed-in person's
|
|
112
|
+
* row level security.
|
|
113
|
+
*
|
|
114
|
+
* @stability Beta
|
|
115
|
+
*/
|
|
116
|
+
declare function notificationsClient(client: NotificationsSupabaseLike): NotificationsClient;
|
|
117
|
+
|
|
118
|
+
export { type DeliveryLogRow, FEED_COLUMNS, type FeedFilter, NOTIFICATIONS_MIGRATIONS, NOTIFICATIONS_PACKAGE, type NotificationsClient, NotificationsClientError, type NotificationsSupabaseLike, type SuppressionRow, choicesOf, notificationsClient };
|
package/dist/supabase.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"supabase.js"}
|