@cronwatch/sdk 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Store, a as AlertChannel, T as TriageFn, D as Duration, J as JobOptions, b as JobDefinition, C as CheckResult, c as JobSummary, R as Run, d as JobState, A as Alert, e as AlertDraft, f as StoredJobDefinition } from './types-C1PyRjI5.cjs';
2
- export { g as AlertDetails, h as AlertType, B as BudgetBreach, i as Condition, E as ExpectRule, j as JobHealth, k as RunStatus, l as StoredJob, m as TriageContext } from './types-C1PyRjI5.cjs';
1
+ import { S as Store, a as AlertChannel, T as TriageFn, D as Duration, J as JobOptions, b as JobDefinition, C as CheckResult, c as JobSummary, R as Run, d as JobState, A as Alert, e as AlertDraft, f as StoredJobDefinition } from './types-DUt2apl0.cjs';
2
+ export { g as AlertDetails, h as AlertType, B as BudgetBreach, i as Condition, E as ExpectRule, j as JobHealth, k as RunStatus, l as StoredJob, m as TriageContext } from './types-DUt2apl0.cjs';
3
3
 
4
4
  /** What a job function receives. */
5
5
  interface JobContext {
@@ -20,9 +20,10 @@ interface RoutesOptions {
20
20
  * Required to reach anything. Send it as `Authorization: Bearer <token>`,
21
21
  * or open the dashboard once with `?token=<token>` and a cookie is set.
22
22
  * Defaults to process.env.CRONWATCH_TOKEN; an empty string counts as unset.
23
- * With no token, the routes answer only requests to localhost while NODE_ENV
24
- * is "development" or "test", and 503 otherwise. Pass `null` to opt out and serve them
25
- * open everywhere, for example behind your own auth.
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.
26
27
  *
27
28
  * The check endpoint (/api/check) also accepts the client's cronSecret, so
28
29
  * a platform cron that sends `Authorization: Bearer <CRON_SECRET>` can
@@ -89,9 +90,11 @@ interface CronWatchOptions {
89
90
  /**
90
91
  * Applied to every run's output and error before it is stored, shown or
91
92
  * sent to an alert channel or triage. The default blanks values that look
92
- * like secrets (password=..., URL credentials, bearer tokens, AWS, GitHub,
93
- * Slack, Stripe and API key formats). Pass your own function, or false to
94
- * keep output exactly as logged.
93
+ * like secrets (password=..., Authorization headers, URL credentials, bearer
94
+ * tokens, JWTs, PEM private keys, webhook URLs, AWS, GitHub, Slack, Stripe,
95
+ * Google and API key formats). Pass your own function, or false to keep
96
+ * output exactly as logged. A function that throws or returns something
97
+ * other than a string is reported to onError and the default is used.
95
98
  */
96
99
  redact?: ((text: string) => string) | false;
97
100
  /**
@@ -133,6 +136,7 @@ declare class CronWatch {
133
136
  private firstTick;
134
137
  private usingDefaultStore;
135
138
  private warnedNoSecret;
139
+ private warnedDeferredStart;
136
140
  constructor(options?: CronWatchOptions);
137
141
  /** Declare a job. Call it once, at module level, and keep the handle. */
138
142
  job(name: string, options?: JobOptions): JobHandle;
@@ -143,15 +147,32 @@ declare class CronWatch {
143
147
  definedJobs(): JobDefinition[];
144
148
  private handle;
145
149
  private warnNoSecret;
150
+ /** onError, for places that must carry on even when onError itself throws. */
151
+ private report;
146
152
  private ensureReady;
147
153
  private sync;
148
154
  /**
149
155
  * Runs `fn` after every earlier state update for the same job has settled,
150
156
  * so two runs (or a run and a check) in this process never read and write
151
- * the job's state over each other. Other processes are not coordinated.
157
+ * the job's state over each other. Other processes are coordinated by
158
+ * updateState() instead.
152
159
  */
153
160
  private serial;
154
161
  private readState;
162
+ /**
163
+ * Every read-modify-write of a job's state goes through here. In turn with
164
+ * this process's other updates to the job (serial()), it reads the state,
165
+ * asks `change` for the next one, and writes it with the version one
166
+ * higher, only if the stored version is still the one read. When another
167
+ * process wrote in between, the write is refused and it starts again from
168
+ * a fresh read, up to STATE_ATTEMPTS times. So `change` may run more than
169
+ * once and must only compute: whatever it returns from the attempt that
170
+ * was written is the result. Nothing is written when the state is
171
+ * unchanged. Returns the state as stored.
172
+ */
173
+ private updateState;
174
+ /** A conditional write, or for a store without compareAndSetState, a plain one that always succeeds. */
175
+ private writeState;
155
176
  /**
156
177
  * Runs a function as a recorded run. The function always runs, whatever
157
178
  * the store is doing: store errors go to onError, and the result is the
@@ -179,8 +200,10 @@ declare class CronWatch {
179
200
  */
180
201
  check(): Promise<CheckResult>;
181
202
  private runCheck;
182
- /** A job's summary and its newest runs, without alerting. */
203
+ /** A job's summary and its newest runs, without alerting. A job that cannot be evaluated is reported and shown as failing. */
183
204
  private snapshot;
205
+ /** The summary of a job whose evaluation failed, from whatever can still be read. */
206
+ private unevaluable;
184
207
  /** Every job the store knows about, with its health. Does not send alerts. */
185
208
  jobs(): Promise<JobSummary[]>;
186
209
  /** Every job's summary with its newest `limit` runs, read together. What the dashboard shows. */
@@ -205,20 +228,29 @@ declare class CronWatch {
205
228
  start(every?: Duration): void;
206
229
  stop(): void;
207
230
  close(): Promise<void>;
208
- /** Save an evaluation's state, honouring silence, and return what should be sent. Call inside serial(). */
209
- private settle;
210
231
  /**
211
232
  * Compose, triage and send each draft. The state was saved before this
212
- * (settle), so a slow channel holds up nothing else; afterwards only the
213
- * delivery fields are written back, onto a fresh read of the state.
233
+ * (updateState), so a slow channel holds up nothing else; afterwards only
234
+ * the delivery fields are written back, onto a fresh read of the state.
214
235
  */
215
236
  private dispatch;
216
- /** Send the alerts that no channel accepted last time, once each. */
237
+ /**
238
+ * Send the alerts that no channel accepted last time, once each, oldest
239
+ * first. `state` is the job's state as this check left it: an alert that
240
+ * no longer describes it (staleAlert) is dropped instead. Retries across a
241
+ * check share RETRY_BUDGET_MS of wall-clock time; once it is spent the rest
242
+ * stay queued for the next check.
243
+ */
217
244
  private retryUndelivered;
218
- /** Mark delivered alerts done and keep failed ones for the next check. lastAlertAt moves only on a delivery. */
245
+ /**
246
+ * Mark delivered alerts done, drop stale ones, and keep failed ones for the
247
+ * next check. A failed alert replaces its stored copy, so a triage made on
248
+ * this attempt is kept. lastAlertAt moves only on a delivery.
249
+ */
219
250
  private recordDelivery;
220
251
  /** Send to every channel at once. True when at least one accepted it, or there are none. */
221
252
  private deliver;
253
+ /** Sets alert.triage to the diagnosis, or to null when there is none, so it is tried once per alert. */
222
254
  private addTriage;
223
255
  }
224
256
  /** Writes alerts to the console. The default channel. */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Store, a as AlertChannel, T as TriageFn, D as Duration, J as JobOptions, b as JobDefinition, C as CheckResult, c as JobSummary, R as Run, d as JobState, A as Alert, e as AlertDraft, f as StoredJobDefinition } from './types-C1PyRjI5.js';
2
- export { g as AlertDetails, h as AlertType, B as BudgetBreach, i as Condition, E as ExpectRule, j as JobHealth, k as RunStatus, l as StoredJob, m as TriageContext } from './types-C1PyRjI5.js';
1
+ import { S as Store, a as AlertChannel, T as TriageFn, D as Duration, J as JobOptions, b as JobDefinition, C as CheckResult, c as JobSummary, R as Run, d as JobState, A as Alert, e as AlertDraft, f as StoredJobDefinition } from './types-DUt2apl0.js';
2
+ export { g as AlertDetails, h as AlertType, B as BudgetBreach, i as Condition, E as ExpectRule, j as JobHealth, k as RunStatus, l as StoredJob, m as TriageContext } from './types-DUt2apl0.js';
3
3
 
4
4
  /** What a job function receives. */
5
5
  interface JobContext {
@@ -20,9 +20,10 @@ interface RoutesOptions {
20
20
  * Required to reach anything. Send it as `Authorization: Bearer <token>`,
21
21
  * or open the dashboard once with `?token=<token>` and a cookie is set.
22
22
  * Defaults to process.env.CRONWATCH_TOKEN; an empty string counts as unset.
23
- * With no token, the routes answer only requests to localhost while NODE_ENV
24
- * is "development" or "test", and 503 otherwise. Pass `null` to opt out and serve them
25
- * open everywhere, for example behind your own auth.
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.
26
27
  *
27
28
  * The check endpoint (/api/check) also accepts the client's cronSecret, so
28
29
  * a platform cron that sends `Authorization: Bearer <CRON_SECRET>` can
@@ -89,9 +90,11 @@ interface CronWatchOptions {
89
90
  /**
90
91
  * Applied to every run's output and error before it is stored, shown or
91
92
  * sent to an alert channel or triage. The default blanks values that look
92
- * like secrets (password=..., URL credentials, bearer tokens, AWS, GitHub,
93
- * Slack, Stripe and API key formats). Pass your own function, or false to
94
- * keep output exactly as logged.
93
+ * like secrets (password=..., Authorization headers, URL credentials, bearer
94
+ * tokens, JWTs, PEM private keys, webhook URLs, AWS, GitHub, Slack, Stripe,
95
+ * Google and API key formats). Pass your own function, or false to keep
96
+ * output exactly as logged. A function that throws or returns something
97
+ * other than a string is reported to onError and the default is used.
95
98
  */
96
99
  redact?: ((text: string) => string) | false;
97
100
  /**
@@ -133,6 +136,7 @@ declare class CronWatch {
133
136
  private firstTick;
134
137
  private usingDefaultStore;
135
138
  private warnedNoSecret;
139
+ private warnedDeferredStart;
136
140
  constructor(options?: CronWatchOptions);
137
141
  /** Declare a job. Call it once, at module level, and keep the handle. */
138
142
  job(name: string, options?: JobOptions): JobHandle;
@@ -143,15 +147,32 @@ declare class CronWatch {
143
147
  definedJobs(): JobDefinition[];
144
148
  private handle;
145
149
  private warnNoSecret;
150
+ /** onError, for places that must carry on even when onError itself throws. */
151
+ private report;
146
152
  private ensureReady;
147
153
  private sync;
148
154
  /**
149
155
  * Runs `fn` after every earlier state update for the same job has settled,
150
156
  * so two runs (or a run and a check) in this process never read and write
151
- * the job's state over each other. Other processes are not coordinated.
157
+ * the job's state over each other. Other processes are coordinated by
158
+ * updateState() instead.
152
159
  */
153
160
  private serial;
154
161
  private readState;
162
+ /**
163
+ * Every read-modify-write of a job's state goes through here. In turn with
164
+ * this process's other updates to the job (serial()), it reads the state,
165
+ * asks `change` for the next one, and writes it with the version one
166
+ * higher, only if the stored version is still the one read. When another
167
+ * process wrote in between, the write is refused and it starts again from
168
+ * a fresh read, up to STATE_ATTEMPTS times. So `change` may run more than
169
+ * once and must only compute: whatever it returns from the attempt that
170
+ * was written is the result. Nothing is written when the state is
171
+ * unchanged. Returns the state as stored.
172
+ */
173
+ private updateState;
174
+ /** A conditional write, or for a store without compareAndSetState, a plain one that always succeeds. */
175
+ private writeState;
155
176
  /**
156
177
  * Runs a function as a recorded run. The function always runs, whatever
157
178
  * the store is doing: store errors go to onError, and the result is the
@@ -179,8 +200,10 @@ declare class CronWatch {
179
200
  */
180
201
  check(): Promise<CheckResult>;
181
202
  private runCheck;
182
- /** A job's summary and its newest runs, without alerting. */
203
+ /** A job's summary and its newest runs, without alerting. A job that cannot be evaluated is reported and shown as failing. */
183
204
  private snapshot;
205
+ /** The summary of a job whose evaluation failed, from whatever can still be read. */
206
+ private unevaluable;
184
207
  /** Every job the store knows about, with its health. Does not send alerts. */
185
208
  jobs(): Promise<JobSummary[]>;
186
209
  /** Every job's summary with its newest `limit` runs, read together. What the dashboard shows. */
@@ -205,20 +228,29 @@ declare class CronWatch {
205
228
  start(every?: Duration): void;
206
229
  stop(): void;
207
230
  close(): Promise<void>;
208
- /** Save an evaluation's state, honouring silence, and return what should be sent. Call inside serial(). */
209
- private settle;
210
231
  /**
211
232
  * Compose, triage and send each draft. The state was saved before this
212
- * (settle), so a slow channel holds up nothing else; afterwards only the
213
- * delivery fields are written back, onto a fresh read of the state.
233
+ * (updateState), so a slow channel holds up nothing else; afterwards only
234
+ * the delivery fields are written back, onto a fresh read of the state.
214
235
  */
215
236
  private dispatch;
216
- /** Send the alerts that no channel accepted last time, once each. */
237
+ /**
238
+ * Send the alerts that no channel accepted last time, once each, oldest
239
+ * first. `state` is the job's state as this check left it: an alert that
240
+ * no longer describes it (staleAlert) is dropped instead. Retries across a
241
+ * check share RETRY_BUDGET_MS of wall-clock time; once it is spent the rest
242
+ * stay queued for the next check.
243
+ */
217
244
  private retryUndelivered;
218
- /** Mark delivered alerts done and keep failed ones for the next check. lastAlertAt moves only on a delivery. */
245
+ /**
246
+ * Mark delivered alerts done, drop stale ones, and keep failed ones for the
247
+ * next check. A failed alert replaces its stored copy, so a triage made on
248
+ * this attempt is kept. lastAlertAt moves only on a delivery.
249
+ */
219
250
  private recordDelivery;
220
251
  /** Send to every channel at once. True when at least one accepted it, or there are none. */
221
252
  private deliver;
253
+ /** Sets alert.triage to the diagnosis, or to null when there is none, so it is tried once per alert. */
222
254
  private addTriage;
223
255
  }
224
256
  /** Writes alerts to the console. The default channel. */