@cronwatch/sdk 0.10.0 → 0.11.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 +4 -4
- package/dist/anthropic.d.cts +1 -1
- package/dist/anthropic.d.ts +1 -1
- package/dist/bugsnag.d.cts +1 -1
- package/dist/bugsnag.d.ts +1 -1
- package/dist/{client-DUoSea7X.d.cts → client-CRMfhylq.d.cts} +63 -26
- package/dist/{client-BcIjVBiZ.d.ts → client-D2Xk2600.d.ts} +63 -26
- package/dist/d1.cjs +29 -11
- package/dist/d1.cjs.map +1 -1
- package/dist/d1.d.cts +1 -1
- package/dist/d1.d.ts +1 -1
- package/dist/d1.js +29 -11
- package/dist/d1.js.map +1 -1
- package/dist/datadog.d.cts +1 -1
- package/dist/datadog.d.ts +1 -1
- package/dist/discord.cjs +10 -4
- package/dist/discord.cjs.map +1 -1
- package/dist/discord.d.cts +13 -6
- package/dist/discord.d.ts +13 -6
- package/dist/discord.js +7 -1
- package/dist/discord.js.map +1 -1
- package/dist/{email-VrGsVz43.d.ts → email-BWuUiCkw.d.ts} +1 -1
- package/dist/{email-JZs3vxwq.d.cts → email-Dcqe3k8n.d.cts} +1 -1
- package/dist/honeybadger.d.cts +1 -1
- package/dist/honeybadger.d.ts +1 -1
- package/dist/index.cjs +177 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -9
- package/dist/index.d.ts +15 -9
- package/dist/index.js +176 -40
- package/dist/index.js.map +1 -1
- package/dist/mailgun.d.cts +2 -2
- package/dist/mailgun.d.ts +2 -2
- package/dist/newrelic.d.cts +1 -1
- package/dist/newrelic.d.ts +1 -1
- package/dist/pg-cron.cjs +17 -7
- package/dist/pg-cron.cjs.map +1 -1
- package/dist/pg-cron.d.cts +15 -12
- package/dist/pg-cron.d.ts +15 -12
- package/dist/pg-cron.js +14 -4
- package/dist/pg-cron.js.map +1 -1
- package/dist/postgres.cjs +28 -10
- 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 +28 -10
- package/dist/postgres.js.map +1 -1
- package/dist/postmark.d.cts +2 -2
- package/dist/postmark.d.ts +2 -2
- package/dist/resend.d.cts +2 -2
- package/dist/resend.d.ts +2 -2
- package/dist/rollbar.d.cts +1 -1
- package/dist/rollbar.d.ts +1 -1
- package/dist/sendgrid.d.cts +2 -2
- package/dist/sendgrid.d.ts +2 -2
- package/dist/sentry.cjs +4 -1
- package/dist/sentry.cjs.map +1 -1
- package/dist/sentry.d.cts +5 -2
- package/dist/sentry.d.ts +5 -2
- package/dist/sentry.js +4 -1
- package/dist/sentry.js.map +1 -1
- package/dist/ses.d.cts +2 -2
- package/dist/ses.d.ts +2 -2
- package/dist/slack.d.cts +1 -1
- package/dist/slack.d.ts +1 -1
- package/dist/sqlite.cjs +29 -11
- 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 +29 -11
- package/dist/sqlite.js.map +1 -1
- package/dist/twilio.cjs +10 -4
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.d.cts +12 -7
- package/dist/twilio.d.ts +12 -7
- package/dist/twilio.js +7 -1
- package/dist/twilio.js.map +1 -1
- package/dist/{types-DyWCxlSi.d.cts → types-DLnlUie8.d.cts} +5 -2
- package/dist/{types-DyWCxlSi.d.ts → types-DLnlUie8.d.ts} +5 -2
- package/dist/webhook.cjs +10 -5
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +18 -7
- package/dist/webhook.d.ts +18 -7
- package/dist/webhook.js +10 -6
- package/dist/webhook.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ export const nightlyReport = cw.job("nightly-report", {
|
|
|
25
25
|
schedule: "0 2 * * *", // cron, "@hourly", or "every 15m"
|
|
26
26
|
timezone: "UTC",
|
|
27
27
|
grace: "15m", // how late a start may be before it is missed
|
|
28
|
-
timeout: "30m", // a run still going after this is stuck
|
|
28
|
+
timeout: "30m", // a run still going after this is stuck (maxDuration: a finished run longer than it is slow)
|
|
29
29
|
expect: "Report written", // output must contain this, or the run failed
|
|
30
30
|
budget: { cost: 2 }, // cost above 2 is over budget
|
|
31
31
|
});
|
|
@@ -57,7 +57,7 @@ export const { GET, POST, DELETE } = cw.routes();
|
|
|
57
57
|
await nightlyReport.run(async (job) => { /* ... */ });
|
|
58
58
|
|
|
59
59
|
// Missed and stuck runs are found by the check: on an interval, or a cron hitting /cronwatch/api/check
|
|
60
|
-
cw.
|
|
60
|
+
cw.startChecking();
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## What it catches
|
|
@@ -77,13 +77,13 @@ An `expect` RegExp runs in your process on the runtime's own engine and, like an
|
|
|
77
77
|
|
|
78
78
|
| Import | |
|
|
79
79
|
|---|---|
|
|
80
|
-
| `@cronwatch/sdk` | `cronwatch`, `memory`, `custom`, `consoleChannel`, `
|
|
80
|
+
| `@cronwatch/sdk` | `cronwatch`, `Cronwatch`, `memory`, `custom`, `consoleChannel`, types; the dashboard is `cw.routes()` |
|
|
81
81
|
| `@cronwatch/sdk/sqlite` | `sqlite({ path })`, needs `better-sqlite3` (and `@types/better-sqlite3` in TypeScript) |
|
|
82
82
|
| `@cronwatch/sdk/postgres` | `postgres({ connectionString })`, needs `pg` (and `@types/pg` in TypeScript) |
|
|
83
83
|
| `@cronwatch/sdk/d1` | `d1(env.DB)`, the Cloudflare D1 store; needs nothing |
|
|
84
84
|
| `@cronwatch/sdk/pg-cron` | `pgCron(pool)`, a source that reads pg_cron jobs (Supabase Cron included) from Postgres on every check; pass it in `sources` |
|
|
85
85
|
| `@cronwatch/sdk/node` | `toNodeHandler`, `toKoaMiddleware`, `toRequest`, `writeResponse`: fetch handlers on `http.createServer`, Express, Connect, NestJS and Koa |
|
|
86
|
-
| `@cronwatch/sdk/slack`, `/discord`, `/webhook` | chat and signed webhook alert channels |
|
|
86
|
+
| `@cronwatch/sdk/slack`, `/discord`, `/webhook` | chat and signed webhook alert channels; `/webhook` also has `signature(secret, body)` for a receiver |
|
|
87
87
|
| `@cronwatch/sdk/resend`, `/postmark`, `/sendgrid`, `/mailgun`, `/ses` | email alert channels |
|
|
88
88
|
| `@cronwatch/sdk/twilio` | SMS alerts |
|
|
89
89
|
| `@cronwatch/sdk/sentry`, `/honeybadger`, `/datadog`, `/rollbar`, `/bugsnag`, `/newrelic` | alerts as events in an error tracker |
|
package/dist/anthropic.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import { o as TriageFn } from './types-
|
|
2
|
+
import { o as TriageFn } from './types-DLnlUie8.cjs';
|
|
3
3
|
|
|
4
4
|
interface AnthropicTriageOptions {
|
|
5
5
|
/** Defaults to what the Anthropic SDK resolves: ANTHROPIC_API_KEY, or an `ant auth login` profile. */
|
package/dist/anthropic.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import { o as TriageFn } from './types-
|
|
2
|
+
import { o as TriageFn } from './types-DLnlUie8.js';
|
|
3
3
|
|
|
4
4
|
interface AnthropicTriageOptions {
|
|
5
5
|
/** Defaults to what the Anthropic SDK resolves: ANTHROPIC_API_KEY, or an `ant auth login` profile. */
|
package/dist/bugsnag.d.cts
CHANGED
package/dist/bugsnag.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as JobOptions, R as Run, A as Alert, S as Store, a as AlertChannel, o as TriageFn, D as Duration, h as JobDefinition, f as CheckResult, k as JobSummary, j as JobState } from './types-DLnlUie8.cjs';
|
|
2
2
|
|
|
3
3
|
/** What a job function receives. */
|
|
4
4
|
interface JobContext {
|
|
@@ -19,10 +19,14 @@ interface RoutesOptions {
|
|
|
19
19
|
* Required to reach anything. Send it as `Authorization: Bearer <token>`,
|
|
20
20
|
* or open the dashboard once with `?token=<token>` and a cookie is set.
|
|
21
21
|
* Defaults to process.env.CRONWATCH_TOKEN (on Cloudflare Workers, which
|
|
22
|
-
* have no process, pass env.CRONWATCH_TOKEN); an empty string
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* have no process, pass env.CRONWATCH_TOKEN); an empty string or one of
|
|
23
|
+
* only whitespace, given here or in the variable, counts as unset, and any
|
|
24
|
+
* other type (false, a number) throws.
|
|
25
|
+
* With no token in development (the first of CRONWATCH_ENV, APP_ENV and
|
|
26
|
+
* NODE_ENV that is set names "development", "dev", "local", "test" or
|
|
27
|
+
* "testing"), the routes make a random one and print a sign-in link to the
|
|
28
|
+
* server log on their first request; with no token otherwise they answer
|
|
29
|
+
* 503. Pass `null` to opt out
|
|
26
30
|
* and serve them open everywhere, for example behind your own auth.
|
|
27
31
|
*
|
|
28
32
|
* The check endpoint (/api/check) also accepts the client's cronSecret, so
|
|
@@ -62,12 +66,11 @@ interface Routes {
|
|
|
62
66
|
DELETE: FetchHandler;
|
|
63
67
|
}
|
|
64
68
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* export const { GET, POST, DELETE } = cw.routes();
|
|
69
|
+
* The dashboard and its JSON API for a client.
|
|
70
|
+
* @deprecated Use `cw.routes(options)`, the one way to mount the dashboard
|
|
71
|
+
* in every port. This name still works through 1.x and goes in 2.0.
|
|
69
72
|
*/
|
|
70
|
-
declare function createRoutes(cw:
|
|
73
|
+
declare function createRoutes(cw: Cronwatch, options?: RoutesOptions): Routes;
|
|
71
74
|
|
|
72
75
|
type JobFn<T> = (job: JobContext) => Promise<T> | T;
|
|
73
76
|
type HandlerFn<T> = (job: JobContext, request: Request) => Promise<T> | T;
|
|
@@ -75,9 +78,12 @@ interface HandlerOptions {
|
|
|
75
78
|
/**
|
|
76
79
|
* Callers must send `Authorization: Bearer <secret>`. Defaults to the
|
|
77
80
|
* client's cronSecret, which defaults to process.env.CRON_SECRET (what
|
|
78
|
-
* Vercel sends its cron requests with). An empty string
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
+
* Vercel sends its cron requests with). An empty string, or one of only
|
|
82
|
+
* whitespace, counts as unset; any other type (false, a number) throws.
|
|
83
|
+
* With no secret at all the handler answers 503 unless the app is in
|
|
84
|
+
* development (the first of CRONWATCH_ENV, APP_ENV and NODE_ENV that is
|
|
85
|
+
* set names "development", "dev", "local", "test" or "testing"). Pass
|
|
86
|
+
* null to allow anyone.
|
|
81
87
|
*/
|
|
82
88
|
secret?: string | null;
|
|
83
89
|
}
|
|
@@ -162,13 +168,13 @@ interface RunHandle {
|
|
|
162
168
|
}
|
|
163
169
|
/**
|
|
164
170
|
* What a Source may use of the client: declare jobs, read the store, and
|
|
165
|
-
* record runs it found elsewhere. A
|
|
171
|
+
* record runs it found elsewhere. A Cronwatch is one.
|
|
166
172
|
*/
|
|
167
173
|
interface SourceHost {
|
|
168
|
-
/** Declare a job, as
|
|
174
|
+
/** Declare a job, as Cronwatch.job(). Throws for an invalid name or option. */
|
|
169
175
|
job(name: string, options?: JobOptions): unknown;
|
|
170
176
|
/**
|
|
171
|
-
* The definitions declared now, as
|
|
177
|
+
* The definitions declared now, as Cronwatch.definedJobs(): a source reads
|
|
172
178
|
* it to declare again a job forgotten since (the dashboard's forget), whose
|
|
173
179
|
* definition has not changed. A host without it is taken to keep every
|
|
174
180
|
* job the source declared.
|
|
@@ -176,7 +182,7 @@ interface SourceHost {
|
|
|
176
182
|
definedJobs?(): {
|
|
177
183
|
name: string;
|
|
178
184
|
}[];
|
|
179
|
-
/** See
|
|
185
|
+
/** See Cronwatch.recordRun(). */
|
|
180
186
|
recordRun(run: Run, options?: RecordRunOptions): Promise<Alert[]>;
|
|
181
187
|
readonly store: Store;
|
|
182
188
|
readonly now: () => number;
|
|
@@ -196,7 +202,7 @@ interface RecordRunOptions {
|
|
|
196
202
|
/** False stores the run without evaluating it, for history imported on first sight. Default true. */
|
|
197
203
|
evaluate?: boolean;
|
|
198
204
|
}
|
|
199
|
-
interface
|
|
205
|
+
interface CronwatchOptions {
|
|
200
206
|
/** Where jobs, runs and state live. Defaults to an in-memory store that forgets on restart. */
|
|
201
207
|
store?: Store;
|
|
202
208
|
/**
|
|
@@ -212,8 +218,9 @@ interface CronWatchOptions {
|
|
|
212
218
|
/**
|
|
213
219
|
* Shared secret that handler() requests must carry. Defaults to
|
|
214
220
|
* process.env.CRON_SECRET (on Cloudflare Workers, which have no process,
|
|
215
|
-
* pass env.CRON_SECRET); an empty string
|
|
216
|
-
* let handlers run
|
|
221
|
+
* pass env.CRON_SECRET); an empty string or one of only whitespace, given
|
|
222
|
+
* here or in the variable, counts as unset. Pass null to let handlers run
|
|
223
|
+
* without one. Any other type (false, a number) throws.
|
|
217
224
|
*/
|
|
218
225
|
cronSecret?: string | null;
|
|
219
226
|
/** How long finished runs are kept. Default "30d". */
|
|
@@ -243,7 +250,7 @@ interface CronWatchOptions {
|
|
|
243
250
|
/** The clock. Tests use this. */
|
|
244
251
|
now?: () => number;
|
|
245
252
|
}
|
|
246
|
-
declare class
|
|
253
|
+
declare class Cronwatch {
|
|
247
254
|
readonly store: Store;
|
|
248
255
|
readonly alerts: AlertChannel[];
|
|
249
256
|
readonly triage: TriageFn | undefined;
|
|
@@ -256,7 +263,7 @@ declare class CronWatch {
|
|
|
256
263
|
/** cronSecret was passed as null: handlers may run without a secret. */
|
|
257
264
|
private readonly secretOptOut;
|
|
258
265
|
private readonly redact;
|
|
259
|
-
/** "check": queue alerts for another process's check instead of sending them. See
|
|
266
|
+
/** "check": queue alerts for another process's check instead of sending them. See CronwatchOptions.deliver. */
|
|
260
267
|
private readonly deferDelivery;
|
|
261
268
|
private readonly defaults;
|
|
262
269
|
private readonly definitions;
|
|
@@ -275,7 +282,9 @@ declare class CronWatch {
|
|
|
275
282
|
private usingDefaultStore;
|
|
276
283
|
private warnedNoSecret;
|
|
277
284
|
private warnedDeferredStart;
|
|
278
|
-
|
|
285
|
+
/** Stored jobs read with a definition that was not a JSON object (see readJob). */
|
|
286
|
+
private readonly unreadable;
|
|
287
|
+
constructor(options?: CronwatchOptions);
|
|
279
288
|
/** Declare a job. Call it once, at module level, and keep the handle. */
|
|
280
289
|
job(name: string, options?: JobOptions): JobHandle;
|
|
281
290
|
/** Run a job by name without keeping a handle. Defines it on first use. */
|
|
@@ -313,6 +322,10 @@ declare class CronWatch {
|
|
|
313
322
|
* checked and shown while any process still declares it.
|
|
314
323
|
*/
|
|
315
324
|
private storedJobs;
|
|
325
|
+
/** A stored job read leniently (readStoredJob); one whose definition is unreadable is remembered, so that evaluating it throws. */
|
|
326
|
+
private readJob;
|
|
327
|
+
/** Throws for a job whose stored definition was not a JSON object: reported, and shown as failing, while the others carry on. */
|
|
328
|
+
private evaluable;
|
|
316
329
|
/**
|
|
317
330
|
* Runs `fn` after every earlier state update for the same job has settled,
|
|
318
331
|
* so two runs (or a run and a check) in this process never read and write
|
|
@@ -464,14 +477,27 @@ declare class CronWatch {
|
|
|
464
477
|
* it on its next run there, or at that process's next check or dashboard read.
|
|
465
478
|
*/
|
|
466
479
|
forget(name: string): Promise<void>;
|
|
467
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* The dashboard and JSON API as fetch-style handlers: the one way to mount
|
|
482
|
+
* them. A Next.js app exports them from app/cronwatch/[[...path]]/route.ts:
|
|
483
|
+
*
|
|
484
|
+
* export const { GET, POST, DELETE } = cw.routes();
|
|
485
|
+
*/
|
|
468
486
|
routes(options?: RoutesOptions): Routes;
|
|
469
487
|
/**
|
|
470
488
|
* Check on an interval, for long-running servers. Default every minute.
|
|
471
489
|
* Not for serverless functions or Cloudflare Workers, where nothing runs
|
|
472
|
-
* between requests: call check() from a cron there instead.
|
|
490
|
+
* between requests: call check() from a cron there instead. (A job's
|
|
491
|
+
* start() opens a run; this starts the checks.)
|
|
492
|
+
*/
|
|
493
|
+
startChecking(every?: Duration): void;
|
|
494
|
+
/**
|
|
495
|
+
* startChecking(), under its former name.
|
|
496
|
+
* @deprecated Renamed `startChecking`, since a job's `start()` opens a run.
|
|
497
|
+
* This name still works through 1.x and goes in 2.0.
|
|
473
498
|
*/
|
|
474
499
|
start(every?: Duration): void;
|
|
500
|
+
/** Stop the checks startChecking() began. */
|
|
475
501
|
stop(): void;
|
|
476
502
|
/**
|
|
477
503
|
* Stop the interval, wait for a check already under way (bounded by its
|
|
@@ -516,4 +542,15 @@ declare function consoleChannel(): AlertChannel;
|
|
|
516
542
|
/** Wrap any function as an alert channel. */
|
|
517
543
|
declare function custom(name: string, send: (alert: Alert) => Promise<void> | void): AlertChannel;
|
|
518
544
|
|
|
519
|
-
|
|
545
|
+
/**
|
|
546
|
+
* The client's former name.
|
|
547
|
+
* @deprecated Renamed `Cronwatch`, the spelling every port uses. This name
|
|
548
|
+
* still works through 1.x and goes in 2.0.
|
|
549
|
+
*/
|
|
550
|
+
declare const CronWatch: typeof Cronwatch;
|
|
551
|
+
/** @deprecated Renamed `Cronwatch`; this name goes in 2.0. */
|
|
552
|
+
type CronWatch = Cronwatch;
|
|
553
|
+
/** @deprecated Renamed `CronwatchOptions`; this name goes in 2.0. */
|
|
554
|
+
type CronWatchOptions = CronwatchOptions;
|
|
555
|
+
|
|
556
|
+
export { type CronwatchOptions as C, type FetchHandler as F, type HandlerFn as H, type JobContext as J, type RecordRunOptions as R, type Source as S, Cronwatch as a, CronWatch as b, type CronWatchOptions as c, type HandlerOptions as d, type JobFn as e, type JobHandle as f, type Routes as g, type RoutesOptions as h, type RunHandle as i, type RunOutcome as j, type SourceHost as k, type StartOptions as l, consoleChannel as m, createRoutes as n, custom as o };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as JobOptions, R as Run, A as Alert, S as Store, a as AlertChannel, o as TriageFn, D as Duration, h as JobDefinition, f as CheckResult, k as JobSummary, j as JobState } from './types-DLnlUie8.js';
|
|
2
2
|
|
|
3
3
|
/** What a job function receives. */
|
|
4
4
|
interface JobContext {
|
|
@@ -19,10 +19,14 @@ interface RoutesOptions {
|
|
|
19
19
|
* Required to reach anything. Send it as `Authorization: Bearer <token>`,
|
|
20
20
|
* or open the dashboard once with `?token=<token>` and a cookie is set.
|
|
21
21
|
* Defaults to process.env.CRONWATCH_TOKEN (on Cloudflare Workers, which
|
|
22
|
-
* have no process, pass env.CRONWATCH_TOKEN); an empty string
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* have no process, pass env.CRONWATCH_TOKEN); an empty string or one of
|
|
23
|
+
* only whitespace, given here or in the variable, counts as unset, and any
|
|
24
|
+
* other type (false, a number) throws.
|
|
25
|
+
* With no token in development (the first of CRONWATCH_ENV, APP_ENV and
|
|
26
|
+
* NODE_ENV that is set names "development", "dev", "local", "test" or
|
|
27
|
+
* "testing"), the routes make a random one and print a sign-in link to the
|
|
28
|
+
* server log on their first request; with no token otherwise they answer
|
|
29
|
+
* 503. Pass `null` to opt out
|
|
26
30
|
* and serve them open everywhere, for example behind your own auth.
|
|
27
31
|
*
|
|
28
32
|
* The check endpoint (/api/check) also accepts the client's cronSecret, so
|
|
@@ -62,12 +66,11 @@ interface Routes {
|
|
|
62
66
|
DELETE: FetchHandler;
|
|
63
67
|
}
|
|
64
68
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* export const { GET, POST, DELETE } = cw.routes();
|
|
69
|
+
* The dashboard and its JSON API for a client.
|
|
70
|
+
* @deprecated Use `cw.routes(options)`, the one way to mount the dashboard
|
|
71
|
+
* in every port. This name still works through 1.x and goes in 2.0.
|
|
69
72
|
*/
|
|
70
|
-
declare function createRoutes(cw:
|
|
73
|
+
declare function createRoutes(cw: Cronwatch, options?: RoutesOptions): Routes;
|
|
71
74
|
|
|
72
75
|
type JobFn<T> = (job: JobContext) => Promise<T> | T;
|
|
73
76
|
type HandlerFn<T> = (job: JobContext, request: Request) => Promise<T> | T;
|
|
@@ -75,9 +78,12 @@ interface HandlerOptions {
|
|
|
75
78
|
/**
|
|
76
79
|
* Callers must send `Authorization: Bearer <secret>`. Defaults to the
|
|
77
80
|
* client's cronSecret, which defaults to process.env.CRON_SECRET (what
|
|
78
|
-
* Vercel sends its cron requests with). An empty string
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
+
* Vercel sends its cron requests with). An empty string, or one of only
|
|
82
|
+
* whitespace, counts as unset; any other type (false, a number) throws.
|
|
83
|
+
* With no secret at all the handler answers 503 unless the app is in
|
|
84
|
+
* development (the first of CRONWATCH_ENV, APP_ENV and NODE_ENV that is
|
|
85
|
+
* set names "development", "dev", "local", "test" or "testing"). Pass
|
|
86
|
+
* null to allow anyone.
|
|
81
87
|
*/
|
|
82
88
|
secret?: string | null;
|
|
83
89
|
}
|
|
@@ -162,13 +168,13 @@ interface RunHandle {
|
|
|
162
168
|
}
|
|
163
169
|
/**
|
|
164
170
|
* What a Source may use of the client: declare jobs, read the store, and
|
|
165
|
-
* record runs it found elsewhere. A
|
|
171
|
+
* record runs it found elsewhere. A Cronwatch is one.
|
|
166
172
|
*/
|
|
167
173
|
interface SourceHost {
|
|
168
|
-
/** Declare a job, as
|
|
174
|
+
/** Declare a job, as Cronwatch.job(). Throws for an invalid name or option. */
|
|
169
175
|
job(name: string, options?: JobOptions): unknown;
|
|
170
176
|
/**
|
|
171
|
-
* The definitions declared now, as
|
|
177
|
+
* The definitions declared now, as Cronwatch.definedJobs(): a source reads
|
|
172
178
|
* it to declare again a job forgotten since (the dashboard's forget), whose
|
|
173
179
|
* definition has not changed. A host without it is taken to keep every
|
|
174
180
|
* job the source declared.
|
|
@@ -176,7 +182,7 @@ interface SourceHost {
|
|
|
176
182
|
definedJobs?(): {
|
|
177
183
|
name: string;
|
|
178
184
|
}[];
|
|
179
|
-
/** See
|
|
185
|
+
/** See Cronwatch.recordRun(). */
|
|
180
186
|
recordRun(run: Run, options?: RecordRunOptions): Promise<Alert[]>;
|
|
181
187
|
readonly store: Store;
|
|
182
188
|
readonly now: () => number;
|
|
@@ -196,7 +202,7 @@ interface RecordRunOptions {
|
|
|
196
202
|
/** False stores the run without evaluating it, for history imported on first sight. Default true. */
|
|
197
203
|
evaluate?: boolean;
|
|
198
204
|
}
|
|
199
|
-
interface
|
|
205
|
+
interface CronwatchOptions {
|
|
200
206
|
/** Where jobs, runs and state live. Defaults to an in-memory store that forgets on restart. */
|
|
201
207
|
store?: Store;
|
|
202
208
|
/**
|
|
@@ -212,8 +218,9 @@ interface CronWatchOptions {
|
|
|
212
218
|
/**
|
|
213
219
|
* Shared secret that handler() requests must carry. Defaults to
|
|
214
220
|
* process.env.CRON_SECRET (on Cloudflare Workers, which have no process,
|
|
215
|
-
* pass env.CRON_SECRET); an empty string
|
|
216
|
-
* let handlers run
|
|
221
|
+
* pass env.CRON_SECRET); an empty string or one of only whitespace, given
|
|
222
|
+
* here or in the variable, counts as unset. Pass null to let handlers run
|
|
223
|
+
* without one. Any other type (false, a number) throws.
|
|
217
224
|
*/
|
|
218
225
|
cronSecret?: string | null;
|
|
219
226
|
/** How long finished runs are kept. Default "30d". */
|
|
@@ -243,7 +250,7 @@ interface CronWatchOptions {
|
|
|
243
250
|
/** The clock. Tests use this. */
|
|
244
251
|
now?: () => number;
|
|
245
252
|
}
|
|
246
|
-
declare class
|
|
253
|
+
declare class Cronwatch {
|
|
247
254
|
readonly store: Store;
|
|
248
255
|
readonly alerts: AlertChannel[];
|
|
249
256
|
readonly triage: TriageFn | undefined;
|
|
@@ -256,7 +263,7 @@ declare class CronWatch {
|
|
|
256
263
|
/** cronSecret was passed as null: handlers may run without a secret. */
|
|
257
264
|
private readonly secretOptOut;
|
|
258
265
|
private readonly redact;
|
|
259
|
-
/** "check": queue alerts for another process's check instead of sending them. See
|
|
266
|
+
/** "check": queue alerts for another process's check instead of sending them. See CronwatchOptions.deliver. */
|
|
260
267
|
private readonly deferDelivery;
|
|
261
268
|
private readonly defaults;
|
|
262
269
|
private readonly definitions;
|
|
@@ -275,7 +282,9 @@ declare class CronWatch {
|
|
|
275
282
|
private usingDefaultStore;
|
|
276
283
|
private warnedNoSecret;
|
|
277
284
|
private warnedDeferredStart;
|
|
278
|
-
|
|
285
|
+
/** Stored jobs read with a definition that was not a JSON object (see readJob). */
|
|
286
|
+
private readonly unreadable;
|
|
287
|
+
constructor(options?: CronwatchOptions);
|
|
279
288
|
/** Declare a job. Call it once, at module level, and keep the handle. */
|
|
280
289
|
job(name: string, options?: JobOptions): JobHandle;
|
|
281
290
|
/** Run a job by name without keeping a handle. Defines it on first use. */
|
|
@@ -313,6 +322,10 @@ declare class CronWatch {
|
|
|
313
322
|
* checked and shown while any process still declares it.
|
|
314
323
|
*/
|
|
315
324
|
private storedJobs;
|
|
325
|
+
/** A stored job read leniently (readStoredJob); one whose definition is unreadable is remembered, so that evaluating it throws. */
|
|
326
|
+
private readJob;
|
|
327
|
+
/** Throws for a job whose stored definition was not a JSON object: reported, and shown as failing, while the others carry on. */
|
|
328
|
+
private evaluable;
|
|
316
329
|
/**
|
|
317
330
|
* Runs `fn` after every earlier state update for the same job has settled,
|
|
318
331
|
* so two runs (or a run and a check) in this process never read and write
|
|
@@ -464,14 +477,27 @@ declare class CronWatch {
|
|
|
464
477
|
* it on its next run there, or at that process's next check or dashboard read.
|
|
465
478
|
*/
|
|
466
479
|
forget(name: string): Promise<void>;
|
|
467
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* The dashboard and JSON API as fetch-style handlers: the one way to mount
|
|
482
|
+
* them. A Next.js app exports them from app/cronwatch/[[...path]]/route.ts:
|
|
483
|
+
*
|
|
484
|
+
* export const { GET, POST, DELETE } = cw.routes();
|
|
485
|
+
*/
|
|
468
486
|
routes(options?: RoutesOptions): Routes;
|
|
469
487
|
/**
|
|
470
488
|
* Check on an interval, for long-running servers. Default every minute.
|
|
471
489
|
* Not for serverless functions or Cloudflare Workers, where nothing runs
|
|
472
|
-
* between requests: call check() from a cron there instead.
|
|
490
|
+
* between requests: call check() from a cron there instead. (A job's
|
|
491
|
+
* start() opens a run; this starts the checks.)
|
|
492
|
+
*/
|
|
493
|
+
startChecking(every?: Duration): void;
|
|
494
|
+
/**
|
|
495
|
+
* startChecking(), under its former name.
|
|
496
|
+
* @deprecated Renamed `startChecking`, since a job's `start()` opens a run.
|
|
497
|
+
* This name still works through 1.x and goes in 2.0.
|
|
473
498
|
*/
|
|
474
499
|
start(every?: Duration): void;
|
|
500
|
+
/** Stop the checks startChecking() began. */
|
|
475
501
|
stop(): void;
|
|
476
502
|
/**
|
|
477
503
|
* Stop the interval, wait for a check already under way (bounded by its
|
|
@@ -516,4 +542,15 @@ declare function consoleChannel(): AlertChannel;
|
|
|
516
542
|
/** Wrap any function as an alert channel. */
|
|
517
543
|
declare function custom(name: string, send: (alert: Alert) => Promise<void> | void): AlertChannel;
|
|
518
544
|
|
|
519
|
-
|
|
545
|
+
/**
|
|
546
|
+
* The client's former name.
|
|
547
|
+
* @deprecated Renamed `Cronwatch`, the spelling every port uses. This name
|
|
548
|
+
* still works through 1.x and goes in 2.0.
|
|
549
|
+
*/
|
|
550
|
+
declare const CronWatch: typeof Cronwatch;
|
|
551
|
+
/** @deprecated Renamed `Cronwatch`; this name goes in 2.0. */
|
|
552
|
+
type CronWatch = Cronwatch;
|
|
553
|
+
/** @deprecated Renamed `CronwatchOptions`; this name goes in 2.0. */
|
|
554
|
+
type CronWatchOptions = CronwatchOptions;
|
|
555
|
+
|
|
556
|
+
export { type CronwatchOptions as C, type FetchHandler as F, type HandlerFn as H, type JobContext as J, type RecordRunOptions as R, type Source as S, Cronwatch as a, CronWatch as b, type CronWatchOptions as c, type HandlerOptions as d, type JobFn as e, type JobHandle as f, type Routes as g, type RoutesOptions as h, type RunHandle as i, type RunOutcome as j, type SourceHost as k, type StartOptions as l, consoleChannel as m, createRoutes as n, custom as o };
|
package/dist/d1.cjs
CHANGED
|
@@ -54,7 +54,7 @@ function statements(dialect, p) {
|
|
|
54
54
|
const byName = "name";
|
|
55
55
|
const version = (column) => {
|
|
56
56
|
const v = `json_extract(${column}, '$.version')`;
|
|
57
|
-
return `CASE WHEN json_type(${column}, '$.version') NOT IN ('integer', 'real') THEN 0 WHEN ${v} = CAST(${v} AS INTEGER) AND ${v} BETWEEN 0 AND 9007199254740991 THEN CAST(${v} AS INTEGER) ELSE 0 END`;
|
|
57
|
+
return `CASE WHEN NOT json_valid(${column}) THEN 0 WHEN json_type(${column}, '$.version') NOT IN ('integer', 'real') THEN 0 WHEN ${v} = CAST(${v} AS INTEGER) AND ${v} BETWEEN 0 AND 9007199254740991 THEN CAST(${v} AS INTEGER) ELSE 0 END`;
|
|
58
58
|
};
|
|
59
59
|
const sql = {
|
|
60
60
|
upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)
|
|
@@ -119,23 +119,41 @@ var params = {
|
|
|
119
119
|
casInsert: (state) => [state.job, jsonText(state)],
|
|
120
120
|
casUpdate: (state, expectedVersion) => [jsonText(state), state.job, expectedVersion]
|
|
121
121
|
};
|
|
122
|
-
var json = (v) =>
|
|
123
|
-
|
|
122
|
+
var json = (v) => {
|
|
123
|
+
if (typeof v !== "string") return v;
|
|
124
|
+
try {
|
|
125
|
+
return JSON.parse(v);
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var number = (v) => typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : Number.NaN;
|
|
131
|
+
var time = (v) => {
|
|
132
|
+
const n = number(v);
|
|
133
|
+
return Number.isFinite(n) ? n : 0;
|
|
134
|
+
};
|
|
135
|
+
var maybeTime = (v) => {
|
|
136
|
+
const n = number(v);
|
|
137
|
+
return Number.isFinite(n) ? n : null;
|
|
138
|
+
};
|
|
139
|
+
var textOrNull = (v) => typeof v === "string" ? v : null;
|
|
140
|
+
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
124
141
|
function rowToJob(r) {
|
|
125
|
-
return { name: r.name, definition: json(r.definition), createdAt:
|
|
142
|
+
return { name: r.name, definition: json(r.definition), createdAt: time(r.created_at), updatedAt: time(r.updated_at) };
|
|
126
143
|
}
|
|
127
144
|
function rowToRun(r) {
|
|
145
|
+
const metrics = json(r.metrics);
|
|
128
146
|
return {
|
|
129
147
|
id: r.id,
|
|
130
148
|
job: r.job,
|
|
131
149
|
status: r.status,
|
|
132
|
-
startedAt:
|
|
133
|
-
finishedAt:
|
|
134
|
-
durationMs:
|
|
135
|
-
error: r.error,
|
|
136
|
-
output: r.output,
|
|
137
|
-
metrics:
|
|
138
|
-
trigger: r.trigger
|
|
150
|
+
startedAt: time(r.started_at),
|
|
151
|
+
finishedAt: maybeTime(r.finished_at),
|
|
152
|
+
durationMs: maybeTime(r.duration_ms),
|
|
153
|
+
error: textOrNull(r.error),
|
|
154
|
+
output: textOrNull(r.output),
|
|
155
|
+
metrics: isObject(metrics) ? metrics : {},
|
|
156
|
+
trigger: typeof r.trigger === "string" ? r.trigger : "run"
|
|
139
157
|
};
|
|
140
158
|
}
|
|
141
159
|
function rowToState(r) {
|