@masters-union/union-stack 0.1.2
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/dist/cdn/loader.v1.global.js +81 -0
- package/dist/cdn/loader.v1.global.js.map +1 -0
- package/dist/chunk-5Q2UADFS.js +344 -0
- package/dist/chunk-5Q2UADFS.js.map +1 -0
- package/dist/chunk-QE2P5WH4.cjs +346 -0
- package/dist/chunk-QE2P5WH4.cjs.map +1 -0
- package/dist/client-xFfk4uu4.d.cts +188 -0
- package/dist/client-xFfk4uu4.d.ts +188 -0
- package/dist/index.cjs +18 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/picker.cjs +489 -0
- package/dist/picker.cjs.map +1 -0
- package/dist/picker.d.cts +39 -0
- package/dist/picker.d.ts +39 -0
- package/dist/picker.js +486 -0
- package/dist/picker.js.map +1 -0
- package/dist/react.cjs +158 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +72 -0
- package/dist/react.d.ts +72 -0
- package/dist/react.js +133 -0
- package/dist/react.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/uploader.ts","../src/client.ts"],"names":[],"mappings":";AAEO,SAAS,SAAA,CACd,IAAA,EACA,OAAA,EACA,IAAA,GAAkE,EAAC,EACtD;AACb,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,WAAW,IAAA,CAAK,SAAA,IAAa,gBAAA,CAAiB,IAAA,EAAM,KAAK,MAAM,CAAA;AAAA,IAC/D,OAAO,IAAA,CAAK;AAAA,GACd;AACF;AAEA,SAAS,gBAAA,CAAiB,MAAuB,MAAA,EAA0B;AACzE,EAAA,IAAI,IAAA,KAAS,SAAA,IAAa,IAAA,KAAS,aAAA,EAAe,OAAO,IAAA;AACzD,EAAA,IAAI,IAAA,KAAS,QAAA,IAAY,MAAA,IAAU,MAAA,IAAU,KAAK,OAAO,IAAA;AACzD,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,eAAA,CAAgB,QAAgB,IAAA,EAA4B;AAC1E,EAAA,MAAM,MAAO,IAAA,EAAiE,KAAA;AAC9E,EAAA,MAAM,GAAA,GAAM,GAAA,EAAK,OAAA,IAAW,CAAA,2BAAA,EAA8B,MAAM,CAAA,CAAA;AAChE,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAA,EAAK,IAAA,IAAQ,EAAA,EAAI,WAAA,EAAY;AAE3C,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,OAAO,UAAU,MAAA,EAAQ,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,EAC5D;AACA,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,OAAO,UAAU,MAAA,EAAQ,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,EAC5D;AACA,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,OAAO,UAAU,YAAA,EAAc,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,OAAO,UAAU,YAAA,EAAc,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,MAAM,UAAU,IAAA,KAAS,gBAAA;AACzB,IAAA,OAAO,SAAA,CAAU,OAAA,GAAU,OAAA,GAAU,SAAA,EAAW,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAC,OAAA,EAAS,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,UAAU,GAAA,EAAK;AACjB,IAAA,OAAO,UAAU,QAAA,EAAU,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,MAAM,CAAA;AAAA,EAC7D;AACA,EAAA,OAAO,UAAU,QAAA,EAAU,GAAA,EAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAC9D;;;ACbA,IAAM,mBAAA,GAAsB,CAAA;AAC5B,IAAM,4BAAA,GAA+B,CAAA;AAErC,IAAM,UAAA,GAAa,CAAC,GAAA,EAAK,IAAA,EAAM,IAAI,CAAA;AAE5B,IAAM,WAAN,MAAe;AAAA,EACpB,YAAoB,GAAA,EAAmB;AAAnB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAoB;AAAA;AAAA,EAGxC,QAAA,CAAS,IAAA,EAAmB,IAAA,GAAiD,EAAC,EAAe;AAC3F,IAAA,MAAM,MAAA,GAAS,OAAO,IAAA,KAAS,WAAA,IAAe,IAAA,YAAgB,IAAA;AAC9D,IAAA,MAAM,QAAA,GACJ,IAAA,CAAK,QAAA,KACJ,MAAA,GAAU,KAAc,IAAA,GAAO,UAAA,CAAA;AAClC,IAAA,MAAM,QAAA,GACJ,IAAA,CAAK,QAAA,IAAa,IAAA,CAAc,IAAA,IAAQ,0BAAA;AAC1C,IAAA,OAAO;AAAA,MACL,UAAU,cAAA,EAAe;AAAA,MACzB,QAAA;AAAA,MACA,QAAA;AAAA,MACA,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,MAAA,EAAQ;AAAA,KACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAA,CAAO,IAAA,EAAmB,IAAA,GAAsB,EAAC,EAA0B;AAC/E,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,EAAE,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,CAAA;AACvF,IAAA,IAAA,CAAK,sBAAsB,MAAM,CAAA;AAEjC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,UAAA,CAAW,QAAQ,IAAI,CAAA;AAC/C,MAAA,MAAM,QAAQ,MAAM,IAAA,CAAK,eAAe,IAAA,EAAM,IAAA,EAAM,QAAQ,IAAI,CAAA;AAChE,MAAA,MAAM,YAAY,MAAM,IAAA,CAAK,eAAe,IAAA,CAAK,SAAA,EAAW,OAAO,IAAI,CAAA;AAEvE,MAAA,MAAM,QAAA,GAAyB;AAAA,QAC7B,GAAG,MAAA;AAAA,QACH,QAAQ,SAAA,CAAU,MAAA;AAAA,QAClB,QAAQ,SAAA,CAAU,MAAA;AAAA,QAClB,KAAK,SAAA,CAAU,GAAA;AAAA,QACf,MAAM,SAAA,CAAU,IAAA;AAAA,QAChB,UAAU,SAAA,CAAU,QAAA;AAAA,QACpB,UAAU,SAAA,CAAU,QAAA;AAAA,QACpB,MAAA,EAAQ,QAAA;AAAA,QACR,MAAM,SAAA,CAAU;AAAA,OAClB;AACA,MAAA,IAAA,CAAK,uBAAuB,QAAQ,CAAA;AACpC,MAAA,OAAO,QAAA;AAAA,IACT,SAAS,MAAA,EAAQ;AACf,MAAA,MAAM,GAAA,GAAM,eAAe,MAAM,CAAA;AACjC,MAAA,IAAA,CAAK,kBAAA,GAAqB,QAAQ,GAAG,CAAA;AAErC,MAAA,IAAK,QAAmC,SAAA,EAAW;AACjD,QAAA,IAAA,CAAK,aAAA,CAAe,MAAA,CAAiC,SAAS,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MAChF;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,MAAM,SAAA,EAAkC;AAC5C,IAAA,MAAM,KAAK,GAAA,CAAI,MAAA,EAAQ,uBAAA,EAAyB,EAAE,WAAW,CAAA;AAAA,EAC/D;AAAA;AAAA,EAGA,MAAM,iBAAA,GAA2C;AAC/C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAkB,KAAA,EAAO,uBAAuB,CAAA;AAAA,EAC9D;AAAA,EAEA,MAAc,UAAA,CAAW,MAAA,EAAoB,IAAA,EAA4C;AACvF,IAAA,OAAO,IAAA,CAAK,GAAA,CAAkB,MAAA,EAAQ,sBAAA,EAAwB;AAAA,MAC5D,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,UAAU,IAAA,CAAK;AAAA,KACjB,EAAG,KAAK,MAAM,CAAA;AAAA,EAChB;AAAA,EAEA,MAAc,cAAA,CACZ,IAAA,EACA,IAAA,EACA,QACA,IAAA,EACuB;AACvB,IAAA,MAAM,aAAa,IAAA,CAAK,UAAA;AACxB,IAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AACvB,IAAA,MAAM,cAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,eAAe,mBAAmB,CAAA;AACvE,IAAA,MAAM,UAAA,GAAa,KAAK,iBAAA,IAAqB,4BAAA;AAG7C,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,CAAA,CAAA,KAAK,CAAC,CAAA,CAAE,UAAA,EAAY,CAAA,CAAE,GAAG,CAAU,CAAC,CAAA;AAEhF,IAAA,MAAM,OAAA,GAAwB,IAAI,KAAA,CAAM,UAAU,CAAA;AAClD,IAAA,MAAM,gBAA0B,IAAI,KAAA,CAAM,UAAU,CAAA,CAAE,KAAK,CAAC,CAAA;AAC5D,IAAA,IAAI,MAAA,GAAS,CAAA;AAEb,IAAA,MAAM,aAAa,IAAA,CAAK,IAAA;AAExB,IAAA,MAAM,iBAAiB,MAAM;AAC3B,MAAA,MAAM,MAAA,GAAS,cAAc,MAAA,CAAO,CAAC,GAAG,CAAA,KAAM,CAAA,GAAI,GAAG,CAAC,CAAA;AACtD,MAAA,MAAM,YAAA,GAAe,UAAA,GAAa,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,KAAA,CAAO,MAAA,GAAS,UAAA,GAAc,GAAG,CAAC,CAAA,GAAI,GAAA;AAC/F,MAAA,MAAM,EAAA,GAAsB,EAAE,UAAA,EAAY,MAAA,EAAQ,YAAA,EAAa;AAC/D,MAAA,IAAA,CAAK,oBAAA,GAAuB,QAAQ,EAAE,CAAA;AAAA,IACxC,CAAA;AAEA,IAAA,MAAM,SAAS,YAA2B;AACxC,MAAA,OAAO,IAAA,EAAM;AACX,QAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,UAAA,MAAM,UAAU,SAAA,EAAW,2BAAA,EAA6B,EAAE,SAAA,EAAW,OAAO,CAAA;AAAA,QAC9E;AACA,QAAA,MAAM,UAAA,GAAa,MAAA,EAAA;AACnB,QAAA,IAAI,aAAa,UAAA,EAAY;AAE7B,QAAA,MAAM,KAAA,GAAA,CAAS,aAAa,CAAA,IAAK,SAAA;AACjC,QAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,SAAA,EAAW,KAAK,IAAI,CAAA;AACjD,QAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,KAAA,EAAO,GAAG,CAAA;AAEnC,QAAA,IAAI,OAAA,GAAU,CAAA;AACd,QAAA,IAAI,OAAA;AACJ,QAAA,OAAO,WAAW,UAAA,EAAY;AAC5B,UAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,YAAA,MAAM,UAAU,SAAA,EAAW,2BAAA,EAA6B,EAAE,SAAA,EAAW,OAAO,CAAA;AAAA,UAC9E;AACA,UAAA,IAAI;AACF,YAAA,IAAI,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA;AAClC,YAAA,IAAI,CAAC,GAAA,EAAK;AACR,cAAA,MAAM,SAAA,GAAY,MAAM,IAAA,CAAK,GAAA;AAAA,gBAC3B,MAAA;AAAA,gBAAQ,2BAAA;AAAA,gBACR,EAAE,SAAA,EAAW,IAAA,CAAK,SAAA,EAAW,UAAA,EAAW;AAAA,gBAAG,IAAA,CAAK;AAAA,eAClD;AACA,cAAA,GAAA,GAAM,SAAA,CAAU,GAAA;AAChB,cAAA,SAAA,CAAU,GAAA,CAAI,YAAY,GAAG,CAAA;AAAA,YAC/B;AAEA,YAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,GAAA,CAAI,QAAQ,GAAA,EAAK;AAAA,cACtC,MAAA,EAAQ,KAAA;AAAA,cACR,IAAA,EAAM,KAAA;AAAA,cACN,QAAQ,IAAA,CAAK;AAAA,aACd,CAAA,IAAK,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAE1E,YAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AAGX,cAAA,IAAI,GAAA,CAAI,WAAW,GAAA,IAAO,GAAA,CAAI,WAAW,GAAA,EAAK,SAAA,CAAU,OAAO,UAAU,CAAA;AACzE,cAAA,MAAM,SAAA,CAAU,aAAA,EAAe,CAAA,KAAA,EAAQ,UAAU,CAAA,kBAAA,EAAqB,GAAA,CAAI,MAAM,CAAA,CAAA,CAAA,EAAK,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,CAAA;AAAA,YAC7G;AACA,YAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACnC,YAAA,IAAI,CAAC,IAAA,EAAM;AACT,cAAA,MAAM,SAAA,CAAU,aAAA,EAAe,CAAA,KAAA,EAAQ,UAAU,CAAA,4BAAA,CAA8B,CAAA;AAAA,YACjF;AAEA,YAAA,OAAA,CAAQ,UAAA,GAAa,CAAC,CAAA,GAAI,EAAE,YAAY,IAAA,EAAM,IAAA,EAAM,MAAM,IAAA,EAAK;AAC/D,YAAA,aAAA,CAAc,UAAA,GAAa,CAAC,CAAA,GAAI,KAAA,CAAM,IAAA;AACtC,YAAA,cAAA,EAAe;AACf,YAAA;AAAA,UACF,SAAS,GAAA,EAAK;AACZ,YAAA,OAAA,GAAU,GAAA;AACV,YAAA,OAAA,EAAA;AACA,YAAA,IAAI,UAAU,UAAA,EAAY;AAC1B,YAAA,MAAM,KAAA,CAAM,UAAA,CAAW,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,GAAG,UAAA,CAAW,MAAA,GAAS,CAAC,CAAC,CAAC,CAAA;AAAA,UACtE;AAAA,QACF;AACA,QAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,GAAa,CAAC,CAAA,EAAG;AAC5B,UAAA,MAAM,aAAA;AAAA,YACJ,cAAA,CAAe,WAAW,SAAA,CAAU,aAAA,EAAe,QAAQ,UAAU,CAAA,cAAA,EAAiB,UAAU,CAAA,SAAA,CAAW,CAAC,CAAA;AAAA,YAC5G,IAAA,CAAK;AAAA,WACP;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,UAAU,CAAA,EAAE,EAAG,MAAM,CAAA;AAChF,IAAA,MAAM,OAAA,CAAQ,IAAI,OAAO,CAAA;AACzB,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAc,cAAA,CACZ,SAAA,EACA,KAAA,EACA,IAAA,EAC2B;AAC3B,IAAA,OAAO,IAAA,CAAK,GAAA,CAAsB,MAAA,EAAQ,0BAAA,EAA4B;AAAA,MACpE,SAAA;AAAA,MACA,KAAA,EAAO,KAAA,CAAM,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,UAAA,EAAY,CAAA,CAAE,UAAA,EAAY,IAAA,EAAM,CAAA,CAAE,IAAA,EAAK,CAAE;AAAA,KACpE,EAAG,KAAK,MAAM,CAAA;AAAA,EAChB;AAAA,EAEA,MAAc,cAAc,SAAA,EAAkC;AAC5D,IAAA,IAAI;AAAE,MAAA,MAAM,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,IAAG,CAAA,CAAA,MAAQ;AAAA,IAAe;AAAA,EAC5D;AAAA,EAEA,MAAc,GAAA,CACZ,MAAA,EACA,IAAA,EACA,MACA,MAAA,EACY;AACZ,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,KAAA,IAAS,KAAA;AACpC,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,QAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,EAAG,IAAI,CAAA,CAAA;AACzD,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,MAAM,UAAU,GAAA,EAAK;AAAA,QACzB,MAAA;AAAA,QACA,OAAA,EAAS;AAAA,UACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,CAAA;AAAA,UACxC,cAAA,EAAgB;AAAA,SAClB;AAAA,QACA,MAAM,IAAA,KAAS,KAAA,CAAA,GAAY,KAAA,CAAA,GAAY,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,QAC1D;AAAA,OACD,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAK,KAAA,EAAiB,SAAS,YAAA,EAAc;AAC3C,QAAA,MAAM,UAAU,SAAA,EAAW,kBAAA,EAAoB,EAAE,SAAA,EAAW,KAAA,EAAO,OAAO,CAAA;AAAA,MAC5E;AACA,MAAA,MAAM,UAAU,SAAA,EAAW,yBAAA,EAA2B,EAAE,SAAA,EAAW,IAAA,EAAM,OAAO,CAAA;AAAA,IAClF;AACA,IAAA,IAAI,MAAA,GAAkB,IAAA;AACtB,IAAA,IAAI;AAAE,MAAA,MAAA,GAAS,MAAM,IAAI,IAAA,EAAK;AAAA,IAAG,CAAA,CAAA,MAAQ;AAAA,IAA4B;AACrE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,QAAU,eAAA,CAAgB,GAAA,CAAI,QAAQ,MAAM,CAAA;AACrD,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;AAEA,SAAS,cAAA,GAAyB;AAEhC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,YAAA,IAAgB,MAAA,EAAQ;AAC3D,IAAA,OAAQ,MAAA,CAAwC,UAAA,EAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AAAA,EAC/E;AACA,EAAA,OAAO,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CAAA;AACrE;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAA,CAAA,KAAK,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAC3C;AAEA,SAAS,eAAe,GAAA,EAA4C;AAClE,EAAA,IAAI,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,UAAU,GAAA,IAAO,SAAA,IAAa,GAAA,IAAO,WAAA,IAAe,GAAA,EAAK;AAC7F,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,MAAM,GAAA,GAAO,KAAe,OAAA,IAAW,gBAAA;AACvC,EAAA,OAAO,UAAU,SAAA,EAAW,GAAA,EAAK,EAAE,KAAA,EAAO,KAAK,CAAA;AACjD;AAEA,SAAS,aAAA,CAAiB,KAAQ,SAAA,EAAsB;AACtD,EAAA,IAAI,OAAO,OAAO,GAAA,KAAQ,QAAA,EAAW,IAA+B,SAAA,GAAY,SAAA;AAChF,EAAA,OAAO,GAAA;AACT;;;ACxQO,IAAM,mBAAN,MAAuB;AAAA,EAS5B,YAAoB,GAAA,EAAmB;AAAnB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAClB,IAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,MAAM,SAAA,CAAU,UAAU,qBAAA,EAAuB,EAAE,SAAA,EAAW,KAAA,EAAO,CAAA;AACtF,IAAA,IAAI,CAAC,GAAA,CAAI,OAAA,EAAS,MAAM,SAAA,CAAU,UAAU,sBAAA,EAAwB,EAAE,SAAA,EAAW,KAAA,EAAO,CAAA;AACxF,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,QAAA,CAAS,GAAG,CAAA;AAChC,IAAA,IAAA,CAAK,mBAAA,GAAsB,GAAA,CAAI,kBAAA,GAC3B,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,GACpB,IAAA,CAAK,QAAA,CAAS,iBAAA,EAAkB,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,EACxD;AAAA;AAAA,EAGA,MAAA,CAAO,IAAA,EAAmB,IAAA,GAAsB,EAAC,EAA0B;AACzE,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAA,CACJ,KAAA,EACA,IAAA,GAA2B,EAAC,EACL;AACvB,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IAAK,KAAA,CAAM,WAAW,CAAA,EAAG;AAC/C,MAAA,MAAM,MAAM,SAAA,CAAU,YAAA,EAAc,mDAAmD,EAAE,SAAA,EAAW,OAAO,CAAA;AAC3G,MAAA,IAAA,CAAK,UAAU,GAAG,CAAA;AAClB,MAAA,MAAM,GAAA;AAAA,IACR;AAEA,IAAA,MAAM,SAAuB,KAAA,CAAM,GAAA;AAAA,MAAI,CAAA,CAAA,KACrC,IAAA,CAAK,QAAA,CAAS,QAAA,CAAS,CAAA,EAAG,EAAE,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU;AAAA,KAChF;AACA,IAAA,IAAA,CAAK,kBAAkB,MAAM,CAAA;AAE7B,IAAA,MAAM,gBAAgC,EAAC;AACvC,IAAA,MAAM,cAA+D,EAAC;AAItE,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,OAAO,GAAG,CAAA,KAAM;AAC1C,MAAA,IAAI;AACF,QAAA,MAAM,WAAW,MAAM,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,GAAG,IAAI,CAAA;AAEnD,QAAA,QAAA,CAAS,QAAA,GAAW,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA;AAC9B,QAAA,aAAA,CAAc,KAAK,QAAQ,CAAA;AAAA,MAC7B,SAAS,GAAA,EAAK;AACZ,QAAA,WAAA,CAAY,IAAA,CAAK,EAAE,IAAA,EAAM,MAAA,CAAO,CAAC,CAAA,EAAG,KAAA,EAAO,KAAoB,CAAA;AAAA,MACjE;AAAA,IACF,CAAC,CAAC,CAAA;AAEF,IAAA,MAAM,MAAA,GAAuB,EAAE,aAAA,EAAe,WAAA,EAAY;AAC1D,IAAA,IAAA,CAAK,eAAe,MAAM,CAAA;AAC1B,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAA,CAAO,IAAA,GAAkD,EAAC,EAAqB;AAC7E,IAAA,IAAI,IAAA,GAAgC,IAAA;AACpC,IAAA,IAAI,MAAA,GAAS,KAAA;AACb,IAAA,IAAI,cAAA;AACJ,IAAA,IAAI,aAAA;AACJ,IAAA,MAAM,WAAA,GAAc,IAAI,OAAA,CAAsB,CAAC,KAAK,GAAA,KAAQ;AAC1D,MAAA,cAAA,GAAiB,GAAA;AAAK,MAAA,aAAA,GAAgB,GAAA;AAAA,IACxC,CAAC,CAAA;AAED,IAAA,OAAO;AAAA,MACL,MAAM,YAAY;AAChB,QAAA,IAAI,QAAQ,OAAO,WAAA;AACnB,QAAA,MAAA,GAAS,IAAA;AACT,QAAA,IAAI;AACF,UAAA,MAAM,GAAA,GAAM,MAAM,OAAO,aAAmB,CAAA;AAC5C,UAAA,IAAA,GAAO,GAAA,CAAI,UAAA,CAAW,IAAA,EAAM,IAAI,CAAA;AAChC,UAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,EAAK;AAC/B,UAAA,cAAA,CAAe,MAAM,CAAA;AACrB,UAAA,OAAO,MAAA;AAAA,QACT,SAAS,GAAA,EAAK;AACZ,UAAA,MAAM,CAAA,GAAK,GAAA,EAAqB,IAAA,GAC3B,GAAA,GACD,SAAA,CAAU,QAAA,EAAU,wBAAA,EAA0B,EAAE,SAAA,EAAW,KAAA,EAAO,KAAA,EAAO,GAAA,EAAK,CAAA;AAClF,UAAA,aAAA,CAAc,CAAC,CAAA;AACf,UAAA,MAAM,CAAA;AAAA,QACR;AAAA,MACF,CAAA;AAAA,MACA,OAAO,MAAM;AAAE,QAAA,IAAA,EAAM,KAAA,EAAM;AAAA,MAAG,CAAA;AAAA,MAC9B,QAAQ,MAAM;AAAE,QAAA,IAAA,EAAM,MAAA,EAAO;AAAA,MAAG;AAAA,KAClC;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,MAAA,GAAiC;AAAE,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,EAAK;AAC1D","file":"chunk-5Q2UADFS.js","sourcesContent":["import type { UploadError, UploadErrorCode } from './types.js';\n\nexport function makeError(\n code: UploadErrorCode,\n message: string,\n opts: { status?: number; retryable?: boolean; cause?: unknown } = {}\n): UploadError {\n return {\n code,\n message,\n status: opts.status,\n retryable: opts.retryable ?? defaultRetryable(code, opts.status),\n cause: opts.cause,\n };\n}\n\nfunction defaultRetryable(code: UploadErrorCode, status?: number): boolean {\n if (code === 'NETWORK' || code === 'PART_FAILED') return true;\n if (code === 'SERVER' && status && status >= 500) return true;\n return false;\n}\n\n/** Map an HTTP error response from the cdn-be API to an UploadError. */\nexport function fromApiResponse(status: number, body: unknown): UploadError {\n const err = (body as { error?: { code?: string; message?: string } } | null)?.error;\n const msg = err?.message || `Request failed with status ${status}`;\n const code = (err?.code || '').toUpperCase();\n\n if (status === 401) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 403) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 413) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 415) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 429) {\n const isQuota = code === 'QUOTA_EXCEEDED';\n return makeError(isQuota ? 'QUOTA' : 'NETWORK', msg, { status, retryable: !isQuota });\n }\n if (status >= 500) {\n return makeError('SERVER', msg, { status, retryable: true });\n }\n return makeError('SERVER', msg, { status, retryable: false });\n}\n","import type {\n ClientConfig,\n PickedFile,\n PickerConfig,\n UploadedFile,\n UploadOptions,\n ProgressEvent as USProgressEvent,\n} from './types.js';\nimport { fromApiResponse, makeError } from './errors.js';\n\ninterface InitResponse {\n sessionId: string;\n fileId: string;\n chunkSize: number;\n totalParts: number;\n partUrls: Array<{ partNumber: number; url: string }>;\n expiresAt: string;\n}\n\ninterface CompleteResponse {\n handle: string;\n fileId: string;\n url: string;\n filename: string;\n mimetype: string;\n size: number;\n etag?: string;\n}\n\ninterface PartResult {\n partNumber: number;\n etag: string;\n size: number;\n}\n\nconst DEFAULT_CONCURRENCY = 3;\nconst DEFAULT_MAX_RETRIES_PER_PART = 3;\n// Backoff schedule between part retries. Capped — past this we surrender.\nconst BACKOFF_MS = [400, 1200, 3600];\n\nexport class Uploader {\n constructor(private cfg: ClientConfig) {}\n\n /** Returns a stable PickedFile descriptor for the input blob/file. */\n describe(file: File | Blob, opts: { filename?: string; mimeType?: string } = {}): PickedFile {\n const isFile = typeof File !== 'undefined' && file instanceof File;\n const filename =\n opts.filename ||\n (isFile ? (file as File).name : 'untitled');\n const mimetype =\n opts.mimeType || (file as Blob).type || 'application/octet-stream';\n return {\n uploadId: cryptoRandomId(),\n filename,\n mimetype,\n size: file.size,\n source: 'local',\n };\n }\n\n /**\n * Upload a single Blob/File. Returns the persisted UploadedFile.\n * Fires Filestack-style per-file callbacks from `opts`.\n */\n async upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n const picked = this.describe(file, { filename: opts.filename, mimeType: opts.mimeType });\n opts.onFileUploadStarted?.(picked);\n\n try {\n const init = await this.initUpload(picked, opts);\n const parts = await this.uploadAllParts(file, init, picked, opts);\n const completed = await this.completeUpload(init.sessionId, parts, opts);\n\n const uploaded: UploadedFile = {\n ...picked,\n handle: completed.handle,\n fileId: completed.fileId,\n url: completed.url,\n size: completed.size,\n mimetype: completed.mimetype,\n filename: completed.filename,\n status: 'Stored',\n etag: completed.etag,\n };\n opts.onFileUploadFinished?.(uploaded);\n return uploaded;\n } catch (rawErr) {\n const err = normalizeError(rawErr);\n opts.onFileUploadFailed?.(picked, err);\n // Best effort: tell the server to clean up the multipart, but ignore failures.\n if ((rawErr as { sessionId?: string })?.sessionId) {\n this.abortSilently((rawErr as { sessionId: string }).sessionId).catch(() => {});\n }\n throw err;\n }\n }\n\n /** Cancel an in-flight session server-side. */\n async abort(sessionId: string): Promise<void> {\n await this.api('POST', '/sdk/v1/uploads/abort', { sessionId });\n }\n\n /** Fetch the server-managed picker config (branding, theme, constraints). */\n async fetchPickerConfig(): Promise<PickerConfig> {\n return this.api<PickerConfig>('GET', '/sdk/v1/picker-config');\n }\n\n private async initUpload(picked: PickedFile, opts: UploadOptions): Promise<InitResponse> {\n return this.api<InitResponse>('POST', '/sdk/v1/uploads/init', {\n filename: picked.filename,\n mimeType: picked.mimetype,\n size: picked.size,\n metadata: opts.metadata,\n }, opts.signal);\n }\n\n private async uploadAllParts(\n file: File | Blob,\n init: InitResponse,\n picked: PickedFile,\n opts: UploadOptions,\n ): Promise<PartResult[]> {\n const totalParts = init.totalParts;\n const chunkSize = init.chunkSize;\n const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);\n const maxRetries = opts.maxRetriesPerPart ?? DEFAULT_MAX_RETRIES_PER_PART;\n\n // Quick lookup: partNumber → presigned URL.\n const urlByPart = new Map(init.partUrls.map(p => [p.partNumber, p.url] as const));\n\n const results: PartResult[] = new Array(totalParts);\n const loadedPerPart: number[] = new Array(totalParts).fill(0);\n let cursor = 1;\n\n const totalBytes = file.size;\n\n const reportProgress = () => {\n const loaded = loadedPerPart.reduce((a, b) => a + b, 0);\n const totalPercent = totalBytes > 0 ? Math.min(100, Math.round((loaded / totalBytes) * 100)) : 100;\n const ev: USProgressEvent = { totalBytes, loaded, totalPercent };\n opts.onFileUploadProgress?.(picked, ev);\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n const partNumber = cursor++;\n if (partNumber > totalParts) return;\n\n const start = (partNumber - 1) * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const chunk = file.slice(start, end);\n\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= maxRetries) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n try {\n let url = urlByPart.get(partNumber);\n if (!url) {\n const refreshed = await this.api<{ url: string }>(\n 'POST', '/sdk/v1/uploads/sign-part',\n { sessionId: init.sessionId, partNumber }, opts.signal,\n );\n url = refreshed.url;\n urlByPart.set(partNumber, url);\n }\n\n const res = await this.cfg.fetch?.(url, {\n method: 'PUT',\n body: chunk,\n signal: opts.signal,\n }) ?? await fetch(url, { method: 'PUT', body: chunk, signal: opts.signal });\n\n if (!res.ok) {\n // If R2 rejects with 403 the URL likely expired — drop it so the\n // next attempt re-signs.\n if (res.status === 403 || res.status === 401) urlByPart.delete(partNumber);\n throw makeError('PART_FAILED', `Part ${partNumber} PUT failed (HTTP ${res.status})`, { status: res.status });\n }\n const etag = res.headers.get('etag');\n if (!etag) {\n throw makeError('PART_FAILED', `Part ${partNumber}: R2 did not return an ETag.`);\n }\n\n results[partNumber - 1] = { partNumber, etag, size: chunk.size };\n loadedPerPart[partNumber - 1] = chunk.size;\n reportProgress();\n break;\n } catch (err) {\n lastErr = err;\n attempt++;\n if (attempt > maxRetries) break;\n await sleep(BACKOFF_MS[Math.min(attempt - 1, BACKOFF_MS.length - 1)]);\n }\n }\n if (!results[partNumber - 1]) {\n throw withSessionId(\n normalizeError(lastErr ?? makeError('PART_FAILED', `Part ${partNumber} failed after ${maxRetries} retries.`)),\n init.sessionId,\n );\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, totalParts) }, worker);\n await Promise.all(workers);\n return results;\n }\n\n private async completeUpload(\n sessionId: string,\n parts: PartResult[],\n opts: UploadOptions,\n ): Promise<CompleteResponse> {\n return this.api<CompleteResponse>('POST', '/sdk/v1/uploads/complete', {\n sessionId,\n parts: parts.map(p => ({ partNumber: p.partNumber, etag: p.etag })),\n }, opts.signal);\n }\n\n private async abortSilently(sessionId: string): Promise<void> {\n try { await this.abort(sessionId); } catch { /* ignore */ }\n }\n\n private async api<T>(\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const fetchImpl = this.cfg.fetch ?? fetch;\n const url = `${this.cfg.apiBase.replace(/\\/$/, '')}${path}`;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.cfg.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (cause) {\n if ((cause as Error)?.name === 'AbortError') {\n throw makeError('ABORTED', 'Request aborted.', { retryable: false, cause });\n }\n throw makeError('NETWORK', 'Network request failed.', { retryable: true, cause });\n }\n let parsed: unknown = null;\n try { parsed = await res.json(); } catch { /* tolerate empty body */ }\n if (!res.ok) throw fromApiResponse(res.status, parsed);\n return parsed as T;\n }\n}\n\nfunction cryptoRandomId(): string {\n // Cheap unique id for client-side uploadId. Not security-sensitive.\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction normalizeError(err: unknown): ReturnType<typeof makeError> {\n if (err && typeof err === 'object' && 'code' in err && 'message' in err && 'retryable' in err) {\n return err as ReturnType<typeof makeError>;\n }\n const msg = (err as Error)?.message || 'Upload failed.';\n return makeError('NETWORK', msg, { cause: err });\n}\n\nfunction withSessionId<T>(err: T, sessionId: string): T {\n if (err && typeof err === 'object') (err as { sessionId?: string }).sessionId = sessionId;\n return err;\n}\n","import type {\n ClientConfig,\n UploadOptions,\n BatchUploadOptions,\n UploadedFile,\n PickResponse,\n PickedFile,\n PickerConfig,\n UploadError,\n} from './types.js';\nimport { Uploader } from './uploader.js';\nimport { makeError } from './errors.js';\n\n// Forward-declared minimal handle so client.ts doesn't pull in the picker bundle.\nexport interface PickerHandleLike {\n open(): Promise<PickResponse>;\n close(): void;\n cancel(): void;\n}\n\nexport class UnionStackClient {\n private uploader: Uploader;\n /**\n * Promise that resolves to the server-managed picker config. Pre-fetched on\n * construction (unless `skipConfigPrefetch: true`) so the picker opens\n * without a network hit. Fails silently — picker falls back to defaults.\n */\n pickerConfigPromise: Promise<PickerConfig | null>;\n\n constructor(private cfg: ClientConfig) {\n if (!cfg.apiKey) throw makeError('CONFIG', 'apiKey is required.', { retryable: false });\n if (!cfg.apiBase) throw makeError('CONFIG', 'apiBase is required.', { retryable: false });\n this.uploader = new Uploader(cfg);\n this.pickerConfigPromise = cfg.skipConfigPrefetch\n ? Promise.resolve(null)\n : this.uploader.fetchPickerConfig().catch(() => null);\n }\n\n /** Upload a single file. Mirrors Filestack's `client.upload()`. */\n upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n return this.uploader.upload(file, opts);\n }\n\n /**\n * Upload multiple files concurrently. Mirrors Filestack's batch behavior:\n * onUploadDone fires once even if some files failed — failures land in\n * `filesFailed`, successes in `filesUploaded`. `onError` is reserved for\n * batch-level failures (no files even started).\n */\n async uploadMany(\n files: (File | Blob)[],\n opts: BatchUploadOptions = {},\n ): Promise<PickResponse> {\n if (!Array.isArray(files) || files.length === 0) {\n const err = makeError('VALIDATION', 'uploadMany requires a non-empty array of files.', { retryable: false });\n opts.onError?.(err);\n throw err;\n }\n\n const picked: PickedFile[] = files.map(f =>\n this.uploader.describe(f, { filename: opts.filename, mimeType: opts.mimeType })\n );\n opts.onUploadStarted?.(picked);\n\n const filesUploaded: UploadedFile[] = [];\n const filesFailed: Array<{ file: PickedFile; error: UploadError }> = [];\n\n // Process per-file in parallel; the per-part concurrency from `opts` still\n // applies inside each upload.\n await Promise.all(files.map(async (f, i) => {\n try {\n const uploaded = await this.uploader.upload(f, opts);\n // Sync the uploadId so callers can correlate picked → uploaded.\n uploaded.uploadId = picked[i].uploadId;\n filesUploaded.push(uploaded);\n } catch (err) {\n filesFailed.push({ file: picked[i], error: err as UploadError });\n }\n }));\n\n const result: PickResponse = { filesUploaded, filesFailed };\n opts.onUploadDone?.(result);\n return result;\n }\n\n /**\n * Open the file picker modal. Lazy-loads the picker bundle so the core\n * SDK stays small for callers that only use `upload()` directly.\n */\n picker(opts: import('./picker/types.js').PickerOptions = {}): PickerHandleLike {\n let real: PickerHandleLike | null = null;\n let opened = false;\n let pendingResolve!: (r: PickResponse) => void;\n let pendingReject!: (e: UploadError) => void;\n const donePromise = new Promise<PickResponse>((res, rej) => {\n pendingResolve = res; pendingReject = rej;\n });\n\n return {\n open: async () => {\n if (opened) return donePromise;\n opened = true;\n try {\n const mod = await import('./picker/index.js');\n real = mod.openPicker(this, opts);\n const result = await real.open();\n pendingResolve(result);\n return result;\n } catch (err) {\n const e = (err as UploadError)?.code\n ? (err as UploadError)\n : makeError('CONFIG', 'Failed to load picker.', { retryable: false, cause: err });\n pendingReject(e);\n throw e;\n }\n },\n close: () => { real?.close(); },\n cancel: () => { real?.cancel(); },\n };\n }\n\n /** Read-only accessor used by the React/picker packages. */\n get config(): Readonly<ClientConfig> { return this.cfg; }\n}\n"]}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
function makeError(code, message, opts = {}) {
|
|
5
|
+
return {
|
|
6
|
+
code,
|
|
7
|
+
message,
|
|
8
|
+
status: opts.status,
|
|
9
|
+
retryable: opts.retryable ?? defaultRetryable(code, opts.status),
|
|
10
|
+
cause: opts.cause
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function defaultRetryable(code, status) {
|
|
14
|
+
if (code === "NETWORK" || code === "PART_FAILED") return true;
|
|
15
|
+
if (code === "SERVER" && status && status >= 500) return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function fromApiResponse(status, body) {
|
|
19
|
+
const err = body?.error;
|
|
20
|
+
const msg = err?.message || `Request failed with status ${status}`;
|
|
21
|
+
const code = (err?.code || "").toUpperCase();
|
|
22
|
+
if (status === 401) {
|
|
23
|
+
return makeError("AUTH", msg, { status, retryable: false });
|
|
24
|
+
}
|
|
25
|
+
if (status === 403) {
|
|
26
|
+
return makeError("AUTH", msg, { status, retryable: false });
|
|
27
|
+
}
|
|
28
|
+
if (status === 413) {
|
|
29
|
+
return makeError("VALIDATION", msg, { status, retryable: false });
|
|
30
|
+
}
|
|
31
|
+
if (status === 415) {
|
|
32
|
+
return makeError("VALIDATION", msg, { status, retryable: false });
|
|
33
|
+
}
|
|
34
|
+
if (status === 429) {
|
|
35
|
+
const isQuota = code === "QUOTA_EXCEEDED";
|
|
36
|
+
return makeError(isQuota ? "QUOTA" : "NETWORK", msg, { status, retryable: !isQuota });
|
|
37
|
+
}
|
|
38
|
+
if (status >= 500) {
|
|
39
|
+
return makeError("SERVER", msg, { status, retryable: true });
|
|
40
|
+
}
|
|
41
|
+
return makeError("SERVER", msg, { status, retryable: false });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/uploader.ts
|
|
45
|
+
var DEFAULT_CONCURRENCY = 3;
|
|
46
|
+
var DEFAULT_MAX_RETRIES_PER_PART = 3;
|
|
47
|
+
var BACKOFF_MS = [400, 1200, 3600];
|
|
48
|
+
var Uploader = class {
|
|
49
|
+
constructor(cfg) {
|
|
50
|
+
this.cfg = cfg;
|
|
51
|
+
}
|
|
52
|
+
/** Returns a stable PickedFile descriptor for the input blob/file. */
|
|
53
|
+
describe(file, opts = {}) {
|
|
54
|
+
const isFile = typeof File !== "undefined" && file instanceof File;
|
|
55
|
+
const filename = opts.filename || (isFile ? file.name : "untitled");
|
|
56
|
+
const mimetype = opts.mimeType || file.type || "application/octet-stream";
|
|
57
|
+
return {
|
|
58
|
+
uploadId: cryptoRandomId(),
|
|
59
|
+
filename,
|
|
60
|
+
mimetype,
|
|
61
|
+
size: file.size,
|
|
62
|
+
source: "local"
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Upload a single Blob/File. Returns the persisted UploadedFile.
|
|
67
|
+
* Fires Filestack-style per-file callbacks from `opts`.
|
|
68
|
+
*/
|
|
69
|
+
async upload(file, opts = {}) {
|
|
70
|
+
const picked = this.describe(file, { filename: opts.filename, mimeType: opts.mimeType });
|
|
71
|
+
opts.onFileUploadStarted?.(picked);
|
|
72
|
+
try {
|
|
73
|
+
const init = await this.initUpload(picked, opts);
|
|
74
|
+
const parts = await this.uploadAllParts(file, init, picked, opts);
|
|
75
|
+
const completed = await this.completeUpload(init.sessionId, parts, opts);
|
|
76
|
+
const uploaded = {
|
|
77
|
+
...picked,
|
|
78
|
+
handle: completed.handle,
|
|
79
|
+
fileId: completed.fileId,
|
|
80
|
+
url: completed.url,
|
|
81
|
+
size: completed.size,
|
|
82
|
+
mimetype: completed.mimetype,
|
|
83
|
+
filename: completed.filename,
|
|
84
|
+
status: "Stored",
|
|
85
|
+
etag: completed.etag
|
|
86
|
+
};
|
|
87
|
+
opts.onFileUploadFinished?.(uploaded);
|
|
88
|
+
return uploaded;
|
|
89
|
+
} catch (rawErr) {
|
|
90
|
+
const err = normalizeError(rawErr);
|
|
91
|
+
opts.onFileUploadFailed?.(picked, err);
|
|
92
|
+
if (rawErr?.sessionId) {
|
|
93
|
+
this.abortSilently(rawErr.sessionId).catch(() => {
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Cancel an in-flight session server-side. */
|
|
100
|
+
async abort(sessionId) {
|
|
101
|
+
await this.api("POST", "/sdk/v1/uploads/abort", { sessionId });
|
|
102
|
+
}
|
|
103
|
+
/** Fetch the server-managed picker config (branding, theme, constraints). */
|
|
104
|
+
async fetchPickerConfig() {
|
|
105
|
+
return this.api("GET", "/sdk/v1/picker-config");
|
|
106
|
+
}
|
|
107
|
+
async initUpload(picked, opts) {
|
|
108
|
+
return this.api("POST", "/sdk/v1/uploads/init", {
|
|
109
|
+
filename: picked.filename,
|
|
110
|
+
mimeType: picked.mimetype,
|
|
111
|
+
size: picked.size,
|
|
112
|
+
metadata: opts.metadata
|
|
113
|
+
}, opts.signal);
|
|
114
|
+
}
|
|
115
|
+
async uploadAllParts(file, init, picked, opts) {
|
|
116
|
+
const totalParts = init.totalParts;
|
|
117
|
+
const chunkSize = init.chunkSize;
|
|
118
|
+
const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);
|
|
119
|
+
const maxRetries = opts.maxRetriesPerPart ?? DEFAULT_MAX_RETRIES_PER_PART;
|
|
120
|
+
const urlByPart = new Map(init.partUrls.map((p) => [p.partNumber, p.url]));
|
|
121
|
+
const results = new Array(totalParts);
|
|
122
|
+
const loadedPerPart = new Array(totalParts).fill(0);
|
|
123
|
+
let cursor = 1;
|
|
124
|
+
const totalBytes = file.size;
|
|
125
|
+
const reportProgress = () => {
|
|
126
|
+
const loaded = loadedPerPart.reduce((a, b) => a + b, 0);
|
|
127
|
+
const totalPercent = totalBytes > 0 ? Math.min(100, Math.round(loaded / totalBytes * 100)) : 100;
|
|
128
|
+
const ev = { totalBytes, loaded, totalPercent };
|
|
129
|
+
opts.onFileUploadProgress?.(picked, ev);
|
|
130
|
+
};
|
|
131
|
+
const worker = async () => {
|
|
132
|
+
while (true) {
|
|
133
|
+
if (opts.signal?.aborted) {
|
|
134
|
+
throw makeError("ABORTED", "Upload aborted by caller.", { retryable: false });
|
|
135
|
+
}
|
|
136
|
+
const partNumber = cursor++;
|
|
137
|
+
if (partNumber > totalParts) return;
|
|
138
|
+
const start = (partNumber - 1) * chunkSize;
|
|
139
|
+
const end = Math.min(start + chunkSize, file.size);
|
|
140
|
+
const chunk = file.slice(start, end);
|
|
141
|
+
let attempt = 0;
|
|
142
|
+
let lastErr;
|
|
143
|
+
while (attempt <= maxRetries) {
|
|
144
|
+
if (opts.signal?.aborted) {
|
|
145
|
+
throw makeError("ABORTED", "Upload aborted by caller.", { retryable: false });
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
let url = urlByPart.get(partNumber);
|
|
149
|
+
if (!url) {
|
|
150
|
+
const refreshed = await this.api(
|
|
151
|
+
"POST",
|
|
152
|
+
"/sdk/v1/uploads/sign-part",
|
|
153
|
+
{ sessionId: init.sessionId, partNumber },
|
|
154
|
+
opts.signal
|
|
155
|
+
);
|
|
156
|
+
url = refreshed.url;
|
|
157
|
+
urlByPart.set(partNumber, url);
|
|
158
|
+
}
|
|
159
|
+
const res = await this.cfg.fetch?.(url, {
|
|
160
|
+
method: "PUT",
|
|
161
|
+
body: chunk,
|
|
162
|
+
signal: opts.signal
|
|
163
|
+
}) ?? await fetch(url, { method: "PUT", body: chunk, signal: opts.signal });
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
if (res.status === 403 || res.status === 401) urlByPart.delete(partNumber);
|
|
166
|
+
throw makeError("PART_FAILED", `Part ${partNumber} PUT failed (HTTP ${res.status})`, { status: res.status });
|
|
167
|
+
}
|
|
168
|
+
const etag = res.headers.get("etag");
|
|
169
|
+
if (!etag) {
|
|
170
|
+
throw makeError("PART_FAILED", `Part ${partNumber}: R2 did not return an ETag.`);
|
|
171
|
+
}
|
|
172
|
+
results[partNumber - 1] = { partNumber, etag, size: chunk.size };
|
|
173
|
+
loadedPerPart[partNumber - 1] = chunk.size;
|
|
174
|
+
reportProgress();
|
|
175
|
+
break;
|
|
176
|
+
} catch (err) {
|
|
177
|
+
lastErr = err;
|
|
178
|
+
attempt++;
|
|
179
|
+
if (attempt > maxRetries) break;
|
|
180
|
+
await sleep(BACKOFF_MS[Math.min(attempt - 1, BACKOFF_MS.length - 1)]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!results[partNumber - 1]) {
|
|
184
|
+
throw withSessionId(
|
|
185
|
+
normalizeError(lastErr ?? makeError("PART_FAILED", `Part ${partNumber} failed after ${maxRetries} retries.`)),
|
|
186
|
+
init.sessionId
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const workers = Array.from({ length: Math.min(concurrency, totalParts) }, worker);
|
|
192
|
+
await Promise.all(workers);
|
|
193
|
+
return results;
|
|
194
|
+
}
|
|
195
|
+
async completeUpload(sessionId, parts, opts) {
|
|
196
|
+
return this.api("POST", "/sdk/v1/uploads/complete", {
|
|
197
|
+
sessionId,
|
|
198
|
+
parts: parts.map((p) => ({ partNumber: p.partNumber, etag: p.etag }))
|
|
199
|
+
}, opts.signal);
|
|
200
|
+
}
|
|
201
|
+
async abortSilently(sessionId) {
|
|
202
|
+
try {
|
|
203
|
+
await this.abort(sessionId);
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async api(method, path, body, signal) {
|
|
208
|
+
const fetchImpl = this.cfg.fetch ?? fetch;
|
|
209
|
+
const url = `${this.cfg.apiBase.replace(/\/$/, "")}${path}`;
|
|
210
|
+
let res;
|
|
211
|
+
try {
|
|
212
|
+
res = await fetchImpl(url, {
|
|
213
|
+
method,
|
|
214
|
+
headers: {
|
|
215
|
+
Authorization: `Bearer ${this.cfg.apiKey}`,
|
|
216
|
+
"Content-Type": "application/json"
|
|
217
|
+
},
|
|
218
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
219
|
+
signal
|
|
220
|
+
});
|
|
221
|
+
} catch (cause) {
|
|
222
|
+
if (cause?.name === "AbortError") {
|
|
223
|
+
throw makeError("ABORTED", "Request aborted.", { retryable: false, cause });
|
|
224
|
+
}
|
|
225
|
+
throw makeError("NETWORK", "Network request failed.", { retryable: true, cause });
|
|
226
|
+
}
|
|
227
|
+
let parsed = null;
|
|
228
|
+
try {
|
|
229
|
+
parsed = await res.json();
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
if (!res.ok) throw fromApiResponse(res.status, parsed);
|
|
233
|
+
return parsed;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
function cryptoRandomId() {
|
|
237
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
238
|
+
return crypto.randomUUID().replace(/-/g, "");
|
|
239
|
+
}
|
|
240
|
+
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
241
|
+
}
|
|
242
|
+
function sleep(ms) {
|
|
243
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
244
|
+
}
|
|
245
|
+
function normalizeError(err) {
|
|
246
|
+
if (err && typeof err === "object" && "code" in err && "message" in err && "retryable" in err) {
|
|
247
|
+
return err;
|
|
248
|
+
}
|
|
249
|
+
const msg = err?.message || "Upload failed.";
|
|
250
|
+
return makeError("NETWORK", msg, { cause: err });
|
|
251
|
+
}
|
|
252
|
+
function withSessionId(err, sessionId) {
|
|
253
|
+
if (err && typeof err === "object") err.sessionId = sessionId;
|
|
254
|
+
return err;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/client.ts
|
|
258
|
+
var UnionStackClient = class {
|
|
259
|
+
constructor(cfg) {
|
|
260
|
+
this.cfg = cfg;
|
|
261
|
+
if (!cfg.apiKey) throw makeError("CONFIG", "apiKey is required.", { retryable: false });
|
|
262
|
+
if (!cfg.apiBase) throw makeError("CONFIG", "apiBase is required.", { retryable: false });
|
|
263
|
+
this.uploader = new Uploader(cfg);
|
|
264
|
+
this.pickerConfigPromise = cfg.skipConfigPrefetch ? Promise.resolve(null) : this.uploader.fetchPickerConfig().catch(() => null);
|
|
265
|
+
}
|
|
266
|
+
/** Upload a single file. Mirrors Filestack's `client.upload()`. */
|
|
267
|
+
upload(file, opts = {}) {
|
|
268
|
+
return this.uploader.upload(file, opts);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Upload multiple files concurrently. Mirrors Filestack's batch behavior:
|
|
272
|
+
* onUploadDone fires once even if some files failed — failures land in
|
|
273
|
+
* `filesFailed`, successes in `filesUploaded`. `onError` is reserved for
|
|
274
|
+
* batch-level failures (no files even started).
|
|
275
|
+
*/
|
|
276
|
+
async uploadMany(files, opts = {}) {
|
|
277
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
278
|
+
const err = makeError("VALIDATION", "uploadMany requires a non-empty array of files.", { retryable: false });
|
|
279
|
+
opts.onError?.(err);
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
const picked = files.map(
|
|
283
|
+
(f) => this.uploader.describe(f, { filename: opts.filename, mimeType: opts.mimeType })
|
|
284
|
+
);
|
|
285
|
+
opts.onUploadStarted?.(picked);
|
|
286
|
+
const filesUploaded = [];
|
|
287
|
+
const filesFailed = [];
|
|
288
|
+
await Promise.all(files.map(async (f, i) => {
|
|
289
|
+
try {
|
|
290
|
+
const uploaded = await this.uploader.upload(f, opts);
|
|
291
|
+
uploaded.uploadId = picked[i].uploadId;
|
|
292
|
+
filesUploaded.push(uploaded);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
filesFailed.push({ file: picked[i], error: err });
|
|
295
|
+
}
|
|
296
|
+
}));
|
|
297
|
+
const result = { filesUploaded, filesFailed };
|
|
298
|
+
opts.onUploadDone?.(result);
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Open the file picker modal. Lazy-loads the picker bundle so the core
|
|
303
|
+
* SDK stays small for callers that only use `upload()` directly.
|
|
304
|
+
*/
|
|
305
|
+
picker(opts = {}) {
|
|
306
|
+
let real = null;
|
|
307
|
+
let opened = false;
|
|
308
|
+
let pendingResolve;
|
|
309
|
+
let pendingReject;
|
|
310
|
+
const donePromise = new Promise((res, rej) => {
|
|
311
|
+
pendingResolve = res;
|
|
312
|
+
pendingReject = rej;
|
|
313
|
+
});
|
|
314
|
+
return {
|
|
315
|
+
open: async () => {
|
|
316
|
+
if (opened) return donePromise;
|
|
317
|
+
opened = true;
|
|
318
|
+
try {
|
|
319
|
+
const mod = await import('./picker.cjs');
|
|
320
|
+
real = mod.openPicker(this, opts);
|
|
321
|
+
const result = await real.open();
|
|
322
|
+
pendingResolve(result);
|
|
323
|
+
return result;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
const e = err?.code ? err : makeError("CONFIG", "Failed to load picker.", { retryable: false, cause: err });
|
|
326
|
+
pendingReject(e);
|
|
327
|
+
throw e;
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
close: () => {
|
|
331
|
+
real?.close();
|
|
332
|
+
},
|
|
333
|
+
cancel: () => {
|
|
334
|
+
real?.cancel();
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/** Read-only accessor used by the React/picker packages. */
|
|
339
|
+
get config() {
|
|
340
|
+
return this.cfg;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
exports.UnionStackClient = UnionStackClient;
|
|
345
|
+
//# sourceMappingURL=chunk-QE2P5WH4.cjs.map
|
|
346
|
+
//# sourceMappingURL=chunk-QE2P5WH4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/uploader.ts","../src/client.ts"],"names":[],"mappings":"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","file":"chunk-QE2P5WH4.cjs","sourcesContent":["import type { UploadError, UploadErrorCode } from './types.js';\n\nexport function makeError(\n code: UploadErrorCode,\n message: string,\n opts: { status?: number; retryable?: boolean; cause?: unknown } = {}\n): UploadError {\n return {\n code,\n message,\n status: opts.status,\n retryable: opts.retryable ?? defaultRetryable(code, opts.status),\n cause: opts.cause,\n };\n}\n\nfunction defaultRetryable(code: UploadErrorCode, status?: number): boolean {\n if (code === 'NETWORK' || code === 'PART_FAILED') return true;\n if (code === 'SERVER' && status && status >= 500) return true;\n return false;\n}\n\n/** Map an HTTP error response from the cdn-be API to an UploadError. */\nexport function fromApiResponse(status: number, body: unknown): UploadError {\n const err = (body as { error?: { code?: string; message?: string } } | null)?.error;\n const msg = err?.message || `Request failed with status ${status}`;\n const code = (err?.code || '').toUpperCase();\n\n if (status === 401) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 403) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 413) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 415) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 429) {\n const isQuota = code === 'QUOTA_EXCEEDED';\n return makeError(isQuota ? 'QUOTA' : 'NETWORK', msg, { status, retryable: !isQuota });\n }\n if (status >= 500) {\n return makeError('SERVER', msg, { status, retryable: true });\n }\n return makeError('SERVER', msg, { status, retryable: false });\n}\n","import type {\n ClientConfig,\n PickedFile,\n PickerConfig,\n UploadedFile,\n UploadOptions,\n ProgressEvent as USProgressEvent,\n} from './types.js';\nimport { fromApiResponse, makeError } from './errors.js';\n\ninterface InitResponse {\n sessionId: string;\n fileId: string;\n chunkSize: number;\n totalParts: number;\n partUrls: Array<{ partNumber: number; url: string }>;\n expiresAt: string;\n}\n\ninterface CompleteResponse {\n handle: string;\n fileId: string;\n url: string;\n filename: string;\n mimetype: string;\n size: number;\n etag?: string;\n}\n\ninterface PartResult {\n partNumber: number;\n etag: string;\n size: number;\n}\n\nconst DEFAULT_CONCURRENCY = 3;\nconst DEFAULT_MAX_RETRIES_PER_PART = 3;\n// Backoff schedule between part retries. Capped — past this we surrender.\nconst BACKOFF_MS = [400, 1200, 3600];\n\nexport class Uploader {\n constructor(private cfg: ClientConfig) {}\n\n /** Returns a stable PickedFile descriptor for the input blob/file. */\n describe(file: File | Blob, opts: { filename?: string; mimeType?: string } = {}): PickedFile {\n const isFile = typeof File !== 'undefined' && file instanceof File;\n const filename =\n opts.filename ||\n (isFile ? (file as File).name : 'untitled');\n const mimetype =\n opts.mimeType || (file as Blob).type || 'application/octet-stream';\n return {\n uploadId: cryptoRandomId(),\n filename,\n mimetype,\n size: file.size,\n source: 'local',\n };\n }\n\n /**\n * Upload a single Blob/File. Returns the persisted UploadedFile.\n * Fires Filestack-style per-file callbacks from `opts`.\n */\n async upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n const picked = this.describe(file, { filename: opts.filename, mimeType: opts.mimeType });\n opts.onFileUploadStarted?.(picked);\n\n try {\n const init = await this.initUpload(picked, opts);\n const parts = await this.uploadAllParts(file, init, picked, opts);\n const completed = await this.completeUpload(init.sessionId, parts, opts);\n\n const uploaded: UploadedFile = {\n ...picked,\n handle: completed.handle,\n fileId: completed.fileId,\n url: completed.url,\n size: completed.size,\n mimetype: completed.mimetype,\n filename: completed.filename,\n status: 'Stored',\n etag: completed.etag,\n };\n opts.onFileUploadFinished?.(uploaded);\n return uploaded;\n } catch (rawErr) {\n const err = normalizeError(rawErr);\n opts.onFileUploadFailed?.(picked, err);\n // Best effort: tell the server to clean up the multipart, but ignore failures.\n if ((rawErr as { sessionId?: string })?.sessionId) {\n this.abortSilently((rawErr as { sessionId: string }).sessionId).catch(() => {});\n }\n throw err;\n }\n }\n\n /** Cancel an in-flight session server-side. */\n async abort(sessionId: string): Promise<void> {\n await this.api('POST', '/sdk/v1/uploads/abort', { sessionId });\n }\n\n /** Fetch the server-managed picker config (branding, theme, constraints). */\n async fetchPickerConfig(): Promise<PickerConfig> {\n return this.api<PickerConfig>('GET', '/sdk/v1/picker-config');\n }\n\n private async initUpload(picked: PickedFile, opts: UploadOptions): Promise<InitResponse> {\n return this.api<InitResponse>('POST', '/sdk/v1/uploads/init', {\n filename: picked.filename,\n mimeType: picked.mimetype,\n size: picked.size,\n metadata: opts.metadata,\n }, opts.signal);\n }\n\n private async uploadAllParts(\n file: File | Blob,\n init: InitResponse,\n picked: PickedFile,\n opts: UploadOptions,\n ): Promise<PartResult[]> {\n const totalParts = init.totalParts;\n const chunkSize = init.chunkSize;\n const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);\n const maxRetries = opts.maxRetriesPerPart ?? DEFAULT_MAX_RETRIES_PER_PART;\n\n // Quick lookup: partNumber → presigned URL.\n const urlByPart = new Map(init.partUrls.map(p => [p.partNumber, p.url] as const));\n\n const results: PartResult[] = new Array(totalParts);\n const loadedPerPart: number[] = new Array(totalParts).fill(0);\n let cursor = 1;\n\n const totalBytes = file.size;\n\n const reportProgress = () => {\n const loaded = loadedPerPart.reduce((a, b) => a + b, 0);\n const totalPercent = totalBytes > 0 ? Math.min(100, Math.round((loaded / totalBytes) * 100)) : 100;\n const ev: USProgressEvent = { totalBytes, loaded, totalPercent };\n opts.onFileUploadProgress?.(picked, ev);\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n const partNumber = cursor++;\n if (partNumber > totalParts) return;\n\n const start = (partNumber - 1) * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const chunk = file.slice(start, end);\n\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= maxRetries) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n try {\n let url = urlByPart.get(partNumber);\n if (!url) {\n const refreshed = await this.api<{ url: string }>(\n 'POST', '/sdk/v1/uploads/sign-part',\n { sessionId: init.sessionId, partNumber }, opts.signal,\n );\n url = refreshed.url;\n urlByPart.set(partNumber, url);\n }\n\n const res = await this.cfg.fetch?.(url, {\n method: 'PUT',\n body: chunk,\n signal: opts.signal,\n }) ?? await fetch(url, { method: 'PUT', body: chunk, signal: opts.signal });\n\n if (!res.ok) {\n // If R2 rejects with 403 the URL likely expired — drop it so the\n // next attempt re-signs.\n if (res.status === 403 || res.status === 401) urlByPart.delete(partNumber);\n throw makeError('PART_FAILED', `Part ${partNumber} PUT failed (HTTP ${res.status})`, { status: res.status });\n }\n const etag = res.headers.get('etag');\n if (!etag) {\n throw makeError('PART_FAILED', `Part ${partNumber}: R2 did not return an ETag.`);\n }\n\n results[partNumber - 1] = { partNumber, etag, size: chunk.size };\n loadedPerPart[partNumber - 1] = chunk.size;\n reportProgress();\n break;\n } catch (err) {\n lastErr = err;\n attempt++;\n if (attempt > maxRetries) break;\n await sleep(BACKOFF_MS[Math.min(attempt - 1, BACKOFF_MS.length - 1)]);\n }\n }\n if (!results[partNumber - 1]) {\n throw withSessionId(\n normalizeError(lastErr ?? makeError('PART_FAILED', `Part ${partNumber} failed after ${maxRetries} retries.`)),\n init.sessionId,\n );\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, totalParts) }, worker);\n await Promise.all(workers);\n return results;\n }\n\n private async completeUpload(\n sessionId: string,\n parts: PartResult[],\n opts: UploadOptions,\n ): Promise<CompleteResponse> {\n return this.api<CompleteResponse>('POST', '/sdk/v1/uploads/complete', {\n sessionId,\n parts: parts.map(p => ({ partNumber: p.partNumber, etag: p.etag })),\n }, opts.signal);\n }\n\n private async abortSilently(sessionId: string): Promise<void> {\n try { await this.abort(sessionId); } catch { /* ignore */ }\n }\n\n private async api<T>(\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const fetchImpl = this.cfg.fetch ?? fetch;\n const url = `${this.cfg.apiBase.replace(/\\/$/, '')}${path}`;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.cfg.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (cause) {\n if ((cause as Error)?.name === 'AbortError') {\n throw makeError('ABORTED', 'Request aborted.', { retryable: false, cause });\n }\n throw makeError('NETWORK', 'Network request failed.', { retryable: true, cause });\n }\n let parsed: unknown = null;\n try { parsed = await res.json(); } catch { /* tolerate empty body */ }\n if (!res.ok) throw fromApiResponse(res.status, parsed);\n return parsed as T;\n }\n}\n\nfunction cryptoRandomId(): string {\n // Cheap unique id for client-side uploadId. Not security-sensitive.\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction normalizeError(err: unknown): ReturnType<typeof makeError> {\n if (err && typeof err === 'object' && 'code' in err && 'message' in err && 'retryable' in err) {\n return err as ReturnType<typeof makeError>;\n }\n const msg = (err as Error)?.message || 'Upload failed.';\n return makeError('NETWORK', msg, { cause: err });\n}\n\nfunction withSessionId<T>(err: T, sessionId: string): T {\n if (err && typeof err === 'object') (err as { sessionId?: string }).sessionId = sessionId;\n return err;\n}\n","import type {\n ClientConfig,\n UploadOptions,\n BatchUploadOptions,\n UploadedFile,\n PickResponse,\n PickedFile,\n PickerConfig,\n UploadError,\n} from './types.js';\nimport { Uploader } from './uploader.js';\nimport { makeError } from './errors.js';\n\n// Forward-declared minimal handle so client.ts doesn't pull in the picker bundle.\nexport interface PickerHandleLike {\n open(): Promise<PickResponse>;\n close(): void;\n cancel(): void;\n}\n\nexport class UnionStackClient {\n private uploader: Uploader;\n /**\n * Promise that resolves to the server-managed picker config. Pre-fetched on\n * construction (unless `skipConfigPrefetch: true`) so the picker opens\n * without a network hit. Fails silently — picker falls back to defaults.\n */\n pickerConfigPromise: Promise<PickerConfig | null>;\n\n constructor(private cfg: ClientConfig) {\n if (!cfg.apiKey) throw makeError('CONFIG', 'apiKey is required.', { retryable: false });\n if (!cfg.apiBase) throw makeError('CONFIG', 'apiBase is required.', { retryable: false });\n this.uploader = new Uploader(cfg);\n this.pickerConfigPromise = cfg.skipConfigPrefetch\n ? Promise.resolve(null)\n : this.uploader.fetchPickerConfig().catch(() => null);\n }\n\n /** Upload a single file. Mirrors Filestack's `client.upload()`. */\n upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n return this.uploader.upload(file, opts);\n }\n\n /**\n * Upload multiple files concurrently. Mirrors Filestack's batch behavior:\n * onUploadDone fires once even if some files failed — failures land in\n * `filesFailed`, successes in `filesUploaded`. `onError` is reserved for\n * batch-level failures (no files even started).\n */\n async uploadMany(\n files: (File | Blob)[],\n opts: BatchUploadOptions = {},\n ): Promise<PickResponse> {\n if (!Array.isArray(files) || files.length === 0) {\n const err = makeError('VALIDATION', 'uploadMany requires a non-empty array of files.', { retryable: false });\n opts.onError?.(err);\n throw err;\n }\n\n const picked: PickedFile[] = files.map(f =>\n this.uploader.describe(f, { filename: opts.filename, mimeType: opts.mimeType })\n );\n opts.onUploadStarted?.(picked);\n\n const filesUploaded: UploadedFile[] = [];\n const filesFailed: Array<{ file: PickedFile; error: UploadError }> = [];\n\n // Process per-file in parallel; the per-part concurrency from `opts` still\n // applies inside each upload.\n await Promise.all(files.map(async (f, i) => {\n try {\n const uploaded = await this.uploader.upload(f, opts);\n // Sync the uploadId so callers can correlate picked → uploaded.\n uploaded.uploadId = picked[i].uploadId;\n filesUploaded.push(uploaded);\n } catch (err) {\n filesFailed.push({ file: picked[i], error: err as UploadError });\n }\n }));\n\n const result: PickResponse = { filesUploaded, filesFailed };\n opts.onUploadDone?.(result);\n return result;\n }\n\n /**\n * Open the file picker modal. Lazy-loads the picker bundle so the core\n * SDK stays small for callers that only use `upload()` directly.\n */\n picker(opts: import('./picker/types.js').PickerOptions = {}): PickerHandleLike {\n let real: PickerHandleLike | null = null;\n let opened = false;\n let pendingResolve!: (r: PickResponse) => void;\n let pendingReject!: (e: UploadError) => void;\n const donePromise = new Promise<PickResponse>((res, rej) => {\n pendingResolve = res; pendingReject = rej;\n });\n\n return {\n open: async () => {\n if (opened) return donePromise;\n opened = true;\n try {\n const mod = await import('./picker/index.js');\n real = mod.openPicker(this, opts);\n const result = await real.open();\n pendingResolve(result);\n return result;\n } catch (err) {\n const e = (err as UploadError)?.code\n ? (err as UploadError)\n : makeError('CONFIG', 'Failed to load picker.', { retryable: false, cause: err });\n pendingReject(e);\n throw e;\n }\n },\n close: () => { real?.close(); },\n cancel: () => { real?.cancel(); },\n };\n }\n\n /** Read-only accessor used by the React/picker packages. */\n get config(): Readonly<ClientConfig> { return this.cfg; }\n}\n"]}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
type UploadErrorCode = 'NETWORK' | 'VALIDATION' | 'AUTH' | 'QUOTA' | 'ABORTED' | 'SERVER' | 'CONFIG' | 'PART_FAILED';
|
|
2
|
+
interface UploadError {
|
|
3
|
+
code: UploadErrorCode;
|
|
4
|
+
message: string;
|
|
5
|
+
status?: number;
|
|
6
|
+
/** True if a retry has a reasonable chance of succeeding. */
|
|
7
|
+
retryable: boolean;
|
|
8
|
+
/** Raw error from fetch / server (debugging only — don't display). */
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface PickedFile {
|
|
12
|
+
uploadId: string;
|
|
13
|
+
filename: string;
|
|
14
|
+
mimetype: string;
|
|
15
|
+
size: number;
|
|
16
|
+
source: 'local' | 'url' | 'camera' | 'drop';
|
|
17
|
+
}
|
|
18
|
+
interface UploadedFile extends PickedFile {
|
|
19
|
+
/** Server-side file id — also stored as `handle` for Filestack compatibility. */
|
|
20
|
+
handle: string;
|
|
21
|
+
fileId: string;
|
|
22
|
+
url: string;
|
|
23
|
+
/** Object key in R2 (debugging only). */
|
|
24
|
+
key?: string;
|
|
25
|
+
status: 'Stored';
|
|
26
|
+
etag?: string;
|
|
27
|
+
uploadTags?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
interface ProgressEvent {
|
|
30
|
+
totalBytes: number;
|
|
31
|
+
loaded: number;
|
|
32
|
+
totalPercent: number;
|
|
33
|
+
}
|
|
34
|
+
interface PickResponse {
|
|
35
|
+
filesUploaded: UploadedFile[];
|
|
36
|
+
filesFailed: Array<{
|
|
37
|
+
file: PickedFile;
|
|
38
|
+
error: UploadError;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
interface UploadOptions {
|
|
42
|
+
/** Override filename. Defaults to `file.name` when uploading a File. */
|
|
43
|
+
filename?: string;
|
|
44
|
+
/** Override mimeType. Defaults to `file.type` when uploading a File. */
|
|
45
|
+
mimeType?: string;
|
|
46
|
+
/** Arbitrary tags forwarded to the server and stored on the file record. */
|
|
47
|
+
metadata?: Record<string, unknown>;
|
|
48
|
+
/** Cancel an in-flight upload. */
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
/** How many part PUTs run in parallel. Defaults to 3. */
|
|
51
|
+
concurrency?: number;
|
|
52
|
+
/** Max retries per part. Defaults to 3. */
|
|
53
|
+
maxRetriesPerPart?: number;
|
|
54
|
+
onFileUploadStarted?: (file: PickedFile) => void;
|
|
55
|
+
onFileUploadProgress?: (file: PickedFile, ev: ProgressEvent) => void;
|
|
56
|
+
onFileUploadFinished?: (file: UploadedFile) => void;
|
|
57
|
+
onFileUploadFailed?: (file: PickedFile, error: UploadError) => void;
|
|
58
|
+
}
|
|
59
|
+
interface BatchUploadOptions extends UploadOptions {
|
|
60
|
+
/** Called once at the start of the batch with the file list. */
|
|
61
|
+
onUploadStarted?: (files: PickedFile[]) => void;
|
|
62
|
+
/** Terminal success — fires once even if some files failed (see `filesFailed`). */
|
|
63
|
+
onUploadDone?: (result: PickResponse) => void;
|
|
64
|
+
/** Terminal failure for the batch itself (auth, no network, ...). */
|
|
65
|
+
onError?: (error: UploadError) => void;
|
|
66
|
+
}
|
|
67
|
+
interface ClientConfig {
|
|
68
|
+
/** UnionStack API key, e.g. `unionstack_live_xxx`. */
|
|
69
|
+
apiKey: string;
|
|
70
|
+
/** Base URL of the cdn-be API. e.g. `https://api.unionstack.com/cdn-api` */
|
|
71
|
+
apiBase: string;
|
|
72
|
+
/** Override the upload chunk size in bytes (server may still cap this). */
|
|
73
|
+
chunkSize?: number;
|
|
74
|
+
/** Custom fetch impl — useful for testing / non-browser environments. */
|
|
75
|
+
fetch?: typeof fetch;
|
|
76
|
+
/**
|
|
77
|
+
* Disable auto-fetch of the picker config (`branding` + `theme`) when init()
|
|
78
|
+
* runs. Use when you only call `upload()` and never open the picker.
|
|
79
|
+
*/
|
|
80
|
+
skipConfigPrefetch?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Server-managed picker chrome + upload constraints fetched on init(). */
|
|
83
|
+
interface PickerConfig {
|
|
84
|
+
branding: {
|
|
85
|
+
logoUrl: string | null;
|
|
86
|
+
title: string | null;
|
|
87
|
+
hideFooter: boolean;
|
|
88
|
+
footerText: string | null;
|
|
89
|
+
};
|
|
90
|
+
theme: {
|
|
91
|
+
primary: string | null;
|
|
92
|
+
background: string | null;
|
|
93
|
+
foreground: string | null;
|
|
94
|
+
border: string | null;
|
|
95
|
+
radius: string | null;
|
|
96
|
+
mode: 'light' | 'dark' | null;
|
|
97
|
+
};
|
|
98
|
+
constraints: {
|
|
99
|
+
maxFileSizeBytes: number;
|
|
100
|
+
minFileSizeBytes: number;
|
|
101
|
+
maxFilesPerUpload: number;
|
|
102
|
+
chunkSizeBytes: number;
|
|
103
|
+
allowedMimeTypes: string[];
|
|
104
|
+
};
|
|
105
|
+
mode: 'live' | 'test';
|
|
106
|
+
}
|
|
107
|
+
type Source = PickedFile['source'];
|
|
108
|
+
|
|
109
|
+
interface PickerTheme {
|
|
110
|
+
/** Primary accent color (buttons, progress bar, focus rings). */
|
|
111
|
+
primary?: string;
|
|
112
|
+
/** Modal background. */
|
|
113
|
+
background?: string;
|
|
114
|
+
/** Default text color. */
|
|
115
|
+
foreground?: string;
|
|
116
|
+
/** Muted text (filenames, hints). */
|
|
117
|
+
muted?: string;
|
|
118
|
+
/** Border color for dropzone + cards. */
|
|
119
|
+
border?: string;
|
|
120
|
+
/** Border radius — accepts any CSS length. */
|
|
121
|
+
radius?: string;
|
|
122
|
+
/** Color mode hint — used to pick contrast-friendly defaults. */
|
|
123
|
+
mode?: 'light' | 'dark';
|
|
124
|
+
}
|
|
125
|
+
interface PickerBranding {
|
|
126
|
+
/** URL of a small logo to show in the modal header. */
|
|
127
|
+
logoUrl?: string;
|
|
128
|
+
/** Header title. Default: "Upload". */
|
|
129
|
+
title?: string;
|
|
130
|
+
/** Hide "Powered by UnionStack" footer. */
|
|
131
|
+
hideFooter?: boolean;
|
|
132
|
+
}
|
|
133
|
+
interface PickerOptions extends Omit<BatchUploadOptions, 'signal'> {
|
|
134
|
+
/** Constrain selectable files (HTML accept syntax). */
|
|
135
|
+
accept?: string;
|
|
136
|
+
/** Cap on file count selected at once. */
|
|
137
|
+
maxFiles?: number;
|
|
138
|
+
/** Max file size in bytes — additional client-side check before init. */
|
|
139
|
+
maxFileSize?: number;
|
|
140
|
+
theme?: PickerTheme;
|
|
141
|
+
branding?: PickerBranding;
|
|
142
|
+
/** Where to mount the modal. Default: document.body. */
|
|
143
|
+
container?: HTMLElement;
|
|
144
|
+
onOpen?: () => void;
|
|
145
|
+
onClose?: () => void;
|
|
146
|
+
onCancel?: () => void;
|
|
147
|
+
onFileSelected?: (file: PickedFile) => void | PickedFile | Promise<PickedFile | void>;
|
|
148
|
+
}
|
|
149
|
+
interface PickerHandle {
|
|
150
|
+
open(): Promise<PickResponse>;
|
|
151
|
+
close(): void;
|
|
152
|
+
cancel(): void;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface PickerHandleLike {
|
|
156
|
+
open(): Promise<PickResponse>;
|
|
157
|
+
close(): void;
|
|
158
|
+
cancel(): void;
|
|
159
|
+
}
|
|
160
|
+
declare class UnionStackClient {
|
|
161
|
+
private cfg;
|
|
162
|
+
private uploader;
|
|
163
|
+
/**
|
|
164
|
+
* Promise that resolves to the server-managed picker config. Pre-fetched on
|
|
165
|
+
* construction (unless `skipConfigPrefetch: true`) so the picker opens
|
|
166
|
+
* without a network hit. Fails silently — picker falls back to defaults.
|
|
167
|
+
*/
|
|
168
|
+
pickerConfigPromise: Promise<PickerConfig | null>;
|
|
169
|
+
constructor(cfg: ClientConfig);
|
|
170
|
+
/** Upload a single file. Mirrors Filestack's `client.upload()`. */
|
|
171
|
+
upload(file: File | Blob, opts?: UploadOptions): Promise<UploadedFile>;
|
|
172
|
+
/**
|
|
173
|
+
* Upload multiple files concurrently. Mirrors Filestack's batch behavior:
|
|
174
|
+
* onUploadDone fires once even if some files failed — failures land in
|
|
175
|
+
* `filesFailed`, successes in `filesUploaded`. `onError` is reserved for
|
|
176
|
+
* batch-level failures (no files even started).
|
|
177
|
+
*/
|
|
178
|
+
uploadMany(files: (File | Blob)[], opts?: BatchUploadOptions): Promise<PickResponse>;
|
|
179
|
+
/**
|
|
180
|
+
* Open the file picker modal. Lazy-loads the picker bundle so the core
|
|
181
|
+
* SDK stays small for callers that only use `upload()` directly.
|
|
182
|
+
*/
|
|
183
|
+
picker(opts?: PickerOptions): PickerHandleLike;
|
|
184
|
+
/** Read-only accessor used by the React/picker packages. */
|
|
185
|
+
get config(): Readonly<ClientConfig>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export { type BatchUploadOptions as B, type ClientConfig as C, type PickResponse as P, type Source as S, UnionStackClient as U, type PickedFile as a, type ProgressEvent as b, type UploadError as c, type UploadErrorCode as d, type UploadOptions as e, type UploadedFile as f, type PickerOptions as g, type PickerHandle as h, type PickerBranding as i, type PickerTheme as j };
|