{"version":3,"file":"add-to-cart-elements-section.min.js","mappings":"yMACA,IAAAA,EAAA,GAGA,SAAAC,EAAAC,GAEA,IAAAC,EAAAH,EAAAE,GACA,QAAAE,IAAAD,EACA,OAAAA,EAAAE,QAGA,IAAAC,EAAAN,EAAAE,GAAA,CAGAG,QAAA,IAIA,KAAAH,KAAAK,GAAA,QACAP,EAAAE,GACA,IAAAM,EAAA,IAAAC,MAAA,uBAAAP,EAAA,KAEA,MADAM,EAAAE,KAAA,mBACAF,CACA,CAIA,OAHAD,EAAAL,GAAAI,EAAAA,EAAAD,QAAAJ,GAGAK,EAAAD,OACA,CC5BAJ,EAAAU,EAAA,oBAAAC,UAAAA,SAAAC,SAAAC,KAAAC,SAAAC,gBCCA,IAIAC,EAFA,kIAFA,IAAAC,IAAyCjB,EAAA,8EAAAA,EAAAU,GAE6F,kbCKtI,MAAMQ,kBAAkBC,YACtB,6BAAkBC,GAChB,MAAO,CACL,YACA,cAEJ,CAuGQC,mCAAqCC,KAAKC,8BAA8BC,KAAKF,MAC7EG,aAAeH,KAAKI,QAAQF,KAAKF,MACjCK,mBAAqBL,KAAKM,cAAcJ,KAAKF,MAI7CO,SAAwC,KAazC,iBAAAC,GACLR,KAAKS,uBAELT,KAAKU,aCjFF,SAAeC,EACpBC,GACA,QAD2B,IAA3BA,IAAAA,EAA8B,oBACF,aAAxBvB,SAASwB,YAAuC,qBAATD,GACjB,gBAAxBvB,SAASwB,WAA+B,CACxC,MAAMC,EAAQC,WAAWJ,EAAU,GAEnC,MAD8B,IAAMK,aAAaF,EAEnD,CAGE,OAFAG,iBAAiBL,EAAMD,GACO,IAAMO,oBAAoBN,EAAMD,EAGlE,CDqEwBQ,CAAMnB,KAAKG,aACjC,CAEO,oBAAAiB,GACLF,oBAAoB,iCAAkClB,KAAKD,oCAEvDC,KAAKU,cACPV,KAAKU,cAET,CAEO,wBAAAW,CAAyBC,EAAcC,EAAkBC,GAC9DxB,KAAKS,uBAEDc,IAAaC,IAIbxB,KAAKyB,aAAwB,gBAATH,EACtBtB,KAAK0B,2BACI1B,KAAKyB,aAAwB,cAATH,GAC7BtB,KAAK2B,uBAAuBH,GAEhC,CAEQ,oBAAAf,GACDT,KAAK4B,aACR5B,KAAK6B,aAAa,CAAEC,KAAM,SAC1B9B,KAAK4B,WAAWG,UAAY,gqCAA2BrC,IAE3D,CAEQ,OAAAU,GACFJ,KAAKyB,cACPR,iBAAiB,iCAAkCjB,KAAKD,oCAExDC,KAAKgC,QAAUhC,KAAKiC,oBACpBjC,KAAKkC,SAET,CAEQ,wBAAAR,GAA2B,IAAAS,EAAAC,EAAAC,EAAAC,EACjCtC,KAAKgC,QAAUhC,KAAKiC,oBAG+B,OAAnDE,EAAAnC,KAAK4B,WAAWW,cAAc,wBAA9BJ,EAAqDK,SACQ,OAA7DJ,EAAApC,KAAK4B,WAAWW,cAAc,kCAA9BH,EAA+DI,SAC7B,OAAlCH,EAAArC,KAAK4B,WAAWW,cAAc,OAA9BF,EAAoCG,SACC,OAArCF,EAAAtC,KAAK4B,WAAWW,cAAc,UAA9BD,EAAuCE,SAEvCxC,KAAKkC,QACP,CAEQ,sBAAAP,CAAuBH,GAK7B,MAAMiB,EAAUzC,KAAK4B,WAAWc,iBAC9B,oDAEIC,EAA2B,SAAbnB,EAAsB,OAAS,QACnD,IAAK,MAAMoB,KAAUH,EACnBG,EAAOC,QAAQC,KAAOH,CAE1B,CAEQ,6BAAA1C,CAA8B8C,GAAyD,IAAAC,EAC7F,GAAID,EAAME,OAAOC,MAAmB,OAAjBF,EAAKhD,KAAKgC,cAAO,EAAZgB,EAAcE,IAAI,KAAAC,EAAAC,EAAAC,EAAAC,EACxC,MAAMtB,EAAUhC,KAAKuC,cAAc,mBACnC,IAAKP,EACH,OAGFA,EAAQa,QAAQU,kBAAoBR,EAAME,OAAOO,oBAAoBC,WACrEzD,KAAKgC,QAAQwB,oBAAsBT,EAAME,OAAOO,oBAGG,OAAnDL,EAAAnD,KAAK4B,WAAWW,cAAc,wBAA9BY,EAAqDX,SACQ,OAA7DY,EAAApD,KAAK4B,WAAWW,cAAc,kCAA9Ba,EAA+DZ,SAC7B,OAAlCa,EAAArD,KAAK4B,WAAWW,cAAc,OAA9Bc,EAAoCb,SACC,OAArCc,EAAAtD,KAAK4B,WAAWW,cAAc,UAA9Be,EAAuCd,SAEvCxC,KAAKkC,QACP,CACF,CAMQ,8BAAAwB,CAA+BC,GACrC,IAAIC,EAAU5D,KAAK4B,WAAWW,cAAc,gCAC5C,GAAIqB,EACFA,EAAQf,QAAQgB,WAAa7D,KAAKgC,QAAQ8B,IAC1CF,EAAQf,QAAQkB,UAAY/D,KAAKgC,QAAQkB,GAAGO,WAC5CG,EAAQf,QAAQmB,aAAehE,KAAKgC,QAAQiC,MAC5CL,EAAQf,QAAQqB,IAAMP,EAAQO,IAC9BN,EAAQf,QAAQsB,UAAYR,EAAQT,GAAGO,WACvCG,EAAQf,QAAQuB,aAAeT,EAAQM,MACvCL,EAAQf,QAAQC,KAA6B,SAAtB9C,KAAK6C,QAAQC,KAAkB,OAAS,aAkB/D,GAhBAc,EAAUvE,SAASgF,cAAc,gCAI7BrE,KAAK6C,QAAQyB,gBACfV,EAAQf,QAAQ0B,MAAQvE,KAAK6C,QAAQyB,eAGvCV,EAAQf,QAAQgB,WAAa7D,KAAKgC,QAAQ8B,IAC1CF,EAAQf,QAAQkB,UAAY/D,KAAKgC,QAAQkB,GAAGO,WAC5CG,EAAQf,QAAQmB,aAAehE,KAAKgC,QAAQiC,MAC5CL,EAAQf,QAAQqB,IAAMP,EAAQO,IAC9BN,EAAQf,QAAQsB,UAAYR,EAAQT,GAAGO,WACvCG,EAAQf,QAAQuB,aAAeT,EAAQM,MACvCL,EAAQf,QAAQC,KAA6B,SAAtB9C,KAAK6C,QAAQC,KAAkB,OAAS,QAEpC,SAAvB9C,KAAK6C,QAAQ2B,MAAkB,CACTxE,KAAK4B,WAAWW,cAAc,eACtCkC,YAAYb,GAC5BA,EAAQc,aAAa,mBAAoB,yBAEzC1E,KAAK2E,gBAAgBhB,EACvB,KAAO,CACmB3D,KAAK4B,WAAWW,cAAc,WACtCkC,YAAYb,GAC5BA,EAAQc,aAAa,mBAAoB,0BAC3C,CAEJ,CAEQ,4BAAAE,CAA6BC,GACnC,MAAMC,EAAgBzF,SAASgF,cAAc,mBAC7CS,EAAcC,KAAO,WACrBD,EAAcjC,QAAQK,GAAK2B,EAAgB3B,GAAGO,WAC9CqB,EAAcjC,QAAQmC,SAAW,IAEjC,IAAK,MAAMC,KAAaJ,EAAgBK,kBAAmB,CACzD,MAAMC,EAAqB9F,SAASgF,cAAc,oBAClDc,EAAmBtC,QAAQuC,IAAMH,EAAUG,IAC3CD,EAAmBtC,QAAQwC,MAAQJ,EAAUI,MAC7CP,EAAcL,YAAYU,EAC5B,CAEA,GAAInF,KAAK6C,QAAQyC,cAAe,CAC9B,IAAIC,EACFT,EAAcvC,cAA2B,6CACvCgD,EACFA,EAAc1C,QAAQwC,MAAQrF,KAAK6C,QAAQyC,eAE3CC,EAAgBlG,SAASgF,cAAc,oBACvCkB,EAAc1C,QAAQuC,IAAM,eAC5BG,EAAc1C,QAAQwC,MAAQrF,KAAK6C,QAAQyC,cAC3CR,EAAcL,YAAYc,GAE9B,CAMA,IAAIC,EAAkBxF,KAAK4B,WAAWW,cAAc,sBAEpD,GAAIiD,EAAiB,CAUnB,GATAA,EAAgB3C,QAAQuB,aAAyC,kBAA1BS,EAAgBZ,MACrDjE,KAAKgC,QAAQiC,MAAQ,GAAGjE,KAAKgC,QAAQiC,SAASY,EAAgBZ,QAChEuB,EAAgB3C,QAAQ4C,WAAazF,KAAK6C,QAAQ4C,YAAc,aAChED,EAAgB3C,QAAQC,KAA6B,SAAtB9C,KAAK6C,QAAQC,KAAkB,OAAS,QACvE0C,EAAgB3C,QAAQ6C,aAAe1F,KAAK6C,QAAQ6C,aACpDF,EAAgB3C,QAAQ8C,mBAAyD,SAApC3F,KAAK6C,QAAQ8C,mBACxD,OACA,QAEiC,SAA/B3F,KAAK6C,QAAQ+C,cAA0B,CAEzC,MAAMC,EAAwBL,EAAgB9C,iBAC5C,yCACF,IAAK,MAAMoD,KAAwBD,EACjCC,EAAqBtD,SAGvB,MACMuD,EAAuBC,EADPhG,KAAKiG,0BAG3B,IAAK,MAAMC,KAAuBH,EAChCP,EAAgBf,YAAYyB,EAEhC,CAEA,MAAMC,EAAYX,EAAgBjD,cAAc,mBAChDiD,EAAgBY,aAAatB,EAAeqB,GAC5CX,EAAgB3C,QAAQwD,OAASC,KAAKC,MAAM9C,UAC9C,KAAO,CAaL,GAZA+B,EAAkBnG,SAASgF,cAAc,sBACzCmB,EAAgB3C,QAAQ2D,QAAUxG,KAAK6C,QAAQ2D,QAC/ChB,EAAgB3C,QAAQuB,aAAyC,kBAA1BS,EAAgBZ,MACrDjE,KAAKgC,QAAQiC,MAAQ,GAAGjE,KAAKgC,QAAQiC,SAASY,EAAgBZ,QAChEuB,EAAgB3C,QAAQ4C,WAAazF,KAAK6C,QAAQ4C,YAAc,aAChED,EAAgB3C,QAAQC,KAA6B,SAAtB9C,KAAK6C,QAAQC,KAAkB,OAAS,QACvE0C,EAAgB3C,QAAQ6C,aAAe1F,KAAK6C,QAAQ6C,aACpDF,EAAgB3C,QAAQwD,OAASC,KAAKC,MAAM9C,WAC5C+B,EAAgB3C,QAAQ8C,mBACc,SAApC3F,KAAK6C,QAAQ8C,mBAAgC,OAAS,QACxDH,EAAgBf,YAAYK,GAEW,SAAnC9E,KAAK6C,QAAQ4D,kBAA8B,CAC7CjB,EAAgB3C,QAAQ4D,kBAAoB,OAC5CzG,KAAK0G,cAAgB1G,KAAK2G,yBAC1B,IAAK,MAAMC,KAAgB5G,KAAK0G,cAAe,CAC7C,MAAM5B,EAAgBzF,SAASgF,cAAc,mBAC7CS,EAAcC,KAAO,WACrBD,EAAcjC,QAAQK,GAAK0D,EAAa1D,GAAGO,WAC3CqB,EAAcjC,QAAQmC,SAAW,IAEjC,IAAK,MAAMC,KAAa2B,EAAa1B,kBAAmB,CACtD,MAAMC,EAAqB9F,SAASgF,cAAc,oBAClDc,EAAmBtC,QAAQuC,IAAMH,EAAUG,IAC3CD,EAAmBtC,QAAQwC,MAAQJ,EAAUI,MAC7CP,EAAcL,YAAYU,EAC5B,CAEA,GAAInF,KAAK6C,QAAQyC,cAAe,CAC9B,MAAMH,EAAqB9F,SAASgF,cAAc,oBAClDc,EAAmBtC,QAAQuC,IAAM,eACjCD,EAAmBtC,QAAQwC,MAAQrF,KAAK6C,QAAQyC,cAChDR,EAAcL,YAAYU,EAC5B,CAEAK,EAAgBf,YAAYK,EAC9B,CACF,CAEA,GAAmC,SAA/B9E,KAAK6C,QAAQ+C,cAA0B,CACzC,MACMG,EAAuBC,EADPhG,KAAKiG,0BAG3B,IAAK,MAAMC,KAAuBH,EAChCP,EAAgBf,YAAYyB,EAEhC,CAEA,MAAMW,EAAwB7G,KAAKuC,cACjC,oCACF,GAAIsE,EAAuB,CACzB,MAAMC,EAA4BD,EAAsBE,WAAU,GAClED,EAAO/B,KAAO,qBACdS,EAAgBf,YAAYqC,EAC9B,CAEA,GAA2B,SAAvB9G,KAAK6C,QAAQ2B,MAAkB,CACTxE,KAAK4B,WAAWW,cAAc,eACtCkC,YAAYe,GAC5BA,EAAgBd,aAAa,mBAAoB,2BAEjD1E,KAAK2E,gBAAgBE,EACvB,KAAO,CACmB7E,KAAK4B,WAAWW,cAAc,WACtCkC,YAAYe,GAC5BA,EAAgBd,aAAa,mBAAoB,4BACnD,CACF,CACF,CAEQ,eAAAC,CAAgBhB,GACR3D,KAAK4B,WAAWW,cAAc,sBACtCyE,YAAchH,KAAKgC,QAAQiC,MAEhBjE,KAAK4B,WAAWW,cAAc,yBACtCyE,YAAchH,KAAKgC,QAAQiF,oBAEpC,MAAMC,EAAQlH,KAAK4B,WAAWW,cAAgC,0BAC9D,IAAI4E,EAASnH,KAAKgC,QAAQoF,WACtBzD,EAAQ0D,YACVF,EAASxD,EAAQ0D,WAGnBH,EAAMI,IAqUV,SAAwBA,EAAaC,EAAeC,GAClD,IAAKF,EACH,MAAO,GAIT,MAAMG,EAAgBH,EAAII,WAAW,MAAQ,SAAWJ,EAAMA,EAE9D,IAAIxD,EACJ,IACEA,EAAM,IAAInE,IAAI8H,EAChB,CAAE,MAAOE,GACP,OAAOL,CACT,CAGA,MAAMM,EAAe9D,EAAI+D,SAASC,YAAY,KAC9C,IAAsB,IAAlBF,EACF,OAAON,EAGT,MAAMS,EAAuBjE,EAAI+D,SAASG,MAAM,EAAGJ,GAC7CK,EAAYnE,EAAI+D,SAASG,MAAMJ,GAG/BM,EAAY,IAAMX,EAAQ,IAAMC,EACtC1D,EAAI+D,SAAWE,EAAuBG,EAAYD,EAElD,MAAME,EAAerE,EAAIL,WAGzB,GAAI6D,EAAII,WAAW,MACjB,OAAOS,EAAaC,QAAQ,UAAW,IAGzC,OAAOD,CACT,CAzWgBE,CAAelB,EAAQ,IAAK,KAExC,MAAMmB,EAAiBtI,KAAK4B,WAAWW,cAAc,8BACrD+F,EAAevG,UAAY,GAE3B,MAAMwG,EAAU,CACd5E,EAAQ6E,QACR7E,EAAQ8E,QACR9E,EAAQ+E,SACRC,OAAOC,IAAgB,MAANA,OAAM,EAANA,EAAQC,QAAS,GAAgB,kBAAXD,GAEzC,IAAK,MAAMA,KAAUL,EAAS,CAC5B,MAAMO,EAAOzJ,SAASgF,cAAc,QACpCyE,EAAKC,UAAUC,IAAI,QACnBF,EAAK9B,YAAc4B,EACnBN,EAAe7D,YAAYqE,EAC7B,CACF,CAEQ,OAAAG,GACN,GAAoC,oBAAzBC,uBAAyClJ,KAAKO,SAAU,CACjEP,KAAKO,SAAW,IAAI2I,qBAAqBlJ,KAAKK,mBAAoB,CAAE8I,UAAW,IAC/E,MAAMC,EAAkBpJ,KAAK4B,WAAWW,cAAc,WACtDvC,KAAKO,SAAS0I,QAAQG,GAKtB,MAAMC,EAAShK,SAASkD,cAAc,2BACtCvC,KAAKO,SAAS0I,QAAQI,EACxB,CACF,CAEQ,aAAA/I,CAAcgJ,GAGpB,IAAK,MAAMC,KAASD,EACM,2BAApBC,EAAMC,OAAOtG,GACflD,KAAKyJ,eAAiBF,EAAMG,eAE5B1J,KAAK2J,eAAiBJ,EAAMG,eAIhC,MAAME,GAAe5J,KAAK2J,iBAAmB3J,KAAKyJ,eAE9CG,GAAsC,SAAvB5J,KAAK6C,QAAQ2B,MAC9BxE,KAAK6J,QACKD,GAAsC,SAAvB5J,KAAK6C,QAAQ2B,OACtCxE,KAAK8J,SAET,CAEQ,KAAAD,GACN7J,KAAK6C,QAAQ2B,MAAQ,OAMrB,MAAM4E,EAAkBpJ,KAAK4B,WAAWW,cAA2B,WACnE6G,EAAgBW,MAAMC,UAAY,GAAGZ,EAAgBa,iBAEnCjK,KAAK4B,WAAWW,cAAc,eACtCwG,UAAUC,IAAI,UAExB,MAAMpG,EAAS5C,KAAK4B,WAAWW,cAC7B,oDACEK,GACFA,EAAOJ,SAGTxC,KAAKkC,QACP,CAEQ,OAAA4H,UACC9J,KAAK6C,QAAQ2B,MAEIxE,KAAK4B,WAAWW,cAA2B,WACnDwH,MAAMC,UAAY,GAElC,MAAME,EAAMlK,KAAK4B,WAAWW,cAAc,eACvC,MAAH2H,GAAAA,EAAKnB,UAAUvG,OAAO,UAEtB,MAAMI,EAAS5C,KAAK4B,WAAWW,cAC7B,oDACEK,GACFA,EAAOJ,SAGTxC,KAAKkC,QACP,CAEQ,4BAAAiI,CAA6BxG,GACnC,QAAKA,EAAQyG,oBAI6B,SAAtCpK,KAAK6C,QAAQwH,wBAI+B,SAAvCrK,KAAK6C,QAAQyH,uBACpB3G,EAAQ4G,8BACZ,CAQQ,sBAAAC,CAAuB7G,GAE7B,IAAI8G,EAAUzK,KAAK4B,WAAWW,cADb,mCAEjB,MAAMmI,GAAgBD,EAEjBA,IACHA,EAAUpL,SAASgF,cAAc,KACjCoG,EAAQ5H,QAAQ8H,iBAAmB,QAGM,SAAvC3K,KAAK6C,QAAQyH,uBACfG,EAAQzD,YAAc,8BACtByD,EAAQhL,KAAO,GAAGO,KAAKgC,QAAQ8B,eAAeH,EAAQT,MAEtDuH,EAAQzD,YAAcrD,EAAQyG,kBAG5BM,GACF1K,KAAK4B,WAAWgJ,QAAQH,EAE5B,CAUQ,MAAAvI,GACN,IAAKlC,KAAKgC,QACR,OAGF,IAAK6I,MAAMC,QAAQ9K,KAAKgC,QAAQ+I,UAC9B,OAGF,IAAIlG,EAAkB7E,KAAKgC,QAAQ+I,SAASC,KAAKrH,IACxC,MAAPA,OAAO,EAAPA,EAAST,MAAOlD,KAAKgC,QAAQwB,sBAK1BqB,GAAmB7E,KAAKgC,QAAQ+I,SAASlC,OAAS,IACrDhE,EAAkB7E,KAAKgC,QAAQ+I,SAAS,IAGrClG,IAIDA,EAAgBoG,UAClBjL,KAAK4E,6BAA6BC,GAElC7E,KAAK0D,+BAA+BmB,GAGZ,SAAtB7E,KAAK6C,QAAQqI,MACflL,KAAKiJ,UAGFpE,EAAgBoG,WAIjBjL,KAAKmK,6BAA6BtF,IACpC7E,KAAKwK,uBAAuB3F,GAEhC,CAOQ,iBAAA5C,GACN,MAAMkJ,EAAiBnL,KAAKuC,cAAc,mBAC1C,IAAK4I,EACH,OAGF,MAAMnJ,EAAmB,CAAC,EAC1BA,EAAQiJ,UAAiD,SAArCE,EAAetI,QAAQoI,UAC3CjJ,EAAQkB,GAAKkI,SAASD,EAAetI,QAAQK,GAAI,IACjDlB,EAAQ8B,IAAMqH,EAAetI,QAAQiB,IACrC9B,EAAQwB,oBAAsB4H,SAASD,EAAetI,QAAQU,kBAAmB,IACjFvB,EAAQiC,MAAQkH,EAAetI,QAAQoB,MACvCjC,EAAQoF,WAAa+D,EAAetI,QAAQwI,UAC5CrJ,EAAQiF,oBAAsBkE,EAAetI,QAAQyI,mBACrDtJ,EAAQuJ,KAAOJ,EAAetI,QAAQ0I,KACtCvJ,EAAQpB,KAAOuK,EAAetI,QAAQjC,KACtCoB,EAAQ+I,SAAW,GAInB,MAAMA,EAAWI,EAAezI,iBAAiC,4BACjE,IAAK,MAAM8I,KAAkBT,EAAU,CACrC,MAAMpH,EAAmB,CAAC,EAC1BA,EAAQT,GAAKkI,SAASI,EAAe3I,QAAQK,GAAI,IACjDS,EAAQsH,UAAiD,SAArCO,EAAe3I,QAAQoI,UAC3CtH,EAAQyG,kBAAoBoB,EAAe3I,QAAQ4I,UACnD9H,EAAQ4G,6BAA+BiB,EAAe3I,QAAQ6I,0BAC9D/H,EAAQuB,kBAAoB,GAC5BvB,EAAQO,IAAMsH,EAAe3I,QAAQqB,IACrCP,EAAQM,MAAQuH,EAAe3I,QAAQoB,MACvCN,EAAQ0D,UAAYmE,EAAe3I,QAAQ8I,SAC3ChI,EAAQ6E,QAAUgD,EAAe3I,QAAQ2F,QACzC7E,EAAQ8E,QAAU+C,EAAe3I,QAAQ4F,QACzC9E,EAAQ+E,QAAU8C,EAAe3I,QAAQ6F,QAEzC,MAAMkD,EAAaJ,EAAe9I,iBAAiB,oBACnD,IAAK,MAAMmJ,KAAoBD,EAC7BjI,EAAQuB,kBAAkB4G,KAAK,CAC7B1G,IAAKyG,EAAiBhJ,QAAQuC,IAC9BC,MAAOwG,EAAiBhJ,QAAQwC,QAIpCrD,EAAQ+I,SAASe,KAAKnI,EACxB,CAEA,OAAO3B,CACT,CAEQ,sBAAA2E,GACN,MAAMD,EAAgB,GAEhBqF,EAAkB/L,KAAK0C,iBAC3B,yCACF,IAAK,MAAM8I,KAAkBO,EAAiB,CAC5C,MAAMpI,EAAmB,CAAC,EAC1BA,EAAQT,GAAKkI,SAASI,EAAe3I,QAAQK,GAAI,IACjDS,EAAQsH,UAAiD,SAArCO,EAAe3I,QAAQoI,UAC3CtH,EAAQyG,kBAAoBoB,EAAe3I,QAAQ4I,UACnD9H,EAAQ4G,6BAA+BiB,EAAe3I,QAAQ6I,0BAC9D/H,EAAQuB,kBAAoB,GAC5BvB,EAAQO,IAAMsH,EAAe3I,QAAQqB,IACrCP,EAAQM,MAAQuH,EAAe3I,QAAQoB,MAEvC,MAAM2H,EAAaJ,EAAe9I,iBAAiB,oBACnD,IAAK,MAAMmJ,KAAoBD,EAC7BjI,EAAQuB,kBAAkB4G,KAAK,CAC7B1G,IAAKyG,EAAiBhJ,QAAQuC,IAC9BC,MAAOwG,EAAiBhJ,QAAQwC,QAIpCqB,EAAcoF,KAAKnI,EACrB,CAEA,OAAO+C,CACT,CAEQ,sBAAAT,GACN,MAAM+F,EAAgB,GAEhBD,EAAkB/L,KAAK0C,iBAC3B,yCACF,IAAK,MAAM8I,KAAkBO,EAAiB,CAC5C,MAAMpI,EAAmB,CAAC,EAC1BA,EAAQT,GAAKkI,SAASI,EAAe3I,QAAQK,GAAI,IACjDS,EAAQsH,UAAiD,SAArCO,EAAe3I,QAAQoI,UAC3CtH,EAAQyG,kBAAoBoB,EAAe3I,QAAQ4I,UACnD9H,EAAQ4G,6BAA+BiB,EAAe3I,QAAQ6I,0BAC9D/H,EAAQuB,kBAAoB,GAC5BvB,EAAQO,IAAMsH,EAAe3I,QAAQqB,IACrCP,EAAQM,MAAQuH,EAAe3I,QAAQoB,MAEvC,MAAM2H,EAAaJ,EAAe9I,iBAAiB,oBACnD,IAAK,MAAMmJ,KAAoBD,EAC7BjI,EAAQuB,kBAAkB4G,KAAK,CAC7B1G,IAAKyG,EAAiBhJ,QAAQuC,IAC9BC,MAAOwG,EAAiBhJ,QAAQwC,QAIpC2G,EAAcF,KAAKnI,EACrB,CAEA,OAAOqI,CACT,EAGF,SAAShG,EAA2B+E,GAClC,MAAMgB,EAAkB,GACxB,IAAK,MAAMpI,KAAWoH,EAAU,CAC9B,MAAMjG,EAAgBzF,SAASgF,cAAc,mBAC7CS,EAAcjC,QAAQoJ,QAAU,OAChCnH,EAAcC,KAAO,WACrBD,EAAcjC,QAAQK,GAAKS,EAAQT,GAAGO,WACtCqB,EAAcjC,QAAQmC,SAAW,IAEjC,IAAK,MAAMC,KAAatB,EAAQuB,kBAAmB,CACjD,MAAMC,EAAqB9F,SAASgF,cAAc,oBAClDc,EAAmBtC,QAAQuC,IAAMH,EAAUG,IAC3CD,EAAmBtC,QAAQwC,MAAQJ,EAAUI,MAC7CP,EAAcL,YAAYU,EAC5B,CAEA4G,EAAgBD,KAAKhH,EACvB,CAEA,OAAOiH,CACT,CEttBM,SAAUG,EAAOC,EAAqB1B,GAC1C,IAAK0B,EACH,MAAM,IAAIC,eAAe3B,EAE7B,CF+3BK4B,eAAeC,IAAI,gBACtBD,eAAeE,OAAO,cAAe3M,WE93BjC,MAAOwM,uBAAuBlN,MAClC,WAAAsN,CAAY/B,QAAO,IAAPA,IAAAA,EAAU,mBACpBgC,MAAMhC,GACNzK,KAAKsB,KAAOtB,KAAKwM,YAAYlL,IAC/B,ECshBF,SAASoL,EAAaC,GACpB,IACE,OAAOA,EAASC,QAAQN,IAAI,eAC9B,CAAE,MAAO3E,GACP,CAEJ,CA+BA,SAASkF,EAAWxH,GAClB,OAAc,OAAVA,GAAmC,iBAAVA,IAIL,kBAAbA,EAAMyH,KAIW,iBAAjBzH,EAAM0H,SAIS,mBAAf1H,EAAM2H,MAIS,mBAAf3H,EAAM4H,OAKnB,CAYA,SAASC,EAAcvF,EAAYvC,EAAaC,GAC9C,IACEsC,EAAMvC,GAAOC,CACf,CAAE,MAAOsC,GACP,CAEJ,CAcA,SAASwF,EAAWC,GAClB,OAAO,IAAIzN,IAAIyN,EAAM,GAAG5N,SAAS6N,aAAa7N,SAAS8N,OACzD,CA0aOC,eAAeC,IACpB,MAAM1J,EAAMqJ,EAAWM,QAAQC,OAAOC,KAAO,WAE7C,IAAIhB,EAuCAiB,EASAC,EA/CJ,IACElB,QAAiBmB,MAAMhK,EAAK,CAAEiK,MAAO,YACvC,CAAE,MAAOpG,GAEP,MADAuF,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKkF,EAAWF,GAAW,CACzB,MAAMhF,EAAa,IAAIzI,MAAM,iDAG7B,MAFAgO,EAAcvF,EAAO,WAAYgF,GACjCO,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKgF,EAASG,GAAI,CAChB,MAAMnF,EAAa,IAAIzI,MAAM,mBAAmByN,EAASI,wBACzDG,EAAcvF,EAAO,aAAc+E,EAAaC,IAChDO,EAAcvF,EAAO,SAAUgF,EAASI,QACxCG,EAAcvF,EAAO,MAAOgF,EAAS7I,KAErC,IACEoJ,EAAcvF,EAAO,sBAAuBgF,EAASM,OACvD,CAAE,MAAOe,GACP,CAGF,MAAMrG,CACR,CAYA,IACEiG,QAAyBjB,EAASK,MACpC,CAAE,MAAOrF,GAGP,MAFAuF,EAAcvF,EAAO,SAAUgF,EAASI,QACxCG,EAAcvF,EAAO,MAAOgF,EAAS7I,KAC/B6D,CACR,CAGA,IACEkG,EAAqBI,KAAKC,MAAMN,EAClC,CAAE,MAAOjG,GAIP,MAHAuF,EAAcvF,EAAO,gBAAiBiG,GACtCV,EAAcvF,EAAO,SAAUgF,EAASI,QACxCG,EAAcvF,EAAO,MAAOgF,EAAS7I,KAC/B6D,CACR,CAEA,IAAKwG,EAAON,GAAqB,CAC/B,MAAMlG,EAAa,IAAIzI,MAAM,qCAI7B,MAHAgO,EAAcvF,EAAO,gBAAiBkG,GACtCX,EAAcvF,EAAO,SAAUgF,EAASI,QACxCG,EAAcvF,EAAO,MAAOgF,EAAS7I,KAC/B6D,CACR,CAEA,OAAOkG,CACT,CA8TM,SAAUM,EAAO9I,GACrB,OAAO+I,EAAS/I,IAAUwF,MAAMC,QAAQzF,EAAMgJ,MAChD,CAEA,SAASD,EAAS/I,GAChB,MAAwB,iBAAVA,GAAgC,OAAVA,CACtC,CC96CO,SAASiJ,EAAQjJ,GACtB,GAA+B,mBAApBkJ,gBAAgC,CACzC,MAAMC,EAASD,gBAAgBlJ,GAS/B,IAAIoJ,GAAc,EAKlB,GARqBpJ,SACAmJ,UAInBC,GAAc,GAGZA,EACF,OAAOD,CAEX,CAEA,OAAUP,KAAKC,MAAMD,KAAKS,UAAUrJ,GACtC,CCnBO,SAASkB,IACd,IAAI,IAAAoI,EACF,GAAgC,mBAAV,OAAlBA,EAAOC,kBAAW,EAAXD,EAAapI,KACtB,OAAOqI,YAAYrI,KAEvB,CAAE,MAAOoB,GACPkH,QAAQC,MAAMnH,EAChB,CAEA,OAAOrB,KAAKC,KACd,CCZO,SAASwI,EAAa1J,GAC3B,GAAqB,iBAAVA,EACT,OAAO,EAGT,GAAqB,IAAjBA,EAAMwD,OACR,OAAO,EAIT,GAAIxD,EAAM2J,SAAS,MAAQ3J,EAAM2J,SAAS,MACxC,OAAO,EAGT,GAAI3J,EAAM2J,SAAS,KACjB,OAAO,EAGT,MAAMC,EAAQ5J,EAAM6J,cAGpB,MAAc,cAAVD,GAAmC,SAAVA,IC1BxB,SAAgC5J,GACrC,MAAwB,iBAAVA,GAAsB,cAAc8J,KAAK9J,EACzD,CD4BM+J,CAAuBH,EAK7B,CAkBO,SAASI,EAA2BC,GACzC,OAAKA,GAIAzE,MAAMC,QAAQwE,EAAKC,gBAIjBD,EAAKC,eACT5G,OAAOxJ,GAAQA,EAAKqQ,YACpBC,IAAItQ,GAAQA,EAAKA,MACjBsQ,IAAItQ,GAAQA,EAAK+P,eAVX,EAWX,CExEA,MAAMQ,EAAQ,IAAIC,WAAW,ICC7B,MAAMC,EAAY,GAClB,IAAK,IAAIC,EAAI,EAAGA,EAAI,MAAOA,EACvBD,EAAU9D,MAAM+D,EAAI,KAAOpM,SAAS,IAAIuE,MAAM,IAE3C,SAAS8H,EAAgBC,EAAKC,GACjC,YADuC,IAANA,IAAAA,EAAS,IAClCJ,EAAUG,EAAIC,EAAS,IAC3BJ,EAAUG,EAAIC,EAAS,IACvBJ,EAAUG,EAAIC,EAAS,IACvBJ,EAAUG,EAAIC,EAAS,IACvB,IACAJ,EAAUG,EAAIC,EAAS,IACvBJ,EAAUG,EAAIC,EAAS,IACvB,IACAJ,EAAUG,EAAIC,EAAS,IACvBJ,EAAUG,EAAIC,EAAS,IACvB,IACAJ,EAAUG,EAAIC,EAAS,IACvBJ,EAAUG,EAAIC,EAAS,IACvB,IACAJ,EAAUG,EAAIC,EAAS,KACvBJ,EAAUG,EAAIC,EAAS,KACvBJ,EAAUG,EAAIC,EAAS,KACvBJ,EAAUG,EAAIC,EAAS,KACvBJ,EAAUG,EAAIC,EAAS,KACvBJ,EAAUG,EAAIC,EAAS,MAAMC,aACrC,CCEA,IAAAC,EA1BA,SAAY3H,EAAS4H,EAAKH,GACtB,OAAKG,GAAQ5H,IAAW6H,OAAOC,WAKnC,SAAa9H,EAAS4H,EAAKH,GAAQ,IAAAM,EAAAC,EAC/BhI,EAAUA,GAAW,CAAC,EACtB,MAAMiI,EAAwC,OAApCF,EAAiB,OAAjBC,EAAGhI,EAAQkI,QAAMF,EAAe,MAAXhI,EAAQmI,SAAG,EAAXnI,EAAQmI,OAAOJ,EFRvCF,OAAOO,gBAAgBjB,GES9B,GAAIc,EAAK3H,OAAS,GACd,MAAM,IAAI3J,MAAM,qCAIpB,GAFAsR,EAAK,GAAgB,GAAVA,EAAK,GAAa,GAC7BA,EAAK,GAAgB,GAAVA,EAAK,GAAa,IACzBL,EAAK,CAEL,IADAH,EAASA,GAAU,GACN,GAAKA,EAAS,GAAKG,EAAItH,OAChC,MAAM,IAAI+H,WAAW,mBAAmBZ,KAAUA,EAAS,8BAE/D,IAAK,IAAIH,EAAI,EAAGA,EAAI,KAAMA,EACtBM,EAAIH,EAASH,GAAKW,EAAKX,GAE3B,OAAOM,CACX,CACA,OAAOL,EAAgBU,EAC3B,CArBWK,CAAItI,EAAS4H,EAAKH,GAFdI,OAAOC,YAGtB,ECqQO,SAASS,IAEd,OADA5E,EAAOiC,EAAO4C,eAAezB,MAAO,wCAC7BhB,EAAKyC,eAAezB,KAC7B,CA4CO/B,eAAeyD,EAAUzI,QAAqB,IAArBA,IAAAA,EAAwB,CAAC,GAGvD,MAAM0I,EAAQ1I,EAAQ2I,UAAY,EAM5BC,EAAS5I,EAAQ4I,QAAU,CAAC,IAAK,KAEvC,IAAIC,EAEJ,IAAK,IAAIF,EAAW,EAAGA,EAAWD,EAAOC,IAAY,CACnD,GAAIA,EAAW,EAAG,CAChB,MAAMG,EAAQF,EAAOD,EAAW,SAC1BI,EAAoBD,EAC5B,CAEA,IAEE,aADmBE,EAAyBhJ,EAAQiJ,QAEtD,CAAE,MAAO7J,GACPyJ,EAAYzJ,CACd,CACF,CAEA,MAAMyJ,CACR,CAUA7D,eAAegE,EAAyBC,GACtC,IAAKA,EACH,OAAOhE,IAGT,IAAI1M,EACJ,MAAM2Q,EAAiB,IAAIC,QAAe,CAACC,EAAUC,KACnD,MAAMjK,EAAQ,IAAIzI,MAAM,2BACxB4B,EAAQC,WAAW6Q,EAAQJ,EAAS7J,KAGtC,IAEE,aADmB+J,QAAQG,KAAK,CAACrE,IAAiBiE,GAEpD,CAAC,QACCzQ,aAAaF,EACf,CACF,CAKA,SAASwQ,EAAoBQ,GAC3B,OAAO,IAAIJ,QAAQK,IACjBhR,WAAWgR,EAASD,IAExB,CA4BA,SAASE,EAAe1C,GACtB,MAAM2C,EAA4B,CAAC,EAMnC,IAAK3C,EACH,OAAO,KAGT,IAAK,MAAM4C,KAAQ5C,EAAKjB,MAAO,CAC7B,MAAM1K,EAAUsO,EAAOC,EAAKC,WAAW1O,YACvC,GAAIE,EAAS,CAEX,IAAK,MAAMyO,KAAYF,EAAKG,YAAc,CAAC,EAAG,CAC5C1O,EAAQ2O,kBAAkBF,GAAYzO,EAAQ2O,kBAAkBF,IAAa,GAC7E,MAAM/M,EAAQ6M,EAAKG,WAAWD,GACzBzO,EAAQ2O,kBAAkBF,GAAUpD,SAAS3J,IAChD1B,EAAQ2O,kBAAkBF,GAAUtG,KAAKzG,EAE7C,CAEA1B,EAAQqB,UAAYkN,EAAKlN,QAC3B,KAAO,CAEL,MAAMqN,EAAuC,CAAC,EAC9C,IAAK,MAAMjN,KAAO8M,EAAKG,YAAc,CAAC,EACpCA,EAAWjN,GAAO,CAAC8M,EAAKG,WAAWjN,IAGrC6M,EAAOC,EAAKC,WAAW1O,YAAc,CACnC8O,UAAWL,EAAKhL,MAChBoL,kBAAmBD,EAEnBG,MAAON,EAAKM,MACZC,WAAYP,EAAKO,WACjBC,cAAeR,EAAKQ,cACpBC,aAAcT,EAAKS,aACnB3N,SAAUkN,EAAKlN,SACfd,IAAKgO,EAAKhO,IACVJ,IAAKoO,EAAKpO,IACVqO,WAAYD,EAAKC,WACjBS,cAAeV,EAAKU,cAExB,CACF,CAEA,OAAOX,CACT,CAWA,SAASY,EAAkBC,EAA0BC,GAOnD,IAAKD,EACH,MAAO,GAQT,MAAME,EAAgC,CAAC,EACvC,IAAK,MAAM5N,KAAO2N,EACZD,EAAM1N,GAGJ2N,EAAM3N,GAAKJ,SAAW8N,EAAM1N,GAAKJ,WACnCgO,EAAW5N,GAAOkJ,EAAKyE,EAAM3N,IAC7B4N,EAAW5N,GAAKJ,SAAW+N,EAAM3N,GAAKJ,SAAW8N,EAAM1N,GAAKJ,UAO9DgO,EAAW5N,GAAOkJ,EAAKyE,EAAM3N,IAIjC,OAAO6N,OAAOC,KAAKF,GAAYvD,IAAIrK,GAAO4N,EAAW5N,GACvD,CAEA,SAAS+N,EAAoBL,EAA0BC,GACrD,IAAKD,EACH,MAAO,GAGT,MAAMM,EAAgC,CAAC,EACvC,IAAK,MAAMhO,KAAO0N,EACZC,EAAM3N,GACJ2N,EAAM3N,GAAKJ,SAAW8N,EAAM1N,GAAKJ,WACnCoO,EAAWhO,GAAOkJ,EAAKyE,EAAM3N,IAC7BgO,EAAWhO,GAAKJ,SAAW8N,EAAM1N,GAAKJ,SAAW+N,EAAM3N,GAAKJ,UAM9DoO,EAAWhO,GAAOkJ,EAAKwE,EAAM1N,IAIjC,OAAO6N,OAAOC,KAAKE,GAAY3D,IAAIrK,GAAOgO,EAAWhO,GACvD,CAQA,SAASiO,EAAWC,EAAalU,GAG/B,OAFA8M,EAAOrB,MAAMC,QAAQwI,GAAI,4CACzBpH,EAAOrB,MAAMC,QAAQ1L,GAAI,4CAClBkU,EAAE3K,OAAOtD,IAAUjG,EAAE4P,SAAS3J,GACvC,CCrfOkI,eAAegG,EAAqBjE,EAAYkE,EACrDC,EAA+BC,QAAY,IAAZA,IAAAA,GAAe,GA4FhD,SAAmCF,GACjC,GAAIA,EAAS3K,OAAS,EACpB,MAAM,IAAI3J,MAAM,gDAMlB,IAAK,MAAMC,KAAQqU,EACjB,IAAKzE,EAAa5P,GAAO,CACvB,MAAMwI,EAAa,IAAIzI,MAAM,yBAAyBC,KAGtD,MAFAwI,EAAMgM,cAAgBxU,EACtBwI,EAAM4H,eAAiBiE,EACjB7L,CACR,CAEJ,CA3GEiM,CAA0BJ,GAE1B,MAAMK,EAuDR,SAAyBvE,EAAYwE,GACnC,MAAMC,EAAelJ,MAAMC,QAAQwE,EAAKC,gBAAkBD,EAAKC,eAAiB,GAuBhF,GAAIwE,EAAalL,OAASiL,EAAgB,EACxC,OAAOC,EACJpL,OAAOxJ,GAAQA,EAAKqQ,YAAc,QAAQL,KAAKhQ,EAAKA,OACpDsQ,IAAItQ,GAAQA,EAAKA,MACjBsQ,IAAItQ,GAAQA,EAAK+P,eAGtB,ONlCK,SAA0BI,GAC/B,OAAKA,GAIAzE,MAAMC,QAAQwE,EAAKC,gBAIjBD,EAAKC,eAAeE,IAAItQ,GAAQA,EAAKA,MAAMsQ,IAAItQ,GAAQA,EAAK+P,eAP1D,EAQX,CMwBS8E,CAAiB1E,EAC1B,CAvFoB2E,CAAgB3E,EAAMkE,EAAS3K,QAC3CqL,EAAYV,EAAS/D,IAAItQ,GAAQA,EAAK+P,eAEtCiF,EAAoBN,EAAUlL,OAAOxJ,IACxCsU,EAAUW,KAAKC,GACdA,EAASC,WAAanV,GAAQ+U,EAAUlF,SAASqF,EAASE,YAKxDC,EAFc,IADC,IAAIC,IAAY,IAAIN,KAAsBD,KAGlCQ,KAAK,WT8qC7BnH,eAA0BoH,GAC/B,MAAM7Q,EAAMqJ,EAAWM,QAAQC,OAAOC,KAAO,kBAEvCiH,EAAoB,CACxBC,KAAM5G,KAAKS,UAAUiG,GACrB5G,MAAO,WACPnB,QAAS,CACP,eAAgB,oBAElBkI,OAAQ,QAGV,IAAInI,EACJ,IACEA,QAAiBmB,MAAMhK,EAAK8Q,EAC9B,CAAE,MAAOjN,GAGP,MAFAuF,EAAcvF,EAAO,QAASgN,GAC9BzH,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKkF,EAAWF,GAAW,CACzB,MAAMhF,EAAa,IAAIzI,MAAM,iDAI7B,MAHAgO,EAAcvF,EAAO,eAAgBgN,GACrCzH,EAAcvF,EAAO,WAAYgF,GACjCO,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKgF,EAASG,GAAI,KAAAiI,EAAAC,EAChB,MAAMrN,EAAa,IAAIzI,MAAM,mBAAmByN,EAASI,wBACzDG,EAAcvF,EAAO,eAAgBgN,GACrCzH,EAAcvF,EAAO,aAAc+E,EAAaC,IAChDO,EAAcvF,EAAO,kBAAmBgF,EAASI,QAEjD,IAEEG,EAAcvF,EAAO,sBADiBgF,EAASM,OAEjD,CAAE,MAAOe,GACP,CAWF,IAAIvD,EAWJ,MAVwB,MAApBkC,EAASI,QAA8D,iBAAlB,OAA1BgI,EAAOpN,EAAMsN,oBAAa,EAAnBF,EAAqBG,aACzDzK,EAAU9C,EAAMsN,cAAcC,YACmB,iBAAd,OAA1BF,EAAOrN,EAAMsN,oBAAa,EAAnBD,EAAqBvK,WACrCA,EAAU9C,EAAMsN,cAAcxK,SAG5BA,GACFyC,EAAcvF,EAAO,UAAW8C,GAG5B9C,CACR,CAEA,OAAOgF,CACT,CS9uCQwI,CAAW,CAAEX,aACnB,MAAMY,QAAoBpE,IAY1B,OAJI0C,GA2GN,SAAkCpE,EAAYkE,GAC5C,MAAM6B,EAAqBhG,EAA2BC,GAMtD,IAAK,MAAMgG,KAAW9B,EAAU,CAC9B,MAAM+B,EAAeD,EAAQpG,cAC7B,IAAKmG,EAAmBrG,SAASuG,GAAe,CAC9C,MAAM5N,EAAa,IAAIzI,MAAM,qBAAqBqW,qBAGlD,MAFA5N,EAAMgM,cAAgB4B,EACtB5N,EAAM4H,eAAiBiE,EACjB7L,CACR,CACF,CACF,CA1HI6N,CAAyBJ,EAAa5B,GAGjC4B,CACT,CChCO,SAASK,EAAc9N,EAAYvC,EAAaC,GACrD,IACEsC,EAAMvC,GAAOC,CACf,CAAE,MAAOqQ,GACP,IAAIC,EAEFA,EADED,aAA2BxW,MACpBwW,EAAgBjL,QAEhB,GAAGiL,IAGd7G,QAAQ+G,IAAI,kCAAmC,CAC7CxQ,MACA9D,KAAW,MAALqG,OAAK,EAALA,EAAOrG,KACbuU,IAAK5C,OAAO6C,UAAUrS,SAASsS,KAAKpO,GACpCgO,UAEJ,CACF,CCuBO,SAASK,EAAOC,EAAgChT,IAkBvD,SAAiCgT,EAC/BhT,GASA,IAAIuG,EAGAA,EAFAyM,aAAsBC,QACpBD,EAAWxU,YACJwU,EAEAE,OAEFF,GAGAE,OAGX,MAAMpT,EAAQ,IAAIqT,YAA2B,oBAAqB,CAChEnT,SACAoT,SAAS,EACTC,UAAU,IAGZ9M,EAAO+M,cAAcxT,EACvB,CA/CEyT,CAAwBP,EAAYhT,GA2EtC,SAA2BA,GAUzB,MAAMwT,EAAe,CACnB,kBACA,gBACA,6BACA,yBACA,kBACA,mBAGF,GAAIxT,IAAWwT,EAAazH,SAAS/L,EAAO9D,MAAO,KAAAuX,EAAAC,EACjD,IAAIhP,EAGFA,EADE1E,EAAO0E,iBAAiBzI,MAClB+D,EAAO0E,MAEP,IAAIzI,MAAM,GAAG+D,EAAO0E,SAG9B8N,EAAc9N,EAAO,OAAQ1E,EAAO9D,MACpCsW,EAAc9N,EAAO,SAAU1E,EAAO2T,QACtCnB,EAAc9N,EAAO,WAAY1E,EAAO4T,UACxCpB,EAAc9N,EAAO,OAAQ1E,EAAOqM,MACpCmG,EAAc9N,EAAO,iBAAoC,OAApB+O,EAAEzT,EAAO6T,kBAAW,EAAlBJ,EAAoBxT,IAC3DuS,EAAc9N,EAAO,mBAAsC,OAApBgP,EAAE1T,EAAO6T,kBAAW,EAAlBH,EAAoBrV,MAElC,mBAAhByV,YACTA,YAAYpP,GAEZ5G,WAAWiW,EAAO,EAAGrP,EAEzB,CACF,CAnHEsP,CAAkBhU,EACpB,CAoHA,SAAS+T,EAAM3R,GACb,MAAMA,CACR,CC/JA,SAAS6R,IACPf,OAAOgB,iBAAmBhB,OAAOgB,kBAAoB,CAAEC,YAAa,EAAGC,MAAO,EAAGC,SAAU,IAE3F,MAAMC,EAASpB,OAAOgB,iBActB,QAZKK,OAAOC,UAAUF,EAAOH,cAAgBG,EAAOH,YAAc,KAChEG,EAAOH,YAAc,GAGlBI,OAAOC,UAAUF,EAAOF,SAC3BE,EAAOF,MAAQ,GAGZxM,MAAMC,QAAQyM,EAAOD,YACxBC,EAAOD,SAAW,IAGbC,CACT,CA6CO,SAASG,EAAMpW,GACpB,MAAMiW,EAASL,IAKfK,EAAOF,QAAQ,QAAAM,EAAAC,UAAA/O,OANsBgP,EAAI,IAAAhN,MAAA8M,EAAA,EAAAA,EAAA,KAAAG,EAAA,EAAAA,EAAAH,EAAAG,IAAJD,EAAIC,EAAA,GAAAF,UAAAE,GAQzC,MAAMhB,EAA2B,CAC/Be,OACAE,MAAOzR,KAAKC,MACZrD,GAAIqU,EAAOF,MACX/V,OACA0W,WAAW,EACXC,YAAa1R,IAOfuQ,SAAuB,SAACoB,GAAkB,YAAR,IAAVA,IAAAA,GAAa,GAAUC,EAASrB,EAAaoB,EAAW,GAIhFX,EAAOH,cAMHG,EAAOH,YAAc,GAkG3B,SAA2BN,GACzB,MAUMsB,EAAW,IAVHlB,IAUaI,UAE3BzI,QAAQwJ,KAAK,iCAAkC,CAC7CnV,GAAI4T,EAAY5T,GAChB5B,KAAMwV,EAAYxV,KAClBgW,SAAUc,GAEd,CAnHIE,CAAkBxB,GAMpByB,EAAO,CAAER,MAAOjB,EAAYiB,MAAO7U,GAAI4T,EAAY5T,GAAI5B,KAAMwV,EAAYxV,KAAMV,KAAM,UAMrFkW,EAAYhW,MAAQC,WAAWoX,EAAU,IAAWrB,GAAa,GAEjEjI,QAAQ+G,IAAI,2BAA4BkB,EAAYxV,MAGpD,MAAMyB,EAAQ,IAAIqT,YAAoB,2BAA4B,CAAEnT,OAAQ6T,IAG5E,OAFAP,cAAcxT,GAEP+T,CACT,CAwBA,SAASqB,EAASrB,EAA0BoB,GAK1C,QALoD,IAAVA,IAAAA,GAAa,GAKnDpB,EAAYkB,UAAW,CACzB,MAAMQ,EAAWC,KAAKC,MAAMnS,IAAQuQ,EAAYmB,aAEhD,YADApJ,QAAQ+G,IAAI,qCAAsCkB,EAAYxV,KAAM4W,EAAYM,EAElF,CAEA1B,EAAYkB,WAAY,EAExBhX,aAAa8V,EAAYhW,OACzBgW,EAAYhW,WAAQjC,EAEpB0Z,EAAO,CACLR,MAAOzR,KAAKC,MACZrD,GAAI4T,EAAY5T,GAChB5B,KAAMwV,EAAYxV,KAClBkQ,QAAS0G,EACTtX,KAAM,QAGR,MAAM2W,EAASL,IAEXK,EAAOH,YAAc,GACvBG,EAAOH,cAGLc,GACFrJ,QAAQ+G,IAAI,6BAA8BkB,EAAYxV,MAGxDuN,QAAQ+G,IAAI,6BAA8BkB,EAAYxV,MV1JjD,SAAiBA,EAAcoW,EAAeiB,EAAa1V,GAChE,IAAI,IAAA2V,EACkC,mBAAd,OAAlBA,EAAOhK,kBAAW,EAAXgK,EAAaC,UACtBjK,YAAYiK,QAAQvX,EAAM,CAAEoW,QAAOiB,MAAK1V,UAE5C,CAAE,MAAO0E,GACPkH,QAAQC,MAAMnH,EAChB,CACF,CUsJEkR,CAAQ,QAAQ/B,EAAYxV,QAAQwV,EAAY5T,KAAM4T,EAAYmB,YAAa1R,IAAO,CACpFrD,GAAI4T,EAAY5T,GAChB5B,KAAMwV,EAAYxV,KAClBkQ,QAAS0G,IAIX,MAAMnV,EAAQ,IAAIqT,YAAoB,6BAA8B,CAAEnT,OAAQ6T,IAC9EP,cAAcxT,EAChB,CA+BA,SAASwV,EAAOhP,GACd,MACM+N,EADQJ,IACSI,SAEvBA,EAASxL,KAAKvC,GASd+N,EAASwB,OAAO,EAAGL,KAAKM,IAAIzB,EAASzO,OAPpB,GAOuC,GAC1D,CC9NO0E,eAAevE,EAAIQ,EAA4B6E,EAAkB2K,EACtEnC,GACA,MAAMC,EAAcY,EAAM,WAAYrJ,EAAOwI,GAE7C,UAkBFtJ,eAAgC/D,EAA4BsN,EAC1DzI,EAAkB2K,EAAyBnC,GAC3C3K,EAAOmC,EAAMxF,OAAS,EAAG,8CAEzB,MAAMoQ,EAAWnI,IAEXoI,EAAgB7K,EAAMoB,IAAI0J,GAMhC,IAAIC,EAAY7S,UbwpBXgH,eAAuBc,GAC5B,MAAMvK,EAAMqJ,EAAWM,QAAQC,OAAOC,KAAO,eAEvCiH,EAAoB,CACxBC,KAAM5G,KAAKS,UAAU,CAAEL,UACvBzB,QAAS,CACP,eAAgB,oBAElBmB,MAAO,WACP+G,OAAQ,QAGV,IAAInI,EACJ,IACEA,QAAiBmB,MAAMhK,EAAK8Q,EAC9B,CAAE,MAAOjN,GAGP,MAFAuF,EAAcvF,EAAO,QAAS0G,GAC9BnB,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKkF,EAAWF,GAAW,CACzB,MAAMhF,EAAa,IAAIzI,MAAM,kDAI7B,MAHAgO,EAAcvF,EAAO,QAAS0G,GAC9BnB,EAAcvF,EAAO,WAAYgF,GACjCO,EAAcvF,EAAO,MAAO7D,EAAIrE,MAC1BkI,CACR,CAEA,IAAKgF,EAASG,GAAI,KAAAuM,EAAAC,EAChB,MAAM3R,EAAa,IAAIzI,MAAM,mBAAmByN,EAASI,yBACzDG,EAAcvF,EAAO,QAAS0G,GAC9BnB,EAAcvF,EAAO,aAAc+E,EAAaC,IAChDO,EAAcvF,EAAO,kBAAmBgF,EAASI,QAEjD,IAEEG,EAAcvF,EAAO,sBADMgF,EAASM,OAEtC,CAAE,MAAOsM,GACP,CAcF,IAAI9O,EAWJ,MAVwB,MAApBkC,EAASI,QAA8D,iBAAlB,OAA1BsM,EAAO1R,EAAMsN,oBAAa,EAAnBoE,EAAqBnE,aACzDzK,EAAU9C,EAAMsN,cAAcC,YACmB,iBAAd,OAA1BoE,EAAO3R,EAAMsN,oBAAa,EAAnBqE,EAAqB7O,WACrCA,EAAU9C,EAAMsN,cAAcxK,SAG5BA,GACFyC,EAAcvF,EAAO,UAAW8C,GAG5B9C,CACR,CAEA,OAAOgF,CACT,Ca3tBQ6M,CAAQN,GACdrK,QAAQ+G,IAAI,4BAA6B6C,KAAKC,MAAMnS,IAAQ6S,IAe5DA,EAAY7S,IACZ,IAAI+I,QAiBN/B,eAAmCkM,EAAkBT,EAAyBnC,GAG5E,GAAImC,EAAcnQ,OAAS,EACzB,OAAO,KAKT,IAAK,MAAM1J,KAAQ6Z,EACjB,IAAKjK,EAAa5P,GAAO,CACvB,MAAMwI,EAAa,IAAIzI,MAAM,2BAK7B,OAJAyI,EAAMkP,SAAWA,EACjBlP,EAAM4H,eAAiByJ,EACvBrR,EAAMgM,cAAgBxU,EACtB0P,QAAQwJ,KAAK1Q,GACN,IACT,CAcF,IAAKkD,MAAMC,QAAQ2O,EAAWlK,gBAC5B,OAAO,KAMT,IAAImK,EAAkB,KAEtB,MAAMjG,EAAY1C,eAAe4I,mBAKjC,IACED,QAAkBnG,EAAqBkG,EAAYT,EAAevF,GAAW,EAC/E,CAAE,MAAO9L,GACPkH,QAAQwJ,KAAK1Q,EAEf,CAEA,OAAO+R,CACT,CAtEmBE,CAAoBX,EAAUD,GAAiB,GAAInC,GACpEhI,QAAQ+G,IAAI,kCAAmC6C,KAAKC,MAAMnS,IAAQ6S,IAE7D9J,IACH8J,EAAY7S,IACZ+I,QAAa0B,IACbnC,QAAQ+G,IAAI,0BAA2B6C,KAAKC,MAAMnS,IAAQ6S,ML+FvD,SAAenD,EAAgC3G,EAAYwH,EAChED,EAAmBgD,QAAO,IAAPA,IAAAA,GAAU,GAC7B,MAAMZ,EAAWY,EAAU,KAAO/I,IAGlC5E,EAAOiC,EAAOmB,GAAO,6BACrByB,eAAezB,KAAOhB,EAAKgB,GAG3B,MAAMwK,EAAc9H,EAAeiH,GAC7Bc,EAAc/H,EAAe1C,GAK7B0K,EAAY3K,EAA2B4J,GACvCgB,EAAY5K,EAA2BC,GAKvCvM,EAAQ,IAAIqT,YAAoB,eAAgB,CACpDnT,OAAQ,CACNqM,OACA4K,qBAAsB7G,EAAW4G,EAAWD,GAC5CG,uBAAwB9G,EAAW2G,EAAWC,GAC9CG,SAAUlK,IACVmK,WAAYR,EACZZ,WACApC,WACAyD,eAAgBzH,EAAkBiH,EAAaC,GAC/CQ,iBAAkBpH,EAAoB2G,EAAaC,GACnDjD,YAAa,CACX5T,GAAI4T,EAAY5T,GAChB5B,KAAMwV,EAAYxV,OAGtB+U,SAAS,EACTC,UAAU,IAUZ,IAAI9M,EAGAA,EAFAyM,aAAsBC,QACpBD,EAAWxU,YACJwU,EAEAE,OAEFF,GAGAE,OAGX3M,EAAO+M,cAAcxT,EACvB,CK1JEyX,CAAMhR,EAAQ8F,EAAMwH,EAAaD,EACnC,CAzDU4D,CAAiBjR,EAAQsN,EAAazI,EAAO2K,EAAenC,EACpE,CAAE,MAAOlP,GACPqO,EAAOxM,EAAQ,CACbrK,KAAMub,EAAmBrM,EAAO1G,GAChCA,QACAiP,OAAQ,CACNjD,cAAeqF,EACf3K,SAEFwI,WACAC,eAEJ,CAAC,QACCA,EAAYqB,UACd,CACF,CAkHA,SAASuC,EAAmBrM,EAAkB1G,GAC5C,GbuoCI,SAA6BA,GACjC,GAAIA,aAAiBzI,OAAS,kBAAmByI,GAASyG,EAASzG,EAAMsN,gBACxC,MAA/BtN,EAAMsN,cAAclI,OAAgB,CACpC,MAAMmI,EAAcvN,EAAMsN,cAAcC,YACxC,GAA2B,iBAAhBA,EAA0B,CAEnC,GAAIA,EAAYxN,WAAW,UAAYwN,EAAYlG,SAAS,gBAC1D,OAAO,EAIT,GAAIkG,EAAYlG,SAAS,oBACvB,OAAO,CAEX,CACF,CAEA,OAAO,CACT,CazpCM2L,CAAmBhT,GACrB,MAAO,gBAGT,GbosCI,SAAgCA,GACpC,GAAIA,aAAiBzI,OAAS,kBAAmByI,GAASyG,EAASzG,EAAMsN,gBACxC,MAA/BtN,EAAMsN,cAAclI,OAAgB,CACpC,MAAMmI,EAAcvN,EAAMsN,cAAcC,YACxC,GAA2B,iBAAhBA,GACqBA,EAAYjF,cAChBjB,SAAS,+CACjC,OAAO,CAGb,CAEA,OAAO,CACT,CajtCM4L,CAAsBjT,GACxB,MAAO,6BAGT,GbkqCI,SAA6BA,GACjC,GAAIA,aAAiBzI,OAAS,kBAAmByI,GAASyG,EAASzG,EAAMsN,gBACxC,MAA/BtN,EAAMsN,cAAclI,OAAgB,CACpC,MAAMmI,EAAcvN,EAAMsN,cAAcC,YACxC,GAA2B,iBAAhBA,GACqBA,EAAYjF,cAChBjB,SAAS,yDACjC,OAAO,CAGb,CAEA,OAAO,CACT,Ca/qCM6L,CAAmBlT,GACrB,MAAO,yBAGT,Gb4tCI,SAAoCA,GACxC,GAAIA,aAAiBzI,OAAS,kBAAmByI,GAASyG,EAASzG,EAAMsN,gBACxC,MAA/BtN,EAAMsN,cAAclI,OAAgB,CACpC,MAAMmI,EAAcvN,EAAMsN,cAAcC,YACxC,GAA2B,iBAAhBA,GACLA,EAAYlG,SAAS,wCACvB,OAAO,CAGb,CAEA,OAAO,CACT,CaxuCM8L,CAA0BnT,GAAQ,KAAAoT,EAAAC,EACpC,IAAI9I,EAKJ,OAJIrH,MAAMC,QAAQuD,KAChB6D,EAAO7D,EAAMrD,KAAKiQ,GAAY,MAAJA,OAAI,EAAJA,EAAMC,eAG1B,OAARH,EAAI7I,IAAgB,OAAZ6I,EAAJA,EAAM1I,aAAN0I,EAAkBI,mBACb,2CAGD,OAARH,EAAI9I,IAAgB,OAAZ8I,EAAJA,EAAM3I,aAAN2I,EAAkBI,SACb,sCAGF,gCACT,CAEA,MAAO,WACT,CAEA,SAASjC,EAAOxE,GAEd,MAAM1C,EAAS3D,EAAKqG,GAGpB,OAFA1C,EAAOI,WAAaJ,EAAOI,YAAc,CAAC,EAC1CJ,EAAOI,WAAWgJ,UAAYpJ,EAAOI,WAAWgJ,WAAa7b,SAASqI,SAC/DoK,CACT,CCrMO,SAASqJ,EAAmBjW,GACjC,OAAOA,aAAiBkW,OAAwB,iBAAflW,EAAMzE,IACzC,CCLA,MAAM4a,wBAAwB3b,YAC5B,6BAAkBC,GAChB,MAAO,CAAC,cACV,CA2DQ2b,mBAAoB,EACpBC,aAAe1b,KAAK2b,QAAQzb,KAAKF,MACjC4b,8BAAgC5b,KAAK6b,yBAAyB3b,KAAKF,MACnE8b,4BAA8B9b,KAAK+b,uBAAuB7b,KAAKF,MAC/Dgc,mBAAqBhc,KAAKic,cAAc/b,KAAKF,MAC7Ckc,kBAAoBlc,KAAKmc,aAAajc,KAAKF,MAG5C,iBAAAQ,GACLR,KAAKS,uBAEWT,KAAK4B,WAAWW,cAAc,yBACtCtB,iBAAiB,aAAcjB,KAAKkc,mBAE5Clc,KAAKqO,MAAQrO,KAAKoc,gBAElB,MAAMxZ,EAAS5C,KAAK4B,WAAWW,cAAc,UAC7CK,EAAO3B,iBAAiB,QAASjB,KAAK0b,cACtC9Y,EAAO8B,aAAa,aAAc,OAAO1E,KAAK6C,QAAQuB,wBAEtDpE,KAAKqc,mBAEL,IAAK,MAAMtZ,KDxFNgO,eAAeuL,MCyFlB,GAAIhB,EAAmBvY,GACrB,IACE/C,KAAKgc,mBAAmBjZ,EAC1B,CAAE,MAAO4E,GACPkH,QAAQwJ,KAAK1Q,EACf,CAIJ1G,iBAAiB,eAAgBjB,KAAKgc,oBACtC/a,iBAAiB,2BAA4BjB,KAAK4b,+BAClD3a,iBAAiB,6BAA8BjB,KAAK8b,4BACtD,CAEO,oBAAA1a,GACL,MAAMuC,EAAU3D,KAAK4B,WAAWW,cAAc,yBACvC,MAAPoB,GAAAA,EAASzC,oBAAoB,aAAclB,KAAKkc,mBAEhD,MAAMtZ,EAAS5C,KAAK4B,WAAWW,cAAc,UACvC,MAANK,GAAAA,EAAQ1B,oBAAoB,QAASlB,KAAK0b,cAE1Cxa,oBAAoB,eAAgBlB,KAAKgc,oBACzC9a,oBAAoB,2BAA4BlB,KAAK4b,+BACrD1a,oBAAoB,6BAA8BlB,KAAK8b,4BACzD,CAEO,wBAAAza,CAAyBC,EAAcib,EAAmBC,GAC/Dxc,KAAKS,uBACDT,KAAKyB,aAAwB,gBAATH,IACtBtB,KAAKqO,MAAQrO,KAAKoc,gBAClBpc,KAAKqc,mBAET,CAEQ,oBAAA5b,GACDT,KAAK4B,aACR5B,KAAK6B,aAAa,CAAEC,KAAM,SAC1B9B,KAAK4B,WAAWG,UAAY,q6CAEhC,CAEQ,YAAAoa,CAAaM,GAMHzc,KAAKuC,cAAc,uBAEjCvC,KAAKqO,MAAQrO,KAAKoc,gBAClBpc,KAAKqc,mBAET,CAEQ,mBAAAK,GACN,MAAM9Z,EAAS5C,KAAK4B,WAAWW,cAAc,UAC7CK,EAAO8B,aAAa,aAAc,OAAO1E,KAAK6C,QAAQuB,wBACtDxB,EAAO+Z,UAAW,EAClB/Z,EAAOmG,UAAUvG,OAAO,UAC1B,CAEQ,kBAAAoa,GACN,MAAMha,EAAS5C,KAAK4B,WAAWW,cAAc,UAC7CK,EAAO8B,aAAa,aAAc,OAAO1E,KAAK6C,QAAQuB,wBACtDxB,EAAO+Z,UAAW,EAClB/Z,EAAOmG,UAAUvG,OAAO,UAC1B,CAEQ,kBAAAqa,GACN7c,KAAK4B,WAAWW,cAAc,UAAUwG,UAAUC,IAAI,WACtDhJ,KAAK4B,WAAWW,cAA2B,UAAUua,QAAS,GACR,SAApC9c,KAAK6C,QAAQ8C,mBAC7B3F,KAAK4B,WAAWW,cAA2B,iCAC3CvC,KAAK4B,WAAWW,cAAc,sBAEtBua,QAAS,CACrB,CAEQ,iBAAAC,GACN,MAAMna,EAAS5C,KAAK4B,WAAWW,cAAc,UAC7CK,EAAO8B,aAAa,aAAc,OAAO1E,KAAK6C,QAAQuB,wBACtDxB,EAAO+Z,UAAW,EAClB/Z,EAAOmG,UAAUC,IAAI,UACrBhJ,KAAK6C,QAAQ4C,WAAa,gBAC1BzF,KAAK6C,QAAQwD,OAASC,KAAKC,MAAM9C,UACnC,CAEQ,aAAAwY,CAAclZ,GAChBA,EAAME,OAAOoX,aAAera,KAAKyb,oBACnCzb,KAAKyb,mBAAoB,EACzBzb,KAAK4c,qBAET,CAEQ,wBAAAf,CAAyBY,GAC/Bzc,KAAK0c,qBACP,CAEQ,sBAAAX,CAAuBU,GAC7B,GAAIzc,KAAKyb,kBAAmB,CACXzb,KAAK4B,WAAWW,cAAc,UAClCwG,UAAUiU,SAAS,UAC5Bhd,KAAK+c,oBAEL/c,KAAK4c,oBAET,CAGA,MAAM3Y,EAAQjE,KAAK4B,WAAWW,cAA2B,UACrD0B,IACFA,EAAM6Y,QAAS,GAGjB,MAAMG,EAAgD,SAApCjd,KAAK6C,QAAQ8C,mBAC7B3F,KAAK4B,WAAWW,cAA2B,iCAC3CvC,KAAK4B,WAAWW,cAAc,qBAC5B0a,IACFA,EAAUH,QAAS,EAEvB,CAiBQ,OAAAnB,CAAQ5Y,GACd8L,QAAQC,MAAM,uBAEd,MAAMlM,EAASG,EAAMma,cACrB,KAAMta,aAAkBua,mBACtB,OAGF,GAAIva,EAAO+Z,SACT,OAGF,IAAK9R,MAAMC,QAAQ9K,KAAKqO,QAAgC,IAAtBrO,KAAKqO,MAAMxF,OAC3C,MAAM,IAAI3J,MAAM,yEAGlBc,KAAK6c,qBAEL,MAAM7D,EAAgB,GACtB,GAAIhZ,KAAK6C,QAAQ6C,cAA8C,cAA9B1F,KAAK6C,QAAQ6C,cACd,SAA9B1F,KAAK6C,QAAQ6C,aAAyB,CACtC,MAAMA,EAAe1F,KAAK6C,QAAQ6C,aAAa0X,OAC3C1X,GACFsT,EAAclN,KAAKpG,EAEvB,CAIA,GAFAsD,EAAIhJ,KAAMA,KAAKqO,MAAO2K,EAAe,UAAUqE,MAAMxO,QAAQwJ,MAElB,SAAvCrY,KAAK6C,QAAQya,sBACf,OAGF,MAAMC,EAA8Bd,IAMlC,GALAvb,oBAAoB,6BAA8Bqc,GAKV,SAApCvd,KAAK6C,QAAQ8C,mBACf3F,KAAK4c,yBACA,CAKL,MAAMY,EAAa,IAAIjC,MAAM,6BAA8B,CACzDlF,SAAS,EACTC,UAAU,IAEZ1T,EAAO2T,cAAciH,GAGrB,MAAMC,EAAgB,IAAIrH,YAAoB,sBAAuB,CACnEC,SAAS,EACTC,UAAU,EACVrT,OAAQ,CAAEkE,OAAQ,wBAEpBnH,KAAKuW,cAAckH,EACrB,GAGFxc,iBAAiB,6BAA8Bsc,EACjD,CAEQ,aAAAnB,GACN,MAAM/N,EAAmB,GAGnBtD,EAAW/K,KAAK0C,iBAAiC,qBACvD,IAAIgb,EACJ,GAAuC,SAAnC1d,KAAK6C,QAAQ4D,kBAA8B,CAC7C,MAAMkX,EAAW9S,MAAM+S,KAAK7S,GAAUC,KAAKrH,IAAYA,EAAQd,QAAQgb,SACnEF,IACFD,EAAWC,EAAS9a,QAAQK,GAEhC,CAEA,IAAK,MAAMsI,KAAkBT,EAAU,CACrC,IAAKS,EAAe3I,QAAQK,GAC1B,SAGF,MAAMgP,EAAgB,CACpBhP,GAAIsI,EAAe3I,QAAQK,GAC3BmP,WAAY,CAAC,EACbrN,SAAUoG,SAASI,EAAe3I,QAAQmC,SAAU,KAGhD4G,EAAaJ,EAAe9I,iBAAiB,oBACnD,IAAK,MAAMuC,KAAa2G,EACQ,iBAA1B3G,EAAUpC,QAAQuC,IACpB8M,EAAKgJ,aAAejW,EAAUpC,QAAQwC,MAEtC6M,EAAKG,WAAWpN,EAAUpC,QAAQuC,KAAOH,EAAUpC,QAAQwC,MAM/D,MAAMyY,EAAY9d,KAAKuC,cAAiC,+BACpDub,IACF5L,EAAKG,WAAW0L,sBAAwBD,EAAU9W,aAGhD0W,GAAYlS,EAAe3I,QAAQK,KAAOwa,IAC5CxL,EAAK8L,UAAYN,GAGnBrP,EAAMvC,KAAKoG,EACb,CAEA,OAAO7D,CACT,CAEQ,gBAAAgO,GACN,MAAM4B,EAAcje,KAAK4B,WAAWW,cAAc,UAC9CvC,KAAK6C,QAAQ4C,WACfwY,EAAYjX,YAAchH,KAAK6C,QAAQ4C,WAC9BzF,KAAKqO,MAAM+F,KAAK6G,GAAQA,EAAKC,cACtC+C,EAAYjX,YAAc,cAE1BiX,EAAYjX,YAAc,YAE9B,EASGqF,eAAeC,IAAI,uBACtBD,eAAeE,OAAO,qBAAsBiP,iBC1W9C,MAAM0C,gCAAgCre,YA+C5B6b,aAAe1b,KAAK2b,QAAQzb,KAAKF,MAElC,iBAAAQ,GACLR,KAAKS,uBAEL,IAAIwD,EAAQjE,KAAK6C,QAAQmB,aACS,kBAA9BhE,KAAK6C,QAAQuB,eACfH,GAAS,KAAKjE,KAAK6C,QAAQuB,iBAGdpE,KAAK4B,WAAWW,cAAc,UACtCmC,aAAa,aAAc,OAAOT,0EAQzCjE,KAAKiB,iBAAiB,QAASjB,KAAK0b,aACtC,CAEO,oBAAAta,GACLpB,KAAKkB,oBAAoB,QAASlB,KAAK0b,aACzC,CAEQ,oBAAAjb,GACDT,KAAK4B,aAMR5B,KAAK6B,aAAa,CAAEC,KAAM,OAAQqc,gBAAgB,IAClDne,KAAK4B,WAAWG,UAAY,wtBAEhC,CASQ,OAAA4Z,CAAQc,GACd,MAAMe,EAAa,IAAIjC,MAAM,6BAA8B,CACzDlF,SAAS,EACTC,UAAU,IAEZtW,KAAKuW,cAAciH,GAGnB,MAAMY,EAAc,IAAIhI,YAAoB,oBAAqB,CAC/DiI,YAAY,EACZhI,SAAS,EACTC,UAAU,EACVrT,OAAQ,CACNsB,MAAOvE,KAAK6C,QAAQ0B,MACpBR,UAAWqH,SAASpL,KAAK6C,QAAQkB,UAAW,IAC5CC,aAAchE,KAAK6C,QAAQmB,aAC3BH,WAAY7D,KAAK6C,QAAQgB,WACzBK,IAAKlE,KAAK6C,QAAQqB,IAClBC,UAAWiH,SAASpL,KAAK6C,QAAQsB,UAAW,IAC5CC,aAAcpE,KAAK6C,QAAQuB,gBAI/BpE,KAAKuW,cAAc6H,EACrB,EAyDG/R,eAAeC,IAAI,iCAEtBD,eAAeE,OAAO,+BAAgC2R","sources":["webpack://lange/webpack/bootstrap","webpack://lange/webpack/runtime/jsonp chunk loading","webpack://lange/./sections/add-to-cart-elements/add-to-cart/index.html","webpack://lange/./sections/add-to-cart-elements/add-to-cart/index.ts","webpack://lange/./lib/dom-ready.ts","webpack://lange/../node_modules/@integrabeauty/assert/src/index.ts","webpack://lange/../node_modules/@integrabeauty/shopify-ajax-api/src/cart.ts","webpack://lange/./lib/object.ts","webpack://lange/./lib/perf.ts","webpack://lange/./lib/cart/discount/discount.ts","webpack://lange/./lib/untrusted-value.ts","webpack://lange/../node_modules/uuid/dist/rng.js","webpack://lange/../node_modules/uuid/dist/stringify.js","webpack://lange/../node_modules/uuid/dist/v4.js","webpack://lange/./lib/cart/state.ts","webpack://lange/./lib/cart/discount/helper.ts","webpack://lange/./lib/decorate-error.ts","webpack://lange/./lib/cart/error.ts","webpack://lange/./lib/cart/transaction.ts","webpack://lange/./lib/cart/add.ts","webpack://lange/./lib/cart-event-queue.ts","webpack://lange/./sections/add-to-cart-elements/add-to-cart-button/index.ts","webpack://lange/./sections/add-to-cart-elements/add-to-cart-notify-me-button/index.ts"],"sourcesContent":["// The module cache\nlet __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tlet cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tlet module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tif (!(moduleId in __webpack_modules__)) {\n\t\tdelete __webpack_module_cache__[moduleId];\n\t\tlet e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.b = (typeof document !== 'undefined' && document.baseURI) || self.location.href;\n\n// no installed chunks\n\n// no chunk on demand loading\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// no jsonp function","// Imports\nvar ___HTML_LOADER_IMPORT_0___ = new URL(\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\", import.meta.url);\n// Module\nvar code = \"<slot></slot><div class=\\\"sticky-bar\\\"><div class=\\\"product-card\\\"><div class=\\\"image\\\" style=\\\"width: 64px; height: 64px;\\\"><img src=\\\"\" + ___HTML_LOADER_IMPORT_0___ + \"\\\" width=\\\"64\\\" height=\\\"64\\\" alt=\\\"\\\" loading=\\\"eager\\\"></div><div style=\\\"display: flex; flex-direction: column; gap: 6px;\\\"><h3 class=\\\"title\\\" style=\\\"display: block; font-size: 1rem; line-height: 1; font-weight: 700; padding: 4px 0; margin: 0;\\\"></h3><p class=\\\"subtitle\\\" style=\\\"display: block; font-size: 1rem; line-height: 1; margin: 0;\\\"></p><div class=\\\"variant-pills\\\"></div></div></div></div><div class=\\\"button\\\" style=\\\"width: 100%;\\\"></div>\";\n// Exports\nexport default code;","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport { ready, type ReleaseHandle } from '../../../lib/dom-ready.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Renders an add to cart button. It relies on a `<product-element>` slot element.\n */\nclass AddToCart extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-full',\n      'data-update'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * A boolean flag represented as string to indicate whether add-ons are enabled for the product\n     * If true the add-on products will be added to the cart alongside the main product where\n     * applicable. Internally this is used whether to parse the addon variants and append them to\n     * the add-to-cart-button.\n     */\n    addonsEnabled: 'false' | 'true';\n\n    /**\n     * If a product is on backorder but can still purchased, it will have messaging stating the date\n     * the product will be shipped. This attribute is set using the backorder string of a product's\n     * metafield containing the text explaining the actual ship date. This messaging is displayed as\n     * needed to inform customers on product detail pages and when added to a user's shopping cart.\n     */\n    backorder: string;\n\n    /**\n     * Override the button text.\n     */\n    buttonText: string;\n\n    /**\n     * Country ISO code.\n     *\n     * Required.\n     *\n     * The country code is relayed to child elements that need a country specified. You probably\n     * want to use Shopify Liquid's localization.country.iso_code to initialize this element from\n     * liquid.\n     */\n    country: string;\n\n    /**\n     * Email of currently signed in customer.\n     */\n    customerEmail: string;\n\n    /**\n     * If specified, is passed to `add-to-cart-button` which will attempt to apply the discount code\n     * after variants are added.\n     */\n    discountCode: string;\n\n    /**\n     * A boolean flag signaling whether the current product is a free promo template. This is used\n     * internally to signal the need for additional parsing of the attached free promo variants and\n     * appending the <variant-element> tags to the <add-to-bag-button> to be added to cart on click.\n     */\n    freePromoTemplate: 'false' | 'true';\n\n    /**\n     * A boolean flag for rendering `add-to-cart-button` to the full width allowed by the parent\n     * container.\n     */\n    full: 'false' | 'true';\n\n    /**\n     * Boolean flag represented as string to not display the backorder message when rendering. The\n     * default behavior when not set is to display the backorder message.\n     */\n    hideBackorderMessage: 'false' | 'true';\n\n    /**\n     * A boolean flag for rendering `add-to-cart-button` as the main button on the page. The main\n     * button becomes sticky on scroll.\n     */\n    main: 'false' | 'true';\n\n    /**\n     * A boolean flag indicating where the element is rendered. Passed \"true\" if rendered in a list\n     * of products and \"false\" if rendered in PDPs. The default behavior when not set is the product\n     * card will assume it is not being rendered on a product listing page and display any backorder\n     * message provided the hideBackorderMessage dataset flag is not set to \"true\".\n     */\n    productListingDisplay: 'false' | 'true';\n\n    /**\n     * The selling plan id of the product variant if it is being added as a subscription product.\n     */\n    sellingPlanId: string;\n\n    /**\n     * Present and \"true\" while the sticky bar is pinned to the bottom of the viewport, and absent\n     * otherwise. The element sets and removes this itself as it pins and unpins on scroll, so\n     * nothing outside should write it. A style rule in the section's Liquid reads it to hide\n     * Attentive's overlay while a bar is pinned, so the two do not overlap.\n     */\n    stuck: 'false' | 'true';\n\n    /**\n     * Mutate this attribute's value to trigger rendering.\n     */\n    update: string;\n\n    /**\n     * Boolean flag represented as string to display the button in a variant for the upsell modal\n     */\n    upsellModalVariant: 'false' | 'true';\n  };\n\n  private onProductVariantOptionChangedBound = this.onProductVariantOptionChanged.bind(this);\n  private onReadyBound = this.onReady.bind(this);\n  private onIntersectedBound = this.onIntersected.bind(this);\n  private declare product?: Product;\n  private declare promoVariants?: Variant[];\n\n  private observer: IntersectionObserver | null = null;\n  private declare releaseReady: ReleaseHandle;\n\n  /**\n   * Whether the spot the button normally sits in is in the viewport.\n   */\n  private declare isButtonInView: boolean;\n\n  /**\n   * Whether the page footer is in the viewport.\n   */\n  private declare isFooterInView: boolean;\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.releaseReady = ready(this.onReadyBound);\n  }\n\n  public disconnectedCallback() {\n    removeEventListener('product-variant-option-changed', this.onProductVariantOptionChangedBound);\n\n    if (this.releaseReady) {\n      this.releaseReady();\n    }\n  }\n\n  public attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (oldValue === newValue) {\n      return;\n    }\n\n    if (this.isConnected && name === 'data-update') {\n      this.onUpdateAttributeChanged();\n    } else if (this.isConnected && name === 'data-full') {\n      this.onFullAttributeChanged(newValue);\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private onReady() {\n    if (this.isConnected) {\n      addEventListener('product-variant-option-changed', this.onProductVariantOptionChangedBound);\n\n      this.product = this.scrapeProductInfo();\n      this.render();\n    }\n  }\n\n  private onUpdateAttributeChanged() {\n    this.product = this.scrapeProductInfo();\n\n    // these elements are created / removed dynamically. there's no guarantee they exist\n    this.shadowRoot.querySelector('add-to-cart-button')?.remove();\n    this.shadowRoot.querySelector('add-to-cart-notify-me-button')?.remove();\n    this.shadowRoot.querySelector('a')?.remove();\n    this.shadowRoot.querySelector('span')?.remove();\n\n    this.render();\n  }\n\n  private onFullAttributeChanged(newValue: string) {\n    type AddToCartButton = HTMLElementTagNameMap['add-to-cart-button'];\n    type AddToCartNotifyMeButton = HTMLElementTagNameMap['add-to-cart-notify-me-button'];\n    type LangeButton = AddToCartButton | AddToCartNotifyMeButton;\n\n    const buttons = this.shadowRoot.querySelectorAll<LangeButton>(\n      'add-to-cart-button, add-to-cart-notify-me-button');\n\n    const buttonsFull = newValue === 'true' ? 'true' : 'false';\n    for (const button of buttons) {\n      button.dataset.full = buttonsFull;\n    }\n  }\n\n  private onProductVariantOptionChanged(event: WindowEventMap['product-variant-option-changed']) {\n    if (event.detail.id === this.product?.id) {\n      const product = this.querySelector('product-element');\n      if (!product) {\n        return;\n      }\n\n      product.dataset.selectedVariantId = event.detail.selected_variant_id.toString();\n      this.product.selected_variant_id = event.detail.selected_variant_id;\n\n      // these elements are created / removed dynamically. there's no guarantee they exist\n      this.shadowRoot.querySelector('add-to-cart-button')?.remove();\n      this.shadowRoot.querySelector('add-to-cart-notify-me-button')?.remove();\n      this.shadowRoot.querySelector('a')?.remove();\n      this.shadowRoot.querySelector('span')?.remove();\n\n      this.render();\n    }\n  }\n\n  /**\n   * This considers both the initial render and a re-render. When re-rendering, the existing element\n   * is updated.\n   */\n  private renderWhenProductIsUnavailable(variant: Variant) {\n    let element = this.shadowRoot.querySelector('add-to-cart-notify-me-button');\n    if (element) {\n      element.dataset.productUrl = this.product.url;\n      element.dataset.productId = this.product.id.toString();\n      element.dataset.productTitle = this.product.title;\n      element.dataset.sku = variant.sku;\n      element.dataset.variantId = variant.id.toString();\n      element.dataset.variantTitle = variant.title;\n      element.dataset.full = this.dataset.full === 'true' ? 'true' : 'false';\n    } else {\n      element = document.createElement('add-to-cart-notify-me-button');\n\n      // If we know the email address, inform the out of stock button about it so that it can pre-\n      // fill the email address in its input form.\n      if (this.dataset.customerEmail) {\n        element.dataset.email = this.dataset.customerEmail;\n      }\n\n      element.dataset.productUrl = this.product.url;\n      element.dataset.productId = this.product.id.toString();\n      element.dataset.productTitle = this.product.title;\n      element.dataset.sku = variant.sku;\n      element.dataset.variantId = variant.id.toString();\n      element.dataset.variantTitle = variant.title;\n      element.dataset.full = this.dataset.full === 'true' ? 'true' : 'false';\n\n      if (this.dataset.stuck === 'true') {\n        const buttonContainer = this.shadowRoot.querySelector('.sticky-bar');\n        buttonContainer.appendChild(element);\n        element.setAttribute('data-click-track', 'Main Notify Me Sticky');\n\n        this.renderStickyBar(variant);\n      } else {\n        const buttonContainer = this.shadowRoot.querySelector('.button');\n        buttonContainer.appendChild(element);\n        element.setAttribute('data-click-track', 'Main Notify Me Standard');\n      }\n    }\n  }\n\n  private renderWhenProductIsAvailable(selectedVariant: Variant) {\n    const variantDataEl = document.createElement('variant-element');\n    variantDataEl.slot = 'variants';\n    variantDataEl.dataset.id = selectedVariant.id.toString();\n    variantDataEl.dataset.quantity = '1';\n\n    for (const attribute of selectedVariant.custom_attributes) {\n      const variantAttributeEl = document.createElement('custom-attribute');\n      variantAttributeEl.dataset.key = attribute.key;\n      variantAttributeEl.dataset.value = attribute.value;\n      variantDataEl.appendChild(variantAttributeEl);\n    }\n\n    if (this.dataset.sellingPlanId) {\n      let sellingPlanEl =\n        variantDataEl.querySelector<HTMLElement>('custom-attribute[data-key=\"selling_plan\"]');\n      if (sellingPlanEl) {\n        sellingPlanEl.dataset.value = this.dataset.sellingPlanId;\n      } else {\n        sellingPlanEl = document.createElement('custom-attribute');\n        sellingPlanEl.dataset.key = 'selling_plan';\n        sellingPlanEl.dataset.value = this.dataset.sellingPlanId;\n        variantDataEl.appendChild(sellingPlanEl);\n      }\n    }\n\n    // We may be re-rendering. Look for the existing button. We expect to only find one if we are re\n    // rendering. If the button exists, update its properties. If the button does not exist, create\n    // it, set its properties for the first time, and append it.\n\n    let addToCartButton = this.shadowRoot.querySelector('add-to-cart-button');\n\n    if (addToCartButton) {\n      addToCartButton.dataset.variantTitle = selectedVariant.title === 'Default Title' ?\n        this.product.title : `${this.product.title} ${selectedVariant.title}`;\n      addToCartButton.dataset.buttonText = this.dataset.buttonText || 'Add to Bag';\n      addToCartButton.dataset.full = this.dataset.full === 'true' ? 'true' : 'false';\n      addToCartButton.dataset.discountCode = this.dataset.discountCode;\n      addToCartButton.dataset.upsellModalVariant = this.dataset.upsellModalVariant === 'true' ?\n        'true' :\n        'false';\n\n      if (this.dataset.addonsEnabled === 'true') {\n        // clear any existing addon variants\n        const existingAddonVariants = addToCartButton.querySelectorAll(\n          'variant-element[data-is-addon=\"true\"]');\n        for (const existingAddonVariant of existingAddonVariants) {\n          existingAddonVariant.remove();\n        }\n\n        const addonVariants = this.scrapeAddonVariantInfo();\n        const addonVariantElements = createAddonVariantElements(addonVariants);\n\n        for (const addonVariantElement of addonVariantElements) {\n          addToCartButton.appendChild(addonVariantElement);\n        }\n      }\n\n      const variantEl = addToCartButton.querySelector('variant-element');\n      addToCartButton.replaceChild(variantDataEl, variantEl);\n      addToCartButton.dataset.update = Date.now().toString();\n    } else {\n      addToCartButton = document.createElement('add-to-cart-button');\n      addToCartButton.dataset.country = this.dataset.country;\n      addToCartButton.dataset.variantTitle = selectedVariant.title === 'Default Title' ?\n        this.product.title : `${this.product.title} ${selectedVariant.title}`;\n      addToCartButton.dataset.buttonText = this.dataset.buttonText || 'Add to Bag';\n      addToCartButton.dataset.full = this.dataset.full === 'true' ? 'true' : 'false';\n      addToCartButton.dataset.discountCode = this.dataset.discountCode;\n      addToCartButton.dataset.update = Date.now().toString();\n      addToCartButton.dataset.upsellModalVariant =\n        this.dataset.upsellModalVariant === 'true' ? 'true' : 'false';\n      addToCartButton.appendChild(variantDataEl);\n\n      if (this.dataset.freePromoTemplate === 'true') {\n        addToCartButton.dataset.freePromoTemplate = 'true';\n        this.promoVariants = this.scrapePromoVariantInfo();\n        for (const promoVariant of this.promoVariants) {\n          const variantDataEl = document.createElement('variant-element');\n          variantDataEl.slot = 'variants';\n          variantDataEl.dataset.id = promoVariant.id.toString();\n          variantDataEl.dataset.quantity = '1';\n\n          for (const attribute of promoVariant.custom_attributes) {\n            const variantAttributeEl = document.createElement('custom-attribute');\n            variantAttributeEl.dataset.key = attribute.key;\n            variantAttributeEl.dataset.value = attribute.value;\n            variantDataEl.appendChild(variantAttributeEl);\n          }\n\n          if (this.dataset.sellingPlanId) {\n            const variantAttributeEl = document.createElement('custom-attribute');\n            variantAttributeEl.dataset.key = 'selling_plan';\n            variantAttributeEl.dataset.value = this.dataset.sellingPlanId;\n            variantDataEl.appendChild(variantAttributeEl);\n          }\n\n          addToCartButton.appendChild(variantDataEl);\n        }\n      }\n\n      if (this.dataset.addonsEnabled === 'true') {\n        const addonVariants = this.scrapeAddonVariantInfo();\n        const addonVariantElements = createAddonVariantElements(addonVariants);\n\n        for (const addonVariantElement of addonVariantElements) {\n          addToCartButton.appendChild(addonVariantElement);\n        }\n      }\n\n      const subscriptionPlansData = this.querySelector<HTMLScriptElement>(\n        '.subscription-plans-options-data');\n      if (subscriptionPlansData) {\n        const script = <HTMLScriptElement>subscriptionPlansData.cloneNode(true);\n        script.slot = 'subscription-plans';\n        addToCartButton.appendChild(script);\n      }\n\n      if (this.dataset.stuck === 'true') {\n        const buttonContainer = this.shadowRoot.querySelector('.sticky-bar');\n        buttonContainer.appendChild(addToCartButton);\n        addToCartButton.setAttribute('data-click-track', 'Main Add To Cart Sticky');\n\n        this.renderStickyBar(selectedVariant);\n      } else {\n        const buttonContainer = this.shadowRoot.querySelector('.button');\n        buttonContainer.appendChild(addToCartButton);\n        addToCartButton.setAttribute('data-click-track', 'Main Add To Cart Standard');\n      }\n    }\n  }\n\n  private renderStickyBar(variant: Variant) {\n    const title = this.shadowRoot.querySelector('.sticky-bar .title');\n    title.textContent = this.product.title;\n\n    const subtitle = this.shadowRoot.querySelector('.sticky-bar .subtitle');\n    subtitle.textContent = this.product.metafields_subtitle;\n\n    const image = this.shadowRoot.querySelector<HTMLImageElement>('.sticky-bar .image img');\n    let source = this.product.main_image;\n    if (variant.image_src) {\n      source = variant.image_src;\n    }\n\n    image.src = resizeImageUrl(source, 128, 128);\n\n    const pillsContainer = this.shadowRoot.querySelector('.sticky-bar .variant-pills');\n    pillsContainer.innerHTML = '';\n\n    const options = [\n      variant.option1,\n      variant.option2,\n      variant.option3\n    ].filter(option => option?.length > 0 && option !== 'Default Title');\n\n    for (const option of options) {\n      const pill = document.createElement('span');\n      pill.classList.add('pill');\n      pill.textContent = option;\n      pillsContainer.appendChild(pill);\n    }\n  }\n\n  private observe() {\n    if (typeof IntersectionObserver !== 'undefined' && !this.observer) {\n      this.observer = new IntersectionObserver(this.onIntersectedBound, { threshold: 0 });\n      const buttonContainer = this.shadowRoot.querySelector('.button');\n      this.observer.observe(buttonContainer);\n\n      // We need to hide it sticky bar when the footer is in view to prevent overlap. The target\n      // section is a statically included singleton section, so its section id is stable and safe to\n      // look up directly.\n      const footer = document.querySelector('#shopify-section-footer');\n      this.observer.observe(footer);\n    }\n  }\n\n  private onIntersected(entries: IntersectionObserverEntry[]) {\n    // The observer watches both the button spot and the footer, and one callback can carry an entry\n    // for each, so we track them separately and decide from both.\n    for (const entry of entries) {\n      if (entry.target.id === 'shopify-section-footer') {\n        this.isFooterInView = entry.isIntersecting;\n      } else {\n        this.isButtonInView = entry.isIntersecting;\n      }\n    }\n\n    const shouldStick = !this.isButtonInView && !this.isFooterInView;\n\n    if (shouldStick && this.dataset.stuck !== 'true') {\n      this.stick();\n    } else if (!shouldStick && this.dataset.stuck === 'true') {\n      this.unstick();\n    }\n  }\n\n  private stick() {\n    this.dataset.stuck = 'true';\n\n    // Pinning moves the button into the bar, which would collapse its spot in the page and pull the\n    // footer up. With the footer just under the bottom of the screen, that pulls it into view,\n    // which unpins the bar, which pushes the footer back out, and the bar flickers on and off\n    // forever. Holding the spot's height while pinned keeps everything below it still.\n    const buttonContainer = this.shadowRoot.querySelector<HTMLElement>('.button');\n    buttonContainer.style.minHeight = `${buttonContainer.offsetHeight}px`;\n\n    const stickyBar = this.shadowRoot.querySelector('.sticky-bar');\n    stickyBar.classList.add('active');\n\n    const button = this.shadowRoot.querySelector(\n      'add-to-cart-button, add-to-cart-notify-me-button');\n    if (button) {\n      button.remove();\n    }\n\n    this.render();\n  }\n\n  private unstick() {\n    delete this.dataset.stuck;\n\n    const buttonContainer = this.shadowRoot.querySelector<HTMLElement>('.button');\n    buttonContainer.style.minHeight = '';\n\n    const bar = this.shadowRoot.querySelector('.sticky-bar');\n    bar?.classList.remove('active');\n\n    const button = this.shadowRoot.querySelector(\n      'add-to-cart-button, add-to-cart-notify-me-button');\n    if (button) {\n      button.remove();\n    }\n\n    this.render();\n  }\n\n  private shouldRenderBackorderMessage(variant: Variant) {\n    if (!variant.backorder_message) {\n      return false;\n    }\n\n    if (this.dataset.hideBackorderMessage === 'true') {\n      return false;\n    }\n\n    return !(this.dataset.productListingDisplay === 'true' &&\n      variant.hide_backorder_in_collection);\n  }\n\n  /**\n   * On a product card the message links to the product page, but on the product page itself there\n   * is nowhere to link to, so there the message is just text. We use an anchor for both and leave\n   * off the href in the text-only case. An anchor without an href is not focusable and screen\n   * readers read it as plain text, so it does not pretend to be a link.\n   */\n  private renderBackorderMessage(variant: Variant) {\n    const selector = '[data-backorder-message=\"true\"]';\n    let message = this.shadowRoot.querySelector<HTMLAnchorElement>(selector);\n    const shouldAppend = !message;\n\n    if (!message) {\n      message = document.createElement('a');\n      message.dataset.backorderMessage = 'true';\n    }\n\n    if (this.dataset.productListingDisplay === 'true') {\n      message.textContent = 'On backorder, see ship date';\n      message.href = `${this.product.url}?variant=${variant.id}`;\n    } else {\n      message.textContent = variant.backorder_message;\n    }\n\n    if (shouldAppend) {\n      this.shadowRoot.prepend(message);\n    }\n  }\n\n  /**\n   * Renders the product associated with this element. The type of the product fundamentally changes\n   * the meaning of its properties. In the case of bundles, the variants of the product are actually\n   * products of their own, and the meanings of being out of stock or backordered change.\n   *\n   * We also distinguish between a product that has only one variant and one that has many. We call\n   * a single variant product a simple product and a many variant product a configurable product.\n   */\n  private render() {\n    if (!this.product) {\n      return;\n    }\n\n    if (!Array.isArray(this.product.variants)) {\n      return;\n    }\n\n    let selectedVariant = this.product.variants.find(variant =>\n      variant?.id === this.product.selected_variant_id);\n\n    // If we are unable to determine which variant is selected, fall back to whatever is the first\n    // variant in the array.\n\n    if (!selectedVariant && this.product.variants.length > 0) {\n      selectedVariant = this.product.variants[0];\n    }\n\n    if (!selectedVariant) {\n      return;\n    }\n\n    if (selectedVariant.available) {\n      this.renderWhenProductIsAvailable(selectedVariant);\n    } else {\n      this.renderWhenProductIsUnavailable(selectedVariant);\n    }\n\n    if (this.dataset.main === 'true') {\n      this.observe();\n    }\n\n    if (!selectedVariant.available) {\n      return;\n    }\n\n    if (this.shouldRenderBackorderMessage(selectedVariant)) {\n      this.renderBackorderMessage(selectedVariant);\n    }\n  }\n\n  /**\n   * Creates and returns a {@link Product} object that represents the product associated with this\n   * element at this time. The state of the product is spread out over the attributes of this\n   * element and its descendant elements, some of which come in through slots.\n   */\n  private scrapeProductInfo() {\n    const productElement = this.querySelector('product-element');\n    if (!productElement) {\n      return;\n    }\n\n    const product = <Product>{};\n    product.available = productElement.dataset.available === 'true';\n    product.id = parseInt(productElement.dataset.id, 10);\n    product.url = productElement.dataset.url;\n    product.selected_variant_id = parseInt(productElement.dataset.selectedVariantId, 10);\n    product.title = productElement.dataset.title;\n    product.main_image = productElement.dataset.mainImage;\n    product.metafields_subtitle = productElement.dataset.metafieldsSubtitle;\n    product.tags = productElement.dataset.tags;\n    product.type = productElement.dataset.type;\n    product.variants = [];\n\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n\n    const variants = productElement.querySelectorAll<VariantElement>('variant-element[data-id]');\n    for (const variantElement of variants) {\n      const variant = <Variant>{};\n      variant.id = parseInt(variantElement.dataset.id, 10);\n      variant.available = variantElement.dataset.available === 'true';\n      variant.backorder_message = variantElement.dataset.backorder;\n      variant.hide_backorder_in_collection = variantElement.dataset.hideBackorderInCollection;\n      variant.custom_attributes = [];\n      variant.sku = variantElement.dataset.sku;\n      variant.title = variantElement.dataset.title;\n      variant.image_src = variantElement.dataset.imageSrc;\n      variant.option1 = variantElement.dataset.option1;\n      variant.option2 = variantElement.dataset.option2;\n      variant.option3 = variantElement.dataset.option3;\n\n      const attributes = variantElement.querySelectorAll('custom-attribute');\n      for (const attributeElement of attributes) {\n        variant.custom_attributes.push({\n          key: attributeElement.dataset.key,\n          value: attributeElement.dataset.value\n        });\n      }\n\n      product.variants.push(variant);\n    }\n\n    return product;\n  }\n\n  private scrapePromoVariantInfo() {\n    const promoVariants = [];\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n    const variantElements = this.querySelectorAll<VariantElement>(\n      'variant-element[data-is-promo=\"true\"]');\n    for (const variantElement of variantElements) {\n      const variant = <Variant>{};\n      variant.id = parseInt(variantElement.dataset.id, 10);\n      variant.available = variantElement.dataset.available === 'true';\n      variant.backorder_message = variantElement.dataset.backorder;\n      variant.hide_backorder_in_collection = variantElement.dataset.hideBackorderInCollection;\n      variant.custom_attributes = [];\n      variant.sku = variantElement.dataset.sku;\n      variant.title = variantElement.dataset.title;\n\n      const attributes = variantElement.querySelectorAll('custom-attribute');\n      for (const attributeElement of attributes) {\n        variant.custom_attributes.push({\n          key: attributeElement.dataset.key,\n          value: attributeElement.dataset.value\n        });\n      }\n\n      promoVariants.push(variant);\n    }\n\n    return promoVariants;\n  }\n\n  private scrapeAddonVariantInfo() {\n    const addonVariants = [];\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n    const variantElements = this.querySelectorAll<VariantElement>(\n      'variant-element[data-is-addon=\"true\"]');\n    for (const variantElement of variantElements) {\n      const variant = <Variant>{};\n      variant.id = parseInt(variantElement.dataset.id, 10);\n      variant.available = variantElement.dataset.available === 'true';\n      variant.backorder_message = variantElement.dataset.backorder;\n      variant.hide_backorder_in_collection = variantElement.dataset.hideBackorderInCollection;\n      variant.custom_attributes = [];\n      variant.sku = variantElement.dataset.sku;\n      variant.title = variantElement.dataset.title;\n\n      const attributes = variantElement.querySelectorAll('custom-attribute');\n      for (const attributeElement of attributes) {\n        variant.custom_attributes.push({\n          key: attributeElement.dataset.key,\n          value: attributeElement.dataset.value\n        });\n      }\n\n      addonVariants.push(variant);\n    }\n\n    return addonVariants;\n  }\n}\n\nfunction createAddonVariantElements(variants: Variant[]) {\n  const variantElements = [];\n  for (const variant of variants) {\n    const variantDataEl = document.createElement('variant-element');\n    variantDataEl.dataset.isAddon = 'true';\n    variantDataEl.slot = 'variants';\n    variantDataEl.dataset.id = variant.id.toString();\n    variantDataEl.dataset.quantity = '1';\n\n    for (const attribute of variant.custom_attributes) {\n      const variantAttributeEl = document.createElement('custom-attribute');\n      variantAttributeEl.dataset.key = attribute.key;\n      variantAttributeEl.dataset.value = attribute.value;\n      variantDataEl.appendChild(variantAttributeEl);\n    }\n\n    variantElements.push(variantDataEl);\n  }\n\n  return variantElements;\n}\n\n/**\n * Optimizes an image URL by adding Shopify CDN size parameters.\n *\n * Transforms image URLs to use Shopify's CDN image resizing by inserting dimension parameters\n * before the file extension. This reduces file size and improves page load performance.\n */\nfunction resizeImageUrl(src: string, width: number, height: number) {\n  if (!src) {\n    return '';\n  }\n\n  // Handle protocol-relative URLs by prepending https:\n  const normalizedSrc = src.startsWith('//') ? 'https:' + src : src;\n\n  let url: URL;\n  try {\n    url = new URL(normalizedSrc);\n  } catch (error) {\n    return src;\n  }\n\n  // Find the last dot in the pathname to locate the extension\n  const lastDotIndex = url.pathname.lastIndexOf('.');\n  if (lastDotIndex === -1) {\n    return src;\n  }\n\n  const pathWithoutExtension = url.pathname.slice(0, lastDotIndex);\n  const extension = url.pathname.slice(lastDotIndex);\n\n  // Insert the size parameters before the file extension. Format: _WIDTHxHEIGHT\n  const sizeParam = '_' + width + 'x' + height;\n  url.pathname = pathWithoutExtension + sizeParam + extension;\n\n  const optimizedUrl = url.toString();\n\n  // If original was protocol-relative, return protocol-relative\n  if (src.startsWith('//')) {\n    return optimizedUrl.replace(/^https:/, '');\n  }\n\n  return optimizedUrl;\n}\n\n/**\n * Represents a Shopify product and its variants.\n */\ninterface Product {\n  /**\n   * Whether the product has inventory in stock and is available for sale.\n   */\n  available: boolean;\n\n  /**\n   * Product id.\n   */\n  id: number;\n\n  /**\n   * Product main image\n   */\n  main_image: string;\n\n  /**\n   * Product metafields subtitle\n   */\n  metafields_subtitle: string;\n\n  /**\n   * The id of the selected variant of the product.\n   */\n  selected_variant_id: number;\n\n  /**\n   * The tags tied to the product.\n   */\n  tags: string;\n\n  /**\n   * The original product title (not metafields title).\n   */\n  title: string;\n\n  /**\n   * The custom type of the product.\n   *\n   * @example \"bundle\"\n   */\n  type: string;\n\n  /**\n   * The url of the product details page for the product.\n   */\n  url: string;\n\n  /**\n   * One or more product variants.\n   */\n  variants: Variant[];\n}\n\n/**\n * Represents a Shopify variant\n */\ninterface Variant {\n  /**\n   * Whether the variant has inventory in stock and is available for sale.\n   */\n  available: boolean;\n\n  backorder_message: string;\n\n  custom_attributes: {\n    /**\n     * Key or name of the attribute.\n     */\n    key: string;\n\n    /**\n     * Value of the attribute.\n     */\n    value: string;\n  }[];\n\n  /**\n   * Boolean as a string to indicate whether to hide any backorder message if the product card is\n   * being viewed in a collection context.\n   */\n  hide_backorder_in_collection: string;\n\n  /**\n   * Variant id\n   */\n  id: number;\n\n  /**\n   * The URL for the variant image\n   */\n  image_src: string;\n\n  /**\n   * The value of the variant for the first product option. If there's no first product option, then\n   * nil is returned.\n   */\n  option1: string;\n\n  /**\n   * The value of the variant for the second product option. If there's no first product option,\n   * then nil is returned.\n   */\n  option2: string;\n\n  /**\n   * The value of the variant for the third product option. If there's no first product option, then\n   * nil is returned.\n   */\n  option3: string;\n\n  price: number;\n\n  sku: string;\n\n  title: string;\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'add-to-cart': AddToCart;\n  }\n}\n\nif (!customElements.get('add-to-cart')) {\n  customElements.define('add-to-cart', AddToCart);\n}\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n","/**\n * Throws an assertion error if the given condition is falsy.\n *\n * @param expression any expression, although one that evaluates to true or false is preferred by\n * convention\n * @param message optional error text\n */\nexport function assert(expression: unknown, message?: string) {\n  if (!expression) {\n    throw new AssertionError(message);\n  }\n}\n\nexport class AssertionError extends Error {\n  constructor(message = 'Assertion error') {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n","import type {} from '@integrabeauty/shopify-browser-types';\n\n/**\n * Represents the value of a cart attribute. Ordinarily values are strings. However, Shopify also\n * supports storing nested objects as values.\n */\nexport type AttributeValue = Record<string, string> | string;\n\n/**\n * The type of the value of `Cart['attributes']`\n */\nexport type Attributes = Record<string, AttributeValue>;\n\n/**\n * This is the stored cart, which is what a get cart answers with. A mutation answers with a\n * superset, so see the response body types alongside each mutation for the extra properties those\n * carry.\n *\n * @todo selling plan properties\n * @todo endpoint request body response body types\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart\n */\nexport interface Cart {\n  /**\n   * An object where the key is the attribute name and the value is the attribute value.\n   */\n  attributes: Attributes;\n\n  /**\n   * A discount application represents a type of discount.\n   *\n   * When a discount is applied to one or more line items, it is said to be _allocated_ to the line\n   * items. That is why line level discount allocation objects have a nested application object that\n   * will correlate with one of the applications in this array.\n   *\n   * When a discount is applied to the whole cart, it will only be present in this array because it\n   * is not allocated to particular lines.\n   */\n  cart_level_discount_applications: DiscountApplication[];\n\n  /**\n   * The ISO currency code for the cart.\n   *\n   * @example \"USD\"\n   */\n  currency: string;\n\n  /**\n   * Discount codes that have been applied to the cart.\n   *\n   * Not all codes are applicable. It is possible for a code to be applied but inapplicable.\n   *\n   * This does not provide information about the amount of each discount. Refer to the cart level\n   * discount applications or line level discount allocations for that information.\n   *\n   * This array will include shipping discounts. A shipping discount will be present in the array if\n   * applied but in the inapplicable state while in the cart. The discount will appear as applicable\n   * at checkout automatically.\n   */\n  discount_codes: DiscountCode[];\n\n  /**\n   * Total quantity of items in a cart\n   */\n  item_count: number;\n\n  /**\n   * The cart's line items.\n   *\n   * A cart will always have a defined items array, even if the cart is empty.\n   */\n  items: Item[];\n\n  items_subtotal_price: number;\n\n  /**\n   * An optional textual note that may be applied to the cart. If a cart becomes a checkout, and\n   * then the checkout becomes an order, the cart note will carry over to the order note.\n   *\n   * An order's note, and an order's note attributes, are two completely separate concepts.\n   */\n  note: string;\n\n  original_total_price: number;\n\n  /**\n   * This is true when at least one item in the cart requires shipping.\n   *\n   * This is false when no items in the cart require shipping.\n   *\n   * Shopify supports digital-only carts. These are carts that when purchased do not require the\n   * creation of a fulfillment. For example, a cart that consists only of a digital gift card.\n   */\n  requires_shipping: boolean;\n\n  /**\n   * This property is defined when there are cart mutations that involve the use of liquid-rendered\n   * sections.\n   *\n   * @see https://shopify.dev/docs/api/ajax/reference/cart#request-sections\n   */\n  sections: Record<string, string>;\n\n  /**\n   * This is the publicly visible cart id.\n   *\n   * If a checkout is created from this cart, the checkout's cart token will be equal to this value.\n   *\n   * Never write logic that parses this value. The format can change at any time, and has changed\n   * several times in the past.\n   *\n   * The value may contain url-like values, e.g. \"abc?key=123\"\n   */\n  token: string;\n\n  /**\n   * The total reduction in price in currency subunits (e.g. in cents for USD).\n   *\n   * @example 5010\n   */\n  total_discount: number;\n\n  /**\n   * Total price in currency subunits. For example, if the currency is USD, this is in cents.\n   *\n   * @example 1050\n   */\n  total_price: number;\n\n  /**\n   * The total weight of the cart (in grams?). This is a simple sum of the weight of the items of\n   * the cart (unit weight * quantity).\n   *\n   * @example 123.456\n   */\n  total_weight: number;\n}\n\n/**\n * Represents a discount that has been applied to an item.\n */\nexport interface Discount {\n  amount: number;\n  title: string;\n}\n\n/**\n * Represents a discount application that has been allocated to a line item.\n */\nexport interface DiscountAllocation {\n  /**\n   * The amount of the application that was allocated to a particular line item.\n   *\n   * When a discount application is allocated to several line items, this is a partial value of the\n   * full discount application amount that is allocated to this particular line. When the discount\n   * application is only allocated to a single line, this value will be equal to the total value of\n   * the discount application.\n   *\n   * For example, let's say we have cart with 2 line items and 10 dollar off discount application.\n   * The total value of the discount application is 10 dollars. Let's say the discount is allocated\n   * to both lines. The value of each allocation is 5 dollars. For each line this amount will\n   * contain the value 500. Within the nested application object, the application's total amount\n   * will be 1000. This is because each nested application object is a copy of the one application\n   * that happens to have multiple allocations (the discount is applied to multiple lines).\n   *\n   * The amount is in currency subunits. For example, for USD, this is the value of cents. For\n   * example, a discount of $2.50 is 250.\n   *\n   * @example 1050\n   */\n  amount: number;\n\n  /**\n   * The corresponding discount application that was allocated to a particular line item.\n   */\n  discount_application: DiscountApplication;\n}\n\n/**\n * A discount application represents a discount of some kind. There are automatically applied\n * discount applications, discount code applications, and script applied discount applications.\n */\nexport interface DiscountApplication {\n  allocation_method: string;\n  created_at: string;\n  description: string;\n  key: string;\n  target_selection: string;\n  target_type: string;\n  title: string;\n  total_allocated_amount: number;\n  type: string;\n  value: string;\n  value_type: string;\n}\n\n/**\n * The details of an image resource, such as the hero image for a product.\n */\nexport interface Image {\n  alt: string;\n  aspect_ratio: number;\n  height: number;\n  url: string;\n  width: number;\n}\n\n/**\n * Represents a line item in a cart.\n */\nexport interface Item {\n  /**\n   * The price of the line item after discounts\n   */\n  discounted_price: number;\n\n  /**\n   * Discount codes applied to the line item.\n   *\n   * @deprecated\n   */\n  discounts: Discount[];\n\n  /**\n   * The main image for the product\n   */\n  featured_image: Image;\n\n  /**\n   * The combined price, in the currency's subunit, of all of the items in the line item. This\n   * includes any line-level discounts.\n   *\n   * The value is equal to line_item.final_price multiplied by line_item.quantity. It's output in\n   * the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  final_line_price: number;\n\n  /**\n   * The price of the line item in the currency's subunit. This includes any line-level discounts.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  final_price: number;\n\n  /**\n   * Returns true if the product associated with the line item is a gift card. Returns false if not.\n   */\n  gift_card: boolean;\n\n  /**\n   * The weight of the line item in the store's default weight unit.\n   */\n  grams: number;\n\n  handle: string;\n\n  has_components: boolean;\n\n  /**\n   * The ID of the line item. The ID differs depending on the context. The following table outlines\n   * the possible contexts and their associated values:\n   *\n   * Context Value\n   *\n   * * cart.items The ID of the line item's variant. This ID isn't unique, and can be shared by\n   *   multiple items with the same variant.\n   * * checkout.line_items A temporary unique hash generated for the checkout.\n   * * order.line_items A unique integer ID.\n   */\n  id: number;\n\n  image: string;\n\n  /**\n   * Added in conjunction with parent_relationship to support determining whether a line item can be\n   * removed or have its quantity updated when the line item is a child of another line item, e.g.\n   * part of a bundle or value set or promotional gift.\n   */\n  instructions: {\n    can_remove: boolean;\n    can_update_quantity: boolean;\n  };\n\n  key: string;\n\n  /**\n   * The discount allocations that apply to the line item.\n   */\n  line_level_discount_allocations: DiscountAllocation[];\n\n  /**\n   * The total amount of any discounts applied to the line item in the currency's subunit.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  line_level_total_discount: number;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  line_price: number;\n\n  /**\n   * The name and value pairs for each option of the variant associated with the line item.\n   *\n   * The array is never empty because variants with no options still have a default option. Because\n   * of this, you should use line_item.product.has_only_default_variant to check whether there's any\n   * information to output.\n   */\n  options_with_values: Option[];\n\n  /**\n   * The combined price of all of the items in a line item in the currency's subunit, before any\n   * discounts have been applied.\n   *\n   * The value is equal to line_item.original_price multiplied by line_item.quantity. It's output in\n   * the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  original_line_price: number;\n\n  /**\n   * The price of the line item in the currency's subunit, before discounts have been applied.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  original_price: number;\n\n  /**\n   * The parent relationship of the line item. This is only present if the line item is a child of\n   * another line item. For example, when the line item is part of a bundle or value set or\n   * promotional gift.\n   *\n   * The parent relationship must be established by adding the \"parent_id\" property to the add to\n   * bag request.\n   *\n   * @see https://shopify.dev/docs/apps/build/product-merchandising/nested-cart-lines/create-nested-cart-lines#cart-ajax-api\n   */\n  parent_relationship: {\n    parent_key: string;\n  };\n\n  presentment_price: number;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  price: number;\n\n  product_description: string;\n\n  product_has_only_default_variant: boolean;\n\n  /**\n   * The ID of the line item's product.\n   */\n  product_id: number;\n\n  product_title: string;\n\n  product_type: string;\n\n  properties: Record<string, string>;\n  quantity: number;\n\n  quantity_rule: QuantityRule;\n\n  requires_shipping: boolean;\n\n  /**\n   * The selling plan allocation of the line item.\n   */\n  selling_plan_allocation: SellingPlanAllocation;\n\n  /**\n   * The SKU of the variant in the line item.\n   *\n   * Stock Keeping Unit.\n   */\n  sku: string;\n\n  successfully_fulfilled_quantity: number;\n\n  taxable: boolean;\n\n  /**\n   * The title of the line item. The title is a combination of line_item.product.title and\n   * line_item.variant.title, separated by a hyphen.\n   *\n   * In most contexts, the line item title appears in the customer's preferred language. However, in\n   * the context of an order, the line item title appears in the language that the customer checked\n   * out in. The title can receive an override value from the Cart Transform API. Overrides take\n   * precedence over translations.\n   *\n   * #### Line item title history\n   *\n   * When referencing line item, product, and variant titles in the context of an order, the\n   * following changes might result in a different output than you expect:\n   * * A product or variant being deleted\n   * * A product or variant title being edited\n   *\n   * When line_item.title is referenced for an order, the line item title at the time of the order\n   * is returned. However, when line_item.product.title and line_item.variant.title are referenced,\n   * the current value for each title is returned.\n   */\n  title: string;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  total_discount: number;\n\n  unit_price_measurement: {\n    measured_type: string;\n    quantity_unit: string;\n    quantity_value: string;\n    reference_unit: string;\n    reference_value: number;\n  };\n\n  untranslated_product_title: string;\n\n  untranslated_variant_title: string;\n\n  /**\n   * The relative URL of the variant associated with the line item.\n   */\n  url: string;\n\n  /**\n   * The ID of the line item's variant.\n   */\n  variant_id: number;\n\n  variant_options: string[];\n\n  variant_title: string;\n\n  /**\n   * The vendor of the variant associated with the line item.\n   */\n  vendor: string;\n}\n\nexport interface SellingPlanAllocation {\n  compare_at_price: number;\n\n  per_delivery_price: number;\n\n  /**\n   * @example 900\n   */\n  price: number;\n\n  price_adjustments: SellingPlanAllocationPriceAdjustment[];\n  selling_plan: SellingPlan;\n}\n\nexport interface SellingPlanAllocationPriceAdjustment {\n  /**\n   * @example 1\n   */\n  position: number;\n\n  /**\n   * @example 900\n   */\n  price: number;\n}\n\nexport interface SellingPlan {\n  description: unknown;\n\n  fixed_selling_plan: boolean;\n\n  id: number;\n  name: string;\n\n  options: SellingPlanOption[];\n\n  price_adjustments: SellingPlanPriceAdjustment[];\n\n  recurring_deliveries: boolean;\n}\n\nexport interface SellingPlanOption {\n  name: string;\n  position: number;\n  value: string;\n}\n\nexport interface SellingPlanPriceAdjustment {\n  order_count: unknown;\n  position: number;\n  value: number;\n  value_type: string;\n}\n\nexport interface Option {\n  name: string;\n  value: string;\n}\n\nexport interface QuantityRule {\n  increment: number;\n  max: number;\n  min: number;\n}\n\nexport interface DiscountCode {\n  /**\n   * Whether the discount is currently applicable. It is possible to apply a discount code to a cart\n   * but have it not yet be applicable. When not applicable, there will not be any associated\n   * discount applications or allocations. The moment someone performs an action that makes the\n   * discount code applicable, Shopify will update the property to true.\n   */\n  applicable: boolean;\n\n  /**\n   * The discount code string.\n   *\n   * This is NOT guaranteed to be uppercase. Discount codes entered at checkout are captured in\n   * whatever case was used during input.\n   *\n   * @example \"MONEY1234\"\n   * @example \"Abc123def\"\n   */\n  code: string;\n}\n\n/**\n * Returns the request id header from the ajax cart api response. Capturing this value is important\n * when helping Shopify support investigate issues.\n *\n * This does not error. If there is a problem, this returns null or undefined.\n */\nfunction getRequestId(response: Response) {\n  try {\n    return response.headers.get('X-Request-Id');\n  } catch (error) {\n    // ignore\n  }\n}\n\n/**\n * Returns whether the given value is shaped like a fetch Response.\n *\n * The return type of fetch is a contract, not a verified fact. A third party that overrides the\n * global fetch can resolve it to null, undefined, or some unrelated value, in which case the very\n * next property access throws an opaque error with no indication that a bad override was the cause.\n * This checks structurally for the members this module reads rather than with an instanceof, so a\n * conforming fetch polyfill that returns its own Response class still passes while a missing or\n * malformed value is trapped at the point it is received.\n *\n * This type check does not technically prove that the value is a genuine Response object, only that\n * it has the expected shape. We are intentionally lying with types here, so that we can leverage\n * the type inference to support the subset of use cases this module actually relies on. More over,\n * this lying is ok, as the call to `response = await fetch` is also lying because it overly trusts\n * that fetch returns value of type Response. We are going to keep trying to \"make fetch happen\"\n * regardless.\n *\n * ## War Story\n *\n * As much as it seems like this is an insane and irrationally paranoid check, you would be\n * surprised. For example, in the wild, we observed a third party vendor that hooked global fetch,\n * had an error in their hook, and simultaneously, another vendor's third party polyfill that only\n * defined fetch without also defining Response, leading to even more obscure cases when older\n * browsers were involved. Then, to make matters worse, a fix was implemented leveraging the clean\n * global fetch using an iframe content window hack to work around the inability to cleanly capture\n * the native fetch before it was erroneously hooked, and this broke certain expressions that use\n * instanceof because instanceof has unintuitive behavior when checking instanceof of a value from a\n * different global context, e.g. from an iframe. A web of lies!\n */\nfunction isResponse(value: any): value is Response {\n  if (value === null || typeof value !== 'object') {\n    return false;\n  }\n\n  if (typeof value.ok !== 'boolean') {\n    return false;\n  }\n\n  if (typeof value.status !== 'number') {\n    return false;\n  }\n\n  if (typeof value.text !== 'function') {\n    return false;\n  }\n\n  if (typeof value.json !== 'function') {\n    return false;\n  }\n\n  return true;\n}\n\n/**\n * Attaches a single piece of context onto an error before it is thrown.\n *\n * The target may be a value caught upstream rather than one constructed here, and such a value can\n * carry a non-writable property, such as a DOMException whose code is a read-only accessor, or be\n * frozen outright, in which case assigning to it throws. Decorating an error must never itself\n * fail, so a rejected assignment is swallowed. This context is best effort metadata, so the\n * property is skipped when it cannot be set, leaving the error otherwise intact to be thrown and\n * handled by the caller.\n */\nfunction decorateError(error: any, key: string, value: unknown) {\n  try {\n    error[key] = value;\n  } catch (error) {\n    // The target rejects this property. Skip it rather than letting decoration fail.\n  }\n}\n\n/**\n * Returns an absolute url for the given path.\n *\n * It would be nice to just use a relative url in fetch. However, we see real world errors about\n * requests including credentials. Visitors might have buggy extensions inserting document.baseURI\n * or they are browsing the site in some weird way. To work around this, we build up a full url that\n * omits the credentials.\n *\n * Unfortunately, specifying the base url means we have to also solve the problem of determining it.\n * There is currently a desire to avoid location.origin due to browser support. we do not want to\n * touch the document base uri. Use host over hostname since that includes port.\n */\nfunction resolveUrl(path: string) {\n  return new URL(path, `${location.protocol}//${location.host}`);\n}\n\n/**\n * An item to add to the cart\n */\nexport interface AddItem {\n  /**\n   * Variant id\n   *\n   * Shopify says that it supports variant ids as numbers, but we have witnessed strange errors when\n   * using numbers so we impose an artificial limitation on the type here and require strings.\n   */\n  id: string;\n\n  /**\n   * The id of the line item to add the item as a child of. This is used for adding items as\n   * children of value sets and bundles. If not specified, the item is added as a top level line\n   * item.\n   *\n   * @see https://shopify.dev/docs/apps/build/product-merchandising/nested-cart-lines/create-nested-cart-lines#cart-ajax-api\n   */\n  parent_id?: number | string;\n\n  /**\n   * Line item properties to include.\n   *\n   * If the variant does not already exist in the cart, then the logic is straightforward. This will\n   * result in a new line item with these properties.\n   *\n   * If the variant exists in the cart, then the logic is convoluted. If the specified properties\n   * are identical to the existing properties, then the quantity of the existing line item\n   * increments. If the specified properties are different, then a new line item is created with\n   * these properties, resulting in the same variant being present in multiple line items.\n   */\n  properties?: Attributes;\n\n  /**\n   * The amount of the variant to add.\n   *\n   * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-add-js\n   */\n  quantity: number;\n\n  selling_plan?: number | string;\n}\n\n/**\n * The response body of a cart add.\n *\n * This one is not a cart at all, unlike the response body of a change or an update. It carries only\n * the items the request asked for, so there is no token, no totals, and no view of the rest of the\n * cart. See the notes on the add function for why the body is not worth reading.\n *\n * None of the functions here parse this body, because a caller that wants the resulting state\n * performs its own get cart afterward. So this type exists to describe what the endpoint answers\n * with rather than to annotate a return value.\n */\nexport interface CartAddResponseBody {\n  /**\n   * The line items the add produced, one per item in the request.\n   *\n   * These look like ordinary cart line items and are typed as such, though a few of the rarer line\n   * item properties have not been seen on one, so treat the shape as the usual line item rather\n   * than a guaranteed match.\n   */\n  items: Item[];\n}\n\n/**\n * Adds items to the cart. At least one item should be specified.\n *\n * This does not fetch the response body. The raw response body does not contain the full cart\n * state. The raw response body may not be the latest state of cart items. The raw response body may\n * contain zero-quantity items still present after prior removal.\n *\n * This validates the response status. If the status is not ok this throws an error.\n *\n * This specifies fetch mode no-store so that the call is not unintentionally cached.\n *\n * Adding an item the cart does not necessarily mean a new line item is created. For each item in\n * the input, Shopify makes a server side determination of whether to update the quantity of an\n * existing line item or add a new line item. The factors in that decision are related to how a line\n * item key is determined, which is poorly documented. Basically, if an item has some different\n * properties, e.g. line item properties, or pricing, then the item will always be added as a new\n * line item, even though the variant id is the same as an existing line item.\n *\n * The line item scripts run on every mutation. The scripts will remove any orphaned value set line\n * items implicitly. Keep this in mind when debugging.\n *\n * The content type header must be specified in the request or Shopify interprets the request as a\n * form submission, which we are not doing here, which leads to an error.\n *\n * Shopify's injected front end code (e.g. Trekkie, Monorail) triggers a console warning when it\n * detects that the number of items requested to be added is less than the number that was added.\n * This is not necessarily an error. It can indicate that the line item scripts removed one or more\n * line items during the add mutation.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-add-js\n * @see https://help.shopify.com/en/manual/checkout-settings/script-editor/shopify-scripts\n */\nexport async function cartAdd(items: AddItem[]) {\n  const url = resolveUrl(Shopify.routes.root + 'cart/add.js');\n\n  const init: RequestInit = {\n    body: JSON.stringify({ items }),\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    cache: 'no-store',\n    method: 'POST'\n  };\n\n  let response;\n  try {\n    response = await fetch(url, init);\n  } catch (error) {\n    decorateError(error, 'items', items);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response adding to cart');\n    decorateError(error, 'items', items);\n    decorateError(error, 'response', response);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} adding to cart`);\n    decorateError(error, 'items', items);\n    decorateError(error, 'request_id', getRequestId(response));\n    decorateError(error, 'response_status', response.status);\n\n    try {\n      const responseBody = await response.json();\n      decorateError(error, 'response_body', responseBody);\n    } catch (responseConsumeAndParseBodyError) {\n      // ignore\n    }\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // On some 422 responses a more specific description is present alongside the generic message,\n    // so prefer the description, fall back to the generic message, and otherwise leave the default\n    // received-status message in place. For example, adding a bundle whose component is unavailable\n    // yields a body like the following, where description is meaningful and message is generic:\n    //\n    // { \"description\": \"The bundle product 'Product Title' cannot be added to the cart.\",\n    // \"message\": \"Cart Error\", \"status\": 422 }\n    //\n    // The status property of the response body mirrors the HTTP status code (empirical).\n\n    let message;\n    if (response.status === 422 && typeof error.response_body?.description === 'string') {\n      message = error.response_body.description;\n    } else if (typeof error.response_body?.message === 'string') {\n      message = error.response_body.message;\n    }\n\n    if (message) {\n      decorateError(error, 'message', message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Either id or line must be specified.\n */\nexport interface CartChangeInput {\n  /**\n   * Can be either the variant id or the line item key.\n   */\n  id?: number | string;\n\n  /**\n   * A cart can have multiple line items that share the same variant_id. For example, when variants\n   * have different line item properties, or automatic discounts create variants at different\n   * prices. To account for this, you can use the line property when updating the cart.\n   *\n   * The value of line is the 1-based index position of the item in the cart.\n   */\n  line?: number;\n\n  /**\n   * The properties property sets the line item properties. Its value must be an object of key-value\n   * pairs.\n   *\n   * Whenever a POST request includes properties, it overwrites the entire properties object. Any\n   * key-value pairs that were already in the properties object are erased.\n   *\n   * It's not possible to set a line item's properties property to an empty object once a value is\n   * set. If you need to remove all line item properties, then you need to remove the entire line\n   * item.\n   *\n   * You can visually hide line item properties at checkout by creating private properties. This\n   * technique might be an alternative to removing a line item when managing properties.\n   */\n  properties?: Record<string, any>;\n\n  /**\n   * The value of the quantity property represents the new quantity you want for the line item. It\n   * must be an integer.\n   *\n   * You can remove a line item by setting the quantity to 0.\n   *\n   * It is not clear from the documentation, but based on the description of the selling plan\n   * property, it looks like quantity is actually optional and defaults to 1 when not specified.\n   */\n  quantity?: number;\n\n  /**\n   * The selling_plan property sets the line item selling plan. Its value must be one of the\n   * following:\n   *\n   * * The selling plan ID: To set a specific selling plan for the line item.\n   * * null or an empty string: To remove any selling plan from the line item.\n   *\n   * When specifying the selling_plan property, consider the following:\n   * * You can use only the line property for identifying the line item.\n   * * You should always specify the quantity property. If the quantity isn't specified, then it's\n   *   assumed to be 1.\n   */\n  selling_plan?: number;\n}\n\n/**\n * The response body of a cart change.\n *\n * The two extra arrays are not part of the stored cart. Neither one is present in a get cart\n * response, and a cart update answers with a different extra property instead, so these describe\n * what the change did rather than what the cart holds.\n *\n * None of the functions here parse this body, because a caller that wants the resulting state\n * performs its own get cart afterward. So this type exists to describe what the endpoint answers\n * with rather than to annotate a return value.\n */\nexport interface CartChangeResponseBody extends Cart {\n  /**\n   * The line items the change brought into the cart.\n   *\n   * Nothing has been observed to put an entry in here, including a change that raised a quantity,\n   * so whether an entry looks like a removed one is unconfirmed.\n   */\n  items_added: CartChangeItem[];\n\n  /**\n   * The line items the change took out of the cart.\n   *\n   * A removal driven from a cart drawer, which identifies the line by its position rather than by\n   * variant, does report the removed line here. A removal that identifies the line by variant id\n   * has come back empty, so what qualifies is not settled.\n   */\n  items_removed: CartChangeItem[];\n}\n\n/**\n * An entry in one of the two arrays a cart change response carries.\n *\n * This is undocumented and the shape here is what has been observed on a removal. It is close\n * enough to a line item to be mistaken for one and it is not one, so a few differences are worth\n * knowing. The id is not the variant id, the line key arrives under a different property name, and\n * the price is a decimal string rather than an integer in subunits. It also carries no line item\n * properties, no weight, no discount details, and no parent relationship.\n */\nexport interface CartChangeItem {\n  /**\n   * An opaque identifier for the entry, formatted as a uuid.\n   *\n   * This is not the variant id, unlike the id of a cart line item.\n   */\n  id: string;\n\n  image: string;\n\n  presentment_price: number;\n\n  /**\n   * The price as a decimal string, in the presentment currency.\n   *\n   * Note that this is not an integer in currency subunits, which is how a price is expressed\n   * everywhere else in the cart.\n   *\n   * @example \"289.00\"\n   */\n  price: string;\n\n  product_id: number;\n\n  /**\n   * The title of the product, which here includes the variant.\n   *\n   * Note that a cart line item excludes the variant from this property and only combines the two in\n   * its title.\n   *\n   * @example \"TurboStyle - Blush\"\n   */\n  product_title: string;\n\n  product_type: string;\n  quantity: number;\n  sku: string;\n  title: string;\n  untranslated_product_title: string;\n  untranslated_variant_title: string;\n  url: string;\n  variant_id: number;\n  variant_title: string;\n  vendor: string;\n\n  /**\n   * The line item key, which a cart line item exposes as its key instead.\n   *\n   * @example \"47267055599716:fa232aa1a0b5b250164c9843d7a2ee6c\"\n   */\n  view_key: string;\n}\n\n/**\n * Use the /{locale}/cart/change.js endpoint to change the quantity, properties, and selling_plan\n * properties of a cart line item. Only items already in your cart can be changed, and you can\n * change only one line item at a time.\n *\n * The POST data requires either an id or line property to identify the line item to be changed.\n *\n * The value of the id property can be one of the following:\n *\n * * The line item's variant_id\n * * The line item's key\n *\n * A cart can have multiple line items that share the same variant_id. For example, when variants\n * have different line item properties, or automatic discounts create variants at different prices.\n * Because of this, it's recommended to use the line item key to ensure that you're only changing\n * the intended line item.\n *\n * If the item that you're attempting to change isn't already in the cart, then\n * /{locale}/cart/change.js doesn't add it. Instead, it returns a 404 error.\n *\n * The documentation states that the response body is the JSON of the cart. This exposes the\n * response object but does not fetch the response body.\n *\n * ### Removing out of stock items\n * It is unclear but based on a conversation with Shopify support, prefer to use cart/change instead\n * of cart/update to avoid issues with removing an out of stock item from the cart. Using update\n * results in a 422 error but supposedly cart/change does not.\n */\nexport async function cartChange(input: CartChangeInput) {\n  const url = resolveUrl(Shopify.routes.root + 'cart/change.js');\n\n  const init: RequestInit = {\n    body: JSON.stringify(input),\n    cache: 'no-store',\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    method: 'POST'\n  };\n\n  let response;\n  try {\n    response = await fetch(url, init);\n  } catch (error) {\n    decorateError(error, 'input', input);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response changing cart');\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'response', response);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} changing cart`);\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'request_id', getRequestId(response));\n\n    try {\n      decorateError(error, 'response_body', await response.json());\n    } catch (consumeError) {\n      // ignore\n    }\n\n    decorateError(error, 'response_status', response.status);\n    decorateError(error, 'response_url', response.url);\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // On some 422 responses a more specific description is present alongside the generic message,\n    // so prefer the description, fall back to the generic message, and otherwise leave the default\n    // received-status message in place. Shopify's generic message on these is literally \"Cart\n    // Error\", so promoting it over the description would discard the only meaningful text.\n    //\n    // The status property of the response body mirrors the HTTP status code (empirical).\n\n    let message;\n    if (response.status === 422 && typeof error.response_body?.description === 'string') {\n      message = error.response_body.description;\n    } else if (typeof error.response_body?.message === 'string') {\n      message = error.response_body.message;\n    }\n\n    if (message) {\n      decorateError(error, 'message', message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Use the GET /{locale}/cart.js endpoint to get the cart as JSON.\n *\n * All monetary properties are returned in the customer's presentment currency. To check the\n * customer's presentment currency, you can use the currency field in the response.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#get-locale-cart-js\n */\nexport async function cartFetch() {\n  const url = resolveUrl(Shopify.routes.root + 'cart.js');\n\n  let response;\n  try {\n    response = await fetch(url, { cache: 'no-store' });\n  } catch (error) {\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetching cart did not return a valid response');\n    decorateError(error, 'response', response);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} fetching cart`);\n    decorateError(error, 'request_id', getRequestId(response));\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n\n    try {\n      decorateError(error, 'response_body', await response.json());\n    } catch (consumeError) {\n      // ignore\n    }\n\n    throw error;\n  }\n\n  // Using response.json() obscures errors and prevents us from being able to see what the raw\n  // response body looks like. The usual cases of errant response bodies include:\n  //\n  // - default response bodies for errors, which are using plain text or html\n  // - firewall interceptions that produce html as a response, usually html\n  // - wifi connect interstitial page load, usually html\n  //\n  // So we are intentionally using response.text() and JSON.parse.\n\n  let responseBodyText;\n  try {\n    responseBodyText = await response.text();\n  } catch (error) {\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  let responseBodyObject;\n  try {\n    responseBodyObject = JSON.parse(responseBodyText);\n  } catch (error) {\n    decorateError(error, 'response_body', responseBodyText);\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  if (!isCart(responseBodyObject)) {\n    const error: any = new Error('Unexpected get cart response body');\n    decorateError(error, 'response_body', responseBodyObject);\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  return responseBodyObject;\n}\n\nexport interface CartUpdateInput {\n  /**\n   * New cart attributes. Existing cart attributes are overwritten.\n   */\n  attributes?: Attributes;\n\n  /**\n   * Beta shopify feature. Essentially works like Storefront discount apply mutation.\n   *\n   * Pass an empty string to remove discount(s).\n   */\n  discount?: string;\n\n  /**\n   * Cart note.\n   */\n  note?: string;\n\n  /**\n   * Even though Shopify supports specifying variant ids as numbers, we impose the restriction that\n   * the property names of the updates object must be strings due to some strange looking errors\n   * when specifying numbers.\n   */\n  updates?: Record<string, any>;\n}\n\n/**\n * The response body of a cart update.\n *\n * The changelog is not part of the stored cart. It is absent from a get cart response, and a cart\n * change answers with two differently named arrays instead, so it describes what the update did\n * rather than what the cart holds.\n *\n * None of the functions here parse this body, because a caller that wants the resulting state\n * performs its own get cart afterward. So this type exists to describe what the endpoint answers\n * with rather than to annotate a return value.\n */\nexport interface CartUpdateResponseBody extends Cart {\n  /**\n   * A summary of what the update brought into the cart.\n   *\n   * This is present on every successful update, including one that only writes attributes or a\n   * note, in which case the arrays inside it are empty.\n   */\n  items_changelog: ItemsChangelog;\n}\n\n/**\n * The summary of what an update brought into the cart.\n *\n * This is undocumented, so the shape here is only what has been observed. The added array is the\n * only property seen so far. A removed counterpart would be the obvious sibling but no update\n * response has produced one, not even one that set a quantity to zero, so it is left out rather\n * than guessed at.\n */\nexport interface ItemsChangelog {\n  /**\n   * The variants the update brought into the cart, and the quantity each one landed at.\n   *\n   * Only a variant that was not in the cart beforehand shows up here. Raising or lowering the\n   * quantity of a line that already exists leaves this empty, as does removing one, so this reports\n   * newly created lines rather than every quantity change.\n   */\n  added: ItemsChangelogAddition[];\n}\n\n/**\n * An entry in the added array of an update's changelog.\n *\n * This is not a line item. It carries only the variant and the quantity, so it cannot stand in for\n * the corresponding entry in the cart's items array.\n */\nexport interface ItemsChangelogAddition {\n  /**\n   * The quantity the line item landed at, not the amount by which it changed.\n   */\n  quantity: number;\n\n  /**\n   * Note that this is a number here, unlike elsewhere in this module where a variant id is\n   * restricted to a string on the request side.\n   */\n  variant_id: number;\n}\n\n/**\n * Performs a cart update.\n *\n * This does not return the cart updated state. You will need to do a followup call to fetch the\n * updated state of the cart. One reason this does also return the updated state is that we must\n * perform a second call to fetch the updated state but not all callers are interested in doing so.\n *\n * This does not return the response body of the call. There is no need to fetch the body because\n * the body does not contain useful data. The response body can contain 0 quantity items and other\n * kinds of strange things.\n *\n * This must specify the content type header. Shopify supports both a JSON request body and a form\n * data request body. Shopify defaults to interpreting requests as form submissions. This uses the\n * JSON format so the header is required.\n *\n * Backend scripts may affect the output.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-update-js\n * @see https://shopify.dev/docs/api/ajax/reference/cart#update-line-item-quantities\n * @see https://shopify.dev/docs/api/ajax/reference/cart#private-cart-attributes\n */\nexport async function cartUpdate(input: CartUpdateInput) {\n  const url = resolveUrl(Shopify.routes.root + 'cart/update.js');\n\n  const init: RequestInit = {\n    body: JSON.stringify(input),\n    cache: 'no-store',\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    method: 'POST'\n  };\n\n  let response;\n  try {\n    response = await fetch(url, init);\n  } catch (error) {\n    decorateError(error, 'input', input);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response updating cart');\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'response', response);\n    decorateError(error, 'url', url.href);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} updating cart`);\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'request_id', getRequestId(response));\n    decorateError(error, 'response_status', response.status);\n\n    try {\n      const parsedErrorResponseBody = await response.json();\n      decorateError(error, 'response_body', parsedErrorResponseBody);\n    } catch (consumeError) {\n      // ignore\n    }\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // On some 422 responses a more specific description is present alongside the generic message,\n    // so prefer the description, fall back to the generic message, and otherwise leave the default\n    // received-status message in place. Shopify's generic message on these is literally \"Cart\n    // Error\", so promoting it over the description would discard the only meaningful text.\n    //\n    // The status property of the response body mirrors the HTTP status code (empirical).\n\n    let message;\n    if (response.status === 422 && typeof error.response_body?.description === 'string') {\n      message = error.response_body.description;\n    } else if (typeof error.response_body?.message === 'string') {\n      message = error.response_body.message;\n    }\n\n    if (message) {\n      decorateError(error, 'message', message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Returns whether the error indicates the add failed because one of the products is out of stock\n * (not available for sale). In this case Shopify provides an error response body that looks like\n * this:\n *\n * ```json\n * {\n *   \"description\": \"All 0 <product title> are in your cart\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * ```json\n * {\n *   \"description\": \"The product '<title>' is already sold out.\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * This error can happen both when adding an item or updating the quantity of an item.\n *\n * This assumes error messages are in English.\n */\nexport function isUnavailableError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      // this one is only present in the add error response\n      if (description.startsWith('All 0') && description.includes('in your cart')) {\n        return true;\n      }\n\n      // this string is sometimes present in the update error response, not just the add response\n      if (description.includes('already sold out')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the error indicates the add failed because the shopper already holds the item's\n * per-order purchase limit in their cart. Shopify provides an error response body that looks like\n * this:\n *\n * ```json\n * {\n *   \"description\": \"The maximum quantity of this item is already in your cart.\",\n *   \"message\": \"The maximum quantity of this item is already in your cart.\",\n *   \"status\": 422\n * }\n * ```\n *\n * This assumes error messages are in English.\n */\nexport function isMaxQuantityError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      const normalizedDescription = description.toLowerCase();\n      if (normalizedDescription.includes('maximum quantity of this item is already in your cart')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the error indicates the add failed because the request mixed a bundle product and\n * an add-on in the same `cart/add.js` call, which Shopify does not allow. Shopify provides an error\n * response body that looks like this:\n *\n * ```json\n * {\n *   \"description\": \"Bundle products and add-ons cannot be mixed\",\n *   \"message\": \"Bundle products and add-ons cannot be mixed\",\n *   \"status\": 422\n * }\n * ```\n *\n * This assumes error messages are in English.\n */\nexport function isBundleAddonMixError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      const normalizedDescription = description.toLowerCase();\n      if (normalizedDescription.includes('bundle products and add-ons cannot be mixed')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the error indicates the add failed because the selling plan sent with the item is\n * not one Shopify will accept for that variant. This happens when a rendered page carries a selling\n * plan ID (for example a pre-order deferred-payment plan) that has since become stale: the variant\n * came back in stock, or the plan was edited or removed, after the page was rendered but before the\n * shopper clicked add. Shopify responds with a body that looks like this:\n *\n * ```json\n * {\n *   \"description\": \"Cannot apply selling plan to variant\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * This assumes error messages are in English.\n */\nexport function isInvalidSellingPlanError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      if (description.includes('Cannot apply selling plan to variant')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the given value looks like a cart object.\n *\n * This is not a complete validation. This is a shallow check that examines whether the value is an\n * object containing an items property that is an array. Any value that meets that criteria\n * satisfies the meaning of a cart.\n */\nexport function isCart(value: any): value is Cart {\n  return isObject(value) && Array.isArray(value.items);\n}\n\nfunction isObject(value: any): value is Record<string, any> {\n  return typeof value === 'object' && value !== null;\n}\n","/**\n * Returns a deep clone of the given value.\n *\n * Prefers structuredClone and falls back to a JSON round trip. The fallback covers both an\n * environment without structuredClone and one whose structuredClone cannot be trusted. On the\n * storefront a compromised browser can leave a monkey-patched structuredClone that returns\n * undefined for a real object rather than cloning it, which otherwise surfaces as confusing\n * property-access failures far from the cause. The values cloned through here are plain data that\n * round trips faithfully through JSON, so the fallback is equivalent rather than a degradation.\n */\nexport function copy<T>(value: T) {\n  if (typeof structuredClone === 'function') {\n    const cloned = structuredClone(value);\n\n    // A healthy structuredClone returns a nullish result only for a nullish input, so a nullish\n    // clone of a non-nullish value means the primitive cannot be trusted. Fall through to the\n    // serialization clone in that case.\n\n    const inputPresent = value !== null && value !== undefined;\n    const cloneMissing = cloned === null || cloned === undefined;\n\n    let trustworthy = true;\n    if (inputPresent && cloneMissing) {\n      trustworthy = false;\n    }\n\n    if (trustworthy) {\n      return cloned;\n    }\n  }\n\n  return <T>JSON.parse(JSON.stringify(value));\n}\n","/**\n * Returns a timestamp in milliseconds for measuring durations.\n *\n * This decorates performance.now().\n *\n * This tries to use the native high-resolution epoch, falling back to Date.now().\n *\n * If native throws, this still returns a value, but it logs a message as a side effect.\n *\n * This happens in a try/catch, so it is safe.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Performance/now\n */\nexport function now() {\n  try {\n    if (typeof performance?.now === 'function') {\n      return performance.now();\n    }\n  } catch (error) {\n    console.debug(error);\n  }\n\n  return Date.now();\n}\n\n/**\n * Records a named point on the User Timing timeline in devtools.\n *\n * This decorates performance.mark within a try/catch that suppresses errors.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark\n */\nexport function mark(name: string) {\n  try {\n    if (typeof performance?.mark === 'function') {\n      performance.mark(name);\n    }\n  } catch (error) {\n    console.debug(error);\n  }\n}\n\n/**\n * Records a named span between two timestamps on the User Timing timeline in devtools.\n *\n * This decorates performance.measure within a try/catch that suppresses errors.\n *\n * The bounds are explicit timestamps rather than named marks, which avoids the error the marked\n * name form throws when a start mark was never recorded.\n *\n * The detail param is optional and must be structured-cloneable. It should be a plain object. If\n * the value cannot be cloned, then performance.measure throws, which is caught and logged by this\n * wrapper. If specified, the detail shows up in the tooltip in the devtools panel.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure\n */\nexport function measure(name: string, start: number, end: number, detail?: unknown) {\n  try {\n    if (typeof performance?.measure === 'function') {\n      performance.measure(name, { start, end, detail });\n    }\n  } catch (error) {\n    console.debug(error);\n  }\n}\n","import type { Cart } from '@integrabeauty/shopify-ajax-api';\nimport { containsJavascriptWord } from '../../untrusted-value.js';\n\n/**\n * Returns whether a discount code string is well-formed. This does not guarantee the discount code\n * is valid and in the applicable state. That is why this function is not named isValid.\n *\n * This tolerates any kind of input so as to provide a convenient API. The caller is not obligated\n * to check that the input is a string beforehand. This function exists to avoid that boilerplate.\n * However, callers should be providing values that are strings.\n */\nexport function isWellFormed(value: any) {\n  if (typeof value !== 'string') {\n    return false;\n  }\n\n  if (value.length === 0) {\n    return false;\n  }\n\n  // This catches abuse of path parameters\n  if (value.includes('/') || value.includes('\\\\')) {\n    return false;\n  }\n\n  if (value.includes(' ')) {\n    return false;\n  }\n\n  const upper = value.toUpperCase();\n\n  // This catches common js bugs in the calling code\n  if (upper === 'UNDEFINED' || upper === 'NULL') {\n    return false;\n  }\n\n  if (containsJavascriptWord(upper)) {\n    return false;\n  }\n\n  return true;\n}\n\n/**\n * Returns normalized array of discount code strings currently applied to the cart.\n *\n * This filters out codes from the cart's discount_codes array property where the applicable value\n * is not true. That is what distinguishes this function from {@link getDiscountCodes}.\n *\n * This normalizes the codes. Shopify does not guarantee the code strings are uppercase. This will\n * uppercase.\n *\n * Tolerates null/undefined cart. Returns an empty array. This is safe to use without checking\n * whether cart is defined.\n *\n * Tolerates invalid discount_codes property. This supports a rare case where the cart was SSRed\n * correctly, but then not fetched. The SSR state is missing the property. In this case an empty\n * array is returned.\n */\nexport function getApplicableDiscountCodes(cart: Cart) {\n  if (!cart) {\n    return [];\n  }\n\n  if (!Array.isArray(cart.discount_codes)) {\n    return [];\n  }\n\n  return cart.discount_codes\n    .filter(code => code.applicable)\n    .map(code => code.code)\n    .map(code => code.toUpperCase());\n}\n\n/**\n * Returns all codes currently in the cart's discount codes array.\n *\n * This includes applicable as well as inapplicable codes. This inclusion of inapplicable codes is\n * what distinguishes this from the function {@link getApplicableDiscountCodes}.\n *\n * The returned codes are always uppercase. This must uppercase the codes returned because Shopify\n * does not guarantee the codes are upper case. Codes applied at checkout are saved in whatever\n * input case was used. The logic that applies codes while shopping can guarantee the input codes\n * are in uppercase but the logic at checkout cannot because the checkout logic is managed by\n * Shopify.\n *\n * It is possible that the discount_codes array property in the input cart is undefined. Shopify\n * does not support rendering the discount_codes array in liquid. There is a situation where the\n * cart state was server side rendered without discount_codes and then we failed to update the state\n * and therefore failed to set the property dynamically during init. This tolerates that so as to\n * guarantee that an array is always returned.\n */\nexport function getDiscountCodes(cart: Cart) {\n  if (!cart) {\n    return [];\n  }\n\n  if (!Array.isArray(cart.discount_codes)) {\n    return [];\n  }\n\n  return cart.discount_codes.map(code => code.code).map(code => code.toUpperCase());\n}\n\n/**\n * Totals up the discount amounts the cart has allocated and returns them as a map. Values are in\n * cents.\n *\n * The keys are discount application titles in uppercase rather than discount codes. For an ordinary\n * code those are the same string, which is what lets a caller match this map against the cart's\n * discount codes. They are not guaranteed to agree though, because Shopify treats the title as the\n * customer facing name of the discount rather than as the code. So a caller can end up holding a\n * title here that matches none of the codes on the cart.\n *\n * A code missing from this map has not necessarily done nothing. It means the cart allocated no\n * amount to it. A free shipping code behaves that way. It sits among the cart's discount codes as\n * applicable while appearing in neither array read here, because shipping is not priced until\n * checkout.\n *\n * Tolerates a null or undefined cart, and a cart missing any of the arrays this reads. Returns an\n * empty map. This is safe to use without checking whether the cart is defined.\n */\nexport function getDiscountAmounts(cart: Cart) {\n  // Shopify keeps discounts in two separate places, split by scope, so we read both to find all of\n  // them. A discount covering the cart as a whole lands in cart_level_discount_applications. A\n  // discount covering particular products lands on each line item holding one of those products, in\n  // that item's line_level_discount_allocations.\n  //\n  // The two do not overlap, which is what makes it safe to add both into one map without counting a\n  // discount twice. We checked this in real carts from both directions. A cart wide code showed up\n  // only in the cart level array with the line allocations empty, and a product code showed up only\n  // on the lines with the cart level array empty.\n  //\n  // A product discount is allocated separately to every line it touches, so its total is the sum of\n  // those per line amounts. That is what the running total is for, and it is also why the same code\n  // can appear many times while we walk the lines.\n  //\n  // Note which field each loop reads, because the two arrays do not name their amounts the same\n  // way. The line loop reads the allocation's amount, which is the share allocated to that one\n  // line. The application sitting inside that allocation carries a total_allocated_amount too, and\n  // in the carts we looked at it happened to equal the same share, but it describes the application\n  // rather than the line, so we do not read it there.\n  //\n  // We skip anything that is not a discount code. Automatically applied discounts and script\n  // applied discounts land in these same arrays, but they carry no code for a caller to match\n  // against, so they do not belong in a map keyed this way.\n\n  const discountAmounts = new Map<string, number>();\n\n  if (!cart) {\n    return discountAmounts;\n  }\n\n  // The isArray checks in the following code are considered acceptable paranoia. We want this to\n  // work in bad environments with partial carts, which we have surprisingly encountered.\n\n  // Discounts covering the cart as a whole\n\n  if (Array.isArray(cart.cart_level_discount_applications)) {\n    for (const application of cart.cart_level_discount_applications) {\n      if (application.type === 'discount_code') {\n        const code = application.title.toUpperCase();\n        const discount = application.total_allocated_amount;\n        const currentTotal = discountAmounts.get(code) || 0;\n        discountAmounts.set(code, currentTotal + discount);\n      }\n    }\n  }\n\n  // Discounts covering particular products, spread over the lines holding those products\n\n  if (Array.isArray(cart.items)) {\n    for (const lineItem of cart.items) {\n      if (!Array.isArray(lineItem.line_level_discount_allocations)) {\n        continue;\n      }\n\n      for (const allocation of lineItem.line_level_discount_allocations) {\n        if (allocation.discount_application.type === 'discount_code') {\n          const code = allocation.discount_application.title.toUpperCase();\n          const discount = allocation.amount;\n          const currentTotal = discountAmounts.get(code) || 0;\n          discountAmounts.set(code, currentTotal + discount);\n        }\n      }\n    }\n  }\n\n  return discountAmounts;\n}\n","/**\n * Returns whether a string contains the word \"javascript\" anywhere. This is a lightweight heuristic\n * for rejecting obviously-junk externally-sourced values before they enter state, not real XSS\n * defense, which happens more strictly at the actual sinks. The broad match suits values from a\n * constrained alphabet, such as discount codes, where the word never legitimately appears.\n */\nexport function containsJavascriptWord(value: any) {\n  return typeof value === 'string' && /javascript/i.test(value);\n}\n\n/**\n * Returns whether a string contains the \"javascript:\" URI scheme. This is a lightweight heuristic\n * for rejecting the classic scheme-injection vector before a value enters state, not real XSS\n * defense, which happens more strictly at the actual sinks. Unlike {@link containsJavascriptWord}\n * this tolerates the bare word, so a value that is itself an arbitrary external URL, such as a\n * referrer, is not falsely rejected merely for mentioning javascript in a path.\n */\nexport function containsJavascriptScheme(value: any) {\n  return typeof value === 'string' && /javascript:/i.test(value);\n}\n","const rnds8 = new Uint8Array(16);\nexport default function rng() {\n    return crypto.getRandomValues(rnds8);\n}\n","import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n    byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n    return (byteToHex[arr[offset + 0]] +\n        byteToHex[arr[offset + 1]] +\n        byteToHex[arr[offset + 2]] +\n        byteToHex[arr[offset + 3]] +\n        '-' +\n        byteToHex[arr[offset + 4]] +\n        byteToHex[arr[offset + 5]] +\n        '-' +\n        byteToHex[arr[offset + 6]] +\n        byteToHex[arr[offset + 7]] +\n        '-' +\n        byteToHex[arr[offset + 8]] +\n        byteToHex[arr[offset + 9]] +\n        '-' +\n        byteToHex[arr[offset + 10]] +\n        byteToHex[arr[offset + 11]] +\n        byteToHex[arr[offset + 12]] +\n        byteToHex[arr[offset + 13]] +\n        byteToHex[arr[offset + 14]] +\n        byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n    const uuid = unsafeStringify(arr, offset);\n    if (!validate(uuid)) {\n        throw TypeError('Stringified UUID is invalid');\n    }\n    return uuid;\n}\nexport default stringify;\n","import rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction v4(options, buf, offset) {\n    if (!buf && !options && crypto.randomUUID) {\n        return crypto.randomUUID();\n    }\n    return _v4(options, buf, offset);\n}\nfunction _v4(options, buf, offset) {\n    options = options || {};\n    const rnds = options.random ?? options.rng?.() ?? rng();\n    if (rnds.length < 16) {\n        throw new Error('Random bytes length must be >= 16');\n    }\n    rnds[6] = (rnds[6] & 0x0f) | 0x40;\n    rnds[8] = (rnds[8] & 0x3f) | 0x80;\n    if (buf) {\n        offset = offset || 0;\n        if (offset < 0 || offset + 16 > buf.length) {\n            throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n        }\n        for (let i = 0; i < 16; ++i) {\n            buf[offset + i] = rnds[i];\n        }\n        return buf;\n    }\n    return unsafeStringify(rnds);\n}\nexport default v4;\n","import { assert } from '@integrabeauty/assert';\nimport { type Cart, cartFetch as cartFetchOnce, isCart } from '@integrabeauty/shopify-ajax-api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { copy } from '../object.js';\nimport { getApplicableDiscountCodes } from './discount/discount.js';\nimport type { Transaction } from './transaction.js';\n\n/**\n * This event is generated when the state of the cart changes. The event includes the old state and\n * the new state and some deltas.\n *\n * Various cart api calls can result in unexpected behavior. For example, adding an item to cart\n * could cause the line key of unrelated cart lines to change. Therefore, this does not generate\n * deltas based on line key. In addition, each time cart state is obtained, there is no guarantee\n * that the state only changed because of the specified change. For example, a visitor interacting\n * with the same cart in multiple tabs may update the state in one tab, but because the deltas are\n * based on the prior state per tab, the next update in one of the tabs would diff against older\n * state. Therefore, listeners should refrain from operating on deltas and always treat each cart\n * update holistically.\n */\ntype CartUpdatedEvent = CustomEvent<{\n  /**\n   * The new cart state.\n   */\n  cart: Cart;\n\n  /**\n   * Discount codes present in the new cart state that are not present in the old cart state.\n   *\n   * This only includes discount codes. This does not include automatically applied discounts or\n   * script applied discounts.\n   */\n  discount_codes_added: string[];\n\n  /**\n   * Discount codes present in the old cart state that are not present in the new cart state.\n   *\n   * This only includes discount codes. This does not include automatically applied discounts or\n   * script applied discounts.\n   */\n  discount_codes_removed: string[];\n\n  /**\n   * A unique event id. This is useful if tracking this event client side and server side and there\n   * is a need to correlate front end and backend events.\n   */\n  event_id: string;\n\n  /**\n   * Whether this event represents a cart initialization.\n   *\n   * The diffs for an initial update are not useful. In this case the transition is from the empty\n   * state. Do not treat an initial update as a non-initial update.\n   */\n  is_initial?: boolean;\n\n  /**\n   * The previous state of the cart.\n   *\n   * This may be null. For example, when initializing the cart.\n   */\n  previous: Cart;\n\n  /**\n   * Certain transactions provide a description of the context in which the transaction occurred.\n   * For example, two add to cart elements might specify two scenarios when adding to cart, which\n   * then generates an update event with different scenarios, which can then be relayed downstream\n   * to analytics to differentiate in reporting.\n   *\n   * This is primarily an analytics label, but be aware that some consumers also branch on specific\n   * values as behavior, for example suppressing UI reactions to updates the shopper did not\n   * initiate in this tab. Certain values are therefore effectively part of behavior, not only\n   * reporting, so renaming one can change how the UI reacts, not just how a change is reported.\n   *\n   * This is intentionally a broad type. This is not meant to be a type safe enumeration. There are\n   * no heavy safeguards here.\n   */\n  scenario?: string;\n\n  /**\n   * Details of the cart service transaction that triggered this update event.\n   */\n  transaction: {\n    /**\n     * Transaction id\n     */\n    id: number;\n\n    /**\n     * Transaction name\n     */\n    name: string;\n  };\n\n  /**\n   * An array of variant units added to cart. This includes both variants added as new line items\n   * and quantity increases of existing line items.\n   *\n   * A product variant in this array does not contain all the properties of a cart item because we\n   * cannot trivially discern which lines changed when a cart updates.\n   */\n  variants_added: ProductVariant[];\n\n  /**\n   * Variants removed from cart. This can be removal of lines or quantity decreases.\n   *\n   * A product variant does not contain all the properties of a cart item because we cannot\n   * trivially discern which lines changed as a result of a state change.\n   */\n  variants_removed: ProductVariant[];\n}>;\n\n/**\n * Represents a product variant. A Shopify product has one or more variants. A Shopify product with\n * no explicitly configured variant still has a default variant.\n *\n * This should not be confused with a canonical product variant that exists in a single line item in\n * a cart object. This is an amalgamation, a composition of the variant as it exists in one or more\n * line items in a cart. This assumes that price and other properties are the same, apart from the\n * special merged_properties property, which combines the line item properties of the lines\n * containing the variant together.\n */\ninterface ProductVariant {\n  /**\n   * Path to variant image\n   *\n   * @example \"//langehair.com/cdn/shop/products/filename.jpg?v=1680740790\"\n   */\n  image_url: string;\n\n  /**\n   * The combined line item properties of the line items containing this variant\n   *\n   * The key of this object is the property name.\n   *\n   * The value for each key in this object are the distinct values that were merged together.\n   */\n  merged_properties: Record<string, string[]>;\n\n  price: number;\n  product_id: number;\n  product_title: string;\n  product_type: string;\n  quantity: number;\n  sku: string;\n\n  /**\n   * Product variant url (relative)\n   *\n   * @example \"/products/salt-sea-texturizing-spray?variant=41345560477796\"\n   */\n  url: string;\n\n  variant_id: number;\n  variant_title: string;\n}\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'cart-updated': CartUpdatedEvent;\n  }\n}\n\n/**\n * Sets the current state of the cart. This is the in memory state that is stored as the value of a\n * global variable, not the server side state.\n *\n * In memory state is PER TAB PER WINDOW/APP. If someone is browsing in multiple tabs concurrently\n * the state may conflict. The cart token corresponds to a server side cart id and is stored in a\n * cookie that is shared among all tabs, so it is possible for multiple tabs to refer to the same\n * cart object.\n *\n * Always use this method to update the state of the global cart variable. Do not update the state\n * of the global cart variable directly.\n *\n * Throws an error if the given cart is not a cart. This also indicates a more insidious problem\n * where there is no longer a guarantee that client side state reflects server side state, so\n * consider some error handling.\n *\n * This saves a copy of the cart so that future updates by reference to the cart object do not\n * modify the global cart state.\n *\n * Emit a cart updated event that contains some of the differences. This emits the updated event\n * even when there are no changes detected.\n *\n * Any update can result in unrelated cart changes. Changing one line's properties could cause line\n * merging. Also, prior state comes from page state, which is per tab, so interactions in multiple\n * tabs can produce strange behavior.\n *\n * The dispatcher is the target the updated event is dispatched from. It is required but may be\n * null, and it may be any event target, not necessarily an element or window. The target is used as\n * given, with two exceptions that exist only to keep the event reaching window level listeners: a\n * null target dispatches from window, and so does a detached element. A detached element only\n * bubbles up to its detached subtree root, which is not window, so dispatching from it directly\n * would not reach those listeners. Callers should prefer the element most directly responsible for\n * the change, so a connected element carries truthful provenance and bubbles, crossing shadow\n * boundaries, up to the global listeners. Genuinely ambient updates with no originating element,\n * such as init or a cross tab sync, pass window (or null).\n */\nexport function write(dispatcher: EventTarget | null, cart: Cart, transaction: Transaction,\n  scenario?: string, initial = false) {\n  const previous = initial ? null : read();\n\n  // Update global state.\n  assert(isCart(cart), 'Cannot write invalid cart');\n  cart_lifecycle.cart = copy(cart);\n\n  // Prepare for diffing. We group by variant because line keys change in unexpected ways.\n  const oldVariants = groupByVariant(previous);\n  const newVariants = groupByVariant(cart);\n\n  // For discount code deltas, we only examine deltas in codes that both exist and are in the\n  // applicable state in the cart's discount_codes array.\n\n  const prevCodes = getApplicableDiscountCodes(previous);\n  const currCodes = getApplicableDiscountCodes(cart);\n\n  // Emit an event with diffs\n\n  type Detail = CartUpdatedEvent['detail'];\n  const event = new CustomEvent<Detail>('cart-updated', {\n    detail: {\n      cart,\n      discount_codes_added: complement(currCodes, prevCodes),\n      discount_codes_removed: complement(prevCodes, currCodes),\n      event_id: uuidv4(),\n      is_initial: initial,\n      previous,\n      scenario,\n      variants_added: findVariantsAdded(oldVariants, newVariants),\n      variants_removed: findVariantsRemoved(oldVariants, newVariants),\n      transaction: {\n        id: transaction.id,\n        name: transaction.name\n      }\n    },\n    bubbles: true,\n    composed: true\n  });\n\n  // Dispatch the update from whatever target the caller designates; this forms no opinion about\n  // what kind of target it is. The only special handling exists to keep the event reaching the\n  // global listeners: a detached element has no live ancestor chain to bubble through, so it is\n  // swapped for the global scope, and a null target means the global scope outright. Any other\n  // target, including a connected element, is used as given, so it carries truthful provenance and\n  // bubbles, crossing shadow boundaries, up to the global listeners.\n\n  let target: EventTarget;\n  if (dispatcher instanceof Element) {\n    if (dispatcher.isConnected) {\n      target = dispatcher;\n    } else {\n      target = window;\n    }\n  } else if (dispatcher) {\n    target = dispatcher;\n  } else {\n    target = window;\n  }\n\n  target.dispatchEvent(event);\n}\n\n/**\n * Returns a copy of the global cart state.\n *\n * Throws an error if the global cart object is not a cart object.\n */\nexport function read() {\n  assert(isCart(cart_lifecycle.cart), 'Global cart state is invalid on read');\n  return copy(cart_lifecycle.cart);\n}\n\ninterface FetchOptions {\n  /**\n   * The total number of requests to make, counting the first one.\n   */\n  attempts?: number;\n\n  /**\n   * How long to wait in milliseconds before each retry, one entry per retry.\n   */\n  delays?: number[];\n\n  /**\n   * How long in milliseconds to wait on a single attempt before counting it as failed. Leaving it\n   * out means each attempt waits as long as the request takes.\n   */\n  timeout?: number;\n}\n\n/**\n * Reads the server side cart state.\n *\n * This wraps the ajax api call of the same name and is what cart code here should reach for\n * instead. The only things it adds are a few attempts and an optional time limit on each one, so it\n * behaves the same otherwise.\n *\n * A cart read is idempotent and it sits in the middle of things the shopper is waiting on, like\n * adding to cart, so a single failed request is worth another go rather than turning into an error\n * the shopper sees. We make up to a few attempts, waiting a little longer before each retry. If\n * every attempt fails then the error from the last one is thrown and the caller decides what that\n * means. Some callers treat it as fatal, some stay quiet about it, so this forms no opinion.\n *\n * Do not use this to react to a mutation you just made unless you are fine with a repeat request,\n * because each attempt sends a fresh one.\n *\n * Callers can pass options to change how many attempts are made, how long to wait between them, and\n * how long a single attempt gets before it counts as failed. Leaving them out gets the defaults\n * below, and no time limit.\n *\n * There is no limit on the total time across all attempts. The caller is responsible for picking\n * options that keep the worst case acceptable, which is every attempt using its full time limit\n * plus every wait between them.\n */\nexport async function cartFetch(options: FetchOptions = {}) {\n  // How many times we are willing to ask the server before giving up. The first attempt counts, so\n  // this is the total number of requests and not the number of retries on top of one.\n  const limit = options.attempts || 3;\n\n  // How long we wait before each retry. Shopify throttles cart reads, so going right back at it\n  // just piles more requests onto something that already told us to slow down. These stay short as\n  // a balance between waiting long enough to avoid another 429 and waiting so long the user\n  // perceives the UI as broken.\n  const delays = options.delays || [100, 250];\n\n  let lastError: Error;\n\n  for (let attempts = 0; attempts < limit; attempts++) {\n    if (attempts > 0) {\n      const delay = delays[attempts - 1];\n      await resolveAfterTimeout(delay);\n    }\n\n    try {\n      const cart = await cartFetchOnceWithTimeout(options.timeout);\n      return cart;\n    } catch (error) {\n      lastError = error;\n    }\n  }\n\n  throw lastError;\n}\n\n/**\n * Reads the cart once, giving up after the given number of milliseconds. Leaving out the timeout\n * means there is no limit.\n *\n * Giving up does not stop the request. It keeps going in the background and whatever it eventually\n * returns is ignored. That is fine because reading the cart changes nothing, but it means this must\n * never be used for a cart write, since a write that times out here can still land later.\n */\nasync function cartFetchOnceWithTimeout(timeout: number) {\n  if (!timeout) {\n    return cartFetchOnce();\n  }\n\n  let timer: ReturnType<typeof setTimeout>;\n  const timeoutPromise = new Promise<never>((_resolve, reject) => {\n    const error = new Error('Timed out fetching cart');\n    timer = setTimeout(reject, timeout, error);\n  });\n\n  try {\n    const cart = await Promise.race([cartFetchOnce(), timeoutPromise]);\n    return cart;\n  } finally {\n    clearTimeout(timer);\n  }\n}\n\n/**\n * Returns a promise which resolves after the given timeout\n */\nfunction resolveAfterTimeout(milliseconds: number) {\n  return new Promise(resolve => {\n    setTimeout(resolve, milliseconds);\n  });\n}\n\ntype ProductVariantMap = Record<string, ProductVariant>;\n\n/**\n * Aggregates cart line items by variant. Returns the aggregated variants as an object where each\n * key of the object is a variant id and each value is the corresponding variant object.\n *\n * This does some complex stuff. One key thing to constantly point out is that a variant can appear\n * in more than one line item in a cart. There are several reasons why. Shopify generally refers to\n * this as a variant split. Variant splits happen for reasons such as having different line item\n * properties, or different prices as a result of allocated discounted applications, or because of\n * more rare reasons like product subscriptions. Because we are grouping by variant id, we have to\n * invent some novel ways of combining the different values of the line items containing the same\n * variant.\n *\n * To combine the line item properties, we invent a new property, merged_properties, that contains\n * the properties of each line item. In each merged properties value, there is an array of values.\n * When appending to the values array, we only append unique values (using exact value comparison).\n *\n * There are many downstream concerns. Here is one example. When relaying an add to cart event to a\n * third party analytics system, we sometimes want the product's image url. This function's output\n * is the input to the variants_added array of the cart updated event emitted as a result of a cart\n * write. So we have to embed the image url here in the variant object so that it can be accessed\n * without having to do some kind of later lookup.\n *\n * @param cart the input cart to aggregate\n */\nfunction groupByVariant(cart: Cart) {\n  const output: ProductVariantMap = {};\n\n  // It is important to return null here, and not the empty object, so that later functions that use\n  // this output can trivially distinguish between transitions from null state and transitions from\n  // empty cart state. The transition from null state happens on init.\n\n  if (!cart) {\n    return null;\n  }\n\n  for (const item of cart.items) {\n    const variant = output[item.variant_id.toString()];\n    if (variant) {\n      // Merge the item's properties into the variant's properties.\n      for (const property in item.properties || {}) {\n        variant.merged_properties[property] = variant.merged_properties[property] || [];\n        const value = item.properties[property];\n        if (!variant.merged_properties[property].includes(value)) {\n          variant.merged_properties[property].push(value);\n        }\n      }\n\n      variant.quantity += item.quantity;\n    } else {\n      // Merge in the initial properties, where each value is a one value array.\n      const properties: Record<string, string[]> = {};\n      for (const key in item.properties || {}) {\n        properties[key] = [item.properties[key]];\n      }\n\n      output[item.variant_id.toString()] = {\n        image_url: item.image,\n        merged_properties: properties,\n        // eslint-disable-next-line @typescript-eslint/no-deprecated\n        price: item.price,\n        product_id: item.product_id,\n        product_title: item.product_title,\n        product_type: item.product_type,\n        quantity: item.quantity,\n        sku: item.sku,\n        url: item.url,\n        variant_id: item.variant_id,\n        variant_title: item.variant_title\n      };\n    }\n  }\n\n  return output;\n}\n\n/**\n * Returns an array of variant data objects that were added as a result of the write mutation.\n *\n * This tolerates nulls\n *\n * @param c0map variants in old cart state\n * @param c1map variants in new cart state\n * @returns variants added array\n */\nfunction findVariantsAdded(c0map: ProductVariantMap, c1map: ProductVariantMap) {\n  // When the cart transitions from the null state, there are no previous variants. This means that\n  // all variants in the new state have been added. Transitioning from null can mean multiple\n  // things. It can mean either that cart state was initialized on page load, or that cart state was\n  // recovered from error. Because of the ambiguity we are not emitting adds for transitions from\n  // the null state.\n\n  if (!c0map) {\n    return [];\n  }\n\n  // Figure out which variants had quantity increases in any of the line items containing those\n  // variants. Any variant that had a quantity increase is referred to here as an increment. In\n  // addition, look for any variants that are present in the new state but not in the old state, in\n  // which case their full quantity is the quantity added.\n\n  const increments: ProductVariantMap = {};\n  for (const key in c1map) {\n    if (c0map[key]) {\n      // In this branch, the variant was present in the old map and is present also in the new map.\n\n      if (c1map[key].quantity > c0map[key].quantity) {\n        increments[key] = copy(c1map[key]);\n        increments[key].quantity = c1map[key].quantity - c0map[key].quantity;\n      } else {\n        // ignore 0 quantity change and negative quantity change (decrements).\n      }\n    } else {\n      // In this branch, the variant is present in the new map but not in the old map.\n\n      increments[key] = copy(c1map[key]);\n    }\n  }\n\n  return Object.keys(increments).map(key => increments[key]);\n}\n\nfunction findVariantsRemoved(c0map: ProductVariantMap, c1map: ProductVariantMap) {\n  if (!c0map) {\n    return [];\n  }\n\n  const decrements: ProductVariantMap = {};\n  for (const key in c0map) {\n    if (c1map[key]) {\n      if (c1map[key].quantity < c0map[key].quantity) {\n        decrements[key] = copy(c1map[key]);\n        decrements[key].quantity = c0map[key].quantity - c1map[key].quantity;\n      } else {\n        // ignore 0 quantity change\n      }\n    } else {\n      // if the old variant is not found in the updated cart, all quantity was removed\n      decrements[key] = copy(c0map[key]);\n    }\n  }\n\n  return Object.keys(decrements).map(key => decrements[key]);\n}\n\n/**\n * Return all values present in array a that are not present in array b.\n *\n * The complement of a set is the set that includes all the elements of the universal set that are\n * not present in the given set. Here, a is the universal set and b is the given set.\n */\nfunction complement(a: string[], b: string[]) {\n  assert(Array.isArray(a), 'complement parameter \"a\" is not an array');\n  assert(Array.isArray(b), 'complement parameter \"b\" is not an array');\n  return a.filter(value => !b.includes(value));\n}\n","import { type Cart, cartUpdate } from '@integrabeauty/shopify-ajax-api';\nimport { cartFetch } from '../state.js';\nimport { getApplicableDiscountCodes, getDiscountCodes, isWellFormed } from './discount.js';\n\ntype DiscountOverride = typeof cart_lifecycle.discount_overrides[number];\n\n/**\n * Updates a cart's discount codes. This uses a specially enabled feature on the lange Shopify shop\n * that enables the use of the cart update ajax api to modify the cart's discounts. The special\n * feature enables the use of a discount property in the update call.\n *\n * New codes are partially validated before the http request is sent. If any new code is invalid,\n * this throws an error immediately.\n *\n * This will send an update request even if the discount codes have not changed. This does not check\n * whether sending the request is pointless. One of the reasons why this check is not performed is\n * because sometimes callers want to call this function even when it seems pointless because of the\n * risk that client side state is out of sync with server side state. That kind of thing can happen\n * for example when browsing the site in multiple tabs, because client side state is largely managed\n * per tab, so one tab may naively update discounts without awareness of changes made in other tabs.\n * While we attempt to keep state synchronized across tabs, there is no guarantee.\n *\n * The special discount property of the update request body has no documentation. We figured out\n * that it works similar to the checkout links discount url parameter. Multiple codes can be\n * specified as a comma separated value. This also means that codes should not contain commas. We\n * also figured out that it overwrites all discounts. This does not just add new discounts. That is\n * why this requires both the set of existing codes and the new codes.\n *\n * @param cart the state of the cart prior to update\n * @param newCodes the codes that should be applied to the cart in addition to the existing codes\n * @param overrides discount overrides, this is expected to always be defined even if there are no\n * overrides\n * @param postValidate whether to check if the new codes were actually applied after the update\n *\n * @see https://help.shopify.com/en/manual/discounts/managing-discount-codes\n */\nexport async function applyDiscountsHelper(cart: Cart, newCodes: string[],\n  overrides: DiscountOverride[], postValidate = true) {\n  validateCodesBeforeUpdate(newCodes);\n\n  const oldUppers = getCurrentCodes(cart, newCodes.length);\n  const newUppers = newCodes.map(code => code.toUpperCase());\n\n  const retainedOldUppers = oldUppers.filter(code =>\n    !overrides.some(override =>\n      override.old_code === code && newUppers.includes(override.new_code)));\n\n  const mergedUppers = new Set<string>([...retainedOldUppers, ...newUppers]);\n  const mergedCodes = [...mergedUppers];\n\n  const discount = mergedCodes.join(',');\n  await cartUpdate({ discount });\n  const updatedCart = await cartFetch();\n\n  // Only validate if post validation requested. Validation is performed by default but there are\n  // some use cases where discounts are applied even in applicable situations, such as when the cart\n  // is empty (the discount is applied but if you look at the discount object in the discount_codes\n  // array the applicable property is false). By only validating if requested we avoid generating\n  // routine validation errors.\n\n  if (postValidate) {\n    validateCodesAfterUpdate(updatedCart, newCodes);\n  }\n\n  return updatedCart;\n}\n\n/**\n * Obtain the existing discount codes. Generally, we want to retain all codes, including currently\n * inapplicable codes, as those codes may become applicable later and have been deemed applied even\n * though they have no effect. However, retaining all codes leads to the possible accumulation of\n * several inapplicable codes. This leads into a poorly documented issue. Once there are about 5 or\n * 6 codes applied, in either the applicable or inapplicable state, future applications fail without\n * regard to code applicability.\n *\n * Therefore, this needs to take into account the total number of discount codes that will be sent\n * as a part of a cart update. This is why the new codes array is also an input to this function\n * that has only to do with old codes.\n *\n * This is a hacky rushed fix that needs to be revisited. Expired codes stick around. Typo codes\n * stick around. Once we reach more than 5 codes (in any state), all future applications, even of\n * valid codes, incorrectly fail to apply. So in this case, we have to filter down the list. The\n * quickest thing we can do is just drop codes that are applied but in the inapplicable state. We\n * prefer not to do this because we want to support visitors who apply a code, it becomes applied\n * but inapplicable, then add a product, and it becomes applicable.\n *\n * This hacky fix is not perfect because it is not deduplicating, this is an inaccurate and quick\n * fix for \"several\" issues, not all of the issues stemming from bad codes sticking around.\n *\n * There is a limitation on the number of discount codes that can be applied to the same order,\n * including codes in the inapplicable state. According to Shopify, \"Customers can use a maximum of\n * five product or order discount codes and one shipping discount code on the same order.\"\n *\n * @see https://help.shopify.com/en/manual/discounts/combining-discounts/discount-combinations#limitations\n */\nfunction getCurrentCodes(cart: Cart, newCodesCount: number) {\n  const currentCodes = Array.isArray(cart.discount_codes) ? cart.discount_codes : [];\n\n  // We test against what would be the total number of applied codes in determining which codes\n  // should be included in the output. If adding the new codes would exceed the threshold, then we\n  // try our best to trim the number of currently applied codes down to below the threshold. Since\n  // we cannot tell which discount codes are shipping discount codes, we use the threshold of 5\n  // instead of 6.\n\n  // There is currently no reliable additional way of choosing which discount codes to remove. There\n  // is no clear documentation on the order of the codes in the array. So we have to choose an\n  // arbitrary method of ejecting some codes. For now, this basically just gets rid of any codes\n  // that are inapplicable. That will most likely remove typos and expired codes. Most visitors will\n  // not discern the missing codes.\n\n  // This does not guarantee that the output is reduced to below the threshold. If there are 5\n  // applicable codes, then no codes will be removed, and the 6th application can still fail.\n\n  // This is not trying to get down to just below the threshold. This does a whole sweep. We might\n  // want to change this to remove inapplicable codes up until the threshold is no longer exceeded.\n\n  // Drop all inapplicable codes by keeping around only those older codes that are applicable. Try\n  // to retain inapplicable shipping codes.\n\n  if (currentCodes.length + newCodesCount > 5) {\n    return currentCodes\n      .filter(code => code.applicable || /ship/i.test(code.code))\n      .map(code => code.code)\n      .map(code => code.toUpperCase());\n  }\n\n  return getDiscountCodes(cart);\n}\n\nfunction validateCodesBeforeUpdate(newCodes: string[]) {\n  if (newCodes.length < 1) {\n    throw new Error('At least one discount code must be specified');\n  }\n\n  // If any one of the input codes is invalid, reject with a specific error message that mentions\n  // the code. It is important to be explicit here because this error may be visible to users.\n\n  for (const code of newCodes) {\n    if (!isWellFormed(code)) {\n      const error: any = new Error(`Invalid discount code ${code}`);\n      error.discount_code = code;\n      error.discount_codes = newCodes;\n      throw error;\n    }\n  }\n}\n\n/**\n * When validating whether the codes applied, we only validate whether new codes applied. We do not\n * validate whether existing codes applied, because it possible that applying a new code caused a\n * previous code to be removed, because the new code replaces the existing code, for example,\n * because the new code offers a better discount.\n *\n * If a code was applied but in the inapplicable state, then we treat the add as a failure, even\n * though the code persists in the discount_codes array. The rationale is complex. Basically, we\n * must address the UX concern that we must provide feedback that an invalid code was applied, so we\n * must trigger an error. However, some codes, such as shipping codes, can be early applied. Some\n * codes, such as shipping codes, can apply but have no cart level applications or line level\n * allocations, but still be applied, and still be valid, even though the code is not actually\n * affecting the estimated total. We do not want to treat that as an error.\n *\n * For a shipping discount that is applicable, even though it has no effect, it will appear in the\n * discount_codes array with applicable true.\n *\n * For a fictional discount that is inapplicable, it will appear in the discount_codes array with\n * applicable false.\n */\nfunction validateCodesAfterUpdate(cart: Cart, newCodes: string[]) {\n  const updatedNormalCodes = getApplicableDiscountCodes(cart);\n\n  // Downstream analytics sometimes does a breakdown on the error message. Input new codes are in\n  // mixed cases, leading to nearly duplicate messages. To make it easier on analysis, we report on\n  // the normalized discount code, not the actual characters entered by visitors.\n\n  for (const newCode of newCodes) {\n    const upperNewCode = newCode.toUpperCase();\n    if (!updatedNormalCodes.includes(upperNewCode)) {\n      const error: any = new Error(`The discount code ${upperNewCode} was not applied`);\n      error.discount_code = upperNewCode;\n      error.discount_codes = newCodes;\n      throw error;\n    }\n  }\n}\n","/**\n * Copies a single piece of supplementary context onto an error before it is thrown or reported.\n *\n * Use this when decorating a value the calling code did not construct, such as a caught value or an\n * error handed in by another module. An error the caller just built with new is always writable, so\n * plain assignment is fine there. A value from elsewhere may be a foreign object this code does not\n * own. Such a value can carry a non-writable property, such as a DOMException whose code is a read-\n * only accessor, or be frozen outright, and assigning to it then throws. Attaching context must\n * never itself fail, so a rejected assignment is swallowed and the remaining context is still\n * attempted. Guarding each property in isolation means a single read-only slot, the common case,\n * costs only that one property rather than all of them.\n *\n * In practice the value that triggers this is almost always a DOMException that reached the caller\n * from a fetch the browser cut short, such as an abort when an in-flight request is interrupted.\n * The platform defines a DOMException's code, name, and message as read-only attributes, so\n * assigning a code onto one throws under the strict mode that all bundled module code runs in. It\n * surfaces from Safari far more than elsewhere for two reasons. The reported wording, attempted to\n * assign to readonly property, is JavaScriptCore's phrasing for this failure, so the same bug in\n * another engine is worded differently and groups as a separate report, which makes this one look\n * Safari specific even though the read-only rule is not. And iOS Safari abandons in-flight fetches\n * aggressively when a tab is backgrounded or a connection drops, which is when such a DOMException\n * tends to be produced and handed to code that assumed an ordinary writable error.\n *\n * A swallowed failure is logged at a level low enough that it is recorded as a breadcrumb for\n * monitoring rather than escalated into its own captured event. That keeps the lost context visible\n * alongside the eventual report without adding fresh noise of its own. The breadcrumb names the\n * property that could not be attached, the kind of value that rejected it, and why, which is enough\n * to recognize a read-only or frozen caught value after the fact.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Read-only\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n * @see https://webidl.spec.whatwg.org/#dfn-read-only\n */\nexport function decorateError(error: any, key: string, value: unknown) {\n  try {\n    error[key] = value;\n  } catch (assignmentError) {\n    let reason: string;\n    if (assignmentError instanceof Error) {\n      reason = assignmentError.message;\n    } else {\n      reason = `${assignmentError}`;\n    }\n\n    console.log('Failed to assign error property', {\n      key,\n      name: error?.name,\n      tag: Object.prototype.toString.call(error),\n      reason\n    });\n  }\n}\n","import type { Cart } from '@integrabeauty/shopify-ajax-api';\nimport { decorateError } from '../decorate-error.js';\nimport type { Transaction } from './transaction.js';\n\ntype CartUpdateErredEvent = CustomEvent<{\n  /**\n   * Cart state.\n   *\n   * This may be undefined or null or invalid because almost nothing is guaranteed.\n   */\n  cart?: Cart;\n\n  /**\n   * An error code that describes the context of the error or the type of the error.\n   */\n  code: string;\n\n  /**\n   * The error that occurred. This will almost always be of type `Error`. However, this is set to\n   * the value of whatever error was caught. Values not of type `Error` can be thrown. We are not\n   * checking that the value is an instance of an `Error` first. It is not safe to assume this is of\n   * type `Error` particularly because of code minimization and third party modifications.\n   */\n  error: any;\n\n  /**\n   * A mix of misc properties that various cart transactions will include that help provide more\n   * information about the inputs to the code that led to the error.\n   */\n  inputs?: Record<string, any>;\n\n  /**\n   * If true, this means the event was emitted by a multi-fetch operation where some of the fetches\n   * succeeded. For example, consider the addItems call, which has to do two fetches in its virtual\n   * transaction. This will be true when the fetch to add the item succeeded but the fetch to get\n   * the new cart state after the add failed. This flag exists so that callers can choose to react\n   * to this situation differently, e.g. by retrying just the cart state fetch, or changing what\n   * error message is displayed (a failure to add versus a partially successful add).\n   */\n  partial?: boolean;\n\n  /**\n   * An optional programmatic code or human-readable description of the context in which some cart\n   * update was taken. This is primarily for analytics, but can also be used for ui logic.\n   *\n   * For example:\n   * * distinctly identify two different add to cart buttons\n   * * distinctly identify a particular ui element used to apply a discount\n   */\n  scenario?: string;\n\n  /**\n   * The transaction in which the error occurred.\n   */\n  transaction: Transaction;\n}>;\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'cart-update-erred': CartUpdateErredEvent;\n  }\n}\n\n/**\n * Broadcasts a cart failure and, when it is a genuine programmer error, surfaces it to the global\n * error handler so that monitoring records it.\n *\n * The cart helpers deliberately catch their own failures so that the code awaiting them is not\n * forced to handle a promise rejection. Every failure is broadcast as an event for the interface to\n * react to. That catch keeps the promise from rejecting, but for an unexpected failure it does not\n * mean the problem was actually dealt with. It is still a real error that would have reached the\n * top level had it never been caught. The escalation step re-throws those so that they do, leaving\n * error monitoring to record whatever uncaught errors arrive with no knowledge of the cart.\n */\nexport function report(dispatcher: EventTarget | null, detail: CartUpdateErredEvent['detail']) {\n  dispatchCartUpdateErred(dispatcher, detail);\n  escalateCartError(detail);\n}\n\n/**\n * Dispatches the failure as an event for the interface to react to.\n *\n * The dispatcher is the target the event is dispatched from. It is required but may be null, and it\n * may be any event target, not necessarily an element or window. The target is used as given, with\n * two exceptions that exist only to keep the event reaching window level listeners: a null target\n * dispatches from window, and so does a detached element.\n *\n * A detached element is swapped for window because of how bubbling works. We want to guarantee the\n * event bubbles up to window so that window level listeners are notified. A detached element only\n * bubbles up to its detached subtree root, which is not window, so dispatching from it directly\n * would not reach those listeners.\n */\nfunction dispatchCartUpdateErred(dispatcher: EventTarget | null,\n  detail: CartUpdateErredEvent['detail']) {\n  // Dispatch the failure from whatever target the caller designates; this forms no opinion about\n  // what kind of target it is. The only special handling exists to keep the event reaching the\n  // global listeners: a detached element has no live ancestor chain to bubble through, so it is\n  // swapped for the global scope, and a null target means the global scope outright. Any other\n  // target, including a connected element, is used as given. Callers should still prefer the\n  // element most directly responsible for the failure, so a connected element carries truthful\n  // provenance and bubbles, crossing shadow boundaries, up to the global listeners.\n\n  let target: EventTarget;\n  if (dispatcher instanceof Element) {\n    if (dispatcher.isConnected) {\n      target = dispatcher;\n    } else {\n      target = window;\n    }\n  } else if (dispatcher) {\n    target = dispatcher;\n  } else {\n    target = window;\n  }\n\n  const event = new CustomEvent<typeof detail>('cart-update-erred', {\n    detail,\n    bubbles: true,\n    composed: true\n  });\n\n  target.dispatchEvent(event);\n}\n\n/**\n * Surfaces a genuine programmer error to the global error handler so that monitoring records it.\n *\n * Some failures are expected outcomes with their own user facing messaging rather than programmer\n * errors: a shopper adding an out of stock item, or mistyping a discount code. Those are never\n * escalated to monitoring. Each such failure is identified by its code, the same classification the\n * cart helpers attach for the interface, so the list here must keep pace with the codes those\n * helpers produce.\n *\n * The throw is deferred to a later task so that it escapes the surrounding promise chain instead of\n * rejecting it, preserving the rejection-free contract of the cart helpers. The error keeps the\n * stack captured when it was first created, so the report still points at the origin of the failure\n * rather than at this deferred throw. However, beware that this does mean that the error event\n * properties will reflect this throw context, not the original error context, which means that the\n * instrumenting code must prioritize the original error properties over the error event properties.\n *\n * When available, the throw is routed through the reportError api, which surfaces the value to the\n * global error handler without the extra task hop. Older engines that lack it fall back to a\n * deferred throw, which reaches the same handler.\n *\n * The context describing the failure is attached to the error so that monitoring which serializes\n * the non-standard properties of an error can include it. A value that is not an error is wrapped\n * so that the context still has somewhere to live.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/reportError\n */\nfunction escalateCartError(detail: CartUpdateErredEvent['detail']) {\n  // These are expected outcomes with their own user facing messaging rather than programmer errors.\n  //\n  // This list of codes to ignore deliberately excludes invalid selling plan codes such as\n  // ADD_ITEMS_INVALID_SELLING_PLAN, ADD_ITEMS_INVALID_BACKORDER_SELLING_PLAN, and\n  // ADD_ITEMS_INVALID_TBYB_SELLING_PLAN. Unlike the ephemeral shopper conditions, a rejected\n  // selling plan is a plan we attached to the item, so it signals that something is misconfigured\n  // rather than a transient outcome. We want these to continue reaching error diagnostics so that\n  // misconfiguration is visible.\n\n  const ignoredCodes = [\n    'APPLY_DISCOUNTS',\n    'ADD_ITEMS_OOS',\n    'ADD_ITEMS_BUNDLE_ADDON_MIX',\n    'ADD_ITEMS_MAX_QUANTITY',\n    'CHANGE_CART_OOS',\n    'UPDATE_CART_OOS'\n  ];\n\n  if (detail && !ignoredCodes.includes(detail.code)) {\n    let error: any;\n\n    if (detail.error instanceof Error) {\n      error = detail.error;\n    } else {\n      error = new Error(`${detail.error}`);\n    }\n\n    decorateError(error, 'code', detail.code);\n    decorateError(error, 'inputs', detail.inputs);\n    decorateError(error, 'scenario', detail.scenario);\n    decorateError(error, 'cart', detail.cart);\n    decorateError(error, 'transaction_id', detail.transaction?.id);\n    decorateError(error, 'transaction_name', detail.transaction?.name);\n\n    if (typeof reportError === 'function') {\n      reportError(error);\n    } else {\n      setTimeout(raise, 0, error);\n    }\n  }\n}\n\nfunction raise(value: unknown) {\n  throw value;\n}\n","import { measure, now } from '../perf.js';\n\ntype CartTransactionStartedEvent = CustomEvent<Transaction>;\ntype CartTransactionCompletedEvent = CustomEvent<Transaction>;\n\n/**\n * The bookkeeping shared by every copy of this module. Each entry bundles its own copy, so the\n * numbers and the timeline have to sit on the window for the copies to agree on them.\n */\ninterface State {\n  /** The number of currently active cart service transactions. */\n  activeCount: number;\n\n  /** Number of cart operations started so far, and the source of transaction ids. */\n  count: number;\n\n  /** A rolling log of recent transaction start and end entries. */\n  timeline: Record<string, any>[];\n}\n\ndeclare global {\n  var cart_transaction: State;\n\n  interface WindowEventMap {\n    'cart-transaction-completed': CartTransactionCompletedEvent;\n    'cart-transaction-started': CartTransactionStartedEvent;\n  }\n}\n\n/**\n * Obtain the shared state, initializing it as needed.\n *\n * We do not assume the global state is reliably present and instead access state via this function\n * every time because we have seen very surprising errors in the wild to which we wish to remain\n * resilient, even at the cost of generating artificial and inaccurate state.\n */\nfunction getSharedState() {\n  window.cart_transaction = window.cart_transaction || { activeCount: 0, count: 0, timeline: [] };\n\n  const shared = window.cart_transaction;\n\n  if (!Number.isInteger(shared.activeCount) || shared.activeCount < 0) {\n    shared.activeCount = 0;\n  }\n\n  if (!Number.isInteger(shared.count)) {\n    shared.count = 0;\n  }\n\n  if (!Array.isArray(shared.timeline)) {\n    shared.timeline = [];\n  }\n\n  return shared;\n}\n\n/**\n * A transaction represents an action carried out by the cart service. For example, an add to cart\n * transaction or a remove from cart transaction.\n *\n * A transaction is a plain record minted by the start function and completed through the handle it\n * carries. The bookkeeping fields live on the record because completion is a module function that\n * operates on the record rather than a method that closes over it.\n */\nexport interface Transaction {\n  /** The arguments to the start of the transaction */\n  args: any[];\n\n  /** Complete this transaction. Idempotent, and reachable only through the handle. */\n  complete?(didTimeOut?: boolean): void;\n\n  /** Whether completion has already run, so a second call is a no-op */\n  completed: boolean;\n\n  /** The transaction start time in milliseconds */\n  epoch: number;\n\n  /** Transaction unique global id */\n  id: number;\n\n  /** Transaction name, e.g. function name */\n  name: string;\n\n  /**\n   * The transaction start time on the monotonic clock, used only to compute duration.\n   *\n   * This is separate from epoch, which stays wall-clock so it still answers when the transaction\n   * happened. The two clocks have different origins, so never mix them in one subtraction.\n   */\n  startMillis: number;\n\n  /** Timeout timer id */\n  timer?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Start a cart service transaction and return its handle. Use the handle's complete method to end\n * the transaction.\n */\nexport function start(name: string, ...args: any[]) {\n  const shared = getSharedState();\n\n  // Increment the shared transaction id. A simple monotonic counter ensures that no two\n  // transactions have the same id (but see the caveats mentioned in getting shared state).\n\n  shared.count++;\n\n  const transaction: Transaction = {\n    args,\n    epoch: Date.now(),\n    id: shared.count,\n    name,\n    completed: false,\n    startMillis: now()\n  };\n\n  // Expose complete as a \"method\" of the returned object. Callers are expected to complete the\n  // transaction via its own method. This is better than exporting the top level complete function\n  // as a free function where callers could mistakenly complete the wrong transaction.\n\n  transaction.complete = (didTimeOut = false) => complete(transaction, didTimeOut);\n\n  // Increment the number of active transactions. This should be a transition from 0 to 1.\n\n  shared.activeCount++;\n\n  // If as a result of the increment there is now more than one active transaction, report the\n  // problem but continue. The goal is not to error. Many transactions are able to reconcile from\n  // overlapping flights.\n\n  if (shared.activeCount > 1) {\n    reportConcurrency(transaction);\n  }\n\n  // Record the transaction in the active transactions timeline. This is a fresh object instead of\n  // the original transaction so that it snapshots.\n\n  append({ epoch: transaction.epoch, id: transaction.id, name: transaction.name, type: 'start' });\n\n  // Start a timer that ensures the transaction settles eventually. Ensuring this is important\n  // because the upper layers typically lock down the UI during a transaction, and we want to\n  // prevent locking the UI indefinitely.\n\n  transaction.timer = setTimeout(complete, 30 * 1000, transaction, true);\n\n  console.log('Cart transaction started', transaction.name);\n\n  type Detail = CartTransactionStartedEvent['detail'];\n  const event = new CustomEvent<Detail>('cart-transaction-started', { detail: transaction });\n  dispatchEvent(event);\n\n  return transaction;\n}\n\n/**\n * Complete a transaction.\n *\n * Appends the transaction to the transaction history log.\n *\n * Emits a transaction completed event. Various transactions should complete when done to signal the\n * completion to listeners.\n *\n * This runs either manually whenever the transaction completes, or automatically, if the\n * transaction did not complete quickly enough (e.g. because of an error, omission, or a genuinely\n * long operation that timed out).\n *\n * Transactions should be short-lived. Transactions are automatically considered complete after a\n * certain amount of time so as to guarantee completion. Higher layer interfaces rely on transaction\n * state to lock interfaces for periods of time. Locking must be definite.\n *\n * Completion is idempotent. The timeout safety net and the caller's own completion can both fire\n * for one transaction when a tab is suspended long enough for the operation to outlive the timeout,\n * as happens on a backgrounded mobile tab that resumes. Whichever fires first records the outcome\n * and any later call is a no-op, so the active transaction count decrements exactly once per start\n * and cannot drift negative and defeat the interface locks that read it.\n */\nfunction complete(transaction: Transaction, didTimeOut = false) {\n  // A transaction that already timed out lands here when its operation finally finishes, so we log\n  // how long it really took. That tells a stalled request that eventually came back apart from one\n  // that never did.\n\n  if (transaction.completed) {\n    const duration = Math.round(now() - transaction.startMillis);\n    console.log('Cart transaction already completed', transaction.name, didTimeOut, duration);\n    return;\n  }\n\n  transaction.completed = true;\n\n  clearTimeout(transaction.timer);\n  transaction.timer = undefined;\n\n  append({\n    epoch: Date.now(),\n    id: transaction.id,\n    name: transaction.name,\n    timeout: didTimeOut,\n    type: 'end'\n  });\n\n  const shared = getSharedState();\n\n  if (shared.activeCount > 0) {\n    shared.activeCount--;\n  }\n\n  if (didTimeOut) {\n    console.log('Cart transaction timed out', transaction.name);\n  }\n\n  console.log('Cart transaction completed', transaction.name);\n\n  // Capture performance data representing each transaction's duration.\n\n  measure(`cart-${transaction.name}-${transaction.id}`, transaction.startMillis, now(), {\n    id: transaction.id,\n    name: transaction.name,\n    timeout: didTimeOut\n  });\n\n  type Detail = CartTransactionCompletedEvent['detail'];\n  const event = new CustomEvent<Detail>('cart-transaction-completed', { detail: transaction });\n  dispatchEvent(event);\n}\n\n/**\n * Capture a concurrent transaction in way that observers can see it\n */\nfunction reportConcurrency(transaction: Transaction) {\n  const state = getSharedState();\n\n  // Snapshot the state. The reason for this is because the state that is rendered in the devtools\n  // console is the state of the live reference when the value is viewed in the log, which is after\n  // the time it was recorded. Snapshotting saves the value at the time of recording it.\n  //\n  // This is a shallow snapshot. Since the timeline is append only, we are basically just recording\n  // the timeline up to the time of the snapshot, e.g. basically this is just a partial view of an\n  // array that may have more entries by the time it is expanded but those entries are hidden.\n\n  const snapshot = [...state.timeline];\n\n  console.warn('Concurrent transaction started', {\n    id: transaction.id,\n    name: transaction.name,\n    timeline: snapshot\n  });\n}\n\n/**\n * Add a timeline entry.\n *\n * The type of entry here is intentionally general because this has a relaxed opinion on what gets\n * stored.\n */\nfunction append(entry: Record<string, any>) {\n  const state = getSharedState();\n  const timeline = state.timeline;\n\n  timeline.push(entry);\n\n  const capacity = 50;\n\n  // Keep only the most recent entries so the timeline does not grow without bound over a long\n  // session. Removing from the front drops the oldest; the clamp guards against a negative removal\n  // count while the timeline is still under the cap.\n  // TODO: the clamp may be unnecessary, since splice already treats a negative delete count as\n  // zero, so it is likely defensive rather than required. Verify and drop it if so.\n  timeline.splice(0, Math.max(timeline.length - capacity, 0));\n}\n","import { assert } from '@integrabeauty/assert';\nimport { type AddItem, isUnavailableError, cartAdd, type Cart, isInvalidSellingPlanError, isMaxQuantityError, isBundleAddonMixError } from '@integrabeauty/shopify-ajax-api';\nimport { copy } from '../object.js';\nimport { now } from '../perf.js';\nimport { isWellFormed } from './discount/discount.js';\nimport { applyDiscountsHelper } from './discount/helper.js';\nimport { report } from './error.js';\nimport { cartFetch, read, write } from './state.js';\nimport { start, type Transaction } from './transaction.js';\n\n/**\n * Adds items to the cart. Emits an event when completed.\n *\n * This fails if the request to add fails, or if the request to get the updated state fails.\n *\n * This must perform multiple http requests due to limitations in Shopify's ajax cart api. The first\n * fetch mutates, and the second fetch gets the updated state. Unfortunately, this means that it is\n * possible for the mutation to succeed but for the subsequent fetch to fail. Callers should be wary\n * of this situation. On some future successful retrieval of server side state, the added items will\n * appear, despite this function emitting an error event. This means, for example, that callers\n * should not assume that failure to add is necessarily a real failure.\n *\n * If a discount is specified to be applied on add, it is applied as a part of the same transaction.\n * However, this also introduces some unwanted complexity. This function does not emit an error if\n * applying the discount fails, because this must preserve client side to server side state\n * fidelity, and the second fetch call happens after the first, and Shopify does not support multi-\n * request transactions (e.g. batched conditional requests). There is no simple way to rollback a\n * partial commit because the rollback itself could also fail as it would be an additional request.\n *\n * The new discount is not applied if it appears as existing. This avoids an unnecessary request.\n * The cart's discount_codes array automatically updates the applicable state of applied discount\n * codes as a result of adding new items. There is no need to reapply to trigger the transition from\n * inapplicable to applicable.\n *\n * At the moment, it is unclear whether this should be fetching cart state prior to applying new\n * discounts. Local cart state is assumed to be accurate, so the existing discount codes should be\n * preserved, so there should not be a need, so this does not perform the additional fetch.\n *\n * @param target the element most directly responsible for the add, used to originate any failure\n * event with truthful provenance instead of at global scope\n * @param items the items to add\n * @param discountCodes optional discount codes to attempt to apply to the cart after adding the\n * items\n * @param scenario optional description of the context of the add to cart, e.g. the type of ui\n * element or ux experience, that is used by downstream analytics\n */\nexport async function add(target: EventTarget | null, items: AddItem[], discountCodes?: string[],\n  scenario?: string) {\n  const transaction = start('addItems', items, scenario);\n\n  try {\n    await addItemsInternal(target, transaction, items, discountCodes, scenario);\n  } catch (error) {\n    report(target, {\n      code: determineErrorCode(items, error),\n      error,\n      inputs: {\n        discount_code: discountCodes,\n        items\n      },\n      scenario,\n      transaction\n    });\n  } finally {\n    transaction.complete();\n  }\n}\n\nasync function addItemsInternal(target: EventTarget | null, transaction: Transaction,\n  items: AddItem[], discountCodes: string[], scenario: string) {\n  assert(items.length > 0, 'At least one item must be specified to add');\n\n  const previous = read();\n\n  const enrichedItems = items.map(enrich);\n\n  // An add can outlast the transaction timeout, and then nothing says which of its requests\n  // stalled. So we log each step with how long it took. When a later cart error is reported, the\n  // steps missing from its breadcrumbs point at the request that never came back.\n\n  let stepStart = now();\n  await cartAdd(enrichedItems);\n  console.log('Cart add request finished', Math.round(now() - stepStart));\n\n  // We ignore the output of cart/add.js for several reasons. One reason is that  we need the full\n  // cart state, but add.js output only contains an items property. We cannot safely merge the new\n  // items with the local state because multiple changes can happen to a cart in multiple tabs, and\n  // because it is not possible to compare new line items to old line items by line item key because\n  // we have observed line item key shifting, line item splitting, and other surprises. Another\n  // reason is that the response body includes zombie items (0 quantity items) from failed adds.\n\n  // If applying a discount code on add, then we will use the output of that call as the new cart\n  // state. If applying the discount fails, we still treat the add to cart as a success, and\n  // fallback to fetching the new cart state. We treat failure to apply the discount in this case as\n  // non-fatal to add to cart because we have effectively already updated cart state and must\n  // reflect that state change even though the discount failed to apply.\n\n  stepStart = now();\n  let cart = await applyDiscountsOnAdd(previous, discountCodes || [], scenario);\n  console.log('Cart add discount step finished', Math.round(now() - stepStart));\n\n  if (!cart) {\n    stepStart = now();\n    cart = await cartFetch();\n    console.log('Cart add fetch finished', Math.round(now() - stepStart));\n  }\n\n  write(target, cart, transaction, scenario);\n}\n\n/**\n * Try to apply some discount codes to the cart.\n *\n * Returns null when there was no mutation.\n */\nasync function applyDiscountsOnAdd(beforeCart: Cart, discountCodes: string[], scenario?: string) {\n  // We tolerate pointless calls so as to reduce caller boilerplate\n\n  if (discountCodes.length < 1) {\n    return null;\n  }\n\n  // If any input discount code is malformed, do nothing. This does not throw.\n\n  for (const code of discountCodes) {\n    if (!isWellFormed(code)) {\n      const error: any = new Error('Malformed discount code');\n      error.scenario = scenario;\n      error.discount_codes = discountCodes;\n      error.discount_code = code;\n      console.warn(error);\n      return null;\n    }\n  }\n\n  // This is currently using local state. There are two modes for local state. There is the initial\n  // server side render, and there is the state after init. The states are subtly different. On\n  // initial server side render the cart is missing the discount_codes array because that is not\n  // accessible to liquid. The cart init logic was changed to do a fetch of cart state on every page\n  // load just to get the discount_codes array. Sometimes that fetch fails, leaving the server side\n  // rendered state as is, in which case the discount_codes property is undefined. This must account\n  // for that. In doing so, opt out of apply on add entirely, as any apply discounts call must\n  // specify all codes to apply, including existing codes. As we do not know what the previous codes\n  // are when init partially failed, it is better to just skip applying than to unintentionally\n  // cause existing codes to drop.\n\n  if (!Array.isArray(beforeCart.discount_codes)) {\n    return null;\n  }\n\n  // As a part of this function's contract, it returns null to signal that a new cart state was not\n  // obtained. Therefore, as tempting as it might be, do not init afterCart to beforeCart.\n\n  let afterCart: Cart = null;\n\n  const overrides = cart_lifecycle.discount_overrides;\n\n  // Try to apply. Post validation is disabled because this is a form of best effort automatic\n  // application where errors are routine and not fatal.\n\n  try {\n    afterCart = await applyDiscountsHelper(beforeCart, discountCodes, overrides, false);\n  } catch (error) {\n    console.warn(error);\n    // continue\n  }\n\n  return afterCart;\n}\n\n/**\n * Classifies a caught add-to-cart failure into a cart error code.\n *\n * Shopify rejects an invalid selling plan with the same generic description regardless of what the\n * plan was for, so the specific code is disambiguated from the items we sent rather than from the\n * error itself. At render time each line item is tagged with a type-specific property: a backorder\n * message for pre-order plans and a marker for try-before-you-buy plans. A selling-plan rejection\n * carrying neither marker keeps the generic code, because nothing on the item positively identifies\n * it as anything more specific.\n */\nfunction determineErrorCode(items: AddItem[], error: any) {\n  if (isUnavailableError(error)) {\n    return 'ADD_ITEMS_OOS';\n  }\n\n  if (isBundleAddonMixError(error)) {\n    return 'ADD_ITEMS_BUNDLE_ADDON_MIX';\n  }\n\n  if (isMaxQuantityError(error)) {\n    return 'ADD_ITEMS_MAX_QUANTITY';\n  }\n\n  if (isInvalidSellingPlanError(error)) {\n    let item: AddItem;\n    if (Array.isArray(items)) {\n      item = items.find(line => line?.selling_plan);\n    }\n\n    if (item?.properties?._backorder_message) {\n      return 'ADD_ITEMS_INVALID_BACKORDER_SELLING_PLAN';\n    }\n\n    if (item?.properties?._is_tbyb) {\n      return 'ADD_ITEMS_INVALID_TBYB_SELLING_PLAN';\n    }\n\n    return 'ADD_ITEMS_INVALID_SELLING_PLAN';\n  }\n\n  return 'ADD_ITEMS';\n}\n\nfunction enrich(input: AddItem) {\n  // Clone so that the enrichment below does not mutate the caller's input.\n  const output = copy(input);\n  output.properties = output.properties || {};\n  output.properties._url_path = output.properties._url_path || location.pathname;\n  return output;\n}\n","// Accessors for the ambient cart event queue seeded and filled by the always-on cart lifecycle\n// snippet. The snippet registers a parse-time listener that buffers every cart event into the\n// queue, so a section whose bundle loads later can replay what it missed. Reaching the queue\n// through this lib, rather than the global directly, keeps the coupling to the ambient layer\n// explicit. The buffered events come off the queue untyped, so the guards here narrow each one\n// during replay.\n\ntype CartUpdatedEvent = GlobalEventHandlersEventMap['cart-updated'];\ntype CartUpdateErredEvent = GlobalEventHandlersEventMap['cart-update-erred'];\n\n/**\n * Returns the ambient queue of buffered cart events. A consumer replays this as it initializes,\n * then listens live for further events.\n */\nexport function getEventQueue() {\n  return cart_lifecycle.queue;\n}\n\n/**\n * Whether the value is a cart updated event. A consumer replaying the queue narrows each buffered\n * event with this before handling it.\n */\nexport function isCartUpdatedEvent(value: any): value is CartUpdatedEvent {\n  return value instanceof Event && value.type === 'cart-updated';\n}\n\n/**\n * Whether the value is a cart update erred event.\n */\nexport function isCartUpdateErredEvent(value: any): value is CartUpdateErredEvent {\n  return value instanceof Event && value.type === 'cart-update-erred';\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport type { AddItem } from '@integrabeauty/shopify-ajax-api';\nimport { add } from '../../../lib/cart/add.js';\nimport { getEventQueue, isCartUpdatedEvent } from '../../../lib/cart-event-queue.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * The button that actually adds items to the cart. It reads the variants to add from its variants\n * slot and adds them when clicked.\n *\n * This is the lower level button created by the add to cart element, but it is also reused directly\n * in places like product cards, the upsell modal, and the inline cart upsell, with a styling\n * variant for each.\n *\n * This is disabled when a cart transaction is in flight.\n *\n * After a successful add it requests the side cart to open, unless that is turned off.\n */\nclass AddToCartButton extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return ['data-update'];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Overrides the button label. When empty the label falls back to a default that depends on\n     * whether the items being added carry a selling plan.\n     */\n    buttonText: string;\n\n    /**\n     * Country ISO code, relayed from the parent add to cart element. Empty unless a country needs\n     * to be specified for the add to cart request.\n     */\n    country: string;\n\n    /**\n     * set to true to prevent side cart open on storefront call complete\n     */\n    disableSideCartAction: 'false' | 'true';\n\n    /**\n     * If specified, will attempt to apply discount code after variants are added.\n     */\n    discountCode: string;\n\n    /**\n     * A boolean flag signaling whether the current product is a free promo template. This is used\n     * internally to signal the need for additional parsing of the attached free promo variants and\n     * appending the <variant-element> tags to the <add-to-bag-button> to be added to cart on click.\n     */\n    freePromoTemplate: 'false' | 'true';\n\n    /**\n     * Boolean flag indicating the button should render to the full width of its parent container.\n     */\n    full: 'false' | 'true';\n\n    /**\n     * Set this attribute to a value different than its previous value to cause this element to re-\n     * render itself. Typically we use the current epoch as the value (e.g. `Date.now()`).\n     */\n    update: string;\n\n    /**\n     * Boolean flag represented as string to display the button styled for the inline cart upsell\n     */\n    upsellCartVariant: 'false' | 'true';\n\n    /**\n     * Boolean flag represented as string to display the button styled for the upsell modal\n     */\n    upsellModalVariant: 'false' | 'true';\n\n    /**\n     * Variant title\n     */\n    variantTitle: string;\n  };\n\n  private cartInitSucceeded = false;\n  private onClickBound = this.onClick.bind(this);\n  private onCartTransactionStartedBound = this.onCartTransactionStarted.bind(this);\n  private onCartTransactionEndedBound = this.onCartTransactionEnded.bind(this);\n  private onCartUpdatedBound = this.onCartUpdated.bind(this);\n  private onSlotChangeBound = this.onSlotChange.bind(this);\n  private declare items?: AddItem[];\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const variant = this.shadowRoot.querySelector('slot[name=\"variants\"]');\n    variant.addEventListener('slotchange', this.onSlotChangeBound);\n\n    this.items = this.populateItems();\n\n    const button = this.shadowRoot.querySelector('button');\n    button.addEventListener('click', this.onClickBound);\n    button.setAttribute('aria-label', `Add ${this.dataset.variantTitle} to cart`);\n\n    this.updateButtonText();\n\n    for (const event of getEventQueue()) {\n      if (isCartUpdatedEvent(event)) {\n        try {\n          this.onCartUpdatedBound(event);\n        } catch (error) {\n          console.warn(error);\n        }\n      }\n    }\n\n    addEventListener('cart-updated', this.onCartUpdatedBound);\n    addEventListener('cart-transaction-started', this.onCartTransactionStartedBound);\n    addEventListener('cart-transaction-completed', this.onCartTransactionEndedBound);\n  }\n\n  public disconnectedCallback() {\n    const variant = this.shadowRoot.querySelector('slot[name=\"variants\"]');\n    variant?.removeEventListener('slotchange', this.onSlotChangeBound);\n\n    const button = this.shadowRoot.querySelector('button');\n    button?.removeEventListener('click', this.onClickBound);\n\n    removeEventListener('cart-updated', this.onCartUpdatedBound);\n    removeEventListener('cart-transaction-started', this.onCartTransactionStartedBound);\n    removeEventListener('cart-transaction-completed', this.onCartTransactionEndedBound);\n  }\n\n  public attributeChangedCallback(name: string, _oldValue: string, _newValue: string) {\n    this.initializeShadowRoot();\n    if (this.isConnected && name === 'data-update') {\n      this.items = this.populateItems();\n      this.updateButtonText();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private onSlotChange(_event: Event) {\n    // this event is called when the slot is changed, but also when the element is destroyed this\n    // causes race conditions in exotic browsers, i.e. neither the slot contents, nor the shadowRoot\n    // contents are guaranteed to exist here\n\n    // only populate items and render if we have the slot properly filled\n    const variant = this.querySelector('[slot=\"variants\"]');\n    if (variant) {\n      this.items = this.populateItems();\n      this.updateButtonText();\n    }\n  }\n\n  private renderDisabledState() {\n    const button = this.shadowRoot.querySelector('button');\n    button.setAttribute('aria-label', `Add ${this.dataset.variantTitle} to cart`);\n    button.disabled = true;\n    button.classList.remove('loading');\n  }\n\n  private renderEnabledState() {\n    const button = this.shadowRoot.querySelector('button');\n    button.setAttribute('aria-label', `Add ${this.dataset.variantTitle} to cart`);\n    button.disabled = false;\n    button.classList.remove('loading');\n  }\n\n  private renderLoadingState() {\n    this.shadowRoot.querySelector('button').classList.add('loading');\n    this.shadowRoot.querySelector<HTMLElement>('.title').hidden = true;\n    const loadingEl = this.dataset.upsellModalVariant === 'true' ?\n      this.shadowRoot.querySelector<HTMLElement>('.loading-upsell-modal-variant') :\n      this.shadowRoot.querySelector('loading-indicator');\n\n    loadingEl.hidden = false;\n  }\n\n  private renderLoadedState() {\n    const button = this.shadowRoot.querySelector('button');\n    button.setAttribute('aria-label', `Add ${this.dataset.variantTitle} to cart`);\n    button.disabled = true;\n    button.classList.add('loaded');\n    this.dataset.buttonText = 'Added To Bag!';\n    this.dataset.update = Date.now().toString();\n  }\n\n  private onCartUpdated(event: GlobalEventHandlersEventMap['cart-updated']) {\n    if (event.detail.is_initial && !this.cartInitSucceeded) {\n      this.cartInitSucceeded = true;\n      this.renderEnabledState();\n    }\n  }\n\n  private onCartTransactionStarted(_event: WindowEventMap['cart-transaction-started']) {\n    this.renderDisabledState();\n  }\n\n  private onCartTransactionEnded(_event: WindowEventMap['cart-transaction-completed']) {\n    if (this.cartInitSucceeded) {\n      const button = this.shadowRoot.querySelector('button');\n      if (button.classList.contains('loaded')) {\n        this.renderLoadedState();\n      } else {\n        this.renderEnabledState();\n      }\n    }\n\n    // make sure the button is not in a loading state\n    const title = this.shadowRoot.querySelector<HTMLElement>('.title');\n    if (title) {\n      title.hidden = false;\n    }\n\n    const loadingEl = this.dataset.upsellModalVariant === 'true' ?\n      this.shadowRoot.querySelector<HTMLElement>('.loading-upsell-modal-variant') :\n      this.shadowRoot.querySelector('loading-indicator');\n    if (loadingEl) {\n      loadingEl.hidden = true;\n    }\n  }\n\n  /**\n   * This function could be designed to load the addItems function lazily. Previously, when dealing\n   * with some large webpack chunks, the function was optimized to do so, as this kept the size of\n   * the chunk containing this element definition small. However, the problem with that approach is\n   * that it determines whether an add to cart succeeds based on whether the additional webpack\n   * chunk load request to load the add to cart function js works. It turns out that webpack chunks\n   * routinely fail to load in certain cases. Therefore we must be judicious about which\n   * functionality we decide to load lazily. I have decided now that add to cart is a critical user\n   * action that should not fail because of a webpack chunk load error. Therefore, we statically\n   * import the addItems function into this module instead of dynamically importing the function\n   * here. The tradeoff is that the chunk is now larger, so whatever code imports the add to cart\n   * button module itself needs to be aware of its larger size. At the moment, this is actually\n   * fine, because it turns out that this element is loaded as its own chunk and is a relatively\n   * tiny module.\n   */\n  private onClick(event: MouseEvent) {\n    console.debug('Add to cart clicked');\n\n    const button = event.currentTarget;\n    if (!(button instanceof HTMLButtonElement)) {\n      return;\n    }\n\n    if (button.disabled) {\n      return;\n    }\n\n    if (!Array.isArray(this.items) || this.items.length === 0) {\n      throw new Error('add to cart bundle clicked but lines prop is not initialized or empty');\n    }\n\n    this.renderLoadingState();\n\n    const discountCodes = [];\n    if (this.dataset.discountCode && this.dataset.discountCode !== 'undefined' &&\n      this.dataset.discountCode !== 'null') {\n      const discountCode = this.dataset.discountCode.trim();\n      if (discountCode) {\n        discountCodes.push(discountCode);\n      }\n    }\n\n    add(this, this.items, discountCodes, 'button').catch(console.warn);\n\n    if (this.dataset.disableSideCartAction === 'true') {\n      return;\n    }\n\n    const onCartTransactionCompleted = (_event: WindowEventMap['cart-transaction-completed']) => {\n      removeEventListener('cart-transaction-completed', onCartTransactionCompleted);\n\n      // Show the cart when the call ends. This should happen on every page except for the cart\n      // page, which has no side cart.\n\n      if (this.dataset.upsellModalVariant === 'true') {\n        this.renderEnabledState();\n      } else {\n        // This button is about to open the cart drawer, and keyboard focus should return here once\n        // the drawer closes. We do not track that ourselves. We hand this button to the modal\n        // manager's focus stack, which is responsible for restoring focus to it when the drawer\n        // later closes.\n        const focusEvent = new Event('focus-stack-push-requested', {\n          bubbles: true,\n          composed: true\n        });\n        button.dispatchEvent(focusEvent);\n\n        type Detail = WindowEventMap['cart-open-requested']['detail'];\n        const cartOpenEvent = new CustomEvent<Detail>('cart-open-requested', {\n          bubbles: true,\n          composed: true,\n          detail: { source: 'add-to-cart-button' }\n        });\n        this.dispatchEvent(cartOpenEvent);\n      }\n    };\n\n    addEventListener('cart-transaction-completed', onCartTransactionCompleted);\n  }\n\n  private populateItems() {\n    const items: AddItem[] = [];\n\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n    const variants = this.querySelectorAll<VariantElement>('[slot=\"variants\"]');\n    let parentId;\n    if (this.dataset.freePromoTemplate === 'true') {\n      const parentEl = Array.from(variants).find(variant => !variant.dataset.isPromo);\n      if (parentEl) {\n        parentId = parentEl.dataset.id;\n      }\n    }\n\n    for (const variantElement of variants) {\n      if (!variantElement.dataset.id) {\n        continue;\n      }\n\n      const item: AddItem = {\n        id: variantElement.dataset.id,\n        properties: {},\n        quantity: parseInt(variantElement.dataset.quantity, 10)\n      };\n\n      const attributes = variantElement.querySelectorAll('custom-attribute');\n      for (const attribute of attributes) {\n        if (attribute.dataset.key === 'selling_plan') {\n          item.selling_plan = attribute.dataset.value;\n        } else {\n          item.properties[attribute.dataset.key] = attribute.dataset.value;\n        }\n      }\n\n      // if the product has selling plans we need to add it to the item properties, so that we can\n      // render the selling plan options select input in the cart line item\n      const scriptTag = this.querySelector<HTMLScriptElement>('[slot=\"subscription-plans\"]');\n      if (scriptTag) {\n        item.properties._selling_plan_options = scriptTag.textContent;\n      }\n\n      if (parentId && variantElement.dataset.id !== parentId) {\n        item.parent_id = parentId;\n      }\n\n      items.push(item);\n    }\n\n    return items;\n  }\n\n  private updateButtonText() {\n    const buttonTitle = this.shadowRoot.querySelector('.title');\n    if (this.dataset.buttonText) {\n      buttonTitle.textContent = this.dataset.buttonText;\n    } else if (this.items.some(line => line.selling_plan)) {\n      buttonTitle.textContent = 'Get it Now!';\n    } else {\n      buttonTitle.textContent = 'Add To Bag';\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'add-to-cart-button': AddToCartButton;\n  }\n}\n\nif (!customElements.get('add-to-cart-button')) {\n  customElements.define('add-to-cart-button', AddToCartButton);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * This is a lower level element that renders an notify me button to register for an out of stock\n * notification to a particular variant id.\n *\n * This uses the Back In Stock app installed on Shopify.\n *\n * @deprecated this element should not have been implemented, add support for out of stock feature\n * to the other button element and then remove this element\n */\nclass AddToCartNotifyMeButton extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Customer email.\n     */\n    email: string;\n\n    /**\n     * Whether to stretch the button to fill the available width. The parent element owns this and\n     * mirrors it down from its own state, and it may change at runtime as the viewport changes, so\n     * the button can switch between a fixed width and full width during its lifetime. In the fixed\n     * width layout the host is wider than the button, which the stylesheet relies on to keep only\n     * the button clickable rather than the bare space beside it.\n     */\n    full: string;\n\n    /**\n     * The ID of the product.\n     */\n    productId: string;\n\n    /**\n     * The title of the product.\n     */\n    productTitle: string;\n\n    /**\n     * The URL of the product page.\n     */\n    productUrl: string;\n\n    /**\n     * The sku of the product.\n     */\n    sku: string;\n\n    /**\n     * The ID of variant.\n     */\n    variantId: string;\n\n    /**\n     * The title of the variant.\n     */\n    variantTitle: string;\n  };\n\n  private onClickBound = this.onClick.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    let title = this.dataset.productTitle;\n    if (this.dataset.variantTitle !== 'Default Title') {\n      title += ` (${this.dataset.variantTitle})`;\n    }\n\n    const button = this.shadowRoot.querySelector('button');\n    button.setAttribute('aria-label', `The ${title} is out of stock, click to be ` +\n    'notified by email when available again.');\n\n    // Activate on the host rather than the inner button so the whole element is the click target.\n    // The host can be wider than the button in the fixed width layout, so the stylesheet makes the\n    // bare area around the button transparent to pointer events. Clicks on the button still bubble\n    // up to this listener, while clicks on that bare area fall through and never reach here.\n\n    this.addEventListener('click', this.onClickBound);\n  }\n\n  public disconnectedCallback() {\n    this.removeEventListener('click', this.onClickBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      // This host carries no tabindex or role of its own and is not focusable on its own. The\n      // native button in its shadow tree is the focusable element. delegatesFocus routes a focus\n      // call on the host to that inner button, which is what lets focus land back here when the\n      // modal manager restores focus to this button after the notification form closes.\n\n      this.attachShadow({ mode: 'open', delegatesFocus: true });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  /**\n   * Handles activation of the button. It first asks the modal manager to remember this element so\n   * that focus returns here once the notification form closes, then announces the click so the form\n   * can open for this product and variant. Both events are dispatched from the host, which is bound\n   * as the listener target and delegates focus to its inner button, so the focus restore lands on\n   * that button.\n   */\n  private onClick(_event: MouseEvent) {\n    const focusEvent = new Event('focus-stack-push-requested', {\n      bubbles: true,\n      composed: true\n    });\n    this.dispatchEvent(focusEvent);\n\n    type Detail = NotifyMeClickedEvent['detail'];\n    const notifyEvent = new CustomEvent<Detail>('notify-me-clicked', {\n      cancelable: false,\n      bubbles: true,\n      composed: true,\n      detail: {\n        email: this.dataset.email,\n        productId: parseInt(this.dataset.productId, 10),\n        productTitle: this.dataset.productTitle,\n        productUrl: this.dataset.productUrl,\n        sku: this.dataset.sku,\n        variantId: parseInt(this.dataset.variantId, 10),\n        variantTitle: this.dataset.variantTitle\n      }\n    });\n\n    this.dispatchEvent(notifyEvent);\n  }\n}\n\n/**\n * This is fired when the \"Email when available\" link is clicked.\n *\n * This event is used as a signal to open the modal that allows the user to sign up for an email\n * notification when the product becomes available.\n */\ntype NotifyMeClickedEvent = CustomEvent<{\n  /**\n   * The user email\n   */\n  email: string;\n\n  /**\n   * The ID of the product\n   */\n  productId: number;\n\n  /**\n   * The title of the product\n   */\n  productTitle: string;\n\n  /**\n   * The URL of the product page\n   */\n  productUrl: string;\n\n  /**\n   * The sku of the product\n   */\n  sku: string;\n\n  /**\n   * The ID of variant\n   */\n  variantId: number;\n\n  /**\n   * The title of the variant\n   */\n  variantTitle: string;\n}>;\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'notify-me-clicked': NotifyMeClickedEvent;\n  }\n\n  interface HTMLElementTagNameMap {\n    // eslint-disable-next-line @typescript-eslint/no-deprecated\n    'add-to-cart-notify-me-button': AddToCartNotifyMeButton;\n  }\n}\n\nif (!customElements.get('add-to-cart-notify-me-button')) {\n  // eslint-disable-next-line @typescript-eslint/no-deprecated\n  customElements.define('add-to-cart-notify-me-button', AddToCartNotifyMeButton);\n}\n"],"names":["__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","e","Error","code","b","document","baseURI","self","location","href","add_to_cart","URL","AddToCart","HTMLElement","observedAttributes","onProductVariantOptionChangedBound","this","onProductVariantOptionChanged","bind","onReadyBound","onReady","onIntersectedBound","onIntersected","observer","connectedCallback","initializeShadowRoot","releaseReady","callback","type","readyState","timer","setTimeout","clearTimeout","addEventListener","removeEventListener","ready","disconnectedCallback","attributeChangedCallback","name","oldValue","newValue","isConnected","onUpdateAttributeChanged","onFullAttributeChanged","shadowRoot","attachShadow","mode","innerHTML","product","scrapeProductInfo","render","_this$shadowRoot$quer","_this$shadowRoot$quer2","_this$shadowRoot$quer3","_this$shadowRoot$quer4","querySelector","remove","buttons","querySelectorAll","buttonsFull","button","dataset","full","event","_this$product","detail","id","_this$shadowRoot$quer5","_this$shadowRoot$quer6","_this$shadowRoot$quer7","_this$shadowRoot$quer8","selectedVariantId","selected_variant_id","toString","renderWhenProductIsUnavailable","variant","element","productUrl","url","productId","productTitle","title","sku","variantId","variantTitle","createElement","customerEmail","email","stuck","appendChild","setAttribute","renderStickyBar","renderWhenProductIsAvailable","selectedVariant","variantDataEl","slot","quantity","attribute","custom_attributes","variantAttributeEl","key","value","sellingPlanId","sellingPlanEl","addToCartButton","buttonText","discountCode","upsellModalVariant","addonsEnabled","existingAddonVariants","existingAddonVariant","addonVariantElements","createAddonVariantElements","scrapeAddonVariantInfo","addonVariantElement","variantEl","replaceChild","update","Date","now","country","freePromoTemplate","promoVariants","scrapePromoVariantInfo","promoVariant","subscriptionPlansData","script","cloneNode","textContent","metafields_subtitle","image","source","main_image","image_src","src","width","height","normalizedSrc","startsWith","error","lastDotIndex","pathname","lastIndexOf","pathWithoutExtension","slice","extension","sizeParam","optimizedUrl","replace","resizeImageUrl","pillsContainer","options","option1","option2","option3","filter","option","length","pill","classList","add","observe","IntersectionObserver","threshold","buttonContainer","footer","entries","entry","target","isFooterInView","isIntersecting","isButtonInView","shouldStick","stick","unstick","style","minHeight","offsetHeight","bar","shouldRenderBackorderMessage","backorder_message","hideBackorderMessage","productListingDisplay","hide_backorder_in_collection","renderBackorderMessage","message","shouldAppend","backorderMessage","prepend","Array","isArray","variants","find","available","main","productElement","parseInt","mainImage","metafieldsSubtitle","tags","variantElement","backorder","hideBackorderInCollection","imageSrc","attributes","attributeElement","push","variantElements","addonVariants","isAddon","assert","expression","AssertionError","customElements","get","define","constructor","super","getRequestId","response","headers","isResponse","ok","status","text","json","decorateError","resolveUrl","path","protocol","host","async","cartFetch","Shopify","routes","root","responseBodyText","responseBodyObject","fetch","cache","consumeError","JSON","parse","isCart","isObject","items","copy","structuredClone","cloned","trustworthy","stringify","_performance","performance","console","debug","isWellFormed","includes","upper","toUpperCase","test","containsJavascriptWord","getApplicableDiscountCodes","cart","discount_codes","applicable","map","rnds8","Uint8Array","byteToHex","i","unsafeStringify","arr","offset","toLowerCase","dist_v4","buf","crypto","randomUUID","_ref","_options$random","rnds","random","rng","getRandomValues","RangeError","_v4","read","cart_lifecycle","state_cartFetch","limit","attempts","delays","lastError","delay","resolveAfterTimeout","cartFetchOnceWithTimeout","timeout","timeoutPromise","Promise","_resolve","reject","race","milliseconds","resolve","groupByVariant","output","item","variant_id","property","properties","merged_properties","image_url","price","product_id","product_title","product_type","variant_title","findVariantsAdded","c0map","c1map","increments","Object","keys","findVariantsRemoved","decrements","complement","a","applyDiscountsHelper","newCodes","overrides","postValidate","discount_code","validateCodesBeforeUpdate","oldUppers","newCodesCount","currentCodes","getDiscountCodes","getCurrentCodes","newUppers","retainedOldUppers","some","override","old_code","new_code","discount","Set","join","input","init","body","method","_error$response_body5","_error$response_body6","response_body","description","cartUpdate","updatedCart","updatedNormalCodes","newCode","upperNewCode","validateCodesAfterUpdate","decorate_error_decorateError","assignmentError","reason","log","tag","prototype","call","report","dispatcher","Element","window","CustomEvent","bubbles","composed","dispatchEvent","dispatchCartUpdateErred","ignoredCodes","_detail$transaction","_detail$transaction2","inputs","scenario","transaction","reportError","raise","escalateCartError","getSharedState","cart_transaction","activeCount","count","timeline","shared","Number","isInteger","start","_len","arguments","args","_key","epoch","completed","startMillis","didTimeOut","complete","snapshot","warn","reportConcurrency","append","duration","Math","round","end","_performance3","measure","splice","max","discountCodes","previous","enrichedItems","enrich","stepStart","_error$response_body","_error$response_body2","responseConsumeAndParseBodyError","cartAdd","beforeCart","afterCart","discount_overrides","applyDiscountsOnAdd","initial","oldVariants","newVariants","prevCodes","currCodes","discount_codes_added","discount_codes_removed","event_id","is_initial","variants_added","variants_removed","write","addItemsInternal","determineErrorCode","isUnavailableError","isBundleAddonMixError","isMaxQuantityError","isInvalidSellingPlanError","_item","_item2","line","selling_plan","_backorder_message","_is_tbyb","_url_path","isCartUpdatedEvent","Event","AddToCartButton","cartInitSucceeded","onClickBound","onClick","onCartTransactionStartedBound","onCartTransactionStarted","onCartTransactionEndedBound","onCartTransactionEnded","onCartUpdatedBound","onCartUpdated","onSlotChangeBound","onSlotChange","populateItems","updateButtonText","queue","_oldValue","_newValue","_event","renderDisabledState","disabled","renderEnabledState","renderLoadingState","hidden","renderLoadedState","contains","loadingEl","currentTarget","HTMLButtonElement","trim","catch","disableSideCartAction","onCartTransactionCompleted","focusEvent","cartOpenEvent","parentId","parentEl","from","isPromo","scriptTag","_selling_plan_options","parent_id","buttonTitle","AddToCartNotifyMeButton","delegatesFocus","notifyEvent","cancelable"],"sourceRoot":""}