@cronwatch/sdk 0.3.1 → 0.4.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 +8 -2
- package/dist/anthropic.d.cts +1 -1
- package/dist/anthropic.d.ts +1 -1
- package/dist/bugsnag.cjs +108 -0
- package/dist/bugsnag.cjs.map +1 -0
- package/dist/bugsnag.d.cts +26 -0
- package/dist/bugsnag.d.ts +26 -0
- package/dist/bugsnag.js +106 -0
- package/dist/bugsnag.js.map +1 -0
- package/dist/client-B7bqnkI5.d.cts +450 -0
- package/dist/client-pmqH3apS.d.ts +450 -0
- package/dist/d1.cjs +214 -0
- package/dist/d1.cjs.map +1 -0
- package/dist/d1.d.cts +42 -0
- package/dist/d1.d.ts +42 -0
- package/dist/d1.js +212 -0
- package/dist/d1.js.map +1 -0
- package/dist/datadog.cjs +95 -0
- package/dist/datadog.cjs.map +1 -0
- package/dist/datadog.d.cts +24 -0
- package/dist/datadog.d.ts +24 -0
- package/dist/datadog.js +93 -0
- package/dist/datadog.js.map +1 -0
- package/dist/discord.cjs +2 -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 +2 -0
- package/dist/discord.js.map +1 -1
- package/dist/email-BJ-z_epD.d.cts +21 -0
- package/dist/email-_H0faCh3.d.ts +21 -0
- package/dist/honeybadger.cjs +101 -0
- package/dist/honeybadger.cjs.map +1 -0
- package/dist/honeybadger.d.cts +23 -0
- package/dist/honeybadger.d.ts +23 -0
- package/dist/honeybadger.js +99 -0
- package/dist/honeybadger.js.map +1 -0
- package/dist/index.cjs +462 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -260
- package/dist/index.d.ts +5 -260
- package/dist/index.js +462 -45
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs +115 -0
- package/dist/mailgun.cjs.map +1 -0
- package/dist/mailgun.d.cts +21 -0
- package/dist/mailgun.d.ts +21 -0
- package/dist/mailgun.js +113 -0
- package/dist/mailgun.js.map +1 -0
- package/dist/newrelic.cjs +88 -0
- package/dist/newrelic.cjs.map +1 -0
- package/dist/newrelic.d.cts +25 -0
- package/dist/newrelic.d.ts +25 -0
- package/dist/newrelic.js +86 -0
- package/dist/newrelic.js.map +1 -0
- package/dist/node.cjs +191 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +97 -0
- package/dist/node.d.ts +97 -0
- package/dist/node.js +186 -0
- package/dist/node.js.map +1 -0
- package/dist/pg-cron.cjs +286 -0
- package/dist/pg-cron.cjs.map +1 -0
- package/dist/pg-cron.d.cts +93 -0
- package/dist/pg-cron.d.ts +93 -0
- package/dist/pg-cron.js +280 -0
- package/dist/pg-cron.js.map +1 -0
- package/dist/postgres.cjs +20 -0
- 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 +20 -0
- package/dist/postgres.js.map +1 -1
- package/dist/postmark.cjs +106 -0
- package/dist/postmark.cjs.map +1 -0
- package/dist/postmark.d.cts +18 -0
- package/dist/postmark.d.ts +18 -0
- package/dist/postmark.js +104 -0
- package/dist/postmark.js.map +1 -0
- package/dist/resend.cjs +114 -0
- package/dist/resend.cjs.map +1 -0
- package/dist/resend.d.cts +16 -0
- package/dist/resend.d.ts +16 -0
- package/dist/resend.js +112 -0
- package/dist/resend.js.map +1 -0
- package/dist/rollbar.cjs +108 -0
- package/dist/rollbar.cjs.map +1 -0
- package/dist/rollbar.d.cts +20 -0
- package/dist/rollbar.d.ts +20 -0
- package/dist/rollbar.js +106 -0
- package/dist/rollbar.js.map +1 -0
- package/dist/sendgrid.cjs +114 -0
- package/dist/sendgrid.cjs.map +1 -0
- package/dist/sendgrid.d.cts +19 -0
- package/dist/sendgrid.d.ts +19 -0
- package/dist/sendgrid.js +112 -0
- package/dist/sendgrid.js.map +1 -0
- package/dist/sentry.cjs +130 -0
- package/dist/sentry.cjs.map +1 -0
- package/dist/sentry.d.cts +28 -0
- package/dist/sentry.d.ts +28 -0
- package/dist/sentry.js +127 -0
- package/dist/sentry.js.map +1 -0
- package/dist/ses.cjs +170 -0
- package/dist/ses.cjs.map +1 -0
- package/dist/ses.d.cts +25 -0
- package/dist/ses.d.ts +25 -0
- package/dist/ses.js +168 -0
- package/dist/ses.js.map +1 -0
- package/dist/slack.cjs +2 -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 +2 -0
- package/dist/slack.js.map +1 -1
- package/dist/sqlite.cjs +18 -0
- 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 +18 -0
- package/dist/sqlite.js.map +1 -1
- package/dist/twilio.cjs +168 -0
- package/dist/twilio.cjs.map +1 -0
- package/dist/twilio.d.cts +52 -0
- package/dist/twilio.d.ts +52 -0
- package/dist/twilio.js +162 -0
- package/dist/twilio.js.map +1 -0
- package/dist/{types-DUt2apl0.d.cts → types-Ddq1MUPL.d.cts} +29 -2
- package/dist/{types-DUt2apl0.d.ts → types-Ddq1MUPL.d.ts} +29 -2
- package/dist/webhook.cjs +11 -5
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +6 -3
- package/dist/webhook.d.ts +6 -3
- package/dist/webhook.js +11 -6
- package/dist/webhook.js.map +1 -1
- package/package.json +208 -2
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { S as Store, a as AlertChannel, n as TriageFn, i as JobOptions, R as Run, A as Alert, D as Duration, J as JobDefinition, f as CheckResult, k as JobSummary, j as JobState } from './types-Ddq1MUPL.js';
|
|
2
|
+
|
|
3
|
+
/** What a job function receives. */
|
|
4
|
+
interface JobContext {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly runId: string;
|
|
7
|
+
readonly startedAt: number;
|
|
8
|
+
/** Aborts when the job's timeout elapses. Honour it if the work can stop. */
|
|
9
|
+
readonly signal: AbortSignal;
|
|
10
|
+
/** Append a line of output. Kept with the run, capped at 16 KB, shown in alerts and the dashboard. */
|
|
11
|
+
log(...parts: unknown[]): void;
|
|
12
|
+
/** Report a number for this run: tokens, cost, rows, anything. Watched against budgets and baselines. */
|
|
13
|
+
metric(name: string, value: number): void;
|
|
14
|
+
metrics(values: Record<string, number>): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RoutesOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Required to reach anything. Send it as `Authorization: Bearer <token>`,
|
|
20
|
+
* or open the dashboard once with `?token=<token>` and a cookie is set.
|
|
21
|
+
* Defaults to process.env.CRONWATCH_TOKEN (on Cloudflare Workers, which
|
|
22
|
+
* have no process, pass env.CRONWATCH_TOKEN); an empty string counts as unset.
|
|
23
|
+
* With no token while NODE_ENV is "development" or "test", the routes make
|
|
24
|
+
* a random one and print a sign-in link to the server log on their first
|
|
25
|
+
* request; with no token otherwise they answer 503. Pass `null` to opt out
|
|
26
|
+
* and serve them open everywhere, for example behind your own auth.
|
|
27
|
+
*
|
|
28
|
+
* The check endpoint (/api/check) also accepts the client's cronSecret, so
|
|
29
|
+
* a platform cron that sends `Authorization: Bearer <CRON_SECRET>` can
|
|
30
|
+
* trigger checks without knowing the dashboard token.
|
|
31
|
+
*/
|
|
32
|
+
token?: string | null;
|
|
33
|
+
/** Where the routes are mounted, so links resolve. Default "/cronwatch". */
|
|
34
|
+
basePath?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The public origin the dashboard is served from, such as
|
|
37
|
+
* "https://app.example.com", for an app behind a proxy whose request URLs
|
|
38
|
+
* carry an internal host or scheme. Used in place of the request URL's
|
|
39
|
+
* origin for the cross-site check on writes, the sign-in redirect (its
|
|
40
|
+
* cookie is Secure when this is https, and the redirect back after a form
|
|
41
|
+
* follows a Referer on this origin) and the development sign-in line.
|
|
42
|
+
* Takes precedence over trustProxy.
|
|
43
|
+
*/
|
|
44
|
+
origin?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Take the public origin from X-Forwarded-Proto and X-Forwarded-Host (the
|
|
47
|
+
* first value of each, falling back to the request URL's scheme or host
|
|
48
|
+
* for whichever is missing) when a request carries either. Only for an app
|
|
49
|
+
* whose proxy sets or overwrites both headers: a client can send them too.
|
|
50
|
+
* Default false, which ignores them.
|
|
51
|
+
*/
|
|
52
|
+
trustProxy?: boolean;
|
|
53
|
+
}
|
|
54
|
+
type FetchHandler = (request: Request) => Promise<Response>;
|
|
55
|
+
interface Routes {
|
|
56
|
+
handler: FetchHandler;
|
|
57
|
+
GET: FetchHandler;
|
|
58
|
+
POST: FetchHandler;
|
|
59
|
+
DELETE: FetchHandler;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A fetch-style handler serving the dashboard and a small JSON API. Mount it
|
|
63
|
+
* in a Next.js app at app/cronwatch/[[...path]]/route.ts:
|
|
64
|
+
*
|
|
65
|
+
* export const { GET, POST, DELETE } = cw.routes();
|
|
66
|
+
*/
|
|
67
|
+
declare function createRoutes(cw: CronWatch, options?: RoutesOptions): Routes;
|
|
68
|
+
|
|
69
|
+
type JobFn<T> = (job: JobContext) => Promise<T> | T;
|
|
70
|
+
type HandlerFn<T> = (job: JobContext, request: Request) => Promise<T> | T;
|
|
71
|
+
interface HandlerOptions {
|
|
72
|
+
/**
|
|
73
|
+
* Callers must send `Authorization: Bearer <secret>`. Defaults to the
|
|
74
|
+
* client's cronSecret, which defaults to process.env.CRON_SECRET (what
|
|
75
|
+
* Vercel sends its cron requests with). An empty string counts as unset.
|
|
76
|
+
* With no secret at all the handler answers 503 unless NODE_ENV is
|
|
77
|
+
* "development" or "test". Pass null to allow anyone.
|
|
78
|
+
*/
|
|
79
|
+
secret?: string | null;
|
|
80
|
+
}
|
|
81
|
+
interface JobHandle {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly definition: JobDefinition;
|
|
84
|
+
/** Run the function now, recording the run. Rethrows whatever the function throws. */
|
|
85
|
+
run<T>(fn: JobFn<T>, options?: {
|
|
86
|
+
trigger?: string;
|
|
87
|
+
}): Promise<T>;
|
|
88
|
+
/** A fetch-style request handler (Next.js route, Hono, Bun, Deno) that runs the function and records the run. */
|
|
89
|
+
handler<T>(fn: HandlerFn<T>, options?: HandlerOptions): (request: Request) => Promise<Response>;
|
|
90
|
+
/**
|
|
91
|
+
* Record a running run now and finish it later, perhaps from another
|
|
92
|
+
* process (see resume()). Store failures go to onError; it never throws for
|
|
93
|
+
* them. A run that is never finished is marked stuck by the first check
|
|
94
|
+
* after the job's timeout.
|
|
95
|
+
*/
|
|
96
|
+
start(options?: StartOptions): Promise<RunHandle>;
|
|
97
|
+
/** A handle on a run this job started elsewhere, by its id, so this process can log to it and finish it. */
|
|
98
|
+
resume(runId: string): Promise<RunHandle>;
|
|
99
|
+
}
|
|
100
|
+
interface StartOptions {
|
|
101
|
+
/** What started the run, as with run(). Default "start". */
|
|
102
|
+
trigger?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Your own stable id for the run, such as an Inngest run id, 1 to 200
|
|
105
|
+
* characters, not starting with "pgcron:" (the pg_cron source's). A start
|
|
106
|
+
* with an id already recorded for this job records nothing and returns a
|
|
107
|
+
* handle on that run instead; an id recorded for another job throws,
|
|
108
|
+
* whether that job's start is still in flight or long done.
|
|
109
|
+
*/
|
|
110
|
+
id?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* How a started run ended. `{ error }` is a failure, recorded like an error
|
|
114
|
+
* run() caught. Otherwise the run succeeded, and `result` (or a string
|
|
115
|
+
* passed on its own) is treated like the value run()'s function returns:
|
|
116
|
+
* a string is the output when nothing was logged, `expect` is checked, and
|
|
117
|
+
* a Response with status 400 or above is a failure.
|
|
118
|
+
*/
|
|
119
|
+
type RunOutcome = {
|
|
120
|
+
status?: "ok";
|
|
121
|
+
result?: unknown;
|
|
122
|
+
} | {
|
|
123
|
+
error: unknown;
|
|
124
|
+
};
|
|
125
|
+
/** A run recorded by job.start() or found by job.resume(), to finish later. */
|
|
126
|
+
interface RunHandle {
|
|
127
|
+
readonly id: string;
|
|
128
|
+
readonly job: string;
|
|
129
|
+
/** When the run started; null when a resumed run could not be read. */
|
|
130
|
+
readonly startedAt: number | null;
|
|
131
|
+
/** False once finished, and from the start for a resumed run that already finished or does not exist. */
|
|
132
|
+
readonly active: boolean;
|
|
133
|
+
/** Add a line of output. Kept in the handle until flush() or finish(). */
|
|
134
|
+
log(...parts: unknown[]): void;
|
|
135
|
+
/** Report a number for this run. A later value for the same name replaces an earlier one. */
|
|
136
|
+
metric(name: string, value: number): void;
|
|
137
|
+
metrics(values: Record<string, number>): void;
|
|
138
|
+
/**
|
|
139
|
+
* Append the lines and metrics added so far to the stored run, which must
|
|
140
|
+
* still be running and belong to this job. A read, change and write of the
|
|
141
|
+
* run's row, written only while it is still running: two processes
|
|
142
|
+
* appending to one run at the same moment can lose one's lines, but a flush
|
|
143
|
+
* never undoes a finish. The first 16 KB of everything flushed stay in the
|
|
144
|
+
* handle, so an expect rule at finish() sees an early line as run() would.
|
|
145
|
+
*/
|
|
146
|
+
flush(): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Finish the run, judge it like any other and send what that produces.
|
|
149
|
+
* Resolves to the run as recorded, or null when nothing was recorded: the
|
|
150
|
+
* run was already finished (here or elsewhere), was not found, or belongs
|
|
151
|
+
* to another job, which is reported to onError. When several processes
|
|
152
|
+
* finish one run, only the one whose write lands judges it. Never throws
|
|
153
|
+
* for the store: a store that fails is reported, nothing is recorded, and
|
|
154
|
+
* the handle stays active so finish() can be called again.
|
|
155
|
+
*/
|
|
156
|
+
finish(outcome?: RunOutcome | string): Promise<Run | null>;
|
|
157
|
+
/** finish({ error }). */
|
|
158
|
+
fail(error: unknown): Promise<Run | null>;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* What a Source may use of the client: declare jobs, read the store, and
|
|
162
|
+
* record runs it found elsewhere. A CronWatch is one.
|
|
163
|
+
*/
|
|
164
|
+
interface SourceHost {
|
|
165
|
+
/** Declare a job, as CronWatch.job(). Throws for an invalid name or option. */
|
|
166
|
+
job(name: string, options?: JobOptions): unknown;
|
|
167
|
+
/** See CronWatch.recordRun(). */
|
|
168
|
+
recordRun(run: Run, options?: RecordRunOptions): Promise<Alert[]>;
|
|
169
|
+
readonly store: Store;
|
|
170
|
+
readonly now: () => number;
|
|
171
|
+
readonly onError: (error: unknown, where: string) => void;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Runs that happen somewhere CronWatch cannot wrap, such as inside the
|
|
175
|
+
* database (see @cronwatch/sdk/pg-cron). check() calls sync() on each source
|
|
176
|
+
* first, so what it records is evaluated in the same check.
|
|
177
|
+
*/
|
|
178
|
+
interface Source {
|
|
179
|
+
name: string;
|
|
180
|
+
/** Declare the jobs and record their new runs. Returns the alerts recording them sent. */
|
|
181
|
+
sync(host: SourceHost): Promise<Alert[] | void>;
|
|
182
|
+
}
|
|
183
|
+
interface RecordRunOptions {
|
|
184
|
+
/** False stores the run without evaluating it, for history imported on first sight. Default true. */
|
|
185
|
+
evaluate?: boolean;
|
|
186
|
+
}
|
|
187
|
+
interface CronWatchOptions {
|
|
188
|
+
/** Where jobs, runs and state live. Defaults to an in-memory store that forgets on restart. */
|
|
189
|
+
store?: Store;
|
|
190
|
+
/**
|
|
191
|
+
* Where runs this process does not wrap come from, such as pg_cron jobs.
|
|
192
|
+
* Each is synced at the start of every check(); one that throws is
|
|
193
|
+
* reported to onError and the check carries on.
|
|
194
|
+
*/
|
|
195
|
+
sources?: Source[];
|
|
196
|
+
/** Where alerts go. Defaults to the console. */
|
|
197
|
+
alerts?: AlertChannel[];
|
|
198
|
+
/** Adds a short diagnosis to every alert except recoveries. See @cronwatch/sdk/anthropic. */
|
|
199
|
+
triage?: TriageFn;
|
|
200
|
+
/**
|
|
201
|
+
* Shared secret that handler() requests must carry. Defaults to
|
|
202
|
+
* process.env.CRON_SECRET (on Cloudflare Workers, which have no process,
|
|
203
|
+
* pass env.CRON_SECRET); an empty string counts as unset. Pass null to
|
|
204
|
+
* let handlers run without one.
|
|
205
|
+
*/
|
|
206
|
+
cronSecret?: string | null;
|
|
207
|
+
/** How long finished runs are kept. Default "30d". */
|
|
208
|
+
retention?: Duration;
|
|
209
|
+
/** Applied to every job unless the job sets its own. */
|
|
210
|
+
defaults?: Pick<JobOptions, "grace" | "timeout" | "timezone" | "failuresBeforeAlert">;
|
|
211
|
+
/**
|
|
212
|
+
* Applied to every run's output and error before it is stored, shown or
|
|
213
|
+
* sent to an alert channel or triage. The default blanks values that look
|
|
214
|
+
* like secrets (password=..., Authorization headers, URL credentials, bearer
|
|
215
|
+
* tokens, JWTs, PEM private keys, webhook URLs, AWS, GitHub, Slack, Stripe,
|
|
216
|
+
* Google and API key formats). Pass your own function, or false to keep
|
|
217
|
+
* output exactly as logged. A function that throws or returns something
|
|
218
|
+
* other than a string is reported to onError and the default is used.
|
|
219
|
+
*/
|
|
220
|
+
redact?: ((text: string) => string) | false;
|
|
221
|
+
/**
|
|
222
|
+
* "now" (the default) sends alerts from this process. "check" sends nothing
|
|
223
|
+
* from here: each alert is queued in the store and the next check, in a
|
|
224
|
+
* process that delivers now, sends it (with triage). For a process that
|
|
225
|
+
* records runs but cannot reach the network, such as a sandboxed backup job.
|
|
226
|
+
* Its `alerts` and `triage` are not used.
|
|
227
|
+
*/
|
|
228
|
+
deliver?: "now" | "check";
|
|
229
|
+
/** Called with anything that goes wrong outside a job: the store failing, an alert channel failing, a triage timeout. */
|
|
230
|
+
onError?: (error: unknown, where: string) => void;
|
|
231
|
+
/** The clock. Tests use this. */
|
|
232
|
+
now?: () => number;
|
|
233
|
+
}
|
|
234
|
+
declare class CronWatch {
|
|
235
|
+
readonly store: Store;
|
|
236
|
+
readonly alerts: AlertChannel[];
|
|
237
|
+
readonly triage: TriageFn | undefined;
|
|
238
|
+
readonly sources: Source[];
|
|
239
|
+
/** The secret handler() requests must carry, or null when none is set. */
|
|
240
|
+
readonly cronSecret: string | null;
|
|
241
|
+
readonly retentionMs: number;
|
|
242
|
+
readonly now: () => number;
|
|
243
|
+
readonly onError: (error: unknown, where: string) => void;
|
|
244
|
+
/** cronSecret was passed as null: handlers may run without a secret. */
|
|
245
|
+
private readonly secretOptOut;
|
|
246
|
+
private readonly redact;
|
|
247
|
+
/** "check": queue alerts for another process's check instead of sending them. See CronWatchOptions.deliver. */
|
|
248
|
+
private readonly deferDelivery;
|
|
249
|
+
private readonly defaults;
|
|
250
|
+
private readonly definitions;
|
|
251
|
+
private readonly synced;
|
|
252
|
+
/** The tail of each job's queue of state updates. See serial(). */
|
|
253
|
+
private readonly queues;
|
|
254
|
+
/** start() calls with an id still in flight, so two at once in this process record one run. */
|
|
255
|
+
private readonly starting;
|
|
256
|
+
private ready;
|
|
257
|
+
private checking;
|
|
258
|
+
private lastPruneAt;
|
|
259
|
+
private timer;
|
|
260
|
+
private firstTick;
|
|
261
|
+
private usingDefaultStore;
|
|
262
|
+
private warnedNoSecret;
|
|
263
|
+
private warnedDeferredStart;
|
|
264
|
+
constructor(options?: CronWatchOptions);
|
|
265
|
+
/** Declare a job. Call it once, at module level, and keep the handle. */
|
|
266
|
+
job(name: string, options?: JobOptions): JobHandle;
|
|
267
|
+
/** Run a job by name without keeping a handle. Defines it on first use. */
|
|
268
|
+
run<T>(name: string, fn: JobFn<T>): Promise<T>;
|
|
269
|
+
run<T>(name: string, options: JobOptions, fn: JobFn<T>): Promise<T>;
|
|
270
|
+
/** The definitions declared in this process. */
|
|
271
|
+
definedJobs(): JobDefinition[];
|
|
272
|
+
/** A handle on a run started elsewhere, as job(name).resume(runId). The job must be declared in this process. */
|
|
273
|
+
resumeRun(name: string, runId: string): Promise<RunHandle>;
|
|
274
|
+
private handle;
|
|
275
|
+
private warnNoSecret;
|
|
276
|
+
/** onError, for places that must carry on even when onError itself throws. */
|
|
277
|
+
private report;
|
|
278
|
+
private ensureReady;
|
|
279
|
+
private sync;
|
|
280
|
+
/**
|
|
281
|
+
* Runs `fn` after every earlier state update for the same job has settled,
|
|
282
|
+
* so two runs (or a run and a check) in this process never read and write
|
|
283
|
+
* the job's state over each other. Other processes are coordinated by
|
|
284
|
+
* updateState() instead.
|
|
285
|
+
*/
|
|
286
|
+
private serial;
|
|
287
|
+
private readState;
|
|
288
|
+
/**
|
|
289
|
+
* Every read-modify-write of a job's state goes through here. In turn with
|
|
290
|
+
* this process's other updates to the job (serial()), it reads the state,
|
|
291
|
+
* asks `change` for the next one, and writes it with the version one
|
|
292
|
+
* higher, only if the stored version is still the one read. When another
|
|
293
|
+
* process wrote in between, the write is refused and it starts again from
|
|
294
|
+
* a fresh read, up to STATE_ATTEMPTS times. So `change` may run more than
|
|
295
|
+
* once and must only compute: whatever it returns from the attempt that
|
|
296
|
+
* was written is the result. Nothing is written when the state is
|
|
297
|
+
* unchanged. Returns the state as stored.
|
|
298
|
+
*/
|
|
299
|
+
private updateState;
|
|
300
|
+
/** A conditional write, or for a store without compareAndSetState, a plain one that always succeeds. */
|
|
301
|
+
private writeState;
|
|
302
|
+
/**
|
|
303
|
+
* Runs a function as a recorded run. The function always runs, whatever
|
|
304
|
+
* the store is doing: store errors go to onError, and the result is the
|
|
305
|
+
* function's own outcome. Never throws for the job's own error; see `threw`.
|
|
306
|
+
*/
|
|
307
|
+
private execute;
|
|
308
|
+
/**
|
|
309
|
+
* Sets a finished run's status and error from how it ended, then redacts
|
|
310
|
+
* its output and error. Shared by execute() and RunHandle.finish().
|
|
311
|
+
*/
|
|
312
|
+
private conclude;
|
|
313
|
+
/**
|
|
314
|
+
* Writes a finished run and evaluates it. `recorded` says whether its
|
|
315
|
+
* start was written; if not, it is inserted now. Returns why nothing was
|
|
316
|
+
* recorded (another process finished the run first, say), or null. Throws
|
|
317
|
+
* when the store does, so a handle can be finished again. Shared by
|
|
318
|
+
* execute() and RunHandle.finish().
|
|
319
|
+
*/
|
|
320
|
+
private recordFinish;
|
|
321
|
+
/** A conditional write (Store.updateRunIf), or for a store without one, a read then a plain write. */
|
|
322
|
+
private writeRunIf;
|
|
323
|
+
/**
|
|
324
|
+
* Writes a finished run over its stored row, only while that row is still
|
|
325
|
+
* running, or else still marked timeout by a check. Only the process whose
|
|
326
|
+
* write lands goes on to evaluate the run; for the others it returns why
|
|
327
|
+
* nothing was written. `lateAfterTimeout` means a check already counted
|
|
328
|
+
* the run as a stuck failure: a late failure must not count twice, while
|
|
329
|
+
* a late success still closes stuck and recovers. Throws when the store does.
|
|
330
|
+
*/
|
|
331
|
+
private claimFinish;
|
|
332
|
+
/** job.start(): records a running run and returns a handle to finish it. See JobHandle.start. */
|
|
333
|
+
private startRun;
|
|
334
|
+
/**
|
|
335
|
+
* The start of execute() without the function: the run is inserted and
|
|
336
|
+
* missed and stuck close (onRunStart). A store that fails is reported and
|
|
337
|
+
* the handle inserts the finished run instead, as execute() does.
|
|
338
|
+
*/
|
|
339
|
+
private recordStart;
|
|
340
|
+
/** job.resume() and cw.resumeRun(). A store that cannot be read is reported, and finish() reads it again. */
|
|
341
|
+
private resumeHandle;
|
|
342
|
+
/** A handle on a stored run. One still running, or marked timeout by a check, can be finished. */
|
|
343
|
+
private existingHandle;
|
|
344
|
+
/**
|
|
345
|
+
* The handle itself. `base` is the run as last known here, `recorded`
|
|
346
|
+
* whether its start is in the store, and `inactive` why finish() has
|
|
347
|
+
* nothing to do, or null. Lines and metrics wait in the handle until
|
|
348
|
+
* flush() or finish() merges them onto a fresh read of the stored run.
|
|
349
|
+
*/
|
|
350
|
+
private runHandle;
|
|
351
|
+
/**
|
|
352
|
+
* Record a run that happened outside this process, for a Source. Its job
|
|
353
|
+
* must be declared with job() first. Runs are keyed by id: a new one is
|
|
354
|
+
* inserted, a stored one still running (or marked timeout by a check) is
|
|
355
|
+
* finished when this one is not running, and anything else is left alone,
|
|
356
|
+
* so recording the same run twice changes nothing. Finishing is
|
|
357
|
+
* conditional (see Store.updateRunIf): when two processes record the same
|
|
358
|
+
* finish, only the one whose write lands evaluates it, and the other
|
|
359
|
+
* reports it as already finished. A stored run of another job is left
|
|
360
|
+
* alone and reported. A finished run is judged as if it had been wrapped
|
|
361
|
+
* here (expect, failures, duration, budgets) and its output and error are
|
|
362
|
+
* redacted the same way; one finishing after a check marked it timeout is
|
|
363
|
+
* judged only when it succeeded, as RunHandle.finish() does. Returns the
|
|
364
|
+
* alerts it sent.
|
|
365
|
+
*/
|
|
366
|
+
recordRun(input: Run, options?: RecordRunOptions): Promise<Alert[]>;
|
|
367
|
+
/** recordRun() for a run already stored. */
|
|
368
|
+
private recordOver;
|
|
369
|
+
/**
|
|
370
|
+
* Evaluate a finished run (ok, failed, or timed out by a check), already
|
|
371
|
+
* written, against the job's state and send what that produces. Never throws.
|
|
372
|
+
*/
|
|
373
|
+
private finishRun;
|
|
374
|
+
/**
|
|
375
|
+
* The runs before `run`, newest first, with up to BASELINE_WINDOW
|
|
376
|
+
* successful ones when the store has them. One small read normally; a
|
|
377
|
+
* larger one only when failures crowd the successes out of it.
|
|
378
|
+
*/
|
|
379
|
+
private history;
|
|
380
|
+
/**
|
|
381
|
+
* Look for missed and stuck runs across every job, send alerts, retry
|
|
382
|
+
* alerts no channel accepted, and prune old runs. Call it from an interval
|
|
383
|
+
* (start()), a cron hitting the mounted routes, or by hand. Concurrent
|
|
384
|
+
* calls share one check.
|
|
385
|
+
*/
|
|
386
|
+
check(): Promise<CheckResult>;
|
|
387
|
+
private runCheck;
|
|
388
|
+
/** A job's summary and its newest runs, without alerting. A job that cannot be evaluated is reported and shown as failing. */
|
|
389
|
+
private snapshot;
|
|
390
|
+
/** The summary of a job whose evaluation failed, from whatever can still be read. */
|
|
391
|
+
private unevaluable;
|
|
392
|
+
/** Every job the store knows about, with its health. Does not send alerts. */
|
|
393
|
+
jobs(): Promise<JobSummary[]>;
|
|
394
|
+
/** Every job's summary with its newest `limit` runs, read together. What the dashboard shows. */
|
|
395
|
+
jobsWithRuns(limit?: number): Promise<{
|
|
396
|
+
job: JobSummary;
|
|
397
|
+
runs: Run[];
|
|
398
|
+
}[]>;
|
|
399
|
+
jobSummary(name: string): Promise<JobSummary | null>;
|
|
400
|
+
/** A job's runs, newest first. `limit` is a whole number from 1 to 500. */
|
|
401
|
+
runs(name: string, limit?: number): Promise<Run[]>;
|
|
402
|
+
getRun(id: string): Promise<Run | null>;
|
|
403
|
+
/** Stop alerts for a job for a while. State keeps updating underneath. */
|
|
404
|
+
silence(name: string, duration: Duration): Promise<JobState>;
|
|
405
|
+
unsilence(name: string): Promise<JobState>;
|
|
406
|
+
/** Read, change and write one job's state, in turn with every other update to it. */
|
|
407
|
+
private patchState;
|
|
408
|
+
/** Remove a job and its runs from the store. A job still declared in code comes back on its next run. */
|
|
409
|
+
forget(name: string): Promise<void>;
|
|
410
|
+
/** The dashboard and JSON API as fetch-style handlers. See createRoutes(). */
|
|
411
|
+
routes(options?: RoutesOptions): Routes;
|
|
412
|
+
/**
|
|
413
|
+
* Check on an interval, for long-running servers. Default every minute.
|
|
414
|
+
* Not for serverless functions or Cloudflare Workers, where nothing runs
|
|
415
|
+
* between requests: call check() from a cron there instead.
|
|
416
|
+
*/
|
|
417
|
+
start(every?: Duration): void;
|
|
418
|
+
stop(): void;
|
|
419
|
+
close(): Promise<void>;
|
|
420
|
+
/**
|
|
421
|
+
* Compose, triage and send each draft. The state was saved before this
|
|
422
|
+
* (updateState), so a slow channel holds up nothing else; afterwards only
|
|
423
|
+
* the delivery fields are written back, onto a fresh read of the state.
|
|
424
|
+
*/
|
|
425
|
+
private dispatch;
|
|
426
|
+
/**
|
|
427
|
+
* Send the alerts that no channel accepted last time, once each, oldest
|
|
428
|
+
* first. `state` is the job's state as this check left it: an alert that
|
|
429
|
+
* no longer describes it (staleAlert) is dropped instead. Retries across a
|
|
430
|
+
* check share RETRY_BUDGET_MS of wall-clock time; once it is spent the rest
|
|
431
|
+
* stay queued for the next check.
|
|
432
|
+
*/
|
|
433
|
+
private retryUndelivered;
|
|
434
|
+
/**
|
|
435
|
+
* Mark delivered alerts done, drop stale ones, and keep failed ones for the
|
|
436
|
+
* next check. A failed alert replaces its stored copy, so a triage made on
|
|
437
|
+
* this attempt is kept. lastAlertAt moves only on a delivery.
|
|
438
|
+
*/
|
|
439
|
+
private recordDelivery;
|
|
440
|
+
/** Send to every channel at once. True when at least one accepted it, or there are none. */
|
|
441
|
+
private deliver;
|
|
442
|
+
/** Sets alert.triage to the diagnosis, or to null when there is none, so it is tried once per alert. */
|
|
443
|
+
private addTriage;
|
|
444
|
+
}
|
|
445
|
+
/** Writes alerts to the console. The default channel. */
|
|
446
|
+
declare function consoleChannel(): AlertChannel;
|
|
447
|
+
/** Wrap any function as an alert channel. */
|
|
448
|
+
declare function custom(name: string, send: (alert: Alert) => Promise<void> | void): AlertChannel;
|
|
449
|
+
|
|
450
|
+
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, type HandlerOptions as b, type JobFn as c, type JobHandle as d, type Routes as e, type RoutesOptions as f, type RunHandle as g, type RunOutcome as h, type SourceHost as i, type StartOptions as j, consoleChannel as k, createRoutes as l, custom as m };
|
package/dist/d1.cjs
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/stores/sql.ts
|
|
4
|
+
var DEFAULT_PREFIX = "cronwatch_";
|
|
5
|
+
var MAX_PREFIX = 63 - "runs_job_started".length;
|
|
6
|
+
function tablePrefix(prefix = DEFAULT_PREFIX) {
|
|
7
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
`cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, not starting with a digit, at most ${MAX_PREFIX} characters.`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return prefix;
|
|
13
|
+
}
|
|
14
|
+
function schema(dialect, p) {
|
|
15
|
+
const int = "INTEGER";
|
|
16
|
+
const json2 = "TEXT";
|
|
17
|
+
return `
|
|
18
|
+
CREATE TABLE IF NOT EXISTS ${p}jobs (
|
|
19
|
+
name TEXT PRIMARY KEY,
|
|
20
|
+
definition ${json2} NOT NULL,
|
|
21
|
+
created_at ${int} NOT NULL,
|
|
22
|
+
updated_at ${int} NOT NULL
|
|
23
|
+
);
|
|
24
|
+
CREATE TABLE IF NOT EXISTS ${p}runs (${""}
|
|
25
|
+
id TEXT PRIMARY KEY,
|
|
26
|
+
job TEXT NOT NULL,
|
|
27
|
+
status TEXT NOT NULL,
|
|
28
|
+
started_at ${int} NOT NULL,
|
|
29
|
+
finished_at ${int},
|
|
30
|
+
duration_ms ${int},
|
|
31
|
+
error TEXT,
|
|
32
|
+
output TEXT,
|
|
33
|
+
metrics ${json2} NOT NULL DEFAULT '{}',
|
|
34
|
+
trigger TEXT NOT NULL DEFAULT 'run'
|
|
35
|
+
);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';
|
|
38
|
+
CREATE TABLE IF NOT EXISTS ${p}state (
|
|
39
|
+
job TEXT PRIMARY KEY,
|
|
40
|
+
state ${json2} NOT NULL
|
|
41
|
+
);
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
function statements(dialect, p) {
|
|
45
|
+
const seq = "rowid";
|
|
46
|
+
const byName = "name";
|
|
47
|
+
const version = (column) => `COALESCE(json_extract(${column}, '$.version'), 0)`;
|
|
48
|
+
const sql = {
|
|
49
|
+
upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)
|
|
50
|
+
ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,
|
|
51
|
+
getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,
|
|
52
|
+
listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,
|
|
53
|
+
deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,
|
|
54
|
+
deleteState: `DELETE FROM ${p}state WHERE job = ?`,
|
|
55
|
+
deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,
|
|
56
|
+
insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
58
|
+
updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,
|
|
59
|
+
getRun: `SELECT * FROM ${p}runs WHERE id = ?`,
|
|
60
|
+
listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,
|
|
61
|
+
runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,
|
|
62
|
+
getState: `SELECT state FROM ${p}state WHERE job = ?`,
|
|
63
|
+
setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,
|
|
64
|
+
// compareAndSetState. Expecting version 0 also matches a missing row, so
|
|
65
|
+
// that case inserts; any other version must find its row.
|
|
66
|
+
casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)
|
|
67
|
+
ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,
|
|
68
|
+
casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version("state")} = ?`,
|
|
69
|
+
// Each job's newest run is kept whatever its age: without it, a job that
|
|
70
|
+
// runs less often than the retention looks like it never ran.
|
|
71
|
+
prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?
|
|
72
|
+
AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`
|
|
73
|
+
};
|
|
74
|
+
return sql;
|
|
75
|
+
}
|
|
76
|
+
function updateRunIfSql(dialect, p, count) {
|
|
77
|
+
const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
|
|
78
|
+
return text;
|
|
79
|
+
}
|
|
80
|
+
var params = {
|
|
81
|
+
upsertJob: (definition, now) => [definition.name, JSON.stringify(definition), now, now],
|
|
82
|
+
insertRun: (run) => [
|
|
83
|
+
run.id,
|
|
84
|
+
run.job,
|
|
85
|
+
run.status,
|
|
86
|
+
run.startedAt,
|
|
87
|
+
run.finishedAt,
|
|
88
|
+
run.durationMs,
|
|
89
|
+
run.error,
|
|
90
|
+
run.output,
|
|
91
|
+
JSON.stringify(run.metrics),
|
|
92
|
+
run.trigger
|
|
93
|
+
],
|
|
94
|
+
updateRun: (run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],
|
|
95
|
+
updateRunIf: (run, fromStatuses) => [
|
|
96
|
+
run.status,
|
|
97
|
+
run.finishedAt,
|
|
98
|
+
run.durationMs,
|
|
99
|
+
run.error,
|
|
100
|
+
run.output,
|
|
101
|
+
JSON.stringify(run.metrics),
|
|
102
|
+
run.id,
|
|
103
|
+
...fromStatuses
|
|
104
|
+
],
|
|
105
|
+
setState: (state) => [state.job, JSON.stringify(state)],
|
|
106
|
+
casInsert: (state) => [state.job, JSON.stringify(state)],
|
|
107
|
+
casUpdate: (state, expectedVersion) => [JSON.stringify(state), state.job, expectedVersion]
|
|
108
|
+
};
|
|
109
|
+
var json = (v) => typeof v === "string" ? JSON.parse(v) : v;
|
|
110
|
+
var num = (v) => v === null ? null : Number(v);
|
|
111
|
+
function rowToJob(r) {
|
|
112
|
+
return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };
|
|
113
|
+
}
|
|
114
|
+
function rowToRun(r) {
|
|
115
|
+
return {
|
|
116
|
+
id: r.id,
|
|
117
|
+
job: r.job,
|
|
118
|
+
status: r.status,
|
|
119
|
+
startedAt: Number(r.started_at),
|
|
120
|
+
finishedAt: num(r.finished_at),
|
|
121
|
+
durationMs: num(r.duration_ms),
|
|
122
|
+
error: r.error,
|
|
123
|
+
output: r.output,
|
|
124
|
+
metrics: r.metrics === null ? {} : json(r.metrics),
|
|
125
|
+
trigger: r.trigger
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function rowToState(r) {
|
|
129
|
+
return json(r.state);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/stores/d1.ts
|
|
133
|
+
var created = /* @__PURE__ */ new WeakMap();
|
|
134
|
+
function d1(database, options = {}) {
|
|
135
|
+
if (!database || typeof database.prepare !== "function" || typeof database.batch !== "function") {
|
|
136
|
+
throw new Error("cronwatch: d1() needs a D1 database binding, such as env.DB");
|
|
137
|
+
}
|
|
138
|
+
const p = tablePrefix(options.prefix);
|
|
139
|
+
if (p.startsWith("_cf_")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);
|
|
140
|
+
const sql = statements("sqlite", p);
|
|
141
|
+
const createTables = options.createTables ?? true;
|
|
142
|
+
const stmt = (text, values = []) => {
|
|
143
|
+
const s = database.prepare(text);
|
|
144
|
+
return values.length > 0 ? s.bind(...values) : s;
|
|
145
|
+
};
|
|
146
|
+
const first = async (text, values) => await stmt(text, values).first() ?? null;
|
|
147
|
+
const all = async (text, values = []) => (await stmt(text, values).all()).results ?? [];
|
|
148
|
+
const changes = async (text, values) => (await stmt(text, values).run()).meta?.changes ?? 0;
|
|
149
|
+
return {
|
|
150
|
+
async init() {
|
|
151
|
+
if (!createTables || created.get(database)?.has(p)) return;
|
|
152
|
+
const ddl = schema("sqlite", p).split(";").map((s) => s.trim()).filter(Boolean);
|
|
153
|
+
await database.batch(ddl.map((s) => database.prepare(s)));
|
|
154
|
+
let prefixes = created.get(database);
|
|
155
|
+
if (!prefixes) created.set(database, prefixes = /* @__PURE__ */ new Set());
|
|
156
|
+
prefixes.add(p);
|
|
157
|
+
},
|
|
158
|
+
async upsertJob(definition, now) {
|
|
159
|
+
await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();
|
|
160
|
+
},
|
|
161
|
+
async getJob(name) {
|
|
162
|
+
const r = await first(sql.getJob, [name]);
|
|
163
|
+
return r ? rowToJob(r) : null;
|
|
164
|
+
},
|
|
165
|
+
async listJobs() {
|
|
166
|
+
return (await all(sql.listJobs)).map(rowToJob);
|
|
167
|
+
},
|
|
168
|
+
async deleteJob(name) {
|
|
169
|
+
await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);
|
|
170
|
+
},
|
|
171
|
+
async insertRun(run) {
|
|
172
|
+
await stmt(sql.insertRun, params.insertRun(run)).run();
|
|
173
|
+
},
|
|
174
|
+
async updateRun(run) {
|
|
175
|
+
await stmt(sql.updateRun, params.updateRun(run)).run();
|
|
176
|
+
},
|
|
177
|
+
async updateRunIf(run, fromStatuses) {
|
|
178
|
+
if (fromStatuses.length === 0) return false;
|
|
179
|
+
return await changes(updateRunIfSql("sqlite", p, fromStatuses.length), params.updateRunIf(run, fromStatuses)) > 0;
|
|
180
|
+
},
|
|
181
|
+
async getRun(id) {
|
|
182
|
+
const r = await first(sql.getRun, [id]);
|
|
183
|
+
return r ? rowToRun(r) : null;
|
|
184
|
+
},
|
|
185
|
+
async listRuns(job, limit) {
|
|
186
|
+
return (await all(sql.listRuns, [job, limit])).map(rowToRun);
|
|
187
|
+
},
|
|
188
|
+
async lastRun(job) {
|
|
189
|
+
const r = await first(sql.listRuns, [job, 1]);
|
|
190
|
+
return r ? rowToRun(r) : null;
|
|
191
|
+
},
|
|
192
|
+
async runningRuns() {
|
|
193
|
+
return (await all(sql.runningRuns)).map(rowToRun);
|
|
194
|
+
},
|
|
195
|
+
async getState(job) {
|
|
196
|
+
const r = await first(sql.getState, [job]);
|
|
197
|
+
return r ? rowToState(r) : null;
|
|
198
|
+
},
|
|
199
|
+
async setState(state) {
|
|
200
|
+
await stmt(sql.setState, params.setState(state)).run();
|
|
201
|
+
},
|
|
202
|
+
async compareAndSetState(state, expectedVersion) {
|
|
203
|
+
const written = expectedVersion === 0 ? await changes(sql.casInsert, params.casInsert(state)) : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));
|
|
204
|
+
return written > 0;
|
|
205
|
+
},
|
|
206
|
+
async prune(before) {
|
|
207
|
+
return changes(sql.prune, [before]);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
exports.d1 = d1;
|
|
213
|
+
//# sourceMappingURL=d1.cjs.map
|
|
214
|
+
//# sourceMappingURL=d1.cjs.map
|