@cboxdk/id-js 0.14.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -2
- package/dist/chunk-ASOQN47M.js +81 -0
- package/dist/chunk-ASOQN47M.js.map +1 -0
- package/dist/{chunk-UPRO73GX.js → chunk-G3CD7X4U.js} +334 -71
- package/dist/chunk-G3CD7X4U.js.map +1 -0
- package/dist/chunk-WYTGLCMF.js +154 -0
- package/dist/chunk-WYTGLCMF.js.map +1 -0
- package/dist/{client-CecDIu_N.d.cts → client-CEXddiCR.d.cts} +81 -136
- package/dist/{client-CecDIu_N.d.ts → client-CdwdigzG.d.ts} +81 -136
- package/dist/index.cjs +419 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -4
- package/dist/index.d.ts +39 -4
- package/dist/index.js +64 -25
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +531 -24
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +29 -8
- package/dist/nextjs.d.ts +29 -8
- package/dist/nextjs.js +41 -12
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +220 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +105 -0
- package/dist/server.d.ts +105 -0
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -0
- package/dist/types-BYZldock.d.cts +316 -0
- package/dist/types-BYZldock.d.ts +316 -0
- package/package.json +6 -1
- package/dist/chunk-UPRO73GX.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pkce.ts","../src/claims.ts","../src/vault.ts","../src/webhook.ts","../src/discovery.ts","../src/client.ts"],"names":["key"],"mappings":";;;;AAKA,SAAS,UAAU,KAAA,EAA2B;AAC5C,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,IAAA,CAAK,MAAM,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/E;AAGO,SAAS,WAAA,CAAY,QAAQ,EAAA,EAAY;AAC9C,EAAA,OAAO,UAAU,MAAA,CAAO,eAAA,CAAgB,IAAI,UAAA,CAAW,KAAK,CAAC,CAAC,CAAA;AAChE;AAGO,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,YAAY,EAAE,CAAA;AACvB;AAGA,eAAsB,UAAU,QAAA,EAAmC;AACjE,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,QAAQ,CAAC,CAAA;AACvF,EAAA,OAAO,SAAA,CAAU,IAAI,UAAA,CAAW,MAAM,CAAC,CAAA;AACzC;;;ACjBA,IAAM,kBAAA,uBAA8C,GAAA,CAAsB;AAAA,EACxE,OAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AA6CM,SAAS,aAAa,MAAA,EAAoD;AAC/E,EAAA,MAAM,MAAA,GAAS,SAAS,MAAM,CAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,OAAO,KAAK,CAAA;AAEvB,EAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,EAAA,KAAO,EAAA,EAAI;AACvC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,OAAO,UAAU,CAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,OAAO,UAAU,CAAA;AAE9B,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,MAAM,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,KAAK,IAAA,GAAO,IAAA;AAAA,IACvD,IAAA,EAAM,OAAO,IAAA,KAAS,QAAA,IAAY,mBAAmB,GAAA,CAAI,IAAI,IAAK,IAAA,GAA4B;AAAA,GAChG;AACF;AAMO,SAAS,MAAM,MAAA,EAAuC;AAC3D,EAAA,OAAO,WAAW,QAAA,CAAS,MAAM,CAAA,CAAE,KAAK,GAAG,CAAC,CAAA;AAC9C;AAWO,SAAS,iBAAiB,MAAA,EAA8B;AAC7D,EAAA,OAAO,KAAA,CAAM,MAAM,CAAA,KAAM,IAAA;AAC3B;AAUO,SAAS,UAAU,MAAA,EAAoC;AAC5D,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,MAAM,CAAA,CAAE,KAAK,CAAA;AAElC,EAAA,OAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,KAAK,GAAA,GAAM,IAAA;AACvD;AAGO,SAAS,MAAM,MAAA,EAA+B;AACnD,EAAA,OAAO,UAAA,CAAW,QAAA,CAAS,MAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAC7C;AAMO,SAAS,YAAY,MAAA,EAA+B;AACzD,EAAA,OAAO,UAAA,CAAW,QAAA,CAAS,MAAM,CAAA,CAAE,aAAa,CAAC,CAAA;AACnD;AAGO,SAAS,OAAA,CAAQ,QAAqB,IAAA,EAAuB;AAClE,EAAA,OAAO,KAAA,CAAM,MAAM,CAAA,CAAE,QAAA,CAAS,IAAI,CAAA;AACpC;AAOO,SAAS,aAAA,CAAc,QAAqB,UAAA,EAA6B;AAC9E,EAAA,OAAO,WAAA,CAAY,MAAM,CAAA,CAAE,QAAA,CAAS,UAAU,CAAA;AAChD;AAOA,IAAM,eAAA,GAAkB,CAAA;AAExB,SAAS,UAAA,CAAW,OAAgB,KAAA,EAAiC;AACnE,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACzC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACrD,IAAA,OAAO,EAAE,GAAA,EAAK,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AAAA,EAClC;AAEA,EAAA,MAAM,MAAA,GAAS,KAAA;AACf,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AAExB,EAAA,OAAO;AAAA,IACL,KAAK,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,KAAK,GAAA,GAAM,IAAA;AAAA,IACnD,KAAA,EAAO,KAAA,GAAQ,CAAA,GAAI,eAAA,GAAkB,UAAA,CAAW,OAAO,KAAK,CAAA,EAAG,KAAA,GAAQ,CAAC,CAAA,GAAI;AAAA,GAC9E;AACF;AAEA,SAAS,WAAW,KAAA,EAA0B;AAC5C,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GACtB,MAAM,MAAA,CAAO,CAAC,KAAA,KAA2B,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,EAAE,IAClF,EAAC;AACP;AAEA,SAAS,SAAS,MAAA,EAAwD;AACxE,EAAA,OAAO,MAAA,CAAO,MAAM,CAAA,GAAI,MAAA,CAAO,MAAA,GAAS,MAAA;AAC1C;AAOA,SAAS,OAAO,MAAA,EAAyC;AACvD,EAAA,OACE,OAAO,MAAA,CAAO,QAAQ,CAAA,KAAM,QAAA,IAC5B,MAAA,CAAO,QAAQ,CAAA,KAAM,IAAA,IACrB,OAAO,MAAA,CAAO,aAAa,CAAA,KAAM,QAAA;AAErC;;;ACxJO,IAAM,cAAN,MAAkB;AAAA,EAGvB,WAAA,CACE,MAAA,EACiB,KAAA,EACA,SAAA,GAAY,GAAA,EAC7B;AAFiB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAEjB,IAAA,IAAA,CAAK,OAAO,CAAA,EAAG,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,aAAA,CAAA;AAAA,EAC1C;AAAA,EAJmB,KAAA;AAAA,EACA,SAAA;AAAA,EALF,IAAA;AAAA;AAAA,EAWjB,MAAM,KAAA,EAAkD;AACtD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAqB,MAAA,EAAQ,UAAA,EAAY;AAAA,MACnD,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,YAAY,KAAA,CAAM,SAAA;AAAA,MAClB,UAAU,KAAA,CAAM,OAAA;AAAA,MAChB,YAAY,KAAA,CAAM;AAAA,KACnB,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAA,CAAO,UAAkB,MAAA,EAAyC;AAChE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAqB,MAAA,EAAQ,CAAA,SAAA,EAAY,kBAAA,CAAmB,QAAQ,CAAC,CAAA,OAAA,CAAA,EAAW,EAAE,MAAA,EAAQ,CAAA;AAAA,EACxG;AAAA;AAAA,EAGA,MAAM,OAAO,QAAA,EAAiC;AAC5C,IAAA,MAAM,KAAK,OAAA,CAAQ,QAAA,EAAU,YAAY,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA,EACzE;AAAA;AAAA,EAGA,KAAA,CACE,QAAA,EACA,QAAA,EACA,aAAA,EAC+E;AAC/E,IAAA,OAAO,KAAK,IAAA,CAAK,MAAA,EAAQ,YAAY,kBAAA,CAAmB,QAAQ,CAAC,CAAA,OAAA,CAAA,EAAW;AAAA,MAC1E,SAAA,EAAW,QAAA;AAAA,MACX,eAAA,EAAiB;AAAA,KAClB,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ;AACf,MAAA,MAAM,CAAA,GAAI,GAAA;AACV,MAAA,OAAO,EAAE,UAAU,CAAA,CAAE,SAAA,EAAW,UAAU,CAAA,CAAE,SAAA,EAAW,aAAA,EAAe,CAAA,CAAE,eAAA,EAAgB;AAAA,IAC1F,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,WAAA,CAAY,QAAA,EAAkB,QAAA,EAAiC;AACnE,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,MACT,QAAA;AAAA,MACA,YAAY,kBAAA,CAAmB,QAAQ,CAAC,CAAA,QAAA,EAAW,kBAAA,CAAmB,QAAQ,CAAC,CAAA;AAAA,KACjF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAA,CAAM,UAAkB,OAAA,EAAsC;AAC5D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,CAAA,SAAA,EAAY,mBAAmB,QAAQ,CAAC,CAAA,MAAA,CAAA,EAAU,EAAE,OAAA,EAAS,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ;AACpG,MAAA,MAAM,CAAA,GAAI,GAAA;AACV,MAAA,OAAO,EAAE,QAAA,EAAU,CAAA,CAAE,SAAA,EAAW,QAAA,EAAU,CAAA,CAAE,QAAA,EAAU,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAQ,SAAA,EAAW,CAAA,CAAE,UAAA,EAAW;AAAA,IAClG,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,IAAA,CAAQ,MAAA,EAAgB,IAAA,EAAc,IAAA,EAA2C;AAC7F,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,MAAM,IAAI,CAAA;AACtD,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B;AAAA,EAEA,MAAc,OAAA,CAAQ,MAAA,EAAgB,IAAA,EAAc,IAAA,EAAmD;AACrG,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,SAAS,CAAA;AACjE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAoB;AAAA,QACxB,MAAA;AAAA,QACA,OAAA,EAAS;AAAA,UACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,UACnC,GAAI,IAAA,GAAO,EAAE,cAAA,EAAgB,kBAAA,KAAuB;AAAC,SACvD;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA,OACrB;AACA,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAAA,MACjC;AACA,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA;AACxD,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,oBAAoB,CAAA,MAAA,EAAS,MAAM,IAAI,IAAI,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,MAChG;AACA,MAAA,OAAO,QAAA;AAAA,IACT,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF;AACF;;;AC3HA,SAAS,eAAA,CAAgB,GAAW,CAAA,EAAoB;AACtD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,QAAA,IAAY,EAAE,UAAA,CAAW,CAAC,CAAA,GAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AAAA,EAC9C;AACA,EAAA,OAAO,QAAA,KAAa,CAAA;AACtB;AAEA,SAAS,MAAM,KAAA,EAA4B;AACzC,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,UAAA,CAAW,KAAK,CAAC,CAAA,CACpC,IAAI,CAAC,CAAA,KAAM,EAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAC1C,KAAK,EAAE,CAAA;AACZ;AAgBA,eAAsB,cAAc,OAAA,EAAiD;AACnF,EAAA,MAAM,EAAE,OAAA,EAAS,eAAA,EAAiB,MAAA,EAAO,GAAI,OAAA;AAC7C,EAAA,MAAM,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,GAAA;AACrD,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,IAAO,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAEvD,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,OAAA,IAAW,eAAA,CAAgB,KAAA,CAAM,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,CAACA,MAAK,KAAK,CAAA,GAAI,QAAQ,IAAA,EAAK,CAAE,KAAA,CAAM,GAAA,EAAK,CAAC,CAAA;AAChD,IAAA,IAAIA,IAAAA,EAAK;AACP,MAAA,KAAA,CAAMA,IAAG,IAAI,KAAA,IAAS,EAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,GAAG,CAAA,IAAK,EAAA;AAChC,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,IAAI,CAAA,IAAK,EAAA;AAEjC,EAAA,IAAI,SAAA,KAAc,MAAM,SAAA,KAAc,EAAA,IAAM,CAAC,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAA,EAAG;AACpE,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAK,GAAA,CAAI,GAAA,GAAM,OAAO,SAAS,CAAC,IAAI,gBAAA,EAAkB;AACxD,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,IAC9B,KAAA;AAAA,IACA,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,MAAM,CAAA;AAAA,IAC/B,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAU;AAAA,IAChC,KAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACA,EAAA,MAAM,QAAA,GAAW,KAAA;AAAA,IACf,MAAM,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,GAAA,EAAK,IAAI,WAAA,EAAY,CAAE,OAAO,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,OAAO,EAAE,CAAC;AAAA,GAC3F;AAEA,EAAA,OAAO,eAAA,CAAgB,UAAU,SAAS,CAAA;AAC5C;;;ACxEO,IAAM,YAAN,MAAgB;AAAA,EAGrB,YACmB,MAAA,EACA,SAAA,EACA,UAAA,EACA,GAAA,GAAoB,KAAK,GAAA,EAC1C;AAJiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAChB;AAAA,EAJgB,MAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,GAAA;AAAA,EANX,MAAA,GAA+D,IAAA;AAAA,EASvE,MAAM,QAAA,GAAuC;AAC3C,IAAA,IAAI,KAAK,MAAA,IAAU,IAAA,CAAK,OAAO,SAAA,GAAY,IAAA,CAAK,KAAI,EAAG;AACrD,MAAA,OAAO,KAAK,MAAA,CAAO,GAAA;AAAA,IACrB;AAEA,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,SAAA,CAAU,IAAA,CAAK,MAAM,CAAC,CAAA,iCAAA,CAAA;AACrC,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,SAAS,CAAA;AAEjE,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,UAAA,CAAW,QAAQ,CAAA;AAC/D,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,qCAAA,EAAwC,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,MAC1F;AACA,MAAA,GAAA,GAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,mBAAA,EAAqB;AACxC,QAAA,MAAM,KAAA;AAAA,MACR;AACA,MAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,0BAAA,EAA6B,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AAAA,IAC5E,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAEA,IAAA,IAAI,OAAO,GAAA,CAAI,MAAA,KAAW,QAAA,IAAY,GAAA,CAAI,WAAW,EAAA,EAAI;AACvD,MAAA,MAAM,IAAI,oBAAoB,+CAA+C,CAAA;AAAA,IAC/E;AAOA,IAAA,IAAI,UAAU,GAAA,CAAI,MAAM,MAAM,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA,EAAG;AACpD,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,kDAAA,EAAqD,IAAI,MAAM,CAAA,EAAA;AAAA,OACjE;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,MAAA,GAAS,EAAE,GAAA,EAAK,SAAA,EAAW,KAAK,GAAA,EAAI,GAAI,KAAK,UAAA,EAAW;AAC7D,IAAA,OAAO,GAAA;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SAAS,IAAA,EAAgD;AAC7D,IAAA,MAAM,KAAA,GAAA,CAAS,MAAM,IAAA,CAAK,QAAA,IAAY,IAAI,CAAA;AAC1C,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,EAAA,EAAI;AAC7C,MAAA,MAAM,IAAI,kBAAA,CAAmB,CAAA,kCAAA,EAAqC,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IAC3E;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,iBAAiB,IAAA,EAAuD;AAC5E,IAAA,MAAM,KAAA,GAAA,CAAS,MAAM,IAAA,CAAK,QAAA,IAAY,IAAI,CAAA;AAC1C,IAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,KAAK,KAAA,GAAQ,IAAA;AAAA,EAC7D;AACF;AAGA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACjC;;;ACJO,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAA6B,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAC3B,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,mBAAmB,sCAAsC,CAAA;AAAA,IACrE;AACA,IAAA,IAAI,CAAC,OAAO,QAAA,EAAU;AACpB,MAAA,MAAM,IAAI,mBAAmB,wCAAwC,CAAA;AAAA,IACvE;AACA,IAAA,kBAAA,CAAmB,OAAO,MAAM,CAAA;AAChC,IAAA,IAAA,CAAK,YAAY,IAAI,SAAA;AAAA,MACnB,MAAA,CAAO,MAAA;AAAA,MACP,OAAO,SAAA,IAAa,GAAA;AAAA,MACpB,OAAO,UAAA,IAAc;AAAA,KACvB;AAAA,EACF;AAAA,EAb6B,MAAA;AAAA,EAHZ,SAAA;AAAA,EACA,SAAA,uBAAgB,GAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsB3D,MAAM,0BAAA,CAA2B,OAAA,GAAuC,EAAC,EAAkC;AACzG,IAAA,MAAM,MAAA,GAAS,WAAA,CAAY,OAAA,CAAQ,MAAM,CAAA;AACzC,IAAA,yBAAA,CAA0B,SAAS,MAAM,CAAA;AAEzC,IAAA,MAAM,eAAe,cAAA,EAAe;AACpC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,WAAA,CAAY,EAAE,CAAA;AAC7C,IAAA,MAAM,KAAA,GAAQ,YAAY,EAAE,CAAA;AAE5B,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,IAAA,CAAK,MAAA,CAAO,WAAA;AAEvD,IAAA,IAAI,WAAA,KAAgB,MAAA,IAAa,WAAA,KAAgB,EAAA,EAAI;AAKnD,MAAA,MAAM,IAAI,kBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB;AAAA,MACjC,aAAA,EAAe,MAAA;AAAA,MACf,SAAA,EAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACvB,YAAA,EAAc,WAAA;AAAA,MACd,QAAQ,OAAA,CAAQ,MAAA,IAAU,KAAK,MAAA,EAAO,EAAG,KAAK,GAAG,CAAA;AAAA,MACjD,KAAA;AAAA,MACA,KAAA;AAAA,MACA,cAAA,EAAgB,MAAM,SAAA,CAAU,YAAY,CAAA;AAAA,MAC5C,qBAAA,EAAuB;AAAA,KACxB,CAAA;AACD,IAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,MAAA,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,IACvC;AACA,IAAA,IAAI,QAAQ,SAAA,EAAW;AACrB,MAAA,MAAA,CAAO,GAAA,CAAI,YAAA,EAAc,OAAA,CAAQ,SAAS,CAAA;AAAA,IAC5C;AACA,IAAA,IAAI,OAAO,OAAA,CAAQ,MAAA,KAAW,QAAA,EAAU;AACtC,MAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,IAC9C;AACA,IAAA,IAAI,OAAA,CAAQ,iBAAiB,MAAA,EAAW;AACtC,MAAA,MAAA,CAAO,GAAA,CAAI,cAAA,EAAgB,OAAA,CAAQ,YAAY,CAAA;AAAA,IACjD;AACA,IAAA,IAAI,OAAA,CAAQ,qBAAqB,MAAA,EAAW;AAC1C,MAAA,MAAA,CAAO,GAAA,CAAI,mBAAA,EAAqB,OAAA,CAAQ,gBAAgB,CAAA;AAAA,IAC1D;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,SAAS,wBAAwB,CAAA;AACvE,IAAA,OAAO;AAAA,MACL,KAAK,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,MAAA,CAAO,UAAU,CAAA,CAAA;AAAA,MACrC,KAAA;AAAA,MACA,YAAA;AAAA,MACA,KAAA;AAAA;AAAA;AAAA,MAGA,GAAI,OAAO,OAAA,CAAQ,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAO,GAAI,EAAC;AAAA,MACvE,GAAI,QAAQ,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,OAAA,CAAQ,YAAA,EAAa,GAAI;AAAC,KACrF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,kBAAA,CACE,cAAA,EACA,OAAA,GAAkF,EAAC,EACpD;AAC/B,IAAA,OAAO,KAAK,0BAAA,CAA2B,EAAE,GAAG,OAAA,EAAS,YAAA,EAAc,gBAAgB,CAAA;AAAA,EACrF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aAAa,KAAA,EAIG;AACpB,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,KAAA;AAE3B,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,IAAS,CAAC,MAAA,CAAO,KAAA,IAAS,CAAC,qBAAA,CAAsB,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,KAAK,CAAA,EAAG;AACxF,MAAA,MAAM,IAAI,kBAAkB,0EAAqE,CAAA;AAAA,IACnG;AAEA,IAAA,IAAI,OAAO,KAAA,EAAO;AAIhB,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,2BAAA,EAA8B,MAAA,CAAO,KAAK,CAAA,EAAG,MAAA,CAAO,oBAAoB,CAAA,EAAA,EAAK,MAAA,CAAO,iBAAiB,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAA;AAAA,QAC7G,MAAA,CAAO,KAAA;AAAA,QACP,OAAO,iBAAA,IAAqB;AAAA,OAC9B;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,OAAO,IAAA,EAAM;AAChB,MAAA,MAAM,IAAI,oBAAoB,iDAAiD,CAAA;AAAA,IACjF;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,QAAA,CAAS,OAAO,IAAA,EAAM,MAAA,CAAO,YAAA,EAAc,KAAA,CAAM,WAAW,CAAA;AAEtF,IAAA,IAAI,CAAC,OAAO,YAAA,EAAc;AACxB,MAAA,MAAM,IAAI,oBAAoB,+BAA+B,CAAA;AAAA,IAC/D;AAEA,IAAA,IAAI,WAAoC,EAAC;AAEzC,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,QAAA,GAAW,MAAM,KAAK,aAAA,CAAc,MAAA,CAAO,UAAU,MAAA,CAAO,KAAA,EAAO,OAAO,MAAM,CAAA;AAAA,IAClF,CAAA,MAAA,IAAA,CAAY,OAAO,MAAA,IAAU,IAAA,CAAK,QAAO,EAAG,QAAA,CAAS,QAAQ,CAAA,EAAG;AAQ9D,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF,CAAA,MAAA,IAAW,OAAO,MAAA,CAAO,MAAA,KAAW,QAAA,EAAU;AAG5C,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,YAAA,CAAa,QAAQ,QAAQ,CAAA;AAErD,IAAA,IAAI,OAAO,YAAA,KAAiB,MAAA,IAAa,IAAA,CAAK,cAAA,KAAmB,OAAO,YAAA,EAAc;AACpF,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,yCAAyC,MAAA,CAAO,YAAY,CAAA,yBAAA,EAA4B,IAAA,CAAK,kBAAkB,iBAAiB,CAAA,sDAAA;AAAA,OAClI;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,YAAA,CACZ,MAAA,EACA,QAAA,EACmB;AACnB,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,QAAA,CAAS,OAAO,YAAY,CAAA;AAMvD,IAAA,MAAM,WAAA,GAAc,SAAS,KAAK,CAAA;AAClC,IAAA,MAAM,UAAA,GAAa,QAAQ,KAAK,CAAA;AAEhC,IAAA,IAAI,OAAO,WAAA,KAAgB,QAAA,IAAY,OAAO,UAAA,KAAe,YAAY,CAAC,qBAAA,CAAsB,WAAA,EAAa,UAAU,CAAA,EAAG;AACxH,MAAA,MAAM,IAAI,oBAAoB,4DAA4D,CAAA;AAAA,IAC5F;AAMA,IAAA,MAAM,MAAA,GAAkC,EAAE,GAAG,OAAA,EAAS,GAAG,QAAA,EAAS;AAElE,IAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AACxB,IAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,EAAA,EAAI;AACzC,MAAA,MAAM,IAAI,oBAAoB,wCAAwC,CAAA;AAAA,IACxE;AAEA,IAAA,MAAM,aAAA,GAAgB,kBAAA,CAAmB,MAAA,CAAO,eAAe,CAAC,CAAA;AAEhE,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,GAAA;AAAA,MACJ,KAAA,EAAO,OAAO,MAAA,CAAO,OAAO,MAAM,QAAA,GAAW,MAAA,CAAO,OAAO,CAAA,GAAI,IAAA;AAAA,MAC/D,IAAA,EAAM,OAAO,MAAA,CAAO,MAAM,MAAM,QAAA,GAAW,MAAA,CAAO,MAAM,CAAA,GAAI,IAAA;AAAA,MAC5D,cAAA,EAAgB,OAAO,MAAA,CAAO,KAAK,MAAM,QAAA,GAAW,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AAAA,MACpE,YAAA,EAAc,aAAa,MAAM,CAAA;AAAA,MACjC,KAAA,EAAO,MAAM,MAAM,CAAA;AAAA,MACnB,WAAA,EAAa,YAAY,MAAM,CAAA;AAAA,MAC/B,KAAA,EAAO,MAAM,MAAM,CAAA;AAAA,MACnB,SAAA,EAAW,UAAU,MAAM,CAAA;AAAA;AAAA,MAE3B,GAAI,aAAA,GAAgB,EAAE,aAAA,KAAkB,EAAC;AAAA,MACzC,MAAA;AAAA,MACA,aAAa,MAAA,CAAO,YAAA;AAAA,MACpB,YAAA,EAAc,OAAO,aAAA,IAAiB,IAAA;AAAA,MACtC,OAAA,EAAS,OAAO,QAAA,IAAY,IAAA;AAAA,MAC5B,WAAW,OAAO,MAAA,CAAO,UAAA,KAAe,QAAA,GAAW,OAAO,UAAA,GAAa;AAAA,KACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,2BAA2B,MAAA,EAAiD;AAChF,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,iBAAiB,+BAA+B,CAAA;AAEtF,IAAA,IAAI,aAAa,IAAA,EAAM;AACrB,MAAA,MAAM,IAAI,kBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,MAC/B,SAAA,EAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACvB,QAAQ,MAAA,IAAU,IAAA,CAAK,MAAA,EAAO,EAAG,KAAK,GAAG;AAAA,KAC1C,CAAA;AACD,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,IAAA,CAAK,MAAA,CAAO,YAAY,CAAA;AAAA,IACpD;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,UAAU,IAAI,CAAA;AAC/C,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,qCAAqC,CAAA;AAAA,IACxE;AAEA,IAAA,MAAM,OAAA,GAAW,MAAM,QAAA,CAAS,IAAA,EAAK;AAErC,IAAA,MAAM,UAAA,GAAa,QAAQ,aAAa,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,QAAQ,WAAW,CAAA;AACpC,IAAA,MAAM,eAAA,GAAkB,QAAQ,kBAAkB,CAAA;AAElD,IAAA,IAAI,OAAO,eAAe,QAAA,IAAY,OAAO,aAAa,QAAA,IAAY,OAAO,oBAAoB,QAAA,EAAU;AACzG,MAAA,MAAM,IAAI,oBAAoB,mDAAmD,CAAA;AAAA,IACnF;AAEA,IAAA,OAAO;AAAA,MACL,UAAA;AAAA,MACA,QAAA;AAAA,MACA,eAAA;AAAA,MACA,uBAAA,EACE,OAAO,OAAA,CAAQ,2BAA2B,MAAM,QAAA,GAC5C,OAAA,CAAQ,2BAA2B,CAAA,GACnC,IAAA;AAAA,MACN,SAAA,EAAW,OAAO,OAAA,CAAQ,YAAY,MAAM,QAAA,GAAW,OAAA,CAAQ,YAAY,CAAA,GAAI,GAAA;AAAA;AAAA;AAAA,MAG/E,QAAA,EAAU,OAAO,OAAA,CAAQ,UAAU,MAAM,QAAA,GAAW,OAAA,CAAQ,UAAU,CAAA,GAAI;AAAA,KAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,eAAA,CACJ,aAAA,EACA,OAAA,GAAoC,EAAC,EAClB;AACnB,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,SAAS,gBAAgB,CAAA;AAC/D,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,cAAc,SAAA,GAAY,GAAA;AACxD,IAAA,IAAI,UAAA,GAAa,cAAc,QAAA,GAAW,GAAA;AAE1C,IAAA,WAAS;AACP,MAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,OAAA,KAAY,IAAA,EAAM;AACpC,QAAA,MAAM,IAAI,oBAAoB,+BAA+B,CAAA;AAAA,MAC/D;AACA,MAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AACzB,QAAA,MAAM,IAAI,mBAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAEA,MAAA,MAAM,KAAA,CAAM,UAAA,EAAY,OAAA,CAAQ,MAAM,CAAA;AAEtC,MAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,QAC/B,UAAA,EAAY,8CAAA;AAAA,QACZ,aAAa,aAAA,CAAc,UAAA;AAAA,QAC3B,SAAA,EAAW,KAAK,MAAA,CAAO;AAAA,OACxB,CAAA;AACD,MAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,QAAA,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,IAAA,CAAK,MAAA,CAAO,YAAY,CAAA;AAAA,MACpD;AAEA,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,UAAU,IAAI,CAAA;AAE/C,MAAA,IAAI,SAAS,EAAA,EAAI;AACf,QAAA,MAAM,MAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AAEpC,QAAA,IAAI,CAAC,OAAO,YAAA,EAAc;AACxB,UAAA,MAAM,IAAI,oBAAoB,oDAAoD,CAAA;AAAA,QACpF;AAKA,QAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,GAAW,MAAM,IAAA,CAAK,cAAc,MAAA,CAAO,QAAA,EAAU,EAAE,CAAA,GAAI,EAAC;AAEpF,QAAA,OAAO,MAAM,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,QAAQ,CAAA;AAAA,MACjD;AAEA,MAAA,MAAM,OAAA,GAAW,MAAM,QAAA,CAAS,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AACvD,MAAA,MAAM,KAAA,GAAQ,OAAO,OAAA,CAAQ,OAAO,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAO,CAAA,GAAI,iBAAA;AAExE,MAAA,IAAI,UAAU,uBAAA,EAAyB;AACrC,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,UAAU,WAAA,EAAa;AAGzB,QAAA,UAAA,IAAc,GAAA;AACd,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,UAAU,eAAA,EAAiB;AAC7B,QAAA,MAAM,IAAI,oBAAoB,uBAAuB,CAAA;AAAA,MACvD;AAEA,MAAA,IAAI,UAAU,eAAA,EAAiB;AAC7B,QAAA,MAAM,IAAI,mBAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAEA,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,uBAAuB,CAAA;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,QAAQ,YAAA,EAAgD;AAC5D,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,MAC/B,UAAA,EAAY,eAAA;AAAA,MACZ,SAAA,EAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACvB,aAAA,EAAe;AAAA,KAChB,CAAA;AACD,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,IAAA,CAAK,MAAA,CAAO,YAAY,CAAA;AAAA,IACpD;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,MAAM,KAAK,SAAA,CAAU,QAAA,CAAS,gBAAgB,CAAA,EAAG,IAAI,CAAA;AACtF,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAKhB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,sBAAsB,CAAA;AAAA,IACzD;AAEA,IAAA,MAAM,MAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,IAAA,IAAI,CAAC,OAAO,YAAA,EAAc;AACxB,MAAA,MAAM,IAAI,oBAAoB,+CAA+C,CAAA;AAAA,IAC/E;AAQA,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAM,IAAA,CAAK,aAAA,CAAc,MAAA,CAAO,QAAA,EAAU,EAAE,CAAA;AAAA,IAC9C;AAEA,IAAA,OAAO;AAAA,MACL,aAAa,MAAA,CAAO,YAAA;AAAA;AAAA;AAAA,MAGpB,YAAA,EAAc,OAAO,aAAA,IAAiB,YAAA;AAAA,MACtC,OAAA,EAAS,OAAO,QAAA,IAAY,IAAA;AAAA,MAC5B,WAAW,OAAO,MAAA,CAAO,UAAA,KAAe,QAAA,GAAW,OAAO,UAAA,GAAa,CAAA;AAAA,MACvE,OAAO,OAAO,MAAA,CAAO,KAAA,KAAU,QAAA,GAAW,OAAO,KAAA,GAAQ;AAAA,KAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,QAAA,EAA2B;AACpC,IAAA,MAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,EAAG,IAAA,CAAK,WAAA,EAAa,CAAA,CAAA;AAC1E,IAAA,OAAO,QAAA,GAAW,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,eAAA,CAAgB,EAAE,SAAA,EAAW,QAAA,EAAU,CAAA,CAAE,QAAA,EAAU,CAAA,CAAA,GAAK,IAAA;AAAA,EAC3F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,UAAA,CAAW,OAAA,GAA2E,EAAC,EAAW;AAChG,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,EAAE,SAAA,EAAW,QAAQ,QAAA,IAAY,IAAA,CAAK,MAAA,CAAO,QAAA,EAAU,CAAA;AAC1F,IAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,MAAA,MAAA,CAAO,GAAA,CAAI,WAAA,EAAa,OAAA,CAAQ,QAAQ,CAAA;AAAA,IAC1C;AACA,IAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,MAAA,MAAA,CAAO,GAAA,CAAI,cAAA,EAAgB,OAAA,CAAQ,YAAY,CAAA;AAAA,IACjD;AAEA,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,kBAAA,EAAqB,MAAA,CAAO,QAAA,EAAU,CAAA,CAAA;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,SAAA,CAAU,QAAA,EAAmB,WAAA,EAA8C;AAC/E,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,iBAAiB,sBAAsB,CAAA;AAC7E,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,EAAE,WAAW,IAAA,CAAK,MAAA,CAAO,UAAU,CAAA;AACtE,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAA,CAAO,GAAA,CAAI,4BAA4B,QAAQ,CAAA;AAAA,IACjD;AACA,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAA,CAAO,GAAA,CAAI,iBAAiB,WAAW,CAAA;AAAA,IACzC;AACA,IAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,MAAA,CAAO,UAAU,CAAA,CAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAA,CAAa,OAAA,GAAoD,EAAC,EAAoB;AAC1F,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,MAC/B,UAAA,EAAY,oBAAA;AAAA,MACZ,SAAA,EAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACvB,aAAA,EAAe,KAAK,aAAA;AAAc,KACnC,CAAA;AACD,IAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,OAAA,CAAQ,MAAA,CAAO,SAAS,CAAA,EAAG;AAC/C,MAAA,IAAA,CAAK,IAAI,OAAA,EAAS,OAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,IAC5C;AACA,IAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,MAAA,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,OAAA,CAAQ,QAAQ,CAAA;AAAA,IACvC;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,MAAM,KAAK,SAAA,CAAU,QAAA,CAAS,gBAAgB,CAAA,EAAG,IAAI,CAAA;AACtF,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,8BAA8B,CAAA;AAAA,IACjE;AACA,IAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,IAAA,IAAI,OAAO,IAAA,CAAK,YAAA,KAAiB,QAAA,EAAU;AACzC,MAAA,MAAM,IAAI,oBAAoB,yCAAyC,CAAA;AAAA,IACzE;AACA,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,SAAS,WAAA,EAAuD;AACpE,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,iBAAiB,mBAAmB,CAAA;AAC1E,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,EAAC;AAAA,IACV;AACA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU;AAAA,MACrD,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA;AAAG,KACnD,CAAA;AACD,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,yBAAyB,CAAA;AAAA,IAC5D;AACA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,KAAA,EAAiD;AAChE,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,SAAS,wBAAwB,CAAA;AACvE,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,aAAA,EAAe,CAAA,CAAE,CAAA;AACpE,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA;AAAA,MAC1B,QAAA;AAAA,MACA,IAAI,eAAA,CAAgB,EAAE,KAAA,EAAO,CAAA;AAAA,MAC7B,EAAE,aAAA,EAAe,CAAA,MAAA,EAAS,KAAK,CAAA,CAAA;AAAG,KACpC;AACA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,8BAA8B,CAAA;AAAA,IACjE;AACA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,MAAA,CAAO,KAAA,EAAe,aAAA,EAA8C;AACxE,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,SAAS,qBAAqB,CAAA;AACpE,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB,EAAE,OAAO,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,QAAA,EAAU,CAAA;AAC3E,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IAAA,CAAK,GAAA,CAAI,mBAAmB,aAAa,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,UAAkC,EAAC;AACzC,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,OAAA,CAAQ,aAAA,GAAgB,CAAA,MAAA,EAAS,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,CAAO,YAAY,CAAA,CAAE,CAAC,CAAA,CAAA;AAAA,IAC9F;AAEA,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,OAAO,CAAA;AACxD,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,2BAA2B,CAAA;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA,EAGA,cAAc,OAAA,EAAiD;AAC7D,IAAA,OAAO,cAAc,OAAO,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAA,EAAkC;AACtC,IAAA,OAAO,IAAI,YAAY,IAAA,CAAK,MAAA,CAAO,QAAQ,WAAA,EAAa,IAAA,CAAK,MAAA,CAAO,SAAA,IAAa,GAAM,CAAA;AAAA,EACzF;AAAA,EAEA,MAAc,QAAA,CAAS,IAAA,EAAc,QAAA,EAAkB,WAAA,EAA8C;AACnG,IAAA,MAAM,QAAA,GAAW,WAAA,IAAe,IAAA,CAAK,MAAA,CAAO,WAAA;AAE5C,IAAA,IAAI,QAAA,KAAa,MAAA,IAAa,QAAA,KAAa,EAAA,EAAI;AAC7C,MAAA,MAAM,IAAI,kBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,MAC/B,UAAA,EAAY,oBAAA;AAAA,MACZ,IAAA;AAAA,MACA,YAAA,EAAc,QAAA;AAAA,MACd,SAAA,EAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACvB,aAAA,EAAe;AAAA,KAChB,CAAA;AACD,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,IAAA,CAAK,MAAA,CAAO,YAAY,CAAA;AAAA,IACpD;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,MAAM,KAAK,SAAA,CAAU,QAAA,CAAS,gBAAgB,CAAA,EAAG,IAAI,CAAA;AACtF,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,MAAM,UAAA,CAAW,QAAA,EAAU,uBAAuB,CAAA;AAAA,IAC1D;AACA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B;AAAA,EAEA,MAAc,aAAA,CACZ,OAAA,EAIA,KAAA,EACA,MAAA,EACkC;AAClC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,SAAA,CAAU,SAAS,UAAU,CAAA;AACxD,IAAA,IAAI,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAO,CAAA;AACzC,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,QAAA,GAAW,kBAAA,CAAmB,IAAI,GAAA,CAAI,OAAO,CAAC,CAAA;AAC9C,MAAA,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAA,EAAS,QAAQ,CAAA;AAAA,IACtC;AAEA,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,CAAC,EAAE,OAAA,EAAQ,GAAI,MAAM,SAAA,CAAU,SAAS,QAAA,EAAU;AAAA,QAChD,MAAA,EAAQ,KAAK,MAAA,CAAO,MAAA;AAAA,QACpB,QAAA,EAAU,KAAK,MAAA,CAAO,QAAA;AAAA;AAAA;AAAA;AAAA,QAItB,UAAA,EAAY,CAAC,OAAA,EAAS,OAAO;AAAA,OAC9B,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,oCAAA,EAAuC,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AAAA,IACtF;AAEA,IAAA,IAAI,KAAA,IAAS,OAAA,CAAQ,OAAO,CAAA,KAAM,KAAA,EAAO;AACvC,MAAA,MAAM,IAAI,oBAAoB,0DAAqD,CAAA;AAAA,IACrF;AAQA,IAAA,IAAI,OAAO,WAAW,QAAA,EAAU;AAC9B,MAAA,MAAM,QAAA,GAAW,QAAQ,WAAW,CAAA;AAEpC,MAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,QAAA,MAAM,IAAI,mBAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,wBAAA,IAA4B,EAAA;AAC1D,MAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,QAAA;AAE5C,MAAA,IAAI,GAAA,GAAM,SAAS,SAAA,EAAW;AAC5B,QAAA,MAAM,IAAI,mBAAA;AAAA,UACR,CAAA,sBAAA,EAAyB,GAAG,CAAA,2BAAA,EAA8B,MAAM,CAAA,0CAAA;AAAA,SAClE;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEQ,MAAA,GAAmB;AACzB,IAAA,MAAM,MAAA,GAAS,KAAK,MAAA,CAAO,MAAA;AAC3B,IAAA,OAAO,MAAA,IAAU,OAAO,MAAA,GAAS,CAAA,GAAI,SAAS,CAAC,QAAA,EAAU,WAAW,OAAO,CAAA;AAAA,EAC7E;AAAA,EAEQ,WAAA,GAAsB;AAC5B,IAAA,MAAM,IAAA,GAAO,KAAK,MAAA,CAAO,WAAA;AAIzB,IAAA,OAAO,IAAA,IAAQ,SAAS,EAAA,GAAK,CAAA,CAAA,EAAI,KAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA,GAAK,UAAA;AAAA,EAC/D;AAAA,EAEQ,aAAA,GAAwB;AAC9B,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,YAAA,EAAc;AAC7B,MAAA,MAAM,IAAI,mBAAmB,8DAA8D,CAAA;AAAA,IAC7F;AACA,IAAA,OAAO,KAAK,MAAA,CAAO,YAAA;AAAA,EACrB;AAAA,EAEQ,IAAA,CAAK,GAAA,EAAa,IAAA,EAAuB,OAAA,GAAkC,EAAC,EAAsB;AACxG,IAAA,OAAO,IAAA,CAAK,iBAAiB,GAAA,EAAK;AAAA,MAChC,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,mCAAA,EAAqC,GAAG,OAAA,EAAQ;AAAA,MAC3E;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,gBAAA,CAAiB,GAAA,EAAa,IAAA,EAAsC;AAChF,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,UAAA,CAAW,OAAM,EAAG,IAAA,CAAK,MAAA,CAAO,SAAA,IAAa,GAAM,CAAA;AAClF,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,MAAM,GAAA,EAAK,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAA;AAAA,IAChE,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF;AACF;AAGA,SAAS,YAAY,MAAA,EAAsE;AACzF,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,IAAI,GAAA,CAAI,OAAO,MAAA,KAAW,QAAA,GAAW,CAAC,MAAM,CAAA,GAAI,MAAM,CAAC,CAAA;AAI1E,EAAA,IAAI,OAAO,QAAA,CAAS,MAAM,CAAA,IAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,mBAAmB,gEAAgE,CAAA;AAAA,EAC/F;AAEA,EAAA,OAAO,MAAA;AACT;AAOA,SAAS,yBAAA,CAA0B,SAAsC,MAAA,EAAqC;AAG5G,EAAA,IAAI,OAAA,CAAQ,iBAAiB,EAAA,EAAI;AAC/B,IAAA,MAAM,IAAI,mBAAmB,iFAAiF,CAAA;AAAA,EAChH;AACA,EAAA,IAAI,OAAA,CAAQ,qBAAqB,EAAA,EAAI;AACnC,IAAA,MAAM,IAAI,mBAAmB,gFAAgF,CAAA;AAAA,EAC/G;AAEA,EAAA,IAAI,OAAA,CAAQ,iBAAiB,MAAA,EAAW;AACtC,IAAA;AAAA,EACF;AAIA,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,qBAAqB,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,qBAAqB,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACF;AAGA,SAAS,qBAAA,CAAsB,GAAW,CAAA,EAAoB;AAC5D,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,QAAA,IAAY,EAAE,UAAA,CAAW,CAAC,CAAA,GAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AAAA,EAC9C;AACA,EAAA,OAAO,QAAA,KAAa,CAAA;AACtB;AAOA,SAAS,mBAAmB,KAAA,EAAgD;AAC1E,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,OAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,SAAS,KAAA,EAAO;AACzB,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,KAAA;AACf,IAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA;AACtB,IAAA,MAAM,IAAA,GAAO,OAAO,MAAM,CAAA;AAC1B,IAAA,IAAI,OAAO,OAAO,QAAA,IAAY,EAAA,KAAO,MAAM,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,EAAA,EAAI;AAClF,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,EAAM,MAAM,OAAO,MAAA,CAAO,MAAM,CAAA,KAAM,QAAA,GAAW,MAAA,CAAO,MAAM,CAAA,GAAI,MAAM,CAAA;AAAA,IAC1F;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AASA,SAAS,KAAA,CAAM,IAAY,MAAA,EAAqC;AAC9D,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,MAAA,EAAQ,YAAY,IAAA,EAAM;AAC5B,MAAA,MAAA,CAAO,IAAI,mBAAA,CAAoB,+BAA+B,CAAC,CAAA;AAC/D,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,MAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC5C,MAAA,OAAA,EAAQ;AAAA,IACV,GAAG,EAAE,CAAA;AAEL,IAAA,MAAM,UAAU,MAAY;AAC1B,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,MAAA,CAAO,IAAI,mBAAA,CAAoB,+BAA+B,CAAC,CAAA;AAAA,IACjE,CAAA;AAEA,IAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,EAC3D,CAAC,CAAA;AACH","file":"chunk-G3CD7X4U.js","sourcesContent":["/**\n * PKCE (RFC 7636) and the random values the login flow needs, built on Web Crypto\n * (`globalThis.crypto`) so the same code runs on Node, edge runtimes and browsers.\n */\n\nfunction base64url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/** A URL-safe random token of `bytes` bytes (default 32), for state and nonce. */\nexport function randomToken(bytes = 32): string {\n return base64url(crypto.getRandomValues(new Uint8Array(bytes)));\n}\n\n/** A high-entropy PKCE code verifier. */\nexport function createVerifier(): string {\n return randomToken(32);\n}\n\n/** The S256 code challenge for a verifier. */\nexport async function challenge(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64url(new Uint8Array(digest));\n}\n","import type { CboxUser } from './types.js';\n\n/**\n * The membership tier a person holds in the organization a token is bound to — the\n * `org_role` claim. Coarse on purpose: it says who may administer the organization\n * itself (invite, bill, delete). What they may do inside YOUR app is `roles` /\n * `permissions`, which your app declares and Cbox ID assigns.\n */\nexport type OrganizationRole = 'owner' | 'admin' | 'developer' | 'member' | 'viewer';\n\nconst ORGANIZATION_ROLES: ReadonlySet<string> = new Set<OrganizationRole>([\n 'owner',\n 'admin',\n 'developer',\n 'member',\n 'viewer',\n]);\n\n/** The organization a token is bound to: the `org`, `org_name` and `org_role` claims. */\nexport interface CboxActiveOrganization {\n /** The stable organization id (`org`). */\n id: string;\n /** Its display name (`org_name`), when the instance sent one. */\n name: string | null;\n /**\n * The person's membership tier in it (`org_role`). Null when the claim is absent, or\n * carries a tier this SDK version does not know — an unrecognised tier is never\n * guessed upward into one that grants something.\n */\n role: OrganizationRole | null;\n}\n\n/**\n * Who is actually driving a delegated session — the RFC 8693 §4.1 `act` claim. Cbox ID\n * sets it on the tokens of a SUPPORT SESSION: a staff member acting as one of your users,\n * with a stated reason, for at most an hour, with no refresh token.\n *\n * `sub` is the actor's subject id. It is null when an `act` claim is present but not in\n * the shape RFC 8693 describes — the token is still acted, and saying so matters more\n * than knowing by whom (see {@link isSupportSession}).\n */\nexport interface CboxActor {\n sub: string | null;\n /** The prior actor, when delegation was chained (RFC 8693 §4.1 nested `act`). */\n actor: CboxActor | null;\n}\n\n/**\n * Anything the helpers below can read claims from: a {@link CboxUser} from sign-in, or a\n * claim set you verified yourself — an access token's payload on a resource server, say.\n */\nexport type ClaimSource = CboxUser | Readonly<Record<string, unknown>>;\n\n/**\n * The organization this session is bound to, or null when it is bound to none.\n *\n * ```ts\n * const org = organization(user);\n * if (org?.role === 'owner') showBilling();\n * ```\n */\nexport function organization(source: ClaimSource): CboxActiveOrganization | null {\n const claims = claimsOf(source);\n const id = claims['org'];\n\n if (typeof id !== 'string' || id === '') {\n return null;\n }\n\n const name = claims['org_name'];\n const role = claims['org_role'];\n\n return {\n id,\n name: typeof name === 'string' && name !== '' ? name : null,\n role: typeof role === 'string' && ORGANIZATION_ROLES.has(role) ? (role as OrganizationRole) : null,\n };\n}\n\n/**\n * The actor behind a support session (`act`), or null for an ordinary session in which\n * the person is acting as themselves.\n */\nexport function actor(source: ClaimSource): CboxActor | null {\n return parseActor(claimsOf(source)['act'], 0);\n}\n\n/**\n * Whether somebody other than the signed-in person is driving this session — a staff\n * member in a support session (the token carries `act`).\n *\n * FAIL-CLOSED. Any `act` claim at all counts, including one whose shape this SDK cannot\n * read: the check exists so an app can show a banner and refuse the things a helper\n * should never do on somebody's behalf (change their password, move their money), and a\n * malformed claim is not evidence that nobody else is at the keyboard.\n */\nexport function isSupportSession(source: ClaimSource): boolean {\n return actor(source) !== null;\n}\n\n/**\n * The Cbox ID sign-in session this token came from — the ID Token's `sid` claim — or null\n * when the instance sent none.\n *\n * Store it with your own session: an OIDC Back-Channel Logout token names the session it\n * ends by this value, and matching on it is how you end the right one (rather than every\n * session the person has) when they sign out of Cbox ID elsewhere.\n */\nexport function sessionId(source: ClaimSource): string | null {\n const sid = claimsOf(source)['sid'];\n\n return typeof sid === 'string' && sid !== '' ? sid : null;\n}\n\n/** The app roles this session holds — the `roles` claim; empty when there are none. */\nexport function roles(source: ClaimSource): string[] {\n return stringList(claimsOf(source)['roles']);\n}\n\n/**\n * The permissions this session holds — the `permissions` claim, already expanded from\n * `roles` by Cbox ID; empty when there are none.\n */\nexport function permissions(source: ClaimSource): string[] {\n return stringList(claimsOf(source)['permissions']);\n}\n\n/** Whether the session holds `role`. Exact match; no wildcards. */\nexport function hasRole(source: ClaimSource, role: string): boolean {\n return roles(source).includes(role);\n}\n\n/**\n * Whether the session holds `permission` (`feature:action`). Exact match; no wildcards\n * — a claim of `invoices:*` does not grant `invoices:delete`, because nothing on the\n * issuing side ever mints one.\n */\nexport function hasPermission(source: ClaimSource, permission: string): boolean {\n return permissions(source).includes(permission);\n}\n\n/**\n * Nesting is bounded: RFC 8693 chains are a handful deep in practice, and a claim nested\n * thousands deep is either a bug or an attempt to exhaust the stack. Past the bound the\n * chain is cut, not dropped — the session is still acted.\n */\nconst MAX_ACTOR_DEPTH = 8;\n\nfunction parseActor(claim: unknown, depth: number): CboxActor | null {\n if (claim === undefined || claim === null) {\n return null;\n }\n\n if (typeof claim !== 'object' || Array.isArray(claim)) {\n return { sub: null, actor: null };\n }\n\n const record = claim as Record<string, unknown>;\n const sub = record['sub'];\n\n return {\n sub: typeof sub === 'string' && sub !== '' ? sub : null,\n actor: depth + 1 < MAX_ACTOR_DEPTH ? parseActor(record['act'], depth + 1) : null,\n };\n}\n\nfunction stringList(claim: unknown): string[] {\n return Array.isArray(claim)\n ? claim.filter((value): value is string => typeof value === 'string' && value !== '')\n : [];\n}\n\nfunction claimsOf(source: ClaimSource): Readonly<Record<string, unknown>> {\n return isUser(source) ? source.claims : source;\n}\n\n/**\n * A `CboxUser` is told apart by the credential it carries alongside its claims. A raw\n * claim set never has an `accessToken` key — JWT claims are snake_case — so a verified\n * payload is never mistaken for a user and read from the wrong place.\n */\nfunction isUser(source: ClaimSource): source is CboxUser {\n return (\n typeof source['claims'] === 'object' &&\n source['claims'] !== null &&\n typeof source['accessToken'] === 'string'\n );\n}\n","import { AuthenticationError } from './errors.js';\n\n/** A stored vault secret's metadata (never its plaintext). */\nexport interface VaultSecretRef {\n id: string;\n name: string;\n provider: string;\n ownerType: string | null;\n ownerId: string | null;\n expiresAt: string | null;\n revoked: boolean;\n}\n\n/** A brokered credential — the plaintext, for immediate use. */\nexport interface VaultLease {\n secretId: string;\n provider: string;\n secret: string;\n expiresAt: string;\n}\n\nexport interface StoreSecretInput {\n name: string;\n provider: string;\n secret: string;\n ownerType?: 'organization' | 'user';\n ownerId?: string;\n expiresAt?: string;\n}\n\n/**\n * Client for a Cbox ID instance's Token Vault API. Bind it to an access token\n * (obtain one with {@link CboxIdClient.machineToken}, scoped `vault.manage` for\n * provisioning or `vault.lease` for redeeming). All calls are server-to-server.\n */\nexport class VaultClient {\n private readonly base: string;\n\n constructor(\n issuer: string,\n private readonly token: string,\n private readonly timeoutMs = 10_000,\n ) {\n this.base = `${issuer.replace(/\\/$/, '')}/api/v1/vault`;\n }\n\n /** Ingest a downstream credential, sealed at rest (scope `vault.manage`). */\n store(input: StoreSecretInput): Promise<VaultSecretRef> {\n return this.json<VaultSecretRef>('POST', '/secrets', {\n name: input.name,\n provider: input.provider,\n secret: input.secret,\n owner_type: input.ownerType,\n owner_id: input.ownerId,\n expires_at: input.expiresAt,\n });\n }\n\n /** Rotate a secret's value, keeping its id and grants (scope `vault.manage`). */\n rotate(secretId: string, secret: string): Promise<VaultSecretRef> {\n return this.json<VaultSecretRef>('POST', `/secrets/${encodeURIComponent(secretId)}/rotate`, { secret });\n }\n\n /** Revoke a secret permanently (scope `vault.manage`). */\n async revoke(secretId: string): Promise<void> {\n await this.request('DELETE', `/secrets/${encodeURIComponent(secretId)}`);\n }\n\n /** Authorize an agent client to lease a secret (scope `vault.manage`). */\n grant(\n secretId: string,\n clientId: string,\n maxTtlSeconds?: number,\n ): Promise<{ secretId: string; clientId: string; maxTtlSeconds: number | null }> {\n return this.json('POST', `/secrets/${encodeURIComponent(secretId)}/grants`, {\n client_id: clientId,\n max_ttl_seconds: maxTtlSeconds,\n }).then((raw) => {\n const r = raw as { secret_id: string; client_id: string; max_ttl_seconds: number | null };\n return { secretId: r.secret_id, clientId: r.client_id, maxTtlSeconds: r.max_ttl_seconds };\n });\n }\n\n /** Revoke an agent's authorization (scope `vault.manage`). */\n async revokeGrant(secretId: string, clientId: string): Promise<void> {\n await this.request(\n 'DELETE',\n `/secrets/${encodeURIComponent(secretId)}/grants/${encodeURIComponent(clientId)}`,\n );\n }\n\n /**\n * Redeem a leased credential for immediate use (scope `vault.lease`). The caller\n * is identified by the token's client; a lease with no live grant is refused.\n */\n lease(secretId: string, purpose: string): Promise<VaultLease> {\n return this.json('POST', `/secrets/${encodeURIComponent(secretId)}/lease`, { purpose }).then((raw) => {\n const r = raw as { secret_id: string; provider: string; secret: string; expires_at: string };\n return { secretId: r.secret_id, provider: r.provider, secret: r.secret, expiresAt: r.expires_at };\n });\n }\n\n private async json<T>(method: string, path: string, body: Record<string, unknown>): Promise<T> {\n const response = await this.request(method, path, body);\n return (await response.json()) as T;\n }\n\n private async request(method: string, path: string, body?: Record<string, unknown>): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const init: RequestInit = {\n method,\n headers: {\n authorization: `Bearer ${this.token}`,\n ...(body ? { 'content-type': 'application/json' } : {}),\n },\n signal: controller.signal,\n };\n if (body) {\n init.body = JSON.stringify(body);\n }\n const response = await fetch(`${this.base}${path}`, init);\n if (!response.ok) {\n throw new AuthenticationError(`Vault ${method} ${path} failed with status ${response.status}.`);\n }\n return response;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","/**\n * Verify a Cbox ID webhook / inline-action signature. The header is\n * `X-Cbox-Signature: t={unix},v1={hex hmac}` — an HMAC-SHA256 over\n * `\"{timestamp}.{raw body}\"`, valid within a freshness window. Built on Web Crypto\n * so it runs on Node, edge and the browser.\n */\n\n/** Constant-time compare of two hex strings. */\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n let mismatch = 0;\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return mismatch === 0;\n}\n\nfunction toHex(bytes: ArrayBuffer): string {\n return Array.from(new Uint8Array(bytes))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nexport interface VerifyWebhookOptions {\n /** The RAW request body — the exact bytes received, not a re-encoded copy. */\n payload: string;\n /** The value of the `X-Cbox-Signature` header. */\n signatureHeader: string | null | undefined;\n /** The signing secret issued when the webhook/action endpoint was registered. */\n secret: string;\n /** Freshness window in seconds. Defaults to 300 (5 minutes). */\n toleranceSeconds?: number;\n /** Injectable clock (unix seconds), for tests. Defaults to `Date.now()`. */\n now?: number;\n}\n\n/** Returns `true` only when the signature is present, fresh and valid. Never throws. */\nexport async function verifyWebhook(options: VerifyWebhookOptions): Promise<boolean> {\n const { payload, signatureHeader, secret } = options;\n const toleranceSeconds = options.toleranceSeconds ?? 300;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n\n if (!signatureHeader) {\n return false;\n }\n\n const parts: Record<string, string> = {};\n for (const segment of signatureHeader.split(',')) {\n const [key, value] = segment.trim().split('=', 2);\n if (key) {\n parts[key] = value ?? '';\n }\n }\n\n const timestamp = parts['t'] ?? '';\n const signature = parts['v1'] ?? '';\n\n if (timestamp === '' || signature === '' || !/^\\d+$/.test(timestamp)) {\n return false;\n }\n\n if (Math.abs(now - Number(timestamp)) > toleranceSeconds) {\n return false;\n }\n\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const expected = toHex(\n await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(`${timestamp}.${payload}`)),\n );\n\n return timingSafeEqual(expected, signature);\n}\n","import { AuthenticationError, ConfigurationError } from './errors.js';\nimport type { DiscoveryDocument } from './types.js';\n\n/**\n * Fetches and caches an instance's OIDC discovery document. One instance of this\n * lives per {@link CboxIdClient}; the document is refetched only after the TTL.\n */\nexport class Discovery {\n private cached: { doc: DiscoveryDocument; expiresAt: number } | null = null;\n\n constructor(\n private readonly issuer: string,\n private readonly timeoutMs: number,\n private readonly cacheTtlMs: number,\n private readonly now: () => number = Date.now,\n ) {}\n\n async document(): Promise<DiscoveryDocument> {\n if (this.cached && this.cached.expiresAt > this.now()) {\n return this.cached.doc;\n }\n\n const url = `${trimSlash(this.issuer)}/.well-known/openid-configuration`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let doc: DiscoveryDocument;\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) {\n throw new AuthenticationError(`Discovery request failed with status ${response.status}.`);\n }\n doc = (await response.json()) as DiscoveryDocument;\n } catch (error) {\n if (error instanceof AuthenticationError) {\n throw error;\n }\n throw new AuthenticationError(`Discovery request failed: ${String(error)}`);\n } finally {\n clearTimeout(timer);\n }\n\n if (typeof doc.issuer !== 'string' || doc.issuer === '') {\n throw new AuthenticationError('The discovery document was missing an issuer.');\n }\n\n // RFC 8414 §3.3: the `issuer` in the document MUST match the one it was fetched for.\n // Without this the SDK sends credentials to whatever endpoints the document names and\n // verifies tokens against whatever JWKS it points at, while the caller still believes\n // it is talking to the issuer it configured — so one tenant's host answering with\n // another tenant's document silently redirects the whole flow.\n if (trimSlash(doc.issuer) !== trimSlash(this.issuer)) {\n throw new AuthenticationError(\n `The discovery document is for a different issuer (${doc.issuer}).`,\n );\n }\n\n this.cached = { doc, expiresAt: this.now() + this.cacheTtlMs };\n return doc;\n }\n\n /** A required endpoint from the discovery document. */\n async endpoint(name: keyof DiscoveryDocument): Promise<string> {\n const value = (await this.document())[name];\n if (typeof value !== 'string' || value === '') {\n throw new ConfigurationError(`The instance does not advertise a ${name}.`);\n }\n return value;\n }\n\n /** An optional endpoint, or null when the instance advertises none. */\n async optionalEndpoint(name: keyof DiscoveryDocument): Promise<string | null> {\n const value = (await this.document())[name];\n return typeof value === 'string' && value !== '' ? value : null;\n }\n}\n\n/** Compare issuers without letting a trailing slash decide identity. */\nfunction trimSlash(value: string): string {\n return value.replace(/\\/+$/, '');\n}\n","import { createRemoteJWKSet, jwtVerify, type JWTPayload } from 'jose';\nimport { Discovery } from './discovery.js';\nimport { assertSecureIssuer } from './issuer.js';\nimport { AuthenticationError, ConfigurationError, InvalidStateError, oauthError } from './errors.js';\nimport { challenge, createVerifier, randomToken } from './pkce.js';\nimport { actor, organization, permissions, roles, sessionId } from './claims.js';\nimport type {\n AuthorizationPrompt,\n AuthorizationRequest,\n AuthorizationRequestOptions,\n CboxIdConfig,\n DeviceAuthorization,\n CboxOrganization,\n CboxUser,\n RefreshedTokens,\n TokenResponse,\n TokenTypeHint,\n} from './types.js';\nimport { VaultClient } from './vault.js';\nimport { verifyWebhook, type VerifyWebhookOptions } from './webhook.js';\n\ntype JwksResolver = ReturnType<typeof createRemoteJWKSet>;\n\n/** Query parameters as they arrive on your callback route. */\nexport interface CallbackParams {\n code?: string | null;\n state?: string | null;\n error?: string | null;\n error_description?: string | null;\n}\n\n/** The values you persisted from {@link CboxIdClient.createAuthorizationRequest}. */\nexport interface StoredAuthState {\n state: string;\n codeVerifier: string;\n nonce: string;\n /** The `maxAge` this login demanded, if any. See {@link AuthorizationRequest.maxAge}. */\n maxAge?: number;\n /**\n * The organization this sign-in was bound to, if any. See\n * {@link AuthorizationRequest.organization}.\n *\n * Checked, not trusted: a binding nobody verifies is a binding in name only. An\n * instance that predates the `organization` parameter ignores it and returns tokens for\n * whichever organization the session already had — and an app that switched to \"Globex\"\n * would then show Globex's name over Acme's data.\n */\n organization?: string;\n /**\n * The scopes THIS authorization asked for, when you overrode the configured set.\n *\n * Read so `authenticate()` can judge the response against what was requested rather\n * than against the config: a login that asked for `openid` and came back without an\n * id_token is a protocol violation, and one that never asked cannot be held to it.\n * Omitted, the configured scopes are assumed — which is what a caller who did not\n * override them actually sent.\n */\n scopes?: string[];\n}\n\n/**\n * Turnkey Cbox ID client for JavaScript/TypeScript. It speaks standard OpenID\n * Connect against a Cbox ID instance — so integrating is a redirect and a callback,\n * not a rewrite — and adds the conveniences a hosted-identity product needs: a\n * redirect to the instance's hosted profile page, and back-channel helpers (machine\n * tokens, userinfo, introspection, webhook verification).\n *\n * Login is hardened by default: PKCE (S256), a CSRF state check, a nonce, and full\n * id_token signature + issuer + audience verification against the instance's JWKS\n * (via `jose`).\n *\n * The class is framework-agnostic: {@link createAuthorizationRequest} hands you the\n * `state`, `codeVerifier` and `nonce` to persist however you like, and\n * {@link authenticate} takes them back. For Next.js, the `@cboxdk/id-js/nextjs`\n * entry wires this to cookies for you.\n */\nexport class CboxIdClient {\n private readonly discovery: Discovery;\n private readonly jwksByUri = new Map<string, JwksResolver>();\n\n constructor(private readonly config: CboxIdConfig) {\n if (!config.issuer) {\n throw new ConfigurationError('Cbox ID config `issuer` is required.');\n }\n if (!config.clientId) {\n throw new ConfigurationError('Cbox ID config `clientId` is required.');\n }\n assertSecureIssuer(config.issuer);\n this.discovery = new Discovery(\n config.issuer,\n config.timeoutMs ?? 10_000,\n config.cacheTtlMs ?? 3_600_000,\n );\n }\n\n /**\n * Begin login. Returns the authorize URL plus the `state`, `codeVerifier` and\n * `nonce` — persist those (signed, httpOnly cookies are ideal) and hand them back\n * to {@link authenticate} on the callback.\n */\n async createAuthorizationRequest(options: AuthorizationRequestOptions = {}): Promise<AuthorizationRequest> {\n const prompt = promptValue(options.prompt);\n assertOrganizationOptions(options, prompt);\n\n const codeVerifier = createVerifier();\n const state = options.state ?? randomToken(16);\n const nonce = randomToken(16);\n\n const redirectUri = options.redirectUri ?? this.config.redirectUri;\n\n if (redirectUri === undefined || redirectUri === '') {\n // Checked where it is needed rather than at construction, so a CLI can build a\n // client at all. An authorize URL without `redirect_uri` would fail at the\n // authorization server instead, describing a request the caller never knowingly\n // made — see the device grant, which has no callback to name.\n throw new ConfigurationError(\n 'A `redirectUri` is required to start the browser sign-in flow. Set it in the config, or pass it to createAuthorizationRequest.',\n );\n }\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: redirectUri,\n scope: (options.scopes ?? this.scopes()).join(' '),\n state,\n nonce,\n code_challenge: await challenge(codeVerifier),\n code_challenge_method: 'S256',\n });\n if (prompt.length > 0) {\n params.set('prompt', prompt.join(' '));\n }\n if (options.loginHint) {\n params.set('login_hint', options.loginHint);\n }\n if (typeof options.maxAge === 'number') {\n params.set('max_age', String(options.maxAge));\n }\n if (options.organization !== undefined) {\n params.set('organization', options.organization);\n }\n if (options.organizationHint !== undefined) {\n params.set('organization_hint', options.organizationHint);\n }\n\n const endpoint = await this.discovery.endpoint('authorization_endpoint');\n return {\n url: `${endpoint}?${params.toString()}`,\n state,\n codeVerifier,\n nonce,\n // Carried through so authenticate() can hold the instance to it. Only present\n // when requested (exactOptionalPropertyTypes).\n ...(typeof options.maxAge === 'number' ? { maxAge: options.maxAge } : {}),\n ...(options.organization !== undefined ? { organization: options.organization } : {}),\n };\n }\n\n /**\n * Switch the signed-in person to another organization: a new authorization bound to\n * `organizationId`. Persist and redirect exactly as for\n * {@link createAuthorizationRequest} — it is one, with `organization` set.\n *\n * Cbox ID already holds the person's session, so this is normally a redirect there and\n * straight back with no sign-in form. The tokens that come back carry the new `org`,\n * `org_role`, `roles` and `permissions`; replace your session with them rather than\n * patching the old one, because every one of those can differ between organizations.\n *\n * A person who is not (or is no longer) an active member of that organization comes\n * back with `error=access_denied`, which {@link authenticate} throws as an\n * `AuthenticationError` with `error === 'access_denied'`.\n */\n switchOrganization(\n organizationId: string,\n options: Omit<AuthorizationRequestOptions, 'organization' | 'organizationHint'> = {},\n ): Promise<AuthorizationRequest> {\n return this.createAuthorizationRequest({ ...options, organization: organizationId });\n }\n\n /**\n * Complete login on your callback route: verify state, exchange the code with the\n * PKCE verifier, verify the id_token, and return the authenticated user.\n *\n * @throws InvalidStateError when state does not match (forged/stale request)\n * @throws AuthenticationError on any other failure\n */\n async authenticate(input: {\n params: CallbackParams;\n stored: StoredAuthState;\n redirectUri?: string;\n }): Promise<CboxUser> {\n const { params, stored } = input;\n\n if (!params.state || !stored.state || !timingSafeEqualString(params.state, stored.state)) {\n throw new InvalidStateError('The login state did not match — the request may be forged or stale.');\n }\n\n if (params.error) {\n // The code travels as `error`, not only in the message: `access_denied` after a\n // switchOrganization() means \"not a member of that organization\", which an app\n // answers by switching back — not by signing the person out.\n throw new AuthenticationError(\n `Cbox ID returned an error: ${params.error}${params.error_description ? ` (${params.error_description})` : ''}`,\n params.error,\n params.error_description ?? undefined,\n );\n }\n\n if (!params.code) {\n throw new AuthenticationError('The callback was missing an authorization code.');\n }\n\n const tokens = await this.exchange(params.code, stored.codeVerifier, input.redirectUri);\n\n if (!tokens.access_token) {\n throw new AuthenticationError('No access token was returned.');\n }\n\n let verified: Record<string, unknown> = {};\n\n if (tokens.id_token) {\n verified = await this.verifyIdToken(tokens.id_token, stored.nonce, stored.maxAge);\n } else if ((stored.scopes ?? this.scopes()).includes('openid')) {\n // AN `openid` REQUEST WITHOUT AN ID_TOKEN IS A PROTOCOL VIOLATION, and refusing it\n // is the difference between an authenticated login and a bearer token. Without this\n // the identity below came from UserInfo alone — a bearer-authenticated endpoint\n // whose response nothing signed — and the `nonce` pulled from storage was never used.\n //\n // Asked of the SCOPES REQUESTED, not of the response: a caller who never asked for\n // `openid` is running an OAuth flow and is not held to an OIDC rule.\n throw new AuthenticationError(\n 'An openid login returned no id_token, so the identity could not be verified.',\n );\n } else if (typeof stored.maxAge === 'number') {\n // No id_token means no auth_time, and therefore no evidence the demanded\n // re-authentication happened. Accepting that silently is the whole defect.\n throw new AuthenticationError(\n 'A max_age was requested but no id_token was returned, so the authentication age could not be verified.',\n );\n }\n\n const user = await this.identityFrom(tokens, verified);\n\n if (stored.organization !== undefined && user.organizationId !== stored.organization) {\n throw new AuthenticationError(\n `The sign-in was bound to organization ${stored.organization}, but the tokens are for ${user.organizationId ?? 'no organization'}. The instance may not support organization selection.`,\n );\n }\n\n return user;\n }\n\n /**\n * Turn a token response into the verified {@link CboxUser} every flow returns.\n *\n * Shared by the browser flow and the device flow rather than written twice: the checks\n * in here — the UserInfo subject matching the id_token's, the merge that lets UserInfo\n * enrich but never overwrite signed claims — are the ones that decide whether an\n * identity is trustworthy. A second copy is a second place for one of them to be\n * quietly missing, and the flow it was missing from would look exactly as correct.\n */\n private async identityFrom(\n tokens: TokenResponse,\n verified: Record<string, unknown>,\n ): Promise<CboxUser> {\n const profile = await this.userinfo(tokens.access_token);\n\n // OIDC Core §5.3.2: the UserInfo `sub` MUST match the id_token's, and when it does\n // not the response MUST NOT be used. UserInfo is fetched with a bearer token and its\n // body carries no signature, so without this an IdP — or anything able to answer as\n // one — hands back `{\"sub\":\"somebody-else\"}` and the SDK returns it as the identity.\n const verifiedSub = verified['sub'];\n const profileSub = profile['sub'];\n\n if (typeof verifiedSub === 'string' && typeof profileSub === 'string' && !timingSafeEqualString(verifiedSub, profileSub)) {\n throw new AuthenticationError('The UserInfo subject does not match the verified id_token.');\n }\n\n // ENRICHES, NEVER REPLACES. UserInfo fills in what a minimal id_token omits (email,\n // name, org) and the verified claims go back on top, so the merge cannot move `sub`,\n // `iss`, `aud`, `nonce` or anything else the signature covered. The spread used to run\n // the other way round.\n const claims: Record<string, unknown> = { ...profile, ...verified };\n\n const sub = claims['sub'];\n if (typeof sub !== 'string' || sub === '') {\n throw new AuthenticationError('The verified token carried no subject.');\n }\n\n const organizations = parseOrganizations(claims['organizations']);\n\n return {\n id: sub,\n email: typeof claims['email'] === 'string' ? claims['email'] : null,\n name: typeof claims['name'] === 'string' ? claims['name'] : null,\n organizationId: typeof claims['org'] === 'string' ? claims['org'] : null,\n organization: organization(claims),\n roles: roles(claims),\n permissions: permissions(claims),\n actor: actor(claims),\n sessionId: sessionId(claims),\n // Only present when the instance emitted the claim (exactOptionalPropertyTypes).\n ...(organizations ? { organizations } : {}),\n claims,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? null,\n idToken: tokens.id_token ?? null,\n expiresIn: typeof tokens.expires_in === 'number' ? tokens.expires_in : 0,\n };\n }\n\n /**\n * Start the device authorization grant (RFC 8628) — the flow for a program with no\n * browser of its own: a CLI, a CI job, a container, a TV.\n *\n * Print `userCode` and `verificationUri`, then call\n * {@link CboxIdClient.pollDeviceToken}. If the machine has a desktop you may also open\n * `verificationUriComplete`, which fills the code in — but print the code anyway: the\n * machine running your program is often not the one the person is looking at.\n *\n * The scopes are bounded by what the app is REGISTERED for. A device request naming one\n * outside that ceiling is refused with `invalid_scope` rather than quietly reduced,\n * because no browser is in front of it to notice a smaller grant.\n *\n * @throws ConfigurationError when the instance advertises no device endpoint.\n */\n async requestDeviceAuthorization(scopes?: string[]): Promise<DeviceAuthorization> {\n const endpoint = await this.discovery.optionalEndpoint('device_authorization_endpoint');\n\n if (endpoint === null) {\n throw new ConfigurationError(\n 'This instance does not advertise a device_authorization_endpoint, so it does not support CLI sign-in.',\n );\n }\n\n const body = new URLSearchParams({\n client_id: this.config.clientId,\n scope: (scopes ?? this.scopes()).join(' '),\n });\n if (this.config.clientSecret) {\n body.set('client_secret', this.config.clientSecret);\n }\n\n const response = await this.post(endpoint, body);\n if (!response.ok) {\n throw await oauthError(response, 'Device authorization request failed');\n }\n\n const payload = (await response.json()) as Record<string, unknown>;\n\n const deviceCode = payload['device_code'];\n const userCode = payload['user_code'];\n const verificationUri = payload['verification_uri'];\n\n if (typeof deviceCode !== 'string' || typeof userCode !== 'string' || typeof verificationUri !== 'string') {\n throw new AuthenticationError('The device authorization response was incomplete.');\n }\n\n return {\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete:\n typeof payload['verification_uri_complete'] === 'string'\n ? payload['verification_uri_complete']\n : null,\n expiresIn: typeof payload['expires_in'] === 'number' ? payload['expires_in'] : 600,\n // RFC 8628 §3.2: absent means 5 seconds. Polling faster than the server allows is\n // answered with `slow_down`, so the default is the one the server assumes too.\n interval: typeof payload['interval'] === 'number' ? payload['interval'] : 5,\n };\n }\n\n /**\n * Poll until the person approves, and return them.\n *\n * Blocks. It honours the server's `interval`, backs off permanently by five seconds on\n * `slow_down` (RFC 8628 §3.5), and stops on the three answers that are final: they\n * declined, the code expired, or tokens arrived. Pass an `AbortSignal` to give up\n * early — a CLI should let Ctrl-C work.\n *\n * @throws AuthenticationError when the person declines or the code expires.\n */\n async pollDeviceToken(\n authorization: DeviceAuthorization,\n options: { signal?: AbortSignal } = {},\n ): Promise<CboxUser> {\n const endpoint = await this.discovery.endpoint('token_endpoint');\n const deadline = Date.now() + authorization.expiresIn * 1000;\n let intervalMs = authorization.interval * 1000;\n\n for (;;) {\n if (options.signal?.aborted === true) {\n throw new AuthenticationError('Device sign-in was cancelled.');\n }\n if (Date.now() > deadline) {\n throw new AuthenticationError(\n 'The device code expired before it was approved. Start the sign-in again.',\n );\n }\n\n await sleep(intervalMs, options.signal);\n\n const body = new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: authorization.deviceCode,\n client_id: this.config.clientId,\n });\n if (this.config.clientSecret) {\n body.set('client_secret', this.config.clientSecret);\n }\n\n const response = await this.post(endpoint, body);\n\n if (response.ok) {\n const tokens = (await response.json()) as TokenResponse;\n\n if (!tokens.access_token) {\n throw new AuthenticationError('The device token response carried no access token.');\n }\n\n // No nonce: RFC 8628 has no browser leg to carry one, so there is nothing to\n // bind. Everything else about the id_token — signature, issuer, audience,\n // expiry — is verified exactly as it is on the browser flow.\n const verified = tokens.id_token ? await this.verifyIdToken(tokens.id_token, '') : {};\n\n return await this.identityFrom(tokens, verified);\n }\n\n const payload = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const error = typeof payload['error'] === 'string' ? payload['error'] : 'invalid_request';\n\n if (error === 'authorization_pending') {\n continue;\n }\n\n if (error === 'slow_down') {\n // Permanently, not for one round: the server is telling us our rate is wrong,\n // and returning to it next tick earns the same answer forever.\n intervalMs += 5000;\n continue;\n }\n\n if (error === 'access_denied') {\n throw new AuthenticationError('Sign-in was declined.');\n }\n\n if (error === 'expired_token') {\n throw new AuthenticationError(\n 'The device code expired before it was approved. Start the sign-in again.',\n );\n }\n\n throw await oauthError(response, 'Device sign-in failed');\n }\n }\n\n /**\n * Exchange a refresh token for a fresh access token (OAuth 2.0 `refresh_token`\n * grant). Cbox ID rotates refresh tokens and detects reuse, so ALWAYS persist the\n * returned `refreshToken` and discard the one you passed — presenting a rotated\n * token again revokes the entire token family.\n *\n * @throws AuthenticationError when the refresh token is invalid, expired, or already rotated.\n */\n async refresh(refreshToken: string): Promise<RefreshedTokens> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.config.clientId,\n refresh_token: refreshToken,\n });\n if (this.config.clientSecret) {\n body.set('client_secret', this.config.clientSecret);\n }\n\n const response = await this.post(await this.discovery.endpoint('token_endpoint'), body);\n if (!response.ok) {\n // The most consequential of these: `invalid_grant` means the refresh token is\n // spent, revoked or replayed and the person must sign in again, while a 5xx or\n // `temporarily_unavailable` means the same token is still good in a moment. One\n // message string for both is what makes callers guess.\n throw await oauthError(response, 'Token refresh failed');\n }\n\n const tokens = (await response.json()) as TokenResponse;\n if (!tokens.access_token) {\n throw new AuthenticationError('The refresh response carried no access token.');\n }\n\n // VERIFIED BEFORE IT IS HANDED BACK. A refresh response carries a fresh id_token, and\n // this returned it unchecked — so an application that refreshes its session claims\n // from `refresh().idToken` accepted a forged, expired, wrong-audience or wrong-issuer\n // token, having verified only the one it got at login. The nonce is not re-checked:\n // RFC 6749 has no nonce on this leg, and OIDC Core §12.2 says an id_token from a\n // refresh need not carry one.\n if (tokens.id_token) {\n await this.verifyIdToken(tokens.id_token, '');\n }\n\n return {\n accessToken: tokens.access_token,\n // Keep the presented token when the server does not rotate (OAuth 2.0 §6);\n // callers persist this value, so it must never come back null.\n refreshToken: tokens.refresh_token ?? refreshToken,\n idToken: tokens.id_token ?? null,\n expiresIn: typeof tokens.expires_in === 'number' ? tokens.expires_in : 0,\n scope: typeof tokens.scope === 'string' ? tokens.scope : null,\n };\n }\n\n /**\n * The URL of the instance's hosted account/profile page (self-service password,\n * MFA, passkeys, sessions). A signed-in user is authenticated there by their Cbox\n * ID session; `returnTo` is passed so the page can link back to your app.\n */\n profileUrl(returnTo?: string): string {\n const base = `${this.config.issuer.replace(/\\/$/, '')}${this.accountPath()}`;\n return returnTo ? `${base}?${new URLSearchParams({ return_to: returnTo }).toString()}` : base;\n }\n\n /**\n * The hosted page where a person creates and revokes API keys for your API — Cbox ID's\n * `/account/api-keys`, preselected to this app (or `clientId`). Keys made there are\n * checked with `ApiKeyVerifier` from `@cboxdk/id-js/server`.\n *\n * `returnTo` becomes a link back to your app, honoured only for an origin the app\n * registered. `organization` picks which of the person's organizations the keys act in;\n * omitted, the page uses the one they are in.\n */\n apiKeysUrl(options: { clientId?: string; returnTo?: string; organization?: string } = {}): string {\n const params = new URLSearchParams({ client_id: options.clientId ?? this.config.clientId });\n if (options.returnTo) {\n params.set('return_to', options.returnTo);\n }\n if (options.organization) {\n params.set('organization', options.organization);\n }\n\n return `${this.config.issuer.replace(/\\/$/, '')}/account/api-keys?${params.toString()}`;\n }\n\n /**\n * The RP-initiated logout URL, or null when the instance advertises none.\n *\n * `client_id` is always sent, even without a `returnTo`: the OP validates\n * `post_logout_redirect_uri` against the registered allow-list of THAT client\n * (OIDC RP-Initiated Logout 1.0 §2). With no way to name the RP the OP cannot\n * check the list, so it silently drops the return URL and strands the user on a\n * bare \"you are signed out\" page. `idTokenHint` — the user's `id_token`, if you\n * still hold it — is the spec's other way to identify the RP, and additionally\n * tells the OP which subject is logging out.\n *\n * PASS THE HINT IF YOU WANT \"SIGN OUT EVERYWHERE\". Cbox ID revokes every session\n * the person holds only when a hint it can VERIFY names the subject holding the\n * browser; with no hint it signs this browser out and leaves their other devices\n * alone. That is deliberate: the endpoint is unauthenticated and reached by a\n * redirect, so a request carrying no proof of who it concerns could otherwise be\n * forged into ending anyone's sessions everywhere. See laravel-id UPGRADING.md\n * for 1.8.0.\n */\n async logoutUrl(returnTo?: string, idTokenHint?: string): Promise<string | null> {\n const endpoint = await this.discovery.optionalEndpoint('end_session_endpoint');\n if (!endpoint) {\n return null;\n }\n const params = new URLSearchParams({ client_id: this.config.clientId });\n if (returnTo) {\n params.set('post_logout_redirect_uri', returnTo);\n }\n if (idTokenHint) {\n params.set('id_token_hint', idTokenHint);\n }\n return `${endpoint}?${params.toString()}`;\n }\n\n /**\n * A machine (client-credentials) access token for calling Cbox ID APIs as your\n * app, not on a user's behalf. Requires a client secret.\n */\n async machineToken(options: { scopes?: string[]; resource?: string } = {}): Promise<string> {\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.config.clientId,\n client_secret: this.requireSecret(),\n });\n if (options.scopes && options.scopes.length > 0) {\n body.set('scope', options.scopes.join(' '));\n }\n if (options.resource) {\n body.set('resource', options.resource);\n }\n\n const response = await this.post(await this.discovery.endpoint('token_endpoint'), body);\n if (!response.ok) {\n throw await oauthError(response, 'Machine token request failed');\n }\n const json = (await response.json()) as { access_token?: unknown };\n if (typeof json.access_token !== 'string') {\n throw new AuthenticationError('The token response had no access_token.');\n }\n return json.access_token;\n }\n\n /** The OIDC userinfo claims for an access token. */\n async userinfo(accessToken: string): Promise<Record<string, unknown>> {\n const endpoint = await this.discovery.optionalEndpoint('userinfo_endpoint');\n if (!endpoint) {\n return {};\n }\n const response = await this.fetchWithTimeout(endpoint, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!response.ok) {\n throw await oauthError(response, 'Userinfo request failed');\n }\n return (await response.json()) as Record<string, unknown>;\n }\n\n /**\n * RFC 7662 token introspection (confidential-client auth). Returns the raw\n * response; `active` tells you if the token is currently valid. Requires a secret.\n */\n async introspect(token: string): Promise<Record<string, unknown>> {\n const endpoint = await this.discovery.endpoint('introspection_endpoint');\n const basic = btoa(`${this.config.clientId}:${this.requireSecret()}`);\n const response = await this.post(\n endpoint,\n new URLSearchParams({ token }),\n { authorization: `Basic ${basic}` },\n );\n if (!response.ok) {\n throw await oauthError(response, 'Introspection request failed');\n }\n return (await response.json()) as Record<string, unknown>;\n }\n\n /**\n * RFC 7009 token revocation. Revokes an access or refresh token; revoking a refresh\n * token drops the whole token family, so this is what a real \"sign out everywhere\" does.\n *\n * **PUBLIC CLIENTS TOO.** This demanded a secret and threw `ConfigurationError` without\n * one — so the clients that most need it were the ones that could not call it. A PKCE\n * browser or native app authenticates with `none`, holds no secret, and is exactly the\n * case where a refresh token sits in storage on a device somebody has just signed out\n * of. Cbox ID's revocation endpoint accepts a public client, and its discovery document\n * advertises `none` among the revocation auth methods; RFC 7009 §2.1 scopes every\n * revocation to the calling client, so the only capability here is \"destroy a token you\n * are already holding\". The SDK was the half saying no.\n *\n * A confidential client still authenticates with Basic; a public one names itself in the\n * body, the same shape {@link exchange} already uses for the token endpoint.\n *\n * Per RFC 7009 the server answers 200 for an unknown or already-revoked token, so\n * success means \"this token is not valid any more\", not \"it existed\".\n *\n * @param tokenTypeHint which store to search first — only a hint, never required.\n */\n async revoke(token: string, tokenTypeHint?: TokenTypeHint): Promise<void> {\n const endpoint = await this.discovery.endpoint('revocation_endpoint');\n const body = new URLSearchParams({ token, client_id: this.config.clientId });\n if (tokenTypeHint) {\n body.set('token_type_hint', tokenTypeHint);\n }\n\n const headers: Record<string, string> = {};\n if (this.config.clientSecret) {\n headers.authorization = `Basic ${btoa(`${this.config.clientId}:${this.config.clientSecret}`)}`;\n }\n\n const response = await this.post(endpoint, body, headers);\n if (!response.ok) {\n throw await oauthError(response, 'Revocation request failed');\n }\n }\n\n /** Verify a Cbox ID webhook / inline-action signature. See {@link verifyWebhook}. */\n verifyWebhook(options: VerifyWebhookOptions): Promise<boolean> {\n return verifyWebhook(options);\n }\n\n /**\n * A Token Vault client bound to an access token. Obtain the token with\n * {@link machineToken} (scope `vault.manage` to provision/grant, `vault.lease` to\n * redeem), then call `store` / `grant` / `lease` etc.\n */\n vault(accessToken: string): VaultClient {\n return new VaultClient(this.config.issuer, accessToken, this.config.timeoutMs ?? 10_000);\n }\n\n private async exchange(code: string, verifier: string, redirectUri?: string): Promise<TokenResponse> {\n const callback = redirectUri ?? this.config.redirectUri;\n\n if (callback === undefined || callback === '') {\n throw new ConfigurationError(\n 'A `redirectUri` is required to exchange an authorization code. It must be the same one the authorization request used.',\n );\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: callback,\n client_id: this.config.clientId,\n code_verifier: verifier,\n });\n if (this.config.clientSecret) {\n body.set('client_secret', this.config.clientSecret);\n }\n\n const response = await this.post(await this.discovery.endpoint('token_endpoint'), body);\n if (!response.ok) {\n throw await oauthError(response, 'Token exchange failed');\n }\n return (await response.json()) as TokenResponse;\n }\n\n private async verifyIdToken(\n idToken: string,\n // Empty skips the nonce comparison below, which is what a refresh leg needs: RFC 6749\n // has no nonce there and OIDC Core §12.2 says the id_token from a refresh need not\n // carry one. Signature, issuer, audience and expiry are checked either way.\n nonce: string,\n maxAge?: number,\n ): Promise<Record<string, unknown>> {\n const jwksUri = await this.discovery.endpoint('jwks_uri');\n let resolver = this.jwksByUri.get(jwksUri);\n if (!resolver) {\n resolver = createRemoteJWKSet(new URL(jwksUri));\n this.jwksByUri.set(jwksUri, resolver);\n }\n\n let payload: JWTPayload;\n try {\n ({ payload } = await jwtVerify(idToken, resolver, {\n issuer: this.config.issuer,\n audience: this.config.clientId,\n // Pin the accepted signature algorithms explicitly. `jose` already refuses\n // `alg: none` and key-type confusion, so this is belt-and-braces — but it\n // states the contract in code rather than relying on a library default.\n algorithms: ['RS256', 'ES256'],\n }));\n } catch (error) {\n throw new AuthenticationError(`The id_token could not be verified: ${String(error)}`);\n }\n\n if (nonce && payload['nonce'] !== nonce) {\n throw new AuthenticationError('The id_token nonce did not match — possible replay.');\n }\n\n // OIDC Core §3.1.3.7 step 12: when max_age was requested, `auth_time` is REQUIRED\n // and the relying party MUST check it. `max_age` is the control you reach for\n // before a payment or an admin grant, and the whole point is that the person\n // authenticated JUST NOW — so a login({maxAge}) whose result is never checked is a\n // step-up in name only: a day-old session comes back carrying its original\n // auth_time and nothing anywhere says so.\n if (typeof maxAge === 'number') {\n const authTime = payload['auth_time'];\n\n if (typeof authTime !== 'number') {\n throw new AuthenticationError(\n 'A max_age was requested but the id_token carried no auth_time, so the authentication age could not be verified.',\n );\n }\n\n const tolerance = this.config.authTimeToleranceSeconds ?? 60;\n const age = Math.floor(Date.now() / 1000) - authTime;\n\n if (age > maxAge + tolerance) {\n throw new AuthenticationError(\n `The authentication is ${age}s old but max_age required ${maxAge}s — the user did not re-authenticate.`,\n );\n }\n }\n\n return payload as Record<string, unknown>;\n }\n\n private scopes(): string[] {\n const scopes = this.config.scopes;\n return scopes && scopes.length > 0 ? scopes : ['openid', 'profile', 'email'];\n }\n\n private accountPath(): string {\n const path = this.config.accountPath;\n // `/account`, not `/settings`. The latter is the organization-admin page: it\n // redirects a non-admin to `/account` and drops `return_to` on the way, so a member\n // following this link arrives at the right screen having lost where they came from.\n return path && path !== '' ? `/${path.replace(/^\\//, '')}` : '/account';\n }\n\n private requireSecret(): string {\n if (!this.config.clientSecret) {\n throw new ConfigurationError('This call requires a `clientSecret`, but none is configured.');\n }\n return this.config.clientSecret;\n }\n\n private post(url: string, body: URLSearchParams, headers: Record<string, string> = {}): Promise<Response> {\n return this.fetchWithTimeout(url, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded', ...headers },\n body,\n });\n }\n\n private async fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 10_000);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/** Normalise `prompt` to a de-duplicated list, refusing what the server would refuse. */\nfunction promptValue(prompt: AuthorizationRequestOptions['prompt']): AuthorizationPrompt[] {\n if (prompt === undefined) {\n return [];\n }\n\n const values = [...new Set(typeof prompt === 'string' ? [prompt] : prompt)];\n\n // OIDC Core §3.1.2.1: `none` with any other value is an error. Failing here names the\n // call that built it; failing at the server names nothing the caller can find.\n if (values.includes('none') && values.length > 1) {\n throw new ConfigurationError(\"`prompt: 'none'` cannot be combined with another prompt value.\");\n }\n\n return values;\n}\n\n/**\n * Refuse organization options that contradict each other. Each of these would reach the\n * server as a request with two incompatible meanings and come back as a generic error\n * after a full redirect — far from the line that built it.\n */\nfunction assertOrganizationOptions(options: AuthorizationRequestOptions, prompt: AuthorizationPrompt[]): void {\n // An empty id is not \"no organization\" at the server — it is a parameter that is\n // present and names nothing. Omit the option instead.\n if (options.organization === '') {\n throw new ConfigurationError('`organization` is empty. Omit it to sign in without binding to an organization.');\n }\n if (options.organizationHint === '') {\n throw new ConfigurationError('`organizationHint` is empty. Omit it when you have no organization to suggest.');\n }\n\n if (options.organization === undefined) {\n return;\n }\n\n // `organization` binds to an existing organization; both prompts below ask the person\n // to choose or create one. Sending both leaves the server to guess which you meant.\n if (prompt.includes('select_organization')) {\n throw new ConfigurationError(\n \"`organization` binds the sign-in to one organization, so `prompt: 'select_organization'` has nothing to choose. Use `organizationHint` to preselect an organization in the picker instead.\",\n );\n }\n if (prompt.includes('create_organization')) {\n throw new ConfigurationError(\n \"`organization` binds the sign-in to an existing organization and `prompt: 'create_organization'` creates a new one. Send one or the other.\",\n );\n }\n}\n\n/** Constant-time string compare, so state validation doesn't leak via timing. */\nfunction timingSafeEqualString(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n let mismatch = 0;\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return mismatch === 0;\n}\n\n/**\n * Parse an `organizations` claim into typed orgs, ignoring anything malformed. The\n * claim is optional and instance-specific; absent or non-array → undefined, so\n * callers can tell \"no claim\" from \"empty\".\n */\nfunction parseOrganizations(claim: unknown): CboxOrganization[] | undefined {\n if (!Array.isArray(claim)) {\n return undefined;\n }\n const orgs: CboxOrganization[] = [];\n for (const entry of claim) {\n if (typeof entry !== 'object' || entry === null) {\n continue;\n }\n const record = entry as Record<string, unknown>;\n const id = record['id'];\n const name = record['name'];\n if (typeof id === 'string' && id !== '' && typeof name === 'string' && name !== '') {\n orgs.push({ id, name, role: typeof record['role'] === 'string' ? record['role'] : null });\n }\n }\n return orgs;\n}\n\n/**\n * Wait, but stay interruptible.\n *\n * A CLI that ignores Ctrl-C for five seconds at a time feels broken, and a bare\n * `setTimeout` promise cannot be cancelled — so the abort is wired to the same timer\n * rather than checked after it.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted === true) {\n reject(new AuthenticationError('Device sign-in was cancelled.'));\n return;\n }\n\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new AuthenticationError('Device sign-in was cancelled.'));\n };\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n"]}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { ConfigurationError, assertSecureIssuer, AuthenticationError, oauthError } from './chunk-ASOQN47M.js';
|
|
2
|
+
|
|
3
|
+
// src/api-keys.ts
|
|
4
|
+
var MAX_API_KEY_CACHE_TTL_MS = 6e4;
|
|
5
|
+
var MAX_CACHE_ENTRIES = 1e3;
|
|
6
|
+
var ORGANIZATION_ROLES = /* @__PURE__ */ new Set([
|
|
7
|
+
"owner",
|
|
8
|
+
"admin",
|
|
9
|
+
"developer",
|
|
10
|
+
"member",
|
|
11
|
+
"viewer"
|
|
12
|
+
]);
|
|
13
|
+
var ApiKeyVerifier = class {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
if (typeof globalThis.document !== "undefined") {
|
|
17
|
+
throw new ConfigurationError(
|
|
18
|
+
"API key verification uses your client secret and must run on your server, never in a browser."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (!config.issuer || !config.clientId || !config.clientSecret) {
|
|
22
|
+
throw new ConfigurationError("Verifying API keys needs issuer, clientId and clientSecret.");
|
|
23
|
+
}
|
|
24
|
+
assertSecureIssuer(config.issuer);
|
|
25
|
+
const cacheTtlMs = config.cacheTtlMs ?? 0;
|
|
26
|
+
if (!Number.isFinite(cacheTtlMs) || cacheTtlMs < 0 || cacheTtlMs > MAX_API_KEY_CACHE_TTL_MS) {
|
|
27
|
+
throw new ConfigurationError(
|
|
28
|
+
`cacheTtlMs must be between 0 and ${MAX_API_KEY_CACHE_TTL_MS}: a cached answer keeps a revoked key working for that long.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
this.endpoint = `${config.issuer.replace(/\/$/, "")}/oauth/api-keys/verify`;
|
|
32
|
+
this.timeoutMs = config.timeoutMs ?? 1e4;
|
|
33
|
+
this.cacheTtlMs = cacheTtlMs;
|
|
34
|
+
}
|
|
35
|
+
config;
|
|
36
|
+
endpoint;
|
|
37
|
+
timeoutMs;
|
|
38
|
+
cacheTtlMs;
|
|
39
|
+
cache = /* @__PURE__ */ new Map();
|
|
40
|
+
/**
|
|
41
|
+
* Ask Cbox ID whether `key` is good for this app, and what it may do.
|
|
42
|
+
*
|
|
43
|
+
* Resolves `{ active: false }` for every kind of bad key, without a request for an empty
|
|
44
|
+
* one. Throws `AuthenticationError` when the call itself fails (wrong client
|
|
45
|
+
* credentials, an unreachable instance) or when the answer cannot be trusted — one that
|
|
46
|
+
* names another app's `client_id`, or does not have the promised shape. Both mean "do
|
|
47
|
+
* not let this request in"; the difference is that a throw is something to look at.
|
|
48
|
+
*/
|
|
49
|
+
async verifyApiKey(key) {
|
|
50
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
51
|
+
return { active: false };
|
|
52
|
+
}
|
|
53
|
+
const cacheKey = this.cacheTtlMs > 0 ? await digest(key) : null;
|
|
54
|
+
if (cacheKey !== null) {
|
|
55
|
+
const hit = this.cache.get(cacheKey);
|
|
56
|
+
if (hit && hit.until > Date.now()) {
|
|
57
|
+
return hit.answer;
|
|
58
|
+
}
|
|
59
|
+
this.cache.delete(cacheKey);
|
|
60
|
+
}
|
|
61
|
+
const answer = this.parse(await this.request(key));
|
|
62
|
+
if (cacheKey !== null && answer.active) {
|
|
63
|
+
this.remember(cacheKey, answer);
|
|
64
|
+
}
|
|
65
|
+
return answer;
|
|
66
|
+
}
|
|
67
|
+
async request(key) {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
70
|
+
let response;
|
|
71
|
+
try {
|
|
72
|
+
response = await fetch(this.endpoint, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: {
|
|
75
|
+
authorization: `Basic ${btoa(`${this.config.clientId}:${this.config.clientSecret}`)}`,
|
|
76
|
+
accept: "application/json",
|
|
77
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
78
|
+
},
|
|
79
|
+
body: new URLSearchParams({ key }),
|
|
80
|
+
signal: controller.signal
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new AuthenticationError(`API key verification request failed: ${String(error)}`);
|
|
84
|
+
} finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw await oauthError(response, "API key verification failed");
|
|
89
|
+
}
|
|
90
|
+
return response.json().catch(() => null);
|
|
91
|
+
}
|
|
92
|
+
parse(body) {
|
|
93
|
+
if (typeof body !== "object" || body === null) {
|
|
94
|
+
throw new AuthenticationError("API key verification returned a body that is not a JSON object.");
|
|
95
|
+
}
|
|
96
|
+
const record = body;
|
|
97
|
+
if (record["active"] !== true) {
|
|
98
|
+
return { active: false };
|
|
99
|
+
}
|
|
100
|
+
const keyId = record["key_id"];
|
|
101
|
+
const sub = record["sub"];
|
|
102
|
+
const org = record["org"];
|
|
103
|
+
const clientId = record["client_id"];
|
|
104
|
+
const expiresAt = record["expires_at"];
|
|
105
|
+
const permissions = record["permissions"];
|
|
106
|
+
if (!nonEmpty(keyId) || !nonEmpty(sub) || !nonEmpty(org) || !nonEmpty(clientId) || !Array.isArray(permissions) || expiresAt !== null && expiresAt !== void 0 && typeof expiresAt !== "string") {
|
|
107
|
+
throw new AuthenticationError("API key verification returned an active answer without the promised fields.");
|
|
108
|
+
}
|
|
109
|
+
if (clientId !== this.config.clientId) {
|
|
110
|
+
throw new AuthenticationError(
|
|
111
|
+
`API key verification answered for client ${clientId}, not this app (${this.config.clientId}); refusing it.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (typeof expiresAt === "string") {
|
|
115
|
+
const expiry = Date.parse(expiresAt);
|
|
116
|
+
if (Number.isNaN(expiry) || expiry <= Date.now()) {
|
|
117
|
+
return { active: false };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const role = record["org_role"];
|
|
121
|
+
return {
|
|
122
|
+
active: true,
|
|
123
|
+
key_id: keyId,
|
|
124
|
+
sub,
|
|
125
|
+
org,
|
|
126
|
+
org_role: typeof role === "string" && ORGANIZATION_ROLES.has(role) ? role : null,
|
|
127
|
+
permissions: permissions.filter((value) => nonEmpty(value)),
|
|
128
|
+
client_id: clientId,
|
|
129
|
+
expires_at: typeof expiresAt === "string" ? expiresAt : null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
remember(cacheKey, answer) {
|
|
133
|
+
const expiry = answer.expires_at === null ? Number.POSITIVE_INFINITY : Date.parse(answer.expires_at);
|
|
134
|
+
const until = Math.min(Date.now() + this.cacheTtlMs, expiry);
|
|
135
|
+
if (this.cache.size >= MAX_CACHE_ENTRIES) {
|
|
136
|
+
const oldest = this.cache.keys().next();
|
|
137
|
+
if (!oldest.done) {
|
|
138
|
+
this.cache.delete(oldest.value);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
this.cache.set(cacheKey, { answer, until });
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
function nonEmpty(value) {
|
|
145
|
+
return typeof value === "string" && value !== "";
|
|
146
|
+
}
|
|
147
|
+
async function digest(key) {
|
|
148
|
+
const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
|
|
149
|
+
return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { ApiKeyVerifier, MAX_API_KEY_CACHE_TTL_MS };
|
|
153
|
+
//# sourceMappingURL=chunk-WYTGLCMF.js.map
|
|
154
|
+
//# sourceMappingURL=chunk-WYTGLCMF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-keys.ts"],"names":[],"mappings":"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","file":"chunk-WYTGLCMF.js","sourcesContent":["import type { OrganizationRole } from './claims.js';\nimport { AuthenticationError, ConfigurationError, oauthError } from './errors.js';\nimport { assertSecureIssuer } from './issuer.js';\n\n/**\n * A customer API key Cbox ID vouches for: live, bound to YOUR app, and held by an active\n * member of `org`.\n *\n * The field names are the endpoint's own, so the claim helpers read it like a token —\n * `hasPermission(answer, 'invoices:create')`, `organization(answer)?.role`. (A type\n * alias rather than an interface for that reason: only an alias is assignable to the\n * helpers' record type.)\n */\nexport type ActiveApiKey = {\n active: true;\n /** The key's id — log this, never the key. */\n key_id: string;\n /** The person who holds the key (their subject id). */\n sub: string;\n /** The organization the key acts in. */\n org: string;\n /** The holder's membership tier in `org`; null for a tier this SDK does not know. */\n org_role: OrganizationRole | null;\n /**\n * What the key may do, already re-capped by Cbox ID: the key's own list intersected\n * with what the holder holds for your app right now. A demoted holder's key loses the\n * permission on the next verification, not at the next reissue.\n */\n permissions: string[];\n /** Your app's client id — always, or the answer is refused. */\n client_id: string;\n /** ISO 8601 UTC expiry, or null for a key that does not expire. */\n expires_at: string | null;\n};\n\n/**\n * Every refusal: unknown, malformed, revoked, expired, another app's key, a holder who\n * left. Cbox ID never says which, so the endpoint cannot be used to probe keys — and\n * neither does this.\n */\nexport interface InactiveApiKey {\n active: false;\n}\n\n/** What {@link ApiKeyVerifier.verifyApiKey} returns. Narrow on `active`. */\nexport type ApiKeyVerification = ActiveApiKey | InactiveApiKey;\n\n/** Configuration for an {@link ApiKeyVerifier}. */\nexport interface ApiKeyVerifierConfig {\n /** Base URL (issuer) of the Cbox ID instance. */\n issuer: string;\n /** Your app's OAuth client id — the app the keys were issued for. */\n clientId: string;\n /** Your app's client secret. Verification is a confidential-client call. */\n clientSecret: string;\n /** Timeout (ms) for the verification request. Defaults to `10000`. */\n timeoutMs?: number;\n /**\n * Cache an ACTIVE answer for this long (ms), so a burst of requests with one key costs\n * one round-trip. Off (`0`) by default and at most {@link MAX_API_KEY_CACHE_TTL_MS}:\n * while an answer is cached, a revoked key keeps working, and a demoted holder keeps\n * the permissions they lost. Never cached past the key's `expires_at`. Refusals are\n * never cached, so a key issued a moment ago works on its first request.\n */\n cacheTtlMs?: number;\n}\n\n/** The longest {@link ApiKeyVerifierConfig.cacheTtlMs} allowed: one minute. */\nexport const MAX_API_KEY_CACHE_TTL_MS = 60_000;\n\n/** Enough for a busy API's working set; oldest entries go first beyond it. */\nconst MAX_CACHE_ENTRIES = 1_000;\n\nconst ORGANIZATION_ROLES: ReadonlySet<string> = new Set<OrganizationRole>([\n 'owner',\n 'admin',\n 'developer',\n 'member',\n 'viewer',\n]);\n\n/**\n * Verifies customer API keys presented to your API: `POST {issuer}/oauth/api-keys/verify`,\n * authenticated with your app's own client credentials.\n *\n * SERVER-SIDE ONLY. It holds your client secret, so it lives in the\n * `@cboxdk/id-js/server` entry and the Next.js adapter, never in the main entry that a\n * browser bundle pulls in — and it refuses to be constructed in a browser at all.\n *\n * ```ts\n * import { ApiKeyVerifier } from '@cboxdk/id-js/server';\n * import { hasPermission } from '@cboxdk/id-js';\n *\n * const keys = new ApiKeyVerifier({ issuer, clientId, clientSecret, cacheTtlMs: 10_000 });\n *\n * const answer = await keys.verifyApiKey(request.headers.get('x-api-key') ?? '');\n * if (!answer.active) return unauthorized();\n * if (!hasPermission(answer, 'invoices:create')) return forbidden();\n * ```\n */\nexport class ApiKeyVerifier {\n private readonly endpoint: string;\n private readonly timeoutMs: number;\n private readonly cacheTtlMs: number;\n private readonly cache = new Map<string, { answer: ActiveApiKey; until: number }>();\n\n constructor(private readonly config: ApiKeyVerifierConfig) {\n // A heuristic, and enough of one: a page has a `document`, and no server runtime\n // this SDK supports (Node, Deno, Bun, edge workers) defines one.\n if (typeof (globalThis as { document?: unknown }).document !== 'undefined') {\n throw new ConfigurationError(\n 'API key verification uses your client secret and must run on your server, never in a browser.',\n );\n }\n if (!config.issuer || !config.clientId || !config.clientSecret) {\n throw new ConfigurationError('Verifying API keys needs issuer, clientId and clientSecret.');\n }\n assertSecureIssuer(config.issuer);\n\n const cacheTtlMs = config.cacheTtlMs ?? 0;\n if (!Number.isFinite(cacheTtlMs) || cacheTtlMs < 0 || cacheTtlMs > MAX_API_KEY_CACHE_TTL_MS) {\n throw new ConfigurationError(\n `cacheTtlMs must be between 0 and ${MAX_API_KEY_CACHE_TTL_MS}: a cached answer keeps a revoked key working for that long.`,\n );\n }\n\n this.endpoint = `${config.issuer.replace(/\\/$/, '')}/oauth/api-keys/verify`;\n this.timeoutMs = config.timeoutMs ?? 10_000;\n this.cacheTtlMs = cacheTtlMs;\n }\n\n /**\n * Ask Cbox ID whether `key` is good for this app, and what it may do.\n *\n * Resolves `{ active: false }` for every kind of bad key, without a request for an empty\n * one. Throws `AuthenticationError` when the call itself fails (wrong client\n * credentials, an unreachable instance) or when the answer cannot be trusted — one that\n * names another app's `client_id`, or does not have the promised shape. Both mean \"do\n * not let this request in\"; the difference is that a throw is something to look at.\n */\n async verifyApiKey(key: string): Promise<ApiKeyVerification> {\n if (typeof key !== 'string' || key.trim() === '') {\n return { active: false };\n }\n\n const cacheKey = this.cacheTtlMs > 0 ? await digest(key) : null;\n if (cacheKey !== null) {\n const hit = this.cache.get(cacheKey);\n if (hit && hit.until > Date.now()) {\n return hit.answer;\n }\n this.cache.delete(cacheKey);\n }\n\n const answer = this.parse(await this.request(key));\n\n if (cacheKey !== null && answer.active) {\n this.remember(cacheKey, answer);\n }\n\n return answer;\n }\n\n private async request(key: string): Promise<unknown> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let response: Response;\n try {\n response = await fetch(this.endpoint, {\n method: 'POST',\n headers: {\n authorization: `Basic ${btoa(`${this.config.clientId}:${this.config.clientSecret}`)}`,\n accept: 'application/json',\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ key }),\n signal: controller.signal,\n });\n } catch (error) {\n throw new AuthenticationError(`API key verification request failed: ${String(error)}`);\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n throw await oauthError(response, 'API key verification failed');\n }\n\n return response.json().catch(() => null);\n }\n\n private parse(body: unknown): ApiKeyVerification {\n if (typeof body !== 'object' || body === null) {\n throw new AuthenticationError('API key verification returned a body that is not a JSON object.');\n }\n\n const record = body as Record<string, unknown>;\n\n // Only a literal `true` lets anybody in.\n if (record['active'] !== true) {\n return { active: false };\n }\n\n const keyId = record['key_id'];\n const sub = record['sub'];\n const org = record['org'];\n const clientId = record['client_id'];\n const expiresAt = record['expires_at'];\n const permissions = record['permissions'];\n\n if (\n !nonEmpty(keyId) ||\n !nonEmpty(sub) ||\n !nonEmpty(org) ||\n !nonEmpty(clientId) ||\n !Array.isArray(permissions) ||\n (expiresAt !== null && expiresAt !== undefined && typeof expiresAt !== 'string')\n ) {\n throw new AuthenticationError('API key verification returned an active answer without the promised fields.');\n }\n\n // THE BINDING, CHECKED AGAIN HERE. Cbox ID only ever finds a key bound to the calling\n // client, so this cannot fire against a correct instance — which is exactly when a\n // check that it holds is worth having: a proxy, a misrouted base URL, or a shared\n // secret between two apps would otherwise let one app's customers into another's API.\n if (clientId !== this.config.clientId) {\n throw new AuthenticationError(\n `API key verification answered for client ${clientId}, not this app (${this.config.clientId}); refusing it.`,\n );\n }\n\n // Past its expiry by our clock is expired, whatever the instance thought a moment ago.\n if (typeof expiresAt === 'string') {\n const expiry = Date.parse(expiresAt);\n if (Number.isNaN(expiry) || expiry <= Date.now()) {\n return { active: false };\n }\n }\n\n const role = record['org_role'];\n\n return {\n active: true,\n key_id: keyId,\n sub,\n org,\n org_role: typeof role === 'string' && ORGANIZATION_ROLES.has(role) ? (role as OrganizationRole) : null,\n permissions: permissions.filter((value): value is string => nonEmpty(value)),\n client_id: clientId,\n expires_at: typeof expiresAt === 'string' ? expiresAt : null,\n };\n }\n\n private remember(cacheKey: string, answer: ActiveApiKey): void {\n const expiry = answer.expires_at === null ? Number.POSITIVE_INFINITY : Date.parse(answer.expires_at);\n const until = Math.min(Date.now() + this.cacheTtlMs, expiry);\n\n if (this.cache.size >= MAX_CACHE_ENTRIES) {\n const oldest = this.cache.keys().next();\n if (!oldest.done) {\n this.cache.delete(oldest.value);\n }\n }\n\n this.cache.set(cacheKey, { answer, until });\n }\n}\n\nfunction nonEmpty(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\n/** The cache is keyed by a hash, so live keys are not sitting in memory as plaintext. */\nasync function digest(key: string): Promise<string> {\n const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(key));\n return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n"]}
|