@cronwatch/sdk 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/anthropic.d.cts +1 -1
- package/dist/anthropic.d.ts +1 -1
- package/dist/bugsnag.cjs +108 -0
- package/dist/bugsnag.cjs.map +1 -0
- package/dist/bugsnag.d.cts +26 -0
- package/dist/bugsnag.d.ts +26 -0
- package/dist/bugsnag.js +106 -0
- package/dist/bugsnag.js.map +1 -0
- package/dist/client-B7bqnkI5.d.cts +450 -0
- package/dist/client-pmqH3apS.d.ts +450 -0
- package/dist/d1.cjs +214 -0
- package/dist/d1.cjs.map +1 -0
- package/dist/d1.d.cts +42 -0
- package/dist/d1.d.ts +42 -0
- package/dist/d1.js +212 -0
- package/dist/d1.js.map +1 -0
- package/dist/datadog.cjs +95 -0
- package/dist/datadog.cjs.map +1 -0
- package/dist/datadog.d.cts +24 -0
- package/dist/datadog.d.ts +24 -0
- package/dist/datadog.js +93 -0
- package/dist/datadog.js.map +1 -0
- package/dist/discord.cjs +2 -0
- package/dist/discord.cjs.map +1 -1
- package/dist/discord.d.cts +1 -1
- package/dist/discord.d.ts +1 -1
- package/dist/discord.js +2 -0
- package/dist/discord.js.map +1 -1
- package/dist/email-BJ-z_epD.d.cts +21 -0
- package/dist/email-_H0faCh3.d.ts +21 -0
- package/dist/honeybadger.cjs +101 -0
- package/dist/honeybadger.cjs.map +1 -0
- package/dist/honeybadger.d.cts +23 -0
- package/dist/honeybadger.d.ts +23 -0
- package/dist/honeybadger.js +99 -0
- package/dist/honeybadger.js.map +1 -0
- package/dist/index.cjs +1238 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -260
- package/dist/index.d.ts +5 -260
- package/dist/index.js +1238 -179
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs +115 -0
- package/dist/mailgun.cjs.map +1 -0
- package/dist/mailgun.d.cts +21 -0
- package/dist/mailgun.d.ts +21 -0
- package/dist/mailgun.js +113 -0
- package/dist/mailgun.js.map +1 -0
- package/dist/newrelic.cjs +88 -0
- package/dist/newrelic.cjs.map +1 -0
- package/dist/newrelic.d.cts +25 -0
- package/dist/newrelic.d.ts +25 -0
- package/dist/newrelic.js +86 -0
- package/dist/newrelic.js.map +1 -0
- package/dist/node.cjs +191 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +97 -0
- package/dist/node.d.ts +97 -0
- package/dist/node.js +186 -0
- package/dist/node.js.map +1 -0
- package/dist/pg-cron.cjs +286 -0
- package/dist/pg-cron.cjs.map +1 -0
- package/dist/pg-cron.d.cts +93 -0
- package/dist/pg-cron.d.ts +93 -0
- package/dist/pg-cron.js +280 -0
- package/dist/pg-cron.js.map +1 -0
- package/dist/postgres.cjs +20 -0
- package/dist/postgres.cjs.map +1 -1
- package/dist/postgres.d.cts +1 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.js +20 -0
- package/dist/postgres.js.map +1 -1
- package/dist/postmark.cjs +106 -0
- package/dist/postmark.cjs.map +1 -0
- package/dist/postmark.d.cts +18 -0
- package/dist/postmark.d.ts +18 -0
- package/dist/postmark.js +104 -0
- package/dist/postmark.js.map +1 -0
- package/dist/resend.cjs +114 -0
- package/dist/resend.cjs.map +1 -0
- package/dist/resend.d.cts +16 -0
- package/dist/resend.d.ts +16 -0
- package/dist/resend.js +112 -0
- package/dist/resend.js.map +1 -0
- package/dist/rollbar.cjs +108 -0
- package/dist/rollbar.cjs.map +1 -0
- package/dist/rollbar.d.cts +20 -0
- package/dist/rollbar.d.ts +20 -0
- package/dist/rollbar.js +106 -0
- package/dist/rollbar.js.map +1 -0
- package/dist/sendgrid.cjs +114 -0
- package/dist/sendgrid.cjs.map +1 -0
- package/dist/sendgrid.d.cts +19 -0
- package/dist/sendgrid.d.ts +19 -0
- package/dist/sendgrid.js +112 -0
- package/dist/sendgrid.js.map +1 -0
- package/dist/sentry.cjs +130 -0
- package/dist/sentry.cjs.map +1 -0
- package/dist/sentry.d.cts +28 -0
- package/dist/sentry.d.ts +28 -0
- package/dist/sentry.js +127 -0
- package/dist/sentry.js.map +1 -0
- package/dist/ses.cjs +170 -0
- package/dist/ses.cjs.map +1 -0
- package/dist/ses.d.cts +25 -0
- package/dist/ses.d.ts +25 -0
- package/dist/ses.js +168 -0
- package/dist/ses.js.map +1 -0
- package/dist/slack.cjs +2 -0
- package/dist/slack.cjs.map +1 -1
- package/dist/slack.d.cts +1 -1
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +2 -0
- package/dist/slack.js.map +1 -1
- package/dist/sqlite.cjs +18 -0
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.js +18 -0
- package/dist/sqlite.js.map +1 -1
- package/dist/twilio.cjs +168 -0
- package/dist/twilio.cjs.map +1 -0
- package/dist/twilio.d.cts +52 -0
- package/dist/twilio.d.ts +52 -0
- package/dist/twilio.js +162 -0
- package/dist/twilio.js.map +1 -0
- package/dist/{types-DUt2apl0.d.cts → types-Ddq1MUPL.d.cts} +29 -2
- package/dist/{types-DUt2apl0.d.ts → types-Ddq1MUPL.d.ts} +29 -2
- package/dist/webhook.cjs +11 -5
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +6 -3
- package/dist/webhook.d.ts +6 -3
- package/dist/webhook.js +11 -6
- package/dist/webhook.js.map +1 -1
- package/package.json +208 -2
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/node.ts"],"names":[],"mappings":";;;;AA+DA,SAAS,OAAO,KAAA,EAA0D;AACxE,EAAA,OAAO,MAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA;AAC3C;AAEA,SAAS,WAAW,KAAA,EAA0D;AAC5E,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA,EAAG,MAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,IAAA,EAAK;AACjD,EAAA,OAAO,QAAQ,KAAA,GAAQ,MAAA;AACzB;AAGA,SAAS,UAAA,CAAW,OAAe,IAAA,EAA6B;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,KAAK,CAAA,GAAA,EAAM,IAAI,CAAA,CAAE,CAAA;AACxC,IAAA,IAAI,GAAA,CAAI,QAAA,KAAa,GAAA,IAAO,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,MAAA,IAAU,GAAA,CAAI,IAAA,EAAM,OAAO,IAAA;AAC3F,IAAA,OAAO,GAAA,CAAI,MAAA;AAAA,EACb,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGA,SAAS,aAAa,MAAA,EAAwB;AAC5C,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,MAAM,CAAA,EAAG;AAC3C,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAM,CAAA;AAC1B,MAAA,OAAO,GAAA,CAAI,WAAW,GAAA,CAAI,MAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,OAAO,UAAA,CAAW,GAAG,CAAA,GAAI,MAAA,GAAS,IAAI,MAAM,CAAA,CAAA;AACrD;AAEA,SAAS,aAAA,CAAc,KAAsB,UAAA,EAA6B;AACxE,EAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,EAAA,IAAI,KAAA,GAAQ,MAAA,EAAQ,SAAA,KAAc,IAAA,GAAO,OAAA,GAAU,MAAA;AACnD,EAAA,IAAI,IAAA,GAAO,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,IAAI,KAAK,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,YAAY,CAAkC,CAAA;AACxG,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,MAAM,KAAK,UAAA,CAAW,GAAA,CAAI,QAAQ,mBAAmB,CAAC,GAAG,WAAA,EAAY;AACrE,IAAA,IAAI,EAAA,KAAO,MAAA,IAAU,EAAA,KAAO,OAAA,EAAS,KAAA,GAAQ,EAAA;AAC7C,IAAA,MAAM,EAAA,GAAK,UAAA,CAAW,GAAA,CAAI,OAAA,CAAQ,kBAAkB,CAAC,CAAA;AACrD,IAAA,IAAI,MAAM,UAAA,CAAW,KAAA,EAAO,EAAE,CAAA,KAAM,MAAM,IAAA,GAAO,EAAA;AAAA,EACnD;AACA,EAAA,OAAA,CAAQ,OAAO,UAAA,CAAW,KAAA,EAAO,IAAI,CAAA,GAAI,IAAA,KAAS,GAAG,KAAK,CAAA,YAAA,CAAA;AAC5D;AAGA,SAAS,OAAO,GAAA,EAA+B;AAC7C,EAAA,IAAI,OAAO,GAAA,CAAI,EAAA,KAAO,cAAc,GAAA,CAAI,QAAA,KAAa,OAAO,OAAO,KAAA;AACnE,EAAA,OAAO,CAAC,GAAA,CAAI,aAAA,IAAiB,GAAA,CAAI,eAAA,KAAoB,IAAA;AACvD;AAOA,SAAS,QAAA,CAAS,QAAiB,OAAA,EAA8C;AAC/E,EAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,IAAA,EAAM,OAAO,IAAA;AACpD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,YAAkB,YAAY,OAAO,MAAA;AACvE,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,IAAA;AACvC,EAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,IAAI,WAAA,EAAY;AAC7D,EAAA,IAAI,IAAA,GAAsB,IAAA;AAC1B,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,mCAAmC,CAAA,EAAG;AACtD,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,EAAgB;AACjC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,MAAA,KAAA,MAAW,CAAA,IAAK,MAAM,OAAA,CAAQ,KAAK,IAAI,KAAA,GAAQ,CAAC,KAAK,CAAA,EAAG;AACtD,QAAA,IAAI,MAAM,MAAA,IAAa,CAAA,KAAM,IAAA,EAAM,IAAA,CAAK,OAAO,GAAA,EAAK,OAAO,CAAA,KAAM,QAAA,GAAW,KAAK,SAAA,CAAU,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MAC3G;AAAA,IACF;AACA,IAAA,IAAA,GAAO,KAAK,QAAA,EAAS;AAAA,EACvB,CAAA,MAAA,IAAW,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG;AAChC,IAAA,IAAA,GAAO,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA,EAC9B;AACA,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,IAAA;AAC1B,EAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,EAAA,OAAA,CAAQ,OAAO,mBAAmB,CAAA;AAClC,EAAA,OAAO,IAAA;AACT;AAUO,SAAS,UAAU,GAAA,EAAsB,OAAA,GAA0D,EAAC,EAAG,MAAA,GAAqB,EAAC,EAAY;AAC9I,EAAA,MAAM,IAAA,GAAO,GAAA;AACb,EAAA,MAAM,SAAS,MAAA,CAAO,GAAA,IAAO,IAAA,CAAK,WAAA,IAAe,IAAI,GAAA,IAAO,GAAA;AAC5D,EAAA,MAAM,GAAA,GAAM,cAAc,GAAA,EAAK,OAAA,CAAQ,eAAe,IAAI,CAAA,GAAI,aAAa,MAAM,CAAA;AAEjF,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAC5B,EAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AAEvD,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACjD,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAA,MAAW,KAAK,KAAA,EAAO,OAAA,CAAQ,MAAA,CAAO,IAAA,EAAM,CAAC,CAAA;AAAA,SAClE,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,MAAA,GAAA,CAAU,GAAA,CAAI,MAAA,IAAU,KAAA,EAAO,WAAA,EAAY;AACjD,EAAA,MAAM,IAAA,GAA0C,EAAE,MAAA,EAAQ,OAAA,EAAQ;AAClE,EAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,IAAA,CAAK,MAAA,GAAS,OAAA,CAAQ,MAAA;AAC1C,EAAA,IAAI,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,MAAA,EAAQ;AACzC,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,IAAW,IAAA,CAAK,OAAA;AAEnC,IAAA,IAAI,GAAA,YAAe,UAAA,EAAY,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,SAAA,IAClC,OAAO,GAAA,KAAQ,QAAA,EAAU,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,SAAA,IACrC,MAAA,CAAO,GAAG,CAAA,EAAG;AACpB,MAAA,IAAA,CAAK,IAAA,GAAO,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAC9B,MAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,IAChB,CAAA,MAAO;AACL,MAAA,MAAM,IAAA,GAAO,SAAS,MAAA,CAAO,IAAA,KAAS,SAAY,MAAA,CAAO,IAAA,GAAO,IAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAClF,MAAA,IAAI,IAAA,KAAS,IAAA,EAAM,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,WAC1B;AAEH,QAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,QAAA,OAAA,CAAQ,OAAO,mBAAmB,CAAA;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,IAAI,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA;AAC9B;AAOA,eAAsB,aAAA,CAAc,GAAA,EAAqB,QAAA,EAAoB,MAAA,GAAS,KAAA,EAAsB;AAC1G,EAAA,GAAA,CAAI,aAAa,QAAA,CAAS,MAAA;AAC1B,EAAA,QAAA,CAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAA,EAAO,IAAA,KAAS;AACxC,IAAA,IAAI,IAAA,KAAS,YAAA,EAAc,GAAA,CAAI,SAAA,CAAU,MAAM,KAAK,CAAA;AAAA,EACtD,CAAC,CAAA;AACD,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,OAAA,CAAQ,YAAA,EAAa;AAC9C,EAAA,IAAI,QAAQ,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,SAAA,CAAU,cAAc,OAAO,CAAA;AAC3D,EAAA,IAAI,SAAS,IAAA,KAAS,IAAA,IAAQ,MAAA,CAAO,WAAA,OAAkB,MAAA,EAAQ;AAC7D,IAAA,IAAI,QAAA,CAAS,SAAS,IAAA,EAAM,MAAM,SAAS,IAAA,CAAK,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AACvE,IAAA,GAAA,CAAI,GAAA,EAAI;AACR,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,QAAA,CAAS,OAAA,CAAQ,QAAA,CAAS,IAAiD,GAAG,GAAG,CAAA;AAAA,EAClG,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,SAAS,QAAQ,QAAA,EAA6C;AAC5D,EAAA,OAAO,aAAa,MAAA,GAAY,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACpE;AAEA,SAAS,OAAA,CAAQ,UAAkB,KAAA,EAA+B;AAChE,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,EAAA,IAAM,QAAA,KAAa,SAAS,QAAA,CAAS,UAAA,CAAW,CAAA,EAAG,KAAK,CAAA,CAAA,CAAG,CAAA;AAChG;AAEA,SAAS,WAAW,MAAA,EAAwB;AAC1C,EAAA,MAAM,IAAA,GAAO,aAAa,MAAM,CAAA;AAChC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA;AAC5B,EAAA,OAAO,MAAM,EAAA,GAAK,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA;AAC1C;AAGA,SAAS,iBAAiB,GAAA,EAAkC;AAC1D,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,GAAA,CAAI,IAAA,CAAK,SAAS,MAAM;AACtB,IAAA,IAAI,CAAC,GAAA,CAAI,gBAAA,EAAkB,UAAA,CAAW,KAAA,EAAM;AAAA,EAC9C,CAAC,CAAA;AACD,EAAA,OAAO,UAAA,CAAW,MAAA;AACpB;AAaO,SAAS,aAAA,CAAc,OAAA,EAAoB,OAAA,GAA8B,EAAC,EAAgB;AAC/F,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,KAAe,IAAA;AAC1C,EAAA,OAAO,OAAO,GAAA,EAAK,GAAA,EAAK,IAAA,KAAS;AAC/B,IAAA,MAAM,IAAA,GAAO,GAAA;AACb,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,WAAA,IAAe,IAAI,GAAA,IAAO,GAAG,CAAA,EAAG,KAAK,CAAA,EAAG;AACnE,MAAA,IAAI,MAAM,IAAA,EAAK;AAAA,WACV;AACH,QAAA,GAAA,CAAI,UAAA,GAAa,GAAA;AACjB,QAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,2BAA2B,CAAA;AACzD,QAAA,GAAA,CAAI,IAAI,WAAW,CAAA;AAAA,MACrB;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,UAAU,GAAA,EAAK,EAAE,YAAY,MAAA,EAAQ,gBAAA,CAAiB,GAAG,CAAA,EAAG,CAAA;AAC5E,MAAA,MAAM,cAAc,GAAA,EAAK,MAAM,QAAQ,OAAO,CAAA,EAAG,QAAQ,MAAM,CAAA;AAAA,IACjE,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,IAAA,OAAW,KAAK,CAAA;AAAA,WAAA,IACX,CAAC,IAAI,WAAA,EAAa;AACzB,QAAA,GAAA,CAAI,UAAA,GAAa,GAAA;AACjB,QAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,2BAA2B,CAAA;AACzD,QAAA,GAAA,CAAI,IAAI,uBAAuB,CAAA;AAAA,MACjC,OAAO,GAAA,CAAI,OAAA,CAAQ,KAAA,YAAiB,KAAA,GAAQ,QAAQ,MAAS,CAAA;AAAA,IAC/D;AAAA,EACF,CAAA;AACF;AAUO,SAAS,eAAA,CAAgB,OAAA,EAAoB,OAAA,GAA8B,EAAC,EAAyE;AAC1J,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,KAAe,IAAA;AAC1C,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,WAAA,IAAe,GAAA,CAAI,IAAI,GAAA,IAAO,GAAA;AACjD,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,EAAG,KAAK,CAAA,EAAG;AACvC,MAAA,MAAM,IAAA,EAAK;AACX,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,GAAA,CAAI,GAAA,EAAK,EAAE,UAAA,EAAY,MAAA,EAAQ,gBAAA,CAAiB,GAAA,CAAI,GAAG,CAAA,EAAE,EAAG;AAAA,MACpF,GAAA,EAAK,MAAA;AAAA,MACL,OAAA,EAAS,IAAI,OAAA,EAAS,OAAA;AAAA,MACtB,IAAA,EAAM,IAAI,OAAA,EAAS;AAAA,KACpB,CAAA;AACD,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAO,CAAA;AACtC,IAAA,GAAA,CAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,aAAA,CAAc,GAAA,CAAI,GAAA,EAAK,QAAA,EAAU,QAAQ,MAAM,CAAA;AAAA,EACvD,CAAA;AACF","file":"node.js","sourcesContent":["/**\n * @cronwatch/sdk/node: serve a fetch-style handler (cw.routes().handler, or\n * a job's handler()) from Node's http module and the frameworks built on it:\n * a plain http server, Express and Connect, NestJS, Koa, Strapi and Firebase\n * onRequest. The SDK core stays free of node: imports so it runs on Workers;\n * this entry point is the only one that uses Node's http and stream types.\n */\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\n\n/** A fetch-style handler: a Request in, a Response out. */\nexport type FetchLike = (request: Request) => Response | Promise<Response>;\n\nexport interface NodeHandlerOptions {\n /**\n * Build the request URL from X-Forwarded-Proto and X-Forwarded-Host (the\n * first value of each) when present, instead of the connection's scheme and\n * the Host header. Only behind a proxy that sets or overwrites both, since\n * a client can send them too. Default false.\n */\n trustProxy?: boolean;\n /**\n * Answer only requests at this path or under it, and pass the rest on: to\n * `next()` as Express or Connect middleware, to the next Koa middleware,\n * and with a 404 when there is no next. The path is read from\n * `req.originalUrl` (so Express mount paths count) or `req.url`. Leave it\n * out to answer every request the handler is given, which suits\n * `app.use(\"/cronwatch\", ...)`, a Nest controller and a Firebase function.\n */\n basePath?: string;\n}\n\n/** Called by Express and Connect for the next middleware, or with an error. */\nexport type NodeNext = (error?: unknown) => void;\n\n/** A handler for http.createServer, Express or Connect, Nest and Firebase onRequest. */\nexport type NodeHandler = (req: IncomingMessage, res: ServerResponse, next?: NodeNext) => Promise<void>;\n\n/** The parts of a Koa context the middleware uses, so the SDK needs no Koa types. */\nexport interface KoaContextLike {\n req: IncomingMessage;\n res: ServerResponse;\n respond?: boolean;\n /** Koa's own copy of the URL, kept when koa-mount rewrites req.url. */\n originalUrl?: string;\n /** Set by koa-bodyparser and similar, when they run before this middleware. */\n request?: { body?: unknown; rawBody?: unknown };\n}\n\n/** Where a request body may already be, when a parser has read the stream. */\nexport interface BodySource {\n /** The request target to use instead of req.originalUrl or req.url. */\n url?: string;\n /** The body's bytes as read (Firebase, Nest with rawBody: true, koa-bodyparser). */\n rawBody?: unknown;\n /** A body a parser has decoded (express.json, express.urlencoded). */\n body?: unknown;\n}\n\ntype NodeRequest = IncomingMessage & { originalUrl?: string; rawBody?: unknown; body?: unknown };\n\nfunction header(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction firstValue(value: string | string[] | undefined): string | undefined {\n const first = header(value)?.split(\",\")[0]?.trim();\n return first ? first : undefined;\n}\n\n/** An origin from a scheme and host, or null when the host is not a bare host[:port]. */\nfunction originFrom(proto: string, host: string): string | null {\n try {\n const url = new URL(`${proto}://${host}`);\n if (url.pathname !== \"/\" || url.username || url.password || url.search || url.hash) return null;\n return url.origin;\n } catch {\n return null;\n }\n}\n\n/** The path and query of a request target, which may be absolute-form. */\nfunction pathAndQuery(target: string): string {\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target)) {\n try {\n const url = new URL(target);\n return url.pathname + url.search;\n } catch {\n return \"/\";\n }\n }\n // \"//x\" must stay a path, not become a host, so it is never parsed relative to a base.\n return target.startsWith(\"/\") ? target : `/${target}`;\n}\n\nfunction requestOrigin(req: IncomingMessage, trustProxy: boolean): string {\n const socket = req.socket as { encrypted?: boolean } | undefined;\n let proto = socket?.encrypted === true ? \"https\" : \"http\";\n let host = header(req.headers.host) ?? header(req.headers[\":authority\"] as string | string[] | undefined);\n if (trustProxy) {\n const fp = firstValue(req.headers[\"x-forwarded-proto\"])?.toLowerCase();\n if (fp === \"http\" || fp === \"https\") proto = fp;\n const fh = firstValue(req.headers[\"x-forwarded-host\"]);\n if (fh && originFrom(proto, fh) !== null) host = fh;\n }\n return (host ? originFrom(proto, host) : null) ?? `${proto}://localhost`;\n}\n\n/** The stream has not been read by a body parser, so it can be passed on as is. */\nfunction unread(req: IncomingMessage): boolean {\n if (typeof req.on !== \"function\" || req.readable === false) return false;\n return !req.readableEnded && req.readableDidRead !== true;\n}\n\n/**\n * A body a parser decoded, encoded again for its content type: JSON as JSON,\n * a urlencoded form as a form. Anything else (multipart, unknown types) is\n * dropped, because it cannot be rebuilt faithfully.\n */\nfunction reencode(parsed: unknown, headers: Headers): string | Uint8Array | null {\n if (parsed === undefined || parsed === null) return null;\n if (typeof parsed === \"string\" || parsed instanceof Uint8Array) return parsed;\n if (typeof parsed !== \"object\") return null;\n const type = (headers.get(\"content-type\") ?? \"\").toLowerCase();\n let text: string | null = null;\n if (type.includes(\"application/x-www-form-urlencoded\")) {\n const form = new URLSearchParams();\n for (const [key, value] of Object.entries(parsed)) {\n for (const v of Array.isArray(value) ? value : [value]) {\n if (v !== undefined && v !== null) form.append(key, typeof v === \"object\" ? JSON.stringify(v) : String(v));\n }\n }\n text = form.toString();\n } else if (type.includes(\"json\")) {\n text = JSON.stringify(parsed);\n }\n if (text === null) return null;\n headers.delete(\"content-length\");\n headers.delete(\"transfer-encoding\");\n return text;\n}\n\n/**\n * A fetch Request for a Node request. The URL is the Host header (or the\n * forwarded host and scheme with trustProxy) plus req.originalUrl or req.url.\n * The body, for anything but GET and HEAD, is the first of: `rawBody` (from\n * the source, or req.rawBody), the request stream when nothing has read it,\n * or a body a parser decoded (req.body), re-encoded as JSON or a urlencoded\n * form to match its content type.\n */\nexport function toRequest(req: IncomingMessage, options: { trustProxy?: boolean; signal?: AbortSignal } = {}, source: BodySource = {}): Request {\n const node = req as NodeRequest;\n const target = source.url ?? node.originalUrl ?? req.url ?? \"/\";\n const url = requestOrigin(req, options.trustProxy === true) + pathAndQuery(target);\n\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n // HTTP/2 pseudo-headers (\":authority\") are not valid header names.\n if (value === undefined || name.startsWith(\":\")) continue;\n if (Array.isArray(value)) for (const v of value) headers.append(name, v);\n else headers.set(name, value);\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const init: RequestInit & { duplex?: \"half\" } = { method, headers };\n if (options.signal) init.signal = options.signal;\n if (method !== \"GET\" && method !== \"HEAD\") {\n const raw = source.rawBody ?? node.rawBody;\n // A Buffer is a Uint8Array view, which fetch reads with its offset.\n if (raw instanceof Uint8Array) init.body = raw as unknown as BodyInit;\n else if (typeof raw === \"string\") init.body = raw;\n else if (unread(req)) {\n init.body = Readable.toWeb(req) as unknown as ReadableStream<Uint8Array>;\n init.duplex = \"half\";\n } else {\n const body = reencode(source.body !== undefined ? source.body : node.body, headers);\n if (body !== null) init.body = body as unknown as BodyInit;\n else {\n // The bytes are gone, so the Request has no body and says so.\n headers.delete(\"content-length\");\n headers.delete(\"transfer-encoding\");\n }\n }\n }\n return new Request(url, init);\n}\n\n/**\n * Writes a fetch Response to a Node response: the status, every header\n * (each Set-Cookie as its own header line), and the body, streamed. A HEAD\n * request gets the headers only.\n */\nexport async function writeResponse(res: ServerResponse, response: Response, method = \"GET\"): Promise<void> {\n res.statusCode = response.status;\n response.headers.forEach((value, name) => {\n if (name !== \"set-cookie\") res.setHeader(name, value);\n });\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) res.setHeader(\"set-cookie\", cookies);\n if (response.body === null || method.toUpperCase() === \"HEAD\") {\n if (response.body !== null) await response.body.cancel().catch(() => {});\n res.end();\n return;\n }\n try {\n await pipeline(Readable.fromWeb(response.body as unknown as NodeReadableStream<Uint8Array>), res);\n } catch {\n // The client went away mid-body; there is no one left to tell.\n }\n}\n\nfunction mountOf(basePath: string | undefined): string | null {\n return basePath === undefined ? null : basePath.replace(/\\/+$/, \"\");\n}\n\nfunction inMount(pathname: string, mount: string | null): boolean {\n return mount === null || mount === \"\" || pathname === mount || pathname.startsWith(`${mount}/`);\n}\n\nfunction pathnameOf(target: string): string {\n const path = pathAndQuery(target);\n const q = path.search(/[?#]/);\n return q === -1 ? path : path.slice(0, q);\n}\n\n/** Aborts the Request's signal when the client disconnects before the response is done. */\nfunction disconnectSignal(res: ServerResponse): AbortSignal {\n const controller = new AbortController();\n res.once(\"close\", () => {\n if (!res.writableFinished) controller.abort();\n });\n return controller.signal;\n}\n\n/**\n * A Node handler for a fetch-style handler. Use it as a plain server\n * (`http.createServer(toNodeHandler(routes.handler))`), as Express or Connect\n * middleware (`app.use(\"/cronwatch\", toNodeHandler(routes.handler))`), from a\n * Nest controller, or as a Firebase `onRequest` function.\n *\n * With `basePath`, requests outside it go to `next()` (or get a 404 when\n * there is no next); without it, every request is answered. An error thrown\n * by the fetch handler goes to `next(error)` when there is a next, and is\n * otherwise answered with a plain 500.\n */\nexport function toNodeHandler(handler: FetchLike, options: NodeHandlerOptions = {}): NodeHandler {\n const mount = mountOf(options.basePath);\n const trustProxy = options.trustProxy === true;\n return async (req, res, next) => {\n const node = req as NodeRequest;\n if (!inMount(pathnameOf(node.originalUrl ?? req.url ?? \"/\"), mount)) {\n if (next) next();\n else {\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Not found\");\n }\n return;\n }\n try {\n const request = toRequest(req, { trustProxy, signal: disconnectSignal(res) });\n await writeResponse(res, await handler(request), request.method);\n } catch (error) {\n if (next) next(error);\n else if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Internal Server Error\");\n } else res.destroy(error instanceof Error ? error : undefined);\n }\n };\n}\n\n/**\n * Koa middleware for a fetch-style handler. Requests outside `basePath` (when\n * given) go on to the next middleware. The rest are answered here with\n * `ctx.respond = false`, so Koa leaves the response alone. Place it before\n * any body parser; if one runs first, its `ctx.request.rawBody` or\n * `ctx.request.body` is used. An error thrown by the fetch handler is thrown\n * on to Koa before anything is written.\n */\nexport function toKoaMiddleware(handler: FetchLike, options: NodeHandlerOptions = {}): (ctx: KoaContextLike, next: () => Promise<unknown>) => Promise<void> {\n const mount = mountOf(options.basePath);\n const trustProxy = options.trustProxy === true;\n return async (ctx, next) => {\n const target = ctx.originalUrl ?? ctx.req.url ?? \"/\";\n if (!inMount(pathnameOf(target), mount)) {\n await next();\n return;\n }\n const request = toRequest(ctx.req, { trustProxy, signal: disconnectSignal(ctx.res) }, {\n url: target,\n rawBody: ctx.request?.rawBody,\n body: ctx.request?.body,\n });\n const response = await handler(request);\n ctx.respond = false;\n await writeResponse(ctx.res, response, request.method);\n };\n}\n"]}
|
package/dist/pg-cron.cjs
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/sources/pgcron.ts
|
|
4
|
+
var BACKFILL = 20;
|
|
5
|
+
var PAGE = 500;
|
|
6
|
+
var MAX_PAGES = 10;
|
|
7
|
+
var PG_CRON_HOLD_MS = 10 * 6e4;
|
|
8
|
+
var JOBS_SQL = `SELECT jobid, jobname, schedule, database, username, active FROM cron.job ORDER BY jobid`;
|
|
9
|
+
var SETTING_SQL = `SELECT setting FROM pg_settings WHERE name = $1`;
|
|
10
|
+
var COLUMNS = `d.runid, d.jobid, d.status, d.return_message, d.start_time, d.end_time`;
|
|
11
|
+
var RUNS_SQL = `SELECT ${COLUMNS}
|
|
12
|
+
FROM cron.job_run_details d
|
|
13
|
+
LEFT JOIN unnest($1::bigint[], $2::bigint[]) AS c(jobid, after) ON d.jobid = c.jobid
|
|
14
|
+
WHERE d.runid > c.after OR d.runid = ANY($3::bigint[])
|
|
15
|
+
ORDER BY d.runid LIMIT ${PAGE}`;
|
|
16
|
+
var NEWEST_SQL = `SELECT ${COLUMNS} FROM cron.job_run_details d WHERE d.jobid = $1 ORDER BY d.runid DESC LIMIT ${BACKFILL}`;
|
|
17
|
+
var finishedStatus = (status) => status === "succeeded" || status === "failed";
|
|
18
|
+
function pgCronSchedule(schedule) {
|
|
19
|
+
const text = schedule.trim();
|
|
20
|
+
const seconds = /^(\d+)\s*seconds?$/i.exec(text);
|
|
21
|
+
if (seconds) return `every ${Number(seconds[1])}s`;
|
|
22
|
+
if (/^@reboot$/i.test(text)) return null;
|
|
23
|
+
const fields = text.split(/\s+/);
|
|
24
|
+
if (fields.length > 5 && !fields[0].startsWith("@")) fields.length = 5;
|
|
25
|
+
if (fields.length === 5 && fields[2].includes("$")) fields[2] = fields[2].replace(/\$/g, "L");
|
|
26
|
+
return fields.join(" ");
|
|
27
|
+
}
|
|
28
|
+
function pgCronJobName(job) {
|
|
29
|
+
const cleaned = (job.jobname ?? "").replace(/[^A-Za-z0-9._:-]+/g, "-").replace(/^[^A-Za-z0-9]+/, "").slice(0, 100);
|
|
30
|
+
return cleaned || `pg_cron:${job.jobid}`;
|
|
31
|
+
}
|
|
32
|
+
function pgCronRun(row, job, idPrefix, fallbackAt = Date.now()) {
|
|
33
|
+
const finishedAt = row.end_time === null ? null : new Date(row.end_time).getTime();
|
|
34
|
+
const done = finishedStatus(row.status);
|
|
35
|
+
if (row.start_time === null && !done) return null;
|
|
36
|
+
const startedAt = row.start_time !== null ? new Date(row.start_time).getTime() : finishedAt ?? fallbackAt;
|
|
37
|
+
const message = row.return_message === null ? null : row.return_message.trim() || null;
|
|
38
|
+
const status = row.status === "succeeded" ? "ok" : row.status === "failed" ? "failed" : "running";
|
|
39
|
+
const end = done ? Math.max(startedAt, finishedAt ?? startedAt) : null;
|
|
40
|
+
return {
|
|
41
|
+
id: `${idPrefix}${row.runid}`,
|
|
42
|
+
job,
|
|
43
|
+
status,
|
|
44
|
+
startedAt,
|
|
45
|
+
finishedAt: end,
|
|
46
|
+
durationMs: end === null ? null : end - startedAt,
|
|
47
|
+
error: status === "failed" ? message ?? "pg_cron reported the run as failed" : null,
|
|
48
|
+
output: status === "ok" ? message : null,
|
|
49
|
+
metrics: {},
|
|
50
|
+
trigger: "pg_cron"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function unscheduled(definition) {
|
|
54
|
+
const { description, tags, grace, timeout, maxDuration, budget, failuresBeforeAlert } = definition;
|
|
55
|
+
const out = {};
|
|
56
|
+
if (description !== void 0) out.description = description;
|
|
57
|
+
if (tags !== void 0) out.tags = tags;
|
|
58
|
+
if (grace !== void 0) out.grace = grace;
|
|
59
|
+
if (timeout !== void 0) out.timeout = timeout;
|
|
60
|
+
if (maxDuration !== void 0) out.maxDuration = maxDuration;
|
|
61
|
+
if (budget !== void 0) out.budget = budget;
|
|
62
|
+
if (failuresBeforeAlert !== void 0) out.failuresBeforeAlert = failuresBeforeAlert;
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
var keyOf = (definition) => JSON.stringify(definition, (_k, v) => v instanceof RegExp || typeof v === "function" ? String(v) : v);
|
|
66
|
+
function pgCron(db, options = {}) {
|
|
67
|
+
const prefix = options.prefix ?? "";
|
|
68
|
+
const idPrefix = `pgcron:${prefix}`;
|
|
69
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
70
|
+
const lastAt = /* @__PURE__ */ new Map();
|
|
71
|
+
const pending = /* @__PURE__ */ new Map();
|
|
72
|
+
const held = /* @__PURE__ */ new Map();
|
|
73
|
+
let known = /* @__PURE__ */ new Map();
|
|
74
|
+
const declared = /* @__PURE__ */ new Map();
|
|
75
|
+
const retired = /* @__PURE__ */ new Set();
|
|
76
|
+
let scanned = false;
|
|
77
|
+
const warned = /* @__PURE__ */ new Set();
|
|
78
|
+
const warnOnce = (host, key, message) => {
|
|
79
|
+
if (warned.has(key)) return;
|
|
80
|
+
warned.add(key);
|
|
81
|
+
host.onError(new Error(message), "source pg_cron");
|
|
82
|
+
};
|
|
83
|
+
const picks = (job) => {
|
|
84
|
+
const { jobs } = options;
|
|
85
|
+
if (jobs === void 0) return true;
|
|
86
|
+
if (typeof jobs === "function") return jobs(job);
|
|
87
|
+
return jobs.some((j) => typeof j === "number" ? j === job.jobid : j === job.jobname);
|
|
88
|
+
};
|
|
89
|
+
const setting = async (name) => {
|
|
90
|
+
try {
|
|
91
|
+
const { rows } = await db.query(SETTING_SQL, [name]);
|
|
92
|
+
return rows[0]?.setting ?? null;
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const runIdOf = (id) => {
|
|
98
|
+
if (!id.startsWith(idPrefix)) return null;
|
|
99
|
+
const n = Number(id.slice(idPrefix.length));
|
|
100
|
+
return Number.isSafeInteger(n) ? n : null;
|
|
101
|
+
};
|
|
102
|
+
const retire = (host, name, definition, why) => {
|
|
103
|
+
const base = unscheduled(definition);
|
|
104
|
+
const next = { ...base, description: `${base.description ?? "pg_cron job"} (${why})` };
|
|
105
|
+
try {
|
|
106
|
+
host.job(name, next);
|
|
107
|
+
declared.set(name, keyOf(next));
|
|
108
|
+
retired.add(name);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
host.onError(e, `source pg_cron: job ${name}`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
name: "pg_cron",
|
|
115
|
+
async sync(host) {
|
|
116
|
+
const now = host.now();
|
|
117
|
+
let timezone = options.timezone;
|
|
118
|
+
if (!timezone) {
|
|
119
|
+
const tz = await setting("cron.timezone");
|
|
120
|
+
if (tz === null) warnOnce(host, "tz", "could not read cron.timezone; assuming UTC. Grant pg_read_all_settings or pass pgCron(pool, { timezone }).");
|
|
121
|
+
timezone = tz === null || /^(gmt|utc|z)$/i.test(tz) ? "UTC" : tz;
|
|
122
|
+
}
|
|
123
|
+
const logRun = await setting("cron.log_run");
|
|
124
|
+
const recording = logRun !== "off";
|
|
125
|
+
if (!recording) warnOnce(host, "log_run", "cron.log_run is off, so pg_cron records no runs: jobs are watched without their schedules and no run can fail. Turn it on to watch them.");
|
|
126
|
+
const { rows } = await db.query(JOBS_SQL);
|
|
127
|
+
if (rows.length === 0) {
|
|
128
|
+
warnOnce(host, "empty", "cron.job shows no jobs. pg_cron's row level security shows a role only the jobs it scheduled: connect as that role, or give this one BYPASSRLS.");
|
|
129
|
+
}
|
|
130
|
+
const all = rows.map((r) => ({ ...r, jobid: Number(r.jobid) }));
|
|
131
|
+
const jobs = all.filter(picks);
|
|
132
|
+
const names = /* @__PURE__ */ new Map();
|
|
133
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
134
|
+
const used = /* @__PURE__ */ new Set();
|
|
135
|
+
for (const job of jobs) {
|
|
136
|
+
let name = prefix + (options.jobName ? options.jobName(job) : pgCronJobName(job));
|
|
137
|
+
if (used.has(name)) name = `${name}:${job.jobid}`;
|
|
138
|
+
used.add(name);
|
|
139
|
+
const extra = typeof options.options === "function" ? options.options(job) : options.options ?? {};
|
|
140
|
+
const schedule = job.active && recording ? pgCronSchedule(job.schedule) : null;
|
|
141
|
+
let definition = {
|
|
142
|
+
description: `pg_cron job ${job.jobid} in ${job.database} as ${job.username}${job.active ? "" : " (paused)"}`,
|
|
143
|
+
tags: ["pg_cron"],
|
|
144
|
+
...extra,
|
|
145
|
+
...schedule ? { schedule, timezone } : {}
|
|
146
|
+
};
|
|
147
|
+
try {
|
|
148
|
+
if (declared.get(name) !== keyOf(definition)) {
|
|
149
|
+
const key = keyOf(definition);
|
|
150
|
+
try {
|
|
151
|
+
host.job(name, definition);
|
|
152
|
+
} catch (e) {
|
|
153
|
+
if (!schedule) throw e;
|
|
154
|
+
host.onError(new Error(`pg_cron job ${job.jobid}: ${e.message}; watching it without a schedule`), "source pg_cron");
|
|
155
|
+
const { schedule: _s, timezone: _t, ...rest } = definition;
|
|
156
|
+
definition = rest;
|
|
157
|
+
host.job(name, rest);
|
|
158
|
+
}
|
|
159
|
+
declared.set(name, key);
|
|
160
|
+
}
|
|
161
|
+
names.set(job.jobid, name);
|
|
162
|
+
definitions.set(job.jobid, definition);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
host.onError(e, `source pg_cron: job ${job.jobid}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const inUse = new Set(names.values());
|
|
168
|
+
for (const name of inUse) retired.delete(name);
|
|
169
|
+
for (const [jobid, previous] of known) {
|
|
170
|
+
if (inUse.has(previous.name)) continue;
|
|
171
|
+
const renamed = names.get(jobid);
|
|
172
|
+
retire(host, previous.name, previous.definition, renamed ? `renamed to ${renamed}` : "no longer watched");
|
|
173
|
+
}
|
|
174
|
+
known = new Map([...names].map(([jobid, name]) => [jobid, { name, definition: definitions.get(jobid) }]));
|
|
175
|
+
if (!scanned && rows.length > 0) {
|
|
176
|
+
scanned = true;
|
|
177
|
+
try {
|
|
178
|
+
const visible = new Set(all.map((j) => j.jobid));
|
|
179
|
+
for (const stored of await host.store.listJobs()) {
|
|
180
|
+
const def = stored.definition;
|
|
181
|
+
if (!stored.name.startsWith(prefix) || inUse.has(stored.name) || !def.schedule || !(def.tags ?? []).includes("pg_cron")) continue;
|
|
182
|
+
const match = /^pg_cron job (\d+) in /.exec(def.description ?? "");
|
|
183
|
+
if (!match) continue;
|
|
184
|
+
const jobid = Number(match[1]);
|
|
185
|
+
const current = names.get(jobid);
|
|
186
|
+
if (!visible.has(jobid)) retire(host, stored.name, def, "no longer in cron.job");
|
|
187
|
+
else if (current && !stored.name.endsWith(current.slice(prefix.length))) retire(host, stored.name, def, `renamed to ${current}`);
|
|
188
|
+
}
|
|
189
|
+
} catch (e) {
|
|
190
|
+
host.onError(e, "source pg_cron");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!recording || names.size === 0) return [];
|
|
194
|
+
const alerts = [];
|
|
195
|
+
const record = async (row, evaluate) => {
|
|
196
|
+
const runid = Number(row.runid);
|
|
197
|
+
const jobid = Number(row.jobid);
|
|
198
|
+
const name = pending.get(runid) ?? names.get(jobid);
|
|
199
|
+
if (!name) {
|
|
200
|
+
held.delete(runid);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let run;
|
|
204
|
+
if (row.start_time === null && !finishedStatus(row.status)) {
|
|
205
|
+
const since = held.get(runid) ?? now;
|
|
206
|
+
if (now - since < PG_CRON_HOLD_MS) {
|
|
207
|
+
held.set(runid, since);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
run = pgCronRun({ ...row, start_time: new Date(since) }, name, idPrefix);
|
|
211
|
+
} else {
|
|
212
|
+
run = pgCronRun(row, name, idPrefix, lastAt.get(jobid) ?? now);
|
|
213
|
+
}
|
|
214
|
+
held.delete(runid);
|
|
215
|
+
if (!run) return;
|
|
216
|
+
try {
|
|
217
|
+
alerts.push(...await host.recordRun(run, { evaluate }));
|
|
218
|
+
} catch (e) {
|
|
219
|
+
host.onError(e, `source pg_cron: run ${runid}`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (run.status === "running") pending.set(runid, name);
|
|
223
|
+
else pending.delete(runid);
|
|
224
|
+
if (run.startedAt > (lastAt.get(jobid) ?? -Infinity)) lastAt.set(jobid, run.startedAt);
|
|
225
|
+
};
|
|
226
|
+
for (const [jobid, name] of names) {
|
|
227
|
+
if (cursors.has(jobid)) continue;
|
|
228
|
+
const ours = (await host.store.listRuns(name, BACKFILL)).filter((r) => runIdOf(r.id) !== null);
|
|
229
|
+
if (ours.length > 0) {
|
|
230
|
+
cursors.set(jobid, Math.max(...ours.map((r) => runIdOf(r.id))));
|
|
231
|
+
lastAt.set(jobid, Math.max(...ours.map((r) => r.startedAt)));
|
|
232
|
+
for (const r of ours) if (r.status === "running" || r.status === "timeout") pending.set(runIdOf(r.id), r.job);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const { rows: newest } = await db.query(NEWEST_SQL, [jobid]);
|
|
236
|
+
const ordered = newest.slice().reverse();
|
|
237
|
+
let lastFinished = -1;
|
|
238
|
+
ordered.forEach((r, i) => {
|
|
239
|
+
if (finishedStatus(r.status)) lastFinished = i;
|
|
240
|
+
});
|
|
241
|
+
for (const [i, row] of ordered.entries()) {
|
|
242
|
+
if (await host.store.getRun(`${idPrefix}${row.runid}`)) continue;
|
|
243
|
+
await record(row, i >= lastFinished);
|
|
244
|
+
}
|
|
245
|
+
cursors.set(jobid, ordered.length > 0 ? Number(ordered[ordered.length - 1].runid) : 0);
|
|
246
|
+
}
|
|
247
|
+
const watched = /* @__PURE__ */ new Set([...names.values(), ...retired]);
|
|
248
|
+
for (const run of await host.store.runningRuns()) {
|
|
249
|
+
const id = runIdOf(run.id);
|
|
250
|
+
if (id !== null && watched.has(run.job)) pending.set(id, run.job);
|
|
251
|
+
}
|
|
252
|
+
const open = /* @__PURE__ */ new Set([...pending.keys(), ...held.keys()]);
|
|
253
|
+
let complete = false;
|
|
254
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
255
|
+
const jobids = [...names.keys()];
|
|
256
|
+
const { rows: details } = await db.query(RUNS_SQL, [jobids, jobids.map((j) => cursors.get(j) ?? 0), [...open]]);
|
|
257
|
+
for (const row of details) {
|
|
258
|
+
const jobid = Number(row.jobid);
|
|
259
|
+
const runid = Number(row.runid);
|
|
260
|
+
open.delete(runid);
|
|
261
|
+
await record(row, true);
|
|
262
|
+
if (names.has(jobid) && runid > (cursors.get(jobid) ?? 0)) cursors.set(jobid, runid);
|
|
263
|
+
}
|
|
264
|
+
if (details.length < PAGE) {
|
|
265
|
+
complete = true;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (complete) {
|
|
270
|
+
for (const runid of open) {
|
|
271
|
+
pending.delete(runid);
|
|
272
|
+
held.delete(runid);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return alerts;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
exports.PG_CRON_HOLD_MS = PG_CRON_HOLD_MS;
|
|
281
|
+
exports.pgCron = pgCron;
|
|
282
|
+
exports.pgCronJobName = pgCronJobName;
|
|
283
|
+
exports.pgCronRun = pgCronRun;
|
|
284
|
+
exports.pgCronSchedule = pgCronSchedule;
|
|
285
|
+
//# sourceMappingURL=pg-cron.cjs.map
|
|
286
|
+
//# sourceMappingURL=pg-cron.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sources/pgcron.ts"],"names":[],"mappings":"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","file":"pg-cron.cjs","sourcesContent":["import type { Source, SourceHost } from \"../client.js\";\nimport type { Alert, JobOptions, Run, StoredJobDefinition } from \"../types.js\";\n\n/**\n * Anything with a pg-style query(): a `pg` Pool or Client, or a wrapper\n * around another driver that answers with `{ rows }`.\n */\nexport interface Queryable {\n query(text: string, values?: unknown[]): Promise<{ rows: any[] }>;\n}\n\n/** A row of cron.job. */\nexport interface PgCronJob {\n jobid: number;\n jobname: string | null;\n schedule: string;\n database: string;\n username: string;\n active: boolean;\n}\n\nexport interface PgCronOptions {\n /**\n * Which jobs to watch: names or ids, or a function that picks them. Default\n * every job the role can see.\n */\n jobs?: (string | number)[] | ((job: PgCronJob) => boolean);\n /** Put before every job name, to keep them apart from your own (\"db:\"). Also keeps run ids apart. */\n prefix?: string;\n /**\n * The CronWatch name for a job. Default its jobname with anything other\n * than letters, digits, \".\", \"_\", \":\" and \"-\" turned into \"-\", or\n * \"pg_cron:<jobid>\" when it has none. The prefix goes in front either way.\n */\n jobName?: (job: PgCronJob) => string;\n /** Grace, timeout, maxDuration, expect and the rest, for every job or per job. The schedule always comes from pg_cron. */\n options?: Omit<JobOptions, \"schedule\" | \"timezone\"> | ((job: PgCronJob) => Omit<JobOptions, \"schedule\" | \"timezone\">);\n /**\n * The timezone pg_cron reads its cron expressions in. Default the server's\n * cron.timezone, read from pg_settings, which shows it only to roles with\n * pg_read_all_settings; UTC (pg_cron's default) is assumed when it cannot\n * be read.\n */\n timezone?: string;\n}\n\n/** How many of a job's newest runs are copied, without alerting, the first time it is seen. */\nconst BACKFILL = 20;\n/** Run details read per query, and the most read in one sync. */\nconst PAGE = 500;\nconst MAX_PAGES = 10;\n/**\n * How long a run pg_cron has queued but not started (no start_time yet) is\n * waited for. After that it is copied as running from when it was first\n * seen, so a run that never starts is marked stuck like any other.\n */\nexport const PG_CRON_HOLD_MS = 10 * 60_000;\n\nconst JOBS_SQL = `SELECT jobid, jobname, schedule, database, username, active FROM cron.job ORDER BY jobid`;\n// pg_settings has no row for a setting the role may not read, where\n// current_setting() raises an error that would abort the caller's transaction.\nconst SETTING_SQL = `SELECT setting FROM pg_settings WHERE name = $1`;\nconst COLUMNS = `d.runid, d.jobid, d.status, d.return_message, d.start_time, d.end_time`;\n// Every tracked job's runs after its cursor, and any run still open here, whatever its job.\nconst RUNS_SQL = `SELECT ${COLUMNS}\n FROM cron.job_run_details d\n LEFT JOIN unnest($1::bigint[], $2::bigint[]) AS c(jobid, after) ON d.jobid = c.jobid\n WHERE d.runid > c.after OR d.runid = ANY($3::bigint[])\n ORDER BY d.runid LIMIT ${PAGE}`;\nconst NEWEST_SQL = `SELECT ${COLUMNS} FROM cron.job_run_details d WHERE d.jobid = $1 ORDER BY d.runid DESC LIMIT ${BACKFILL}`;\n\ninterface DetailRow {\n runid: number | string;\n jobid: number | string;\n status: string | null;\n return_message: string | null;\n start_time: Date | string | null;\n end_time: Date | string | null;\n}\n\nconst finishedStatus = (status: string | null) => status === \"succeeded\" || status === \"failed\";\n\n/**\n * pg_cron takes a cron expression, with \"$\" for the last day of the month,\n * or \"N seconds\" for 1 to 59 seconds. Returns the CronWatch schedule, or\n * null for one that has no cadence to watch. pg_cron reads only the first\n * five fields of an expression and ignores the rest, so only those are kept\n * (a sixth would otherwise be read as seconds).\n */\nexport function pgCronSchedule(schedule: string): string | null {\n const text = schedule.trim();\n const seconds = /^(\\d+)\\s*seconds?$/i.exec(text);\n if (seconds) return `every ${Number(seconds[1])}s`;\n if (/^@reboot$/i.test(text)) return null;\n const fields = text.split(/\\s+/);\n if (fields.length > 5 && !fields[0]!.startsWith(\"@\")) fields.length = 5;\n if (fields.length === 5 && fields[2]!.includes(\"$\")) fields[2] = fields[2]!.replace(/\\$/g, \"L\");\n return fields.join(\" \");\n}\n\n/** The default CronWatch name for a pg_cron job, before the prefix. */\nexport function pgCronJobName(job: Pick<PgCronJob, \"jobid\" | \"jobname\">): string {\n const cleaned = (job.jobname ?? \"\").replace(/[^A-Za-z0-9._:-]+/g, \"-\").replace(/^[^A-Za-z0-9]+/, \"\").slice(0, 100);\n return cleaned || `pg_cron:${job.jobid}`;\n}\n\n/**\n * A row of cron.job_run_details as a CronWatch run, or null for one that has\n * not started (no start_time, not finished). A finished row with no\n * start_time (pg_cron writes these for runs a server restart cut off,\n * \"server restarted\") starts at its end_time, else at `fallbackAt` (the\n * reader passes the job's newest run's start, or now).\n */\nexport function pgCronRun(row: DetailRow, job: string, idPrefix: string, fallbackAt: number = Date.now()): Run | null {\n const finishedAt = row.end_time === null ? null : new Date(row.end_time).getTime();\n const done = finishedStatus(row.status);\n if (row.start_time === null && !done) return null;\n const startedAt = row.start_time !== null ? new Date(row.start_time).getTime() : (finishedAt ?? fallbackAt);\n const message = row.return_message === null ? null : row.return_message.trim() || null;\n const status: Run[\"status\"] = row.status === \"succeeded\" ? \"ok\" : row.status === \"failed\" ? \"failed\" : \"running\";\n const end = done ? Math.max(startedAt, finishedAt ?? startedAt) : null;\n return {\n id: `${idPrefix}${row.runid}`,\n job,\n status,\n startedAt,\n finishedAt: end,\n durationMs: end === null ? null : end - startedAt,\n error: status === \"failed\" ? (message ?? \"pg_cron reported the run as failed\") : null,\n output: status === \"ok\" ? message : null,\n metrics: {},\n trigger: \"pg_cron\",\n };\n}\n\n/** The options of a stored definition that can be declared again, without its schedule. */\nfunction unscheduled(definition: StoredJobDefinition | JobOptions): JobOptions {\n const { description, tags, grace, timeout, maxDuration, budget, failuresBeforeAlert } = definition;\n const out: JobOptions = {};\n if (description !== undefined) out.description = description;\n if (tags !== undefined) out.tags = tags;\n if (grace !== undefined) out.grace = grace;\n if (timeout !== undefined) out.timeout = timeout;\n if (maxDuration !== undefined) out.maxDuration = maxDuration;\n if (budget !== undefined) out.budget = budget;\n if (failuresBeforeAlert !== undefined) out.failuresBeforeAlert = failuresBeforeAlert;\n return out;\n}\n\nconst keyOf = (definition: JobOptions) =>\n JSON.stringify(definition, (_k, v: unknown) => (v instanceof RegExp || typeof v === \"function\" ? String(v) : v));\n\n/**\n * Watches pg_cron jobs, which run inside Postgres where nothing can wrap\n * them. As a source, on every check it reads cron.job and declares each job\n * with its schedule, then copies new rows of cron.job_run_details in as runs\n * (ids \"pgcron:<runid>\"), so the usual evaluation raises missed, failed,\n * stuck and slow alerts.\n *\n * A job that is renamed, unscheduled or no longer picked keeps its old name's\n * runs and history, and that name is declared again without a schedule, so\n * it is never reported missed. Its description says why.\n *\n * cronwatch({ store: postgres({ pool }), sources: [pgCron(pool)] }).start();\n */\nexport function pgCron(db: Queryable, options: PgCronOptions = {}): Source {\n const prefix = options.prefix ?? \"\";\n const idPrefix = `pgcron:${prefix}`;\n /** The newest runid read for each jobid, once known. */\n const cursors = new Map<number, number>();\n /** The start of the newest run copied for each jobid: where a restart row with no times is put. */\n const lastAt = new Map<number, number>();\n /** Runs copied while still going, by runid, with their job: read again until they finish, even once a check marks them timeout. */\n const pending = new Map<number, string>();\n /** Runs read before they started, by runid, with when they were first seen. */\n const held = new Map<number, number>();\n /** Each job's name and definition as last declared, by jobid. */\n let known = new Map<number, { name: string; definition: JobOptions }>();\n /** The last definition declared for each name, so an unchanged job is not declared again. */\n const declared = new Map<string, string>();\n /** Names declared again without a schedule by retire(), whose open runs are still read. */\n const retired = new Set<string>();\n let scanned = false;\n const warned = new Set<string>();\n\n const warnOnce = (host: SourceHost, key: string, message: string) => {\n if (warned.has(key)) return;\n warned.add(key);\n host.onError(new Error(message), \"source pg_cron\");\n };\n\n const picks = (job: PgCronJob): boolean => {\n const { jobs } = options;\n if (jobs === undefined) return true;\n if (typeof jobs === \"function\") return jobs(job);\n return jobs.some((j) => (typeof j === \"number\" ? j === job.jobid : j === job.jobname));\n };\n\n const setting = async (name: string): Promise<string | null> => {\n try {\n const { rows } = await db.query(SETTING_SQL, [name]);\n return (rows[0]?.setting as string | null | undefined) ?? null;\n } catch {\n return null;\n }\n };\n\n const runIdOf = (id: string): number | null => {\n if (!id.startsWith(idPrefix)) return null;\n const n = Number(id.slice(idPrefix.length));\n return Number.isSafeInteger(n) ? n : null;\n };\n\n /** Declares a name this source no longer uses for any job again, without its schedule. */\n const retire = (host: SourceHost, name: string, definition: StoredJobDefinition | JobOptions, why: string) => {\n const base = unscheduled(definition);\n const next: JobOptions = { ...base, description: `${base.description ?? \"pg_cron job\"} (${why})` };\n try {\n host.job(name, next);\n declared.set(name, keyOf(next));\n retired.add(name);\n } catch (e) {\n host.onError(e, `source pg_cron: job ${name}`);\n }\n };\n\n return {\n name: \"pg_cron\",\n async sync(host) {\n const now = host.now();\n let timezone = options.timezone;\n if (!timezone) {\n const tz = await setting(\"cron.timezone\");\n if (tz === null) warnOnce(host, \"tz\", \"could not read cron.timezone; assuming UTC. Grant pg_read_all_settings or pass pgCron(pool, { timezone }).\");\n timezone = tz === null || /^(gmt|utc|z)$/i.test(tz) ? \"UTC\" : tz;\n }\n const logRun = await setting(\"cron.log_run\");\n const recording = logRun !== \"off\";\n if (!recording) warnOnce(host, \"log_run\", \"cron.log_run is off, so pg_cron records no runs: jobs are watched without their schedules and no run can fail. Turn it on to watch them.\");\n\n const { rows } = await db.query(JOBS_SQL);\n if (rows.length === 0) {\n warnOnce(host, \"empty\", \"cron.job shows no jobs. pg_cron's row level security shows a role only the jobs it scheduled: connect as that role, or give this one BYPASSRLS.\");\n }\n const all = (rows as PgCronJob[]).map((r) => ({ ...r, jobid: Number(r.jobid) }));\n const jobs = all.filter(picks);\n\n // Declare each job. A paused one (active = false) keeps its failures but loses its schedule, so it is not missed.\n const names = new Map<number, string>();\n const definitions = new Map<number, JobOptions>();\n const used = new Set<string>();\n for (const job of jobs) {\n let name = prefix + (options.jobName ? options.jobName(job) : pgCronJobName(job));\n if (used.has(name)) name = `${name}:${job.jobid}`;\n used.add(name);\n const extra = typeof options.options === \"function\" ? options.options(job) : (options.options ?? {});\n const schedule = job.active && recording ? pgCronSchedule(job.schedule) : null;\n let definition: JobOptions = {\n description: `pg_cron job ${job.jobid} in ${job.database} as ${job.username}${job.active ? \"\" : \" (paused)\"}`,\n tags: [\"pg_cron\"],\n ...extra,\n ...(schedule ? { schedule, timezone } : {}),\n };\n try {\n if (declared.get(name) !== keyOf(definition)) {\n const key = keyOf(definition);\n try {\n host.job(name, definition);\n } catch (e) {\n if (!schedule) throw e;\n // A schedule CronWatch cannot read: watch the runs, not the cadence.\n host.onError(new Error(`pg_cron job ${job.jobid}: ${(e as Error).message}; watching it without a schedule`), \"source pg_cron\");\n const { schedule: _s, timezone: _t, ...rest } = definition;\n definition = rest;\n host.job(name, rest);\n }\n declared.set(name, key);\n }\n names.set(job.jobid, name);\n definitions.set(job.jobid, definition);\n } catch (e) {\n host.onError(e, `source pg_cron: job ${job.jobid}`);\n }\n }\n\n // A name this source used for a job that has since been renamed, unscheduled or dropped from `jobs`.\n const inUse = new Set(names.values());\n for (const name of inUse) retired.delete(name);\n for (const [jobid, previous] of known) {\n if (inUse.has(previous.name)) continue;\n const renamed = names.get(jobid);\n retire(host, previous.name, previous.definition, renamed ? `renamed to ${renamed}` : \"no longer watched\");\n }\n known = new Map([...names].map(([jobid, name]) => [jobid, { name, definition: definitions.get(jobid)! }]));\n // Once per process, the same for names left scheduled in the store while no process was watching.\n if (!scanned && rows.length > 0) {\n scanned = true;\n try {\n const visible = new Set(all.map((j) => j.jobid));\n for (const stored of await host.store.listJobs()) {\n const def = stored.definition;\n if (!stored.name.startsWith(prefix) || inUse.has(stored.name) || !def.schedule || !(def.tags ?? []).includes(\"pg_cron\")) continue;\n const match = /^pg_cron job (\\d+) in /.exec(def.description ?? \"\");\n if (!match) continue;\n const jobid = Number(match[1]);\n const current = names.get(jobid);\n if (!visible.has(jobid)) retire(host, stored.name, def, \"no longer in cron.job\");\n // Another pg_cron source's name for the same job ends the same way: that one is left alone.\n else if (current && !stored.name.endsWith(current.slice(prefix.length))) retire(host, stored.name, def, `renamed to ${current}`);\n }\n } catch (e) {\n host.onError(e, \"source pg_cron\");\n }\n }\n if (!recording || names.size === 0) return [];\n\n const alerts: Alert[] = [];\n /** Copies one row. A row that cannot be recorded is reported and skipped; it never stops the others. */\n const record = async (row: DetailRow, evaluate: boolean): Promise<void> => {\n const runid = Number(row.runid);\n const jobid = Number(row.jobid);\n const name = pending.get(runid) ?? names.get(jobid);\n if (!name) {\n held.delete(runid);\n return;\n }\n let run: Run | null;\n if (row.start_time === null && !finishedStatus(row.status)) {\n const since = held.get(runid) ?? now;\n if (now - since < PG_CRON_HOLD_MS) {\n held.set(runid, since);\n return;\n }\n run = pgCronRun({ ...row, start_time: new Date(since) }, name, idPrefix);\n } else {\n run = pgCronRun(row, name, idPrefix, lastAt.get(jobid) ?? now);\n }\n held.delete(runid);\n if (!run) return;\n try {\n alerts.push(...(await host.recordRun(run, { evaluate })));\n } catch (e) {\n host.onError(e, `source pg_cron: run ${runid}`);\n return;\n }\n if (run.status === \"running\") pending.set(runid, name);\n else pending.delete(runid);\n if (run.startedAt > (lastAt.get(jobid) ?? -Infinity)) lastAt.set(jobid, run.startedAt);\n };\n\n // Where each job left off. Found from the store the first time, so a restart carries on.\n for (const [jobid, name] of names) {\n if (cursors.has(jobid)) continue;\n const ours = (await host.store.listRuns(name, BACKFILL)).filter((r) => runIdOf(r.id) !== null);\n if (ours.length > 0) {\n cursors.set(jobid, Math.max(...ours.map((r) => runIdOf(r.id)!)));\n lastAt.set(jobid, Math.max(...ours.map((r) => r.startedAt)));\n for (const r of ours) if (r.status === \"running\" || r.status === \"timeout\") pending.set(runIdOf(r.id)!, r.job);\n continue;\n }\n // First sight: copy recent history quietly, and judge only from the newest finished run on.\n // The cursor goes to the newest row read, whatever is held, so history is never judged later.\n const { rows: newest } = await db.query(NEWEST_SQL, [jobid]);\n const ordered = (newest as DetailRow[]).slice().reverse();\n let lastFinished = -1;\n ordered.forEach((r, i) => { if (finishedStatus(r.status)) lastFinished = i; });\n for (const [i, row] of ordered.entries()) {\n // Already copied under another name (the job was renamed while no process watched): left there.\n if (await host.store.getRun(`${idPrefix}${row.runid}`)) continue;\n await record(row, i >= lastFinished);\n }\n cursors.set(jobid, ordered.length > 0 ? Number(ordered[ordered.length - 1]!.runid) : 0);\n }\n\n // New runs, runs copied while still going (or since marked timeout), and runs not yet started.\n const watched = new Set([...names.values(), ...retired]);\n for (const run of await host.store.runningRuns()) {\n const id = runIdOf(run.id);\n if (id !== null && watched.has(run.job)) pending.set(id, run.job);\n }\n const open = new Set([...pending.keys(), ...held.keys()]);\n let complete = false;\n for (let page = 0; page < MAX_PAGES; page++) {\n const jobids = [...names.keys()];\n const { rows: details } = await db.query(RUNS_SQL, [jobids, jobids.map((j) => cursors.get(j) ?? 0), [...open]]);\n for (const row of details as DetailRow[]) {\n const jobid = Number(row.jobid);\n const runid = Number(row.runid);\n open.delete(runid);\n await record(row, true);\n // Held or not, the cursor moves on: a held run is read again by its runid.\n if (names.has(jobid) && runid > (cursors.get(jobid) ?? 0)) cursors.set(jobid, runid);\n }\n if (details.length < PAGE) {\n complete = true;\n break;\n }\n }\n // Every row was read and these were not among them: pg_cron no longer has them.\n if (complete) {\n for (const runid of open) {\n pending.delete(runid);\n held.delete(runid);\n }\n }\n return alerts;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { S as Source } from './client-B7bqnkI5.cjs';
|
|
2
|
+
import { i as JobOptions, R as Run } from './types-Ddq1MUPL.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Anything with a pg-style query(): a `pg` Pool or Client, or a wrapper
|
|
6
|
+
* around another driver that answers with `{ rows }`.
|
|
7
|
+
*/
|
|
8
|
+
interface Queryable {
|
|
9
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
10
|
+
rows: any[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
/** A row of cron.job. */
|
|
14
|
+
interface PgCronJob {
|
|
15
|
+
jobid: number;
|
|
16
|
+
jobname: string | null;
|
|
17
|
+
schedule: string;
|
|
18
|
+
database: string;
|
|
19
|
+
username: string;
|
|
20
|
+
active: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface PgCronOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Which jobs to watch: names or ids, or a function that picks them. Default
|
|
25
|
+
* every job the role can see.
|
|
26
|
+
*/
|
|
27
|
+
jobs?: (string | number)[] | ((job: PgCronJob) => boolean);
|
|
28
|
+
/** Put before every job name, to keep them apart from your own ("db:"). Also keeps run ids apart. */
|
|
29
|
+
prefix?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The CronWatch name for a job. Default its jobname with anything other
|
|
32
|
+
* than letters, digits, ".", "_", ":" and "-" turned into "-", or
|
|
33
|
+
* "pg_cron:<jobid>" when it has none. The prefix goes in front either way.
|
|
34
|
+
*/
|
|
35
|
+
jobName?: (job: PgCronJob) => string;
|
|
36
|
+
/** Grace, timeout, maxDuration, expect and the rest, for every job or per job. The schedule always comes from pg_cron. */
|
|
37
|
+
options?: Omit<JobOptions, "schedule" | "timezone"> | ((job: PgCronJob) => Omit<JobOptions, "schedule" | "timezone">);
|
|
38
|
+
/**
|
|
39
|
+
* The timezone pg_cron reads its cron expressions in. Default the server's
|
|
40
|
+
* cron.timezone, read from pg_settings, which shows it only to roles with
|
|
41
|
+
* pg_read_all_settings; UTC (pg_cron's default) is assumed when it cannot
|
|
42
|
+
* be read.
|
|
43
|
+
*/
|
|
44
|
+
timezone?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* How long a run pg_cron has queued but not started (no start_time yet) is
|
|
48
|
+
* waited for. After that it is copied as running from when it was first
|
|
49
|
+
* seen, so a run that never starts is marked stuck like any other.
|
|
50
|
+
*/
|
|
51
|
+
declare const PG_CRON_HOLD_MS: number;
|
|
52
|
+
interface DetailRow {
|
|
53
|
+
runid: number | string;
|
|
54
|
+
jobid: number | string;
|
|
55
|
+
status: string | null;
|
|
56
|
+
return_message: string | null;
|
|
57
|
+
start_time: Date | string | null;
|
|
58
|
+
end_time: Date | string | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* pg_cron takes a cron expression, with "$" for the last day of the month,
|
|
62
|
+
* or "N seconds" for 1 to 59 seconds. Returns the CronWatch schedule, or
|
|
63
|
+
* null for one that has no cadence to watch. pg_cron reads only the first
|
|
64
|
+
* five fields of an expression and ignores the rest, so only those are kept
|
|
65
|
+
* (a sixth would otherwise be read as seconds).
|
|
66
|
+
*/
|
|
67
|
+
declare function pgCronSchedule(schedule: string): string | null;
|
|
68
|
+
/** The default CronWatch name for a pg_cron job, before the prefix. */
|
|
69
|
+
declare function pgCronJobName(job: Pick<PgCronJob, "jobid" | "jobname">): string;
|
|
70
|
+
/**
|
|
71
|
+
* A row of cron.job_run_details as a CronWatch run, or null for one that has
|
|
72
|
+
* not started (no start_time, not finished). A finished row with no
|
|
73
|
+
* start_time (pg_cron writes these for runs a server restart cut off,
|
|
74
|
+
* "server restarted") starts at its end_time, else at `fallbackAt` (the
|
|
75
|
+
* reader passes the job's newest run's start, or now).
|
|
76
|
+
*/
|
|
77
|
+
declare function pgCronRun(row: DetailRow, job: string, idPrefix: string, fallbackAt?: number): Run | null;
|
|
78
|
+
/**
|
|
79
|
+
* Watches pg_cron jobs, which run inside Postgres where nothing can wrap
|
|
80
|
+
* them. As a source, on every check it reads cron.job and declares each job
|
|
81
|
+
* with its schedule, then copies new rows of cron.job_run_details in as runs
|
|
82
|
+
* (ids "pgcron:<runid>"), so the usual evaluation raises missed, failed,
|
|
83
|
+
* stuck and slow alerts.
|
|
84
|
+
*
|
|
85
|
+
* A job that is renamed, unscheduled or no longer picked keeps its old name's
|
|
86
|
+
* runs and history, and that name is declared again without a schedule, so
|
|
87
|
+
* it is never reported missed. Its description says why.
|
|
88
|
+
*
|
|
89
|
+
* cronwatch({ store: postgres({ pool }), sources: [pgCron(pool)] }).start();
|
|
90
|
+
*/
|
|
91
|
+
declare function pgCron(db: Queryable, options?: PgCronOptions): Source;
|
|
92
|
+
|
|
93
|
+
export { PG_CRON_HOLD_MS, type PgCronJob, type PgCronOptions, type Queryable, pgCron, pgCronJobName, pgCronRun, pgCronSchedule };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { S as Source } from './client-pmqH3apS.js';
|
|
2
|
+
import { i as JobOptions, R as Run } from './types-Ddq1MUPL.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Anything with a pg-style query(): a `pg` Pool or Client, or a wrapper
|
|
6
|
+
* around another driver that answers with `{ rows }`.
|
|
7
|
+
*/
|
|
8
|
+
interface Queryable {
|
|
9
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
10
|
+
rows: any[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
/** A row of cron.job. */
|
|
14
|
+
interface PgCronJob {
|
|
15
|
+
jobid: number;
|
|
16
|
+
jobname: string | null;
|
|
17
|
+
schedule: string;
|
|
18
|
+
database: string;
|
|
19
|
+
username: string;
|
|
20
|
+
active: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface PgCronOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Which jobs to watch: names or ids, or a function that picks them. Default
|
|
25
|
+
* every job the role can see.
|
|
26
|
+
*/
|
|
27
|
+
jobs?: (string | number)[] | ((job: PgCronJob) => boolean);
|
|
28
|
+
/** Put before every job name, to keep them apart from your own ("db:"). Also keeps run ids apart. */
|
|
29
|
+
prefix?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The CronWatch name for a job. Default its jobname with anything other
|
|
32
|
+
* than letters, digits, ".", "_", ":" and "-" turned into "-", or
|
|
33
|
+
* "pg_cron:<jobid>" when it has none. The prefix goes in front either way.
|
|
34
|
+
*/
|
|
35
|
+
jobName?: (job: PgCronJob) => string;
|
|
36
|
+
/** Grace, timeout, maxDuration, expect and the rest, for every job or per job. The schedule always comes from pg_cron. */
|
|
37
|
+
options?: Omit<JobOptions, "schedule" | "timezone"> | ((job: PgCronJob) => Omit<JobOptions, "schedule" | "timezone">);
|
|
38
|
+
/**
|
|
39
|
+
* The timezone pg_cron reads its cron expressions in. Default the server's
|
|
40
|
+
* cron.timezone, read from pg_settings, which shows it only to roles with
|
|
41
|
+
* pg_read_all_settings; UTC (pg_cron's default) is assumed when it cannot
|
|
42
|
+
* be read.
|
|
43
|
+
*/
|
|
44
|
+
timezone?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* How long a run pg_cron has queued but not started (no start_time yet) is
|
|
48
|
+
* waited for. After that it is copied as running from when it was first
|
|
49
|
+
* seen, so a run that never starts is marked stuck like any other.
|
|
50
|
+
*/
|
|
51
|
+
declare const PG_CRON_HOLD_MS: number;
|
|
52
|
+
interface DetailRow {
|
|
53
|
+
runid: number | string;
|
|
54
|
+
jobid: number | string;
|
|
55
|
+
status: string | null;
|
|
56
|
+
return_message: string | null;
|
|
57
|
+
start_time: Date | string | null;
|
|
58
|
+
end_time: Date | string | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* pg_cron takes a cron expression, with "$" for the last day of the month,
|
|
62
|
+
* or "N seconds" for 1 to 59 seconds. Returns the CronWatch schedule, or
|
|
63
|
+
* null for one that has no cadence to watch. pg_cron reads only the first
|
|
64
|
+
* five fields of an expression and ignores the rest, so only those are kept
|
|
65
|
+
* (a sixth would otherwise be read as seconds).
|
|
66
|
+
*/
|
|
67
|
+
declare function pgCronSchedule(schedule: string): string | null;
|
|
68
|
+
/** The default CronWatch name for a pg_cron job, before the prefix. */
|
|
69
|
+
declare function pgCronJobName(job: Pick<PgCronJob, "jobid" | "jobname">): string;
|
|
70
|
+
/**
|
|
71
|
+
* A row of cron.job_run_details as a CronWatch run, or null for one that has
|
|
72
|
+
* not started (no start_time, not finished). A finished row with no
|
|
73
|
+
* start_time (pg_cron writes these for runs a server restart cut off,
|
|
74
|
+
* "server restarted") starts at its end_time, else at `fallbackAt` (the
|
|
75
|
+
* reader passes the job's newest run's start, or now).
|
|
76
|
+
*/
|
|
77
|
+
declare function pgCronRun(row: DetailRow, job: string, idPrefix: string, fallbackAt?: number): Run | null;
|
|
78
|
+
/**
|
|
79
|
+
* Watches pg_cron jobs, which run inside Postgres where nothing can wrap
|
|
80
|
+
* them. As a source, on every check it reads cron.job and declares each job
|
|
81
|
+
* with its schedule, then copies new rows of cron.job_run_details in as runs
|
|
82
|
+
* (ids "pgcron:<runid>"), so the usual evaluation raises missed, failed,
|
|
83
|
+
* stuck and slow alerts.
|
|
84
|
+
*
|
|
85
|
+
* A job that is renamed, unscheduled or no longer picked keeps its old name's
|
|
86
|
+
* runs and history, and that name is declared again without a schedule, so
|
|
87
|
+
* it is never reported missed. Its description says why.
|
|
88
|
+
*
|
|
89
|
+
* cronwatch({ store: postgres({ pool }), sources: [pgCron(pool)] }).start();
|
|
90
|
+
*/
|
|
91
|
+
declare function pgCron(db: Queryable, options?: PgCronOptions): Source;
|
|
92
|
+
|
|
93
|
+
export { PG_CRON_HOLD_MS, type PgCronJob, type PgCronOptions, type Queryable, pgCron, pgCronJobName, pgCronRun, pgCronSchedule };
|