@oncely/core 0.2.2 → 0.3.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/dist/{errors-BUehgS6t.d.cts → errors-DVBjbOG7.d.cts} +35 -1
- package/dist/{errors-BUehgS6t.d.ts → errors-DVBjbOG7.d.ts} +35 -1
- package/dist/index.cjs +37 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +37 -1
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +37 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +37 -1
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/utils.ts","../src/memory.ts","../src/config.ts","../src/oncely.ts","../src/index.ts"],"names":["memory"],"mappings":";;;AAMO,IAAM,MAAA,GAAS;AAGf,IAAM,aAAA,GAAgB;AAGtB,IAAM,aAAA,GAAgB,2BAAA;;;ACatB,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAA;AAAA,EAEjC,UAAA;AAAA;AAAA,EAEA,IAAA;AAAA;AAAA,EAEA,KAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,UAAA,EAAoB,IAAA,EAAc,KAAA,EAAe;AAC5E,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAA,GAAmC;AACjC,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,QAAQ,IAAA,CAAK,UAAA;AAAA,MACb,QAAQ,IAAA,CAAK;AAAA,KACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAyB;AACvB,IAAA,OAAO,KAAK,gBAAA,EAAiB;AAAA,EAC/B;AACF;AAMO,IAAM,eAAA,GAAN,cAA8B,gBAAA,CAAiB;AAAA,EACpD,WAAA,GAAc;AACZ,IAAA,KAAA;AAAA,MACE,oDAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,YAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAMO,IAAM,aAAA,GAAN,cAA4B,gBAAA,CAAiB;AAAA;AAAA,EAEzC,SAAA;AAAA;AAAA,EAEA,UAAA;AAAA,EAET,YAAY,SAAA,EAAmB;AAC7B,IAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAA,CAAO,KAAK,GAAA,EAAI,GAAI,aAAa,GAAI,CAAA;AAC7D,IAAA,KAAA;AAAA,MACE,uEAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,SAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAC,CAAA;AAAA,EACvD;AAAA,EAES,gBAAA,GAAmC;AAC1C,IAAA,OAAO;AAAA,MACL,GAAG,MAAM,gBAAA,EAAiB;AAAA,MAC1B,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,EACF;AACF;AAMO,IAAM,aAAA,GAAN,cAA4B,gBAAA,CAAiB;AAAA;AAAA,EAEzC,YAAA;AAAA;AAAA,EAEA,YAAA;AAAA,EAET,WAAA,CAAY,cAAsB,YAAA,EAAsB;AACtD,IAAA,KAAA;AAAA,MACE,iEAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,SAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AACpB,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AACF;AAMO,IAAM,YAAA,GAAN,cAA2B,gBAAA,CAAiB;AAAA;AAAA,EAE/B,KAAA;AAAA,EAElB,WAAA,CAAY,SAAiB,KAAA,EAAc;AACzC,IAAA,KAAA,CAAM,kBAAkB,OAAO,CAAA,CAAA,EAAI,KAAK,CAAA,EAAG,aAAa,kBAAkB,eAAe,CAAA;AACzF,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AACF;AC3IO,SAAS,SAAS,GAAA,EAA8B;AACrD,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG;AAClB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA;AAC/B,EAAA,IAAI,SAAS,GAAA,IAAO,IAAA,KAAS,OAAO,IAAA,KAAS,GAAA,IAAO,SAAS,GAAA,EAAK;AAChE,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAChC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AACxC,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,UAAA,CAAW,CAAC,CAAA;AAClC,IAAA,IAAI,IAAA,GAAO,EAAA,IAAM,IAAA,GAAO,EAAA,EAAI;AAC1B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,OAC7B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AAEnC,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,GAAA;AAAA,IACjB,KAAK,GAAA;AACH,MAAA,OAAO,QAAQ,EAAA,GAAK,GAAA;AAAA,IACtB,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AAAA,IAC3B,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAAA,IAChC;AACE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mBAAA,EAAsB,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA;AAEnD;AAeO,SAAS,WAAW,GAAA,EAAqB;AAC9C,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK;AACnC,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AAC7B,IAAA,IAAA,GAAA,CAAQ,IAAA,IAAQ,KAAK,IAAA,GAAO,IAAA;AAC5B,IAAA,IAAA,GAAO,IAAA,GAAO,IAAA;AAAA,EAChB;AACA,EAAA,OAAO,IAAA,CAAK,IAAI,IAAI,CAAA,CAAE,SAAS,EAAE,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAA;AACpD;AAQO,SAAS,WAAW,GAAA,EAAqB;AAC9C,EAAA,OAAO,WAAW,QAAQ,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,OAAO,KAAK,CAAA;AACtD;AAiBO,SAAS,WAAW,GAAA,EAAsB;AAC/C,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,IAAK,WAAA;AACnC,EAAA,OAAO,WAAW,GAAG,CAAA;AACvB;AAQO,SAAS,iBAAiB,GAAA,EAAsB;AACrD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,IAAK,WAAA;AACnC,EAAA,OAAO,WAAW,GAAG,CAAA;AACvB;AAOO,SAAS,WAAA,GAAsB;AACpC,EAAA,OAAO,UAAA,EAAW;AACpB;AAKO,SAAS,cAAc,KAAA,EAAoC;AAChE,EAAA,OAAO,KAAA,CAAM,KAAK,GAAG,CAAA;AACvB;;;ACrHO,IAAM,gBAAN,MAA8C;AAAA,EAC3C,KAAA,uBAAY,GAAA,EAA0B;AAAA,EACtC,eAAA,GAAyD,IAAA;AAAA,EAEjE,WAAA,GAAc;AAEZ,IAAA,IAAA,CAAK,kBAAkB,WAAA,CAAY,MAAM,IAAA,CAAK,OAAA,IAAW,GAAM,CAAA;AAE/D,IAAA,IAAI,IAAA,CAAK,gBAAgB,KAAA,EAAO;AAC9B,MAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,GAAA,EAAa,IAAA,EAAqB,GAAA,EAAqC;AACnF,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAEjC,IAAA,IAAI,QAAA,IAAY,QAAA,CAAS,SAAA,IAAa,GAAA,EAAK;AACzC,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,QAAA,GAAW,MAAA;AAAA,IACb;AAGA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,eAAe,QAAA,CAAS,IAAA;AAE9B,MAAA,IAAI,IAAA,IAAQ,YAAA,IAAgB,YAAA,KAAiB,IAAA,EAAM;AACjD,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ,UAAA;AAAA,UACR,YAAA;AAAA,UACA,YAAA,EAAc;AAAA,SAChB;AAAA,MACF;AAGA,MAAA,IAAI,QAAA,CAAS,WAAW,WAAA,EAAa;AACnC,QAAA,MAAM,WAAW,QAAA,CAAS,QAAA;AAC1B,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,QAAA,EAAS;AAAA,QACnC;AAAA,MACF;AAGA,MAAA,IAAI,QAAA,CAAS,WAAW,aAAA,EAAe;AACrC,QAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,SAAA,EAAW,SAAS,SAAA,EAAU;AAAA,MAC7D;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,KAAA,CAAM,IAAI,GAAA,EAAK;AAAA,MAClB,MAAA,EAAQ,aAAA;AAAA,MACR,IAAA;AAAA,MACA,SAAA,EAAW,GAAA;AAAA,MACX,WAAW,GAAA,GAAM;AAAA,KAClB,CAAA;AAED,IAAA,OAAO,EAAE,QAAQ,UAAA,EAAW;AAAA,EAC9B;AAAA,EAEA,MAAM,IAAA,CAAK,GAAA,EAAa,QAAA,EAAyC;AAC/D,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AACnC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,MAAA,GAAS,WAAA;AAClB,MAAA,QAAA,CAAS,QAAA,GAAW,QAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,GAAA,EAA4B;AACxC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,GAAA,EAA4B;AACvC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,aAAA,CAAc,KAAK,eAAe,CAAA;AAClC,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,OAAA,GAAgB;AACtB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,MAAM,CAAA,IAAK,KAAK,KAAA,EAAO;AACtC,MAAA,IAAI,MAAA,CAAO,aAAa,GAAA,EAAK;AAC3B,QAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAasC,IAAI,aAAA;;;ACzH1C,IAAI,eAA6B,EAAC;AAGlC,IAAI,oBAAA,GAA6C,IAAA;AAmB1C,SAAS,UAAU,MAAA,EAA4B;AACpD,EAAA,YAAA,GAAe,EAAE,GAAG,MAAA,EAAO;AAC7B;AAKO,SAAS,SAAA,GAA0B;AACxC,EAAA,OAAO,YAAA;AACT;AAMO,SAAS,WAAA,GAAoB;AAClC,EAAA,YAAA,GAAe,EAAC;AAChB,EAAA,oBAAA,GAAuB,IAAA;AACzB;AAMO,SAAS,iBAAA,GAAoC;AAClD,EAAA,IAAI,aAAa,OAAA,EAAS;AACxB,IAAA,OAAO,YAAA,CAAa,OAAA;AAAA,EACtB;AAEA,EAAA,IAAI,CAAC,oBAAA,EAAsB;AACzB,IAAA,oBAAA,GAAuB,IAAI,aAAA,EAAc;AAAA,EAC3C;AACA,EAAA,OAAO,oBAAA;AACT;AAKO,SAAS,eAAe,OAAA,EAAiD;AAC9E,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,OAAA,EAAS,OAAA,IAAW,MAAA,CAAO,WAAW,iBAAA,EAAkB;AAAA,IACjE,GAAA,EAAK,OAAA,EAAS,GAAA,IAAO,MAAA,CAAO,GAAA,IAAO,KAAA;AAAA,IACnC,WAAA,EAAa,OAAA,EAAS,WAAA,IAAe,MAAA,CAAO,WAAA,IAAe,IAAA;AAAA,IAC3D,KAAA,EAAO,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,KAAA,IAAS,KAAA;AAAA,IACzC,KAAA,EAAO,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,KAAA;AAAA,IAChC,MAAA,EAAQ,OAAA,EAAS,MAAA,IAAU,MAAA,CAAO,MAAA;AAAA,IAClC,UAAA,EAAY,OAAA,EAAS,UAAA,IAAc,MAAA,CAAO,UAAA;AAAA,IAC1C,OAAA,EAAS,OAAA,EAAS,OAAA,IAAW,MAAA,CAAO;AAAA,GACtC;AACF;;;AC5DO,IAAM,SAAN,MAAa;AAAA,EACD,OAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EAEjB,YAAY,OAAA,EAAwB;AAClC,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,GAAA,GAAM,QAAA,CAAS,OAAA,CAAQ,GAAA,IAAO,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAC9B,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACrB,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,IAAO,OAAA,EAA+C;AAC1D,IAAA,MAAM,EAAE,GAAA,EAAK,IAAA,GAAO,IAAA,EAAM,SAAQ,GAAI,OAAA;AAGtC,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,IAAI,eAAA,EAAgB;AAAA,IAC5B;AAEA,IAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,CAAE,CAAA;AAGzC,IAAA,IAAI,aAAA;AACJ,IAAA,IAAI;AACF,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,GAAA,EAAK,IAAA,EAAM,KAAK,GAAG,CAAA;AAAA,IAChE,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,IAAI,YAAA,CAAa,wBAAA,EAA0B,GAAY,CAAA;AAAA,IAC/D;AAGA,IAAA,QAAQ,cAAc,MAAA;AAAQ,MAC5B,KAAK,KAAA,EAAO;AACV,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,mBAAA,EAAsB,GAAG,CAAA,CAAE,CAAA;AACpC,QAAA,IAAA,CAAK,KAAA,GAAQ,GAAA,EAAK,aAAA,CAAc,QAAQ,CAAA;AACxC,QAAA,OAAO;AAAA,UACL,IAAA,EAAM,cAAc,QAAA,CAAS,IAAA;AAAA,UAC7B,MAAA,EAAQ,IAAA;AAAA,UACR,MAAA,EAAQ,KAAA;AAAA,UACR,SAAA,EAAW,cAAc,QAAA,CAAS;AAAA,SACpC;AAAA,MACF;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,kBAAA,EAAqB,GAAG,CAAA,CAAE,CAAA;AACnC,QAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,aAAA,CAAc,SAAS,CAAA;AACvD,QAAA,IAAA,CAAK,aAAa,GAAG,CAAA;AACrB,QAAA,IAAA,CAAK,OAAA,GAAU,KAAK,KAAK,CAAA;AACzB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,uBAAA,EAA0B,GAAG,CAAA,CAAE,CAAA;AACxC,QAAA,MAAM,QAAQ,IAAI,aAAA,CAAc,aAAA,CAAc,YAAA,EAAc,cAAc,YAAY,CAAA;AACtF,QAAA,IAAA,CAAK,OAAA,GAAU,KAAK,KAAK,CAAA;AACzB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,uBAAA,EAA0B,GAAG,CAAA,CAAE,CAAA;AACxC,QAAA,IAAA,CAAK,SAAS,GAAG,CAAA;AACjB,QAAA;AAAA,MACF;AAAA;AAIF,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,OAAA,EAAQ;AAC3B,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,MAAA,MAAM,cAAA,GAAiC;AAAA,QACrC,IAAA;AAAA,QACA,SAAA,EAAW,GAAA;AAAA,QACX;AAAA,OACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,GAAA,EAAK,cAAc,CAAA;AAC3C,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,CAAE,CAAA;AAAA,MAC3C,SAAS,GAAA,EAAK;AAEZ,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,iCAAA,EAAoC,GAAG,CAAA,GAAA,EAAM,GAAG,CAAA,CAAE,CAAA;AAAA,MAC7D;AAEA,MAAA,OAAO;AAAA,QACL,IAAA;AAAA,QACA,MAAA,EAAQ,KAAA;AAAA,QACR,MAAA,EAAQ,SAAA;AAAA,QACR,SAAA,EAAW;AAAA,OACb;AAAA,IACF,SAAS,GAAA,EAAK;AAEZ,MAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,gBAAA,CAAkB,CAAA;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAAA,MAChC,SAAS,UAAA,EAAY;AACnB,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,gCAAA,EAAmC,GAAG,CAAA,GAAA,EAAM,UAAU,CAAA,CAAE,CAAA;AAAA,MACnE;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,IAAI,OAAA,EAAuB;AACjC,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,SAAA,EAAY,OAAO,CAAA,CAAE,CAAA;AAAA,IACnC;AAAA,EACF;AACF;AAuBO,SAAS,eAAe,OAAA,EAA0C;AACvE,EAAA,OAAO,IAAI,MAAA,CAAO,cAAA,CAAe,OAAO,CAAC,CAAA;AAC3C;;;AChJO,IAAM,MAAA,GAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,SAAA;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAA;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AACF;AA2BO,IAAMA,OAAAA,GAAyB,IAAI,aAAA","file":"index.js","sourcesContent":["/**\n * HTTP header constants following IETF draft-ietf-httpapi-idempotency-key-header.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n\n/** Standard header name for idempotency keys */\nexport const HEADER = 'Idempotency-Key';\n\n/** Header indicating a response was replayed from cache */\nexport const HEADER_REPLAY = 'Idempotency-Replay';\n\n/** Base URL for error documentation */\nexport const DOCS_BASE_URL = 'https://oncely.dev/errors';\n","import { DOCS_BASE_URL } from './constants.js';\n\n/**\n * RFC 7807 Problem Details response format.\n * @see https://www.rfc-editor.org/rfc/rfc7807\n */\nexport interface ProblemDetails {\n /** URI reference identifying the problem type */\n type: string;\n /** Short human-readable summary */\n title: string;\n /** HTTP status code */\n status: number;\n /** Detailed human-readable explanation */\n detail: string;\n /** URI reference to the specific occurrence (optional) */\n instance?: string;\n /** Additional properties */\n [key: string]: unknown;\n}\n\n/**\n * Base class for all oncely errors.\n * Provides RFC 7807 Problem Details format for HTTP responses.\n */\nexport class IdempotencyError extends Error {\n /** HTTP status code for this error */\n readonly statusCode: number;\n /** Error type identifier (URL) */\n readonly type: string;\n /** Short title for the error */\n readonly title: string;\n\n constructor(message: string, statusCode: number, type: string, title: string) {\n super(message);\n this.name = 'IdempotencyError';\n this.statusCode = statusCode;\n this.type = type;\n this.title = title;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /**\n * Convert to RFC 7807 Problem Details format.\n */\n toProblemDetails(): ProblemDetails {\n return {\n type: this.type,\n title: this.title,\n status: this.statusCode,\n detail: this.message,\n };\n }\n\n /**\n * Convert to JSON (RFC 7807 format).\n */\n toJSON(): ProblemDetails {\n return this.toProblemDetails();\n }\n}\n\n/**\n * Thrown when an idempotency key is required but not provided.\n * HTTP 400 Bad Request\n */\nexport class MissingKeyError extends IdempotencyError {\n constructor() {\n super(\n 'This operation requires an Idempotency-Key header.',\n 400,\n `${DOCS_BASE_URL}/missing-key`,\n 'Idempotency-Key is missing'\n );\n this.name = 'MissingKeyError';\n }\n}\n\n/**\n * Thrown when a request with the same key is already being processed.\n * HTTP 409 Conflict\n */\nexport class ConflictError extends IdempotencyError {\n /** When the in-progress request started */\n readonly startedAt: number;\n /** Suggested retry delay in seconds */\n readonly retryAfter: number;\n\n constructor(startedAt: number) {\n const ageSeconds = Math.round((Date.now() - startedAt) / 1000);\n super(\n 'A request with the same Idempotency-Key is currently being processed.',\n 409,\n `${DOCS_BASE_URL}/conflict`,\n 'Request in progress'\n );\n this.name = 'ConflictError';\n this.startedAt = startedAt;\n this.retryAfter = Math.max(1, Math.min(5, ageSeconds));\n }\n\n override toProblemDetails(): ProblemDetails {\n return {\n ...super.toProblemDetails(),\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Thrown when the same idempotency key is used with a different request payload.\n * HTTP 422 Unprocessable Content\n */\nexport class MismatchError extends IdempotencyError {\n /** Hash of the original request */\n readonly existingHash: string;\n /** Hash of the current request */\n readonly providedHash: string;\n\n constructor(existingHash: string, providedHash: string) {\n super(\n 'This Idempotency-Key was used with a different request payload.',\n 422,\n `${DOCS_BASE_URL}/mismatch`,\n 'Idempotency-Key reused'\n );\n this.name = 'MismatchError';\n this.existingHash = existingHash;\n this.providedHash = providedHash;\n }\n}\n\n/**\n * Thrown when the storage adapter encounters an error.\n * HTTP 500 Internal Server Error\n */\nexport class StorageError extends IdempotencyError {\n /** The underlying error from the storage adapter */\n override readonly cause: Error;\n\n constructor(message: string, cause: Error) {\n super(`Storage error: ${message}`, 500, `${DOCS_BASE_URL}/storage-error`, 'Storage error');\n this.name = 'StorageError';\n this.cause = cause;\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\n\n/**\n * Parse a TTL string into milliseconds.\n * Supports: '30s', '5m', '24h', '7d'\n */\nexport function parseTtl(ttl: number | string): number {\n if (typeof ttl === 'number') {\n return ttl;\n }\n\n if (ttl.length < 2) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const unit = ttl[ttl.length - 1];\n if (unit !== 's' && unit !== 'm' && unit !== 'h' && unit !== 'd') {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const valueStr = ttl.slice(0, -1);\n if (!valueStr) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n for (let i = 0; i < valueStr.length; i++) {\n const code = valueStr.charCodeAt(i);\n if (code < 48 || code > 57) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n }\n\n const value = parseInt(valueStr, 10);\n\n switch (unit) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Invalid TTL unit: \"${unit}\"`);\n }\n}\n\n/**\n * Generate a simple hash from a string using DJB2 algorithm.\n *\n * **Note:** This is a fast, non-cryptographic hash suitable for request\n * fingerprinting. It has a 32-bit output space (~4 billion unique values).\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision (birthday problem). For high-volume payment systems or\n * security-sensitive applications, use {@link hashObjectSecure} instead.\n *\n * For typical API use cases with moderate volume, DJB2 provides excellent\n * performance (~2.4M ops/sec vs ~600K ops/sec for SHA-256).\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\n/**\n * Generate a SHA-256 hash from a string.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link simpleHash} but cryptographically secure.\n */\nexport function secureHash(str: string): string {\n return createHash('sha256').update(str).digest('hex');\n}\n\n/**\n * Hash an object by JSON stringifying it.\n *\n * Uses the fast DJB2 algorithm (~2.4M ops/sec). For high-volume payment\n * systems or security-sensitive applications where collision resistance\n * is critical, use {@link hashObjectSecure} instead.\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision. A collision could cause:\n * - False mismatch errors (if hashes differ)\n * - Or incorrectly matching different payloads (if hashes collide)\n *\n * **Note:** Object key order affects the hash. If you need order-independent\n * hashing, sort the keys before passing to this function.\n */\nexport function hashObject(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return simpleHash(str);\n}\n\n/**\n * Hash an object using SHA-256.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link hashObject} but cryptographically secure.\n */\nexport function hashObjectSecure(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return secureHash(str);\n}\n\n/**\n * Generate a cryptographically secure unique key (UUID v4).\n *\n * Uses Node.js crypto.randomUUID() for secure random generation.\n */\nexport function generateKey(): string {\n return randomUUID();\n}\n\n/**\n * Compose a deterministic key from multiple parts.\n */\nexport function composeKey(...parts: (string | number)[]): string {\n return parts.join(':');\n}\n","import type { StorageAdapter, AcquireResult, StoredResponse } from './types.js';\n\ninterface MemoryRecord {\n status: 'in_progress' | 'completed';\n hash: string | null;\n response?: StoredResponse;\n startedAt: number;\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter.\n * Suitable for development, testing, and single-instance deployments.\n *\n * **Warning:** Data is lost on restart and not shared across instances.\n * Use Redis for production multi-instance deployments.\n */\nexport class MemoryStorage implements StorageAdapter {\n private store = new Map<string, MemoryRecord>();\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor() {\n // Cleanup expired entries every 60 seconds\n this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);\n // Don't prevent Node from exiting\n if (this.cleanupInterval.unref) {\n this.cleanupInterval.unref();\n }\n }\n\n async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n const now = Date.now();\n let existing = this.store.get(key);\n\n if (existing && existing.expiresAt <= now) {\n this.store.delete(key);\n existing = undefined;\n }\n\n // Check if key exists and hasn't expired\n if (existing) {\n const existingHash = existing.hash;\n // Check for hash mismatch\n if (hash && existingHash && existingHash !== hash) {\n return {\n status: 'mismatch',\n existingHash,\n providedHash: hash,\n };\n }\n\n // If completed, return cached response\n if (existing.status === 'completed') {\n const response = existing.response;\n if (response) {\n return { status: 'hit', response };\n }\n }\n\n // If in progress, return conflict\n if (existing.status === 'in_progress') {\n return { status: 'conflict', startedAt: existing.startedAt };\n }\n }\n\n // Acquire lock\n this.store.set(key, {\n status: 'in_progress',\n hash,\n startedAt: now,\n expiresAt: now + ttl,\n });\n\n return { status: 'acquired' };\n }\n\n async save(key: string, response: StoredResponse): Promise<void> {\n const existing = this.store.get(key);\n if (existing) {\n existing.status = 'completed';\n existing.response = response;\n }\n }\n\n async release(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n /**\n * Stop the cleanup interval.\n * Call this when shutting down to prevent memory leaks in tests.\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n\n private cleanup(): void {\n const now = Date.now();\n for (const [key, record] of this.store) {\n if (record.expiresAt <= now) {\n this.store.delete(key);\n }\n }\n }\n}\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n *\n * @example\n * ```typescript\n * import { oncely, memory } from 'oncely';\n *\n * const idempotency = oncely({ storage: memory });\n * ```\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n","import type { StorageAdapter, OncelyOptions, OncelyConfig } from './types.js';\nimport { MemoryStorage } from './memory.js';\n\n// Re-export GlobalConfig as alias for backwards compatibility\nexport type GlobalConfig = OncelyConfig;\n\n// Global configuration store\nlet globalConfig: OncelyConfig = {};\n\n// Lazy-initialized default memory storage\nlet defaultMemoryStorage: MemoryStorage | null = null;\n\n/**\n * Configure global defaults for oncely.\n * Call this once at application startup to set defaults for all oncely operations.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n * import { redis } from '@oncely/redis';\n *\n * // Set up once at app startup\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * onHit: (key) => console.log(`Cache hit: ${key}`),\n * });\n * ```\n */\nexport function configure(config: OncelyConfig): void {\n globalConfig = { ...config };\n}\n\n/**\n * Get the current global configuration.\n */\nexport function getConfig(): OncelyConfig {\n return globalConfig;\n}\n\n/**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\nexport function resetConfig(): void {\n globalConfig = {};\n defaultMemoryStorage = null;\n}\n\n/**\n * Get the default storage adapter.\n * Returns the configured global storage, or a shared memory instance.\n */\nexport function getDefaultStorage(): StorageAdapter {\n if (globalConfig.storage) {\n return globalConfig.storage;\n }\n // Lazy-init shared memory storage\n if (!defaultMemoryStorage) {\n defaultMemoryStorage = new MemoryStorage();\n }\n return defaultMemoryStorage;\n}\n\n/**\n * Resolve oncely options by merging with global config.\n */\nexport function resolveOptions(options?: Partial<OncelyOptions>): OncelyOptions {\n const config = getConfig();\n return {\n storage: options?.storage ?? config.storage ?? getDefaultStorage(),\n ttl: options?.ttl ?? config.ttl ?? '24h',\n fingerprint: options?.fingerprint ?? config.fingerprint ?? true,\n debug: options?.debug ?? config.debug ?? false,\n onHit: options?.onHit ?? config.onHit,\n onMiss: options?.onMiss ?? config.onMiss,\n onConflict: options?.onConflict ?? config.onConflict,\n onError: options?.onError ?? config.onError,\n };\n}\n","import type {\n StorageAdapter,\n OncelyOptions,\n RunOptions,\n RunResult,\n StoredResponse,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n} from './types.js';\nimport { MissingKeyError, ConflictError, MismatchError, StorageError } from './errors.js';\nimport { parseTtl } from './utils.js';\nimport { resolveOptions } from './config.js';\n\n/**\n * Oncely idempotency service.\n * Ensures operations are executed exactly once per idempotency key.\n */\nexport class Oncely {\n private readonly storage: StorageAdapter;\n private readonly ttl: number;\n private readonly debug: boolean;\n private readonly onHit?: OnHitCallback;\n private readonly onMiss?: OnMissCallback;\n private readonly onConflict?: OnConflictCallback;\n private readonly onError?: OnErrorCallback;\n\n constructor(options: OncelyOptions) {\n this.storage = options.storage;\n this.ttl = parseTtl(options.ttl ?? '24h');\n this.debug = options.debug ?? false;\n this.onHit = options.onHit;\n this.onMiss = options.onMiss;\n this.onConflict = options.onConflict;\n this.onError = options.onError;\n }\n\n /**\n * Run an operation with idempotency protection.\n *\n * @example\n * ```typescript\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => {\n * const order = await createOrder(data);\n * return order;\n * },\n * });\n *\n * if (result.cached) {\n * console.log('Returned cached response');\n * }\n * ```\n */\n async run<T>(options: RunOptions<T>): Promise<RunResult<T>> {\n const { key, hash = null, handler } = options;\n\n // Validate key\n if (!key) {\n throw new MissingKeyError();\n }\n\n this.log(`Acquiring lock for key: ${key}`);\n\n // Try to acquire lock\n let acquireResult;\n try {\n acquireResult = await this.storage.acquire(key, hash, this.ttl);\n } catch (err) {\n throw new StorageError('Failed to acquire lock', err as Error);\n }\n\n // Handle different acquire results\n switch (acquireResult.status) {\n case 'hit': {\n this.log(`Cache hit for key: ${key}`);\n this.onHit?.(key, acquireResult.response);\n return {\n data: acquireResult.response.data as T,\n cached: true,\n status: 'hit',\n createdAt: acquireResult.response.createdAt,\n };\n }\n\n case 'conflict': {\n this.log(`Conflict for key: ${key}`);\n const error = new ConflictError(acquireResult.startedAt);\n this.onConflict?.(key);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'mismatch': {\n this.log(`Hash mismatch for key: ${key}`);\n const error = new MismatchError(acquireResult.existingHash, acquireResult.providedHash);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'acquired': {\n this.log(`Lock acquired for key: ${key}`);\n this.onMiss?.(key);\n break;\n }\n }\n\n // Execute handler\n try {\n const data = await handler();\n const now = Date.now();\n\n // Save response\n const storedResponse: StoredResponse = {\n data,\n createdAt: now,\n hash,\n };\n\n try {\n await this.storage.save(key, storedResponse);\n this.log(`Saved response for key: ${key}`);\n } catch (err) {\n // Log but don't fail - the operation succeeded\n this.log(`Failed to save response for key: ${key} - ${err}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n } catch (err) {\n // Release lock on failure to allow retry\n this.log(`Handler failed for key: ${key}, releasing lock`);\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n throw err;\n }\n }\n\n private log(message: string): void {\n if (this.debug) {\n console.log(`[oncely] ${message}`);\n }\n }\n}\n\n/**\n * Create an oncely idempotency instance.\n *\n * Options are optional - will use global config or sensible defaults.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@oncely/core';\n *\n * // Zero-config (uses memory storage)\n * const idempotency = createInstance();\n *\n * // With explicit storage\n * const idempotency = createInstance({ storage: myRedis });\n *\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => createOrder(data),\n * });\n * ```\n */\nexport function createInstance(options?: Partial<OncelyOptions>): Oncely {\n return new Oncely(resolveOptions(options));\n}\n","import { Oncely, createInstance } from './oncely.js';\nimport { MemoryStorage } from './memory.js';\nimport {\n configure as configureGlobal,\n getConfig,\n resetConfig,\n getDefaultStorage,\n} from './config.js';\nimport { HEADER, HEADER_REPLAY } from './constants.js';\nimport type { StorageAdapter } from './types.js';\n\n/**\n * Oncely namespace object.\n * All oncely functionality is accessed through this namespace.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n *\n * // Configure globally\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * });\n *\n * // Create an instance\n * const instance = oncely.createInstance();\n * const result = await instance.run({\n * key: 'order-123',\n * handler: () => createOrder(data),\n * });\n * ```\n */\nexport const oncely = {\n /**\n * Configure global defaults for oncely.\n * Call this once at application startup.\n */\n configure: configureGlobal,\n\n /**\n * Get the current global configuration.\n */\n getConfig,\n\n /**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\n resetConfig,\n\n /**\n * Get the default storage adapter.\n */\n getDefaultStorage,\n\n /**\n * Create an oncely instance with optional configuration.\n * Uses global config merged with provided options.\n */\n createInstance,\n\n /**\n * Standard header name for idempotency keys.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n HEADER,\n\n /**\n * Header indicating a response was replayed from cache.\n */\n HEADER_REPLAY,\n} as const;\n\n/**\n * Interface for oncely namespace - can be extended via module augmentation.\n */\nexport interface OncelyNamespace {\n readonly configure: typeof configureGlobal;\n readonly getConfig: typeof getConfig;\n readonly resetConfig: typeof resetConfig;\n readonly getDefaultStorage: typeof getDefaultStorage;\n readonly createInstance: typeof createInstance;\n readonly HEADER: typeof HEADER;\n readonly HEADER_REPLAY: typeof HEADER_REPLAY;\n // Allow extension via module augmentation\n [key: string]: unknown;\n}\n\n// Re-export the Oncely class for advanced usage\nexport { Oncely };\n\n// Re-export memory storage\nexport { MemoryStorage };\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n\n// Re-export configuration functions for direct import (backwards compatibility)\nexport { configure, getConfig, resetConfig, getDefaultStorage } from './config.js';\nexport type { GlobalConfig } from './config.js';\n\n// Re-export constants\nexport { HEADER, HEADER_REPLAY } from './constants.js';\n\n// Re-export errors\nexport {\n IdempotencyError,\n MissingKeyError,\n ConflictError,\n MismatchError,\n StorageError,\n type ProblemDetails,\n} from './errors.js';\n\n// Re-export utilities\nexport {\n parseTtl,\n simpleHash,\n secureHash,\n hashObject,\n hashObjectSecure,\n generateKey,\n composeKey,\n} from './utils.js';\n\n// Re-export types\nexport type {\n StorageAdapter,\n AcquireResult,\n StoredResponse,\n OncelyOptions,\n OncelyConfig,\n RunOptions,\n RunResult,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n} from './types.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/utils.ts","../src/memory.ts","../src/config.ts","../src/oncely.ts","../src/index.ts"],"names":["memory"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HTTP header constants following IETF draft-ietf-httpapi-idempotency-key-header.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n\n/** Standard header name for idempotency keys */\nexport const HEADER = 'Idempotency-Key';\n\n/** Header indicating a response was replayed from cache */\nexport const HEADER_REPLAY = 'Idempotency-Replay';\n\n/** Base URL for error documentation */\nexport const DOCS_BASE_URL = 'https://oncely.dev/errors';\n","import { DOCS_BASE_URL } from './constants.js';\n\n/**\n * RFC 7807 Problem Details response format.\n * @see https://www.rfc-editor.org/rfc/rfc7807\n */\nexport interface ProblemDetails {\n /** URI reference identifying the problem type */\n type: string;\n /** Short human-readable summary */\n title: string;\n /** HTTP status code */\n status: number;\n /** Detailed human-readable explanation */\n detail: string;\n /** URI reference to the specific occurrence (optional) */\n instance?: string;\n /** Additional properties */\n [key: string]: unknown;\n}\n\n/**\n * Base class for all oncely errors.\n * Provides RFC 7807 Problem Details format for HTTP responses.\n */\nexport class IdempotencyError extends Error {\n /** HTTP status code for this error */\n readonly statusCode: number;\n /** Error type identifier (URL) */\n readonly type: string;\n /** Short title for the error */\n readonly title: string;\n\n constructor(message: string, statusCode: number, type: string, title: string) {\n super(message);\n this.name = 'IdempotencyError';\n this.statusCode = statusCode;\n this.type = type;\n this.title = title;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /**\n * Convert to RFC 7807 Problem Details format.\n */\n toProblemDetails(): ProblemDetails {\n return {\n type: this.type,\n title: this.title,\n status: this.statusCode,\n detail: this.message,\n };\n }\n\n /**\n * Convert to JSON (RFC 7807 format).\n */\n toJSON(): ProblemDetails {\n return this.toProblemDetails();\n }\n}\n\n/**\n * Thrown when an idempotency key is required but not provided.\n * HTTP 400 Bad Request\n */\nexport class MissingKeyError extends IdempotencyError {\n constructor() {\n super(\n 'This operation requires an Idempotency-Key header.',\n 400,\n `${DOCS_BASE_URL}/missing-key`,\n 'Idempotency-Key is missing'\n );\n this.name = 'MissingKeyError';\n }\n}\n\n/**\n * Thrown when a request with the same key is already being processed.\n * HTTP 409 Conflict\n */\nexport class ConflictError extends IdempotencyError {\n /** When the in-progress request started */\n readonly startedAt: number;\n /** Suggested retry delay in seconds */\n readonly retryAfter: number;\n\n constructor(startedAt: number) {\n const ageSeconds = Math.round((Date.now() - startedAt) / 1000);\n super(\n 'A request with the same Idempotency-Key is currently being processed.',\n 409,\n `${DOCS_BASE_URL}/conflict`,\n 'Request in progress'\n );\n this.name = 'ConflictError';\n this.startedAt = startedAt;\n this.retryAfter = Math.max(1, Math.min(5, ageSeconds));\n }\n\n override toProblemDetails(): ProblemDetails {\n return {\n ...super.toProblemDetails(),\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Thrown when the same idempotency key is used with a different request payload.\n * HTTP 422 Unprocessable Content\n */\nexport class MismatchError extends IdempotencyError {\n /** Hash of the original request */\n readonly existingHash: string;\n /** Hash of the current request */\n readonly providedHash: string;\n\n constructor(existingHash: string, providedHash: string) {\n super(\n 'This Idempotency-Key was used with a different request payload.',\n 422,\n `${DOCS_BASE_URL}/mismatch`,\n 'Idempotency-Key reused'\n );\n this.name = 'MismatchError';\n this.existingHash = existingHash;\n this.providedHash = providedHash;\n }\n}\n\n/**\n * Thrown when the storage adapter encounters an error.\n * HTTP 500 Internal Server Error\n */\nexport class StorageError extends IdempotencyError {\n /** The underlying error from the storage adapter */\n override readonly cause: Error;\n\n constructor(message: string, cause: Error) {\n super(`Storage error: ${message}`, 500, `${DOCS_BASE_URL}/storage-error`, 'Storage error');\n this.name = 'StorageError';\n this.cause = cause;\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\n\n/**\n * Parse a TTL string into milliseconds.\n * Supports: '30s', '5m', '24h', '7d'\n */\nexport function parseTtl(ttl: number | string): number {\n if (typeof ttl === 'number') {\n return ttl;\n }\n\n if (ttl.length < 2) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const unit = ttl[ttl.length - 1];\n if (unit !== 's' && unit !== 'm' && unit !== 'h' && unit !== 'd') {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const valueStr = ttl.slice(0, -1);\n if (!valueStr) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n for (let i = 0; i < valueStr.length; i++) {\n const code = valueStr.charCodeAt(i);\n if (code < 48 || code > 57) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n }\n\n const value = parseInt(valueStr, 10);\n\n switch (unit) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Invalid TTL unit: \"${unit}\"`);\n }\n}\n\n/**\n * Generate a simple hash from a string using DJB2 algorithm.\n *\n * **Note:** This is a fast, non-cryptographic hash suitable for request\n * fingerprinting. It has a 32-bit output space (~4 billion unique values).\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision (birthday problem). For high-volume payment systems or\n * security-sensitive applications, use {@link hashObjectSecure} instead.\n *\n * For typical API use cases with moderate volume, DJB2 provides excellent\n * performance (~2.4M ops/sec vs ~600K ops/sec for SHA-256).\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\n/**\n * Generate a SHA-256 hash from a string.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link simpleHash} but cryptographically secure.\n */\nexport function secureHash(str: string): string {\n return createHash('sha256').update(str).digest('hex');\n}\n\n/**\n * Hash an object by JSON stringifying it.\n *\n * Uses the fast DJB2 algorithm (~2.4M ops/sec). For high-volume payment\n * systems or security-sensitive applications where collision resistance\n * is critical, use {@link hashObjectSecure} instead.\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision. A collision could cause:\n * - False mismatch errors (if hashes differ)\n * - Or incorrectly matching different payloads (if hashes collide)\n *\n * **Note:** Object key order affects the hash. If you need order-independent\n * hashing, sort the keys before passing to this function.\n */\nexport function hashObject(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return simpleHash(str);\n}\n\n/**\n * Hash an object using SHA-256.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link hashObject} but cryptographically secure.\n */\nexport function hashObjectSecure(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return secureHash(str);\n}\n\n/**\n * Generate a cryptographically secure unique key (UUID v4).\n *\n * Uses Node.js crypto.randomUUID() for secure random generation.\n */\nexport function generateKey(): string {\n return randomUUID();\n}\n\n/**\n * Compose a deterministic key from multiple parts.\n */\nexport function composeKey(...parts: (string | number)[]): string {\n return parts.join(':');\n}\n","import type { StorageAdapter, AcquireResult, StoredResponse } from './types.js';\n\ninterface MemoryRecord {\n status: 'in_progress' | 'completed';\n hash: string | null;\n response?: StoredResponse;\n startedAt: number;\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter.\n * Suitable for development, testing, and single-instance deployments.\n *\n * **Warning:** Data is lost on restart and not shared across instances.\n * Use Redis for production multi-instance deployments.\n */\nexport class MemoryStorage implements StorageAdapter {\n /**\n * Maximum payload size in bytes (default: 5MB).\n * Prevents unbounded memory growth from large responses.\n */\n readonly maxPayloadSize: number = 5 * 1024 * 1024;\n\n private store = new Map<string, MemoryRecord>();\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor() {\n // Cleanup expired entries every 60 seconds\n this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);\n // Don't prevent Node from exiting\n if (this.cleanupInterval.unref) {\n this.cleanupInterval.unref();\n }\n }\n\n async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n const now = Date.now();\n let existing = this.store.get(key);\n\n if (existing && existing.expiresAt <= now) {\n this.store.delete(key);\n existing = undefined;\n }\n\n // Check if key exists and hasn't expired\n if (existing) {\n const existingHash = existing.hash;\n // Check for hash mismatch\n if (hash && existingHash && existingHash !== hash) {\n return {\n status: 'mismatch',\n existingHash,\n providedHash: hash,\n };\n }\n\n // If completed, return cached response\n if (existing.status === 'completed') {\n const response = existing.response;\n if (response) {\n return { status: 'hit', response };\n }\n }\n\n // If in progress, return conflict\n if (existing.status === 'in_progress') {\n return { status: 'conflict', startedAt: existing.startedAt };\n }\n }\n\n // Acquire lock\n this.store.set(key, {\n status: 'in_progress',\n hash,\n startedAt: now,\n expiresAt: now + ttl,\n });\n\n return { status: 'acquired' };\n }\n\n async save(key: string, response: StoredResponse): Promise<void> {\n const existing = this.store.get(key);\n if (existing) {\n existing.status = 'completed';\n existing.response = response;\n }\n }\n\n async release(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n /**\n * Stop the cleanup interval.\n * Call this when shutting down to prevent memory leaks in tests.\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n\n private cleanup(): void {\n const now = Date.now();\n for (const [key, record] of this.store) {\n if (record.expiresAt <= now) {\n this.store.delete(key);\n }\n }\n }\n}\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n *\n * @example\n * ```typescript\n * import { oncely, memory } from 'oncely';\n *\n * const idempotency = oncely({ storage: memory });\n * ```\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n","import type { StorageAdapter, OncelyOptions, OncelyConfig } from './types.js';\nimport { MemoryStorage } from './memory.js';\n\n// Re-export GlobalConfig as alias for backwards compatibility\nexport type GlobalConfig = OncelyConfig;\n\n// Global configuration store\nlet globalConfig: OncelyConfig = {};\n\n// Lazy-initialized default memory storage\nlet defaultMemoryStorage: MemoryStorage | null = null;\n\n/**\n * Configure global defaults for oncely.\n * Call this once at application startup to set defaults for all oncely operations.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n * import { redis } from '@oncely/redis';\n *\n * // Set up once at app startup\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * onHit: (key) => console.log(`Cache hit: ${key}`),\n * });\n * ```\n */\nexport function configure(config: OncelyConfig): void {\n globalConfig = { ...config };\n}\n\n/**\n * Get the current global configuration.\n */\nexport function getConfig(): OncelyConfig {\n return globalConfig;\n}\n\n/**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\nexport function resetConfig(): void {\n globalConfig = {};\n defaultMemoryStorage = null;\n}\n\n/**\n * Get the default storage adapter.\n * Returns the configured global storage, or a shared memory instance.\n */\nexport function getDefaultStorage(): StorageAdapter {\n if (globalConfig.storage) {\n return globalConfig.storage;\n }\n // Lazy-init shared memory storage\n if (!defaultMemoryStorage) {\n defaultMemoryStorage = new MemoryStorage();\n }\n return defaultMemoryStorage;\n}\n\n/**\n * Resolve oncely options by merging with global config.\n */\nexport function resolveOptions(options?: Partial<OncelyOptions>): OncelyOptions {\n const config = getConfig();\n return {\n storage: options?.storage ?? config.storage ?? getDefaultStorage(),\n ttl: options?.ttl ?? config.ttl ?? '24h',\n fingerprint: options?.fingerprint ?? config.fingerprint ?? true,\n debug: options?.debug ?? config.debug ?? false,\n onHit: options?.onHit ?? config.onHit,\n onMiss: options?.onMiss ?? config.onMiss,\n onConflict: options?.onConflict ?? config.onConflict,\n onError: options?.onError ?? config.onError,\n onSkip: options?.onSkip ?? config.onSkip,\n };\n}\n","import type {\n StorageAdapter,\n OncelyOptions,\n RunOptions,\n RunResult,\n StoredResponse,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n OnSkipCallback,\n} from './types.js';\nimport { MissingKeyError, ConflictError, MismatchError, StorageError } from './errors.js';\nimport { parseTtl } from './utils.js';\nimport { resolveOptions } from './config.js';\n\n/**\n * Estimate the size of a value in bytes when serialized to JSON.\n */\nfunction estimatePayloadSize(value: unknown): number {\n try {\n return new TextEncoder().encode(JSON.stringify(value)).length;\n } catch {\n // If serialization fails, return 0 to allow normal flow\n // (save() will likely fail anyway)\n return 0;\n }\n}\n\n/**\n * Oncely idempotency service.\n * Ensures operations are executed exactly once per idempotency key.\n */\nexport class Oncely {\n private readonly storage: StorageAdapter;\n private readonly ttl: number;\n private readonly debug: boolean;\n private readonly onHit?: OnHitCallback;\n private readonly onMiss?: OnMissCallback;\n private readonly onConflict?: OnConflictCallback;\n private readonly onError?: OnErrorCallback;\n private readonly onSkip?: OnSkipCallback;\n\n constructor(options: OncelyOptions) {\n this.storage = options.storage;\n this.ttl = parseTtl(options.ttl ?? '24h');\n this.debug = options.debug ?? false;\n this.onHit = options.onHit;\n this.onMiss = options.onMiss;\n this.onConflict = options.onConflict;\n this.onError = options.onError;\n this.onSkip = options.onSkip;\n }\n\n /**\n * Run an operation with idempotency protection.\n *\n * @example\n * ```typescript\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => {\n * const order = await createOrder(data);\n * return order;\n * },\n * });\n *\n * if (result.cached) {\n * console.log('Returned cached response');\n * }\n * ```\n */\n async run<T>(options: RunOptions<T>): Promise<RunResult<T>> {\n const { key, hash = null, handler } = options;\n\n // Validate key\n if (!key) {\n throw new MissingKeyError();\n }\n\n this.log(`Acquiring lock for key: ${key}`);\n\n // Try to acquire lock\n let acquireResult;\n try {\n acquireResult = await this.storage.acquire(key, hash, this.ttl);\n } catch (err) {\n throw new StorageError('Failed to acquire lock', err as Error);\n }\n\n // Handle different acquire results\n switch (acquireResult.status) {\n case 'hit': {\n this.log(`Cache hit for key: ${key}`);\n this.onHit?.(key, acquireResult.response);\n return {\n data: acquireResult.response.data as T,\n cached: true,\n status: 'hit',\n createdAt: acquireResult.response.createdAt,\n };\n }\n\n case 'conflict': {\n this.log(`Conflict for key: ${key}`);\n const error = new ConflictError(acquireResult.startedAt);\n this.onConflict?.(key);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'mismatch': {\n this.log(`Hash mismatch for key: ${key}`);\n const error = new MismatchError(acquireResult.existingHash, acquireResult.providedHash);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'acquired': {\n this.log(`Lock acquired for key: ${key}`);\n this.onMiss?.(key);\n break;\n }\n }\n\n // Execute handler\n try {\n const data = await handler();\n const now = Date.now();\n\n // Save response\n const storedResponse: StoredResponse = {\n data,\n createdAt: now,\n hash,\n };\n\n // Check payload size limit\n const maxSize = this.storage.maxPayloadSize;\n if (maxSize !== undefined && maxSize > 0) {\n const payloadSize = estimatePayloadSize(storedResponse);\n if (payloadSize > maxSize) {\n this.log(\n `Payload too large for key: ${key} (${payloadSize} bytes > ${maxSize} bytes limit). Skipping cache.`\n );\n this.onSkip?.(key, 'payload_too_large', { payloadSize, maxSize });\n\n // Release lock and return without caching\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n }\n }\n\n try {\n await this.storage.save(key, storedResponse);\n this.log(`Saved response for key: ${key}`);\n } catch (err) {\n // Log but don't fail - the operation succeeded\n this.log(`Failed to save response for key: ${key} - ${err}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n } catch (err) {\n // Release lock on failure to allow retry\n this.log(`Handler failed for key: ${key}, releasing lock`);\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n throw err;\n }\n }\n\n private log(message: string): void {\n if (this.debug) {\n console.log(`[oncely] ${message}`);\n }\n }\n}\n\n/**\n * Create an oncely idempotency instance.\n *\n * Options are optional - will use global config or sensible defaults.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@oncely/core';\n *\n * // Zero-config (uses memory storage)\n * const idempotency = createInstance();\n *\n * // With explicit storage\n * const idempotency = createInstance({ storage: myRedis });\n *\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => createOrder(data),\n * });\n * ```\n */\nexport function createInstance(options?: Partial<OncelyOptions>): Oncely {\n return new Oncely(resolveOptions(options));\n}\n","import { Oncely, createInstance } from './oncely.js';\nimport { MemoryStorage } from './memory.js';\nimport {\n configure as configureGlobal,\n getConfig,\n resetConfig,\n getDefaultStorage,\n} from './config.js';\nimport { HEADER, HEADER_REPLAY } from './constants.js';\nimport type { StorageAdapter } from './types.js';\n\n/**\n * Oncely namespace object.\n * All oncely functionality is accessed through this namespace.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n *\n * // Configure globally\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * });\n *\n * // Create an instance\n * const instance = oncely.createInstance();\n * const result = await instance.run({\n * key: 'order-123',\n * handler: () => createOrder(data),\n * });\n * ```\n */\nexport const oncely = {\n /**\n * Configure global defaults for oncely.\n * Call this once at application startup.\n */\n configure: configureGlobal,\n\n /**\n * Get the current global configuration.\n */\n getConfig,\n\n /**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\n resetConfig,\n\n /**\n * Get the default storage adapter.\n */\n getDefaultStorage,\n\n /**\n * Create an oncely instance with optional configuration.\n * Uses global config merged with provided options.\n */\n createInstance,\n\n /**\n * Standard header name for idempotency keys.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n HEADER,\n\n /**\n * Header indicating a response was replayed from cache.\n */\n HEADER_REPLAY,\n} as const;\n\n/**\n * Interface for oncely namespace - can be extended via module augmentation.\n */\nexport interface OncelyNamespace {\n readonly configure: typeof configureGlobal;\n readonly getConfig: typeof getConfig;\n readonly resetConfig: typeof resetConfig;\n readonly getDefaultStorage: typeof getDefaultStorage;\n readonly createInstance: typeof createInstance;\n readonly HEADER: typeof HEADER;\n readonly HEADER_REPLAY: typeof HEADER_REPLAY;\n // Allow extension via module augmentation\n [key: string]: unknown;\n}\n\n// Re-export the Oncely class for advanced usage\nexport { Oncely };\n\n// Re-export memory storage\nexport { MemoryStorage };\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n\n// Re-export configuration functions for direct import (backwards compatibility)\nexport { configure, getConfig, resetConfig, getDefaultStorage } from './config.js';\nexport type { GlobalConfig } from './config.js';\n\n// Re-export constants\nexport { HEADER, HEADER_REPLAY } from './constants.js';\n\n// Re-export errors\nexport {\n IdempotencyError,\n MissingKeyError,\n ConflictError,\n MismatchError,\n StorageError,\n type ProblemDetails,\n} from './errors.js';\n\n// Re-export utilities\nexport {\n parseTtl,\n simpleHash,\n secureHash,\n hashObject,\n hashObjectSecure,\n generateKey,\n composeKey,\n} from './utils.js';\n\n// Re-export types\nexport type {\n StorageAdapter,\n AcquireResult,\n StoredResponse,\n OncelyOptions,\n OncelyConfig,\n RunOptions,\n RunResult,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n OnSkipCallback,\n SkipReason,\n SkipDetails,\n} from './types.js';\n"]}
|
package/dist/testing.cjs
CHANGED
|
@@ -4,6 +4,11 @@ require('crypto');
|
|
|
4
4
|
|
|
5
5
|
// src/memory.ts
|
|
6
6
|
var MemoryStorage = class {
|
|
7
|
+
/**
|
|
8
|
+
* Maximum payload size in bytes (default: 5MB).
|
|
9
|
+
* Prevents unbounded memory growth from large responses.
|
|
10
|
+
*/
|
|
11
|
+
maxPayloadSize = 5 * 1024 * 1024;
|
|
7
12
|
store = /* @__PURE__ */ new Map();
|
|
8
13
|
cleanupInterval = null;
|
|
9
14
|
constructor() {
|
|
@@ -257,11 +262,19 @@ function resolveOptions(options) {
|
|
|
257
262
|
onHit: options?.onHit ?? config.onHit,
|
|
258
263
|
onMiss: options?.onMiss ?? config.onMiss,
|
|
259
264
|
onConflict: options?.onConflict ?? config.onConflict,
|
|
260
|
-
onError: options?.onError ?? config.onError
|
|
265
|
+
onError: options?.onError ?? config.onError,
|
|
266
|
+
onSkip: options?.onSkip ?? config.onSkip
|
|
261
267
|
};
|
|
262
268
|
}
|
|
263
269
|
|
|
264
270
|
// src/oncely.ts
|
|
271
|
+
function estimatePayloadSize(value) {
|
|
272
|
+
try {
|
|
273
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
274
|
+
} catch {
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
265
278
|
var Oncely = class {
|
|
266
279
|
storage;
|
|
267
280
|
ttl;
|
|
@@ -270,6 +283,7 @@ var Oncely = class {
|
|
|
270
283
|
onMiss;
|
|
271
284
|
onConflict;
|
|
272
285
|
onError;
|
|
286
|
+
onSkip;
|
|
273
287
|
constructor(options) {
|
|
274
288
|
this.storage = options.storage;
|
|
275
289
|
this.ttl = parseTtl(options.ttl ?? "24h");
|
|
@@ -278,6 +292,7 @@ var Oncely = class {
|
|
|
278
292
|
this.onMiss = options.onMiss;
|
|
279
293
|
this.onConflict = options.onConflict;
|
|
280
294
|
this.onError = options.onError;
|
|
295
|
+
this.onSkip = options.onSkip;
|
|
281
296
|
}
|
|
282
297
|
/**
|
|
283
298
|
* Run an operation with idempotency protection.
|
|
@@ -347,6 +362,27 @@ var Oncely = class {
|
|
|
347
362
|
createdAt: now,
|
|
348
363
|
hash
|
|
349
364
|
};
|
|
365
|
+
const maxSize = this.storage.maxPayloadSize;
|
|
366
|
+
if (maxSize !== void 0 && maxSize > 0) {
|
|
367
|
+
const payloadSize = estimatePayloadSize(storedResponse);
|
|
368
|
+
if (payloadSize > maxSize) {
|
|
369
|
+
this.log(
|
|
370
|
+
`Payload too large for key: ${key} (${payloadSize} bytes > ${maxSize} bytes limit). Skipping cache.`
|
|
371
|
+
);
|
|
372
|
+
this.onSkip?.(key, "payload_too_large", { payloadSize, maxSize });
|
|
373
|
+
try {
|
|
374
|
+
await this.storage.release(key);
|
|
375
|
+
} catch (releaseErr) {
|
|
376
|
+
this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
data,
|
|
380
|
+
cached: false,
|
|
381
|
+
status: "created",
|
|
382
|
+
createdAt: now
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
350
386
|
try {
|
|
351
387
|
await this.storage.save(key, storedResponse);
|
|
352
388
|
this.log(`Saved response for key: ${key}`);
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/memory.ts","../src/constants.ts","../src/errors.ts","../src/utils.ts","../src/config.ts","../src/oncely.ts","../src/testing.ts"],"names":[],"mappings":"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","file":"testing.cjs","sourcesContent":["import type { StorageAdapter, AcquireResult, StoredResponse } from './types.js';\n\ninterface MemoryRecord {\n status: 'in_progress' | 'completed';\n hash: string | null;\n response?: StoredResponse;\n startedAt: number;\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter.\n * Suitable for development, testing, and single-instance deployments.\n *\n * **Warning:** Data is lost on restart and not shared across instances.\n * Use Redis for production multi-instance deployments.\n */\nexport class MemoryStorage implements StorageAdapter {\n private store = new Map<string, MemoryRecord>();\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor() {\n // Cleanup expired entries every 60 seconds\n this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);\n // Don't prevent Node from exiting\n if (this.cleanupInterval.unref) {\n this.cleanupInterval.unref();\n }\n }\n\n async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n const now = Date.now();\n let existing = this.store.get(key);\n\n if (existing && existing.expiresAt <= now) {\n this.store.delete(key);\n existing = undefined;\n }\n\n // Check if key exists and hasn't expired\n if (existing) {\n const existingHash = existing.hash;\n // Check for hash mismatch\n if (hash && existingHash && existingHash !== hash) {\n return {\n status: 'mismatch',\n existingHash,\n providedHash: hash,\n };\n }\n\n // If completed, return cached response\n if (existing.status === 'completed') {\n const response = existing.response;\n if (response) {\n return { status: 'hit', response };\n }\n }\n\n // If in progress, return conflict\n if (existing.status === 'in_progress') {\n return { status: 'conflict', startedAt: existing.startedAt };\n }\n }\n\n // Acquire lock\n this.store.set(key, {\n status: 'in_progress',\n hash,\n startedAt: now,\n expiresAt: now + ttl,\n });\n\n return { status: 'acquired' };\n }\n\n async save(key: string, response: StoredResponse): Promise<void> {\n const existing = this.store.get(key);\n if (existing) {\n existing.status = 'completed';\n existing.response = response;\n }\n }\n\n async release(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n /**\n * Stop the cleanup interval.\n * Call this when shutting down to prevent memory leaks in tests.\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n\n private cleanup(): void {\n const now = Date.now();\n for (const [key, record] of this.store) {\n if (record.expiresAt <= now) {\n this.store.delete(key);\n }\n }\n }\n}\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n *\n * @example\n * ```typescript\n * import { oncely, memory } from 'oncely';\n *\n * const idempotency = oncely({ storage: memory });\n * ```\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n","/**\n * HTTP header constants following IETF draft-ietf-httpapi-idempotency-key-header.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n\n/** Standard header name for idempotency keys */\nexport const HEADER = 'Idempotency-Key';\n\n/** Header indicating a response was replayed from cache */\nexport const HEADER_REPLAY = 'Idempotency-Replay';\n\n/** Base URL for error documentation */\nexport const DOCS_BASE_URL = 'https://oncely.dev/errors';\n","import { DOCS_BASE_URL } from './constants.js';\n\n/**\n * RFC 7807 Problem Details response format.\n * @see https://www.rfc-editor.org/rfc/rfc7807\n */\nexport interface ProblemDetails {\n /** URI reference identifying the problem type */\n type: string;\n /** Short human-readable summary */\n title: string;\n /** HTTP status code */\n status: number;\n /** Detailed human-readable explanation */\n detail: string;\n /** URI reference to the specific occurrence (optional) */\n instance?: string;\n /** Additional properties */\n [key: string]: unknown;\n}\n\n/**\n * Base class for all oncely errors.\n * Provides RFC 7807 Problem Details format for HTTP responses.\n */\nexport class IdempotencyError extends Error {\n /** HTTP status code for this error */\n readonly statusCode: number;\n /** Error type identifier (URL) */\n readonly type: string;\n /** Short title for the error */\n readonly title: string;\n\n constructor(message: string, statusCode: number, type: string, title: string) {\n super(message);\n this.name = 'IdempotencyError';\n this.statusCode = statusCode;\n this.type = type;\n this.title = title;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /**\n * Convert to RFC 7807 Problem Details format.\n */\n toProblemDetails(): ProblemDetails {\n return {\n type: this.type,\n title: this.title,\n status: this.statusCode,\n detail: this.message,\n };\n }\n\n /**\n * Convert to JSON (RFC 7807 format).\n */\n toJSON(): ProblemDetails {\n return this.toProblemDetails();\n }\n}\n\n/**\n * Thrown when an idempotency key is required but not provided.\n * HTTP 400 Bad Request\n */\nexport class MissingKeyError extends IdempotencyError {\n constructor() {\n super(\n 'This operation requires an Idempotency-Key header.',\n 400,\n `${DOCS_BASE_URL}/missing-key`,\n 'Idempotency-Key is missing'\n );\n this.name = 'MissingKeyError';\n }\n}\n\n/**\n * Thrown when a request with the same key is already being processed.\n * HTTP 409 Conflict\n */\nexport class ConflictError extends IdempotencyError {\n /** When the in-progress request started */\n readonly startedAt: number;\n /** Suggested retry delay in seconds */\n readonly retryAfter: number;\n\n constructor(startedAt: number) {\n const ageSeconds = Math.round((Date.now() - startedAt) / 1000);\n super(\n 'A request with the same Idempotency-Key is currently being processed.',\n 409,\n `${DOCS_BASE_URL}/conflict`,\n 'Request in progress'\n );\n this.name = 'ConflictError';\n this.startedAt = startedAt;\n this.retryAfter = Math.max(1, Math.min(5, ageSeconds));\n }\n\n override toProblemDetails(): ProblemDetails {\n return {\n ...super.toProblemDetails(),\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Thrown when the same idempotency key is used with a different request payload.\n * HTTP 422 Unprocessable Content\n */\nexport class MismatchError extends IdempotencyError {\n /** Hash of the original request */\n readonly existingHash: string;\n /** Hash of the current request */\n readonly providedHash: string;\n\n constructor(existingHash: string, providedHash: string) {\n super(\n 'This Idempotency-Key was used with a different request payload.',\n 422,\n `${DOCS_BASE_URL}/mismatch`,\n 'Idempotency-Key reused'\n );\n this.name = 'MismatchError';\n this.existingHash = existingHash;\n this.providedHash = providedHash;\n }\n}\n\n/**\n * Thrown when the storage adapter encounters an error.\n * HTTP 500 Internal Server Error\n */\nexport class StorageError extends IdempotencyError {\n /** The underlying error from the storage adapter */\n override readonly cause: Error;\n\n constructor(message: string, cause: Error) {\n super(`Storage error: ${message}`, 500, `${DOCS_BASE_URL}/storage-error`, 'Storage error');\n this.name = 'StorageError';\n this.cause = cause;\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\n\n/**\n * Parse a TTL string into milliseconds.\n * Supports: '30s', '5m', '24h', '7d'\n */\nexport function parseTtl(ttl: number | string): number {\n if (typeof ttl === 'number') {\n return ttl;\n }\n\n if (ttl.length < 2) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const unit = ttl[ttl.length - 1];\n if (unit !== 's' && unit !== 'm' && unit !== 'h' && unit !== 'd') {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const valueStr = ttl.slice(0, -1);\n if (!valueStr) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n for (let i = 0; i < valueStr.length; i++) {\n const code = valueStr.charCodeAt(i);\n if (code < 48 || code > 57) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n }\n\n const value = parseInt(valueStr, 10);\n\n switch (unit) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Invalid TTL unit: \"${unit}\"`);\n }\n}\n\n/**\n * Generate a simple hash from a string using DJB2 algorithm.\n *\n * **Note:** This is a fast, non-cryptographic hash suitable for request\n * fingerprinting. It has a 32-bit output space (~4 billion unique values).\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision (birthday problem). For high-volume payment systems or\n * security-sensitive applications, use {@link hashObjectSecure} instead.\n *\n * For typical API use cases with moderate volume, DJB2 provides excellent\n * performance (~2.4M ops/sec vs ~600K ops/sec for SHA-256).\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\n/**\n * Generate a SHA-256 hash from a string.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link simpleHash} but cryptographically secure.\n */\nexport function secureHash(str: string): string {\n return createHash('sha256').update(str).digest('hex');\n}\n\n/**\n * Hash an object by JSON stringifying it.\n *\n * Uses the fast DJB2 algorithm (~2.4M ops/sec). For high-volume payment\n * systems or security-sensitive applications where collision resistance\n * is critical, use {@link hashObjectSecure} instead.\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision. A collision could cause:\n * - False mismatch errors (if hashes differ)\n * - Or incorrectly matching different payloads (if hashes collide)\n *\n * **Note:** Object key order affects the hash. If you need order-independent\n * hashing, sort the keys before passing to this function.\n */\nexport function hashObject(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return simpleHash(str);\n}\n\n/**\n * Hash an object using SHA-256.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link hashObject} but cryptographically secure.\n */\nexport function hashObjectSecure(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return secureHash(str);\n}\n\n/**\n * Generate a cryptographically secure unique key (UUID v4).\n *\n * Uses Node.js crypto.randomUUID() for secure random generation.\n */\nexport function generateKey(): string {\n return randomUUID();\n}\n\n/**\n * Compose a deterministic key from multiple parts.\n */\nexport function composeKey(...parts: (string | number)[]): string {\n return parts.join(':');\n}\n","import type { StorageAdapter, OncelyOptions, OncelyConfig } from './types.js';\nimport { MemoryStorage } from './memory.js';\n\n// Re-export GlobalConfig as alias for backwards compatibility\nexport type GlobalConfig = OncelyConfig;\n\n// Global configuration store\nlet globalConfig: OncelyConfig = {};\n\n// Lazy-initialized default memory storage\nlet defaultMemoryStorage: MemoryStorage | null = null;\n\n/**\n * Configure global defaults for oncely.\n * Call this once at application startup to set defaults for all oncely operations.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n * import { redis } from '@oncely/redis';\n *\n * // Set up once at app startup\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * onHit: (key) => console.log(`Cache hit: ${key}`),\n * });\n * ```\n */\nexport function configure(config: OncelyConfig): void {\n globalConfig = { ...config };\n}\n\n/**\n * Get the current global configuration.\n */\nexport function getConfig(): OncelyConfig {\n return globalConfig;\n}\n\n/**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\nexport function resetConfig(): void {\n globalConfig = {};\n defaultMemoryStorage = null;\n}\n\n/**\n * Get the default storage adapter.\n * Returns the configured global storage, or a shared memory instance.\n */\nexport function getDefaultStorage(): StorageAdapter {\n if (globalConfig.storage) {\n return globalConfig.storage;\n }\n // Lazy-init shared memory storage\n if (!defaultMemoryStorage) {\n defaultMemoryStorage = new MemoryStorage();\n }\n return defaultMemoryStorage;\n}\n\n/**\n * Resolve oncely options by merging with global config.\n */\nexport function resolveOptions(options?: Partial<OncelyOptions>): OncelyOptions {\n const config = getConfig();\n return {\n storage: options?.storage ?? config.storage ?? getDefaultStorage(),\n ttl: options?.ttl ?? config.ttl ?? '24h',\n fingerprint: options?.fingerprint ?? config.fingerprint ?? true,\n debug: options?.debug ?? config.debug ?? false,\n onHit: options?.onHit ?? config.onHit,\n onMiss: options?.onMiss ?? config.onMiss,\n onConflict: options?.onConflict ?? config.onConflict,\n onError: options?.onError ?? config.onError,\n };\n}\n","import type {\n StorageAdapter,\n OncelyOptions,\n RunOptions,\n RunResult,\n StoredResponse,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n} from './types.js';\nimport { MissingKeyError, ConflictError, MismatchError, StorageError } from './errors.js';\nimport { parseTtl } from './utils.js';\nimport { resolveOptions } from './config.js';\n\n/**\n * Oncely idempotency service.\n * Ensures operations are executed exactly once per idempotency key.\n */\nexport class Oncely {\n private readonly storage: StorageAdapter;\n private readonly ttl: number;\n private readonly debug: boolean;\n private readonly onHit?: OnHitCallback;\n private readonly onMiss?: OnMissCallback;\n private readonly onConflict?: OnConflictCallback;\n private readonly onError?: OnErrorCallback;\n\n constructor(options: OncelyOptions) {\n this.storage = options.storage;\n this.ttl = parseTtl(options.ttl ?? '24h');\n this.debug = options.debug ?? false;\n this.onHit = options.onHit;\n this.onMiss = options.onMiss;\n this.onConflict = options.onConflict;\n this.onError = options.onError;\n }\n\n /**\n * Run an operation with idempotency protection.\n *\n * @example\n * ```typescript\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => {\n * const order = await createOrder(data);\n * return order;\n * },\n * });\n *\n * if (result.cached) {\n * console.log('Returned cached response');\n * }\n * ```\n */\n async run<T>(options: RunOptions<T>): Promise<RunResult<T>> {\n const { key, hash = null, handler } = options;\n\n // Validate key\n if (!key) {\n throw new MissingKeyError();\n }\n\n this.log(`Acquiring lock for key: ${key}`);\n\n // Try to acquire lock\n let acquireResult;\n try {\n acquireResult = await this.storage.acquire(key, hash, this.ttl);\n } catch (err) {\n throw new StorageError('Failed to acquire lock', err as Error);\n }\n\n // Handle different acquire results\n switch (acquireResult.status) {\n case 'hit': {\n this.log(`Cache hit for key: ${key}`);\n this.onHit?.(key, acquireResult.response);\n return {\n data: acquireResult.response.data as T,\n cached: true,\n status: 'hit',\n createdAt: acquireResult.response.createdAt,\n };\n }\n\n case 'conflict': {\n this.log(`Conflict for key: ${key}`);\n const error = new ConflictError(acquireResult.startedAt);\n this.onConflict?.(key);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'mismatch': {\n this.log(`Hash mismatch for key: ${key}`);\n const error = new MismatchError(acquireResult.existingHash, acquireResult.providedHash);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'acquired': {\n this.log(`Lock acquired for key: ${key}`);\n this.onMiss?.(key);\n break;\n }\n }\n\n // Execute handler\n try {\n const data = await handler();\n const now = Date.now();\n\n // Save response\n const storedResponse: StoredResponse = {\n data,\n createdAt: now,\n hash,\n };\n\n try {\n await this.storage.save(key, storedResponse);\n this.log(`Saved response for key: ${key}`);\n } catch (err) {\n // Log but don't fail - the operation succeeded\n this.log(`Failed to save response for key: ${key} - ${err}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n } catch (err) {\n // Release lock on failure to allow retry\n this.log(`Handler failed for key: ${key}, releasing lock`);\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n throw err;\n }\n }\n\n private log(message: string): void {\n if (this.debug) {\n console.log(`[oncely] ${message}`);\n }\n }\n}\n\n/**\n * Create an oncely idempotency instance.\n *\n * Options are optional - will use global config or sensible defaults.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@oncely/core';\n *\n * // Zero-config (uses memory storage)\n * const idempotency = createInstance();\n *\n * // With explicit storage\n * const idempotency = createInstance({ storage: myRedis });\n *\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => createOrder(data),\n * });\n * ```\n */\nexport function createInstance(options?: Partial<OncelyOptions>): Oncely {\n return new Oncely(resolveOptions(options));\n}\n","import { MemoryStorage } from './memory.js';\nimport { createInstance } from './oncely.js';\nimport { resetConfig, configure } from './config.js';\nimport type { OncelyOptions, OncelyConfig, StoredResponse, AcquireResult } from './types.js';\n\n/**\n * Mock storage for testing.\n * Same as MemoryStorage but with additional test utilities.\n */\nexport class MockStorage extends MemoryStorage {\n /** Track all operations for assertions */\n readonly operations: Array<{ type: string; key: string; timestamp: number }> = [];\n\n /** Simulated conflict keys */\n private simulatedConflicts = new Set<string>();\n\n /** Simulated mismatch keys */\n private simulatedMismatches = new Map<string, { existingHash: string; providedHash: string }>();\n\n override async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n this.operations.push({ type: 'acquire', key, timestamp: Date.now() });\n\n // Check for simulated conflict\n if (this.simulatedConflicts.has(key)) {\n return { status: 'conflict', startedAt: Date.now() - 1000 };\n }\n\n // Check for simulated mismatch\n const mismatch = this.simulatedMismatches.get(key);\n if (mismatch) {\n return { status: 'mismatch', ...mismatch };\n }\n\n return super.acquire(key, hash, ttl);\n }\n\n override async save(key: string, response: StoredResponse): Promise<void> {\n this.operations.push({ type: 'save', key, timestamp: Date.now() });\n return super.save(key, response);\n }\n\n override async release(key: string): Promise<void> {\n this.operations.push({ type: 'release', key, timestamp: Date.now() });\n return super.release(key);\n }\n\n override async delete(key: string): Promise<void> {\n this.operations.push({ type: 'delete', key, timestamp: Date.now() });\n return super.delete(key);\n }\n\n override async clear(): Promise<void> {\n this.operations.length = 0;\n this.simulatedConflicts.clear();\n this.simulatedMismatches.clear();\n return super.clear();\n }\n\n /**\n * Get operations for a specific key.\n */\n getOperationsForKey(key: string) {\n return this.operations.filter((op) => op.key === key);\n }\n\n /**\n * Assert that a key was acquired.\n */\n assertAcquired(key: string): void {\n const acquired = this.operations.some((op) => op.type === 'acquire' && op.key === key);\n if (!acquired) {\n throw new Error(`Expected key \"${key}\" to be acquired, but it was not.`);\n }\n }\n\n /**\n * Assert that a key was saved (cached).\n */\n assertCached(key: string): void {\n const saved = this.operations.some((op) => op.type === 'save' && op.key === key);\n if (!saved) {\n throw new Error(`Expected key \"${key}\" to be cached, but it was not.`);\n }\n }\n\n /**\n * Assert that a key was NOT saved (not cached).\n */\n assertNotCached(key: string): void {\n const saved = this.operations.some((op) => op.type === 'save' && op.key === key);\n if (saved) {\n throw new Error(`Expected key \"${key}\" to NOT be cached, but it was.`);\n }\n }\n\n /**\n * Assert that a key was released.\n */\n assertReleased(key: string): void {\n const released = this.operations.some((op) => op.type === 'release' && op.key === key);\n if (!released) {\n throw new Error(`Expected key \"${key}\" to be released, but it was not.`);\n }\n }\n\n /**\n * Simulate a conflict error for a specific key.\n * The next acquire for this key will return conflict status.\n */\n simulateConflict(key: string): void {\n this.simulatedConflicts.add(key);\n }\n\n /**\n * Simulate a mismatch error for a specific key.\n * The next acquire for this key will return mismatch status.\n */\n simulateMismatch(\n key: string,\n existingHash = 'existing-hash',\n providedHash = 'provided-hash'\n ): void {\n this.simulatedMismatches.set(key, { existingHash, providedHash });\n }\n\n /**\n * Clear all simulations.\n */\n clearSimulations(): void {\n this.simulatedConflicts.clear();\n this.simulatedMismatches.clear();\n }\n}\n\n/**\n * Create an oncely instance configured for testing.\n *\n * @example\n * ```typescript\n * import { createTestInstance, MockStorage } from '@oncely/core/testing';\n *\n * describe('my api', () => {\n * const storage = new MockStorage();\n * const idempotency = createTestInstance({ storage });\n *\n * beforeEach(() => storage.clear());\n *\n * it('handles duplicate requests', async () => {\n * const handler = vi.fn().mockResolvedValue({ id: 1 });\n *\n * await idempotency.run({ key: 'test', handler });\n * await idempotency.run({ key: 'test', handler });\n *\n * expect(handler).toHaveBeenCalledTimes(1);\n * storage.assertCached('test');\n * });\n * });\n * ```\n */\nexport function createTestInstance(options?: Partial<OncelyOptions>) {\n return createInstance({\n storage: options?.storage ?? new MockStorage(),\n ttl: options?.ttl ?? '1h',\n debug: options?.debug ?? false,\n ...options,\n });\n}\n\n/**\n * @deprecated Use createTestInstance instead.\n */\nexport const createTestOncely = createTestInstance;\n\n/**\n * Set up oncely for testing with auto-reset between tests.\n * Returns utilities for testing assertions.\n *\n * @example\n * ```typescript\n * import { setupTest } from '@oncely/core/testing';\n *\n * describe('my api', () => {\n * const { storage, instance, reset } = setupTest();\n *\n * beforeEach(() => reset());\n *\n * it('caches responses', async () => {\n * await instance.run({ key: 'test', handler: () => ({ ok: true }) });\n * storage.assertCached('test');\n * });\n * });\n * ```\n */\nexport function setupTest(config?: OncelyConfig) {\n const storage = new MockStorage();\n const instance = createInstance({ storage, ...config });\n\n return {\n storage,\n instance,\n reset: () => {\n storage.clear();\n resetConfig();\n if (config) {\n configure(config);\n }\n },\n };\n}\n\n// Re-export useful types\nexport type {\n StorageAdapter,\n StoredResponse,\n OncelyOptions,\n OncelyConfig,\n RunOptions,\n RunResult,\n AcquireResult,\n} from './types.js';\n\n// Re-export errors for test assertions\nexport {\n IdempotencyError,\n MissingKeyError,\n ConflictError,\n MismatchError,\n StorageError,\n} from './errors.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/memory.ts","../src/constants.ts","../src/errors.ts","../src/utils.ts","../src/config.ts","../src/oncely.ts","../src/testing.ts"],"names":[],"mappings":";;;;;AAiBO,IAAM,gBAAN,MAA8C;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1C,cAAA,GAAyB,IAAI,IAAA,GAAO,IAAA;AAAA,EAErC,KAAA,uBAAY,GAAA,EAA0B;AAAA,EACtC,eAAA,GAAyD,IAAA;AAAA,EAEjE,WAAA,GAAc;AAEZ,IAAA,IAAA,CAAK,kBAAkB,WAAA,CAAY,MAAM,IAAA,CAAK,OAAA,IAAW,GAAM,CAAA;AAE/D,IAAA,IAAI,IAAA,CAAK,gBAAgB,KAAA,EAAO;AAC9B,MAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,GAAA,EAAa,IAAA,EAAqB,GAAA,EAAqC;AACnF,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAEjC,IAAA,IAAI,QAAA,IAAY,QAAA,CAAS,SAAA,IAAa,GAAA,EAAK;AACzC,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,QAAA,GAAW,MAAA;AAAA,IACb;AAGA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,eAAe,QAAA,CAAS,IAAA;AAE9B,MAAA,IAAI,IAAA,IAAQ,YAAA,IAAgB,YAAA,KAAiB,IAAA,EAAM;AACjD,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ,UAAA;AAAA,UACR,YAAA;AAAA,UACA,YAAA,EAAc;AAAA,SAChB;AAAA,MACF;AAGA,MAAA,IAAI,QAAA,CAAS,WAAW,WAAA,EAAa;AACnC,QAAA,MAAM,WAAW,QAAA,CAAS,QAAA;AAC1B,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,QAAA,EAAS;AAAA,QACnC;AAAA,MACF;AAGA,MAAA,IAAI,QAAA,CAAS,WAAW,aAAA,EAAe;AACrC,QAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,SAAA,EAAW,SAAS,SAAA,EAAU;AAAA,MAC7D;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,KAAA,CAAM,IAAI,GAAA,EAAK;AAAA,MAClB,MAAA,EAAQ,aAAA;AAAA,MACR,IAAA;AAAA,MACA,SAAA,EAAW,GAAA;AAAA,MACX,WAAW,GAAA,GAAM;AAAA,KAClB,CAAA;AAED,IAAA,OAAO,EAAE,QAAQ,UAAA,EAAW;AAAA,EAC9B;AAAA,EAEA,MAAM,IAAA,CAAK,GAAA,EAAa,QAAA,EAAyC;AAC/D,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AACnC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,MAAA,GAAS,WAAA;AAClB,MAAA,QAAA,CAAS,QAAA,GAAW,QAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,GAAA,EAA4B;AACxC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,GAAA,EAA4B;AACvC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,aAAA,CAAc,KAAK,eAAe,CAAA;AAClC,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,OAAA,GAAgB;AACtB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,MAAM,CAAA,IAAK,KAAK,KAAA,EAAO;AACtC,MAAA,IAAI,MAAA,CAAO,aAAa,GAAA,EAAK;AAC3B,QAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF,CAAA;AAasC,IAAI,aAAA;;;AC1HnC,IAAM,aAAA,GAAgB,2BAAA;;;ACatB,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAA;AAAA,EAEjC,UAAA;AAAA;AAAA,EAEA,IAAA;AAAA;AAAA,EAEA,KAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,UAAA,EAAoB,IAAA,EAAc,KAAA,EAAe;AAC5E,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAA,GAAmC;AACjC,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,QAAQ,IAAA,CAAK,UAAA;AAAA,MACb,QAAQ,IAAA,CAAK;AAAA,KACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAyB;AACvB,IAAA,OAAO,KAAK,gBAAA,EAAiB;AAAA,EAC/B;AACF;AAMO,IAAM,eAAA,GAAN,cAA8B,gBAAA,CAAiB;AAAA,EACpD,WAAA,GAAc;AACZ,IAAA,KAAA;AAAA,MACE,oDAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,YAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAMO,IAAM,aAAA,GAAN,cAA4B,gBAAA,CAAiB;AAAA;AAAA,EAEzC,SAAA;AAAA;AAAA,EAEA,UAAA;AAAA,EAET,YAAY,SAAA,EAAmB;AAC7B,IAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAA,CAAO,KAAK,GAAA,EAAI,GAAI,aAAa,GAAI,CAAA;AAC7D,IAAA,KAAA;AAAA,MACE,uEAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,SAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAC,CAAA;AAAA,EACvD;AAAA,EAES,gBAAA,GAAmC;AAC1C,IAAA,OAAO;AAAA,MACL,GAAG,MAAM,gBAAA,EAAiB;AAAA,MAC1B,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,EACF;AACF;AAMO,IAAM,aAAA,GAAN,cAA4B,gBAAA,CAAiB;AAAA;AAAA,EAEzC,YAAA;AAAA;AAAA,EAEA,YAAA;AAAA,EAET,WAAA,CAAY,cAAsB,YAAA,EAAsB;AACtD,IAAA,KAAA;AAAA,MACE,iEAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG,aAAa,CAAA,SAAA,CAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AACpB,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AACF;AAMO,IAAM,YAAA,GAAN,cAA2B,gBAAA,CAAiB;AAAA;AAAA,EAE/B,KAAA;AAAA,EAElB,WAAA,CAAY,SAAiB,KAAA,EAAc;AACzC,IAAA,KAAA,CAAM,kBAAkB,OAAO,CAAA,CAAA,EAAI,KAAK,CAAA,EAAG,aAAa,kBAAkB,eAAe,CAAA;AACzF,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AACF;AC3IO,SAAS,SAAS,GAAA,EAA8B;AACrD,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG;AAClB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA;AAC/B,EAAA,IAAI,SAAS,GAAA,IAAO,IAAA,KAAS,OAAO,IAAA,KAAS,GAAA,IAAO,SAAS,GAAA,EAAK;AAChE,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAChC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AACxC,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,UAAA,CAAW,CAAC,CAAA;AAClC,IAAA,IAAI,IAAA,GAAO,EAAA,IAAM,IAAA,GAAO,EAAA,EAAI;AAC1B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,wBAAwB,GAAG,CAAA,uEAAA;AAAA,OAC7B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AAEnC,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,GAAA;AAAA,IACjB,KAAK,GAAA;AACH,MAAA,OAAO,QAAQ,EAAA,GAAK,GAAA;AAAA,IACtB,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AAAA,IAC3B,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAAA,IAChC;AACE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mBAAA,EAAsB,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA;AAEnD;;;AC/CA,IAAI,eAA6B,EAAC;AAGlC,IAAI,oBAAA,GAA6C,IAAA;AAmB1C,SAAS,UAAU,MAAA,EAA4B;AACpD,EAAA,YAAA,GAAe,EAAE,GAAG,MAAA,EAAO;AAC7B;AAKO,SAAS,SAAA,GAA0B;AACxC,EAAA,OAAO,YAAA;AACT;AAMO,SAAS,WAAA,GAAoB;AAClC,EAAA,YAAA,GAAe,EAAC;AAChB,EAAA,oBAAA,GAAuB,IAAA;AACzB;AAMO,SAAS,iBAAA,GAAoC;AAClD,EAAA,IAAI,aAAa,OAAA,EAAS;AACxB,IAAA,OAAO,YAAA,CAAa,OAAA;AAAA,EACtB;AAEA,EAAA,IAAI,CAAC,oBAAA,EAAsB;AACzB,IAAA,oBAAA,GAAuB,IAAI,aAAA,EAAc;AAAA,EAC3C;AACA,EAAA,OAAO,oBAAA;AACT;AAKO,SAAS,eAAe,OAAA,EAAiD;AAC9E,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,OAAA,EAAS,OAAA,IAAW,MAAA,CAAO,WAAW,iBAAA,EAAkB;AAAA,IACjE,GAAA,EAAK,OAAA,EAAS,GAAA,IAAO,MAAA,CAAO,GAAA,IAAO,KAAA;AAAA,IACnC,WAAA,EAAa,OAAA,EAAS,WAAA,IAAe,MAAA,CAAO,WAAA,IAAe,IAAA;AAAA,IAC3D,KAAA,EAAO,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,KAAA,IAAS,KAAA;AAAA,IACzC,KAAA,EAAO,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,KAAA;AAAA,IAChC,MAAA,EAAQ,OAAA,EAAS,MAAA,IAAU,MAAA,CAAO,MAAA;AAAA,IAClC,UAAA,EAAY,OAAA,EAAS,UAAA,IAAc,MAAA,CAAO,UAAA;AAAA,IAC1C,OAAA,EAAS,OAAA,EAAS,OAAA,IAAW,MAAA,CAAO,OAAA;AAAA,IACpC,MAAA,EAAQ,OAAA,EAAS,MAAA,IAAU,MAAA,CAAO;AAAA,GACpC;AACF;;;AC7DA,SAAS,oBAAoB,KAAA,EAAwB;AACnD,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,aAAY,CAAE,MAAA,CAAO,KAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAE,MAAA;AAAA,EACzD,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,CAAA;AAAA,EACT;AACF;AAMO,IAAM,SAAN,MAAa;AAAA,EACD,OAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EAEjB,YAAY,OAAA,EAAwB;AAClC,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,GAAA,GAAM,QAAA,CAAS,OAAA,CAAQ,GAAA,IAAO,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAC9B,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACrB,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,IAAO,OAAA,EAA+C;AAC1D,IAAA,MAAM,EAAE,GAAA,EAAK,IAAA,GAAO,IAAA,EAAM,SAAQ,GAAI,OAAA;AAGtC,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,IAAI,eAAA,EAAgB;AAAA,IAC5B;AAEA,IAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,CAAE,CAAA;AAGzC,IAAA,IAAI,aAAA;AACJ,IAAA,IAAI;AACF,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,GAAA,EAAK,IAAA,EAAM,KAAK,GAAG,CAAA;AAAA,IAChE,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,IAAI,YAAA,CAAa,wBAAA,EAA0B,GAAY,CAAA;AAAA,IAC/D;AAGA,IAAA,QAAQ,cAAc,MAAA;AAAQ,MAC5B,KAAK,KAAA,EAAO;AACV,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,mBAAA,EAAsB,GAAG,CAAA,CAAE,CAAA;AACpC,QAAA,IAAA,CAAK,KAAA,GAAQ,GAAA,EAAK,aAAA,CAAc,QAAQ,CAAA;AACxC,QAAA,OAAO;AAAA,UACL,IAAA,EAAM,cAAc,QAAA,CAAS,IAAA;AAAA,UAC7B,MAAA,EAAQ,IAAA;AAAA,UACR,MAAA,EAAQ,KAAA;AAAA,UACR,SAAA,EAAW,cAAc,QAAA,CAAS;AAAA,SACpC;AAAA,MACF;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,kBAAA,EAAqB,GAAG,CAAA,CAAE,CAAA;AACnC,QAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,aAAA,CAAc,SAAS,CAAA;AACvD,QAAA,IAAA,CAAK,aAAa,GAAG,CAAA;AACrB,QAAA,IAAA,CAAK,OAAA,GAAU,KAAK,KAAK,CAAA;AACzB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,uBAAA,EAA0B,GAAG,CAAA,CAAE,CAAA;AACxC,QAAA,MAAM,QAAQ,IAAI,aAAA,CAAc,aAAA,CAAc,YAAA,EAAc,cAAc,YAAY,CAAA;AACtF,QAAA,IAAA,CAAK,OAAA,GAAU,KAAK,KAAK,CAAA;AACzB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,MAEA,KAAK,UAAA,EAAY;AACf,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,uBAAA,EAA0B,GAAG,CAAA,CAAE,CAAA;AACxC,QAAA,IAAA,CAAK,SAAS,GAAG,CAAA;AACjB,QAAA;AAAA,MACF;AAAA;AAIF,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,OAAA,EAAQ;AAC3B,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,MAAA,MAAM,cAAA,GAAiC;AAAA,QACrC,IAAA;AAAA,QACA,SAAA,EAAW,GAAA;AAAA,QACX;AAAA,OACF;AAGA,MAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,cAAA;AAC7B,MAAA,IAAI,OAAA,KAAY,KAAA,CAAA,IAAa,OAAA,GAAU,CAAA,EAAG;AACxC,QAAA,MAAM,WAAA,GAAc,oBAAoB,cAAc,CAAA;AACtD,QAAA,IAAI,cAAc,OAAA,EAAS;AACzB,UAAA,IAAA,CAAK,GAAA;AAAA,YACH,CAAA,2BAAA,EAA8B,GAAG,CAAA,EAAA,EAAK,WAAW,YAAY,OAAO,CAAA,8BAAA;AAAA,WACtE;AACA,UAAA,IAAA,CAAK,SAAS,GAAA,EAAK,mBAAA,EAAqB,EAAE,WAAA,EAAa,SAAS,CAAA;AAGhE,UAAA,IAAI;AACF,YAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAAA,UAChC,SAAS,UAAA,EAAY;AACnB,YAAA,IAAA,CAAK,GAAA,CAAI,CAAA,gCAAA,EAAmC,GAAG,CAAA,GAAA,EAAM,UAAU,CAAA,CAAE,CAAA;AAAA,UACnE;AAEA,UAAA,OAAO;AAAA,YACL,IAAA;AAAA,YACA,MAAA,EAAQ,KAAA;AAAA,YACR,MAAA,EAAQ,SAAA;AAAA,YACR,SAAA,EAAW;AAAA,WACb;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,GAAA,EAAK,cAAc,CAAA;AAC3C,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,CAAE,CAAA;AAAA,MAC3C,SAAS,GAAA,EAAK;AAEZ,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,iCAAA,EAAoC,GAAG,CAAA,GAAA,EAAM,GAAG,CAAA,CAAE,CAAA;AAAA,MAC7D;AAEA,MAAA,OAAO;AAAA,QACL,IAAA;AAAA,QACA,MAAA,EAAQ,KAAA;AAAA,QACR,MAAA,EAAQ,SAAA;AAAA,QACR,SAAA,EAAW;AAAA,OACb;AAAA,IACF,SAAS,GAAA,EAAK;AAEZ,MAAA,IAAA,CAAK,GAAA,CAAI,CAAA,wBAAA,EAA2B,GAAG,CAAA,gBAAA,CAAkB,CAAA;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAAA,MAChC,SAAS,UAAA,EAAY;AACnB,QAAA,IAAA,CAAK,GAAA,CAAI,CAAA,gCAAA,EAAmC,GAAG,CAAA,GAAA,EAAM,UAAU,CAAA,CAAE,CAAA;AAAA,MACnE;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,IAAI,OAAA,EAAuB;AACjC,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,SAAA,EAAY,OAAO,CAAA,CAAE,CAAA;AAAA,IACnC;AAAA,EACF;AACF,CAAA;AAuBO,SAAS,eAAe,OAAA,EAA0C;AACvE,EAAA,OAAO,IAAI,MAAA,CAAO,cAAA,CAAe,OAAO,CAAC,CAAA;AAC3C;;;AClNO,IAAM,WAAA,GAAN,cAA0B,aAAA,CAAc;AAAA;AAAA,EAEpC,aAAsE,EAAC;AAAA;AAAA,EAGxE,kBAAA,uBAAyB,GAAA,EAAY;AAAA;AAAA,EAGrC,mBAAA,uBAA0B,GAAA,EAA4D;AAAA,EAE9F,MAAe,OAAA,CAAQ,GAAA,EAAa,IAAA,EAAqB,GAAA,EAAqC;AAC5F,IAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,EAAE,IAAA,EAAM,SAAA,EAAW,KAAK,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,EAAG,CAAA;AAGpE,IAAA,IAAI,IAAA,CAAK,kBAAA,CAAmB,GAAA,CAAI,GAAG,CAAA,EAAG;AACpC,MAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,WAAW,IAAA,CAAK,GAAA,KAAQ,GAAA,EAAK;AAAA,IAC5D;AAGA,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,mBAAA,CAAoB,GAAA,CAAI,GAAG,CAAA;AACjD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,GAAG,QAAA,EAAS;AAAA,IAC3C;AAEA,IAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAA,EAAK,IAAA,EAAM,GAAG,CAAA;AAAA,EACrC;AAAA,EAEA,MAAe,IAAA,CAAK,GAAA,EAAa,QAAA,EAAyC;AACxE,IAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAK,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,EAAG,CAAA;AACjE,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAA;AAAA,EACjC;AAAA,EAEA,MAAe,QAAQ,GAAA,EAA4B;AACjD,IAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,EAAE,IAAA,EAAM,SAAA,EAAW,KAAK,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,EAAG,CAAA;AACpE,IAAA,OAAO,KAAA,CAAM,QAAQ,GAAG,CAAA;AAAA,EAC1B;AAAA,EAEA,MAAe,OAAO,GAAA,EAA4B;AAChD,IAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,EAAE,IAAA,EAAM,QAAA,EAAU,KAAK,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,EAAG,CAAA;AACnE,IAAA,OAAO,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACzB;AAAA,EAEA,MAAe,KAAA,GAAuB;AACpC,IAAA,IAAA,CAAK,WAAW,MAAA,GAAS,CAAA;AACzB,IAAA,IAAA,CAAK,mBAAmB,KAAA,EAAM;AAC9B,IAAA,IAAA,CAAK,oBAAoB,KAAA,EAAM;AAC/B,IAAA,OAAO,MAAM,KAAA,EAAM;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,GAAA,EAAa;AAC/B,IAAA,OAAO,KAAK,UAAA,CAAW,MAAA,CAAO,CAAC,EAAA,KAAO,EAAA,CAAG,QAAQ,GAAG,CAAA;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,GAAA,EAAmB;AAChC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,SAAA,IAAa,EAAA,CAAG,GAAA,KAAQ,GAAG,CAAA;AACrF,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAG,CAAA,iCAAA,CAAmC,CAAA;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,GAAA,EAAmB;AAC9B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,MAAA,IAAU,EAAA,CAAG,GAAA,KAAQ,GAAG,CAAA;AAC/E,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAG,CAAA,+BAAA,CAAiC,CAAA;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,GAAA,EAAmB;AACjC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,MAAA,IAAU,EAAA,CAAG,GAAA,KAAQ,GAAG,CAAA;AAC/E,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAG,CAAA,+BAAA,CAAiC,CAAA;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,GAAA,EAAmB;AAChC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,SAAA,IAAa,EAAA,CAAG,GAAA,KAAQ,GAAG,CAAA;AACrF,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAG,CAAA,iCAAA,CAAmC,CAAA;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,GAAA,EAAmB;AAClC,IAAA,IAAA,CAAK,kBAAA,CAAmB,IAAI,GAAG,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAA,CACE,GAAA,EACA,YAAA,GAAe,eAAA,EACf,eAAe,eAAA,EACT;AACN,IAAA,IAAA,CAAK,oBAAoB,GAAA,CAAI,GAAA,EAAK,EAAE,YAAA,EAAc,cAAc,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAA,GAAyB;AACvB,IAAA,IAAA,CAAK,mBAAmB,KAAA,EAAM;AAC9B,IAAA,IAAA,CAAK,oBAAoB,KAAA,EAAM;AAAA,EACjC;AACF;AA2BO,SAAS,mBAAmB,OAAA,EAAkC;AACnE,EAAA,OAAO,cAAA,CAAe;AAAA,IACpB,OAAA,EAAS,OAAA,EAAS,OAAA,IAAW,IAAI,WAAA,EAAY;AAAA,IAC7C,GAAA,EAAK,SAAS,GAAA,IAAO,IAAA;AAAA,IACrB,KAAA,EAAO,SAAS,KAAA,IAAS,KAAA;AAAA,IACzB,GAAG;AAAA,GACJ,CAAA;AACH;AAKO,IAAM,gBAAA,GAAmB;AAsBzB,SAAS,UAAU,MAAA,EAAuB;AAC/C,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,WAAW,cAAA,CAAe,EAAE,OAAA,EAAS,GAAG,QAAQ,CAAA;AAEtD,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAO,MAAM;AACX,MAAA,OAAA,CAAQ,KAAA,EAAM;AACd,MAAA,WAAA,EAAY;AACZ,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB;AAAA,IACF;AAAA,GACF;AACF","file":"testing.cjs","sourcesContent":["import type { StorageAdapter, AcquireResult, StoredResponse } from './types.js';\n\ninterface MemoryRecord {\n status: 'in_progress' | 'completed';\n hash: string | null;\n response?: StoredResponse;\n startedAt: number;\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter.\n * Suitable for development, testing, and single-instance deployments.\n *\n * **Warning:** Data is lost on restart and not shared across instances.\n * Use Redis for production multi-instance deployments.\n */\nexport class MemoryStorage implements StorageAdapter {\n /**\n * Maximum payload size in bytes (default: 5MB).\n * Prevents unbounded memory growth from large responses.\n */\n readonly maxPayloadSize: number = 5 * 1024 * 1024;\n\n private store = new Map<string, MemoryRecord>();\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor() {\n // Cleanup expired entries every 60 seconds\n this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);\n // Don't prevent Node from exiting\n if (this.cleanupInterval.unref) {\n this.cleanupInterval.unref();\n }\n }\n\n async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n const now = Date.now();\n let existing = this.store.get(key);\n\n if (existing && existing.expiresAt <= now) {\n this.store.delete(key);\n existing = undefined;\n }\n\n // Check if key exists and hasn't expired\n if (existing) {\n const existingHash = existing.hash;\n // Check for hash mismatch\n if (hash && existingHash && existingHash !== hash) {\n return {\n status: 'mismatch',\n existingHash,\n providedHash: hash,\n };\n }\n\n // If completed, return cached response\n if (existing.status === 'completed') {\n const response = existing.response;\n if (response) {\n return { status: 'hit', response };\n }\n }\n\n // If in progress, return conflict\n if (existing.status === 'in_progress') {\n return { status: 'conflict', startedAt: existing.startedAt };\n }\n }\n\n // Acquire lock\n this.store.set(key, {\n status: 'in_progress',\n hash,\n startedAt: now,\n expiresAt: now + ttl,\n });\n\n return { status: 'acquired' };\n }\n\n async save(key: string, response: StoredResponse): Promise<void> {\n const existing = this.store.get(key);\n if (existing) {\n existing.status = 'completed';\n existing.response = response;\n }\n }\n\n async release(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n /**\n * Stop the cleanup interval.\n * Call this when shutting down to prevent memory leaks in tests.\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n\n private cleanup(): void {\n const now = Date.now();\n for (const [key, record] of this.store) {\n if (record.expiresAt <= now) {\n this.store.delete(key);\n }\n }\n }\n}\n\n/**\n * Pre-configured memory storage instance.\n * Use this for quick setup in development.\n *\n * @example\n * ```typescript\n * import { oncely, memory } from 'oncely';\n *\n * const idempotency = oncely({ storage: memory });\n * ```\n */\nexport const memory: StorageAdapter = new MemoryStorage();\n","/**\n * HTTP header constants following IETF draft-ietf-httpapi-idempotency-key-header.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header\n */\n\n/** Standard header name for idempotency keys */\nexport const HEADER = 'Idempotency-Key';\n\n/** Header indicating a response was replayed from cache */\nexport const HEADER_REPLAY = 'Idempotency-Replay';\n\n/** Base URL for error documentation */\nexport const DOCS_BASE_URL = 'https://oncely.dev/errors';\n","import { DOCS_BASE_URL } from './constants.js';\n\n/**\n * RFC 7807 Problem Details response format.\n * @see https://www.rfc-editor.org/rfc/rfc7807\n */\nexport interface ProblemDetails {\n /** URI reference identifying the problem type */\n type: string;\n /** Short human-readable summary */\n title: string;\n /** HTTP status code */\n status: number;\n /** Detailed human-readable explanation */\n detail: string;\n /** URI reference to the specific occurrence (optional) */\n instance?: string;\n /** Additional properties */\n [key: string]: unknown;\n}\n\n/**\n * Base class for all oncely errors.\n * Provides RFC 7807 Problem Details format for HTTP responses.\n */\nexport class IdempotencyError extends Error {\n /** HTTP status code for this error */\n readonly statusCode: number;\n /** Error type identifier (URL) */\n readonly type: string;\n /** Short title for the error */\n readonly title: string;\n\n constructor(message: string, statusCode: number, type: string, title: string) {\n super(message);\n this.name = 'IdempotencyError';\n this.statusCode = statusCode;\n this.type = type;\n this.title = title;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /**\n * Convert to RFC 7807 Problem Details format.\n */\n toProblemDetails(): ProblemDetails {\n return {\n type: this.type,\n title: this.title,\n status: this.statusCode,\n detail: this.message,\n };\n }\n\n /**\n * Convert to JSON (RFC 7807 format).\n */\n toJSON(): ProblemDetails {\n return this.toProblemDetails();\n }\n}\n\n/**\n * Thrown when an idempotency key is required but not provided.\n * HTTP 400 Bad Request\n */\nexport class MissingKeyError extends IdempotencyError {\n constructor() {\n super(\n 'This operation requires an Idempotency-Key header.',\n 400,\n `${DOCS_BASE_URL}/missing-key`,\n 'Idempotency-Key is missing'\n );\n this.name = 'MissingKeyError';\n }\n}\n\n/**\n * Thrown when a request with the same key is already being processed.\n * HTTP 409 Conflict\n */\nexport class ConflictError extends IdempotencyError {\n /** When the in-progress request started */\n readonly startedAt: number;\n /** Suggested retry delay in seconds */\n readonly retryAfter: number;\n\n constructor(startedAt: number) {\n const ageSeconds = Math.round((Date.now() - startedAt) / 1000);\n super(\n 'A request with the same Idempotency-Key is currently being processed.',\n 409,\n `${DOCS_BASE_URL}/conflict`,\n 'Request in progress'\n );\n this.name = 'ConflictError';\n this.startedAt = startedAt;\n this.retryAfter = Math.max(1, Math.min(5, ageSeconds));\n }\n\n override toProblemDetails(): ProblemDetails {\n return {\n ...super.toProblemDetails(),\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Thrown when the same idempotency key is used with a different request payload.\n * HTTP 422 Unprocessable Content\n */\nexport class MismatchError extends IdempotencyError {\n /** Hash of the original request */\n readonly existingHash: string;\n /** Hash of the current request */\n readonly providedHash: string;\n\n constructor(existingHash: string, providedHash: string) {\n super(\n 'This Idempotency-Key was used with a different request payload.',\n 422,\n `${DOCS_BASE_URL}/mismatch`,\n 'Idempotency-Key reused'\n );\n this.name = 'MismatchError';\n this.existingHash = existingHash;\n this.providedHash = providedHash;\n }\n}\n\n/**\n * Thrown when the storage adapter encounters an error.\n * HTTP 500 Internal Server Error\n */\nexport class StorageError extends IdempotencyError {\n /** The underlying error from the storage adapter */\n override readonly cause: Error;\n\n constructor(message: string, cause: Error) {\n super(`Storage error: ${message}`, 500, `${DOCS_BASE_URL}/storage-error`, 'Storage error');\n this.name = 'StorageError';\n this.cause = cause;\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\n\n/**\n * Parse a TTL string into milliseconds.\n * Supports: '30s', '5m', '24h', '7d'\n */\nexport function parseTtl(ttl: number | string): number {\n if (typeof ttl === 'number') {\n return ttl;\n }\n\n if (ttl.length < 2) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const unit = ttl[ttl.length - 1];\n if (unit !== 's' && unit !== 'm' && unit !== 'h' && unit !== 'd') {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n const valueStr = ttl.slice(0, -1);\n if (!valueStr) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n\n for (let i = 0; i < valueStr.length; i++) {\n const code = valueStr.charCodeAt(i);\n if (code < 48 || code > 57) {\n throw new Error(\n `Invalid TTL format: \"${ttl}\". Use a number (milliseconds) or string like '30s', '5m', '24h', '7d'.`\n );\n }\n }\n\n const value = parseInt(valueStr, 10);\n\n switch (unit) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Invalid TTL unit: \"${unit}\"`);\n }\n}\n\n/**\n * Generate a simple hash from a string using DJB2 algorithm.\n *\n * **Note:** This is a fast, non-cryptographic hash suitable for request\n * fingerprinting. It has a 32-bit output space (~4 billion unique values).\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision (birthday problem). For high-volume payment systems or\n * security-sensitive applications, use {@link hashObjectSecure} instead.\n *\n * For typical API use cases with moderate volume, DJB2 provides excellent\n * performance (~2.4M ops/sec vs ~600K ops/sec for SHA-256).\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\n/**\n * Generate a SHA-256 hash from a string.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link simpleHash} but cryptographically secure.\n */\nexport function secureHash(str: string): string {\n return createHash('sha256').update(str).digest('hex');\n}\n\n/**\n * Hash an object by JSON stringifying it.\n *\n * Uses the fast DJB2 algorithm (~2.4M ops/sec). For high-volume payment\n * systems or security-sensitive applications where collision resistance\n * is critical, use {@link hashObjectSecure} instead.\n *\n * **Collision Risk:** At ~77,000 unique payloads, there's a 50% probability\n * of collision. A collision could cause:\n * - False mismatch errors (if hashes differ)\n * - Or incorrectly matching different payloads (if hashes collide)\n *\n * **Note:** Object key order affects the hash. If you need order-independent\n * hashing, sort the keys before passing to this function.\n */\nexport function hashObject(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return simpleHash(str);\n}\n\n/**\n * Hash an object using SHA-256.\n *\n * Use this for security-sensitive applications where collision resistance\n * is important. Slower than {@link hashObject} but cryptographically secure.\n */\nexport function hashObjectSecure(obj: unknown): string {\n const str = JSON.stringify(obj) ?? 'undefined';\n return secureHash(str);\n}\n\n/**\n * Generate a cryptographically secure unique key (UUID v4).\n *\n * Uses Node.js crypto.randomUUID() for secure random generation.\n */\nexport function generateKey(): string {\n return randomUUID();\n}\n\n/**\n * Compose a deterministic key from multiple parts.\n */\nexport function composeKey(...parts: (string | number)[]): string {\n return parts.join(':');\n}\n","import type { StorageAdapter, OncelyOptions, OncelyConfig } from './types.js';\nimport { MemoryStorage } from './memory.js';\n\n// Re-export GlobalConfig as alias for backwards compatibility\nexport type GlobalConfig = OncelyConfig;\n\n// Global configuration store\nlet globalConfig: OncelyConfig = {};\n\n// Lazy-initialized default memory storage\nlet defaultMemoryStorage: MemoryStorage | null = null;\n\n/**\n * Configure global defaults for oncely.\n * Call this once at application startup to set defaults for all oncely operations.\n *\n * @example\n * ```typescript\n * import { oncely } from '@oncely/core';\n * import { redis } from '@oncely/redis';\n *\n * // Set up once at app startup\n * oncely.configure({\n * storage: redis(),\n * ttl: '1h',\n * onHit: (key) => console.log(`Cache hit: ${key}`),\n * });\n * ```\n */\nexport function configure(config: OncelyConfig): void {\n globalConfig = { ...config };\n}\n\n/**\n * Get the current global configuration.\n */\nexport function getConfig(): OncelyConfig {\n return globalConfig;\n}\n\n/**\n * Reset global configuration to defaults.\n * Useful for testing.\n */\nexport function resetConfig(): void {\n globalConfig = {};\n defaultMemoryStorage = null;\n}\n\n/**\n * Get the default storage adapter.\n * Returns the configured global storage, or a shared memory instance.\n */\nexport function getDefaultStorage(): StorageAdapter {\n if (globalConfig.storage) {\n return globalConfig.storage;\n }\n // Lazy-init shared memory storage\n if (!defaultMemoryStorage) {\n defaultMemoryStorage = new MemoryStorage();\n }\n return defaultMemoryStorage;\n}\n\n/**\n * Resolve oncely options by merging with global config.\n */\nexport function resolveOptions(options?: Partial<OncelyOptions>): OncelyOptions {\n const config = getConfig();\n return {\n storage: options?.storage ?? config.storage ?? getDefaultStorage(),\n ttl: options?.ttl ?? config.ttl ?? '24h',\n fingerprint: options?.fingerprint ?? config.fingerprint ?? true,\n debug: options?.debug ?? config.debug ?? false,\n onHit: options?.onHit ?? config.onHit,\n onMiss: options?.onMiss ?? config.onMiss,\n onConflict: options?.onConflict ?? config.onConflict,\n onError: options?.onError ?? config.onError,\n onSkip: options?.onSkip ?? config.onSkip,\n };\n}\n","import type {\n StorageAdapter,\n OncelyOptions,\n RunOptions,\n RunResult,\n StoredResponse,\n OnHitCallback,\n OnMissCallback,\n OnConflictCallback,\n OnErrorCallback,\n OnSkipCallback,\n} from './types.js';\nimport { MissingKeyError, ConflictError, MismatchError, StorageError } from './errors.js';\nimport { parseTtl } from './utils.js';\nimport { resolveOptions } from './config.js';\n\n/**\n * Estimate the size of a value in bytes when serialized to JSON.\n */\nfunction estimatePayloadSize(value: unknown): number {\n try {\n return new TextEncoder().encode(JSON.stringify(value)).length;\n } catch {\n // If serialization fails, return 0 to allow normal flow\n // (save() will likely fail anyway)\n return 0;\n }\n}\n\n/**\n * Oncely idempotency service.\n * Ensures operations are executed exactly once per idempotency key.\n */\nexport class Oncely {\n private readonly storage: StorageAdapter;\n private readonly ttl: number;\n private readonly debug: boolean;\n private readonly onHit?: OnHitCallback;\n private readonly onMiss?: OnMissCallback;\n private readonly onConflict?: OnConflictCallback;\n private readonly onError?: OnErrorCallback;\n private readonly onSkip?: OnSkipCallback;\n\n constructor(options: OncelyOptions) {\n this.storage = options.storage;\n this.ttl = parseTtl(options.ttl ?? '24h');\n this.debug = options.debug ?? false;\n this.onHit = options.onHit;\n this.onMiss = options.onMiss;\n this.onConflict = options.onConflict;\n this.onError = options.onError;\n this.onSkip = options.onSkip;\n }\n\n /**\n * Run an operation with idempotency protection.\n *\n * @example\n * ```typescript\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => {\n * const order = await createOrder(data);\n * return order;\n * },\n * });\n *\n * if (result.cached) {\n * console.log('Returned cached response');\n * }\n * ```\n */\n async run<T>(options: RunOptions<T>): Promise<RunResult<T>> {\n const { key, hash = null, handler } = options;\n\n // Validate key\n if (!key) {\n throw new MissingKeyError();\n }\n\n this.log(`Acquiring lock for key: ${key}`);\n\n // Try to acquire lock\n let acquireResult;\n try {\n acquireResult = await this.storage.acquire(key, hash, this.ttl);\n } catch (err) {\n throw new StorageError('Failed to acquire lock', err as Error);\n }\n\n // Handle different acquire results\n switch (acquireResult.status) {\n case 'hit': {\n this.log(`Cache hit for key: ${key}`);\n this.onHit?.(key, acquireResult.response);\n return {\n data: acquireResult.response.data as T,\n cached: true,\n status: 'hit',\n createdAt: acquireResult.response.createdAt,\n };\n }\n\n case 'conflict': {\n this.log(`Conflict for key: ${key}`);\n const error = new ConflictError(acquireResult.startedAt);\n this.onConflict?.(key);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'mismatch': {\n this.log(`Hash mismatch for key: ${key}`);\n const error = new MismatchError(acquireResult.existingHash, acquireResult.providedHash);\n this.onError?.(key, error);\n throw error;\n }\n\n case 'acquired': {\n this.log(`Lock acquired for key: ${key}`);\n this.onMiss?.(key);\n break;\n }\n }\n\n // Execute handler\n try {\n const data = await handler();\n const now = Date.now();\n\n // Save response\n const storedResponse: StoredResponse = {\n data,\n createdAt: now,\n hash,\n };\n\n // Check payload size limit\n const maxSize = this.storage.maxPayloadSize;\n if (maxSize !== undefined && maxSize > 0) {\n const payloadSize = estimatePayloadSize(storedResponse);\n if (payloadSize > maxSize) {\n this.log(\n `Payload too large for key: ${key} (${payloadSize} bytes > ${maxSize} bytes limit). Skipping cache.`\n );\n this.onSkip?.(key, 'payload_too_large', { payloadSize, maxSize });\n\n // Release lock and return without caching\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n }\n }\n\n try {\n await this.storage.save(key, storedResponse);\n this.log(`Saved response for key: ${key}`);\n } catch (err) {\n // Log but don't fail - the operation succeeded\n this.log(`Failed to save response for key: ${key} - ${err}`);\n }\n\n return {\n data,\n cached: false,\n status: 'created',\n createdAt: now,\n };\n } catch (err) {\n // Release lock on failure to allow retry\n this.log(`Handler failed for key: ${key}, releasing lock`);\n try {\n await this.storage.release(key);\n } catch (releaseErr) {\n this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);\n }\n throw err;\n }\n }\n\n private log(message: string): void {\n if (this.debug) {\n console.log(`[oncely] ${message}`);\n }\n }\n}\n\n/**\n * Create an oncely idempotency instance.\n *\n * Options are optional - will use global config or sensible defaults.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@oncely/core';\n *\n * // Zero-config (uses memory storage)\n * const idempotency = createInstance();\n *\n * // With explicit storage\n * const idempotency = createInstance({ storage: myRedis });\n *\n * const result = await idempotency.run({\n * key: 'order-123',\n * handler: async () => createOrder(data),\n * });\n * ```\n */\nexport function createInstance(options?: Partial<OncelyOptions>): Oncely {\n return new Oncely(resolveOptions(options));\n}\n","import { MemoryStorage } from './memory.js';\nimport { createInstance } from './oncely.js';\nimport { resetConfig, configure } from './config.js';\nimport type { OncelyOptions, OncelyConfig, StoredResponse, AcquireResult } from './types.js';\n\n/**\n * Mock storage for testing.\n * Same as MemoryStorage but with additional test utilities.\n */\nexport class MockStorage extends MemoryStorage {\n /** Track all operations for assertions */\n readonly operations: Array<{ type: string; key: string; timestamp: number }> = [];\n\n /** Simulated conflict keys */\n private simulatedConflicts = new Set<string>();\n\n /** Simulated mismatch keys */\n private simulatedMismatches = new Map<string, { existingHash: string; providedHash: string }>();\n\n override async acquire(key: string, hash: string | null, ttl: number): Promise<AcquireResult> {\n this.operations.push({ type: 'acquire', key, timestamp: Date.now() });\n\n // Check for simulated conflict\n if (this.simulatedConflicts.has(key)) {\n return { status: 'conflict', startedAt: Date.now() - 1000 };\n }\n\n // Check for simulated mismatch\n const mismatch = this.simulatedMismatches.get(key);\n if (mismatch) {\n return { status: 'mismatch', ...mismatch };\n }\n\n return super.acquire(key, hash, ttl);\n }\n\n override async save(key: string, response: StoredResponse): Promise<void> {\n this.operations.push({ type: 'save', key, timestamp: Date.now() });\n return super.save(key, response);\n }\n\n override async release(key: string): Promise<void> {\n this.operations.push({ type: 'release', key, timestamp: Date.now() });\n return super.release(key);\n }\n\n override async delete(key: string): Promise<void> {\n this.operations.push({ type: 'delete', key, timestamp: Date.now() });\n return super.delete(key);\n }\n\n override async clear(): Promise<void> {\n this.operations.length = 0;\n this.simulatedConflicts.clear();\n this.simulatedMismatches.clear();\n return super.clear();\n }\n\n /**\n * Get operations for a specific key.\n */\n getOperationsForKey(key: string) {\n return this.operations.filter((op) => op.key === key);\n }\n\n /**\n * Assert that a key was acquired.\n */\n assertAcquired(key: string): void {\n const acquired = this.operations.some((op) => op.type === 'acquire' && op.key === key);\n if (!acquired) {\n throw new Error(`Expected key \"${key}\" to be acquired, but it was not.`);\n }\n }\n\n /**\n * Assert that a key was saved (cached).\n */\n assertCached(key: string): void {\n const saved = this.operations.some((op) => op.type === 'save' && op.key === key);\n if (!saved) {\n throw new Error(`Expected key \"${key}\" to be cached, but it was not.`);\n }\n }\n\n /**\n * Assert that a key was NOT saved (not cached).\n */\n assertNotCached(key: string): void {\n const saved = this.operations.some((op) => op.type === 'save' && op.key === key);\n if (saved) {\n throw new Error(`Expected key \"${key}\" to NOT be cached, but it was.`);\n }\n }\n\n /**\n * Assert that a key was released.\n */\n assertReleased(key: string): void {\n const released = this.operations.some((op) => op.type === 'release' && op.key === key);\n if (!released) {\n throw new Error(`Expected key \"${key}\" to be released, but it was not.`);\n }\n }\n\n /**\n * Simulate a conflict error for a specific key.\n * The next acquire for this key will return conflict status.\n */\n simulateConflict(key: string): void {\n this.simulatedConflicts.add(key);\n }\n\n /**\n * Simulate a mismatch error for a specific key.\n * The next acquire for this key will return mismatch status.\n */\n simulateMismatch(\n key: string,\n existingHash = 'existing-hash',\n providedHash = 'provided-hash'\n ): void {\n this.simulatedMismatches.set(key, { existingHash, providedHash });\n }\n\n /**\n * Clear all simulations.\n */\n clearSimulations(): void {\n this.simulatedConflicts.clear();\n this.simulatedMismatches.clear();\n }\n}\n\n/**\n * Create an oncely instance configured for testing.\n *\n * @example\n * ```typescript\n * import { createTestInstance, MockStorage } from '@oncely/core/testing';\n *\n * describe('my api', () => {\n * const storage = new MockStorage();\n * const idempotency = createTestInstance({ storage });\n *\n * beforeEach(() => storage.clear());\n *\n * it('handles duplicate requests', async () => {\n * const handler = vi.fn().mockResolvedValue({ id: 1 });\n *\n * await idempotency.run({ key: 'test', handler });\n * await idempotency.run({ key: 'test', handler });\n *\n * expect(handler).toHaveBeenCalledTimes(1);\n * storage.assertCached('test');\n * });\n * });\n * ```\n */\nexport function createTestInstance(options?: Partial<OncelyOptions>) {\n return createInstance({\n storage: options?.storage ?? new MockStorage(),\n ttl: options?.ttl ?? '1h',\n debug: options?.debug ?? false,\n ...options,\n });\n}\n\n/**\n * @deprecated Use createTestInstance instead.\n */\nexport const createTestOncely = createTestInstance;\n\n/**\n * Set up oncely for testing with auto-reset between tests.\n * Returns utilities for testing assertions.\n *\n * @example\n * ```typescript\n * import { setupTest } from '@oncely/core/testing';\n *\n * describe('my api', () => {\n * const { storage, instance, reset } = setupTest();\n *\n * beforeEach(() => reset());\n *\n * it('caches responses', async () => {\n * await instance.run({ key: 'test', handler: () => ({ ok: true }) });\n * storage.assertCached('test');\n * });\n * });\n * ```\n */\nexport function setupTest(config?: OncelyConfig) {\n const storage = new MockStorage();\n const instance = createInstance({ storage, ...config });\n\n return {\n storage,\n instance,\n reset: () => {\n storage.clear();\n resetConfig();\n if (config) {\n configure(config);\n }\n },\n };\n}\n\n// Re-export useful types\nexport type {\n StorageAdapter,\n StoredResponse,\n OncelyOptions,\n OncelyConfig,\n RunOptions,\n RunResult,\n AcquireResult,\n} from './types.js';\n\n// Re-export errors for test assertions\nexport {\n IdempotencyError,\n MissingKeyError,\n ConflictError,\n MismatchError,\n StorageError,\n} from './errors.js';\n"]}
|
package/dist/testing.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoryStorage, A as AcquireResult,
|
|
2
|
-
export { C as ConflictError, I as IdempotencyError,
|
|
1
|
+
import { M as MemoryStorage, A as AcquireResult, o as StoredResponse, j as OncelyOptions, i as Oncely, O as OncelyConfig } from './errors-DVBjbOG7.cjs';
|
|
2
|
+
export { C as ConflictError, I as IdempotencyError, a as MismatchError, b as MissingKeyError, R as RunOptions, k as RunResult, S as StorageAdapter, n as StorageError } from './errors-DVBjbOG7.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Mock storage for testing.
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoryStorage, A as AcquireResult,
|
|
2
|
-
export { C as ConflictError, I as IdempotencyError,
|
|
1
|
+
import { M as MemoryStorage, A as AcquireResult, o as StoredResponse, j as OncelyOptions, i as Oncely, O as OncelyConfig } from './errors-DVBjbOG7.js';
|
|
2
|
+
export { C as ConflictError, I as IdempotencyError, a as MismatchError, b as MissingKeyError, R as RunOptions, k as RunResult, S as StorageAdapter, n as StorageError } from './errors-DVBjbOG7.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Mock storage for testing.
|
package/dist/testing.js
CHANGED
|
@@ -2,6 +2,11 @@ import 'crypto';
|
|
|
2
2
|
|
|
3
3
|
// src/memory.ts
|
|
4
4
|
var MemoryStorage = class {
|
|
5
|
+
/**
|
|
6
|
+
* Maximum payload size in bytes (default: 5MB).
|
|
7
|
+
* Prevents unbounded memory growth from large responses.
|
|
8
|
+
*/
|
|
9
|
+
maxPayloadSize = 5 * 1024 * 1024;
|
|
5
10
|
store = /* @__PURE__ */ new Map();
|
|
6
11
|
cleanupInterval = null;
|
|
7
12
|
constructor() {
|
|
@@ -255,11 +260,19 @@ function resolveOptions(options) {
|
|
|
255
260
|
onHit: options?.onHit ?? config.onHit,
|
|
256
261
|
onMiss: options?.onMiss ?? config.onMiss,
|
|
257
262
|
onConflict: options?.onConflict ?? config.onConflict,
|
|
258
|
-
onError: options?.onError ?? config.onError
|
|
263
|
+
onError: options?.onError ?? config.onError,
|
|
264
|
+
onSkip: options?.onSkip ?? config.onSkip
|
|
259
265
|
};
|
|
260
266
|
}
|
|
261
267
|
|
|
262
268
|
// src/oncely.ts
|
|
269
|
+
function estimatePayloadSize(value) {
|
|
270
|
+
try {
|
|
271
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
272
|
+
} catch {
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
263
276
|
var Oncely = class {
|
|
264
277
|
storage;
|
|
265
278
|
ttl;
|
|
@@ -268,6 +281,7 @@ var Oncely = class {
|
|
|
268
281
|
onMiss;
|
|
269
282
|
onConflict;
|
|
270
283
|
onError;
|
|
284
|
+
onSkip;
|
|
271
285
|
constructor(options) {
|
|
272
286
|
this.storage = options.storage;
|
|
273
287
|
this.ttl = parseTtl(options.ttl ?? "24h");
|
|
@@ -276,6 +290,7 @@ var Oncely = class {
|
|
|
276
290
|
this.onMiss = options.onMiss;
|
|
277
291
|
this.onConflict = options.onConflict;
|
|
278
292
|
this.onError = options.onError;
|
|
293
|
+
this.onSkip = options.onSkip;
|
|
279
294
|
}
|
|
280
295
|
/**
|
|
281
296
|
* Run an operation with idempotency protection.
|
|
@@ -345,6 +360,27 @@ var Oncely = class {
|
|
|
345
360
|
createdAt: now,
|
|
346
361
|
hash
|
|
347
362
|
};
|
|
363
|
+
const maxSize = this.storage.maxPayloadSize;
|
|
364
|
+
if (maxSize !== void 0 && maxSize > 0) {
|
|
365
|
+
const payloadSize = estimatePayloadSize(storedResponse);
|
|
366
|
+
if (payloadSize > maxSize) {
|
|
367
|
+
this.log(
|
|
368
|
+
`Payload too large for key: ${key} (${payloadSize} bytes > ${maxSize} bytes limit). Skipping cache.`
|
|
369
|
+
);
|
|
370
|
+
this.onSkip?.(key, "payload_too_large", { payloadSize, maxSize });
|
|
371
|
+
try {
|
|
372
|
+
await this.storage.release(key);
|
|
373
|
+
} catch (releaseErr) {
|
|
374
|
+
this.log(`Failed to release lock for key: ${key} - ${releaseErr}`);
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
data,
|
|
378
|
+
cached: false,
|
|
379
|
+
status: "created",
|
|
380
|
+
createdAt: now
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
348
384
|
try {
|
|
349
385
|
await this.storage.save(key, storedResponse);
|
|
350
386
|
this.log(`Saved response for key: ${key}`);
|