@cronwatch/sdk 0.14.0 → 0.14.2
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/dist/bugsnag.cjs +19 -4
- package/dist/bugsnag.cjs.map +1 -1
- package/dist/bugsnag.js +19 -4
- package/dist/bugsnag.js.map +1 -1
- package/dist/datadog.cjs +19 -4
- package/dist/datadog.cjs.map +1 -1
- package/dist/datadog.js +19 -4
- package/dist/datadog.js.map +1 -1
- package/dist/discord.cjs +27 -9
- package/dist/discord.cjs.map +1 -1
- package/dist/discord.js +27 -9
- package/dist/discord.js.map +1 -1
- package/dist/honeybadger.cjs +19 -4
- package/dist/honeybadger.cjs.map +1 -1
- package/dist/honeybadger.js +19 -4
- package/dist/honeybadger.js.map +1 -1
- package/dist/index.cjs +45 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -22
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs +19 -4
- package/dist/mailgun.cjs.map +1 -1
- package/dist/mailgun.js +19 -4
- package/dist/mailgun.js.map +1 -1
- package/dist/newrelic.cjs +19 -4
- package/dist/newrelic.cjs.map +1 -1
- package/dist/newrelic.js +19 -4
- package/dist/newrelic.js.map +1 -1
- package/dist/pg-cron.cjs +7 -1
- package/dist/pg-cron.cjs.map +1 -1
- package/dist/pg-cron.js +7 -1
- package/dist/pg-cron.js.map +1 -1
- package/dist/postmark.cjs +19 -4
- package/dist/postmark.cjs.map +1 -1
- package/dist/postmark.js +19 -4
- package/dist/postmark.js.map +1 -1
- package/dist/resend.cjs +19 -4
- package/dist/resend.cjs.map +1 -1
- package/dist/resend.js +19 -4
- package/dist/resend.js.map +1 -1
- package/dist/rollbar.cjs +19 -4
- package/dist/rollbar.cjs.map +1 -1
- package/dist/rollbar.js +19 -4
- package/dist/rollbar.js.map +1 -1
- package/dist/sendgrid.cjs +19 -4
- package/dist/sendgrid.cjs.map +1 -1
- package/dist/sendgrid.js +19 -4
- package/dist/sendgrid.js.map +1 -1
- package/dist/sentry.cjs +19 -4
- package/dist/sentry.cjs.map +1 -1
- package/dist/sentry.js +19 -4
- package/dist/sentry.js.map +1 -1
- package/dist/ses.cjs +19 -4
- package/dist/ses.cjs.map +1 -1
- package/dist/ses.js +19 -4
- package/dist/ses.js.map +1 -1
- package/dist/slack.cjs +27 -9
- package/dist/slack.cjs.map +1 -1
- package/dist/slack.js +27 -9
- package/dist/slack.js.map +1 -1
- package/dist/twilio.cjs +19 -4
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.js +19 -4
- package/dist/twilio.js.map +1 -1
- package/dist/webhook.cjs +29 -7
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.js +29 -7
- package/dist/webhook.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -11,7 +11,7 @@ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach
|
|
|
11
11
|
declare function memory(): Store & Required<Pick<Store, "lastRun">>;
|
|
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.
|
|
14
|
+
declare const VERSION = "0.14.2";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* "15m" -> 900000. Accepts a plain number of milliseconds, and compound
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach
|
|
|
11
11
|
declare function memory(): Store & Required<Pick<Store, "lastRun">>;
|
|
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.
|
|
14
|
+
declare const VERSION = "0.14.2";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* "15m" -> 900000. Accepts a plain number of milliseconds, and compound
|
package/dist/index.js
CHANGED
|
@@ -720,7 +720,7 @@ function onCheck(def, stored, lastRun, state, now) {
|
|
|
720
720
|
return { state: next, alerts, nextExpectedAt, dueAt: exp.dueAt };
|
|
721
721
|
}
|
|
722
722
|
function isStuck(def, run, now) {
|
|
723
|
-
return run.status === "running" && now - run.startedAt > timeoutMs(def);
|
|
723
|
+
return run.status === "running" && run.finishedAt === null && now - run.startedAt > timeoutMs(def);
|
|
724
724
|
}
|
|
725
725
|
function muteOpens(previous, next) {
|
|
726
726
|
const muted = cloneState(next);
|
|
@@ -991,11 +991,15 @@ function stringify(part) {
|
|
|
991
991
|
}
|
|
992
992
|
}
|
|
993
993
|
function createRecorder(run) {
|
|
994
|
+
const window = OUTPUT_CAP + REDACT_EDGE + 2;
|
|
995
|
+
const copy = (text) => JSON.parse(JSON.stringify(text));
|
|
994
996
|
const lines = [];
|
|
995
997
|
let size = 0;
|
|
996
998
|
const head = [];
|
|
997
999
|
let headSize = 0;
|
|
998
|
-
let
|
|
1000
|
+
let whole = "";
|
|
1001
|
+
let tail2 = "";
|
|
1002
|
+
let logged = false;
|
|
999
1003
|
const metrics = {};
|
|
1000
1004
|
const controller = new AbortController();
|
|
1001
1005
|
const context = {
|
|
@@ -1004,16 +1008,23 @@ function createRecorder(run) {
|
|
|
1004
1008
|
startedAt: run.startedAt,
|
|
1005
1009
|
signal: controller.signal,
|
|
1006
1010
|
log(...parts) {
|
|
1007
|
-
|
|
1011
|
+
let line = parts.map(stringify).join(" ");
|
|
1012
|
+
const separator = logged ? "\n" : "";
|
|
1013
|
+
logged = true;
|
|
1014
|
+
if (whole !== null) whole = whole.length + separator.length + line.length <= 2 * OUTPUT_CAP ? whole + separator + line : null;
|
|
1015
|
+
tail2 = copy((tail2 + separator + copy(line.slice(-OUTPUT_CAP))).slice(-OUTPUT_CAP));
|
|
1008
1016
|
if (headSize < OUTPUT_CAP) {
|
|
1009
|
-
head.push(line);
|
|
1017
|
+
head.push(copy(line.slice(0, OUTPUT_CAP - headSize)));
|
|
1010
1018
|
headSize += line.length + 1;
|
|
1011
1019
|
}
|
|
1020
|
+
line = stripNul(line);
|
|
1021
|
+
if (line.length >= window) {
|
|
1022
|
+
line = copy(line.slice(-68609));
|
|
1023
|
+
}
|
|
1012
1024
|
lines.push(line);
|
|
1013
1025
|
size += line.length + 1;
|
|
1014
|
-
while (size >
|
|
1026
|
+
while (size > window && lines.length > 1) {
|
|
1015
1027
|
size -= lines.shift().length + 1;
|
|
1016
|
-
dropped = true;
|
|
1017
1028
|
}
|
|
1018
1029
|
},
|
|
1019
1030
|
metric(name, value) {
|
|
@@ -1030,10 +1041,8 @@ function createRecorder(run) {
|
|
|
1030
1041
|
context,
|
|
1031
1042
|
output: () => lines.length === 0 ? null : lines.join("\n"),
|
|
1032
1043
|
expectText: () => {
|
|
1033
|
-
if (
|
|
1034
|
-
|
|
1035
|
-
if (!dropped && all.length <= 2 * OUTPUT_CAP) return all;
|
|
1036
|
-
return head.join("\n").slice(0, OUTPUT_CAP) + "\n" + all.slice(-OUTPUT_CAP);
|
|
1044
|
+
if (!logged) return null;
|
|
1045
|
+
return whole ?? head.join("\n").slice(0, OUTPUT_CAP) + "\n" + tail2;
|
|
1037
1046
|
},
|
|
1038
1047
|
metrics: () => ({ ...metrics }),
|
|
1039
1048
|
abort: () => controller.abort()
|
|
@@ -1207,7 +1216,7 @@ function staticAsset(path, base) {
|
|
|
1207
1216
|
}
|
|
1208
1217
|
|
|
1209
1218
|
// src/version.ts
|
|
1210
|
-
var VERSION = "0.14.
|
|
1219
|
+
var VERSION = "0.14.2";
|
|
1211
1220
|
var API_VERSION = 1;
|
|
1212
1221
|
|
|
1213
1222
|
// src/routes/timeline.ts
|
|
@@ -1997,6 +2006,7 @@ function stripBase(pathname, base) {
|
|
|
1997
2006
|
}
|
|
1998
2007
|
var MAX_REQUEST_BODY = 1 << 20;
|
|
1999
2008
|
var TOO_LARGE = /* @__PURE__ */ Symbol("too large");
|
|
2009
|
+
var BAD_BODY = /* @__PURE__ */ Symbol("malformed body");
|
|
2000
2010
|
async function readLimited(request) {
|
|
2001
2011
|
const length = Number(request.headers.get("content-length"));
|
|
2002
2012
|
if (Number.isFinite(length) && length > MAX_REQUEST_BODY) return TOO_LARGE;
|
|
@@ -2043,14 +2053,14 @@ async function readBody(request) {
|
|
|
2043
2053
|
try {
|
|
2044
2054
|
if (json2) {
|
|
2045
2055
|
const data = await copy.json();
|
|
2046
|
-
return typeof data === "object" && data !== null ? Object.fromEntries(Object.entries(data).map(([k, v]) => [k, String(v)])) :
|
|
2056
|
+
return typeof data === "object" && data !== null && !Array.isArray(data) ? Object.fromEntries(Object.entries(data).map(([k, v]) => [k, String(v)])) : BAD_BODY;
|
|
2047
2057
|
}
|
|
2048
2058
|
const fields = await copy.formData();
|
|
2049
2059
|
const out = {};
|
|
2050
2060
|
for (const [k, v] of fields.entries()) out[k] = String(v);
|
|
2051
2061
|
return out;
|
|
2052
2062
|
} catch {
|
|
2053
|
-
return
|
|
2063
|
+
return BAD_BODY;
|
|
2054
2064
|
}
|
|
2055
2065
|
}
|
|
2056
2066
|
function crossSite(request, publicOrigin) {
|
|
@@ -2116,6 +2126,7 @@ function buildRoutes(cw, options = {}) {
|
|
|
2116
2126
|
const signInPage = () => generated ? html(messagePage("Sign in", "CRONWATCH_TOKEN is not set, so this development server made a token. The sign-in link is in the server log: open it once, or enter the token from it below, and this browser stays signed in.", base, true), 401) : html(messagePage("Sign in", "Enter your CRONWATCH_TOKEN and this browser stays signed in.", base, true), 401);
|
|
2117
2127
|
if (token && method === "POST" && path === "/signin") {
|
|
2118
2128
|
const body = await readBody(request);
|
|
2129
|
+
if (body === BAD_BODY) return html(messagePage("Bad request", "Malformed request body", base), 400);
|
|
2119
2130
|
const sent = body === TOO_LARGE ? void 0 : body.token;
|
|
2120
2131
|
if (sent === void 0 || !constantTimeEqual(sent, token)) return signInPage();
|
|
2121
2132
|
return redirect(signInReturn(request.headers.get("referer"), publicOrigin, base), { "set-cookie": await signInCookie() });
|
|
@@ -2173,6 +2184,7 @@ function buildRoutes(cw, options = {}) {
|
|
|
2173
2184
|
if (parts[2] === "silence") {
|
|
2174
2185
|
const body = await readBody(request);
|
|
2175
2186
|
if (body === TOO_LARGE) return html(messagePage("Not silenced", "The request was too large.", base), 413);
|
|
2187
|
+
if (body === BAD_BODY) return html(messagePage("Not silenced", "Malformed request body", base), 400);
|
|
2176
2188
|
let duration;
|
|
2177
2189
|
try {
|
|
2178
2190
|
duration = silenceDuration(body.for);
|
|
@@ -2210,6 +2222,7 @@ function buildRoutes(cw, options = {}) {
|
|
|
2210
2222
|
if (rest[2] === "silence") {
|
|
2211
2223
|
const body = await readBody(request);
|
|
2212
2224
|
if (body === TOO_LARGE) return api({ ok: false, error: "Request body too large" }, 413);
|
|
2225
|
+
if (body === BAD_BODY) return api({ ok: false, error: "Malformed request body" }, 400);
|
|
2213
2226
|
let duration;
|
|
2214
2227
|
try {
|
|
2215
2228
|
duration = silenceDuration(body.for ?? url.searchParams.get("for"));
|
|
@@ -2845,9 +2858,10 @@ var Cronwatch = class {
|
|
|
2845
2858
|
if (stored.job !== run.job) return `belongs to job "${stored.job}"`;
|
|
2846
2859
|
}
|
|
2847
2860
|
}
|
|
2861
|
+
const tracked = (await this.readState(run.job)).pendingRuns?.includes(run.id) ?? false;
|
|
2848
2862
|
const claim = await this.claimFinish(run);
|
|
2849
2863
|
if ("ignored" in claim) return claim.ignored;
|
|
2850
|
-
if (!claim.lateAfterTimeout || run.status === "ok") await this.finishRun(toStored(definition), run, finishedAt);
|
|
2864
|
+
if (!claim.lateAfterTimeout || run.status === "ok") await this.finishRun(toStored(definition), run, finishedAt, tracked && !claim.lateAfterTimeout);
|
|
2851
2865
|
return null;
|
|
2852
2866
|
}
|
|
2853
2867
|
/** A conditional write (Store.updateRunIf), or for a store without one, a read then a plain write. */
|
|
@@ -3132,17 +3146,16 @@ ${id}`;
|
|
|
3132
3146
|
const definition = toStored(declared);
|
|
3133
3147
|
const stored = await this.store.getRun(run.id);
|
|
3134
3148
|
if (stored) return this.recordOver(definition, stored, run, evaluate);
|
|
3149
|
+
const inserted = evaluate ? { ...run, status: "running" } : run;
|
|
3135
3150
|
try {
|
|
3136
|
-
await this.store.insertRun(
|
|
3151
|
+
await this.store.insertRun(inserted);
|
|
3137
3152
|
} catch (e) {
|
|
3138
3153
|
const again = await this.store.getRun(run.id).catch(() => null);
|
|
3139
3154
|
if (again) return this.recordOver(definition, again, run, evaluate);
|
|
3140
3155
|
throw e;
|
|
3141
3156
|
}
|
|
3142
3157
|
if (!evaluate) return [];
|
|
3143
|
-
|
|
3144
|
-
if (run.status === "running") return [];
|
|
3145
|
-
return this.finishRun(definition, run, this.now());
|
|
3158
|
+
return this.recordOver(definition, inserted, run, true);
|
|
3146
3159
|
}
|
|
3147
3160
|
/** recordRun() for a run already stored. */
|
|
3148
3161
|
async recordOver(definition, stored, run, evaluate) {
|
|
@@ -3150,7 +3163,16 @@ ${id}`;
|
|
|
3150
3163
|
this.report(new Error(`run ${run.id} of ${run.job} belongs to job "${stored.job}"; ignored`), `recording ${run.job}`);
|
|
3151
3164
|
return [];
|
|
3152
3165
|
}
|
|
3153
|
-
if (stored.status !== "running" && stored.status !== "timeout"
|
|
3166
|
+
if (stored.status !== "running" && stored.status !== "timeout") return [];
|
|
3167
|
+
if (evaluate && stored.status === "running") {
|
|
3168
|
+
await this.updateState(run.job, async (before) => {
|
|
3169
|
+
const current = await this.store.getRun(run.id);
|
|
3170
|
+
if (!current || current.job !== run.job || current.status !== "running" || before.pendingRuns?.includes(run.id)) return { state: before, result: void 0 };
|
|
3171
|
+
return { state: holdRun(onRunStart(definition, run, before, this.now()), run.id), result: void 0 };
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
if (run.status === "running") return [];
|
|
3175
|
+
const tracked = (await this.readState(run.job)).pendingRuns?.includes(run.id) ?? false;
|
|
3154
3176
|
const claim = await this.claimFinish(run);
|
|
3155
3177
|
if ("ignored" in claim) {
|
|
3156
3178
|
this.report(new Error(`run ${run.id} of ${run.job} ${claim.ignored}; ignored`), `recording ${run.job}`);
|
|
@@ -3161,18 +3183,19 @@ ${id}`;
|
|
|
3161
3183
|
return [];
|
|
3162
3184
|
}
|
|
3163
3185
|
if (claim.lateAfterTimeout && run.status !== "ok") return [];
|
|
3164
|
-
return this.finishRun(definition, run, this.now());
|
|
3186
|
+
return this.finishRun(definition, run, this.now(), tracked && !claim.lateAfterTimeout);
|
|
3165
3187
|
}
|
|
3166
3188
|
/**
|
|
3167
3189
|
* Evaluate a finished run (ok, failed, or timed out by a check), already
|
|
3168
3190
|
* written, against the job's state and send what that produces. The
|
|
3169
3191
|
* alerts are written with that state (see outbox()). Never throws.
|
|
3170
3192
|
*/
|
|
3171
|
-
async finishRun(definition, run, now) {
|
|
3193
|
+
async finishRun(definition, run, now, pendingOnly = false) {
|
|
3172
3194
|
let held;
|
|
3173
3195
|
try {
|
|
3174
3196
|
let history = null;
|
|
3175
3197
|
({ result: held } = await this.updateState(run.job, async (previous) => {
|
|
3198
|
+
if (pendingOnly && !(previous.pendingRuns ?? []).includes(run.id)) return { state: previous, result: { alerts: [], dropped: 0 } };
|
|
3176
3199
|
history ??= await this.history(run);
|
|
3177
3200
|
const before = releaseRun(previous, run.id);
|
|
3178
3201
|
return this.outbox(applySilence(before, onRunFinish(definition, run, before, history, now), now), definition, now);
|
|
@@ -3317,7 +3340,7 @@ ${id}`;
|
|
|
3317
3340
|
continue;
|
|
3318
3341
|
}
|
|
3319
3342
|
if (run.status === "running" || typeof run.finishedAt !== "number" || run.finishedAt > now - SEND_LEASE_MS) continue;
|
|
3320
|
-
alerts.push(...await this.finishRun(stored.definition, run, now));
|
|
3343
|
+
alerts.push(...await this.finishRun(stored.definition, run, now, true));
|
|
3321
3344
|
} catch (e) {
|
|
3322
3345
|
this.onError(e, `evaluating run ${id} of ${stored.name}`);
|
|
3323
3346
|
}
|