@cronwatch/sdk 0.11.1 → 0.12.1
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 +4 -1
- package/dist/anthropic.d.cts +1 -1
- package/dist/anthropic.d.ts +1 -1
- package/dist/bugsnag.cjs +1 -1
- package/dist/bugsnag.cjs.map +1 -1
- package/dist/bugsnag.d.cts +1 -1
- package/dist/bugsnag.d.ts +1 -1
- package/dist/bugsnag.js +1 -1
- package/dist/bugsnag.js.map +1 -1
- package/dist/{client-CRMfhylq.d.cts → client-BkPMRkDq.d.cts} +1 -1
- package/dist/{client-D2Xk2600.d.ts → client-DySsV5T0.d.ts} +1 -1
- package/dist/d1.d.cts +1 -1
- package/dist/d1.d.ts +1 -1
- package/dist/datadog.cjs +1 -0
- package/dist/datadog.cjs.map +1 -1
- package/dist/datadog.d.cts +1 -1
- package/dist/datadog.d.ts +1 -1
- package/dist/datadog.js +1 -0
- package/dist/datadog.js.map +1 -1
- package/dist/discord.cjs +1 -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 +1 -0
- package/dist/discord.js.map +1 -1
- package/dist/{email-BWuUiCkw.d.ts → email-Bx3ehK0O.d.ts} +1 -1
- package/dist/{email-Dcqe3k8n.d.cts → email-CD6NO8CV.d.cts} +1 -1
- package/dist/honeybadger.cjs +1 -0
- package/dist/honeybadger.cjs.map +1 -1
- package/dist/honeybadger.d.cts +1 -1
- package/dist/honeybadger.d.ts +1 -1
- package/dist/honeybadger.js +1 -0
- package/dist/honeybadger.js.map +1 -1
- package/dist/index.cjs +66 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +66 -6
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs.map +1 -1
- package/dist/mailgun.d.cts +2 -2
- package/dist/mailgun.d.ts +2 -2
- package/dist/mailgun.js.map +1 -1
- package/dist/newrelic.cjs +1 -1
- package/dist/newrelic.cjs.map +1 -1
- package/dist/newrelic.d.cts +1 -1
- package/dist/newrelic.d.ts +1 -1
- package/dist/newrelic.js +1 -1
- package/dist/newrelic.js.map +1 -1
- package/dist/pg-cron.cjs +2 -1
- package/dist/pg-cron.cjs.map +1 -1
- package/dist/pg-cron.d.cts +2 -2
- package/dist/pg-cron.d.ts +2 -2
- package/dist/pg-cron.js +2 -1
- package/dist/pg-cron.js.map +1 -1
- package/dist/postgres.d.cts +1 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postmark.cjs.map +1 -1
- package/dist/postmark.d.cts +2 -2
- package/dist/postmark.d.ts +2 -2
- package/dist/postmark.js.map +1 -1
- package/dist/resend.cjs.map +1 -1
- package/dist/resend.d.cts +2 -2
- package/dist/resend.d.ts +2 -2
- package/dist/resend.js.map +1 -1
- package/dist/rollbar.cjs +1 -1
- package/dist/rollbar.cjs.map +1 -1
- package/dist/rollbar.d.cts +1 -1
- package/dist/rollbar.d.ts +1 -1
- package/dist/rollbar.js +1 -1
- package/dist/rollbar.js.map +1 -1
- package/dist/sendgrid.cjs.map +1 -1
- package/dist/sendgrid.d.cts +2 -2
- package/dist/sendgrid.d.ts +2 -2
- package/dist/sendgrid.js.map +1 -1
- package/dist/sentry.cjs +1 -1
- package/dist/sentry.cjs.map +1 -1
- package/dist/sentry.d.cts +1 -1
- package/dist/sentry.d.ts +1 -1
- package/dist/sentry.js +1 -1
- package/dist/sentry.js.map +1 -1
- package/dist/ses.cjs.map +1 -1
- package/dist/ses.d.cts +2 -2
- package/dist/ses.d.ts +2 -2
- package/dist/ses.js.map +1 -1
- package/dist/slack.cjs +1 -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 +1 -0
- package/dist/slack.js.map +1 -1
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.d.cts +1 -1
- package/dist/twilio.d.ts +1 -1
- package/dist/twilio.js.map +1 -1
- package/dist/{types-DLnlUie8.d.cts → types-Bm972Uqj.d.cts} +19 -1
- package/dist/{types-DLnlUie8.d.ts → types-Bm972Uqj.d.ts} +19 -1
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +1 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.js.map +1 -1
- 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-
|
|
2
|
-
export { b as CronWatch, c as CronWatchOptions, F as FetchHandler, H as HandlerFn, d as HandlerOptions, J as JobContext, e as JobFn, f as JobHandle, R as RecordRunOptions, g as Routes, h as RoutesOptions, i as RunHandle, j as RunOutcome, S as Source, k as SourceHost, l as StartOptions, m as consoleChannel, n as createRoutes, o as custom } from './client-
|
|
3
|
-
import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-
|
|
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, h as JobDefinition, i as JobHealth, J as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as SendingAlert, n as StoredJob, T as TriageContext, o as TriageFn } from './types-
|
|
1
|
+
import { C as CronwatchOptions, a as Cronwatch } from './client-BkPMRkDq.cjs';
|
|
2
|
+
export { b as CronWatch, c as CronWatchOptions, F as FetchHandler, H as HandlerFn, d as HandlerOptions, J as JobContext, e as JobFn, f as JobHandle, R as RecordRunOptions, g as Routes, h as RoutesOptions, i as RunHandle, j as RunOutcome, S as Source, k as SourceHost, l as StartOptions, m as consoleChannel, n as createRoutes, o as custom } from './client-BkPMRkDq.cjs';
|
|
3
|
+
import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Bm972Uqj.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, h as JobDefinition, i as JobHealth, J as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as SendingAlert, n as StoredJob, T as TriageContext, o as TriageFn } from './types-Bm972Uqj.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Keeps everything in process memory. The default when no store is given,
|
|
@@ -11,7 +11,7 @@ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach
|
|
|
11
11
|
declare function memory(): Store;
|
|
12
12
|
|
|
13
13
|
/** This release of @cronwatch/sdk. scripts/release.mjs keeps it in step with package.json. */
|
|
14
|
-
declare const VERSION = "0.
|
|
14
|
+
declare const VERSION = "0.12.1";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* "15m" -> 900000. Accepts a plain number of milliseconds, and compound
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { C as CronwatchOptions, a as Cronwatch } from './client-
|
|
2
|
-
export { b as CronWatch, c as CronWatchOptions, F as FetchHandler, H as HandlerFn, d as HandlerOptions, J as JobContext, e as JobFn, f as JobHandle, R as RecordRunOptions, g as Routes, h as RoutesOptions, i as RunHandle, j as RunOutcome, S as Source, k as SourceHost, l as StartOptions, m as consoleChannel, n as createRoutes, o as custom } from './client-
|
|
3
|
-
import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-
|
|
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, h as JobDefinition, i as JobHealth, J as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as SendingAlert, n as StoredJob, T as TriageContext, o as TriageFn } from './types-
|
|
1
|
+
import { C as CronwatchOptions, a as Cronwatch } from './client-DySsV5T0.js';
|
|
2
|
+
export { b as CronWatch, c as CronWatchOptions, F as FetchHandler, H as HandlerFn, d as HandlerOptions, J as JobContext, e as JobFn, f as JobHandle, R as RecordRunOptions, g as Routes, h as RoutesOptions, i as RunHandle, j as RunOutcome, S as Source, k as SourceHost, l as StartOptions, m as consoleChannel, n as createRoutes, o as custom } from './client-DySsV5T0.js';
|
|
3
|
+
import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Bm972Uqj.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, h as JobDefinition, i as JobHealth, J as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as SendingAlert, n as StoredJob, T as TriageContext, o as TriageFn } from './types-Bm972Uqj.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Keeps everything in process memory. The default when no store is given,
|
|
@@ -11,7 +11,7 @@ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach
|
|
|
11
11
|
declare function memory(): Store;
|
|
12
12
|
|
|
13
13
|
/** This release of @cronwatch/sdk. scripts/release.mjs keeps it in step with package.json. */
|
|
14
|
-
declare const VERSION = "0.
|
|
14
|
+
declare const VERSION = "0.12.1";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* "15m" -> 900000. Accepts a plain number of milliseconds, and compound
|
package/dist/index.js
CHANGED
|
@@ -326,7 +326,7 @@ function isObject(value) {
|
|
|
326
326
|
}
|
|
327
327
|
function normalizeState(state, job) {
|
|
328
328
|
if (!isObject(state)) return emptyState(job);
|
|
329
|
-
const { sending, ...rest } = state;
|
|
329
|
+
const { sending, underFloor, ...rest } = state;
|
|
330
330
|
const open = isObject(state.open) ? Object.fromEntries(Object.entries(state.open).filter(([, at]) => typeof at === "number")) : {};
|
|
331
331
|
const number = (value) => typeof value === "number" ? value : null;
|
|
332
332
|
const list = (value) => Array.isArray(value) ? value : [];
|
|
@@ -339,7 +339,8 @@ function normalizeState(state, job) {
|
|
|
339
339
|
lastAlertAt: number(state.lastAlertAt),
|
|
340
340
|
pendingRecovery: list(state.pendingRecovery).filter((c) => typeof c === "string"),
|
|
341
341
|
undelivered: list(state.undelivered).filter((alert) => isObject(alert)),
|
|
342
|
-
...Array.isArray(sending) && sending.length > 0 ? { sending: [...sending] } : {}
|
|
342
|
+
...Array.isArray(sending) && sending.length > 0 ? { sending: [...sending] } : {},
|
|
343
|
+
...Array.isArray(underFloor) && underFloor.some((m) => typeof m === "string") ? { underFloor: underFloor.filter((m) => typeof m === "string") } : {}
|
|
343
344
|
};
|
|
344
345
|
}
|
|
345
346
|
var MAX_UNDELIVERED = 20;
|
|
@@ -439,6 +440,26 @@ function budgetBreaches(def, run, history) {
|
|
|
439
440
|
}
|
|
440
441
|
return breaches;
|
|
441
442
|
}
|
|
443
|
+
function floorBreaches(def, run, history, previous = []) {
|
|
444
|
+
const breaches = [];
|
|
445
|
+
for (const [metric, value] of Object.entries(run.metrics)) {
|
|
446
|
+
const floor = def.floor?.[metric];
|
|
447
|
+
if (floor !== void 0) {
|
|
448
|
+
if (value < floor) breaches.push({ metric, value, limit: floor, basis: "floor" });
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (value > 0) continue;
|
|
452
|
+
if (previous.includes(metric)) {
|
|
453
|
+
breaches.push({ metric, value, limit: 0, basis: "0 or less on the run before too" });
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
const past = history.filter((r) => r.status === "ok" && typeof r.metrics[metric] === "number").slice(0, BASELINE_WINDOW).map((r) => r.metrics[metric]);
|
|
457
|
+
if (past.length < BASELINE_MIN_RUNS || !past.every((v) => v > 0)) continue;
|
|
458
|
+
const lowest = Math.min(...past);
|
|
459
|
+
breaches.push({ metric, value, limit: lowest, basis: `the last ${past.length} runs all reported more than 0, the lowest ${formatNumber(lowest)}` });
|
|
460
|
+
}
|
|
461
|
+
return breaches;
|
|
462
|
+
}
|
|
442
463
|
function hasFullBaseline(history) {
|
|
443
464
|
return history.filter((r) => r.status === "ok").length >= BASELINE_WINDOW;
|
|
444
465
|
}
|
|
@@ -476,6 +497,16 @@ function onRunFinish(def, run, state, history, now) {
|
|
|
476
497
|
} else {
|
|
477
498
|
closeCondition(next, "over_budget");
|
|
478
499
|
}
|
|
500
|
+
const short = floorBreaches(def, run, history, next.underFloor);
|
|
501
|
+
if (short.length > 0) {
|
|
502
|
+
next.underFloor = short.map((b) => b.metric);
|
|
503
|
+
if (openCondition(next, "under_floor", now)) {
|
|
504
|
+
alerts.push({ type: "under_floor", run, details: { breaches: short } });
|
|
505
|
+
}
|
|
506
|
+
} else {
|
|
507
|
+
delete next.underFloor;
|
|
508
|
+
closeCondition(next, "under_floor");
|
|
509
|
+
}
|
|
479
510
|
const pending = next.pendingRecovery ?? [];
|
|
480
511
|
if (pending.length > 0 && openConditions(next).length === 0) {
|
|
481
512
|
alerts.push({ type: "recovered", run, details: { after: [...pending] } });
|
|
@@ -660,6 +691,14 @@ ${out}`);
|
|
|
660
691
|
if (run) lines.push(`Started ${when(run.startedAt, now)}.`);
|
|
661
692
|
break;
|
|
662
693
|
}
|
|
694
|
+
case "under_floor": {
|
|
695
|
+
title = `${name} fell short`;
|
|
696
|
+
for (const b of draft.details.breaches) {
|
|
697
|
+
lines.push(b.basis === "floor" ? `${b.metric}: ${formatNumber(b.value)}, below the floor of ${formatNumber(b.limit)}.` : `${b.metric}: ${formatNumber(b.value)} (${b.basis}).`);
|
|
698
|
+
}
|
|
699
|
+
if (run) lines.push(`Started ${when(run.startedAt, now)}.`);
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
663
702
|
case "recovered": {
|
|
664
703
|
if (draft.details.reason === "unscheduled") {
|
|
665
704
|
title = `${name} is no longer scheduled`;
|
|
@@ -1078,7 +1117,7 @@ function toneOf(run, job, now) {
|
|
|
1078
1117
|
if (run.status === "failed") return "bad";
|
|
1079
1118
|
if (run.status === "timeout") return "timeout";
|
|
1080
1119
|
const latest = job.lastRun?.id === run.id;
|
|
1081
|
-
return latest && (job.open.includes("over_budget") || job.open.includes("slow")) ? "warn" : "ok";
|
|
1120
|
+
return latest && (job.open.includes("over_budget") || job.open.includes("under_floor") || job.open.includes("slow")) ? "warn" : "ok";
|
|
1082
1121
|
}
|
|
1083
1122
|
function timeoutText(job) {
|
|
1084
1123
|
return safely(() => formatDuration(timeoutMs(job.definition)), "configured");
|
|
@@ -1088,13 +1127,17 @@ function describeRun(run, tone, job, now) {
|
|
|
1088
1127
|
if (tone === "running") return `running since ${at}, ${formatDuration(now - run.startedAt)} so far`;
|
|
1089
1128
|
if (tone === "stuck") return `running since ${at}, past its ${timeoutText(job)} timeout`;
|
|
1090
1129
|
const took = run.durationMs !== null ? `, took ${formatDuration(run.durationMs)}` : "";
|
|
1091
|
-
const extra = tone === "warn" ? job.open.includes("over_budget") ? ", over budget" : ", slow" : "";
|
|
1130
|
+
const extra = tone === "warn" ? job.open.includes("over_budget") ? ", over budget" : job.open.includes("under_floor") ? ", under floor" : ", slow" : "";
|
|
1092
1131
|
return `${run.status} at ${at}${took}${extra}`;
|
|
1093
1132
|
}
|
|
1094
1133
|
function overCeilings(job) {
|
|
1095
1134
|
const metrics = job.lastRun?.metrics ?? {};
|
|
1096
1135
|
return Object.entries(job.definition.budget ?? {}).filter(([k, limit]) => (metrics[k] ?? -Infinity) > limit).map(([k]) => k);
|
|
1097
1136
|
}
|
|
1137
|
+
function underFloors(job) {
|
|
1138
|
+
const floors = job.definition.floor ?? {};
|
|
1139
|
+
return Object.entries(job.lastRun?.metrics ?? {}).filter(([k, v]) => floors[k] !== void 0 ? v < floors[k] : v <= 0).map(([k]) => k);
|
|
1140
|
+
}
|
|
1098
1141
|
function laneNote(job, missed, now) {
|
|
1099
1142
|
const last = job.lastRun;
|
|
1100
1143
|
if (job.silencedUntil !== null && job.silencedUntil > now) return `silenced until ${when2(job.silencedUntil, now)}`;
|
|
@@ -1110,6 +1153,10 @@ function laneNote(job, missed, now) {
|
|
|
1110
1153
|
const over = overCeilings(job);
|
|
1111
1154
|
return `went over budget${over.length ? ` on ${over.join(" and ")}` : ""} at ${when2(last.startedAt, now)}`;
|
|
1112
1155
|
}
|
|
1156
|
+
if (job.open.includes("under_floor") && last) {
|
|
1157
|
+
const under = underFloors(job);
|
|
1158
|
+
return `fell short${under.length ? ` on ${under.join(" and ")}` : ""} at ${when2(last.startedAt, now)}`;
|
|
1159
|
+
}
|
|
1113
1160
|
if (job.open.includes("slow") && last?.durationMs != null) return `slow: took ${formatDuration(last.durationMs)}`;
|
|
1114
1161
|
if (job.open.includes("failed")) return "failing";
|
|
1115
1162
|
if (!last && job.nextExpectedAt !== null) return `no runs yet, first due ${when2(job.nextExpectedAt, now)}`;
|
|
@@ -1226,7 +1273,7 @@ function legend() {
|
|
|
1226
1273
|
[box("run ok"), "ran"],
|
|
1227
1274
|
[box("run bad"), "failed"],
|
|
1228
1275
|
[box("run timeout"), "timed out"],
|
|
1229
|
-
[box("run warn"), "over budget or slow"],
|
|
1276
|
+
[box("run warn"), "over budget, under floor or slow"],
|
|
1230
1277
|
[box("run running"), "running"],
|
|
1231
1278
|
[box("missed"), "missed"]
|
|
1232
1279
|
];
|
|
@@ -1644,6 +1691,7 @@ function jobPage(job, runs, now, base, complete = true) {
|
|
|
1644
1691
|
<dt>Timeout</dt><dd>${h(d.timeout ?? "1h")}</dd>
|
|
1645
1692
|
${d.maxDuration ? `<dt>Max duration</dt><dd>${h(d.maxDuration)}</dd>` : ""}
|
|
1646
1693
|
${d.budget ? `<dt>Budget</dt><dd>${h(Object.entries(d.budget).map(([k, v]) => `${k} \u2264 ${v}`).join(", "))}</dd>` : ""}
|
|
1694
|
+
${d.floor ? `<dt>Floor</dt><dd>${h(Object.entries(d.floor).map(([k, v]) => `${k} \u2265 ${v}`).join(", "))}</dd>` : ""}
|
|
1647
1695
|
${d.expect ? `<dt>Expect</dt><dd>${h(d.expect)}</dd>` : ""}
|
|
1648
1696
|
${d.failuresBeforeAlert && d.failuresBeforeAlert > 1 ? `<dt>Alert after</dt><dd>${h(d.failuresBeforeAlert)} consecutive failures</dd>` : ""}
|
|
1649
1697
|
${d.tags?.length ? `<dt>Tags</dt><dd>${d.tags.map((t) => h(t)).join(", ")}</dd>` : ""}
|
|
@@ -1661,7 +1709,7 @@ function messagePage(title, message, base, signIn = false) {
|
|
|
1661
1709
|
}
|
|
1662
1710
|
|
|
1663
1711
|
// src/version.ts
|
|
1664
|
-
var VERSION = "0.
|
|
1712
|
+
var VERSION = "0.12.1";
|
|
1665
1713
|
var API_VERSION = 1;
|
|
1666
1714
|
|
|
1667
1715
|
// src/routes/index.ts
|
|
@@ -2155,6 +2203,18 @@ function validateDefinition(def) {
|
|
|
2155
2203
|
}
|
|
2156
2204
|
}
|
|
2157
2205
|
}
|
|
2206
|
+
if (def.floor !== void 0) {
|
|
2207
|
+
if (typeof def.floor !== "object" || def.floor === null) throw new Error(`job "${name}": floor must be an object of { metric: floor }`);
|
|
2208
|
+
for (const [metric, floor] of Object.entries(def.floor)) {
|
|
2209
|
+
if (typeof floor !== "number" || !Number.isFinite(floor)) {
|
|
2210
|
+
throw new Error(`job "${name}": floor.${metric} must be a finite number (got ${String(floor)})`);
|
|
2211
|
+
}
|
|
2212
|
+
const ceiling = def.budget?.[metric];
|
|
2213
|
+
if (ceiling !== void 0 && floor > ceiling) {
|
|
2214
|
+
throw new Error(`job "${name}": floor.${metric} (${floor}) is above budget.${metric} (${ceiling}), so every run would alert`);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2158
2218
|
if (def.expect !== void 0 && typeof def.expect !== "string" && !(def.expect instanceof RegExp) && typeof def.expect !== "function") {
|
|
2159
2219
|
throw new Error(`job "${name}": expect must be a string, a RegExp or a function`);
|
|
2160
2220
|
}
|