@cronwatch/sdk 0.7.0 → 0.8.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/dist/anthropic.cjs +13 -2
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.js +13 -2
- package/dist/anthropic.js.map +1 -1
- package/dist/bugsnag.cjs +8 -1
- package/dist/bugsnag.cjs.map +1 -1
- package/dist/bugsnag.js +8 -1
- package/dist/bugsnag.js.map +1 -1
- package/dist/d1.cjs +4 -1
- package/dist/d1.cjs.map +1 -1
- package/dist/d1.js +4 -1
- package/dist/d1.js.map +1 -1
- package/dist/datadog.cjs.map +1 -1
- package/dist/datadog.js.map +1 -1
- package/dist/discord.cjs.map +1 -1
- package/dist/discord.js.map +1 -1
- package/dist/honeybadger.cjs +8 -1
- package/dist/honeybadger.cjs.map +1 -1
- package/dist/honeybadger.js +8 -1
- package/dist/honeybadger.js.map +1 -1
- package/dist/index.cjs +69 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -20
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs.map +1 -1
- package/dist/mailgun.js.map +1 -1
- package/dist/newrelic.cjs.map +1 -1
- package/dist/newrelic.js.map +1 -1
- package/dist/pg-cron.cjs +8 -1
- package/dist/pg-cron.cjs.map +1 -1
- package/dist/pg-cron.js +8 -1
- package/dist/pg-cron.js.map +1 -1
- package/dist/postgres.cjs +6 -1
- package/dist/postgres.cjs.map +1 -1
- package/dist/postgres.js +6 -1
- package/dist/postgres.js.map +1 -1
- package/dist/postmark.cjs.map +1 -1
- package/dist/postmark.js.map +1 -1
- package/dist/resend.cjs.map +1 -1
- package/dist/resend.js.map +1 -1
- package/dist/rollbar.cjs +8 -1
- package/dist/rollbar.cjs.map +1 -1
- package/dist/rollbar.js +8 -1
- package/dist/rollbar.js.map +1 -1
- package/dist/sendgrid.cjs.map +1 -1
- package/dist/sendgrid.js.map +1 -1
- package/dist/sentry.cjs +8 -1
- package/dist/sentry.cjs.map +1 -1
- package/dist/sentry.js +8 -1
- package/dist/sentry.js.map +1 -1
- package/dist/ses.cjs.map +1 -1
- package/dist/ses.js.map +1 -1
- package/dist/slack.cjs.map +1 -1
- package/dist/slack.js.map +1 -1
- package/dist/sqlite.cjs +4 -1
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.js +4 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.js.map +1 -1
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -67,6 +67,14 @@ function formatRelative(at, now) {
|
|
|
67
67
|
if (abs < 5e3) return text;
|
|
68
68
|
return diff < 0 ? `${text} ago` : `in ${text}`;
|
|
69
69
|
}
|
|
70
|
+
var FIRST_DATE_MS = -621355968e5;
|
|
71
|
+
var LAST_DATE_MS = 253402300799999;
|
|
72
|
+
function isoTime(at) {
|
|
73
|
+
return at >= FIRST_DATE_MS && at <= LAST_DATE_MS ? new Date(at).toISOString() : null;
|
|
74
|
+
}
|
|
75
|
+
function beyondDates(at) {
|
|
76
|
+
return at > LAST_DATE_MS ? "after 9999-12-31 23:59:59 UTC" : "before 0001-01-01 00:00:00 UTC";
|
|
77
|
+
}
|
|
70
78
|
|
|
71
79
|
// src/env.ts
|
|
72
80
|
function readEnv(name) {
|
|
@@ -104,15 +112,36 @@ function parseSchedule(schedule, timezone) {
|
|
|
104
112
|
cache.set(key, parsed);
|
|
105
113
|
return parsed;
|
|
106
114
|
}
|
|
115
|
+
var CYCLE_MS = 146097 * 864e5;
|
|
116
|
+
var CRONER_FIRST_MS = Date.UTC(400, 0, 1);
|
|
117
|
+
var CRONER_LAST_MS = Date.UTC(2800, 0, 1);
|
|
118
|
+
function runsAfter(cron, n, from) {
|
|
119
|
+
let shift = 0;
|
|
120
|
+
if (from < CRONER_FIRST_MS) shift = Math.ceil((CRONER_FIRST_MS - from) / CYCLE_MS) * CYCLE_MS;
|
|
121
|
+
else if (from >= CRONER_LAST_MS) shift = -(Math.floor((from - CRONER_LAST_MS) / CYCLE_MS) + 1) * CYCLE_MS;
|
|
122
|
+
const out = [];
|
|
123
|
+
for (const date of cron.nextRuns(n, new Date(from + shift))) {
|
|
124
|
+
const t = date.getTime() - shift;
|
|
125
|
+
if (t > LAST_DATE_MS) break;
|
|
126
|
+
out.push(t);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
function countFrom(from) {
|
|
131
|
+
if (from >= LAST_DATE_MS) return null;
|
|
132
|
+
return from >= FIRST_DATE_MS ? from : FIRST_DATE_MS - 1;
|
|
133
|
+
}
|
|
107
134
|
function fireAfter(parsed, from) {
|
|
108
135
|
const cron = crons.get(parsed);
|
|
109
136
|
if (!cron) throw new Error(`schedule "${parsed.source}" was not made by parseSchedule`);
|
|
110
|
-
|
|
137
|
+
const start = countFrom(from);
|
|
138
|
+
if (start === null) return null;
|
|
139
|
+
let probe = start;
|
|
111
140
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
112
|
-
const runs = cron
|
|
141
|
+
const runs = runsAfter(cron, 8, probe);
|
|
113
142
|
if (runs.length === 0) return null;
|
|
114
|
-
const found = runs.find((
|
|
115
|
-
if (found) return found
|
|
143
|
+
const found = runs.find((t) => t > start);
|
|
144
|
+
if (found !== void 0) return found;
|
|
116
145
|
probe += 36e5;
|
|
117
146
|
}
|
|
118
147
|
return null;
|
|
@@ -121,20 +150,21 @@ function firesBetween(parsed, from, to, limit) {
|
|
|
121
150
|
const cron = crons.get(parsed);
|
|
122
151
|
if (!cron) throw new Error(`schedule "${parsed.source}" was not made by parseSchedule`);
|
|
123
152
|
const out = [];
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
const start = countFrom(from);
|
|
154
|
+
if (start === null) return out;
|
|
155
|
+
let probe = start;
|
|
156
|
+
let last = start;
|
|
126
157
|
for (let guard = 0; guard < 1e3; guard++) {
|
|
127
|
-
const batch = cron
|
|
158
|
+
const batch = runsAfter(cron, Math.min(limit + 1 - out.length, 24), probe);
|
|
128
159
|
if (batch.length === 0) return out;
|
|
129
|
-
for (const
|
|
130
|
-
const t = date.getTime();
|
|
160
|
+
for (const t of batch) {
|
|
131
161
|
if (t <= last) continue;
|
|
132
162
|
if (t > to) return out;
|
|
133
163
|
out.push(t);
|
|
134
164
|
last = t;
|
|
135
165
|
if (out.length > limit) return null;
|
|
136
166
|
}
|
|
137
|
-
const end = batch[batch.length - 1]
|
|
167
|
+
const end = batch[batch.length - 1];
|
|
138
168
|
probe = end > probe ? end : probe + 36e5;
|
|
139
169
|
}
|
|
140
170
|
return out;
|
|
@@ -170,6 +200,7 @@ function runCovers(startedAt, dueAt, followingAt = null) {
|
|
|
170
200
|
}
|
|
171
201
|
function inSpringForwardGap(parsed, startedAt, fireAt) {
|
|
172
202
|
const LOOKBACK = 3 * 36e5;
|
|
203
|
+
if (fireAt - LOOKBACK < FIRST_DATE_MS) return false;
|
|
173
204
|
const after2 = utcOffset(fireAt, parsed.timezone);
|
|
174
205
|
const before = utcOffset(fireAt - LOOKBACK, parsed.timezone);
|
|
175
206
|
const gap = after2 - before;
|
|
@@ -206,8 +237,10 @@ function utcOffset(at, timezone) {
|
|
|
206
237
|
for (const part of format.formatToParts(new Date(at))) {
|
|
207
238
|
if (part.type !== "literal") parts[part.type] = Number(part.value);
|
|
208
239
|
}
|
|
209
|
-
const wall =
|
|
210
|
-
|
|
240
|
+
const wall = /* @__PURE__ */ new Date(0);
|
|
241
|
+
wall.setUTCFullYear(parts.year, parts.month - 1, parts.day);
|
|
242
|
+
wall.setUTCHours(parts.hour, parts.minute, parts.second);
|
|
243
|
+
return wall.getTime() - Math.floor(at / 1e3) * 1e3;
|
|
211
244
|
}
|
|
212
245
|
|
|
213
246
|
// src/stats.ts
|
|
@@ -230,6 +263,15 @@ var DEFAULT_TIMEOUT_MS = 60 * 6e4;
|
|
|
230
263
|
var SLOW_FLOOR_MS = 1e4;
|
|
231
264
|
var BASELINE_MIN_RUNS = 5;
|
|
232
265
|
var BASELINE_WINDOW = 20;
|
|
266
|
+
var MAX_DURATION_MS = Number.MAX_SAFE_INTEGER;
|
|
267
|
+
function runDuration(startedAt, finishedAt) {
|
|
268
|
+
const ms = finishedAt - startedAt;
|
|
269
|
+
return ms > 0 ? Math.min(ms, MAX_DURATION_MS) : 0;
|
|
270
|
+
}
|
|
271
|
+
function stateVersion(state) {
|
|
272
|
+
const version = state?.version;
|
|
273
|
+
return typeof version === "number" && Number.isSafeInteger(version) && version >= 0 ? version + 0 : 0;
|
|
274
|
+
}
|
|
233
275
|
function emptyState(job) {
|
|
234
276
|
return { job, open: {}, consecutiveFailures: 0, silencedUntil: null, lastAlertAt: null, pendingRecovery: [], undelivered: [] };
|
|
235
277
|
}
|
|
@@ -440,7 +482,9 @@ function summary(stored, recent, state, nextExpectedAt, health) {
|
|
|
440
482
|
// src/format.ts
|
|
441
483
|
function when(at, now) {
|
|
442
484
|
if (at === null || at === void 0) return "never";
|
|
443
|
-
|
|
485
|
+
const iso = isoTime(at);
|
|
486
|
+
if (iso === null) return beyondDates(at);
|
|
487
|
+
return `${iso.replace("T", " ").slice(0, 19)} UTC (${formatRelative(at, now)})`;
|
|
444
488
|
}
|
|
445
489
|
function firstLines(text, n) {
|
|
446
490
|
if (!text) return "";
|
|
@@ -836,9 +880,13 @@ function dayLabel(t) {
|
|
|
836
880
|
return `${WEEKDAYS[d.getUTCDay()]} ${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]}`;
|
|
837
881
|
}
|
|
838
882
|
function when2(t, now) {
|
|
883
|
+
if (t > LAST_DATE_MS) return "after 31 Dec 9999 23:59";
|
|
884
|
+
if (!(t >= FIRST_DATE_MS)) return "before 1 Jan 0001 00:00";
|
|
839
885
|
if (Math.floor(t / DAY) === Math.floor(now / DAY)) return clock(t);
|
|
840
886
|
const d = new Date(t);
|
|
841
|
-
|
|
887
|
+
const year = d.getUTCFullYear();
|
|
888
|
+
const other = year === new Date(now).getUTCFullYear() ? "" : ` ${String(year).padStart(4, "0")}`;
|
|
889
|
+
return `${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]}${other} ${clock(t)}`;
|
|
842
890
|
}
|
|
843
891
|
function parsedSchedule(job) {
|
|
844
892
|
if (!job.definition.schedule) return null;
|
|
@@ -1329,7 +1377,8 @@ function sparkline(runs) {
|
|
|
1329
1377
|
}
|
|
1330
1378
|
function stamp(at, now) {
|
|
1331
1379
|
if (at === null) return `<span class="muted">never</span>`;
|
|
1332
|
-
const iso =
|
|
1380
|
+
const iso = isoTime(at);
|
|
1381
|
+
if (iso === null) return `<span class="nowrap">${beyondDates(at)}</span>`;
|
|
1333
1382
|
return `<time class="nowrap" datetime="${iso}" title="${iso.replace("T", " ").slice(0, 19)} UTC">${h(formatRelative(at, now))}</time>`;
|
|
1334
1383
|
}
|
|
1335
1384
|
function healthFigures(jobs) {
|
|
@@ -1862,7 +1911,7 @@ function memory() {
|
|
|
1862
1911
|
states.set(state.job, clone(state));
|
|
1863
1912
|
},
|
|
1864
1913
|
async compareAndSetState(state, expectedVersion) {
|
|
1865
|
-
if ((states.get(state.job)
|
|
1914
|
+
if (stateVersion(states.get(state.job)) !== expectedVersion) return false;
|
|
1866
1915
|
states.set(state.job, clone(state));
|
|
1867
1916
|
return true;
|
|
1868
1917
|
},
|
|
@@ -2132,7 +2181,7 @@ var CronWatch = class {
|
|
|
2132
2181
|
const current = await this.readState(job);
|
|
2133
2182
|
const { state, result } = await change(current);
|
|
2134
2183
|
if (sameState(state, current)) return { state: current, result };
|
|
2135
|
-
const version = current
|
|
2184
|
+
const version = stateVersion(current);
|
|
2136
2185
|
const next = { ...state, version: version + 1 };
|
|
2137
2186
|
if (await this.writeState(next, version)) return { state: next, result };
|
|
2138
2187
|
if (attempt >= STATE_ATTEMPTS) {
|
|
@@ -2192,7 +2241,7 @@ var CronWatch = class {
|
|
|
2192
2241
|
}
|
|
2193
2242
|
const finishedAt = this.now();
|
|
2194
2243
|
run.finishedAt = finishedAt;
|
|
2195
|
-
run.durationMs =
|
|
2244
|
+
run.durationMs = runDuration(startedAt, finishedAt);
|
|
2196
2245
|
run.metrics = recorder.metrics();
|
|
2197
2246
|
run.output = recorder.output() ?? (typeof result === "string" ? capOutput(result) : null);
|
|
2198
2247
|
this.conclude(definition, run, result, error, threw, recorder.expectText() ?? (typeof result === "string" ? result : null));
|
|
@@ -2426,7 +2475,7 @@ ${id}`;
|
|
|
2426
2475
|
...from,
|
|
2427
2476
|
status: "running",
|
|
2428
2477
|
finishedAt,
|
|
2429
|
-
durationMs:
|
|
2478
|
+
durationMs: runDuration(from.startedAt, finishedAt),
|
|
2430
2479
|
error: null,
|
|
2431
2480
|
output: joinOutput(from.output, added),
|
|
2432
2481
|
metrics: { ...from.metrics, ...recorder.metrics() }
|
|
@@ -2615,7 +2664,7 @@ ${id}`;
|
|
|
2615
2664
|
const timeout = timeoutMs(definition);
|
|
2616
2665
|
run.status = "timeout";
|
|
2617
2666
|
run.finishedAt = now;
|
|
2618
|
-
run.durationMs =
|
|
2667
|
+
run.durationMs = runDuration(run.startedAt, now);
|
|
2619
2668
|
run.error = `Still running after ${formatDuration(timeout)}; marked as timed out`;
|
|
2620
2669
|
if (!await this.writeRunIf(run, ["running"])) continue;
|
|
2621
2670
|
alerts.push(...await this.finishRun(definition, run, now));
|