@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.cjs CHANGED
@@ -272,6 +272,11 @@ function stateVersion(state) {
272
272
  const version = state?.version;
273
273
  return typeof version === "number" && Number.isSafeInteger(version) && version >= 0 ? version + 0 : 0;
274
274
  }
275
+ function failureCount(state) {
276
+ const count = state?.consecutiveFailures;
277
+ if (typeof count !== "number" || !Number.isInteger(count) || count <= 0) return 0;
278
+ return Math.min(count, MAX_DURATION_MS);
279
+ }
275
280
  function emptyState(job) {
276
281
  return { job, open: {}, consecutiveFailures: 0, silencedUntil: null, lastAlertAt: null, pendingRecovery: [], undelivered: [] };
277
282
  }
@@ -281,6 +286,7 @@ function normalizeState(state, job) {
281
286
  ...emptyState(job),
282
287
  ...state,
283
288
  open: { ...state.open },
289
+ consecutiveFailures: failureCount(state),
284
290
  pendingRecovery: [...state.pendingRecovery ?? []],
285
291
  undelivered: [...state.undelivered ?? []]
286
292
  };
@@ -379,7 +385,7 @@ function onRunFinish(def, run, state, history, now) {
379
385
  }
380
386
  return { state: next, alerts };
381
387
  }
382
- next.consecutiveFailures += 1;
388
+ next.consecutiveFailures = Math.min(next.consecutiveFailures + 1, MAX_DURATION_MS);
383
389
  closeCondition(next, "missed");
384
390
  const threshold = Math.max(1, def.failuresBeforeAlert ?? 1);
385
391
  const condition = run.status === "timeout" ? "stuck" : "failed";
@@ -593,6 +599,9 @@ function capOutput(text) {
593
599
  function stripNul(text) {
594
600
  return text.includes("\0") ? text.replace(/\u0000/g, "") : text;
595
601
  }
602
+ function stripJsonNul(json2) {
603
+ return json2.includes("\\u0000") ? json2.replace(/\\(u0000|[\s\S])/g, (escape, next) => next === "u0000" ? "" : escape) : json2;
604
+ }
596
605
  function errorMessage(error) {
597
606
  return capOutput(describeError(error));
598
607
  }
@@ -1845,13 +1854,21 @@ function memory() {
1845
1854
  const states = /* @__PURE__ */ new Map();
1846
1855
  let seq = 0;
1847
1856
  const clone = (v) => JSON.parse(JSON.stringify(v));
1857
+ const kept = (v) => JSON.parse(stripJsonNul(JSON.stringify(v)));
1858
+ const keptRun = (run) => ({
1859
+ ...clone(run),
1860
+ trigger: stripNul(run.trigger),
1861
+ output: run.output === null ? null : stripNul(run.output),
1862
+ error: run.error === null ? null : stripNul(run.error),
1863
+ metrics: kept(run.metrics)
1864
+ });
1848
1865
  const byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
1849
1866
  return {
1850
1867
  async upsertJob(definition, now) {
1851
1868
  const existing = jobs.get(definition.name);
1852
1869
  jobs.set(definition.name, {
1853
1870
  name: definition.name,
1854
- definition: clone(definition),
1871
+ definition: kept(definition),
1855
1872
  createdAt: existing?.createdAt ?? now,
1856
1873
  updatedAt: now
1857
1874
  });
@@ -1873,19 +1890,19 @@ function memory() {
1873
1890
  },
1874
1891
  async insertRun(run) {
1875
1892
  if (runs.has(run.id)) throw new Error(`run ${run.id} already exists`);
1876
- runs.set(run.id, clone(run));
1893
+ runs.set(run.id, keptRun(run));
1877
1894
  order.set(run.id, ++seq);
1878
1895
  },
1879
1896
  async updateRun(run) {
1880
1897
  const existing = runs.get(run.id);
1881
1898
  if (!existing) return;
1882
- const { status, finishedAt, durationMs, error, output, metrics } = clone(run);
1899
+ const { status, finishedAt, durationMs, error, output, metrics } = keptRun(run);
1883
1900
  runs.set(run.id, { ...existing, status, finishedAt, durationMs, error, output, metrics });
1884
1901
  },
1885
1902
  async updateRunIf(run, fromStatuses) {
1886
1903
  const existing = runs.get(run.id);
1887
1904
  if (!existing || !fromStatuses.includes(existing.status)) return false;
1888
- const { status, finishedAt, durationMs, error, output, metrics } = clone(run);
1905
+ const { status, finishedAt, durationMs, error, output, metrics } = keptRun(run);
1889
1906
  runs.set(run.id, { ...existing, status, finishedAt, durationMs, error, output, metrics });
1890
1907
  return true;
1891
1908
  },
@@ -1908,11 +1925,11 @@ function memory() {
1908
1925
  return s ? clone(s) : null;
1909
1926
  },
1910
1927
  async setState(state) {
1911
- states.set(state.job, clone(state));
1928
+ states.set(state.job, kept(state));
1912
1929
  },
1913
1930
  async compareAndSetState(state, expectedVersion) {
1914
1931
  if (stateVersion(states.get(state.job)) !== expectedVersion) return false;
1915
- states.set(state.job, clone(state));
1932
+ states.set(state.job, kept(state));
1916
1933
  return true;
1917
1934
  },
1918
1935
  async prune(before) {
@@ -2000,6 +2017,8 @@ var CronWatch = class {
2000
2017
  synced = /* @__PURE__ */ new Set();
2001
2018
  /** The tail of each job's queue of state updates. See serial(). */
2002
2019
  queues = /* @__PURE__ */ new Map();
2020
+ /** The tail of each job's queue of declaration writes. See sync(). */
2021
+ syncing = /* @__PURE__ */ new Map();
2003
2022
  /** start() calls with an id still in flight, so two at once in this process record one run. */
2004
2023
  starting = /* @__PURE__ */ new Map();
2005
2024
  ready = null;
@@ -2138,26 +2157,40 @@ var CronWatch = class {
2138
2157
  }
2139
2158
  await this.ready;
2140
2159
  }
2160
+ /**
2161
+ * Writes the declaration of `definition`'s name as it stands, unless the
2162
+ * store has it. A handle kept from an earlier declaration writes the one
2163
+ * that replaced it, never its own over it, and one forgotten since writes
2164
+ * its own. The writes of one name take turns, in the order they were asked
2165
+ * for, so one still under way cannot land after a later one; and a name
2166
+ * declared again while its write was under way is still to be written.
2167
+ */
2141
2168
  async sync(definition) {
2142
2169
  await this.ensureReady();
2143
- if (this.synced.has(definition.name)) return;
2144
- await this.store.upsertJob(toStored(definition), this.now());
2145
- this.synced.add(definition.name);
2170
+ const name = definition.name;
2171
+ if (this.synced.has(name)) return;
2172
+ await this.serial(name, async () => {
2173
+ if (this.synced.has(name)) return;
2174
+ const standing = this.definitions.get(name) ?? definition;
2175
+ await this.store.upsertJob(toStored(standing), this.now());
2176
+ if ((this.definitions.get(name) ?? standing) === standing) this.synced.add(name);
2177
+ }, this.syncing);
2146
2178
  }
2147
2179
  /**
2148
2180
  * Runs `fn` after every earlier state update for the same job has settled,
2149
2181
  * so two runs (or a run and a check) in this process never read and write
2150
2182
  * the job's state over each other. Other processes are coordinated by
2151
- * updateState() instead.
2183
+ * updateState() instead. sync() queues a job's declaration writes the same
2184
+ * way, in `syncing`.
2152
2185
  */
2153
- serial(job, fn) {
2154
- const result = (this.queues.get(job) ?? Promise.resolve()).then(fn);
2186
+ serial(job, fn, queues = this.queues) {
2187
+ const result = (queues.get(job) ?? Promise.resolve()).then(fn);
2155
2188
  const tail2 = result.then(() => {
2156
2189
  }, () => {
2157
2190
  });
2158
- this.queues.set(job, tail2);
2191
+ queues.set(job, tail2);
2159
2192
  void tail2.then(() => {
2160
- if (this.queues.get(job) === tail2) this.queues.delete(job);
2193
+ if (queues.get(job) === tail2) queues.delete(job);
2161
2194
  });
2162
2195
  return result;
2163
2196
  }
@@ -2557,6 +2590,7 @@ ${id}`;
2557
2590
  async recordRun(input, options = {}) {
2558
2591
  const declared = this.definitions.get(input.job);
2559
2592
  if (!declared) throw new Error(`recordRun: job "${input.job}" is not declared; call job() first`);
2593
+ if (input.id.includes("\0")) throw new Error(`recordRun: run ids cannot contain a NUL character (job "${input.job}")`);
2560
2594
  await this.sync(declared);
2561
2595
  const run = { ...input, metrics: { ...input.metrics } };
2562
2596
  if (run.status === "ok") {
@@ -2927,6 +2961,7 @@ function checkRunId(job, id, method) {
2927
2961
  if (typeof id !== "string" || id.length === 0 || id.length > 200) {
2928
2962
  throw new Error(`job "${job}": ${method}() needs a run id of 1 to 200 characters (got ${typeof id === "string" ? `${id.length} characters` : typeof id})`);
2929
2963
  }
2964
+ if (id.includes("\0")) throw new Error(`job "${job}": ${method}() cannot take a run id containing a NUL character`);
2930
2965
  if (id.startsWith(RESERVED_RUN_ID_PREFIX)) {
2931
2966
  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`);
2932
2967
  }