@nevermined-io/payments 1.10.0 → 1.11.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.
Files changed (89) hide show
  1. package/dist/api/nvm-api.d.ts +4 -0
  2. package/dist/api/nvm-api.d.ts.map +1 -1
  3. package/dist/api/nvm-api.js +7 -0
  4. package/dist/api/nvm-api.js.map +1 -1
  5. package/dist/api/organizations-api/organizations-api.d.ts +19 -1
  6. package/dist/api/organizations-api/organizations-api.d.ts.map +1 -1
  7. package/dist/api/organizations-api/organizations-api.js +49 -0
  8. package/dist/api/organizations-api/organizations-api.js.map +1 -1
  9. package/dist/api/organizations-api/types.d.ts +27 -0
  10. package/dist/api/organizations-api/types.d.ts.map +1 -1
  11. package/dist/api/organizations-api/types.js.map +1 -1
  12. package/dist/common/api-version.d.ts +1 -1
  13. package/dist/common/api-version.js +1 -1
  14. package/dist/common/api-version.js.map +1 -1
  15. package/dist/common/types.d.ts +1 -1
  16. package/dist/common/types.js.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp/core/server-manager.d.ts.map +1 -1
  22. package/dist/mcp/core/server-manager.js +3 -0
  23. package/dist/mcp/core/server-manager.js.map +1 -1
  24. package/dist/mcp/http/mcp-handler.d.ts +7 -0
  25. package/dist/mcp/http/mcp-handler.d.ts.map +1 -1
  26. package/dist/mcp/http/mcp-handler.js +8 -1
  27. package/dist/mcp/http/mcp-handler.js.map +1 -1
  28. package/dist/mcp/http/oauth-metadata.d.ts.map +1 -1
  29. package/dist/mcp/http/oauth-metadata.js +31 -6
  30. package/dist/mcp/http/oauth-metadata.js.map +1 -1
  31. package/dist/mcp/http/oauth-router.d.ts +17 -16
  32. package/dist/mcp/http/oauth-router.d.ts.map +1 -1
  33. package/dist/mcp/http/oauth-router.js +56 -28
  34. package/dist/mcp/http/oauth-router.js.map +1 -1
  35. package/dist/mpp/codec.d.ts +92 -0
  36. package/dist/mpp/codec.d.ts.map +1 -0
  37. package/dist/mpp/codec.js +405 -0
  38. package/dist/mpp/codec.js.map +1 -0
  39. package/dist/mpp/errors.d.ts +165 -0
  40. package/dist/mpp/errors.d.ts.map +1 -0
  41. package/dist/mpp/errors.js +177 -0
  42. package/dist/mpp/errors.js.map +1 -0
  43. package/dist/mpp/fetch.d.ts +196 -0
  44. package/dist/mpp/fetch.d.ts.map +1 -0
  45. package/dist/mpp/fetch.js +493 -0
  46. package/dist/mpp/fetch.js.map +1 -0
  47. package/dist/mpp/index.d.ts +11 -0
  48. package/dist/mpp/index.d.ts.map +1 -0
  49. package/dist/mpp/index.js +20 -0
  50. package/dist/mpp/index.js.map +1 -0
  51. package/dist/mpp/mpp-api.d.ts +153 -0
  52. package/dist/mpp/mpp-api.d.ts.map +1 -0
  53. package/dist/mpp/mpp-api.js +298 -0
  54. package/dist/mpp/mpp-api.js.map +1 -0
  55. package/dist/mpp/types.d.ts +43 -0
  56. package/dist/mpp/types.d.ts.map +1 -0
  57. package/dist/mpp/types.js +9 -0
  58. package/dist/mpp/types.js.map +1 -0
  59. package/dist/payments.d.ts +13 -0
  60. package/dist/payments.d.ts.map +1 -1
  61. package/dist/payments.js +3 -0
  62. package/dist/payments.js.map +1 -1
  63. package/dist/x402/delegation-api.d.ts +3 -1
  64. package/dist/x402/delegation-api.d.ts.map +1 -1
  65. package/dist/x402/delegation-api.js.map +1 -1
  66. package/dist/x402/express/index.d.ts +2 -0
  67. package/dist/x402/express/index.d.ts.map +1 -1
  68. package/dist/x402/express/index.js +2 -0
  69. package/dist/x402/express/index.js.map +1 -1
  70. package/dist/x402/express/middleware.d.ts +33 -1
  71. package/dist/x402/express/middleware.d.ts.map +1 -1
  72. package/dist/x402/express/middleware.js +831 -1
  73. package/dist/x402/express/middleware.js.map +1 -1
  74. package/dist/x402/express/mpp-support.d.ts +58 -0
  75. package/dist/x402/express/mpp-support.d.ts.map +1 -0
  76. package/dist/x402/express/mpp-support.js +67 -0
  77. package/dist/x402/express/mpp-support.js.map +1 -0
  78. package/dist/x402/express/raw-body.d.ts +24 -0
  79. package/dist/x402/express/raw-body.d.ts.map +1 -0
  80. package/dist/x402/express/raw-body.js +34 -0
  81. package/dist/x402/express/raw-body.js.map +1 -0
  82. package/dist/x402/token-request.d.ts +15 -0
  83. package/dist/x402/token-request.d.ts.map +1 -0
  84. package/dist/x402/token-request.js +66 -0
  85. package/dist/x402/token-request.js.map +1 -0
  86. package/dist/x402/token.d.ts.map +1 -1
  87. package/dist/x402/token.js +7 -51
  88. package/dist/x402/token.js.map +1 -1
  89. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/mpp/codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAGtC,sFAAsF;AACtF,MAAM,KAAK,GAAG,+BAA+B,CAAA;AAC7C,qFAAqF;AACrF,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAS,0BAA0B,CAAC,IAAY;IAC9C,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QAClB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,GAAG,KAAK,CAAA;YACjB,CAAC;iBAAM,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBACvB,OAAO,GAAG,IAAI,CAAA;YAChB,CAAC;iBAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACtB,QAAQ,GAAG,KAAK,CAAA;YAClB,CAAC;YACD,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,IAAI,CAAA;YACf,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;YACxE,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,MAAM,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC7D,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IAEhE,qEAAqE;IACrE,0EAA0E;IAC1E,sEAAsE;IACtE,sDAAsD;IACtD,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC9D,MAAM,KAAK,GAAG,YAAY,GAAG,OAAO,CAAC,MAAM,CAAA;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAE5C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,yEAAyE;QACzE,sEAAsE;QACtE,mBAAmB;QACnB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACpC,MAAM,GAAG,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,GAAG,UAAU,CAAA;QAC9E,OAAO,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7C,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAA;IAC3D,OAAO,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,4BAA4B,CAAC,UAAkB;IAC7D,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7D,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IAE3D,IAAI,OAAgB,CAAA;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IAE1F,MAAM,EAAE,SAAS,EAAE,GAAG,OAAkC,CAAA;IACxD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAA;IAEhG,MAAM,EAAE,EAAE,EAAE,GAAG,SAA6B,CAAA;IAC5C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACxD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,KAAa,EAAE,KAAa;IACnD,IAAI,CAAC,GAAG,KAAK,CAAA;IACb,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACnB,CAAC,EAAE,CAAA;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,IAAI,EAAE,CAAA;YACX,OAAO,GAAG,KAAK,CAAA;YACf,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,GAAG,IAAI,CAAA;YACd,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG;YAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QACjC,KAAK,IAAI,EAAE,CAAA;IACb,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;AACnB,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,IAAI,CAAC,GAAG,CAAC,CAAA;IAET,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,CAAC,EAAE,CAAA;QACtD,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM;YAAE,MAAK;QAE5B,MAAM,QAAQ,GAAG,CAAC,CAAA;QAClB,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,CAAC,EAAE,CAAA;QAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;QACpC,IAAI,CAAC,GAAG;YAAE,MAAK;QAEf,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,CAAC,EAAE,CAAA;QACnD,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,MAAK;QAC3B,CAAC,EAAE,CAAA;QACH,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,CAAC,EAAE,CAAA;QAEnD,IAAI,KAAa,CAAA;QACjB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;YAC1D,KAAK,GAAG,OAAO,CAAA;YACf,CAAC,GAAG,SAAS,CAAA;QACf,CAAC;aAAM,CAAC;YACN,MAAM,UAAU,GAAG,CAAC,CAAA;YACpB,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,CAAC,EAAE,CAAA;YAChD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3C,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAAI,OAAe,EAAE,OAAe;IAC9D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAM,CAAA;IAC5E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,QAAQ,CAChB,wBAAwB,OAAO,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7F,CAAA;IACH,CAAC;AACH,CAAC;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,4BAA4B,CAAC,KAAc;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACrF,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAgC,CAAA;IACrE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC5E,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IACtE,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;qBAEqB;AACrB,SAAS,kBAAkB,CAAC,KAAmC;IAC7D,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAC9B,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;KAC/D,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAA;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAA;IAClE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;IAC3F,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEhE,MAAM,cAAc,GAAG,mBAAmB,CAAU,OAAO,EAAE,iCAAiC,CAAC,CAAA;IAC/F,IAAI,CAAC,4BAA4B,CAAC,cAAc,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,QAAQ,CAChB,kEAAkE;YAChE,wEAAwE,CAC3E,CAAA;IACH,CAAC;IAED,OAAO;QACL,EAAE;QACF,KAAK;QACL,MAAM;QACN,MAAM;QACN,OAAO,EAAE,kBAAkB,CAAC,cAAc,CAAC;QAC3C,cAAc,EAAE,OAAO;QACvB,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;QAC3B,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAAuB,EACvB,OAAgC;IAEhC,MAAM,IAAI,GAAG;QACX,SAAS,EAAE;YACT,EAAE,EAAE,SAAS,CAAC,EAAE;YAChB,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;YACxD,GAAG,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;YACrD,GAAG,CAAC,SAAS,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC;YACpE,GAAG,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;YACrD,OAAO,EAAE,SAAS,CAAC,cAAc;SAClC;QACD,OAAO;KACR,CAAA;IACD,OAAO,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAA;AACrF,CAAC;AAED;;;;uEAIuE;AACvE,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACrF,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAgC,CAAA;IACjF,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,OAAO,SAAS,KAAK,QAAQ;QAC7B,SAAS,KAAK,EAAE;QAChB,OAAO,MAAM,KAAK,QAAQ;QAC1B,OAAO,SAAS,KAAK,QAAQ,CAC9B,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,MAAM,OAAO,GAAG,mBAAmB,CAAU,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,CAAC,CAAA;IAC/E,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,QAAQ,CAChB,4CAA4C;YAC1C,kFAAkF,CACrF,CAAA;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["/**\n * The MPP wire format, hand-written so the SDK takes no protocol dependency.\n *\n * The one rule that matters: the backend re-derives the challenge id as\n * `HMAC(secret, realm|method|intent|canonicalize(request)|expires|digest|opaque)`\n * from the fields carried inside the credential. `request` and `opaque` are\n * therefore passed through as the exact base64url strings received — this file\n * never re-encodes them.\n */\n\nimport { MppError } from './errors.js'\nimport type { MppChallenge, MppChallengeRequest, MppReceipt } from './types.js'\n\n/** A bare token (RFC 9110 §5.6.2, used for auth-scheme names and auth-param keys). */\nconst TOKEN = \"[!#$%&'*+\\\\-.^_`|~0-9A-Za-z]+\"\n/** Matches the start of a `key=` auth-param, i.e. a Payment challenge continuing. */\nconst AUTH_PARAM_START = new RegExp(`^${TOKEN}\\\\s*=`)\n\n/**\n * Finds where a structured challenge's auth-param list ends within `rest`\n * (the content right after \"Payment \"), scanning quote- and escape-aware so\n * neither a comma nor a `\\\"` inside a `\"…\"` value is mistaken for a\n * boundary. mppx serializes quoted values with\n * `value.replace(/\\\\/g,'\\\\\\\\').replace(/\"/g,'\\\\\"')` (`Challenge.ts:316-319`),\n * so a seller-supplied `description` like `5\" screen replacement plan` wire-\n * encodes as `description=\"5\\\" screen replacement plan\"`. A scanner that\n * toggles quote state on every literal `\"` — escaped or not — desyncs on\n * that `\\\"`, stays stuck \"inside\" a quote for the rest of the header, and\n * swallows a genuine trailing scheme. This mirrors mppx's own reference\n * parser's `escaped` flag (`Challenge.ts:362-379`) rather than inventing new\n * semantics.\n *\n * A top-level (non-quoted, non-escaped) comma only ends the scheme if what\n * follows it does NOT itself look like a continuing `key=value` auth-param —\n * which is also how a following literal \"Payment \" (the merged-challenge\n * case) is recognized as a new scheme: \"Payment\" followed by whitespace\n * never matches `AUTH_PARAM_START`, since that requires \"=\" immediately\n * (modulo optional whitespace) after the leading token.\n *\n * An unterminated quote is not an error here: the loop simply runs out of\n * input without finding a boundary and falls back to treating the whole\n * remainder as this scheme — a safe, total (never-throwing) O(n) fallback.\n */\nfunction findStructuredChallengeEnd(rest: string): number {\n let inQuotes = false\n let escaped = false\n for (let i = 0; i < rest.length; i++) {\n const ch = rest[i]\n if (inQuotes) {\n if (escaped) {\n escaped = false\n } else if (ch === '\\\\') {\n escaped = true\n } else if (ch === '\"') {\n inQuotes = false\n }\n continue\n }\n if (ch === '\"') {\n inQuotes = true\n continue\n }\n if (ch === ',' && !AUTH_PARAM_START.test(rest.slice(i + 1).trimStart())) {\n return i\n }\n }\n return rest.length\n}\n\n/**\n * Matches the `Payment` scheme name at a genuine scheme boundary: start of\n * the header value, or right after a top-level comma (RFC 9110 §11.6.1\n * separates comma-separated challenges/credentials that way). Unanchored,\n * `/Payment\\s+/i` matches mid-token — `XPayment abc`, `NotPayment abc`,\n * `Bearer prepayment xyz` — and also matches \"Payment\" text embedded inside\n * a *different*, preceding scheme's quoted value (e.g.\n * `Digest username=\"my payment plan\", …`), since plain whitespace alone is\n * not a scheme boundary. Anchoring on comma-or-start (not bare whitespace)\n * closes both: a scheme name is never preceded by an arbitrary space, only\n * by the start of the header or the comma that separates it from what came\n * before.\n *\n * This decides which protocol handles a request:\n * `extractCredential` → `extractPaymentScheme` feeds the MPP-vs-x402 routing\n * predicate in `middleware.ts`, so an unanchored match would divert an\n * x402 buyer carrying a perfectly valid `payment-signature` token, plus an\n * unrelated `Authorization` header that happens to contain \"payment\" text,\n * onto the MPP path and challenge them instead of serving the request.\n */\nconst PAYMENT_SCHEME_BOUNDARY = /(?:^|,)\\s*(Payment\\s+)/i\n\n/**\n * Extracts the `Payment` scheme from a header value that may carry several\n * schemes comma-separated (RFC 9110 §11.6.1).\n *\n * The `Payment` scheme takes one of two shapes on our wire: a bare token68\n * credential (`Payment <base64url>`, which cannot itself contain a comma or\n * a quote) or a structured challenge (`Payment id=\"...\", realm=\"...\", ...`,\n * comma-separated `key=\"value\"` auth-params, where a value MAY contain a\n * comma). Bounding the match at the next literal \"Payment \" occurrence — or\n * at end-of-string otherwise — corrupts the first shape whenever a\n * *different* trailing scheme follows: e.g. `Payment <token>, Bearer <jwt>`\n * used to extract the whole remainder including the trailing scheme.\n * Bounding a structured challenge with a naive, quote-unaware comma split\n * corrupts the second shape whenever a value contains a comma: it truncates\n * mid-quote and silently drops every param after it. Instead: a bare\n * token68 is bounded by its first top-level comma; a structured challenge is\n * bounded by {@link findStructuredChallengeEnd}'s quote-aware scan.\n */\nexport function extractPaymentScheme(headerValue: string): string | null {\n const schemeMatch = PAYMENT_SCHEME_BOUNDARY.exec(headerValue)\n if (!schemeMatch || schemeMatch.index === undefined) return null\n\n // Group 1 (\"Payment\\s+\") is the tail of the whole match, so the full\n // match's end position is also where the captured keyword ends; its start\n // is offset back by the keyword's own length, which skips the leading\n // comma/whitespace the boundary alternation consumed.\n const keyword = schemeMatch[1]\n const contentStart = schemeMatch.index + schemeMatch[0].length\n const start = contentStart - keyword.length\n const rest = headerValue.slice(contentStart)\n\n if (!AUTH_PARAM_START.test(rest)) {\n // Bare token68 credential: bounded by its first top-level comma, since a\n // token68 cannot itself contain one (or a quote, so no quote-tracking\n // is needed here).\n const commaIndex = rest.indexOf(',')\n const end = commaIndex === -1 ? headerValue.length : contentStart + commaIndex\n return headerValue.slice(start, end).trim()\n }\n\n const end = contentStart + findStructuredChallengeEnd(rest)\n return headerValue.slice(start, end).replace(/,\\s*$/, '').trim()\n}\n\n/**\n * The stable identity of a credential: the challenge id it carries.\n *\n * The header bytes are NOT that identity. `PAYMENT_SCHEME_BOUNDARY` is\n * case-insensitive and {@link extractPaymentScheme} returns the matched slice\n * verbatim, so `Payment x`, `payment x` and `Payment x` are three different\n * strings for one credential — and the body is base64url of JSON the BUYER\n * assembles, so re-ordering its keys yields more. The backend collapses every\n * one of them onto a single burn, because its own idempotency key is the\n * decoded challenge id. Anything at the seller edge enforcing single-use has\n * to key on the same thing, or a buyer flips one byte of case and buys the\n * response again.\n *\n * @returns the `challenge.id` a token68 credential decodes to, or `null` when\n * the credential cannot be decoded into one — a structured `key=\"value\"`\n * scheme (that shape is a challenge, never a credential), undecodable\n * base64url JSON, or JSON without a non-empty string `challenge.id`. The\n * backend rejects all of those anyway; returning `null` lets the caller refuse\n * them without a round-trip rather than fall back to a key it cannot trust.\n */\nexport function extractCredentialChallengeId(credential: string): string | null {\n const token68 = credential.replace(/^Payment\\s+/i, '').trim()\n if (!token68 || AUTH_PARAM_START.test(token68)) return null\n\n let decoded: unknown\n try {\n decoded = JSON.parse(Buffer.from(token68, 'base64url').toString('utf8'))\n } catch {\n return null\n }\n if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded)) return null\n\n const { challenge } = decoded as { challenge?: unknown }\n if (typeof challenge !== 'object' || challenge === null || Array.isArray(challenge)) return null\n\n const { id } = challenge as { id?: unknown }\n return typeof id === 'string' && id !== '' ? id : null\n}\n\n/**\n * Reads a quoted-string value starting right after its opening `\"`,\n * unescaping `\\\"` to a literal `\"` and `\\\\` to a literal `\\` (mirrors\n * mppx's `readQuotedAuthParamValue`, `Challenge.ts:461-465`).\n *\n * An unterminated quote is not an error here (unlike mppx's reference\n * parser, which throws): the loop runs out of input and returns whatever was\n * accumulated, matching {@link findStructuredChallengeEnd}'s equally\n * permissive fallback for the same input shape.\n */\nfunction readQuotedValue(input: string, start: number): [value: string, nextIndex: number] {\n let i = start\n let value = ''\n let escaped = false\n while (i < input.length) {\n const ch = input[i]\n i++\n if (escaped) {\n value += ch\n escaped = false\n continue\n }\n if (ch === '\\\\') {\n escaped = true\n continue\n }\n if (ch === '\"') return [value, i]\n value += ch\n }\n return [value, i]\n}\n\n/**\n * Splits `key=\"value\", key2=\"value2\"` into a map. Quoted values are read\n * escape-aware and unescaped ({@link readQuotedValue}), so a value may\n * itself contain a comma, a quote (`\\\"`) or a backslash (`\\\\`) without\n * corrupting the param that follows it. Unquoted values are tolerated too.\n */\nfunction parseAuthParams(input: string): Record<string, string> {\n const params: Record<string, string> = {}\n let i = 0\n\n while (i < input.length) {\n while (i < input.length && /[\\s,]/.test(input[i])) i++\n if (i >= input.length) break\n\n const keyStart = i\n while (i < input.length && /[a-zA-Z0-9_-]/.test(input[i])) i++\n const key = input.slice(keyStart, i)\n if (!key) break\n\n while (i < input.length && /\\s/.test(input[i])) i++\n if (input[i] !== '=') break\n i++\n while (i < input.length && /\\s/.test(input[i])) i++\n\n let value: string\n if (input[i] === '\"') {\n const [decoded, nextIndex] = readQuotedValue(input, i + 1)\n value = decoded\n i = nextIndex\n } else {\n const valueStart = i\n while (i < input.length && input[i] !== ',') i++\n value = input.slice(valueStart, i).trim()\n }\n\n params[key] = value\n }\n\n return params\n}\n\n/**\n * Decodes a base64url string as JSON, raising a typed {@link MppError}\n * rather than a raw `SyntaxError` on malformed input.\n *\n * `Buffer.from(x, 'base64url')` never throws — it silently drops invalid\n * characters — so garbage would otherwise reach `JSON.parse` and escape as a\n * bare `SyntaxError` that names neither MPP nor payment, invisible to a\n * caller writing `catch (e) { if (e instanceof MppError) … }` exactly as our\n * own docs tell them to.\n */\nfunction decodeBase64UrlJson<T>(encoded: string, context: string): T {\n try {\n return JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8')) as T\n } catch (error) {\n throw new MppError(\n `Could not decode the ${context}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n}\n\n/** The shape a decoded `request` param must have before it is trusted — see\n * {@link isValidChallengeRequestShape}. `credits` is intentionally looser\n * than {@link MppChallengeRequest}'s declared `string`: it is coerced, not\n * rejected, by {@link toChallengeRequest}. */\ninterface DecodedChallengeRequestShape {\n planId: string\n credits: string | number\n agentId?: string\n}\n\n/**\n * Validates a decoded `request` param's shape before it is trusted.\n *\n * The raw decode only guarantees valid JSON, not the right shape: a remote\n * challenge's `request=` can decode to `null`, an array, or `{}`, all of\n * which would otherwise sail through a bare type cast and reach\n * `payments.mpp.fetch` with an unusable or `undefined` `planId`.\n *\n * `planId` is the field with the documented failure mode (an unusable or\n * `undefined` value reaching the mint) and is checked strictly: a non-empty\n * string, full stop. `agentId` is unchecked structurally but IS load-bearing\n * once present — the buyer helper forwards it straight into the token mint\n * (`options.agentId ?? challenge.request.agentId`) — so a wrong-typed value\n * is rejected here rather than reaching that spend path. `credits`, by\n * contrast, is not what anything spends: the amount the backend re-derives\n * comes from `requestEncoded`, forwarded byte-verbatim, so rejecting a\n * perfectly reasonable JSON-number encoding of \"credits\" would make a valid\n * third-party seller wholly unpayable over a field this SDK does not itself\n * act on — it is coerced by {@link toChallengeRequest} instead.\n */\nfunction isValidChallengeRequestShape(value: unknown): value is DecodedChallengeRequestShape {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const { planId, credits, agentId } = value as Record<string, unknown>\n if (typeof planId !== 'string' || planId === '') return false\n if (typeof credits !== 'string' && typeof credits !== 'number') return false\n if (agentId !== undefined && typeof agentId !== 'string') return false\n return true\n}\n\n/** Normalizes a validated decoded request into {@link MppChallengeRequest}'s\n * declared shape, coercing a numeric `credits` to the decimal string the\n * type promises. */\nfunction toChallengeRequest(shape: DecodedChallengeRequestShape): MppChallengeRequest {\n return {\n planId: shape.planId,\n credits: String(shape.credits),\n ...(shape.agentId !== undefined && { agentId: shape.agentId }),\n }\n}\n\n/**\n * Parses a `WWW-Authenticate` header into a challenge.\n *\n * @returns `null` when there is no usable `Payment` challenge to parse: the\n * header carries no `Payment` scheme at all, OR the scheme is present but\n * missing one of the structural auth-params (`id`/`realm`/`method`/`intent`/\n * `request`) needed to even attempt decoding it. Both cases mean the same\n * thing to a caller deciding whether to retry: there is nothing here to pay.\n *\n * A `Payment` scheme that has all of those structural params present, but\n * whose `request` value fails to decode as JSON or decodes to something\n * that is not a usable `{ planId, credits }` object, raises a typed\n * {@link MppError} instead — that is a seller who tried to speak MPP and\n * sent something broken, a distinct failure from \"there is no challenge\n * here to parse\".\n */\nexport function parseChallengeHeader(headerValue: string): MppChallenge | null {\n const scheme = extractPaymentScheme(headerValue)\n if (!scheme) return null\n\n const params = parseAuthParams(scheme.replace(/^Payment\\s+/i, ''))\n const { id, realm, method, intent, request, expires, digest, opaque, description } = params\n if (!id || !realm || !method || !intent || !request) return null\n\n const decodedRequest = decodeBase64UrlJson<unknown>(request, 'MPP challenge request parameter')\n if (!isValidChallengeRequestShape(decodedRequest)) {\n throw new MppError(\n 'The MPP challenge names a request parameter that is not a valid ' +\n '{ planId: string, credits: string | number, agentId?: string } object.',\n )\n }\n\n return {\n id,\n realm,\n method,\n intent,\n request: toChallengeRequest(decodedRequest),\n requestEncoded: request,\n ...(expires && { expires }),\n ...(digest && { digest }),\n ...(opaque && { opaque }),\n ...(description && { description }),\n }\n}\n\n/**\n * Builds the `Authorization: Payment …` value for a challenge.\n *\n * Key order in the JSON is irrelevant — the server parses it — but the two\n * base64url strings are emitted untouched, which is what keeps the HMAC valid.\n */\nexport function buildCredentialHeader(\n challenge: MppChallenge,\n payload: { accessToken: string },\n): string {\n const wire = {\n challenge: {\n id: challenge.id,\n realm: challenge.realm,\n method: challenge.method,\n intent: challenge.intent,\n ...(challenge.expires && { expires: challenge.expires }),\n ...(challenge.digest && { digest: challenge.digest }),\n ...(challenge.description && { description: challenge.description }),\n ...(challenge.opaque && { opaque: challenge.opaque }),\n request: challenge.requestEncoded,\n },\n payload,\n }\n return `Payment ${Buffer.from(JSON.stringify(wire), 'utf8').toString('base64url')}`\n}\n\n/** Validates a decoded `Payment-Receipt` body against {@link MppReceipt}'s\n * shape — mirrors {@link isValidChallengeRequestShape}'s strictness for the\n * same reason: without it, `null`, an array, or `{}` all sail through a\n * bare type cast and surface later as an untyped `TypeError` on a field\n * access, with nothing pointing back at the header that caused it. */\nfunction isValidReceipt(value: unknown): value is MppReceipt {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const { method, reference, status, timestamp } = value as Record<string, unknown>\n return (\n typeof method === 'string' &&\n typeof reference === 'string' &&\n reference !== '' &&\n typeof status === 'string' &&\n typeof timestamp === 'string'\n )\n}\n\n/**\n * Decodes a `Payment-Receipt` header value.\n *\n * Raises a typed {@link MppError} on malformed input — undecodable base64url\n * JSON, or JSON that decodes to something that is not a usable\n * `{ method, reference, status, timestamp }` object — rather than a raw\n * `SyntaxError` or an untyped receipt. This function does not decide whether\n * that failure is fatal for its caller (the receipt is \"unsigned by design,\n * and carries no balance\", so a caller may reasonably treat a decode\n * failure as non-fatal and simply omit the receipt); it only guarantees the\n * failure is typed and raised at the boundary, not as a later `TypeError`\n * on a field access with nothing pointing back at the header that caused it\n * — the same asymmetry {@link parseChallengeHeader} closes for `request=`.\n */\nexport function parseReceiptHeader(headerValue: string): MppReceipt {\n const decoded = decodeBase64UrlJson<unknown>(headerValue.trim(), 'MPP receipt')\n if (!isValidReceipt(decoded)) {\n throw new MppError(\n 'The Payment-Receipt header is not a valid ' +\n '{ method: string, reference: string, status: string, timestamp: string } object.',\n )\n }\n return decoded\n}\n"]}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Typed errors for the MPP surface.
3
+ *
4
+ * The backend deliberately collapses every rejection reason into one code so
5
+ * the endpoint cannot be used as a forgery oracle. The SDK mirrors that: it
6
+ * does not try to reconstruct why a credential was refused.
7
+ */
8
+ /**
9
+ * What a buyer-side failure reports about money already committed.
10
+ *
11
+ * `payments.mpp.fetch` returns this accounting on its success path
12
+ * (`credentialsPresented` / `creditsPresented` on `MppFetchResult`). It is
13
+ * repeated on the ERROR path because that is where a caller needs it most: the
14
+ * credential and its access token are function-local and gone once the helper
15
+ * throws, so an error without these numbers leaves the caller unable to tell
16
+ * whether money left — the one outcome a buyer helper must never produce.
17
+ *
18
+ * Read it with {@link mppSpendOf} rather than casting: it rides on
19
+ * `PaymentsError` too (a `maxCredits` or `planId` guard can fire on the
20
+ * re-challenge turn, after a credential has already been presented).
21
+ */
22
+ export interface MppSpendReport {
23
+ /** How many credentials were minted and sent before the failure (0, 1 or 2). */
24
+ credentialsPresented: number;
25
+ /**
26
+ * Total credits named by the challenges those credentials were minted
27
+ * against, as a decimal string. Present whenever `credentialsPresented > 0`.
28
+ */
29
+ creditsPresented?: string;
30
+ /** `id` of the challenge the last credential was minted against, so the caller can correlate it with the seller's side. */
31
+ challengeId?: string;
32
+ }
33
+ /**
34
+ * Reads the spend accounting off any error raised by `payments.mpp.fetch`.
35
+ *
36
+ * Exported so a caller never has to hand-cast: the field is declared on
37
+ * {@link MppError} but is also attached to the {@link PaymentsError} a
38
+ * caller-constraint guard throws on the re-challenge turn, and those two do
39
+ * not share a base class.
40
+ *
41
+ * A report is attached **only** when at least one credential was presented, so
42
+ * a truthy result always means money may have left, and `undefined` always
43
+ * means nothing was spent. That is what makes `if (mppSpendOf(err))` a usable
44
+ * test rather than one that fires on plain argument validation too.
45
+ */
46
+ export declare function mppSpendOf(error: unknown): MppSpendReport | undefined;
47
+ export declare class MppError extends Error {
48
+ readonly code?: string | undefined;
49
+ /**
50
+ * Spend accounting, set when this error was raised after a credential had
51
+ * been minted and presented. Absent means nothing was spent. Prefer
52
+ * {@link mppSpendOf}, which also reads it off a `PaymentsError`.
53
+ */
54
+ spend?: MppSpendReport;
55
+ constructor(message: string, code?: string | undefined);
56
+ }
57
+ /** `BCK.MPP.0002` — the deployment has no MPP secret, so MPP routes are off. */
58
+ export declare class MppNotConfiguredError extends MppError {
59
+ constructor(message?: string);
60
+ }
61
+ /** `BCK.MPP.0003` — the credential was refused (replay, forgery, plan, balance). */
62
+ export declare class MppCredentialRejectedError extends MppError {
63
+ constructor(message?: string);
64
+ }
65
+ /** `BCK.MPP.0004` — the challenge expired. Fetch a fresh one; do not retry blindly. */
66
+ export declare class MppChallengeExpiredError extends MppError {
67
+ constructor(message?: string);
68
+ }
69
+ /** `BCK.MPP.0005` — the body sent does not match the digest sealed in the challenge. */
70
+ export declare class MppBodyDigestMismatchError extends MppError {
71
+ constructor(message?: string);
72
+ }
73
+ /**
74
+ * Stable `code` on {@link MppSettlementOutcomeUnknownError}. Not a backend
75
+ * code — no `BCK.MPP.*` prefix, so it can never collide with one.
76
+ */
77
+ export declare const MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE = "settlement_outcome_unknown";
78
+ /**
79
+ * Raised when settlement's outcome cannot be determined: `settleCredential`'s
80
+ * own outbound deadline firing before any response, or a 2xx whose body could
81
+ * not be read. Settlement is the one MPP call that burns, so in both cases the
82
+ * burn may have already happened even though the caller received nothing
83
+ * usable. Collapsing that into the same `network_error` `MppError` used for
84
+ * "nothing happened" failures (connection refused, DNS failure, a hung
85
+ * challenge/verify call) would let a real burn get logged and treated exactly
86
+ * like one that never occurred — silently corrupting the seller's own
87
+ * accounting on the call that is not safe to shrug off.
88
+ */
89
+ export declare class MppSettlementOutcomeUnknownError extends MppError {
90
+ constructor(message?: string);
91
+ }
92
+ /**
93
+ * Stable `code` on {@link MppSpendOutcomeUnknownError}. Not a backend code, for
94
+ * the same reason as {@link MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE}.
95
+ */
96
+ export declare const MPP_SPEND_OUTCOME_UNKNOWN_CODE = "spend_outcome_unknown";
97
+ /**
98
+ * The buyer-side mirror of {@link MppSettlementOutcomeUnknownError}: raised
99
+ * when the retry that carries a credential fails at the transport level — a
100
+ * disconnect, a DNS failure, or the caller's own `AbortSignal` firing between
101
+ * the mint and the retry (`init.signal` is re-sent with it).
102
+ *
103
+ * The credential is on the wire by then, so the seller may already have
104
+ * verified and burned it. Left as the raw `TypeError` that `fetch` rejects
105
+ * with, the failure would escape the `catch (e) { if (e instanceof MppError) }`
106
+ * pattern this module and `markdown/mpp-integration.md` both prescribe, and
107
+ * carry no accounting at all: spent, invisible, unrecoverable. Wrapped, it
108
+ * reaches the documented handler with {@link MppSpendReport} attached and the
109
+ * original error preserved on `cause`.
110
+ *
111
+ * A transport failure BEFORE any credential exists is not this error — it stays
112
+ * exactly as `fetch` threw it, because nothing was spent and wrapping it would
113
+ * only obscure a plain network fault.
114
+ */
115
+ export declare class MppSpendOutcomeUnknownError extends MppError {
116
+ /** The original error `fetch` (or the mint) rejected with. */
117
+ readonly cause?: unknown;
118
+ constructor(message: string, cause?: unknown, spend?: MppSpendReport);
119
+ }
120
+ /**
121
+ * What `paymentMiddleware`'s `onAfterSettle` hook receives as its third
122
+ * argument when settlement raised {@link MppSettlementOutcomeUnknownError}.
123
+ * That parameter's declared type is `unknown` (shared with the x402 hook of
124
+ * the same name — out of scope to narrow here), so nothing stops a consumer
125
+ * from casting straight to `MppSettleResult` and silently reading
126
+ * `undefined` for `creditsRedeemed` on this branch. Exporting this shape
127
+ * gives a consumer something concrete to check for instead — e.g.
128
+ * `if ((result as MppSettlementOutcomeUnknown).outcome === 'unknown')` —
129
+ * and documents, at the type level, that the branch exists at all.
130
+ */
131
+ export interface MppSettlementOutcomeUnknown {
132
+ outcome: 'unknown';
133
+ reason: string;
134
+ }
135
+ /**
136
+ * `BCK.MPP.*` codes a buyer can retry by minting a fresh credential against
137
+ * the NEW challenge the same 402 carries alongside them — as opposed to
138
+ * `BCK.MPP.0003`, which means the credential itself was refused and paying
139
+ * again cannot help.
140
+ *
141
+ * `0004` (expired) is obviously retryable: fetch a fresh challenge, pay it.
142
+ * `0005` (body digest mismatch) is less obvious but equally retryable: the
143
+ * fresh challenge is sealed to the digest of the request that just arrived,
144
+ * so a credential minted against it — presented with the SAME body — would
145
+ * succeed. A buyer gate that only excepts `0004` from a bare
146
+ * `code.startsWith('BCK.MPP.')` check wrongly treats `0005` as terminal and
147
+ * gives up on a request that would have worked on the next attempt.
148
+ *
149
+ * Grouped here, once, so a buyer checks {@link isRetryableMppCode} instead of
150
+ * hardcoding the exception list — and so a future retryable code only needs
151
+ * to be added to this one set.
152
+ *
153
+ * Exported so the buyer's own tests derive their table from this set instead of
154
+ * repeating its members, which is what makes "a code added here is honoured by
155
+ * `payments.mpp.fetch`" an enforced property rather than a convention. It is
156
+ * deliberately NOT re-exported from `./index.js`: a mutable-looking membership
157
+ * list is not something to make public API.
158
+ */
159
+ export declare const RETRYABLE_BCK_MPP_CODES: ReadonlySet<string>;
160
+ /** Whether a `BCK.MPP.*` code means "mint a fresh credential and try again"
161
+ * rather than "this credential was refused; a new one changes nothing". */
162
+ export declare function isRetryableMppCode(code: string | undefined): boolean;
163
+ /** Maps a backend error payload onto the typed error hierarchy. */
164
+ export declare function toMppError(code: string | undefined, message: string): MppError;
165
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/mpp/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,2HAA2H;IAC3H,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAOrE;AAED,qBAAa,QAAS,SAAQ,KAAK;IAU/B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;IATxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,cAAc,CAAA;gBAGpB,OAAO,EAAE,MAAM,EACN,IAAI,CAAC,EAAE,MAAM,YAAA;CAKzB;AAED,gFAAgF;AAChF,qBAAa,qBAAsB,SAAQ,QAAQ;gBACrC,OAAO,SAA8C;CAIlE;AAED,oFAAoF;AACpF,qBAAa,0BAA2B,SAAQ,QAAQ;gBAC1C,OAAO,SAA4B;CAIhD;AAED,uFAAuF;AACvF,qBAAa,wBAAyB,SAAQ,QAAQ;gBACxC,OAAO,SAA0B;CAI9C;AAED,wFAAwF;AACxF,qBAAa,0BAA2B,SAAQ,QAAQ;gBAC1C,OAAO,SAA6B;CAIjD;AAED;;;GAGG;AACH,eAAO,MAAM,mCAAmC,+BAA+B,CAAA;AAE/E;;;;;;;;;;GAUG;AACH,qBAAa,gCAAiC,SAAQ,QAAQ;gBAE1D,OAAO,SAAuI;CAejJ;AAED;;;GAGG;AACH,eAAO,MAAM,8BAA8B,0BAA0B,CAAA;AAErE;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,2BAA4B,SAAQ,QAAQ;IACvD,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;gBAEZ,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,cAAc;CAMrE;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,SAAS,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAGtD,CAAA;AAEF;4EAC4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAa9E"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Typed errors for the MPP surface.
3
+ *
4
+ * The backend deliberately collapses every rejection reason into one code so
5
+ * the endpoint cannot be used as a forgery oracle. The SDK mirrors that: it
6
+ * does not try to reconstruct why a credential was refused.
7
+ */
8
+ /**
9
+ * Reads the spend accounting off any error raised by `payments.mpp.fetch`.
10
+ *
11
+ * Exported so a caller never has to hand-cast: the field is declared on
12
+ * {@link MppError} but is also attached to the {@link PaymentsError} a
13
+ * caller-constraint guard throws on the re-challenge turn, and those two do
14
+ * not share a base class.
15
+ *
16
+ * A report is attached **only** when at least one credential was presented, so
17
+ * a truthy result always means money may have left, and `undefined` always
18
+ * means nothing was spent. That is what makes `if (mppSpendOf(err))` a usable
19
+ * test rather than one that fires on plain argument validation too.
20
+ */
21
+ export function mppSpendOf(error) {
22
+ if (typeof error !== 'object' || error === null)
23
+ return undefined;
24
+ const spend = error.spend;
25
+ if (typeof spend !== 'object' || spend === null)
26
+ return undefined;
27
+ return typeof spend.credentialsPresented === 'number'
28
+ ? spend
29
+ : undefined;
30
+ }
31
+ export class MppError extends Error {
32
+ constructor(message, code) {
33
+ super(message);
34
+ this.code = code;
35
+ this.name = 'MppError';
36
+ }
37
+ }
38
+ /** `BCK.MPP.0002` — the deployment has no MPP secret, so MPP routes are off. */
39
+ export class MppNotConfiguredError extends MppError {
40
+ constructor(message = 'MPP is not configured on this environment') {
41
+ super(message, 'BCK.MPP.0002');
42
+ this.name = 'MppNotConfiguredError';
43
+ }
44
+ }
45
+ /** `BCK.MPP.0003` — the credential was refused (replay, forgery, plan, balance). */
46
+ export class MppCredentialRejectedError extends MppError {
47
+ constructor(message = 'MPP credential rejected') {
48
+ super(message, 'BCK.MPP.0003');
49
+ this.name = 'MppCredentialRejectedError';
50
+ }
51
+ }
52
+ /** `BCK.MPP.0004` — the challenge expired. Fetch a fresh one; do not retry blindly. */
53
+ export class MppChallengeExpiredError extends MppError {
54
+ constructor(message = 'MPP challenge expired') {
55
+ super(message, 'BCK.MPP.0004');
56
+ this.name = 'MppChallengeExpiredError';
57
+ }
58
+ }
59
+ /** `BCK.MPP.0005` — the body sent does not match the digest sealed in the challenge. */
60
+ export class MppBodyDigestMismatchError extends MppError {
61
+ constructor(message = 'MPP body digest mismatch') {
62
+ super(message, 'BCK.MPP.0005');
63
+ this.name = 'MppBodyDigestMismatchError';
64
+ }
65
+ }
66
+ /**
67
+ * Stable `code` on {@link MppSettlementOutcomeUnknownError}. Not a backend
68
+ * code — no `BCK.MPP.*` prefix, so it can never collide with one.
69
+ */
70
+ export const MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE = 'settlement_outcome_unknown';
71
+ /**
72
+ * Raised when settlement's outcome cannot be determined: `settleCredential`'s
73
+ * own outbound deadline firing before any response, or a 2xx whose body could
74
+ * not be read. Settlement is the one MPP call that burns, so in both cases the
75
+ * burn may have already happened even though the caller received nothing
76
+ * usable. Collapsing that into the same `network_error` `MppError` used for
77
+ * "nothing happened" failures (connection refused, DNS failure, a hung
78
+ * challenge/verify call) would let a real burn get logged and treated exactly
79
+ * like one that never occurred — silently corrupting the seller's own
80
+ * accounting on the call that is not safe to shrug off.
81
+ */
82
+ export class MppSettlementOutcomeUnknownError extends MppError {
83
+ constructor(message = 'MPP settlement outcome unknown: the request timed out before the backend responded, so the credits may or may not have been burned') {
84
+ // No backend issues this code — the condition is detected here — but it
85
+ // still carries one, following the same convention as the other
86
+ // SDK-invented codes (`network_error`, `http_${status}`). `code` is the
87
+ // only DATA-level discriminant on this hierarchy, and this is the branch
88
+ // whose whole point is that misclassifying it corrupts the seller's
89
+ // accounting. Two copies of this package in one dependency tree make
90
+ // `instanceof` false for a genuinely-MPP error, and the check would
91
+ // degrade silently to the "nothing happened" path — which the
92
+ // integration guide tells sellers to rely on. Same across a process or
93
+ // serialization boundary.
94
+ super(message, MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE);
95
+ this.name = 'MppSettlementOutcomeUnknownError';
96
+ }
97
+ }
98
+ /**
99
+ * Stable `code` on {@link MppSpendOutcomeUnknownError}. Not a backend code, for
100
+ * the same reason as {@link MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE}.
101
+ */
102
+ export const MPP_SPEND_OUTCOME_UNKNOWN_CODE = 'spend_outcome_unknown';
103
+ /**
104
+ * The buyer-side mirror of {@link MppSettlementOutcomeUnknownError}: raised
105
+ * when the retry that carries a credential fails at the transport level — a
106
+ * disconnect, a DNS failure, or the caller's own `AbortSignal` firing between
107
+ * the mint and the retry (`init.signal` is re-sent with it).
108
+ *
109
+ * The credential is on the wire by then, so the seller may already have
110
+ * verified and burned it. Left as the raw `TypeError` that `fetch` rejects
111
+ * with, the failure would escape the `catch (e) { if (e instanceof MppError) }`
112
+ * pattern this module and `markdown/mpp-integration.md` both prescribe, and
113
+ * carry no accounting at all: spent, invisible, unrecoverable. Wrapped, it
114
+ * reaches the documented handler with {@link MppSpendReport} attached and the
115
+ * original error preserved on `cause`.
116
+ *
117
+ * A transport failure BEFORE any credential exists is not this error — it stays
118
+ * exactly as `fetch` threw it, because nothing was spent and wrapping it would
119
+ * only obscure a plain network fault.
120
+ */
121
+ export class MppSpendOutcomeUnknownError extends MppError {
122
+ constructor(message, cause, spend) {
123
+ super(message, MPP_SPEND_OUTCOME_UNKNOWN_CODE);
124
+ this.name = 'MppSpendOutcomeUnknownError';
125
+ this.cause = cause;
126
+ this.spend = spend;
127
+ }
128
+ }
129
+ /**
130
+ * `BCK.MPP.*` codes a buyer can retry by minting a fresh credential against
131
+ * the NEW challenge the same 402 carries alongside them — as opposed to
132
+ * `BCK.MPP.0003`, which means the credential itself was refused and paying
133
+ * again cannot help.
134
+ *
135
+ * `0004` (expired) is obviously retryable: fetch a fresh challenge, pay it.
136
+ * `0005` (body digest mismatch) is less obvious but equally retryable: the
137
+ * fresh challenge is sealed to the digest of the request that just arrived,
138
+ * so a credential minted against it — presented with the SAME body — would
139
+ * succeed. A buyer gate that only excepts `0004` from a bare
140
+ * `code.startsWith('BCK.MPP.')` check wrongly treats `0005` as terminal and
141
+ * gives up on a request that would have worked on the next attempt.
142
+ *
143
+ * Grouped here, once, so a buyer checks {@link isRetryableMppCode} instead of
144
+ * hardcoding the exception list — and so a future retryable code only needs
145
+ * to be added to this one set.
146
+ *
147
+ * Exported so the buyer's own tests derive their table from this set instead of
148
+ * repeating its members, which is what makes "a code added here is honoured by
149
+ * `payments.mpp.fetch`" an enforced property rather than a convention. It is
150
+ * deliberately NOT re-exported from `./index.js`: a mutable-looking membership
151
+ * list is not something to make public API.
152
+ */
153
+ export const RETRYABLE_BCK_MPP_CODES = new Set([
154
+ 'BCK.MPP.0004',
155
+ 'BCK.MPP.0005',
156
+ ]);
157
+ /** Whether a `BCK.MPP.*` code means "mint a fresh credential and try again"
158
+ * rather than "this credential was refused; a new one changes nothing". */
159
+ export function isRetryableMppCode(code) {
160
+ return code !== undefined && RETRYABLE_BCK_MPP_CODES.has(code);
161
+ }
162
+ /** Maps a backend error payload onto the typed error hierarchy. */
163
+ export function toMppError(code, message) {
164
+ switch (code) {
165
+ case 'BCK.MPP.0002':
166
+ return new MppNotConfiguredError(message);
167
+ case 'BCK.MPP.0003':
168
+ return new MppCredentialRejectedError(message);
169
+ case 'BCK.MPP.0004':
170
+ return new MppChallengeExpiredError(message);
171
+ case 'BCK.MPP.0005':
172
+ return new MppBodyDigestMismatchError(message);
173
+ default:
174
+ return new MppError(message, code);
175
+ }
176
+ }
177
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/mpp/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Typed errors for the MPP surface.\n *\n * The backend deliberately collapses every rejection reason into one code so\n * the endpoint cannot be used as a forgery oracle. The SDK mirrors that: it\n * does not try to reconstruct why a credential was refused.\n */\n\n/**\n * What a buyer-side failure reports about money already committed.\n *\n * `payments.mpp.fetch` returns this accounting on its success path\n * (`credentialsPresented` / `creditsPresented` on `MppFetchResult`). It is\n * repeated on the ERROR path because that is where a caller needs it most: the\n * credential and its access token are function-local and gone once the helper\n * throws, so an error without these numbers leaves the caller unable to tell\n * whether money left — the one outcome a buyer helper must never produce.\n *\n * Read it with {@link mppSpendOf} rather than casting: it rides on\n * `PaymentsError` too (a `maxCredits` or `planId` guard can fire on the\n * re-challenge turn, after a credential has already been presented).\n */\nexport interface MppSpendReport {\n /** How many credentials were minted and sent before the failure (0, 1 or 2). */\n credentialsPresented: number\n /**\n * Total credits named by the challenges those credentials were minted\n * against, as a decimal string. Present whenever `credentialsPresented > 0`.\n */\n creditsPresented?: string\n /** `id` of the challenge the last credential was minted against, so the caller can correlate it with the seller's side. */\n challengeId?: string\n}\n\n/**\n * Reads the spend accounting off any error raised by `payments.mpp.fetch`.\n *\n * Exported so a caller never has to hand-cast: the field is declared on\n * {@link MppError} but is also attached to the {@link PaymentsError} a\n * caller-constraint guard throws on the re-challenge turn, and those two do\n * not share a base class.\n *\n * A report is attached **only** when at least one credential was presented, so\n * a truthy result always means money may have left, and `undefined` always\n * means nothing was spent. That is what makes `if (mppSpendOf(err))` a usable\n * test rather than one that fires on plain argument validation too.\n */\nexport function mppSpendOf(error: unknown): MppSpendReport | undefined {\n if (typeof error !== 'object' || error === null) return undefined\n const spend = (error as { spend?: unknown }).spend\n if (typeof spend !== 'object' || spend === null) return undefined\n return typeof (spend as MppSpendReport).credentialsPresented === 'number'\n ? (spend as MppSpendReport)\n : undefined\n}\n\nexport class MppError extends Error {\n /**\n * Spend accounting, set when this error was raised after a credential had\n * been minted and presented. Absent means nothing was spent. Prefer\n * {@link mppSpendOf}, which also reads it off a `PaymentsError`.\n */\n spend?: MppSpendReport\n\n constructor(\n message: string,\n readonly code?: string,\n ) {\n super(message)\n this.name = 'MppError'\n }\n}\n\n/** `BCK.MPP.0002` — the deployment has no MPP secret, so MPP routes are off. */\nexport class MppNotConfiguredError extends MppError {\n constructor(message = 'MPP is not configured on this environment') {\n super(message, 'BCK.MPP.0002')\n this.name = 'MppNotConfiguredError'\n }\n}\n\n/** `BCK.MPP.0003` — the credential was refused (replay, forgery, plan, balance). */\nexport class MppCredentialRejectedError extends MppError {\n constructor(message = 'MPP credential rejected') {\n super(message, 'BCK.MPP.0003')\n this.name = 'MppCredentialRejectedError'\n }\n}\n\n/** `BCK.MPP.0004` — the challenge expired. Fetch a fresh one; do not retry blindly. */\nexport class MppChallengeExpiredError extends MppError {\n constructor(message = 'MPP challenge expired') {\n super(message, 'BCK.MPP.0004')\n this.name = 'MppChallengeExpiredError'\n }\n}\n\n/** `BCK.MPP.0005` — the body sent does not match the digest sealed in the challenge. */\nexport class MppBodyDigestMismatchError extends MppError {\n constructor(message = 'MPP body digest mismatch') {\n super(message, 'BCK.MPP.0005')\n this.name = 'MppBodyDigestMismatchError'\n }\n}\n\n/**\n * Stable `code` on {@link MppSettlementOutcomeUnknownError}. Not a backend\n * code — no `BCK.MPP.*` prefix, so it can never collide with one.\n */\nexport const MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE = 'settlement_outcome_unknown'\n\n/**\n * Raised when settlement's outcome cannot be determined: `settleCredential`'s\n * own outbound deadline firing before any response, or a 2xx whose body could\n * not be read. Settlement is the one MPP call that burns, so in both cases the\n * burn may have already happened even though the caller received nothing\n * usable. Collapsing that into the same `network_error` `MppError` used for\n * \"nothing happened\" failures (connection refused, DNS failure, a hung\n * challenge/verify call) would let a real burn get logged and treated exactly\n * like one that never occurred — silently corrupting the seller's own\n * accounting on the call that is not safe to shrug off.\n */\nexport class MppSettlementOutcomeUnknownError extends MppError {\n constructor(\n message = 'MPP settlement outcome unknown: the request timed out before the backend responded, so the credits may or may not have been burned',\n ) {\n // No backend issues this code — the condition is detected here — but it\n // still carries one, following the same convention as the other\n // SDK-invented codes (`network_error`, `http_${status}`). `code` is the\n // only DATA-level discriminant on this hierarchy, and this is the branch\n // whose whole point is that misclassifying it corrupts the seller's\n // accounting. Two copies of this package in one dependency tree make\n // `instanceof` false for a genuinely-MPP error, and the check would\n // degrade silently to the \"nothing happened\" path — which the\n // integration guide tells sellers to rely on. Same across a process or\n // serialization boundary.\n super(message, MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE)\n this.name = 'MppSettlementOutcomeUnknownError'\n }\n}\n\n/**\n * Stable `code` on {@link MppSpendOutcomeUnknownError}. Not a backend code, for\n * the same reason as {@link MPP_SETTLEMENT_OUTCOME_UNKNOWN_CODE}.\n */\nexport const MPP_SPEND_OUTCOME_UNKNOWN_CODE = 'spend_outcome_unknown'\n\n/**\n * The buyer-side mirror of {@link MppSettlementOutcomeUnknownError}: raised\n * when the retry that carries a credential fails at the transport level — a\n * disconnect, a DNS failure, or the caller's own `AbortSignal` firing between\n * the mint and the retry (`init.signal` is re-sent with it).\n *\n * The credential is on the wire by then, so the seller may already have\n * verified and burned it. Left as the raw `TypeError` that `fetch` rejects\n * with, the failure would escape the `catch (e) { if (e instanceof MppError) }`\n * pattern this module and `markdown/mpp-integration.md` both prescribe, and\n * carry no accounting at all: spent, invisible, unrecoverable. Wrapped, it\n * reaches the documented handler with {@link MppSpendReport} attached and the\n * original error preserved on `cause`.\n *\n * A transport failure BEFORE any credential exists is not this error — it stays\n * exactly as `fetch` threw it, because nothing was spent and wrapping it would\n * only obscure a plain network fault.\n */\nexport class MppSpendOutcomeUnknownError extends MppError {\n /** The original error `fetch` (or the mint) rejected with. */\n readonly cause?: unknown\n\n constructor(message: string, cause?: unknown, spend?: MppSpendReport) {\n super(message, MPP_SPEND_OUTCOME_UNKNOWN_CODE)\n this.name = 'MppSpendOutcomeUnknownError'\n this.cause = cause\n this.spend = spend\n }\n}\n\n/**\n * What `paymentMiddleware`'s `onAfterSettle` hook receives as its third\n * argument when settlement raised {@link MppSettlementOutcomeUnknownError}.\n * That parameter's declared type is `unknown` (shared with the x402 hook of\n * the same name — out of scope to narrow here), so nothing stops a consumer\n * from casting straight to `MppSettleResult` and silently reading\n * `undefined` for `creditsRedeemed` on this branch. Exporting this shape\n * gives a consumer something concrete to check for instead — e.g.\n * `if ((result as MppSettlementOutcomeUnknown).outcome === 'unknown')` —\n * and documents, at the type level, that the branch exists at all.\n */\nexport interface MppSettlementOutcomeUnknown {\n outcome: 'unknown'\n reason: string\n}\n\n/**\n * `BCK.MPP.*` codes a buyer can retry by minting a fresh credential against\n * the NEW challenge the same 402 carries alongside them — as opposed to\n * `BCK.MPP.0003`, which means the credential itself was refused and paying\n * again cannot help.\n *\n * `0004` (expired) is obviously retryable: fetch a fresh challenge, pay it.\n * `0005` (body digest mismatch) is less obvious but equally retryable: the\n * fresh challenge is sealed to the digest of the request that just arrived,\n * so a credential minted against it — presented with the SAME body — would\n * succeed. A buyer gate that only excepts `0004` from a bare\n * `code.startsWith('BCK.MPP.')` check wrongly treats `0005` as terminal and\n * gives up on a request that would have worked on the next attempt.\n *\n * Grouped here, once, so a buyer checks {@link isRetryableMppCode} instead of\n * hardcoding the exception list — and so a future retryable code only needs\n * to be added to this one set.\n *\n * Exported so the buyer's own tests derive their table from this set instead of\n * repeating its members, which is what makes \"a code added here is honoured by\n * `payments.mpp.fetch`\" an enforced property rather than a convention. It is\n * deliberately NOT re-exported from `./index.js`: a mutable-looking membership\n * list is not something to make public API.\n */\nexport const RETRYABLE_BCK_MPP_CODES: ReadonlySet<string> = new Set([\n 'BCK.MPP.0004',\n 'BCK.MPP.0005',\n])\n\n/** Whether a `BCK.MPP.*` code means \"mint a fresh credential and try again\"\n * rather than \"this credential was refused; a new one changes nothing\". */\nexport function isRetryableMppCode(code: string | undefined): boolean {\n return code !== undefined && RETRYABLE_BCK_MPP_CODES.has(code)\n}\n\n/** Maps a backend error payload onto the typed error hierarchy. */\nexport function toMppError(code: string | undefined, message: string): MppError {\n switch (code) {\n case 'BCK.MPP.0002':\n return new MppNotConfiguredError(message)\n case 'BCK.MPP.0003':\n return new MppCredentialRejectedError(message)\n case 'BCK.MPP.0004':\n return new MppChallengeExpiredError(message)\n case 'BCK.MPP.0005':\n return new MppBodyDigestMismatchError(message)\n default:\n return new MppError(message, code)\n }\n}\n"]}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * The buyer half of MPP: pay a challenged endpoint with an existing Nevermined
3
+ * delegation.
4
+ *
5
+ * The buyer learns nothing about MPP beyond calling this instead of `fetch`.
6
+ * The plan comes out of the challenge, the credential is built from the
7
+ * challenge plus an MPP-domain access token, and the request is retried once.
8
+ *
9
+ * Two error families are used deliberately:
10
+ * - {@link PaymentsError} (`code: 'validation'`) for guards this call refuses
11
+ * to even attempt — a bad argument, a challenge that violates a
12
+ * caller-supplied constraint (`planId`, `maxCredits`), or a body that
13
+ * cannot be replayed. None of these mean a payment failed; nothing was ever
14
+ * attempted.
15
+ * - {@link MppError} (and its typed subclasses) for what the wire actually
16
+ * said: a rejected credential, a malformed challenge, an MPP-disabled
17
+ * environment. A caller branching on `instanceof MppError` to mean
18
+ * "the payment failed" gets exactly that, and no more.
19
+ */
20
+ import type { DelegationConfig, X402TokenOptions } from '../common/types.js';
21
+ import type { MppReceipt } from './types.js';
22
+ /**
23
+ * Options for {@link mppFetch} / `MppAPI.fetch`.
24
+ *
25
+ * @experimental The MPP buyer surface may change in a minor release.
26
+ *
27
+ * The request's `init.body`, if any, must be replayable **if the endpoint may
28
+ * challenge the request**: a `ReadableStream` body throws a typed
29
+ * {@link PaymentsError} once a 402 challenge actually requires a retry, since
30
+ * the stream cannot be resent. A request that is never challenged sends a
31
+ * stream body exactly once, exactly like plain `fetch`.
32
+ *
33
+ * This helper mints `nvm:erc4337` access tokens only in this release — a
34
+ * buyer holding an `nvm:card-delegation` delegation cannot use it yet.
35
+ */
36
+ export interface MppFetchOptions {
37
+ /** The delegation that backs the payment — the same one x402 uses. */
38
+ delegationConfig: DelegationConfig;
39
+ /**
40
+ * Overrides the agent id the seller's challenge names — the minted token is
41
+ * addressed to this agent id instead of whatever the challenge carries.
42
+ * Unlike `planId`, this is not a guard: a mismatch is not checked or
43
+ * refused, it simply replaces what the seller asked for. Leave unset to
44
+ * honor the challenge as issued.
45
+ */
46
+ agentId?: string;
47
+ /** Fail before minting if the challenge names a different plan than this. */
48
+ planId?: string;
49
+ /**
50
+ * Budget for the WHOLE call, not for one challenge: the helper refuses to
51
+ * mint whenever the credits named so far plus the credits this challenge
52
+ * asks for would exceed it. A seller unilaterally names the price, and a
53
+ * re-challenge names it again — a per-turn cap would therefore bound each
54
+ * turn at `maxCredits` and the call at twice it, which is not what "cap"
55
+ * reads as. `creditsPresented` on the result is the same running total, so
56
+ * the two always speak about the same number.
57
+ *
58
+ * Must be a non-negative integer (a decimal string, a safe integer, or a
59
+ * `bigint`); anything else is refused with a {@link PaymentsError} at entry,
60
+ * before the first request, rather than mid-flight on the first 402.
61
+ */
62
+ maxCredits?: string | number | bigint;
63
+ }
64
+ /**
65
+ * What {@link mppFetch} / `MppAPI.fetch` resolves to.
66
+ *
67
+ * @experimental Fields may be added or change meaning in a minor release —
68
+ * `settled` and `creditsPresented` both did during review.
69
+ */
70
+ export interface MppFetchResult {
71
+ /** The final response — the paid one when a payment happened. */
72
+ response: Response;
73
+ /**
74
+ * The decoded `Payment-Receipt`, when the server returned one and it
75
+ * decoded cleanly. A malformed receipt never throws — it leaves this
76
+ * absent (with a console warning) rather than destroying a response the
77
+ * caller already paid for.
78
+ */
79
+ receipt?: MppReceipt;
80
+ /**
81
+ * Whether the endpoint returned settlement evidence: a `Payment-Receipt` that
82
+ * decoded cleanly and does not state failure outright. Never derived from the
83
+ * HTTP status.
84
+ *
85
+ * `receipt.status` is read asymmetrically, on purpose. Success is NOT
86
+ * recognized — `'success'` is the only value with agreement behind it, so
87
+ * treating an unrecognized `'ok'`/`'completed'` as failure would report an
88
+ * unpaid call that was in fact paid. An explicit negative (`'failed'`,
89
+ * `'failure'`, `'declined'`, `'error'`) is a different thing from an unknown
90
+ * vocabulary, and is excluded: `paid: true` on a receipt that says the
91
+ * settlement failed is wrong in the one direction this field must never be
92
+ * wrong. Anything else stays `settled: true` with `receipt` on the result, so
93
+ * a caller paying third parties can judge the value itself.
94
+ *
95
+ * This SDK's own seller attaches a receipt only when settlement succeeded, so
96
+ * the distinction is third-party exposure rather than in-ecosystem.
97
+ */
98
+ settled: boolean;
99
+ /**
100
+ * How many credentials were minted and presented to the endpoint during
101
+ * this call (0, 1 or 2). This is NOT the same as `settled`:
102
+ * `credentialsPresented > 0` with `settled: false` means the seller may
103
+ * already have burned credits for a credential whose fate is unknown to
104
+ * the caller — treat that as "do not blindly retry", not as "nothing
105
+ * happened".
106
+ */
107
+ credentialsPresented: number;
108
+ /**
109
+ * TOTAL credits named by every challenge a credential was minted against
110
+ * during this call, as a decimal string — summed, not the last turn's
111
+ * amount, since a re-challenge is free to name a different price and the
112
+ * caller is accounting for the call. Present whenever
113
+ * `credentialsPresented > 0`.
114
+ *
115
+ * It is an **upper bound** on what may have burned, never a lower one. A
116
+ * seller that answers a retryable code while replaying the identical
117
+ * challenge id gets a second credential minted against that same challenge
118
+ * (a code decides alone — see {@link mppFetch}), and against a seller that
119
+ * keys single-use on the challenge id, as this SDK's middleware does, that
120
+ * second credential is refused as a replay and burns nothing. The count is
121
+ * deliberately not lowered for it: this field answers "what could have left",
122
+ * and guessing which of a remote's credentials it honoured would answer a
123
+ * question the buyer cannot see.
124
+ */
125
+ creditsPresented?: string;
126
+ /**
127
+ * Whether a credential was presented AND the final response looks
128
+ * successful: `response.ok && settled`. A 2xx with no receipt (a
129
+ * settlement that silently failed) and a non-2xx with a receipt (settle-
130
+ * then-error) are both `paid: false` — check `settled` and
131
+ * `credentialsPresented` for the honest picture in either case.
132
+ *
133
+ * `ok: true, paid: false, credentialsPresented: 1` is a ROUTINE outcome, not
134
+ * an exotic one: a seller whose handler streams or flushes has already sent
135
+ * its headers when settlement runs, so `Payment-Receipt` cannot be attached
136
+ * to a response that is already on the wire (this SDK's own middleware says
137
+ * so where it settles detached). The credits were burned. Never read this
138
+ * combination as "the payment did not happen" and retry.
139
+ */
140
+ paid: boolean;
141
+ }
142
+ /** Mints an MPP access token for a plan. Supplied by `MppAPI`. */
143
+ export type MppTokenMinter = (planId: string, agentId?: string, tokenOptions?: X402TokenOptions) => Promise<{
144
+ accessToken: string;
145
+ }>;
146
+ /**
147
+ * Performs the request, paying an MPP challenge if one comes back.
148
+ *
149
+ * At most one re-challenge cycle is followed: a seller that keeps challenging
150
+ * a freshly paid credential is not going to be satisfied by looping, and a
151
+ * loop would burn a credential per turn.
152
+ *
153
+ * The default on a retry-turn 402 is to STOP, not to pay again. **A code, when
154
+ * present, decides alone**: one {@link isRetryableMppCode} accepts
155
+ * (`BCK.MPP.0004` expired, `BCK.MPP.0005` body-digest mismatch) is retried,
156
+ * every other code is terminal — including a non-`BCK.MPP.*` one, e.g. the
157
+ * `network_error`/`http_500`-shaped code `MppAPI.post` can synthesize and this
158
+ * repo's own seller forwards. The challenge id is not consulted on that path,
159
+ * so a retryable code replaying the identical id does re-mint; `maxCredits`,
160
+ * not id-freshness, is what bounds what that can cost.
161
+ *
162
+ * **With no code**, freshness is the whole signal: a challenge whose `id`
163
+ * differs from the one just presented is a real re-challenge and is retried
164
+ * once, while the identical id replayed, an unparseable challenge or an
165
+ * unreadable body are terminal — a credential already proven invalid is never
166
+ * paid for twice.
167
+ *
168
+ * ### What throws and what comes back as a 402
169
+ *
170
+ * Only a rejection the remote NAMED throws. Three dead ends RETURN the 402
171
+ * instead, because the response is evidence the caller may need and throwing
172
+ * would discard it:
173
+ *
174
+ * 1. A 402 with no USABLE `Payment` challenge (either turn): no
175
+ * `WWW-Authenticate`, another scheme, or a `Payment` challenge missing a
176
+ * required param — {@link parseChallengeHeader} yields nothing for all
177
+ * three, so a seller that announced `Payment` and then sent it malformed
178
+ * lands here too, not only one that does not speak MPP.
179
+ * `credentialsPresented: 0`.
180
+ * 2. A retry-turn 402 that IS retryable but carries no challenge to retry
181
+ * against, so the next turn finds nothing to mint for.
182
+ * 3. The one re-challenge cycle spent: two credentials presented and the
183
+ * seller still answering 402.
184
+ *
185
+ * In cases 2 and 3 a credential WAS presented and may have been burned, so the
186
+ * result is `paid: false` with a non-zero `credentialsPresented` and the last
187
+ * 402 as `response`. Checking `response.ok` is therefore not optional — a
188
+ * resolved promise does not mean the request was paid for.
189
+ *
190
+ * Every throw raised after a credential was presented carries the same
191
+ * accounting as {@link MppSpendReport}, readable with `mppSpendOf(error)`,
192
+ * including a transport failure on the credential-bearing retry (wrapped as
193
+ * {@link MppSpendOutcomeUnknownError} so `instanceof MppError` catches it).
194
+ */
195
+ export declare function mppFetch(mintToken: MppTokenMinter, input: string | URL, init: RequestInit | undefined, options: MppFetchOptions): Promise<MppFetchResult>;
196
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/mpp/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAI5E,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,gBAAgB,EAAE,gBAAgB,CAAA;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,EAAE,QAAQ,CAAA;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,UAAU,CAAA;IACpB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,OAAO,CAAA;CACd;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,gBAAgB,KAC5B,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAuPrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAoKzB"}