@cronwatch/sdk 0.8.0 → 0.9.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.
Files changed (69) hide show
  1. package/dist/anthropic.d.cts +1 -1
  2. package/dist/anthropic.d.ts +1 -1
  3. package/dist/bugsnag.d.cts +1 -1
  4. package/dist/bugsnag.d.ts +1 -1
  5. package/dist/{client-B5hM4l-D.d.ts → client-CQm-8-mR.d.ts} +13 -2
  6. package/dist/{client-Di5HAWQz.d.cts → client-D35cfnNV.d.cts} +13 -2
  7. package/dist/d1.cjs +29 -19
  8. package/dist/d1.cjs.map +1 -1
  9. package/dist/d1.d.cts +1 -1
  10. package/dist/d1.d.ts +1 -1
  11. package/dist/d1.js +29 -19
  12. package/dist/d1.js.map +1 -1
  13. package/dist/datadog.d.cts +1 -1
  14. package/dist/datadog.d.ts +1 -1
  15. package/dist/discord.d.cts +1 -1
  16. package/dist/discord.d.ts +1 -1
  17. package/dist/{email-Ciq7GRn4.d.ts → email-BxCp9RQV.d.ts} +1 -1
  18. package/dist/{email-DX6gkd5H.d.cts → email-D1d5niMy.d.cts} +1 -1
  19. package/dist/honeybadger.d.cts +1 -1
  20. package/dist/honeybadger.d.ts +1 -1
  21. package/dist/index.cjs +50 -15
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +4 -4
  24. package/dist/index.d.ts +4 -4
  25. package/dist/index.js +50 -15
  26. package/dist/index.js.map +1 -1
  27. package/dist/mailgun.d.cts +2 -2
  28. package/dist/mailgun.d.ts +2 -2
  29. package/dist/newrelic.d.cts +1 -1
  30. package/dist/newrelic.d.ts +1 -1
  31. package/dist/pg-cron.cjs +45 -4
  32. package/dist/pg-cron.cjs.map +1 -1
  33. package/dist/pg-cron.d.cts +5 -2
  34. package/dist/pg-cron.d.ts +5 -2
  35. package/dist/pg-cron.js +45 -4
  36. package/dist/pg-cron.js.map +1 -1
  37. package/dist/postgres.cjs +22 -14
  38. package/dist/postgres.cjs.map +1 -1
  39. package/dist/postgres.d.cts +1 -1
  40. package/dist/postgres.d.ts +1 -1
  41. package/dist/postgres.js +22 -14
  42. package/dist/postgres.js.map +1 -1
  43. package/dist/postmark.d.cts +2 -2
  44. package/dist/postmark.d.ts +2 -2
  45. package/dist/resend.d.cts +2 -2
  46. package/dist/resend.d.ts +2 -2
  47. package/dist/rollbar.d.cts +1 -1
  48. package/dist/rollbar.d.ts +1 -1
  49. package/dist/sendgrid.d.cts +2 -2
  50. package/dist/sendgrid.d.ts +2 -2
  51. package/dist/sentry.d.cts +1 -1
  52. package/dist/sentry.d.ts +1 -1
  53. package/dist/ses.d.cts +2 -2
  54. package/dist/ses.d.ts +2 -2
  55. package/dist/slack.d.cts +1 -1
  56. package/dist/slack.d.ts +1 -1
  57. package/dist/sqlite.cjs +26 -18
  58. package/dist/sqlite.cjs.map +1 -1
  59. package/dist/sqlite.d.cts +1 -1
  60. package/dist/sqlite.d.ts +1 -1
  61. package/dist/sqlite.js +26 -18
  62. package/dist/sqlite.js.map +1 -1
  63. package/dist/twilio.d.cts +1 -1
  64. package/dist/twilio.d.ts +1 -1
  65. package/dist/{types-BjBbCGeJ.d.cts → types-Cz47Boks.d.cts} +6 -0
  66. package/dist/{types-BjBbCGeJ.d.ts → types-Cz47Boks.d.ts} +6 -0
  67. package/dist/webhook.d.cts +1 -1
  68. package/dist/webhook.d.ts +1 -1
  69. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as CronWatchOptions, a as CronWatch } from './client-Di5HAWQz.cjs';
2
- export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-Di5HAWQz.cjs';
3
- import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-BjBbCGeJ.cjs';
4
- export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-BjBbCGeJ.cjs';
1
+ import { C as CronWatchOptions, a as CronWatch } from './client-D35cfnNV.cjs';
2
+ export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-D35cfnNV.cjs';
3
+ import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Cz47Boks.cjs';
4
+ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-Cz47Boks.cjs';
5
5
 
6
6
  /**
7
7
  * Keeps everything in process memory. The default when no store is given,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { C as CronWatchOptions, a as CronWatch } from './client-B5hM4l-D.js';
2
- export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-B5hM4l-D.js';
3
- import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-BjBbCGeJ.js';
4
- export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-BjBbCGeJ.js';
1
+ import { C as CronWatchOptions, a as CronWatch } from './client-CQm-8-mR.js';
2
+ export { F as FetchHandler, H as HandlerFn, b as HandlerOptions, J as JobContext, c as JobFn, d as JobHandle, R as RecordRunOptions, e as Routes, f as RoutesOptions, g as RunHandle, h as RunOutcome, S as Source, i as SourceHost, j as StartOptions, k as consoleChannel, l as createRoutes, m as custom } from './client-CQm-8-mR.js';
3
+ import { S as Store, D as Duration, b as AlertDraft, c as StoredJobDefinition, A as Alert } from './types-Cz47Boks.js';
4
+ export { a as AlertChannel, d as AlertDetails, e as AlertType, B as BudgetBreach, C as ChannelContext, f as CheckResult, g as Condition, E as ExpectRule, J as JobDefinition, h as JobHealth, i as JobOptions, j as JobState, k as JobSummary, R as Run, l as RunStatus, m as StoredJob, T as TriageContext, n as TriageFn } from './types-Cz47Boks.js';
5
5
 
6
6
  /**
7
7
  * Keeps everything in process memory. The default when no store is given,
package/dist/index.js CHANGED
@@ -270,6 +270,11 @@ function stateVersion(state) {
270
270
  const version = state?.version;
271
271
  return typeof version === "number" && Number.isSafeInteger(version) && version >= 0 ? version + 0 : 0;
272
272
  }
273
+ function failureCount(state) {
274
+ const count = state?.consecutiveFailures;
275
+ if (typeof count !== "number" || !Number.isInteger(count) || count <= 0) return 0;
276
+ return Math.min(count, MAX_DURATION_MS);
277
+ }
273
278
  function emptyState(job) {
274
279
  return { job, open: {}, consecutiveFailures: 0, silencedUntil: null, lastAlertAt: null, pendingRecovery: [], undelivered: [] };
275
280
  }
@@ -279,6 +284,7 @@ function normalizeState(state, job) {
279
284
  ...emptyState(job),
280
285
  ...state,
281
286
  open: { ...state.open },
287
+ consecutiveFailures: failureCount(state),
282
288
  pendingRecovery: [...state.pendingRecovery ?? []],
283
289
  undelivered: [...state.undelivered ?? []]
284
290
  };
@@ -377,7 +383,7 @@ function onRunFinish(def, run, state, history, now) {
377
383
  }
378
384
  return { state: next, alerts };
379
385
  }
380
- next.consecutiveFailures += 1;
386
+ next.consecutiveFailures = Math.min(next.consecutiveFailures + 1, MAX_DURATION_MS);
381
387
  closeCondition(next, "missed");
382
388
  const threshold = Math.max(1, def.failuresBeforeAlert ?? 1);
383
389
  const condition = run.status === "timeout" ? "stuck" : "failed";
@@ -591,6 +597,9 @@ function capOutput(text) {
591
597
  function stripNul(text) {
592
598
  return text.includes("\0") ? text.replace(/\u0000/g, "") : text;
593
599
  }
600
+ function stripJsonNul(json2) {
601
+ return json2.includes("\\u0000") ? json2.replace(/\\(u0000|[\s\S])/g, (escape, next) => next === "u0000" ? "" : escape) : json2;
602
+ }
594
603
  function errorMessage(error) {
595
604
  return capOutput(describeError(error));
596
605
  }
@@ -1843,13 +1852,21 @@ function memory() {
1843
1852
  const states = /* @__PURE__ */ new Map();
1844
1853
  let seq = 0;
1845
1854
  const clone = (v) => JSON.parse(JSON.stringify(v));
1855
+ const kept = (v) => JSON.parse(stripJsonNul(JSON.stringify(v)));
1856
+ const keptRun = (run) => ({
1857
+ ...clone(run),
1858
+ trigger: stripNul(run.trigger),
1859
+ output: run.output === null ? null : stripNul(run.output),
1860
+ error: run.error === null ? null : stripNul(run.error),
1861
+ metrics: kept(run.metrics)
1862
+ });
1846
1863
  const byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
1847
1864
  return {
1848
1865
  async upsertJob(definition, now) {
1849
1866
  const existing = jobs.get(definition.name);
1850
1867
  jobs.set(definition.name, {
1851
1868
  name: definition.name,
1852
- definition: clone(definition),
1869
+ definition: kept(definition),
1853
1870
  createdAt: existing?.createdAt ?? now,
1854
1871
  updatedAt: now
1855
1872
  });
@@ -1871,19 +1888,19 @@ function memory() {
1871
1888
  },
1872
1889
  async insertRun(run) {
1873
1890
  if (runs.has(run.id)) throw new Error(`run ${run.id} already exists`);
1874
- runs.set(run.id, clone(run));
1891
+ runs.set(run.id, keptRun(run));
1875
1892
  order.set(run.id, ++seq);
1876
1893
  },
1877
1894
  async updateRun(run) {
1878
1895
  const existing = runs.get(run.id);
1879
1896
  if (!existing) return;
1880
- const { status, finishedAt, durationMs, error, output, metrics } = clone(run);
1897
+ const { status, finishedAt, durationMs, error, output, metrics } = keptRun(run);
1881
1898
  runs.set(run.id, { ...existing, status, finishedAt, durationMs, error, output, metrics });
1882
1899
  },
1883
1900
  async updateRunIf(run, fromStatuses) {
1884
1901
  const existing = runs.get(run.id);
1885
1902
  if (!existing || !fromStatuses.includes(existing.status)) return false;
1886
- const { status, finishedAt, durationMs, error, output, metrics } = clone(run);
1903
+ const { status, finishedAt, durationMs, error, output, metrics } = keptRun(run);
1887
1904
  runs.set(run.id, { ...existing, status, finishedAt, durationMs, error, output, metrics });
1888
1905
  return true;
1889
1906
  },
@@ -1906,11 +1923,11 @@ function memory() {
1906
1923
  return s ? clone(s) : null;
1907
1924
  },
1908
1925
  async setState(state) {
1909
- states.set(state.job, clone(state));
1926
+ states.set(state.job, kept(state));
1910
1927
  },
1911
1928
  async compareAndSetState(state, expectedVersion) {
1912
1929
  if (stateVersion(states.get(state.job)) !== expectedVersion) return false;
1913
- states.set(state.job, clone(state));
1930
+ states.set(state.job, kept(state));
1914
1931
  return true;
1915
1932
  },
1916
1933
  async prune(before) {
@@ -1998,6 +2015,8 @@ var CronWatch = class {
1998
2015
  synced = /* @__PURE__ */ new Set();
1999
2016
  /** The tail of each job's queue of state updates. See serial(). */
2000
2017
  queues = /* @__PURE__ */ new Map();
2018
+ /** The tail of each job's queue of declaration writes. See sync(). */
2019
+ syncing = /* @__PURE__ */ new Map();
2001
2020
  /** start() calls with an id still in flight, so two at once in this process record one run. */
2002
2021
  starting = /* @__PURE__ */ new Map();
2003
2022
  ready = null;
@@ -2136,26 +2155,40 @@ var CronWatch = class {
2136
2155
  }
2137
2156
  await this.ready;
2138
2157
  }
2158
+ /**
2159
+ * Writes the declaration of `definition`'s name as it stands, unless the
2160
+ * store has it. A handle kept from an earlier declaration writes the one
2161
+ * that replaced it, never its own over it, and one forgotten since writes
2162
+ * its own. The writes of one name take turns, in the order they were asked
2163
+ * for, so one still under way cannot land after a later one; and a name
2164
+ * declared again while its write was under way is still to be written.
2165
+ */
2139
2166
  async sync(definition) {
2140
2167
  await this.ensureReady();
2141
- if (this.synced.has(definition.name)) return;
2142
- await this.store.upsertJob(toStored(definition), this.now());
2143
- this.synced.add(definition.name);
2168
+ const name = definition.name;
2169
+ if (this.synced.has(name)) return;
2170
+ await this.serial(name, async () => {
2171
+ if (this.synced.has(name)) return;
2172
+ const standing = this.definitions.get(name) ?? definition;
2173
+ await this.store.upsertJob(toStored(standing), this.now());
2174
+ if ((this.definitions.get(name) ?? standing) === standing) this.synced.add(name);
2175
+ }, this.syncing);
2144
2176
  }
2145
2177
  /**
2146
2178
  * Runs `fn` after every earlier state update for the same job has settled,
2147
2179
  * so two runs (or a run and a check) in this process never read and write
2148
2180
  * the job's state over each other. Other processes are coordinated by
2149
- * updateState() instead.
2181
+ * updateState() instead. sync() queues a job's declaration writes the same
2182
+ * way, in `syncing`.
2150
2183
  */
2151
- serial(job, fn) {
2152
- const result = (this.queues.get(job) ?? Promise.resolve()).then(fn);
2184
+ serial(job, fn, queues = this.queues) {
2185
+ const result = (queues.get(job) ?? Promise.resolve()).then(fn);
2153
2186
  const tail2 = result.then(() => {
2154
2187
  }, () => {
2155
2188
  });
2156
- this.queues.set(job, tail2);
2189
+ queues.set(job, tail2);
2157
2190
  void tail2.then(() => {
2158
- if (this.queues.get(job) === tail2) this.queues.delete(job);
2191
+ if (queues.get(job) === tail2) queues.delete(job);
2159
2192
  });
2160
2193
  return result;
2161
2194
  }
@@ -2555,6 +2588,7 @@ ${id}`;
2555
2588
  async recordRun(input, options = {}) {
2556
2589
  const declared = this.definitions.get(input.job);
2557
2590
  if (!declared) throw new Error(`recordRun: job "${input.job}" is not declared; call job() first`);
2591
+ if (input.id.includes("\0")) throw new Error(`recordRun: run ids cannot contain a NUL character (job "${input.job}")`);
2558
2592
  await this.sync(declared);
2559
2593
  const run = { ...input, metrics: { ...input.metrics } };
2560
2594
  if (run.status === "ok") {
@@ -2925,6 +2959,7 @@ function checkRunId(job, id, method) {
2925
2959
  if (typeof id !== "string" || id.length === 0 || id.length > 200) {
2926
2960
  throw new Error(`job "${job}": ${method}() needs a run id of 1 to 200 characters (got ${typeof id === "string" ? `${id.length} characters` : typeof id})`);
2927
2961
  }
2962
+ if (id.includes("\0")) throw new Error(`job "${job}": ${method}() cannot take a run id containing a NUL character`);
2928
2963
  if (id.startsWith(RESERVED_RUN_ID_PREFIX)) {
2929
2964
  throw new Error(`job "${job}": ${method}() cannot take a run id starting with "${RESERVED_RUN_ID_PREFIX}", which the pg_cron source uses for its runs`);
2930
2965
  }