@objectstack/plugin-webhooks 7.2.1 → 7.4.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 (38) hide show
  1. package/.turbo/turbo-build.log +30 -26
  2. package/CHANGELOG.md +116 -0
  3. package/dist/chunk-7HS5DLU2.js +319 -0
  4. package/dist/chunk-7HS5DLU2.js.map +1 -0
  5. package/dist/{chunk-FA66GQEO.cjs → chunk-HF7CCDPB.cjs} +3 -3
  6. package/dist/{chunk-FA66GQEO.cjs.map → chunk-HF7CCDPB.cjs.map} +1 -1
  7. package/dist/{chunk-BS2QTZH3.js → chunk-KNGLLSSP.js} +2 -2
  8. package/dist/{chunk-MJZGD37S.cjs → chunk-TDSI7UHY.cjs} +138 -3
  9. package/dist/chunk-TDSI7UHY.cjs.map +1 -0
  10. package/dist/index.cjs +43 -14
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.js +33 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/schema.cjs +4 -2
  15. package/dist/schema.cjs.map +1 -1
  16. package/dist/schema.d.cts +3002 -137
  17. package/dist/schema.d.ts +3002 -137
  18. package/dist/schema.js +3 -1
  19. package/dist/sql-outbox.cjs +3 -3
  20. package/dist/sql-outbox.js +2 -2
  21. package/dist/translations-AV47AVPV.js +727 -0
  22. package/dist/translations-AV47AVPV.js.map +1 -0
  23. package/dist/translations-OAKKANSP.cjs +727 -0
  24. package/dist/translations-OAKKANSP.cjs.map +1 -0
  25. package/package.json +6 -6
  26. package/scripts/i18n-extract.config.ts +32 -0
  27. package/src/schema.ts +7 -3
  28. package/src/sys-webhook.object.ts +177 -0
  29. package/src/translations/en.objects.generated.ts +187 -0
  30. package/src/translations/es-ES.objects.generated.ts +187 -0
  31. package/src/translations/index.ts +23 -0
  32. package/src/translations/ja-JP.objects.generated.ts +187 -0
  33. package/src/translations/zh-CN.objects.generated.ts +187 -0
  34. package/src/webhook-outbox-plugin.ts +38 -7
  35. package/dist/chunk-33LYZT7O.js +0 -184
  36. package/dist/chunk-33LYZT7O.js.map +0 -1
  37. package/dist/chunk-MJZGD37S.cjs.map +0 -1
  38. /package/dist/{chunk-BS2QTZH3.js.map → chunk-KNGLLSSP.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-FA66GQEO.cjs","../src/sql-outbox.ts","../src/outbox.ts","../src/partition.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACFA,gCAA2B;ADI3B;AACA;AEkHO,IAAM,eAAA,EAAN,MAAA,QAA6B,MAAM;AAAA,EACtC,WAAA,CACI,OAAA,EACS,IAAA,EACX;AACE,IAAA,KAAA,CAAM,OAAO,CAAA;AAFJ,IAAA,IAAA,CAAA,KAAA,EAAA,IAAA;AAGT,IAAA,IAAA,CAAK,KAAA,EAAO,gBAAA;AAAA,EAChB;AACJ,CAAA;AFlHA;AACA;AGNO,SAAS,aAAA,CAAc,GAAA,EAAa,KAAA,EAAuB;AAC9D,EAAA,GAAA,CAAI,MAAA,GAAS,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,6BAA6B,CAAA;AAC7D,EAAA,IAAI,EAAA,EAAI,UAAA;AACR,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GAAA,CAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,EAAA,GAAK,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AACrB,IAAA,EAAA,EAAI,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,QAAU,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,CAAC,EAAA,EAAI,KAAA;AAC7B;AHQA;AACA;ACmDO,IAAM,iBAAA,EAAN,MAAiD;AAAA,EAIpD,WAAA,CACqB,MAAA,EACjB,IAAA,EACF;AAFmB,IAAA,IAAA,CAAA,OAAA,EAAA,MAAA;AAGjB,IAAA,GAAA,CAAI,IAAA,CAAK,eAAA,GAAkB,CAAA,EAAG;AAC1B,MAAA,MAAM,IAAI,KAAA,CAAM,8CAA8C,CAAA;AAAA,IAClE;AACA,IAAA,IAAA,CAAK,WAAA,mBAAa,IAAA,CAAK,UAAA,UAAc,wCAAA;AACrC,IAAA,IAAA,CAAK,eAAA,EAAiB,IAAA,CAAK,cAAA;AAAA,EAC/B;AAAA,EAEA,MAAM,OAAA,CAAQ,KAAA,EAAsC;AAIhD,IAAA,MAAM,SAAA,EAAW,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,UAAA,EAAY;AAAA,MACxD,KAAA,EAAO,EAAE,QAAA,EAAU,KAAA,CAAM,OAAA,EAAS,UAAA,EAAY,KAAA,CAAM,UAAU,CAAA;AAAA,MAC9D,MAAA,EAAQ,CAAC,IAAI;AAAA,IACjB,CAAC,CAAA;AACD,IAAA,GAAA,iBAAI,QAAA,2BAAU,IAAA,EAAI,OAAO,QAAA,CAAS,EAAA;AAElC,IAAA,MAAM,GAAA,EAAK,gCAAA,CAAW;AACtB,IAAA,MAAM,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA;AACrB,IAAA,MAAM,IAAA,EAAoD;AAAA,MACtD,EAAA;AAAA,MACA,UAAA,EAAY,KAAA,CAAM,SAAA;AAAA,MAClB,QAAA,EAAU,KAAA,CAAM,OAAA;AAAA,MAChB,UAAA,EAAY,KAAA,CAAM,SAAA;AAAA,MAClB,GAAA,EAAK,KAAA,CAAM,GAAA;AAAA,MACX,MAAA,mBAAQ,KAAA,CAAM,MAAA,UAAU,QAAA;AAAA,MACxB,YAAA,EAAc,KAAA,CAAM,QAAA,EAAU,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,OAAO,EAAA,EAAI,KAAA,CAAA;AAAA,MAC9D,MAAA,EAAQ,KAAA,CAAM,MAAA;AAAA,MACd,UAAA,EAAY,KAAA,CAAM,SAAA;AAAA,MAClB,YAAA,EAAc,IAAA,CAAK,SAAA,kBAAU,KAAA,CAAM,OAAA,UAAW,MAAI,CAAA;AAAA,MAClD,aAAA,EAAe,aAAA,CAAc,KAAA,CAAM,SAAA,EAAW,IAAA,CAAK,cAAc,CAAA;AAAA,MACjE,MAAA,EAAQ,SAAA;AAAA,MACR,QAAA,EAAU,CAAA;AAAA,MACV,UAAA,EAAY,GAAA;AAAA,MACZ,UAAA,EAAY;AAAA,IAChB,CAAA;AACA,IAAA,IAAI;AACA,MAAA,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,UAAA,EAAY,GAAG,CAAA;AAC7C,MAAA,OAAO,EAAA;AAAA,IACX,EAAA,MAAA,CAAS,GAAA,EAAK;AAGV,MAAA,MAAM,OAAA,EAAS,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,UAAA,EAAY;AAAA,QACtD,KAAA,EAAO,EAAE,QAAA,EAAU,KAAA,CAAM,OAAA,EAAS,UAAA,EAAY,KAAA,CAAM,UAAU,CAAA;AAAA,QAC9D,MAAA,EAAQ,CAAC,IAAI;AAAA,MACjB,CAAC,CAAA;AACD,MAAA,GAAA,iBAAI,MAAA,6BAAQ,IAAA,EAAI,OAAO,MAAA,CAAO,EAAA;AAC9B,MAAA,MAAM,GAAA;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,MAAM,KAAA,CAAM,IAAA,EAAgD;AACxD,IAAA,MAAM,IAAA,mBAAM,IAAA,CAAK,GAAA,UAAO,IAAA,CAAK,GAAA,CAAI,GAAA;AAGjC,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MACd,IAAA,CAAK,UAAA;AAAA,MACL,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,IAAI,CAAA;AAAA,MACzE;AAAA,QACI,KAAA,EAAO;AAAA,UACH,MAAA,EAAQ,WAAA;AAAA,UACR,UAAA,EAAY,EAAE,GAAA,EAAK,IAAA,EAAM,IAAA,CAAK,WAAW;AAAA,QAC7C,CAAA;AAAA,QACA,KAAA,EAAO;AAAA,MACX;AAAA,IACJ,CAAA;AAGA,IAAA,MAAM,gBAAA,EAAkB,IAAA,CAAK,UAAA,EACvB,EAAE,aAAA,EAAe,IAAA,CAAK,SAAA,CAAU,MAAM,EAAA,EACtC,CAAC,CAAA;AACP,IAAA,MAAM,WAAA,EAAa,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,UAAA,EAAY;AAAA,MACvD,KAAA,EAAO;AAAA,QACH,MAAA,EAAQ,SAAA;AAAA,QACR,GAAG,eAAA;AAAA;AAAA,QAEH,GAAA,EAAK;AAAA,UACD,EAAE,aAAA,EAAe,KAAK,CAAA;AAAA,UACtB,EAAE,aAAA,EAAe,EAAE,IAAA,EAAM,IAAI,EAAE;AAAA,QACnC;AAAA,MACJ,CAAA;AAAA,MACA,MAAA,EAAQ,CAAC,IAAI,CAAA;AAAA;AAAA,MAEb,KAAA,EAAO,IAAA,CAAK;AAAA,IAChB,CAAC,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW,OAAA,IAAW,CAAA,EAAG,OAAO,CAAC,CAAA;AAErC,IAAA,MAAM,IAAA,EAAO,UAAA,CAAqC,GAAA,CAAI,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,EAAE,CAAA;AAIjE,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MACd,IAAA,CAAK,UAAA;AAAA,MACL;AAAA,QACI,MAAA,EAAQ,WAAA;AAAA,QACR,UAAA,EAAY,IAAA,CAAK,MAAA;AAAA,QACjB,UAAA,EAAY,GAAA;AAAA,QACZ,UAAA,EAAY;AAAA,MAChB,CAAA;AAAA,MACA;AAAA,QACI,KAAA,EAAO,EAAE,EAAA,EAAI,EAAE,GAAA,EAAK,IAAI,CAAA,EAAG,MAAA,EAAQ,UAAU,CAAA;AAAA,QAC7C,KAAA,EAAO;AAAA,MACX;AAAA,IACJ,CAAA;AAGA,IAAA,MAAM,QAAA,EAAW,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,UAAA,EAAY;AAAA,MACrD,KAAA,EAAO;AAAA,QACH,EAAA,EAAI,EAAE,GAAA,EAAK,IAAI,CAAA;AAAA,QACf,UAAA,EAAY,IAAA,CAAK,MAAA;AAAA,QACjB,UAAA,EAAY,GAAA;AAAA,QACZ,MAAA,EAAQ;AAAA,MACZ;AAAA,IACJ,CAAC,CAAA;AAED,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAAA,GAAM,IAAA,CAAK,UAAA,CAAW,CAAC,CAAC,CAAA;AAAA,EAChD;AAAA,EAEA,MAAM,GAAA,CAAI,EAAA,EAAY,MAAA,EAAkC;AAGpD,IAAA,MAAM,QAAA,EAAW,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,UAAA,EAAY;AAAA,MACxD,KAAA,EAAO,EAAE,GAAG,CAAA;AAAA,MACZ,MAAA,EAAQ,CAAC,UAAU;AAAA,IACvB,CAAC,CAAA;AACD,IAAA,GAAA,CAAI,CAAC,OAAA,EAAS,MAAA;AAEd,IAAA,MAAM,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA;AACrB,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,WAAA;AACJ,IAAA,IAAI,KAAA;AAEJ,IAAA,GAAA,CAAI,MAAA,CAAO,OAAA,EAAS;AAChB,MAAA,OAAA,EAAS,SAAA;AACT,MAAA,YAAA,EAAc,IAAA;AACd,MAAA,MAAA,EAAQ,IAAA;AAAA,IACZ,EAAA,KAAA,GAAA,CAAW,MAAA,CAAO,IAAA,EAAM;AACpB,MAAA,OAAA,EAAS,MAAA;AACT,MAAA,YAAA,EAAc,IAAA;AACd,MAAA,MAAA,mBAAQ,MAAA,CAAO,KAAA,UAAS,MAAA;AAAA,IAC5B,EAAA,KAAO;AACH,MAAA,OAAA,EAAS,SAAA;AACT,MAAA,YAAA,mBAAc,MAAA,CAAO,WAAA,UAAe,MAAA;AACpC,MAAA,MAAA,mBAAQ,MAAA,CAAO,KAAA,UAAS,MAAA;AAAA,IAC5B;AAEA,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MACd,IAAA,CAAK,UAAA;AAAA,MACL;AAAA,QACI,MAAA;AAAA,QACA,QAAA,EAAA,kBAAW,OAAA,CAAQ,QAAA,UAAY,GAAA,EAAA,EAAK,CAAA;AAAA,QACpC,iBAAA,EAAmB,GAAA;AAAA,QACnB,UAAA,EAAY,IAAA;AAAA,QACZ,UAAA,EAAY,IAAA;AAAA,QACZ,aAAA,mBAAe,MAAA,CAAO,UAAA,UAAc,MAAA;AAAA,QACpC,aAAA,mBAAe,MAAA,CAAO,YAAA,UAAgB,MAAA;AAAA,QACtC,aAAA,EAAe,WAAA;AAAA,QACf,KAAA;AAAA,QACA,UAAA,EAAY;AAAA,MAChB,CAAA;AAAA,MACA,EAAE,KAAA,EAAO,EAAE,GAAG,CAAA,EAAG,KAAA,EAAO,MAAM;AAAA,IAClC,CAAA;AAAA,EACJ;AAAA,EAEA,MAAM,IAAA,CAAK,MAAA,EAAkE;AACzE,IAAA,MAAM,KAAA,EAAQ,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,UAAA,EAAY;AAAA,MAClD,KAAA,kBAAO,MAAA,6BAAQ,SAAA,EAAS,EAAE,MAAA,EAAQ,MAAA,CAAO,OAAO,EAAA,EAAI,CAAC;AAAA,IACzD,CAAC,CAAA;AACD,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAA,GAAM,IAAA,CAAK,UAAA,CAAW,CAAC,CAAC,CAAA;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAA,CAAU,EAAA,EAAsC;AAClD,IAAA,MAAM,QAAA,EAAW,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,UAAA,EAAY;AAAA,MACxD,KAAA,EAAO,EAAE,GAAG;AAAA,IAChB,CAAC,CAAA;AACD,IAAA,GAAA,CAAI,CAAC,OAAA,EAAS;AACV,MAAA,MAAM,IAAI,cAAA;AAAA,QACN,CAAA,cAAA,EAAiB,EAAE,CAAA,WAAA,CAAA;AAAA,QACnB;AAAA,MACJ,CAAA;AAAA,IACJ;AACA,IAAA,GAAA,CACI,OAAA,CAAQ,OAAA,IAAW,UAAA,GACnB,OAAA,CAAQ,OAAA,IAAW,SAAA,GACnB,OAAA,CAAQ,OAAA,IAAW,MAAA,EACrB;AACE,MAAA,MAAM,IAAI,cAAA;AAAA,QACN,CAAA,cAAA,EAAiB,EAAE,CAAA,MAAA,EAAS,OAAA,CAAQ,MAAM,CAAA,yCAAA,CAAA;AAAA,QAC1C;AAAA,MACJ,CAAA;AAAA,IACJ;AACA,IAAA,MAAM,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA;AAKrB,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MACd,IAAA,CAAK,UAAA;AAAA,MACL;AAAA,QACI,MAAA,EAAQ,SAAA;AAAA,QACR,QAAA,EAAU,CAAA;AAAA,QACV,UAAA,EAAY,IAAA;AAAA,QACZ,UAAA,EAAY,IAAA;AAAA,QACZ,aAAA,EAAe,IAAA;AAAA,QACf,iBAAA,EAAmB,IAAA;AAAA,QACnB,aAAA,EAAe,IAAA;AAAA,QACf,aAAA,EAAe,IAAA;AAAA,QACf,KAAA,EAAO,IAAA;AAAA,QACP,UAAA,EAAY;AAAA,MAChB,CAAA;AAAA,MACA;AAAA,QACI,KAAA,EAAO;AAAA,UACH,EAAA;AAAA,UACA,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAC,SAAA,EAAW,QAAA,EAAU,MAAM,EAAE;AAAA,QACjD,CAAA;AAAA,QACA,KAAA,EAAO;AAAA,MACX;AAAA,IACJ,CAAA;AACA,IAAA,MAAM,MAAA,EAAS,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,UAAA,EAAY;AAAA,MACtD,KAAA,EAAO,EAAE,GAAG;AAAA,IAChB,CAAC,CAAA;AACD,IAAA,GAAA,CAAI,CAAC,MAAA,GAAS,KAAA,CAAM,OAAA,IAAW,SAAA,EAAW;AAEtC,MAAA,MAAM,IAAI,cAAA;AAAA,QACN,CAAA,cAAA,EAAiB,EAAE,CAAA,gCAAA,CAAA;AAAA,QACnB;AAAA,MACJ,CAAA;AAAA,IACJ;AACA,IAAA,OAAO,IAAA,CAAK,UAAA,CAAW,KAAK,CAAA;AAAA,EAChC;AAAA,EAEQ,UAAA,CAAW,CAAA,EAAiC;AAChD,IAAA,OAAO;AAAA,MACH,EAAA,EAAI,CAAA,CAAE,EAAA;AAAA,MACN,SAAA,EAAW,CAAA,CAAE,UAAA;AAAA,MACb,OAAA,EAAS,CAAA,CAAE,QAAA;AAAA,MACX,SAAA,EAAW,CAAA,CAAE,UAAA;AAAA,MACb,GAAA,EAAK,CAAA,CAAE,GAAA;AAAA,MACP,MAAA,mBAAQ,CAAA,CAAE,MAAA,UAAU,KAAA,GAAA;AAAA,MACpB,OAAA,EAAS,CAAA,CAAE,aAAA,EAAe,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,YAAY,EAAA,EAAI,KAAA,CAAA;AAAA,MACvD,MAAA,mBAAQ,CAAA,CAAE,MAAA,UAAU,KAAA,GAAA;AAAA,MACpB,SAAA,mBAAW,CAAA,CAAE,UAAA,UAAc,KAAA,GAAA;AAAA,MAC3B,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,YAAY,CAAA;AAAA,MAClC,MAAA,EAAQ,CAAA,CAAE,MAAA;AAAA,MACV,QAAA,EAAU,CAAA,CAAE,QAAA;AAAA,MACZ,SAAA,mBAAW,CAAA,CAAE,UAAA,UAAc,KAAA,GAAA;AAAA,MAC3B,SAAA,mBAAW,CAAA,CAAE,UAAA,UAAc,KAAA,GAAA;AAAA,MAC3B,WAAA,mBAAa,CAAA,CAAE,aAAA,UAAiB,KAAA,GAAA;AAAA,MAChC,eAAA,mBAAiB,CAAA,CAAE,iBAAA,UAAqB,KAAA,GAAA;AAAA,MACxC,YAAA,mBAAc,CAAA,CAAE,aAAA,UAAiB,KAAA,GAAA;AAAA,MACjC,YAAA,mBAAc,CAAA,CAAE,aAAA,UAAiB,KAAA,GAAA;AAAA,MACjC,KAAA,mBAAO,CAAA,CAAE,KAAA,UAAS,KAAA,GAAA;AAAA,MAClB,SAAA,EAAW,CAAA,CAAE,UAAA;AAAA,MACb,SAAA,EAAW,CAAA,CAAE;AAAA,IACjB,CAAA;AAAA,EACJ;AACJ,CAAA;AD7FA;AACA;AACE;AACA;AACA;AACF,4HAAC","file":"/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-FA66GQEO.cjs","sourcesContent":[null,"// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { randomUUID } from 'node:crypto';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type {\n AckResult,\n ClaimOptions,\n DeliveryStatus,\n EnqueueInput,\n IWebhookOutbox,\n WebhookDelivery,\n} from './outbox.js';\nimport { RedeliverError } from './outbox.js';\nimport { hashPartition } from './partition.js';\nimport { SYS_WEBHOOK_DELIVERY } from './schema.js';\n\nexport interface SqlWebhookOutboxOptions {\n /**\n * Total partition count — MUST match the dispatcher's `partitionCount`.\n * Used at enqueue time to precompute `partition_key`.\n */\n partitionCount: number;\n /**\n * Object name to read/write. Defaults to `sys_webhook_delivery`. Override\n * only if you've registered the schema under a different name.\n */\n objectName?: string;\n}\n\ninterface DeliveryRow {\n id: string;\n webhook_id: string;\n event_id: string;\n event_type: string;\n url: string;\n method?: string | null;\n headers_json?: string | null;\n secret?: string | null;\n timeout_ms?: number | null;\n payload_json: string;\n partition_key: number;\n status: DeliveryStatus;\n attempts: number;\n claimed_by?: string | null;\n claimed_at?: number | null;\n next_retry_at?: number | null;\n last_attempted_at?: number | null;\n response_code?: number | null;\n response_body?: string | null;\n error?: string | null;\n created_at: number;\n updated_at: number;\n}\n\n/**\n * Durable `IWebhookOutbox` backed by ObjectQL — the production storage\n * impl. Works against any registered driver (SQL, Turso, Mongo, in-memory)\n * because everything goes through the driver-agnostic `IDataEngine` API.\n *\n * **Why no `FOR UPDATE SKIP LOCKED`?** ObjectQL is driver-agnostic — that\n * SQL feature is Postgres-only. We get equivalent safety from two layers:\n *\n * 1. `cluster.lock` held per partition by the dispatcher (the primary\n * mutex). One node owns one partition at a time → no two claimers.\n * 2. Atomic `UPDATE WHERE status='pending'` (the backup). Even if two\n * claimers slip through (e.g. admin reschedule + dispatcher), only\n * the first UPDATE matches each row.\n *\n * **Why precompute `partition_key` on enqueue?** ObjectQL has no\n * cross-driver `hash()` function in WHERE clauses. Storing the partition\n * as a column makes the claim query a plain indexed lookup.\n *\n * **Dedup race**: SELECT-then-INSERT has a tiny window where two\n * concurrent producers both miss the SELECT and both INSERT. The unique\n * index `(event_id, webhook_id)` on the table catches it — the second\n * INSERT errors, the producer ignores it. Receivers MUST be idempotent\n * on the `X-Objectstack-Delivery` header anyway.\n */\nexport class SqlWebhookOutbox implements IWebhookOutbox {\n private readonly objectName: string;\n private readonly partitionCount: number;\n\n constructor(\n private readonly engine: IDataEngine,\n opts: SqlWebhookOutboxOptions,\n ) {\n if (opts.partitionCount <= 0) {\n throw new Error('SqlWebhookOutbox: partitionCount must be > 0');\n }\n this.objectName = opts.objectName ?? SYS_WEBHOOK_DELIVERY;\n this.partitionCount = opts.partitionCount;\n }\n\n async enqueue(input: EnqueueInput): Promise<string> {\n // Cheap pre-check to absorb most duplicates without hitting the\n // unique-index error path. Race window with the INSERT below is\n // intentional and documented.\n const existing = await this.engine.findOne(this.objectName, {\n where: { event_id: input.eventId, webhook_id: input.webhookId },\n fields: ['id'],\n });\n if (existing?.id) return existing.id as string;\n\n const id = randomUUID();\n const now = Date.now();\n const row: Omit<DeliveryRow, 'response_body' | 'error'> = {\n id,\n webhook_id: input.webhookId,\n event_id: input.eventId,\n event_type: input.eventType,\n url: input.url,\n method: input.method ?? 'POST',\n headers_json: input.headers ? JSON.stringify(input.headers) : undefined,\n secret: input.secret,\n timeout_ms: input.timeoutMs,\n payload_json: JSON.stringify(input.payload ?? null),\n partition_key: hashPartition(input.webhookId, this.partitionCount),\n status: 'pending',\n attempts: 0,\n created_at: now,\n updated_at: now,\n };\n try {\n await this.engine.insert(this.objectName, row);\n return id;\n } catch (err) {\n // Unique-index collision (dedup race) → look up the winner and\n // return its id. Any other error propagates.\n const winner = await this.engine.findOne(this.objectName, {\n where: { event_id: input.eventId, webhook_id: input.webhookId },\n fields: ['id'],\n });\n if (winner?.id) return winner.id as string;\n throw err;\n }\n }\n\n async claim(opts: ClaimOptions): Promise<WebhookDelivery[]> {\n const now = opts.now ?? Date.now();\n\n // 1. Reap stale in_flight rows — visibility-timeout recovery.\n await this.engine.update(\n this.objectName,\n { status: 'pending', claimed_by: null, claimed_at: null, updated_at: now },\n {\n where: {\n status: 'in_flight',\n claimed_at: { $lt: now - opts.claimTtlMs },\n },\n multi: true,\n },\n );\n\n // 2. Pick candidate ids.\n const partitionFilter = opts.partition\n ? { partition_key: opts.partition.index }\n : {};\n const candidates = await this.engine.find(this.objectName, {\n where: {\n status: 'pending',\n ...partitionFilter,\n // next_retry_at <= now OR null\n $or: [\n { next_retry_at: null },\n { next_retry_at: { $lte: now } },\n ],\n },\n fields: ['id'],\n // No orderBy for portability — drivers handle the natural insert order.\n limit: opts.limit,\n });\n if (candidates.length === 0) return [];\n\n const ids = (candidates as Array<{ id: string }>).map((c) => c.id);\n\n // 3. Atomic claim. WHERE status='pending' rejects any rows another\n // worker swept up between steps 2 and 3.\n await this.engine.update(\n this.objectName,\n {\n status: 'in_flight',\n claimed_by: opts.nodeId,\n claimed_at: now,\n updated_at: now,\n },\n {\n where: { id: { $in: ids }, status: 'pending' },\n multi: true,\n },\n );\n\n // 4. Read back the rows we actually own.\n const claimed = (await this.engine.find(this.objectName, {\n where: {\n id: { $in: ids },\n claimed_by: opts.nodeId,\n claimed_at: now,\n status: 'in_flight',\n },\n })) as DeliveryRow[];\n\n return claimed.map((r) => this.toDelivery(r));\n }\n\n async ack(id: string, result: AckResult): Promise<void> {\n // ObjectQL has no atomic $inc across drivers, so read-then-write.\n // Safe enough: ack is single-writer per row (only the claimer acks).\n const current = (await this.engine.findOne(this.objectName, {\n where: { id },\n fields: ['attempts'],\n })) as { attempts?: number } | null;\n if (!current) return;\n\n const now = Date.now();\n let status: DeliveryStatus;\n let nextRetryAt: number | null;\n let error: string | null;\n\n if (result.success) {\n status = 'success';\n nextRetryAt = null;\n error = null;\n } else if (result.dead) {\n status = 'dead';\n nextRetryAt = null;\n error = result.error ?? null;\n } else {\n status = 'pending';\n nextRetryAt = result.nextRetryAt ?? null;\n error = result.error ?? null;\n }\n\n await this.engine.update(\n this.objectName,\n {\n status,\n attempts: (current.attempts ?? 0) + 1,\n last_attempted_at: now,\n claimed_by: null,\n claimed_at: null,\n response_code: result.httpStatus ?? null,\n response_body: result.responseBody ?? null,\n next_retry_at: nextRetryAt,\n error,\n updated_at: now,\n },\n { where: { id }, multi: false },\n );\n }\n\n async list(filter?: { status?: DeliveryStatus }): Promise<WebhookDelivery[]> {\n const rows = (await this.engine.find(this.objectName, {\n where: filter?.status ? { status: filter.status } : {},\n })) as DeliveryRow[];\n return rows.map((r) => this.toDelivery(r));\n }\n\n async redeliver(id: string): Promise<WebhookDelivery> {\n const current = (await this.engine.findOne(this.objectName, {\n where: { id },\n })) as DeliveryRow | null;\n if (!current) {\n throw new RedeliverError(\n `Delivery row '${id}' not found`,\n 'not_found',\n );\n }\n if (\n current.status !== 'success' &&\n current.status !== 'failed' &&\n current.status !== 'dead'\n ) {\n throw new RedeliverError(\n `Delivery row '${id}' is '${current.status}', expected one of: success, failed, dead`,\n 'not_eligible',\n );\n }\n const now = Date.now();\n // Guarded UPDATE — re-check status server-side so two concurrent\n // redeliver calls cannot both flip the row, and so a dispatcher\n // tick that flipped the row to in_flight between our SELECT and\n // UPDATE cannot be clobbered.\n await this.engine.update(\n this.objectName,\n {\n status: 'pending',\n attempts: 0,\n claimed_by: null,\n claimed_at: null,\n next_retry_at: null,\n last_attempted_at: null,\n response_code: null,\n response_body: null,\n error: null,\n updated_at: now,\n },\n {\n where: {\n id,\n status: { $in: ['success', 'failed', 'dead'] },\n },\n multi: false,\n },\n );\n const after = (await this.engine.findOne(this.objectName, {\n where: { id },\n })) as DeliveryRow | null;\n if (!after || after.status !== 'pending') {\n // Lost the race — another writer flipped the row.\n throw new RedeliverError(\n `Delivery row '${id}' state changed during redeliver`,\n 'not_eligible',\n );\n }\n return this.toDelivery(after);\n }\n\n private toDelivery(r: DeliveryRow): WebhookDelivery {\n return {\n id: r.id,\n webhookId: r.webhook_id,\n eventId: r.event_id,\n eventType: r.event_type,\n url: r.url,\n method: r.method ?? undefined,\n headers: r.headers_json ? JSON.parse(r.headers_json) : undefined,\n secret: r.secret ?? undefined,\n timeoutMs: r.timeout_ms ?? undefined,\n payload: JSON.parse(r.payload_json),\n status: r.status,\n attempts: r.attempts,\n claimedBy: r.claimed_by ?? undefined,\n claimedAt: r.claimed_at ?? undefined,\n nextRetryAt: r.next_retry_at ?? undefined,\n lastAttemptedAt: r.last_attempted_at ?? undefined,\n responseCode: r.response_code ?? undefined,\n responseBody: r.response_body ?? undefined,\n error: r.error ?? undefined,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n };\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Webhook outbox contracts.\n *\n * The outbox stores webhook delivery rows that must be POSTed exactly once\n * (modulo at-least-once + receiver-side idempotency). Implementations are\n * pluggable so the same dispatcher can run against an in-memory test store\n * or a SQL-backed production table.\n *\n * See `content/docs/concepts/webhook-delivery.mdx` §3.2 for the full schema.\n */\n\nexport type DeliveryStatus =\n | 'pending'\n | 'in_flight'\n | 'success'\n | 'failed'\n | 'dead';\n\nexport interface WebhookDelivery {\n /** UUID — also doubles as the receiver-side idempotency key. */\n id: string;\n /** FK to sys_webhook.id — opaque to the dispatcher; only used for hashing. */\n webhookId: string;\n /** Origin event id. UNIQUE(event_id, webhook_id) prevents double-enqueue. */\n eventId: string;\n /** Origin event type, e.g. `data.record.created`. */\n eventType: string;\n /** Destination URL (snapshotted on enqueue — config edits don't rewrite live rows). */\n url: string;\n /** HTTP method — defaults to POST. */\n method?: string;\n /** Custom headers configured on the sink. */\n headers?: Record<string, string>;\n /** HMAC-SHA256 secret. If present, signature is added. */\n secret?: string;\n /** Per-request timeout in ms. */\n timeoutMs?: number;\n /** JSON-serialisable body. */\n payload: unknown;\n\n /** Lifecycle state. */\n status: DeliveryStatus;\n /** Number of POST attempts made so far (0 before first attempt). */\n attempts: number;\n /** Node id currently working on this row, when `status = in_flight`. */\n claimedBy?: string;\n /** Wall-clock ms when the row was claimed. */\n claimedAt?: number;\n /** Earliest ms at which this row becomes eligible for the next attempt. */\n nextRetryAt?: number;\n /** Wall-clock ms of the last attempt (success or fail). */\n lastAttemptedAt?: number;\n /** HTTP status code from the most recent attempt. */\n responseCode?: number;\n /** Truncated response body for diagnostics. */\n responseBody?: string;\n /** Last transport / timeout error message. */\n error?: string;\n\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface EnqueueInput {\n webhookId: string;\n eventId: string;\n eventType: string;\n url: string;\n method?: string;\n headers?: Record<string, string>;\n secret?: string;\n timeoutMs?: number;\n payload: unknown;\n}\n\nexport interface ClaimOptions {\n /** Identifier of the node doing the claim (for `claimedBy`). */\n nodeId: string;\n /** Max rows to claim per call. */\n limit: number;\n /**\n * Partition assignment for this worker. Only rows whose\n * `hash(webhookId) mod count === index` are claimed. Omit to claim\n * across all partitions (single-node mode).\n */\n partition?: { index: number; count: number };\n /** Visibility timeout — claimed rows revert to pending after this many ms. */\n claimTtlMs: number;\n /** \"Now\" reference, ms since epoch. Defaults to Date.now(). */\n now?: number;\n}\n\nexport interface AckSuccess {\n success: true;\n httpStatus: number;\n responseBody?: string;\n durationMs: number;\n}\n\nexport interface AckFailure {\n success: false;\n httpStatus?: number;\n responseBody?: string;\n error?: string;\n durationMs: number;\n /** Computed by the dispatcher per the retry schedule, or undefined for dead. */\n nextRetryAt?: number;\n /** Marks the row terminal — no more attempts. */\n dead?: boolean;\n}\n\nexport type AckResult = AckSuccess | AckFailure;\n\n/**\n * Error raised by `IWebhookOutbox.redeliver` when the requested row is\n * either missing or in a non-terminal state. The dispatcher / admin UI\n * surfaces this verbatim to the caller — never throw it for transient\n * conditions (transport errors should bubble as native `Error`).\n */\nexport class RedeliverError extends Error {\n constructor(\n message: string,\n readonly code: 'not_found' | 'not_eligible',\n ) {\n super(message);\n this.name = 'RedeliverError';\n }\n}\n\n/**\n * Pluggable storage backend for delivery rows. Implementations MUST make\n * `claim()` atomic across concurrent callers — that property is the\n * exactly-once guarantee.\n */\nexport interface IWebhookOutbox {\n /**\n * Insert a new delivery row. Implementations MUST treat\n * `(eventId, webhookId)` as unique and silently drop duplicates.\n * Returns the row id (existing or new).\n */\n enqueue(input: EnqueueInput): Promise<string>;\n\n /**\n * Atomically claim up to `limit` rows whose `nextRetryAt <= now` (or\n * null) and matching the partition predicate. Claimed rows MUST be\n * marked `in_flight` so concurrent claimers don't see them.\n */\n claim(opts: ClaimOptions): Promise<WebhookDelivery[]>;\n\n /** Record the outcome of an attempt. */\n ack(id: string, result: AckResult): Promise<void>;\n\n /** Snapshot accessor for tests / admin tooling. */\n list(filter?: { status?: DeliveryStatus }): Promise<WebhookDelivery[]>;\n\n /**\n * Reset a terminal row back to `pending` so the dispatcher will pick\n * it up again on its next tick.\n *\n * - Eligible source states: `success`, `failed`, `dead`.\n * - Rejects `pending` / `in_flight` rows — replaying those would\n * double-deliver because they're either already queued or actively\n * being sent.\n * - Resets `attempts=0` so the retry budget restarts.\n * - Clears `claimed_by`, `claimed_at`, `next_retry_at`, `error`,\n * `response_code`, `response_body`. URL / payload / secret are NOT\n * touched — replay reproduces the original POST byte-for-byte.\n *\n * Throws `RedeliverError` with code `not_found` or `not_eligible`.\n * Returns the post-reset row.\n */\n redeliver(id: string): Promise<WebhookDelivery>;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Stable, framework-free partition hash. The dispatcher uses this to\n * assign webhooks to partitions; the in-memory outbox uses the same hash\n * to filter rows in `claim()`. Both call sites MUST agree, which is why\n * this is a single shared helper.\n *\n * Uses a 32-bit FNV-1a variant — fast, no allocations, deterministic.\n */\nexport function hashPartition(key: string, count: number): number {\n if (count <= 0) throw new Error('partition count must be > 0');\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return Math.abs(h | 0) % count;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-HF7CCDPB.cjs","../src/sql-outbox.ts","../src/outbox.ts","../src/partition.ts"],"names":[],"mappings":"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","file":"/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-HF7CCDPB.cjs","sourcesContent":[null,"// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { randomUUID } from 'node:crypto';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type {\n AckResult,\n ClaimOptions,\n DeliveryStatus,\n EnqueueInput,\n IWebhookOutbox,\n WebhookDelivery,\n} from './outbox.js';\nimport { RedeliverError } from './outbox.js';\nimport { hashPartition } from './partition.js';\nimport { SYS_WEBHOOK_DELIVERY } from './schema.js';\n\nexport interface SqlWebhookOutboxOptions {\n /**\n * Total partition count — MUST match the dispatcher's `partitionCount`.\n * Used at enqueue time to precompute `partition_key`.\n */\n partitionCount: number;\n /**\n * Object name to read/write. Defaults to `sys_webhook_delivery`. Override\n * only if you've registered the schema under a different name.\n */\n objectName?: string;\n}\n\ninterface DeliveryRow {\n id: string;\n webhook_id: string;\n event_id: string;\n event_type: string;\n url: string;\n method?: string | null;\n headers_json?: string | null;\n secret?: string | null;\n timeout_ms?: number | null;\n payload_json: string;\n partition_key: number;\n status: DeliveryStatus;\n attempts: number;\n claimed_by?: string | null;\n claimed_at?: number | null;\n next_retry_at?: number | null;\n last_attempted_at?: number | null;\n response_code?: number | null;\n response_body?: string | null;\n error?: string | null;\n created_at: number;\n updated_at: number;\n}\n\n/**\n * Durable `IWebhookOutbox` backed by ObjectQL — the production storage\n * impl. Works against any registered driver (SQL, Turso, Mongo, in-memory)\n * because everything goes through the driver-agnostic `IDataEngine` API.\n *\n * **Why no `FOR UPDATE SKIP LOCKED`?** ObjectQL is driver-agnostic — that\n * SQL feature is Postgres-only. We get equivalent safety from two layers:\n *\n * 1. `cluster.lock` held per partition by the dispatcher (the primary\n * mutex). One node owns one partition at a time → no two claimers.\n * 2. Atomic `UPDATE WHERE status='pending'` (the backup). Even if two\n * claimers slip through (e.g. admin reschedule + dispatcher), only\n * the first UPDATE matches each row.\n *\n * **Why precompute `partition_key` on enqueue?** ObjectQL has no\n * cross-driver `hash()` function in WHERE clauses. Storing the partition\n * as a column makes the claim query a plain indexed lookup.\n *\n * **Dedup race**: SELECT-then-INSERT has a tiny window where two\n * concurrent producers both miss the SELECT and both INSERT. The unique\n * index `(event_id, webhook_id)` on the table catches it — the second\n * INSERT errors, the producer ignores it. Receivers MUST be idempotent\n * on the `X-Objectstack-Delivery` header anyway.\n */\nexport class SqlWebhookOutbox implements IWebhookOutbox {\n private readonly objectName: string;\n private readonly partitionCount: number;\n\n constructor(\n private readonly engine: IDataEngine,\n opts: SqlWebhookOutboxOptions,\n ) {\n if (opts.partitionCount <= 0) {\n throw new Error('SqlWebhookOutbox: partitionCount must be > 0');\n }\n this.objectName = opts.objectName ?? SYS_WEBHOOK_DELIVERY;\n this.partitionCount = opts.partitionCount;\n }\n\n async enqueue(input: EnqueueInput): Promise<string> {\n // Cheap pre-check to absorb most duplicates without hitting the\n // unique-index error path. Race window with the INSERT below is\n // intentional and documented.\n const existing = await this.engine.findOne(this.objectName, {\n where: { event_id: input.eventId, webhook_id: input.webhookId },\n fields: ['id'],\n });\n if (existing?.id) return existing.id as string;\n\n const id = randomUUID();\n const now = Date.now();\n const row: Omit<DeliveryRow, 'response_body' | 'error'> = {\n id,\n webhook_id: input.webhookId,\n event_id: input.eventId,\n event_type: input.eventType,\n url: input.url,\n method: input.method ?? 'POST',\n headers_json: input.headers ? JSON.stringify(input.headers) : undefined,\n secret: input.secret,\n timeout_ms: input.timeoutMs,\n payload_json: JSON.stringify(input.payload ?? null),\n partition_key: hashPartition(input.webhookId, this.partitionCount),\n status: 'pending',\n attempts: 0,\n created_at: now,\n updated_at: now,\n };\n try {\n await this.engine.insert(this.objectName, row);\n return id;\n } catch (err) {\n // Unique-index collision (dedup race) → look up the winner and\n // return its id. Any other error propagates.\n const winner = await this.engine.findOne(this.objectName, {\n where: { event_id: input.eventId, webhook_id: input.webhookId },\n fields: ['id'],\n });\n if (winner?.id) return winner.id as string;\n throw err;\n }\n }\n\n async claim(opts: ClaimOptions): Promise<WebhookDelivery[]> {\n const now = opts.now ?? Date.now();\n\n // 1. Reap stale in_flight rows — visibility-timeout recovery.\n await this.engine.update(\n this.objectName,\n { status: 'pending', claimed_by: null, claimed_at: null, updated_at: now },\n {\n where: {\n status: 'in_flight',\n claimed_at: { $lt: now - opts.claimTtlMs },\n },\n multi: true,\n },\n );\n\n // 2. Pick candidate ids.\n const partitionFilter = opts.partition\n ? { partition_key: opts.partition.index }\n : {};\n const candidates = await this.engine.find(this.objectName, {\n where: {\n status: 'pending',\n ...partitionFilter,\n // next_retry_at <= now OR null\n $or: [\n { next_retry_at: null },\n { next_retry_at: { $lte: now } },\n ],\n },\n fields: ['id'],\n // No orderBy for portability — drivers handle the natural insert order.\n limit: opts.limit,\n });\n if (candidates.length === 0) return [];\n\n const ids = (candidates as Array<{ id: string }>).map((c) => c.id);\n\n // 3. Atomic claim. WHERE status='pending' rejects any rows another\n // worker swept up between steps 2 and 3.\n await this.engine.update(\n this.objectName,\n {\n status: 'in_flight',\n claimed_by: opts.nodeId,\n claimed_at: now,\n updated_at: now,\n },\n {\n where: { id: { $in: ids }, status: 'pending' },\n multi: true,\n },\n );\n\n // 4. Read back the rows we actually own.\n const claimed = (await this.engine.find(this.objectName, {\n where: {\n id: { $in: ids },\n claimed_by: opts.nodeId,\n claimed_at: now,\n status: 'in_flight',\n },\n })) as DeliveryRow[];\n\n return claimed.map((r) => this.toDelivery(r));\n }\n\n async ack(id: string, result: AckResult): Promise<void> {\n // ObjectQL has no atomic $inc across drivers, so read-then-write.\n // Safe enough: ack is single-writer per row (only the claimer acks).\n const current = (await this.engine.findOne(this.objectName, {\n where: { id },\n fields: ['attempts'],\n })) as { attempts?: number } | null;\n if (!current) return;\n\n const now = Date.now();\n let status: DeliveryStatus;\n let nextRetryAt: number | null;\n let error: string | null;\n\n if (result.success) {\n status = 'success';\n nextRetryAt = null;\n error = null;\n } else if (result.dead) {\n status = 'dead';\n nextRetryAt = null;\n error = result.error ?? null;\n } else {\n status = 'pending';\n nextRetryAt = result.nextRetryAt ?? null;\n error = result.error ?? null;\n }\n\n await this.engine.update(\n this.objectName,\n {\n status,\n attempts: (current.attempts ?? 0) + 1,\n last_attempted_at: now,\n claimed_by: null,\n claimed_at: null,\n response_code: result.httpStatus ?? null,\n response_body: result.responseBody ?? null,\n next_retry_at: nextRetryAt,\n error,\n updated_at: now,\n },\n { where: { id }, multi: false },\n );\n }\n\n async list(filter?: { status?: DeliveryStatus }): Promise<WebhookDelivery[]> {\n const rows = (await this.engine.find(this.objectName, {\n where: filter?.status ? { status: filter.status } : {},\n })) as DeliveryRow[];\n return rows.map((r) => this.toDelivery(r));\n }\n\n async redeliver(id: string): Promise<WebhookDelivery> {\n const current = (await this.engine.findOne(this.objectName, {\n where: { id },\n })) as DeliveryRow | null;\n if (!current) {\n throw new RedeliverError(\n `Delivery row '${id}' not found`,\n 'not_found',\n );\n }\n if (\n current.status !== 'success' &&\n current.status !== 'failed' &&\n current.status !== 'dead'\n ) {\n throw new RedeliverError(\n `Delivery row '${id}' is '${current.status}', expected one of: success, failed, dead`,\n 'not_eligible',\n );\n }\n const now = Date.now();\n // Guarded UPDATE — re-check status server-side so two concurrent\n // redeliver calls cannot both flip the row, and so a dispatcher\n // tick that flipped the row to in_flight between our SELECT and\n // UPDATE cannot be clobbered.\n await this.engine.update(\n this.objectName,\n {\n status: 'pending',\n attempts: 0,\n claimed_by: null,\n claimed_at: null,\n next_retry_at: null,\n last_attempted_at: null,\n response_code: null,\n response_body: null,\n error: null,\n updated_at: now,\n },\n {\n where: {\n id,\n status: { $in: ['success', 'failed', 'dead'] },\n },\n multi: false,\n },\n );\n const after = (await this.engine.findOne(this.objectName, {\n where: { id },\n })) as DeliveryRow | null;\n if (!after || after.status !== 'pending') {\n // Lost the race — another writer flipped the row.\n throw new RedeliverError(\n `Delivery row '${id}' state changed during redeliver`,\n 'not_eligible',\n );\n }\n return this.toDelivery(after);\n }\n\n private toDelivery(r: DeliveryRow): WebhookDelivery {\n return {\n id: r.id,\n webhookId: r.webhook_id,\n eventId: r.event_id,\n eventType: r.event_type,\n url: r.url,\n method: r.method ?? undefined,\n headers: r.headers_json ? JSON.parse(r.headers_json) : undefined,\n secret: r.secret ?? undefined,\n timeoutMs: r.timeout_ms ?? undefined,\n payload: JSON.parse(r.payload_json),\n status: r.status,\n attempts: r.attempts,\n claimedBy: r.claimed_by ?? undefined,\n claimedAt: r.claimed_at ?? undefined,\n nextRetryAt: r.next_retry_at ?? undefined,\n lastAttemptedAt: r.last_attempted_at ?? undefined,\n responseCode: r.response_code ?? undefined,\n responseBody: r.response_body ?? undefined,\n error: r.error ?? undefined,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n };\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Webhook outbox contracts.\n *\n * The outbox stores webhook delivery rows that must be POSTed exactly once\n * (modulo at-least-once + receiver-side idempotency). Implementations are\n * pluggable so the same dispatcher can run against an in-memory test store\n * or a SQL-backed production table.\n *\n * See `content/docs/concepts/webhook-delivery.mdx` §3.2 for the full schema.\n */\n\nexport type DeliveryStatus =\n | 'pending'\n | 'in_flight'\n | 'success'\n | 'failed'\n | 'dead';\n\nexport interface WebhookDelivery {\n /** UUID — also doubles as the receiver-side idempotency key. */\n id: string;\n /** FK to sys_webhook.id — opaque to the dispatcher; only used for hashing. */\n webhookId: string;\n /** Origin event id. UNIQUE(event_id, webhook_id) prevents double-enqueue. */\n eventId: string;\n /** Origin event type, e.g. `data.record.created`. */\n eventType: string;\n /** Destination URL (snapshotted on enqueue — config edits don't rewrite live rows). */\n url: string;\n /** HTTP method — defaults to POST. */\n method?: string;\n /** Custom headers configured on the sink. */\n headers?: Record<string, string>;\n /** HMAC-SHA256 secret. If present, signature is added. */\n secret?: string;\n /** Per-request timeout in ms. */\n timeoutMs?: number;\n /** JSON-serialisable body. */\n payload: unknown;\n\n /** Lifecycle state. */\n status: DeliveryStatus;\n /** Number of POST attempts made so far (0 before first attempt). */\n attempts: number;\n /** Node id currently working on this row, when `status = in_flight`. */\n claimedBy?: string;\n /** Wall-clock ms when the row was claimed. */\n claimedAt?: number;\n /** Earliest ms at which this row becomes eligible for the next attempt. */\n nextRetryAt?: number;\n /** Wall-clock ms of the last attempt (success or fail). */\n lastAttemptedAt?: number;\n /** HTTP status code from the most recent attempt. */\n responseCode?: number;\n /** Truncated response body for diagnostics. */\n responseBody?: string;\n /** Last transport / timeout error message. */\n error?: string;\n\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface EnqueueInput {\n webhookId: string;\n eventId: string;\n eventType: string;\n url: string;\n method?: string;\n headers?: Record<string, string>;\n secret?: string;\n timeoutMs?: number;\n payload: unknown;\n}\n\nexport interface ClaimOptions {\n /** Identifier of the node doing the claim (for `claimedBy`). */\n nodeId: string;\n /** Max rows to claim per call. */\n limit: number;\n /**\n * Partition assignment for this worker. Only rows whose\n * `hash(webhookId) mod count === index` are claimed. Omit to claim\n * across all partitions (single-node mode).\n */\n partition?: { index: number; count: number };\n /** Visibility timeout — claimed rows revert to pending after this many ms. */\n claimTtlMs: number;\n /** \"Now\" reference, ms since epoch. Defaults to Date.now(). */\n now?: number;\n}\n\nexport interface AckSuccess {\n success: true;\n httpStatus: number;\n responseBody?: string;\n durationMs: number;\n}\n\nexport interface AckFailure {\n success: false;\n httpStatus?: number;\n responseBody?: string;\n error?: string;\n durationMs: number;\n /** Computed by the dispatcher per the retry schedule, or undefined for dead. */\n nextRetryAt?: number;\n /** Marks the row terminal — no more attempts. */\n dead?: boolean;\n}\n\nexport type AckResult = AckSuccess | AckFailure;\n\n/**\n * Error raised by `IWebhookOutbox.redeliver` when the requested row is\n * either missing or in a non-terminal state. The dispatcher / admin UI\n * surfaces this verbatim to the caller — never throw it for transient\n * conditions (transport errors should bubble as native `Error`).\n */\nexport class RedeliverError extends Error {\n constructor(\n message: string,\n readonly code: 'not_found' | 'not_eligible',\n ) {\n super(message);\n this.name = 'RedeliverError';\n }\n}\n\n/**\n * Pluggable storage backend for delivery rows. Implementations MUST make\n * `claim()` atomic across concurrent callers — that property is the\n * exactly-once guarantee.\n */\nexport interface IWebhookOutbox {\n /**\n * Insert a new delivery row. Implementations MUST treat\n * `(eventId, webhookId)` as unique and silently drop duplicates.\n * Returns the row id (existing or new).\n */\n enqueue(input: EnqueueInput): Promise<string>;\n\n /**\n * Atomically claim up to `limit` rows whose `nextRetryAt <= now` (or\n * null) and matching the partition predicate. Claimed rows MUST be\n * marked `in_flight` so concurrent claimers don't see them.\n */\n claim(opts: ClaimOptions): Promise<WebhookDelivery[]>;\n\n /** Record the outcome of an attempt. */\n ack(id: string, result: AckResult): Promise<void>;\n\n /** Snapshot accessor for tests / admin tooling. */\n list(filter?: { status?: DeliveryStatus }): Promise<WebhookDelivery[]>;\n\n /**\n * Reset a terminal row back to `pending` so the dispatcher will pick\n * it up again on its next tick.\n *\n * - Eligible source states: `success`, `failed`, `dead`.\n * - Rejects `pending` / `in_flight` rows — replaying those would\n * double-deliver because they're either already queued or actively\n * being sent.\n * - Resets `attempts=0` so the retry budget restarts.\n * - Clears `claimed_by`, `claimed_at`, `next_retry_at`, `error`,\n * `response_code`, `response_body`. URL / payload / secret are NOT\n * touched — replay reproduces the original POST byte-for-byte.\n *\n * Throws `RedeliverError` with code `not_found` or `not_eligible`.\n * Returns the post-reset row.\n */\n redeliver(id: string): Promise<WebhookDelivery>;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Stable, framework-free partition hash. The dispatcher uses this to\n * assign webhooks to partitions; the in-memory outbox uses the same hash\n * to filter rows in `claim()`. Both call sites MUST agree, which is why\n * this is a single shared helper.\n *\n * Uses a 32-bit FNV-1a variant — fast, no allocations, deterministic.\n */\nexport function hashPartition(key: string, count: number): number {\n if (count <= 0) throw new Error('partition count must be > 0');\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return Math.abs(h | 0) % count;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SYS_WEBHOOK_DELIVERY
3
- } from "./chunk-33LYZT7O.js";
3
+ } from "./chunk-7HS5DLU2.js";
4
4
 
5
5
  // src/sql-outbox.ts
6
6
  import { randomUUID } from "crypto";
@@ -253,4 +253,4 @@ export {
253
253
  hashPartition,
254
254
  SqlWebhookOutbox
255
255
  };
256
- //# sourceMappingURL=chunk-BS2QTZH3.js.map
256
+ //# sourceMappingURL=chunk-KNGLLSSP.js.map
@@ -1,5 +1,139 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/sys-webhook-delivery.object.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/sys-webhook.object.ts
2
2
  var _data = require('@objectstack/spec/data');
3
+ var SysWebhook = _data.ObjectSchema.create({
4
+ name: "sys_webhook",
5
+ label: "Webhook",
6
+ pluralLabel: "Webhooks",
7
+ icon: "webhook",
8
+ isSystem: true,
9
+ managedBy: "config",
10
+ // Authoring a webhook from the UI requires a structured form for the
11
+ // headers / auth / retry / payload blocks — the generic JSON textarea
12
+ // is acceptable as a v1 until a dedicated builder lands. Re-enable
13
+ // create/edit/delete so admins can at least toggle `active` and edit
14
+ // simple URL/method fields without round-tripping through code.
15
+ userActions: { create: true, edit: true, delete: true, import: false },
16
+ description: "Outbound HTTP webhook subscription. Authored via defineWebhook() in code or the Studio editor; executed by the HTTP connector plugin.",
17
+ displayNameField: "name",
18
+ titleFormat: "{label}",
19
+ compactLayout: ["name", "object_name", "url", "active", "updated_at"],
20
+ listViews: {
21
+ active: {
22
+ type: "grid",
23
+ name: "active",
24
+ label: "Active",
25
+ data: { provider: "object", object: "sys_webhook" },
26
+ columns: ["label", "object_name", "url", "method", "active", "updated_at"],
27
+ filter: [{ field: "active", operator: "equals", value: true }],
28
+ sort: [{ field: "label", order: "asc" }],
29
+ pagination: { pageSize: 50 }
30
+ },
31
+ inactive: {
32
+ type: "grid",
33
+ name: "inactive",
34
+ label: "Inactive",
35
+ data: { provider: "object", object: "sys_webhook" },
36
+ columns: ["label", "object_name", "url", "method", "active", "updated_at"],
37
+ filter: [{ field: "active", operator: "equals", value: false }],
38
+ sort: [{ field: "label", order: "asc" }],
39
+ pagination: { pageSize: 50 }
40
+ },
41
+ by_object: {
42
+ type: "grid",
43
+ name: "by_object",
44
+ label: "By Object",
45
+ data: { provider: "object", object: "sys_webhook" },
46
+ columns: ["object_name", "label", "url", "active", "updated_at"],
47
+ sort: [{ field: "object_name", order: "asc" }, { field: "label", order: "asc" }],
48
+ grouping: { fields: [{ field: "object_name", order: "asc", collapsed: false }] },
49
+ pagination: { pageSize: 100 }
50
+ },
51
+ all_webhooks: {
52
+ type: "grid",
53
+ name: "all_webhooks",
54
+ label: "All",
55
+ data: { provider: "object", object: "sys_webhook" },
56
+ columns: ["label", "object_name", "url", "method", "active", "updated_at"],
57
+ sort: [{ field: "label", order: "asc" }],
58
+ pagination: { pageSize: 50 }
59
+ }
60
+ },
61
+ fields: {
62
+ id: _data.Field.text({ label: "Webhook ID", required: true, readonly: true, group: "System" }),
63
+ name: _data.Field.text({
64
+ label: "Name",
65
+ required: true,
66
+ maxLength: 100,
67
+ description: "Unique snake_case name \u2014 referenced in logs and audit",
68
+ group: "Definition"
69
+ }),
70
+ label: _data.Field.text({
71
+ label: "Display Label",
72
+ required: false,
73
+ maxLength: 200,
74
+ group: "Definition"
75
+ }),
76
+ object_name: _data.Field.text({
77
+ label: "Object",
78
+ required: false,
79
+ maxLength: 100,
80
+ description: "Short object name whose events fire this webhook (blank = manual / API-triggered)",
81
+ group: "Definition"
82
+ }),
83
+ triggers: _data.Field.text({
84
+ label: "Triggers",
85
+ required: false,
86
+ maxLength: 200,
87
+ description: "Comma-separated event list: create,update,delete,undelete,api",
88
+ group: "Definition"
89
+ }),
90
+ url: _data.Field.text({
91
+ label: "Target URL",
92
+ required: true,
93
+ maxLength: 2048,
94
+ description: "External endpoint that receives the POST",
95
+ group: "Definition"
96
+ }),
97
+ method: _data.Field.text({
98
+ label: "HTTP Method",
99
+ required: true,
100
+ defaultValue: "POST",
101
+ maxLength: 10,
102
+ description: "GET / POST / PUT / PATCH / DELETE",
103
+ group: "Definition"
104
+ }),
105
+ description: _data.Field.textarea({ label: "Description", required: false, group: "Definition" }),
106
+ active: _data.Field.boolean({
107
+ label: "Active",
108
+ required: true,
109
+ defaultValue: true,
110
+ description: "Inactive webhooks are skipped by the dispatcher",
111
+ group: "Definition"
112
+ }),
113
+ definition_json: _data.Field.textarea({
114
+ label: "Definition",
115
+ required: true,
116
+ description: "Serialised Webhook JSON (see @objectstack/spec/automation/webhook) \u2014 full headers/auth/retry/payload config",
117
+ group: "Definition"
118
+ }),
119
+ created_at: _data.Field.datetime({
120
+ label: "Created At",
121
+ required: true,
122
+ defaultValue: "NOW()",
123
+ readonly: true,
124
+ group: "System"
125
+ }),
126
+ updated_at: _data.Field.datetime({ label: "Updated At", required: false, group: "System" })
127
+ },
128
+ indexes: [
129
+ { fields: ["name"], unique: true },
130
+ { fields: ["object_name"] },
131
+ { fields: ["active", "object_name"] }
132
+ ]
133
+ });
134
+
135
+ // src/sys-webhook-delivery.object.ts
136
+
3
137
  var SysWebhookDelivery = _data.ObjectSchema.create({
4
138
  name: "sys_webhook_delivery",
5
139
  label: "Webhook Delivery",
@@ -180,5 +314,6 @@ var SYS_WEBHOOK_DELIVERY = "sys_webhook_delivery";
180
314
 
181
315
 
182
316
 
183
- exports.SysWebhookDelivery = SysWebhookDelivery; exports.SYS_WEBHOOK_DELIVERY = SYS_WEBHOOK_DELIVERY;
184
- //# sourceMappingURL=chunk-MJZGD37S.cjs.map
317
+
318
+ exports.SysWebhook = SysWebhook; exports.SysWebhookDelivery = SysWebhookDelivery; exports.SYS_WEBHOOK_DELIVERY = SYS_WEBHOOK_DELIVERY;
319
+ //# sourceMappingURL=chunk-TDSI7UHY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-TDSI7UHY.cjs","../src/sys-webhook.object.ts","../src/sys-webhook-delivery.object.ts"],"names":["ObjectSchema","Field"],"mappings":"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","file":"/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-TDSI7UHY.cjs","sourcesContent":[null,"// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_webhook — Outbound HTTP integration configuration (runtime).\n *\n * Persists a single {@link Webhook} envelope per row so administrators\n * can author, enable/disable, and edit webhook subscriptions from the\n * Studio UI without code changes. The canonical Zod schema for the\n * `definition_json` envelope lives at `@objectstack/spec/automation/webhook`.\n *\n * One row per `name`. The automation runtime\n * (`@objectstack/service-automation`, built-in `http_request` node) loads\n * active rows on boot + on `sys_webhook:changed` events, registers\n * `afterInsert` / `afterUpdate` / `afterDelete` listeners for the\n * targeted object, and dispatches outbound HTTP calls when matching\n * record events fire.\n *\n * Ownership (ADR-0029 K2.a): this object is **owned by\n * `@objectstack/plugin-webhooks`** — the plugin that consumes these rows —\n * alongside its sibling `sys_webhook_delivery`. It used to live in the\n * `@objectstack/platform-objects` monolith and be imported here; the\n * definition now lives with its owner so the plugin ships both data and\n * behavior as one unit.\n *\n * Platform-wide on purpose: every project (standalone, single-tenant,\n * cloud) can integrate with external systems (Slack, Stripe, internal\n * services) the same way.\n *\n * @namespace sys\n */\nexport const SysWebhook = ObjectSchema.create({\n name: 'sys_webhook',\n label: 'Webhook',\n pluralLabel: 'Webhooks',\n icon: 'webhook',\n isSystem: true,\n managedBy: 'config',\n // Authoring a webhook from the UI requires a structured form for the\n // headers / auth / retry / payload blocks — the generic JSON textarea\n // is acceptable as a v1 until a dedicated builder lands. Re-enable\n // create/edit/delete so admins can at least toggle `active` and edit\n // simple URL/method fields without round-tripping through code.\n userActions: { create: true, edit: true, delete: true, import: false },\n description: 'Outbound HTTP webhook subscription. Authored via defineWebhook() in code or the Studio editor; executed by the HTTP connector plugin.',\n displayNameField: 'name',\n titleFormat: '{label}',\n compactLayout: ['name', 'object_name', 'url', 'active', 'updated_at'],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n inactive: {\n type: 'grid',\n name: 'inactive',\n label: 'Inactive',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: false }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_object: {\n type: 'grid',\n name: 'by_object',\n label: 'By Object',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['object_name', 'label', 'url', 'active', 'updated_at'],\n sort: [{ field: 'object_name', order: 'asc' }, { field: 'label', order: 'asc' }],\n grouping: { fields: [{ field: 'object_name', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n all_webhooks: {\n type: 'grid',\n name: 'all_webhooks',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Webhook ID', required: true, readonly: true, group: 'System' }),\n\n name: Field.text({\n label: 'Name',\n required: true,\n maxLength: 100,\n description: 'Unique snake_case name — referenced in logs and audit',\n group: 'Definition',\n }),\n\n label: Field.text({\n label: 'Display Label',\n required: false,\n maxLength: 200,\n group: 'Definition',\n }),\n\n object_name: Field.text({\n label: 'Object',\n required: false,\n maxLength: 100,\n description: 'Short object name whose events fire this webhook (blank = manual / API-triggered)',\n group: 'Definition',\n }),\n\n triggers: Field.text({\n label: 'Triggers',\n required: false,\n maxLength: 200,\n description: 'Comma-separated event list: create,update,delete,undelete,api',\n group: 'Definition',\n }),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'External endpoint that receives the POST',\n group: 'Definition',\n }),\n\n method: Field.text({\n label: 'HTTP Method',\n required: true,\n defaultValue: 'POST',\n maxLength: 10,\n description: 'GET / POST / PUT / PATCH / DELETE',\n group: 'Definition',\n }),\n\n description: Field.textarea({ label: 'Description', required: false, group: 'Definition' }),\n\n active: Field.boolean({\n label: 'Active',\n required: true,\n defaultValue: true,\n description: 'Inactive webhooks are skipped by the dispatcher',\n group: 'Definition',\n }),\n\n definition_json: Field.textarea({\n label: 'Definition',\n required: true,\n description: 'Serialised Webhook JSON (see @objectstack/spec/automation/webhook) — full headers/auth/retry/payload config',\n group: 'Definition',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n { fields: ['name'], unique: true },\n { fields: ['object_name'] },\n { fields: ['active', 'object_name'] },\n ],\n});\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Field, ObjectSchema } from '@objectstack/spec/data';\n\n/**\n * sys_webhook_delivery — Durable outbox row for one HTTP attempt.\n *\n * Schema is owned by `@objectstack/plugin-webhooks`. Add it to your stack\n * via:\n *\n * import { SysWebhookDelivery } from '@objectstack/plugin-webhooks/schema';\n * defineStack({ objects: [SysWebhookDelivery, ...], plugins: [...] });\n *\n * Designed for the SqlWebhookOutbox claim algorithm:\n *\n * 1. Producers INSERT pending rows (dedup'd by (event_id, webhook_id)).\n * 2. The dispatcher's per-partition lock-holder runs:\n * SELECT id WHERE status='pending' AND partition_key=? AND (next_retry_at <= now OR null)\n * UPDATE SET status='in_flight' WHERE id IN (...) AND status='pending' ← atomic claim\n * POST to target URL\n * UPDATE SET status=success/pending/dead, attempts=attempts+1, ...\n *\n * `partition_key` is precomputed on enqueue (hash(webhook_id) mod N) so the\n * dispatcher can filter cheaply without DB-side hash functions.\n *\n * Indexes are tuned for the hot path: `(status, partition_key, next_retry_at)`\n * is the claim query; `(event_id, webhook_id)` is the dedup uniqueness.\n *\n * @namespace sys\n */\nexport const SysWebhookDelivery = ObjectSchema.create({\n name: 'sys_webhook_delivery',\n label: 'Webhook Delivery',\n pluralLabel: 'Webhook Deliveries',\n icon: 'package',\n isSystem: true,\n managedBy: 'config',\n userActions: { create: false, edit: false, delete: false, import: false },\n description:\n 'Durable outbox row for one webhook attempt. Managed by @objectstack/plugin-webhooks; do not write directly.',\n displayNameField: 'id',\n titleFormat: '{event_type} → {url}',\n compactLayout: ['event_type', 'url', 'status', 'attempts', 'next_retry_at'],\n\n actions: [\n {\n name: 'redeliver',\n label: 'Redeliver',\n icon: 'refresh-cw',\n variant: 'secondary',\n locations: ['list_item', 'record_header'],\n type: 'api',\n target: '/api/v1/webhooks/redeliver',\n method: 'POST',\n recordIdParam: 'deliveryId',\n confirmText:\n 'Replay this delivery? The receiver will get the original payload again — they must be idempotent on the X-Objectstack-Delivery header.',\n successMessage: 'Queued for redelivery',\n refreshAfter: true,\n // Only terminal rows are safe to replay. Pending / in_flight rows\n // are either already queued or actively being sent — replaying\n // would double-deliver.\n disabled: \"!(status in ['success', 'failed', 'dead'])\",\n },\n ],\n\n listViews: {\n recent: {\n type: 'grid',\n name: 'recent',\n label: 'Recent',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n failures: {\n type: 'grid',\n name: 'failures',\n label: 'Failures',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'error', 'updated_at'],\n filter: [{ field: 'status', operator: 'in', value: ['failed', 'dead'] }],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n in_flight: {\n type: 'grid',\n name: 'in_flight',\n label: 'In Flight',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'claimed_by', 'claimed_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'in_flight' }],\n sort: [{ field: 'claimed_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n pending: {\n type: 'grid',\n name: 'pending',\n label: 'Pending',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'next_retry_at', 'updated_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'pending' }],\n sort: [{ field: 'next_retry_at', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_status: {\n type: 'grid',\n name: 'by_status',\n label: 'By Status',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['status', 'event_type', 'url', 'attempts', 'updated_at'],\n sort: [{ field: 'status', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'status', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n by_webhook: {\n type: 'grid',\n name: 'by_webhook',\n label: 'By Webhook',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['webhook_id', 'event_type', 'status', 'attempts', 'updated_at'],\n sort: [{ field: 'webhook_id', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'webhook_id', order: 'asc', collapsed: true }] },\n pagination: { pageSize: 100 },\n },\n all_deliveries: {\n type: 'grid',\n name: 'all_deliveries',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({\n label: 'Delivery ID',\n required: true,\n maxLength: 64,\n description: 'UUID — also doubles as the receiver-side idempotency key',\n }),\n\n webhook_id: Field.text({\n label: 'Webhook ID',\n required: true,\n maxLength: 64,\n description: 'FK to sys_webhook.id (loosely coupled — denormalised URL/secret on row)',\n }),\n\n event_id: Field.text({\n label: 'Event ID',\n required: true,\n maxLength: 128,\n description: 'Source event id; UNIQUE(event_id, webhook_id) for dedup',\n }),\n\n event_type: Field.text({\n label: 'Event Type',\n required: true,\n maxLength: 128,\n description: 'e.g. data.record.created',\n }),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'Snapshotted at enqueue so config edits do not rewrite live rows',\n }),\n\n method: Field.text({ label: 'Method', required: false, maxLength: 10 }),\n headers_json: Field.textarea({ label: 'Headers JSON', required: false }),\n secret: Field.text({ label: 'HMAC Secret', required: false, maxLength: 256 }),\n timeout_ms: Field.number({ label: 'Timeout (ms)', required: false }),\n payload_json: Field.textarea({ label: 'Payload JSON', required: true }),\n\n partition_key: Field.number({\n label: 'Partition',\n required: true,\n description: 'hash(webhook_id) mod partitionCount — precomputed for cheap WHERE',\n }),\n\n status: Field.text({\n label: 'Status',\n required: true,\n defaultValue: 'pending',\n maxLength: 16,\n description: 'pending | in_flight | success | failed | dead',\n }),\n\n attempts: Field.number({\n label: 'Attempts',\n required: true,\n defaultValue: 0,\n description: 'Number of POST attempts made so far',\n }),\n\n claimed_by: Field.text({ label: 'Claimed By', required: false, maxLength: 128 }),\n claimed_at: Field.number({ label: 'Claimed At (ms)', required: false }),\n next_retry_at: Field.number({ label: 'Next Retry At (ms)', required: false }),\n last_attempted_at: Field.number({ label: 'Last Attempted At (ms)', required: false }),\n response_code: Field.number({ label: 'HTTP Status', required: false }),\n response_body: Field.textarea({ label: 'Response Body (capped)', required: false }),\n error: Field.textarea({ label: 'Error', required: false }),\n\n created_at: Field.number({ label: 'Created At (ms)', required: true }),\n updated_at: Field.number({ label: 'Updated At (ms)', required: true }),\n },\n\n indexes: [\n { fields: ['event_id', 'webhook_id'], unique: true },\n // Hot path: claim query\n { fields: ['status', 'partition_key', 'next_retry_at'] },\n // Reaper: scan stale in_flight rows by claimed_at\n { fields: ['status', 'claimed_at'] },\n { fields: ['webhook_id'] },\n ],\n});\n\n/** Canonical object name — exported so SqlWebhookOutbox callers can override if needed. */\nexport const SYS_WEBHOOK_DELIVERY = 'sys_webhook_delivery' as const;\n"]}
package/dist/index.cjs CHANGED
@@ -1,16 +1,16 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
4
 
5
- var _chunkFA66GQEOcjs = require('./chunk-FA66GQEO.cjs');
5
+ var _chunkHF7CCDPBcjs = require('./chunk-HF7CCDPB.cjs');
6
6
 
7
7
 
8
8
 
9
- var _chunkMJZGD37Scjs = require('./chunk-MJZGD37S.cjs');
9
+
10
+ var _chunkTDSI7UHYcjs = require('./chunk-TDSI7UHY.cjs');
10
11
 
11
12
  // src/webhook-outbox-plugin.ts
12
13
  var _types = require('@objectstack/types');
13
- var _integration = require('@objectstack/platform-objects/integration');
14
14
 
15
15
  // src/auto-enqueuer.ts
16
16
  var AutoEnqueuer = class {
@@ -476,7 +476,7 @@ var MemoryWebhookOutbox = class {
476
476
  if (row.status !== "pending") continue;
477
477
  if (row.nextRetryAt !== void 0 && row.nextRetryAt > now) continue;
478
478
  if (opts.partition) {
479
- const p = _chunkFA66GQEOcjs.hashPartition.call(void 0, row.webhookId, opts.partition.count);
479
+ const p = _chunkHF7CCDPBcjs.hashPartition.call(void 0, row.webhookId, opts.partition.count);
480
480
  if (p !== opts.partition.index) continue;
481
481
  }
482
482
  row.status = "in_flight";
@@ -521,13 +521,13 @@ var MemoryWebhookOutbox = class {
521
521
  async redeliver(id) {
522
522
  const row = this.rows.get(id);
523
523
  if (!row) {
524
- throw new (0, _chunkFA66GQEOcjs.RedeliverError)(
524
+ throw new (0, _chunkHF7CCDPBcjs.RedeliverError)(
525
525
  `Delivery row '${id}' not found`,
526
526
  "not_found"
527
527
  );
528
528
  }
529
529
  if (row.status !== "success" && row.status !== "failed" && row.status !== "dead") {
530
- throw new (0, _chunkFA66GQEOcjs.RedeliverError)(
530
+ throw new (0, _chunkHF7CCDPBcjs.RedeliverError)(
531
531
  `Delivery row '${id}' is '${row.status}', expected one of: success, failed, dead`,
532
532
  "not_eligible"
533
533
  );
@@ -556,7 +556,7 @@ var DeliveryRetentionSweeper = class {
556
556
  constructor(engine, opts = {}) {
557
557
  this.engine = engine;
558
558
  this.running = false;
559
- this.objectName = _nullishCoalesce(opts.objectName, () => ( _chunkMJZGD37Scjs.SYS_WEBHOOK_DELIVERY));
559
+ this.objectName = _nullishCoalesce(opts.objectName, () => ( _chunkTDSI7UHYcjs.SYS_WEBHOOK_DELIVERY));
560
560
  this.successTtlMs = _nullishCoalesce(opts.successTtlMs, () => ( DEFAULTS.successTtlMs));
561
561
  this.deadTtlMs = _nullishCoalesce(opts.deadTtlMs, () => ( DEFAULTS.deadTtlMs));
562
562
  this.sweepIntervalMs = _nullishCoalesce(opts.sweepIntervalMs, () => ( DEFAULTS.sweepIntervalMs));
@@ -644,13 +644,42 @@ var WebhookOutboxPlugin = class {
644
644
  scope: "system",
645
645
  name: "Webhook Outbox Schemas",
646
646
  description: "Registers sys_webhook (configuration) and sys_webhook_delivery (durable outbox telemetry).",
647
- objects: [_integration.SysWebhook, _chunkMJZGD37Scjs.SysWebhookDelivery]
647
+ objects: [_chunkTDSI7UHYcjs.SysWebhook, _chunkTDSI7UHYcjs.SysWebhookDelivery],
648
+ // ADR-0029 D7 — contribute the Webhooks entries into the
649
+ // Setup app's `group_integrations` slot. The plugin owns these
650
+ // objects (K2.a), so it ships their menu too; when the plugin
651
+ // isn't installed the slot stays empty.
652
+ navigationContributions: [
653
+ {
654
+ app: "setup",
655
+ group: "group_integrations",
656
+ priority: 100,
657
+ items: [
658
+ { id: "nav_webhooks", type: "object", label: "Webhooks", objectName: "sys_webhook", icon: "webhook", requiresObject: "sys_webhook" },
659
+ { id: "nav_webhook_deliveries", type: "object", label: "Webhook Deliveries", objectName: "sys_webhook_delivery", icon: "send", requiresObject: "sys_webhook_delivery" }
660
+ ]
661
+ }
662
+ ]
648
663
  });
649
664
  } else {
650
665
  _optionalChain([ctx, 'access', _64 => _64.logger, 'access', _65 => _65.warn, 'optionalCall', _66 => _66(
651
666
  "[webhook-outbox] manifest service unavailable \u2014 sys_webhook / sys_webhook_delivery will NOT appear in REST or Studio nav. Register MetadataService before WebhookOutboxPlugin."
652
667
  )]);
653
668
  }
669
+ if (typeof ctx.hook === "function") {
670
+ ctx.hook("kernel:ready", async () => {
671
+ try {
672
+ const i18n = ctx.getService("i18n");
673
+ if (i18n && typeof i18n.loadTranslations === "function") {
674
+ const { WebhooksTranslations } = await Promise.resolve().then(() => _interopRequireWildcard(require("./translations-OAKKANSP.cjs")));
675
+ for (const [locale, data] of Object.entries(WebhooksTranslations)) {
676
+ i18n.loadTranslations(locale, data);
677
+ }
678
+ }
679
+ } catch (e5) {
680
+ }
681
+ });
682
+ }
654
683
  const outbox = this.resolveOutbox(ctx);
655
684
  this.outboxInstance = outbox;
656
685
  const nodeId = _nullishCoalesce(_nullishCoalesce(this.options.nodeId, () => ( _types.readEnvWithDeprecation.call(void 0, "OS_NODE_ID", "OBJECTSTACK_NODE_ID"))), () => ( `node-${Math.random().toString(36).slice(2, 10)}`));
@@ -715,7 +744,7 @@ var WebhookOutboxPlugin = class {
715
744
  const engine = this.tryGetService(ctx, ["objectql", "data"]);
716
745
  if (engine) {
717
746
  const partitionCount = _nullishCoalesce(this.options.partitionCount, () => ( 8));
718
- const sql = new (0, _chunkFA66GQEOcjs.SqlWebhookOutbox)(engine, { partitionCount });
747
+ const sql = new (0, _chunkHF7CCDPBcjs.SqlWebhookOutbox)(engine, { partitionCount });
719
748
  _optionalChain([ctx, 'access', _82 => _82.logger, 'access', _83 => _83.info, 'optionalCall', _84 => _84(
720
749
  "[webhook-outbox] auto-selected SqlWebhookOutbox (objectql engine detected)",
721
750
  { partitionCount }
@@ -774,7 +803,7 @@ var WebhookOutboxPlugin = class {
774
803
  try {
775
804
  const svc = ctx.getService(n);
776
805
  if (svc) return svc;
777
- } catch (e5) {
806
+ } catch (e6) {
778
807
  }
779
808
  }
780
809
  return void 0;
@@ -811,7 +840,7 @@ var WebhookOutboxPlugin = class {
811
840
  let body;
812
841
  try {
813
842
  body = await c.req.json();
814
- } catch (e6) {
843
+ } catch (e7) {
815
844
  return c.json(
816
845
  {
817
846
  success: false,
@@ -888,7 +917,7 @@ var WebhookOutboxPlugin = class {
888
917
  const session = await api.getSession({ headers: c.req.raw.headers });
889
918
  const uid = _optionalChain([session, 'optionalAccess', _116 => _116.user, 'optionalAccess', _117 => _117.id]);
890
919
  return typeof uid === "string" && uid.length > 0 ? uid : void 0;
891
- } catch (e7) {
920
+ } catch (e8) {
892
921
  return void 0;
893
922
  }
894
923
  }
@@ -905,5 +934,5 @@ var WebhookOutboxPlugin = class {
905
934
 
906
935
 
907
936
 
908
- exports.AutoEnqueuer = AutoEnqueuer; exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS; exports.DeliveryRetentionSweeper = DeliveryRetentionSweeper; exports.MemoryWebhookOutbox = MemoryWebhookOutbox; exports.RedeliverError = _chunkFA66GQEOcjs.RedeliverError; exports.WebhookDispatcher = WebhookDispatcher; exports.WebhookOutboxPlugin = WebhookOutboxPlugin; exports.classifyAttempt = classifyAttempt; exports.hashPartition = _chunkFA66GQEOcjs.hashPartition; exports.nextRetryDelayMs = nextRetryDelayMs; exports.sendOnce = sendOnce;
937
+ exports.AutoEnqueuer = AutoEnqueuer; exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS; exports.DeliveryRetentionSweeper = DeliveryRetentionSweeper; exports.MemoryWebhookOutbox = MemoryWebhookOutbox; exports.RedeliverError = _chunkHF7CCDPBcjs.RedeliverError; exports.WebhookDispatcher = WebhookDispatcher; exports.WebhookOutboxPlugin = WebhookOutboxPlugin; exports.classifyAttempt = classifyAttempt; exports.hashPartition = _chunkHF7CCDPBcjs.hashPartition; exports.nextRetryDelayMs = nextRetryDelayMs; exports.sendOnce = sendOnce;
909
938
  //# sourceMappingURL=index.cjs.map