@cronwatch/sdk 0.6.0 → 0.7.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 (56) hide show
  1. package/README.md +2 -0
  2. package/dist/anthropic.cjs.map +1 -1
  3. package/dist/anthropic.d.cts +1 -1
  4. package/dist/anthropic.d.ts +1 -1
  5. package/dist/anthropic.js.map +1 -1
  6. package/dist/bugsnag.d.cts +1 -1
  7. package/dist/bugsnag.d.ts +1 -1
  8. package/dist/{client-B7bqnkI5.d.cts → client-B5hM4l-D.d.ts} +5 -2
  9. package/dist/{client-pmqH3apS.d.ts → client-Di5HAWQz.d.cts} +5 -2
  10. package/dist/d1.d.cts +1 -1
  11. package/dist/d1.d.ts +1 -1
  12. package/dist/datadog.d.cts +1 -1
  13. package/dist/datadog.d.ts +1 -1
  14. package/dist/discord.d.cts +1 -1
  15. package/dist/discord.d.ts +1 -1
  16. package/dist/{email-_H0faCh3.d.ts → email-Ciq7GRn4.d.ts} +1 -1
  17. package/dist/{email-BJ-z_epD.d.cts → email-DX6gkd5H.d.cts} +1 -1
  18. package/dist/honeybadger.d.cts +1 -1
  19. package/dist/honeybadger.d.ts +1 -1
  20. package/dist/index.cjs +60 -21
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +4 -4
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.js +60 -21
  25. package/dist/index.js.map +1 -1
  26. package/dist/mailgun.d.cts +2 -2
  27. package/dist/mailgun.d.ts +2 -2
  28. package/dist/newrelic.d.cts +1 -1
  29. package/dist/newrelic.d.ts +1 -1
  30. package/dist/pg-cron.d.cts +2 -2
  31. package/dist/pg-cron.d.ts +2 -2
  32. package/dist/postgres.d.cts +1 -1
  33. package/dist/postgres.d.ts +1 -1
  34. package/dist/postmark.d.cts +2 -2
  35. package/dist/postmark.d.ts +2 -2
  36. package/dist/resend.d.cts +2 -2
  37. package/dist/resend.d.ts +2 -2
  38. package/dist/rollbar.d.cts +1 -1
  39. package/dist/rollbar.d.ts +1 -1
  40. package/dist/sendgrid.d.cts +2 -2
  41. package/dist/sendgrid.d.ts +2 -2
  42. package/dist/sentry.d.cts +1 -1
  43. package/dist/sentry.d.ts +1 -1
  44. package/dist/ses.d.cts +2 -2
  45. package/dist/ses.d.ts +2 -2
  46. package/dist/slack.d.cts +1 -1
  47. package/dist/slack.d.ts +1 -1
  48. package/dist/sqlite.d.cts +1 -1
  49. package/dist/sqlite.d.ts +1 -1
  50. package/dist/twilio.d.cts +1 -1
  51. package/dist/twilio.d.ts +1 -1
  52. package/dist/{types-Ddq1MUPL.d.cts → types-BjBbCGeJ.d.cts} +4 -1
  53. package/dist/{types-Ddq1MUPL.d.ts → types-BjBbCGeJ.d.ts} +4 -1
  54. package/dist/webhook.d.cts +1 -1
  55. package/dist/webhook.d.ts +1 -1
  56. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as CronWatchOptions, a as CronWatch } from './client-B7bqnkI5.cjs';
2
- export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-B7bqnkI5.cjs';
3
- import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Ddq1MUPL.cjs';
4
- export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-Ddq1MUPL.cjs';
1
+ import { C as CronWatchOptions, a as CronWatch } from './client-Di5HAWQz.cjs';
2
+ export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-Di5HAWQz.cjs';
3
+ import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-BjBbCGeJ.cjs';
4
+ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-BjBbCGeJ.cjs';
5
5
 
6
6
  /**
7
7
  * Keeps everything in process memory. The default when no store is given,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as CronWatchOptions, a as CronWatch } from './client-pmqH3apS.js';
2
- export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-pmqH3apS.js';
3
- import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Ddq1MUPL.js';
4
- export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-Ddq1MUPL.js';
1
+ import { C as CronWatchOptions, a as CronWatch } from './client-B5hM4l-D.js';
2
+ export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-B5hM4l-D.js';
3
+ import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-BjBbCGeJ.js';
4
+ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-BjBbCGeJ.js';
5
5
 
6
6
  /**
7
7
  * Keeps everything in process memory. The default when no store is given,
package/dist/index.js CHANGED
@@ -9,11 +9,14 @@ var UNIT_MS = {
9
9
  d: 864e5,
10
10
  w: 6048e5
11
11
  };
12
+ var MAX_LENGTH = 64;
13
+ var QUOTED = 32;
12
14
  function parseDuration(value, label = "duration") {
13
15
  if (typeof value === "number") {
14
16
  if (!Number.isFinite(value) || value < 0) throw new Error(`${label} must be a non-negative number of milliseconds`);
15
17
  return value;
16
18
  }
19
+ if (value.length > MAX_LENGTH) tooLong(value, label);
17
20
  const text = value.trim().toLowerCase();
18
21
  if (text === "") throw new Error(`${label} is empty`);
19
22
  const re = /(\d+(?:\.\d+)?)\s*(ms|s|m|h|d|w)/g;
@@ -29,6 +32,16 @@ function parseDuration(value, label = "duration") {
29
32
  }
30
33
  return Math.round(total);
31
34
  }
35
+ function tooLong(value, label) {
36
+ let count = 0;
37
+ let head = "";
38
+ for (const char of value) {
39
+ if (count < QUOTED) head += char;
40
+ if (++count > MAX_LENGTH) {
41
+ throw new Error(`${label} "${head}..." is too long for a duration (more than ${MAX_LENGTH} characters)`);
42
+ }
43
+ }
44
+ }
32
45
  function formatDuration(ms) {
33
46
  if (!Number.isFinite(ms)) return "?";
34
47
  if (ms < 1e3) return `${Math.round(ms)}ms`;
@@ -155,15 +168,15 @@ function runCovers(startedAt, dueAt, followingAt = null) {
155
168
  }
156
169
  function inSpringForwardGap(parsed, startedAt, fireAt) {
157
170
  const LOOKBACK = 3 * 36e5;
158
- const after = utcOffset(fireAt, parsed.timezone);
171
+ const after2 = utcOffset(fireAt, parsed.timezone);
159
172
  const before = utcOffset(fireAt - LOOKBACK, parsed.timezone);
160
- const gap = after - before;
173
+ const gap = after2 - before;
161
174
  if (gap <= 0) return false;
162
175
  let lo = fireAt - LOOKBACK;
163
176
  let hi = fireAt;
164
177
  while (hi - lo > 6e4) {
165
178
  const mid = lo + Math.floor((hi - lo) / 2);
166
- if (utcOffset(mid, parsed.timezone) === after) hi = mid;
179
+ if (utcOffset(mid, parsed.timezone) === after2) hi = mid;
167
180
  else lo = mid;
168
181
  }
169
182
  const jumpAt = Math.floor(hi / 6e4) * 6e4;
@@ -501,8 +514,8 @@ ${out}`);
501
514
  break;
502
515
  }
503
516
  title = `${name} recovered`;
504
- const after = draft.details.after.map((c) => c.replace("_", " ")).join(", ");
505
- lines.push(`A run ${run ? when(run.startedAt, now) : "just now"} succeeded${after ? ` after: ${after}` : ""}.`);
517
+ const after2 = draft.details.after.map((c) => c.replace("_", " ")).join(", ");
518
+ lines.push(`A run ${run ? when(run.startedAt, now) : "just now"} succeeded${after2 ? ` after: ${after2}` : ""}.`);
506
519
  if (run?.durationMs !== null && run?.durationMs !== void 0) lines.push(`Ran ${formatDuration(run.durationMs)}.`);
507
520
  break;
508
521
  }
@@ -689,6 +702,9 @@ function checkExpectation(expect, output) {
689
702
  function escapeHtml(value) {
690
703
  return String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
691
704
  }
705
+ function escapeName(value) {
706
+ return escapeHtml(value).replace(/([_:./-]+)(?=[^_:./-])/g, "$1<wbr>");
707
+ }
692
708
 
693
709
  // src/routes/icons.ts
694
710
  var ICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect width="40" height="40" rx="10" fill="#141417"/><rect x="0.5" y="0.5" width="39" height="39" rx="9.5" fill="none" stroke="#2a2a2f"/><circle cx="20" cy="20" r="10.5" fill="none" stroke="#f3f3f1" stroke-width="2"/><path d="M20 12.5V20h6" fill="none" stroke="#f3f3f1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
@@ -1035,7 +1051,7 @@ function dayTimeline(lanes, span, base, total) {
1035
1051
  const { job } = input;
1036
1052
  const parts = lane(input, span, { nowInLane: false, label: job.name });
1037
1053
  return {
1038
- html: `<li class="lane"><div class="who"><i class="sq ${stateClass(job)}" aria-hidden="true"></i><a class="name" href="${escapeHtml(base)}/jobs/${encodeURIComponent(job.name)}">${escapeHtml(job.name)}</a><span class="sched">${escapeHtml(job.definition.schedule ?? "no schedule")}</span></div><div class="track">${parts.svg}${parts.note}</div></li>`,
1054
+ html: `<li class="lane"><div class="who"><i class="sq ${stateClass(job)}" aria-hidden="true"></i><a class="name" href="${escapeHtml(base)}/jobs/${encodeURIComponent(job.name)}">${escapeName(job.name)}</a><span class="sched">${escapeHtml(job.definition.schedule ?? "no schedule")}</span></div><div class="track">${parts.svg}${parts.note}</div></li>`,
1039
1055
  words: `<li>${escapeHtml(`${job.name} (${job.definition.schedule ?? "no schedule"}): ${parts.words}.`)}</li>`
1040
1056
  };
1041
1057
  });
@@ -1275,7 +1291,7 @@ ${options.refresh ? `<meta http-equiv="refresh" content="${options.refresh}">` :
1275
1291
  }
1276
1292
  function brand(base, crumb) {
1277
1293
  const home = `<a href="${h(base)}/">${MARK}<span>CronWatch</span></a>`;
1278
- return crumb === void 0 ? `<p class="brand">${home}</p>` : `<p class="brand">${home}<span class="slash" aria-hidden="true">/</span><span class="crumb">${h(crumb)}</span></p>`;
1294
+ return crumb === void 0 ? `<p class="brand">${home}</p>` : `<p class="brand">${home}<span class="slash" aria-hidden="true">/</span><span class="crumb">${escapeName(crumb)}</span></p>`;
1279
1295
  }
1280
1296
  var HEALTH = {
1281
1297
  failing: ["bad", "failing"],
@@ -1328,7 +1344,7 @@ function dashboardPage(jobs, runsByJob, now, base, checkedAt, lanes = jobs.slice
1328
1344
  const last = job.lastRun;
1329
1345
  const d = job.definition;
1330
1346
  return `<tr>
1331
- <td class="job"><a class="name" href="${h(base)}/jobs/${encodeURIComponent(job.name)}">${h(job.name)}</a>${d.description ? `<span class="desc">${h(d.description)}</span>` : ""}</td>
1347
+ <td class="job"><a class="name" href="${h(base)}/jobs/${encodeURIComponent(job.name)}">${escapeName(job.name)}</a>${d.description ? `<span class="desc">${h(d.description)}</span>` : ""}</td>
1332
1348
  <td class="health">${healthState(job)}</td>
1333
1349
  <td class="nowrap hide-sm">${d.schedule ? `${h(d.schedule)}${d.timezone ? `<span class="tz">${h(d.timezone)}</span>` : ""}` : `<span class="muted">no schedule</span>`}</td>
1334
1350
  <td class="nowrap last">${last ? `${runState(last)} ${stamp(last.startedAt, now)}${last.durationMs !== null ? `<span class="sub">took ${h(formatDuration(last.durationMs))}</span>` : ""}` : `<span class="muted">never</span>`}</td>
@@ -1400,7 +1416,7 @@ function jobPage(job, runs, now, base, complete = true) {
1400
1416
  <section class="sec intro" aria-label="Job">
1401
1417
  <h2>Job</h2>
1402
1418
  <div>
1403
- <h1 class="jobname">${h(job.name)}</h1>
1419
+ <h1 class="jobname">${escapeName(job.name)}</h1>
1404
1420
  ${d.description ? `<p class="desc">${h(d.description)}</p>` : ""}
1405
1421
  <p class="stateline">${healthState(job)}${why ? `<span class="why">${h(why)}</span>` : ""}</p>
1406
1422
  <div class="actions">
@@ -1477,7 +1493,20 @@ function developmentToken() {
1477
1493
  return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1478
1494
  }
1479
1495
  function developmentSignInLine(origin, base, token) {
1480
- return `[cronwatch] CRONWATCH_TOKEN is not set, so this development server made a token for the dashboard. Sign in: ${origin}${base}/?token=${token}`;
1496
+ const intro = "[cronwatch] CRONWATCH_TOKEN is not set, so this development server made a token for the dashboard. Sign in: ";
1497
+ return origin === null ? `${intro}${base}/?token=${token} on this server (the first request's host is not local, so the link leaves it out)` : `${intro}${origin}${base}/?token=${token}`;
1498
+ }
1499
+ function isLoopbackOrigin(origin) {
1500
+ let host;
1501
+ try {
1502
+ host = new URL(origin).hostname.toLowerCase();
1503
+ } catch {
1504
+ return false;
1505
+ }
1506
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
1507
+ if (host === "[::1]") return true;
1508
+ const octets = /^127\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
1509
+ return octets !== null && octets.slice(1).every((o) => Number(o) <= 255);
1481
1510
  }
1482
1511
  function safeDecode(value) {
1483
1512
  try {
@@ -1582,7 +1611,8 @@ function createRoutes(cw, options = {}) {
1582
1611
  const publicOrigin = originOf(request, url);
1583
1612
  if (generated && !announced) {
1584
1613
  announced = true;
1585
- console.info(developmentSignInLine(publicOrigin, base, token));
1614
+ const shown = fixedOrigin ?? (isLoopbackOrigin(publicOrigin) ? publicOrigin : null);
1615
+ console.info(developmentSignInLine(shown, base, token));
1586
1616
  }
1587
1617
  if (method === "GET" || method === "HEAD") {
1588
1618
  if (path === "/offline") {
@@ -2146,8 +2176,7 @@ var CronWatch = class {
2146
2176
  const started = recorded ? this.updateState(name, (before) => ({ state: onRunStart(before), result: void 0 })).then(() => {
2147
2177
  }, (e) => this.onError(e, `starting ${name}`)) : Promise.resolve();
2148
2178
  const recorder = createRecorder(run);
2149
- const timer = setTimeout(() => recorder.abort(), timeoutMs(definition));
2150
- if (typeof timer === "object" && "unref" in timer) timer.unref();
2179
+ const clearTimer = after(timeoutMs(definition), () => recorder.abort());
2151
2180
  let result;
2152
2181
  let error;
2153
2182
  let threw = false;
@@ -2157,7 +2186,7 @@ var CronWatch = class {
2157
2186
  error = e;
2158
2187
  threw = true;
2159
2188
  } finally {
2160
- clearTimeout(timer);
2189
+ clearTimer();
2161
2190
  }
2162
2191
  const finishedAt = this.now();
2163
2192
  run.finishedAt = finishedAt;
@@ -2712,7 +2741,7 @@ ${id}`;
2712
2741
  */
2713
2742
  start(every = "1m") {
2714
2743
  if (this.timer) return;
2715
- const ms = Math.max(5e3, parseDuration(every, "check interval"));
2744
+ const ms = Math.min(TIMER_MAX_MS, Math.max(5e3, parseDuration(every, "check interval")));
2716
2745
  if (this.deferDelivery && !this.warnedDeferredStart) {
2717
2746
  this.warnedDeferredStart = true;
2718
2747
  console.warn('[cronwatch] start() was called with deliver: "check", so these checks send no alerts. Another process must run checks with deliver: "now" (the default) to send them.');
@@ -2851,20 +2880,30 @@ function checkRunId(job, id, method) {
2851
2880
  throw new Error(`job "${job}": ${method}() cannot take a run id starting with "${RESERVED_RUN_ID_PREFIX}", which the pg_cron source uses for its runs`);
2852
2881
  }
2853
2882
  }
2854
- function joinLines(before, after) {
2855
- if (before === null || before === "") return after;
2856
- if (after === null) return before;
2883
+ function joinLines(before, after2) {
2884
+ if (before === null || before === "") return after2;
2885
+ if (after2 === null) return before;
2857
2886
  return `${before}
2858
- ${after}`;
2887
+ ${after2}`;
2859
2888
  }
2860
- function joinOutput(before, after) {
2861
- const joined = joinLines(before, after);
2889
+ function joinOutput(before, after2) {
2890
+ const joined = joinLines(before, after2);
2862
2891
  return joined === null ? null : capOutput(joined);
2863
2892
  }
2864
2893
  function clampLimit(limit, fallback, min) {
2865
2894
  const n = typeof limit === "number" && Number.isFinite(limit) ? Math.trunc(limit) : fallback;
2866
2895
  return Math.min(500, Math.max(min, n));
2867
2896
  }
2897
+ var TIMER_MAX_MS = 2 ** 31 - 1;
2898
+ function after(ms, fn) {
2899
+ let timer;
2900
+ const arm = (left) => {
2901
+ timer = left > TIMER_MAX_MS ? setTimeout(() => arm(left - TIMER_MAX_MS), TIMER_MAX_MS) : setTimeout(fn, left);
2902
+ if (typeof timer === "object" && "unref" in timer) timer.unref();
2903
+ };
2904
+ arm(ms);
2905
+ return () => clearTimeout(timer);
2906
+ }
2868
2907
  function withTimeout(promise, ms) {
2869
2908
  return new Promise((resolve, reject) => {
2870
2909
  const t = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);