@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
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import { n as TriageFn } from './types-BjBbCGeJ.cjs';
2
+ import { n as TriageFn } from './types-Cz47Boks.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. */
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import { n as TriageFn } from './types-BjBbCGeJ.js';
2
+ import { n as TriageFn } from './types-Cz47Boks.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. */
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.cjs';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.cjs';
2
2
 
3
3
  /**
4
4
  * Bugsnag, Error Reporting API, payload version 5.
package/dist/bugsnag.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.js';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.js';
2
2
 
3
3
  /**
4
4
  * Bugsnag, Error Reporting API, payload version 5.
@@ -1,4 +1,4 @@
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-BjBbCGeJ.js';
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-Cz47Boks.js';
2
2
 
3
3
  /** What a job function receives. */
4
4
  interface JobContext {
@@ -254,6 +254,8 @@ declare class CronWatch {
254
254
  private readonly synced;
255
255
  /** The tail of each job's queue of state updates. See serial(). */
256
256
  private readonly queues;
257
+ /** The tail of each job's queue of declaration writes. See sync(). */
258
+ private readonly syncing;
257
259
  /** start() calls with an id still in flight, so two at once in this process record one run. */
258
260
  private readonly starting;
259
261
  private ready;
@@ -279,12 +281,21 @@ declare class CronWatch {
279
281
  /** onError, for places that must carry on even when onError itself throws. */
280
282
  private report;
281
283
  private ensureReady;
284
+ /**
285
+ * Writes the declaration of `definition`'s name as it stands, unless the
286
+ * store has it. A handle kept from an earlier declaration writes the one
287
+ * that replaced it, never its own over it, and one forgotten since writes
288
+ * its own. The writes of one name take turns, in the order they were asked
289
+ * for, so one still under way cannot land after a later one; and a name
290
+ * declared again while its write was under way is still to be written.
291
+ */
282
292
  private sync;
283
293
  /**
284
294
  * Runs `fn` after every earlier state update for the same job has settled,
285
295
  * so two runs (or a run and a check) in this process never read and write
286
296
  * the job's state over each other. Other processes are coordinated by
287
- * updateState() instead.
297
+ * updateState() instead. sync() queues a job's declaration writes the same
298
+ * way, in `syncing`.
288
299
  */
289
300
  private serial;
290
301
  private readState;
@@ -1,4 +1,4 @@
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-BjBbCGeJ.cjs';
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-Cz47Boks.cjs';
2
2
 
3
3
  /** What a job function receives. */
4
4
  interface JobContext {
@@ -254,6 +254,8 @@ declare class CronWatch {
254
254
  private readonly synced;
255
255
  /** The tail of each job's queue of state updates. See serial(). */
256
256
  private readonly queues;
257
+ /** The tail of each job's queue of declaration writes. See sync(). */
258
+ private readonly syncing;
257
259
  /** start() calls with an id still in flight, so two at once in this process record one run. */
258
260
  private readonly starting;
259
261
  private ready;
@@ -279,12 +281,21 @@ declare class CronWatch {
279
281
  /** onError, for places that must carry on even when onError itself throws. */
280
282
  private report;
281
283
  private ensureReady;
284
+ /**
285
+ * Writes the declaration of `definition`'s name as it stands, unless the
286
+ * store has it. A handle kept from an earlier declaration writes the one
287
+ * that replaced it, never its own over it, and one forgotten since writes
288
+ * its own. The writes of one name take turns, in the order they were asked
289
+ * for, so one still under way cannot land after a later one; and a name
290
+ * declared again while its write was under way is still to be written.
291
+ */
282
292
  private sync;
283
293
  /**
284
294
  * Runs `fn` after every earlier state update for the same job has settled,
285
295
  * so two runs (or a run and a check) in this process never read and write
286
296
  * the job's state over each other. Other processes are coordinated by
287
- * updateState() instead.
297
+ * updateState() instead. sync() queues a job's declaration writes the same
298
+ * way, in `syncing`.
288
299
  */
289
300
  private serial;
290
301
  private readState;
package/dist/d1.cjs CHANGED
@@ -1,5 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ // src/output.ts
4
+ function stripNul(text2) {
5
+ return text2.includes("\0") ? text2.replace(/\u0000/g, "") : text2;
6
+ }
7
+ function stripJsonNul(json2) {
8
+ return json2.includes("\\u0000") ? json2.replace(/\\(u0000|[\s\S])/g, (escape, next) => next === "u0000" ? "" : escape) : json2;
9
+ }
10
+
3
11
  // src/stores/sql.ts
4
12
  var DEFAULT_PREFIX = "cronwatch_";
5
13
  var MAX_PREFIX = 63 - "runs_job_started".length;
@@ -77,11 +85,13 @@ function statements(dialect, p) {
77
85
  return sql;
78
86
  }
79
87
  function updateRunIfSql(dialect, p, count) {
80
- const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
81
- return text;
88
+ const text2 = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
89
+ return text2;
82
90
  }
91
+ var text = (value) => value === null ? null : stripNul(value);
92
+ var jsonText = (value) => stripJsonNul(JSON.stringify(value));
83
93
  var params = {
84
- upsertJob: (definition, now) => [definition.name, JSON.stringify(definition), now, now],
94
+ upsertJob: (definition, now) => [definition.name, jsonText(definition), now, now],
85
95
  insertRun: (run) => [
86
96
  run.id,
87
97
  run.job,
@@ -89,25 +99,25 @@ var params = {
89
99
  run.startedAt,
90
100
  run.finishedAt,
91
101
  run.durationMs,
92
- run.error,
93
- run.output,
94
- JSON.stringify(run.metrics),
95
- run.trigger
102
+ text(run.error),
103
+ text(run.output),
104
+ jsonText(run.metrics),
105
+ stripNul(run.trigger)
96
106
  ],
97
- updateRun: (run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],
107
+ updateRun: (run) => [run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id],
98
108
  updateRunIf: (run, fromStatuses) => [
99
109
  run.status,
100
110
  run.finishedAt,
101
111
  run.durationMs,
102
- run.error,
103
- run.output,
104
- JSON.stringify(run.metrics),
112
+ text(run.error),
113
+ text(run.output),
114
+ jsonText(run.metrics),
105
115
  run.id,
106
116
  ...fromStatuses
107
117
  ],
108
- setState: (state) => [state.job, JSON.stringify(state)],
109
- casInsert: (state) => [state.job, JSON.stringify(state)],
110
- casUpdate: (state, expectedVersion) => [JSON.stringify(state), state.job, expectedVersion]
118
+ setState: (state) => [state.job, jsonText(state)],
119
+ casInsert: (state) => [state.job, jsonText(state)],
120
+ casUpdate: (state, expectedVersion) => [jsonText(state), state.job, expectedVersion]
111
121
  };
112
122
  var json = (v) => typeof v === "string" ? JSON.parse(v) : v;
113
123
  var num = (v) => v === null ? null : Number(v);
@@ -142,13 +152,13 @@ function d1(database, options = {}) {
142
152
  if (p.startsWith("_cf_")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);
143
153
  const sql = statements("sqlite", p);
144
154
  const createTables = options.createTables ?? true;
145
- const stmt = (text, values = []) => {
146
- const s = database.prepare(text);
155
+ const stmt = (text2, values = []) => {
156
+ const s = database.prepare(text2);
147
157
  return values.length > 0 ? s.bind(...values) : s;
148
158
  };
149
- const first = async (text, values) => await stmt(text, values).first() ?? null;
150
- const all = async (text, values = []) => (await stmt(text, values).all()).results ?? [];
151
- const changes = async (text, values) => (await stmt(text, values).run()).meta?.changes ?? 0;
159
+ const first = async (text2, values) => await stmt(text2, values).first() ?? null;
160
+ const all = async (text2, values = []) => (await stmt(text2, values).all()).results ?? [];
161
+ const changes = async (text2, values) => (await stmt(text2, values).run()).meta?.changes ?? 0;
152
162
  return {
153
163
  async init() {
154
164
  if (!createTables || created.get(database)?.has(p)) return;
package/dist/d1.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stores/sql.ts","../src/stores/d1.ts"],"names":["json"],"mappings":"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","file":"d1.cjs","sourcesContent":["import type { JobState, Run, StoredJob, StoredJobDefinition } from \"../types.js\";\n\n/**\n * The schema, statements and row mapping shared by the SQLite and Postgres\n * stores. Imports no driver, so either entry point can pull it in alone.\n * Statements are written with `?` placeholders; Postgres numbers them.\n */\n\nexport type Dialect = \"sqlite\" | \"postgres\";\n\nexport const DEFAULT_PREFIX = \"cronwatch_\";\n\n// Postgres truncates identifiers past 63 bytes; the longest name we build is the prefix plus \"runs_job_started\".\nconst MAX_PREFIX = 63 - \"runs_job_started\".length;\n\n/**\n * Table names are built from the prefix, so it must be a plain lowercase\n * identifier. Uppercase is refused rather than folded: Postgres lowercases\n * unquoted names, so \"Monitoring_\" would quietly become \"monitoring_\".\n */\nexport function tablePrefix(prefix: string = DEFAULT_PREFIX): string {\n if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {\n throw new Error(\n `cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, ` +\n `not starting with a digit, at most ${MAX_PREFIX} characters.`,\n );\n }\n return prefix;\n}\n\nexport function schema(dialect: Dialect, p: string): string {\n const pg = dialect === \"postgres\";\n const int = pg ? \"BIGINT\" : \"INTEGER\";\n const json = pg ? \"JSONB\" : \"TEXT\";\n return `\n CREATE TABLE IF NOT EXISTS ${p}jobs (\n name TEXT PRIMARY KEY,\n definition ${json} NOT NULL,\n created_at ${int} NOT NULL,\n updated_at ${int} NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${p}runs (${pg ? \"\\n seq BIGSERIAL,\" : \"\"}\n id TEXT PRIMARY KEY,\n job TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at ${int} NOT NULL,\n finished_at ${int},\n duration_ms ${int},\n error TEXT,\n output TEXT,\n metrics ${json} NOT NULL DEFAULT '{}',\n trigger TEXT NOT NULL DEFAULT 'run'\n );\n CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);\n CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';\n CREATE TABLE IF NOT EXISTS ${p}state (\n job TEXT PRIMARY KEY,\n state ${json} NOT NULL\n );\n `;\n}\n\nexport function statements(dialect: Dialect, p: string) {\n const pg = dialect === \"postgres\";\n // Insertion order, to break ties between runs that started in the same millisecond.\n const seq = pg ? \"seq\" : \"rowid\";\n // Byte order on both, so names sort the same whatever the database's collation.\n const byName = pg ? `name COLLATE \"C\"` : \"name\";\n // The version inside a state's JSON, as stateVersion() reads it: a whole\n // number from 0 to 2^53 - 1, else 0 (none, or a foreign row's 1.5 or \"x\",\n // which must neither fail the statement nor refuse every write for good).\n // Each CASE tests the JSON type before any cast.\n const version = (column: string) => {\n if (pg) {\n const v = `(${column}->>'version')::numeric`;\n return `CASE WHEN jsonb_typeof(${column}->'version') <> 'number' THEN 0 WHEN ${v} % 1 = 0 AND ${v} BETWEEN 0 AND 9007199254740991 THEN ${v}::bigint ELSE 0 END`;\n }\n const v = `json_extract(${column}, '$.version')`;\n 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`;\n };\n const sql = {\n upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,\n getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,\n listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,\n deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,\n deleteState: `DELETE FROM ${p}state WHERE job = ?`,\n deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,\n insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,\n getRun: `SELECT * FROM ${p}runs WHERE id = ?`,\n listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,\n runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,\n getState: `SELECT state FROM ${p}state WHERE job = ?`,\n setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,\n // compareAndSetState. Expecting version 0 also matches a missing row, so\n // that case inserts; any other version must find its row.\n casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)\n ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,\n casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version(\"state\")} = ?`,\n // Each job's newest run is kept whatever its age: without it, a job that\n // runs less often than the retention looks like it never ran.\n prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?\n AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`,\n };\n if (pg) {\n for (const key of Object.keys(sql) as (keyof typeof sql)[]) {\n let n = 0;\n sql[key] = sql[key].replace(/\\?/g, () => `$${++n}`);\n }\n }\n return sql;\n}\n\n/**\n * updateRunIf: the update above, only while the stored status is one of\n * `count` statuses. Built per count, since the list is bound value by value.\n */\nexport function updateRunIfSql(dialect: Dialect, p: string, count: number): string {\n const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => \"?\").join(\", \")})`;\n if (dialect !== \"postgres\") return text;\n let n = 0;\n return text.replace(/\\?/g, () => `$${++n}`);\n}\n\n// Parameters in statement order, so both drivers bind the same values.\nexport const params = {\n upsertJob: (definition: StoredJobDefinition, now: number) => [definition.name, JSON.stringify(definition), now, now],\n insertRun: (run: Run) => [\n run.id, run.job, run.status, run.startedAt, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.trigger,\n ],\n updateRun: (run: Run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],\n updateRunIf: (run: Run, fromStatuses: Run[\"status\"][]) => [\n run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id, ...fromStatuses,\n ],\n setState: (state: JobState) => [state.job, JSON.stringify(state)],\n casInsert: (state: JobState) => [state.job, JSON.stringify(state)],\n casUpdate: (state: JobState, expectedVersion: number) => [JSON.stringify(state), state.job, expectedVersion],\n};\n\n// SQLite hands back JSON as TEXT and Postgres as parsed JSONB; Postgres returns BIGINT as a string.\ntype Json<T> = T | string;\ntype Int = number | string;\n\nexport interface JobRow { name: string; definition: Json<StoredJobDefinition>; created_at: Int; updated_at: Int }\nexport interface RunRow {\n id: string; job: string; status: Run[\"status\"]; started_at: Int; finished_at: Int | null;\n duration_ms: Int | null; error: string | null; output: string | null; metrics: Json<Record<string, number>> | null; trigger: string;\n}\nexport interface StateRow { state: Json<JobState> }\n\nconst json = <T>(v: Json<T>): T => (typeof v === \"string\" ? (JSON.parse(v) as T) : v);\nconst num = (v: Int | null): number | null => (v === null ? null : Number(v));\n\nexport function rowToJob(r: JobRow): StoredJob {\n return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };\n}\n\nexport function rowToRun(r: RunRow): Run {\n return {\n id: r.id, job: r.job, status: r.status, startedAt: Number(r.started_at), finishedAt: num(r.finished_at),\n durationMs: num(r.duration_ms), error: r.error, output: r.output, metrics: r.metrics === null ? {} : json(r.metrics), trigger: r.trigger,\n };\n}\n\nexport function rowToState(r: StateRow): JobState {\n return json(r.state);\n}\n","import type { Store, StoredJob } from \"../types.js\";\nimport { params, rowToJob, rowToRun, rowToState, schema, statements, tablePrefix, updateRunIfSql, type JobRow, type RunRow, type StateRow } from \"./sql.js\";\n\n/**\n * The parts of a Cloudflare D1 binding this store uses. Written out here so\n * the SDK needs nothing from @cloudflare/workers-types: a Worker's\n * `env.DB: D1Database` fits it as it is.\n */\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first(): Promise<unknown>;\n all(): Promise<{ results?: unknown[] }>;\n run(): Promise<{ meta?: { changes?: number } }>;\n}\n\nexport interface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n}\n\nexport interface D1Options {\n /** Table name prefix: lowercase letters, digits and underscores. Default \"cronwatch_\". */\n prefix?: string;\n /**\n * Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per\n * isolate. Default true. Pass false when a D1 migration creates them\n * instead (see the Cloudflare docs page for the file).\n */\n createTables?: boolean;\n}\n\n/** Databases (and prefixes) whose tables this isolate has already made sure of. */\nconst created = new WeakMap<object, Set<string>>();\n\n/**\n * Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is\n * the SQLite store's schema, statements and row mapping over D1's\n * prepare, bind and batch. Create it where the binding is, inside the\n * handler: `d1(env.DB)`.\n */\nexport function d1(database: D1DatabaseLike, options: D1Options = {}): Store {\n if (!database || typeof database.prepare !== \"function\" || typeof database.batch !== \"function\") {\n throw new Error(\"cronwatch: d1() needs a D1 database binding, such as env.DB\");\n }\n const p = tablePrefix(options.prefix);\n // D1 keeps its own tables under _cf_ and refuses writes to them.\n if (p.startsWith(\"_cf_\")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);\n const sql = statements(\"sqlite\", p);\n const createTables = options.createTables ?? true;\n\n const stmt = (text: string, values: unknown[] = []) => {\n const s = database.prepare(text);\n return values.length > 0 ? s.bind(...values) : s;\n };\n const first = async <T>(text: string, values: unknown[]): Promise<T | null> => ((await stmt(text, values).first()) as T | null) ?? null;\n const all = async <T>(text: string, values: unknown[] = []): Promise<T[]> => ((await stmt(text, values).all()).results ?? []) as T[];\n const changes = async (text: string, values: unknown[]): Promise<number> => (await stmt(text, values).run()).meta?.changes ?? 0;\n\n return {\n async init() {\n if (!createTables || created.get(database)?.has(p)) return;\n // exec() splits on newlines, so each statement goes in on its own, in one batch.\n const ddl = schema(\"sqlite\", p).split(\";\").map((s) => s.trim()).filter(Boolean);\n await database.batch(ddl.map((s) => database.prepare(s)));\n // Remembered only once it worked, and as a flag rather than a promise,\n // so no invocation ever waits on another's unfinished I/O.\n let prefixes = created.get(database);\n if (!prefixes) created.set(database, (prefixes = new Set()));\n prefixes.add(p);\n },\n async upsertJob(definition, now) {\n await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();\n },\n async getJob(name) {\n const r = await first<JobRow>(sql.getJob, [name]);\n return r ? rowToJob(r) : null;\n },\n async listJobs(): Promise<StoredJob[]> {\n return (await all<JobRow>(sql.listJobs)).map(rowToJob);\n },\n async deleteJob(name) {\n // A batch is one transaction.\n await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);\n },\n async insertRun(run) {\n await stmt(sql.insertRun, params.insertRun(run)).run();\n },\n async updateRun(run) {\n await stmt(sql.updateRun, params.updateRun(run)).run();\n },\n async updateRunIf(run, fromStatuses) {\n if (fromStatuses.length === 0) return false;\n return (await changes(updateRunIfSql(\"sqlite\", p, fromStatuses.length), params.updateRunIf(run, fromStatuses))) > 0;\n },\n async getRun(id) {\n const r = await first<RunRow>(sql.getRun, [id]);\n return r ? rowToRun(r) : null;\n },\n async listRuns(job, limit) {\n return (await all<RunRow>(sql.listRuns, [job, limit])).map(rowToRun);\n },\n async lastRun(job) {\n const r = await first<RunRow>(sql.listRuns, [job, 1]);\n return r ? rowToRun(r) : null;\n },\n async runningRuns() {\n return (await all<RunRow>(sql.runningRuns)).map(rowToRun);\n },\n async getState(job) {\n const r = await first<StateRow>(sql.getState, [job]);\n return r ? rowToState(r) : null;\n },\n async setState(state) {\n await stmt(sql.setState, params.setState(state)).run();\n },\n async compareAndSetState(state, expectedVersion) {\n const written = expectedVersion === 0\n ? await changes(sql.casInsert, params.casInsert(state))\n : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));\n return written > 0;\n },\n async prune(before) {\n return changes(sql.prune, [before]);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/output.ts","../src/stores/sql.ts","../src/stores/d1.ts"],"names":["text","json"],"mappings":"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","file":"d1.cjs","sourcesContent":["/** Output is capped so a chatty job cannot fill the store. The tail is kept. */\nexport const OUTPUT_CAP = 16 * 1024;\n\n/**\n * NUL characters are removed first, since Postgres refuses them in TEXT and\n * JSONB and the whole run row would be lost. The cap then applies to what is\n * left.\n */\nexport function capOutput(text: string): string {\n const clean = stripNul(text);\n if (clean.length <= OUTPUT_CAP) return clean;\n return \"[earlier output trimmed]\\n\" + clean.slice(clean.length - OUTPUT_CAP);\n}\n\n/** Removes every U+0000. */\nexport function stripNul(text: string): string {\n return text.includes(\"\\u0000\") ? text.replace(/\\u0000/g, \"\") : text;\n}\n\n/**\n * Removes every U+0000 from JSON text, keys and strings alike, by dropping\n * each \\u0000 escape (a NUL can appear in JSON no other way). Escapes are\n * read left to right in pairs, so an escaped backslash followed by \"u0000\"\n * is left as it is.\n */\nexport function stripJsonNul(json: string): string {\n return json.includes(\"\\\\u0000\") ? json.replace(/\\\\(u0000|[\\s\\S])/g, (escape, next: string) => (next === \"u0000\" ? \"\" : escape)) : json;\n}\n\n/** \"Name: message\" and the first five stack frames, capped like output. */\nexport function errorMessage(error: unknown): string {\n return capOutput(describeError(error));\n}\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) {\n // The stack repeats the header (over several lines when the message has\n // newlines), so take only its frames.\n const frames = (error.stack ?? \"\").split(\"\\n\").filter((line) => /^\\s+at /.test(line)).slice(0, 5);\n return `${error.name}: ${error.message}${frames.length ? `\\n${frames.join(\"\\n\")}` : \"\"}`;\n }\n if (typeof error === \"string\") return error;\n try {\n return JSON.stringify(error) ?? String(error);\n } catch {\n return String(error);\n }\n}\n\nconst REDACTED = \"[redacted]\";\n\n// Bounded quantifiers throughout, so a long line cannot make these backtrack.\n// They apply in this order, each to the text the ones before it left.\nconst SECRET_PATTERNS: [RegExp, string | ((match: string, ...groups: string[]) => string)][] = [\n // A PEM private key, header to footer. Without a footer (the output was\n // trimmed) it runs to the end of the base64 body. A \"-\" that starts five\n // dashes ends the body, so the footer is never swallowed into it.\n [\n /-----BEGIN (?:[A-Z0-9]{1,20} ){0,3}PRIVATE KEY-----(?:[A-Za-z0-9+/=\\s,:]|-(?!----)){0,16384}(?:-----END (?:[A-Z0-9]{1,20} ){0,3}PRIVATE KEY-----)?/g,\n REDACTED,\n ],\n // password=..., API_KEY: ..., \"client_secret\": \"...\", TOKEN='...', token=...,\n // :password=>\"...\" (but not max_tokens: 800). A quoted value is blanked to\n // its closing quote, spaces and all, and keeps its quotes.\n [\n /\\b([A-Za-z0-9_-]{0,40}(?:secret|token|passw(?:or)?d|pwd|api[_-]?key|access[_-]?key|private[_-]?key|credential)[A-Za-z0-9_-]{0,40}(?<![Tt][Oo][Kk][Ee][Nn][Ss])\"?\\s{0,3}(?:=>|[=:])\\s{0,3})(?:(\")[^\"\\n]{1,4096}\"|(')[^'\\n]{1,4096}'|[\"']?[^\\s\"',;&]{1,4096})/gi,\n (_match, name: string, double?: string, single?: string) => {\n const quote = double ?? single ?? \"\";\n return `${name}${quote}${REDACTED}${quote}`;\n },\n ],\n // Authorization: Basic <base64> and Authorization: Token <token>, also as a JSON or hash entry.\n [/\\b((?:proxy-)?authorization[\"']?\\s{0,3}(?:=>|[=:])\\s{0,3}[\"']?\\s{0,3}(?:basic|token)\\s{1,3})[A-Za-z0-9._~+/=:-]{1,4096}/gi, `$1${REDACTED}`],\n // Credentials inside a URL: postgres://user:password@host. The password\n // runs to the last \"@\" before a \"/\" or a space, so one that contains \"@\"\n // is blanked whole.\n [/(\\b[a-z][a-z0-9+.-]{0,30}:\\/\\/[^\\s/:@]{0,256}:)[^\\s/]{1,256}@/gi, `$1${REDACTED}@`],\n // Authorization: Bearer <token>\n [/\\b(Bearer\\s{1,3})[A-Za-z0-9._~+/=-]{8,4096}/g, `$1${REDACTED}`],\n // A bare JWT: three base64url segments, the first starting eyJ.\n [/\\beyJ[A-Za-z0-9_-]{4,4096}\\.[A-Za-z0-9_-]{4,4096}\\.[A-Za-z0-9_-]{0,4096}/g, REDACTED],\n // Incoming webhook URLs carry their secret in the path.\n [/(\\bhooks\\.slack\\.com\\/(?:services|workflows|triggers)\\/)[A-Za-z0-9/_-]{1,255}/gi, `$1${REDACTED}`],\n [/(\\bdiscord(?:app)?\\.com\\/api\\/(?:v\\d{1,2}\\/)?webhooks\\/)[A-Za-z0-9/_-]{1,255}/gi, `$1${REDACTED}`],\n // Well-known token shapes: AWS, GitHub, Slack, Stripe, Anthropic, OpenAI and Google style keys.\n [/\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g, REDACTED],\n [/\\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{20,255})\\b/g, REDACTED],\n [/\\bxox[abposr]-[A-Za-z0-9-]{10,255}/g, REDACTED],\n [/\\b[rsp]k_(?:live|test)_[A-Za-z0-9]{10,255}\\b/g, REDACTED],\n [/\\bwhsec_[A-Za-z0-9+/=]{16,255}/g, REDACTED],\n [/\\bsk-[A-Za-z0-9_-]{20,255}/g, REDACTED],\n [/\\bAIza[0-9A-Za-z_-]{35}(?![0-9A-Za-z_-])/g, REDACTED],\n];\n\n/**\n * The default `redact`: blanks values that look like secrets (key=value pairs\n * with secret-ish names, Authorization headers, URL credentials, bearer\n * tokens, JWTs, PEM private keys, webhook URLs and well-known token formats)\n * before output or an error is stored, shown or sent anywhere.\n */\nexport function redactSecrets(text: string): string {\n let out = text;\n for (const [pattern, replacement] of SECRET_PATTERNS) out = out.replace(pattern, replacement as string);\n return out;\n}\n","import { stripJsonNul, stripNul } from \"../output.js\";\nimport type { JobState, Run, StoredJob, StoredJobDefinition } from \"../types.js\";\n\n/**\n * The schema, statements and row mapping shared by the SQLite and Postgres\n * stores. Imports no driver, so either entry point can pull it in alone.\n * Statements are written with `?` placeholders; Postgres numbers them.\n */\n\nexport type Dialect = \"sqlite\" | \"postgres\";\n\nexport const DEFAULT_PREFIX = \"cronwatch_\";\n\n// Postgres truncates identifiers past 63 bytes; the longest name we build is the prefix plus \"runs_job_started\".\nconst MAX_PREFIX = 63 - \"runs_job_started\".length;\n\n/**\n * Table names are built from the prefix, so it must be a plain lowercase\n * identifier. Uppercase is refused rather than folded: Postgres lowercases\n * unquoted names, so \"Monitoring_\" would quietly become \"monitoring_\".\n */\nexport function tablePrefix(prefix: string = DEFAULT_PREFIX): string {\n if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {\n throw new Error(\n `cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, ` +\n `not starting with a digit, at most ${MAX_PREFIX} characters.`,\n );\n }\n return prefix;\n}\n\nexport function schema(dialect: Dialect, p: string): string {\n const pg = dialect === \"postgres\";\n const int = pg ? \"BIGINT\" : \"INTEGER\";\n const json = pg ? \"JSONB\" : \"TEXT\";\n return `\n CREATE TABLE IF NOT EXISTS ${p}jobs (\n name TEXT PRIMARY KEY,\n definition ${json} NOT NULL,\n created_at ${int} NOT NULL,\n updated_at ${int} NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${p}runs (${pg ? \"\\n seq BIGSERIAL,\" : \"\"}\n id TEXT PRIMARY KEY,\n job TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at ${int} NOT NULL,\n finished_at ${int},\n duration_ms ${int},\n error TEXT,\n output TEXT,\n metrics ${json} NOT NULL DEFAULT '{}',\n trigger TEXT NOT NULL DEFAULT 'run'\n );\n CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);\n CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';\n CREATE TABLE IF NOT EXISTS ${p}state (\n job TEXT PRIMARY KEY,\n state ${json} NOT NULL\n );\n `;\n}\n\nexport function statements(dialect: Dialect, p: string) {\n const pg = dialect === \"postgres\";\n // Insertion order, to break ties between runs that started in the same millisecond.\n const seq = pg ? \"seq\" : \"rowid\";\n // Byte order on both, so names sort the same whatever the database's collation.\n const byName = pg ? `name COLLATE \"C\"` : \"name\";\n // The version inside a state's JSON, as stateVersion() reads it: a whole\n // number from 0 to 2^53 - 1, else 0 (none, or a foreign row's 1.5 or \"x\",\n // which must neither fail the statement nor refuse every write for good).\n // Each CASE tests the JSON type before any cast.\n const version = (column: string) => {\n if (pg) {\n const v = `(${column}->>'version')::numeric`;\n return `CASE WHEN jsonb_typeof(${column}->'version') <> 'number' THEN 0 WHEN ${v} % 1 = 0 AND ${v} BETWEEN 0 AND 9007199254740991 THEN ${v}::bigint ELSE 0 END`;\n }\n const v = `json_extract(${column}, '$.version')`;\n 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`;\n };\n const sql = {\n upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,\n getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,\n listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,\n deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,\n deleteState: `DELETE FROM ${p}state WHERE job = ?`,\n deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,\n insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,\n getRun: `SELECT * FROM ${p}runs WHERE id = ?`,\n listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,\n runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,\n getState: `SELECT state FROM ${p}state WHERE job = ?`,\n setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,\n // compareAndSetState. Expecting version 0 also matches a missing row, so\n // that case inserts; any other version must find its row.\n casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)\n ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,\n casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version(\"state\")} = ?`,\n // Each job's newest run is kept whatever its age: without it, a job that\n // runs less often than the retention looks like it never ran.\n prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?\n AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`,\n };\n if (pg) {\n for (const key of Object.keys(sql) as (keyof typeof sql)[]) {\n let n = 0;\n sql[key] = sql[key].replace(/\\?/g, () => `$${++n}`);\n }\n }\n return sql;\n}\n\n/**\n * updateRunIf: the update above, only while the stored status is one of\n * `count` statuses. Built per count, since the list is bound value by value.\n */\nexport function updateRunIfSql(dialect: Dialect, p: string, count: number): string {\n const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => \"?\").join(\", \")})`;\n if (dialect !== \"postgres\") return text;\n let n = 0;\n return text.replace(/\\?/g, () => `$${++n}`);\n}\n\n/**\n * Postgres refuses U+0000 in TEXT and JSONB, and a refused write loses the\n * whole row, so every dialect writes text without it: a run's trigger,\n * output, error and metric names, and every key and string of a definition\n * and a state. Identifiers (a job's name, a run's id) are written as given;\n * the client refuses one with a NUL before it gets here.\n */\nconst text = (value: string | null) => (value === null ? null : stripNul(value));\nconst jsonText = (value: unknown) => stripJsonNul(JSON.stringify(value));\n\n// Parameters in statement order, so both drivers bind the same values.\nexport const params = {\n upsertJob: (definition: StoredJobDefinition, now: number) => [definition.name, jsonText(definition), now, now],\n insertRun: (run: Run) => [\n run.id, run.job, run.status, run.startedAt, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), stripNul(run.trigger),\n ],\n updateRun: (run: Run) => [run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id],\n updateRunIf: (run: Run, fromStatuses: Run[\"status\"][]) => [\n run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id, ...fromStatuses,\n ],\n setState: (state: JobState) => [state.job, jsonText(state)],\n casInsert: (state: JobState) => [state.job, jsonText(state)],\n casUpdate: (state: JobState, expectedVersion: number) => [jsonText(state), state.job, expectedVersion],\n};\n\n// SQLite hands back JSON as TEXT and Postgres as parsed JSONB; Postgres returns BIGINT as a string.\ntype Json<T> = T | string;\ntype Int = number | string;\n\nexport interface JobRow { name: string; definition: Json<StoredJobDefinition>; created_at: Int; updated_at: Int }\nexport interface RunRow {\n id: string; job: string; status: Run[\"status\"]; started_at: Int; finished_at: Int | null;\n duration_ms: Int | null; error: string | null; output: string | null; metrics: Json<Record<string, number>> | null; trigger: string;\n}\nexport interface StateRow { state: Json<JobState> }\n\nconst json = <T>(v: Json<T>): T => (typeof v === \"string\" ? (JSON.parse(v) as T) : v);\nconst num = (v: Int | null): number | null => (v === null ? null : Number(v));\n\nexport function rowToJob(r: JobRow): StoredJob {\n return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };\n}\n\nexport function rowToRun(r: RunRow): Run {\n return {\n id: r.id, job: r.job, status: r.status, startedAt: Number(r.started_at), finishedAt: num(r.finished_at),\n durationMs: num(r.duration_ms), error: r.error, output: r.output, metrics: r.metrics === null ? {} : json(r.metrics), trigger: r.trigger,\n };\n}\n\nexport function rowToState(r: StateRow): JobState {\n return json(r.state);\n}\n","import type { Store, StoredJob } from \"../types.js\";\nimport { params, rowToJob, rowToRun, rowToState, schema, statements, tablePrefix, updateRunIfSql, type JobRow, type RunRow, type StateRow } from \"./sql.js\";\n\n/**\n * The parts of a Cloudflare D1 binding this store uses. Written out here so\n * the SDK needs nothing from @cloudflare/workers-types: a Worker's\n * `env.DB: D1Database` fits it as it is.\n */\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first(): Promise<unknown>;\n all(): Promise<{ results?: unknown[] }>;\n run(): Promise<{ meta?: { changes?: number } }>;\n}\n\nexport interface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n}\n\nexport interface D1Options {\n /** Table name prefix: lowercase letters, digits and underscores. Default \"cronwatch_\". */\n prefix?: string;\n /**\n * Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per\n * isolate. Default true. Pass false when a D1 migration creates them\n * instead (see the Cloudflare docs page for the file).\n */\n createTables?: boolean;\n}\n\n/** Databases (and prefixes) whose tables this isolate has already made sure of. */\nconst created = new WeakMap<object, Set<string>>();\n\n/**\n * Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is\n * the SQLite store's schema, statements and row mapping over D1's\n * prepare, bind and batch. Create it where the binding is, inside the\n * handler: `d1(env.DB)`.\n */\nexport function d1(database: D1DatabaseLike, options: D1Options = {}): Store {\n if (!database || typeof database.prepare !== \"function\" || typeof database.batch !== \"function\") {\n throw new Error(\"cronwatch: d1() needs a D1 database binding, such as env.DB\");\n }\n const p = tablePrefix(options.prefix);\n // D1 keeps its own tables under _cf_ and refuses writes to them.\n if (p.startsWith(\"_cf_\")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);\n const sql = statements(\"sqlite\", p);\n const createTables = options.createTables ?? true;\n\n const stmt = (text: string, values: unknown[] = []) => {\n const s = database.prepare(text);\n return values.length > 0 ? s.bind(...values) : s;\n };\n const first = async <T>(text: string, values: unknown[]): Promise<T | null> => ((await stmt(text, values).first()) as T | null) ?? null;\n const all = async <T>(text: string, values: unknown[] = []): Promise<T[]> => ((await stmt(text, values).all()).results ?? []) as T[];\n const changes = async (text: string, values: unknown[]): Promise<number> => (await stmt(text, values).run()).meta?.changes ?? 0;\n\n return {\n async init() {\n if (!createTables || created.get(database)?.has(p)) return;\n // exec() splits on newlines, so each statement goes in on its own, in one batch.\n const ddl = schema(\"sqlite\", p).split(\";\").map((s) => s.trim()).filter(Boolean);\n await database.batch(ddl.map((s) => database.prepare(s)));\n // Remembered only once it worked, and as a flag rather than a promise,\n // so no invocation ever waits on another's unfinished I/O.\n let prefixes = created.get(database);\n if (!prefixes) created.set(database, (prefixes = new Set()));\n prefixes.add(p);\n },\n async upsertJob(definition, now) {\n await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();\n },\n async getJob(name) {\n const r = await first<JobRow>(sql.getJob, [name]);\n return r ? rowToJob(r) : null;\n },\n async listJobs(): Promise<StoredJob[]> {\n return (await all<JobRow>(sql.listJobs)).map(rowToJob);\n },\n async deleteJob(name) {\n // A batch is one transaction.\n await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);\n },\n async insertRun(run) {\n await stmt(sql.insertRun, params.insertRun(run)).run();\n },\n async updateRun(run) {\n await stmt(sql.updateRun, params.updateRun(run)).run();\n },\n async updateRunIf(run, fromStatuses) {\n if (fromStatuses.length === 0) return false;\n return (await changes(updateRunIfSql(\"sqlite\", p, fromStatuses.length), params.updateRunIf(run, fromStatuses))) > 0;\n },\n async getRun(id) {\n const r = await first<RunRow>(sql.getRun, [id]);\n return r ? rowToRun(r) : null;\n },\n async listRuns(job, limit) {\n return (await all<RunRow>(sql.listRuns, [job, limit])).map(rowToRun);\n },\n async lastRun(job) {\n const r = await first<RunRow>(sql.listRuns, [job, 1]);\n return r ? rowToRun(r) : null;\n },\n async runningRuns() {\n return (await all<RunRow>(sql.runningRuns)).map(rowToRun);\n },\n async getState(job) {\n const r = await first<StateRow>(sql.getState, [job]);\n return r ? rowToState(r) : null;\n },\n async setState(state) {\n await stmt(sql.setState, params.setState(state)).run();\n },\n async compareAndSetState(state, expectedVersion) {\n const written = expectedVersion === 0\n ? await changes(sql.casInsert, params.casInsert(state))\n : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));\n return written > 0;\n },\n async prune(before) {\n return changes(sql.prune, [before]);\n },\n };\n}\n"]}
package/dist/d1.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Store } from './types-BjBbCGeJ.cjs';
1
+ import { S as Store } from './types-Cz47Boks.cjs';
2
2
 
3
3
  /**
4
4
  * The parts of a Cloudflare D1 binding this store uses. Written out here so
package/dist/d1.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Store } from './types-BjBbCGeJ.js';
1
+ import { S as Store } from './types-Cz47Boks.js';
2
2
 
3
3
  /**
4
4
  * The parts of a Cloudflare D1 binding this store uses. Written out here so
package/dist/d1.js CHANGED
@@ -1,3 +1,11 @@
1
+ // src/output.ts
2
+ function stripNul(text2) {
3
+ return text2.includes("\0") ? text2.replace(/\u0000/g, "") : text2;
4
+ }
5
+ function stripJsonNul(json2) {
6
+ return json2.includes("\\u0000") ? json2.replace(/\\(u0000|[\s\S])/g, (escape, next) => next === "u0000" ? "" : escape) : json2;
7
+ }
8
+
1
9
  // src/stores/sql.ts
2
10
  var DEFAULT_PREFIX = "cronwatch_";
3
11
  var MAX_PREFIX = 63 - "runs_job_started".length;
@@ -75,11 +83,13 @@ function statements(dialect, p) {
75
83
  return sql;
76
84
  }
77
85
  function updateRunIfSql(dialect, p, count) {
78
- const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
79
- return text;
86
+ const text2 = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
87
+ return text2;
80
88
  }
89
+ var text = (value) => value === null ? null : stripNul(value);
90
+ var jsonText = (value) => stripJsonNul(JSON.stringify(value));
81
91
  var params = {
82
- upsertJob: (definition, now) => [definition.name, JSON.stringify(definition), now, now],
92
+ upsertJob: (definition, now) => [definition.name, jsonText(definition), now, now],
83
93
  insertRun: (run) => [
84
94
  run.id,
85
95
  run.job,
@@ -87,25 +97,25 @@ var params = {
87
97
  run.startedAt,
88
98
  run.finishedAt,
89
99
  run.durationMs,
90
- run.error,
91
- run.output,
92
- JSON.stringify(run.metrics),
93
- run.trigger
100
+ text(run.error),
101
+ text(run.output),
102
+ jsonText(run.metrics),
103
+ stripNul(run.trigger)
94
104
  ],
95
- updateRun: (run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],
105
+ updateRun: (run) => [run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id],
96
106
  updateRunIf: (run, fromStatuses) => [
97
107
  run.status,
98
108
  run.finishedAt,
99
109
  run.durationMs,
100
- run.error,
101
- run.output,
102
- JSON.stringify(run.metrics),
110
+ text(run.error),
111
+ text(run.output),
112
+ jsonText(run.metrics),
103
113
  run.id,
104
114
  ...fromStatuses
105
115
  ],
106
- setState: (state) => [state.job, JSON.stringify(state)],
107
- casInsert: (state) => [state.job, JSON.stringify(state)],
108
- casUpdate: (state, expectedVersion) => [JSON.stringify(state), state.job, expectedVersion]
116
+ setState: (state) => [state.job, jsonText(state)],
117
+ casInsert: (state) => [state.job, jsonText(state)],
118
+ casUpdate: (state, expectedVersion) => [jsonText(state), state.job, expectedVersion]
109
119
  };
110
120
  var json = (v) => typeof v === "string" ? JSON.parse(v) : v;
111
121
  var num = (v) => v === null ? null : Number(v);
@@ -140,13 +150,13 @@ function d1(database, options = {}) {
140
150
  if (p.startsWith("_cf_")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);
141
151
  const sql = statements("sqlite", p);
142
152
  const createTables = options.createTables ?? true;
143
- const stmt = (text, values = []) => {
144
- const s = database.prepare(text);
153
+ const stmt = (text2, values = []) => {
154
+ const s = database.prepare(text2);
145
155
  return values.length > 0 ? s.bind(...values) : s;
146
156
  };
147
- const first = async (text, values) => await stmt(text, values).first() ?? null;
148
- const all = async (text, values = []) => (await stmt(text, values).all()).results ?? [];
149
- const changes = async (text, values) => (await stmt(text, values).run()).meta?.changes ?? 0;
157
+ const first = async (text2, values) => await stmt(text2, values).first() ?? null;
158
+ const all = async (text2, values = []) => (await stmt(text2, values).all()).results ?? [];
159
+ const changes = async (text2, values) => (await stmt(text2, values).run()).meta?.changes ?? 0;
150
160
  return {
151
161
  async init() {
152
162
  if (!createTables || created.get(database)?.has(p)) return;
package/dist/d1.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stores/sql.ts","../src/stores/d1.ts"],"names":["json"],"mappings":";AAUO,IAAM,cAAA,GAAiB,YAAA;AAG9B,IAAM,UAAA,GAAa,KAAK,kBAAA,CAAmB,MAAA;AAOpC,SAAS,WAAA,CAAY,SAAiB,cAAA,EAAwB;AACnE,EAAA,IAAI,CAAC,oBAAA,CAAqB,IAAA,CAAK,MAAM,CAAA,IAAK,MAAA,CAAO,SAAS,UAAA,EAAY;AACpE,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,mCAAmC,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,uFACjB,UAAU,CAAA,YAAA;AAAA,KACpD;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,MAAA,CAAO,SAAkB,CAAA,EAAmB;AAE1D,EAAA,MAAM,GAAA,GAAsB,SAAA;AAC5B,EAAA,MAAMA,KAAAA,GAAsB,MAAA;AAC5B,EAAA,OAAO;AAAA,+BAAA,EACwB,CAAC,CAAA;AAAA;AAAA,iBAAA,EAEfA,KAAI,CAAA;AAAA,iBAAA,EACJ,GAAG,CAAA;AAAA,iBAAA,EACH,GAAG,CAAA;AAAA;AAAA,+BAAA,EAEW,CAAC,CAAA,MAAA,EAAyC,EAAE;AAAA;AAAA;AAAA;AAAA,iBAAA,EAI1D,GAAG,CAAA;AAAA,kBAAA,EACF,GAAG,CAAA;AAAA,kBAAA,EACH,GAAG,CAAA;AAAA;AAAA;AAAA,cAAA,EAGPA,KAAI,CAAA;AAAA;AAAA;AAAA,+BAAA,EAGa,CAAC,uBAAuB,CAAC,CAAA;AAAA,+BAAA,EACzB,CAAC,mBAAmB,CAAC,CAAA;AAAA,+BAAA,EACrB,CAAC,CAAA;AAAA;AAAA,YAAA,EAEpBA,KAAI,CAAA;AAAA;AAAA,EAAA,CAAA;AAGlB;AAEO,SAAS,UAAA,CAAW,SAAkB,CAAA,EAAW;AAGtD,EAAA,MAAM,GAAA,GAAmB,OAAA;AAEzB,EAAA,MAAM,MAAA,GAAmC,MAAA;AAKzC,EAAA,MAAM,OAAA,GAAU,CAAC,MAAA,KAAmB;AAKlC,IAAA,MAAM,CAAA,GAAI,gBAAgB,MAAM,CAAA,cAAA,CAAA;AAChC,IAAA,OAAO,CAAA,oBAAA,EAAuB,MAAM,CAAA,sDAAA,EAAyD,CAAC,WAAW,CAAC,CAAA,iBAAA,EAAoB,CAAC,CAAA,0CAAA,EAA6C,CAAC,CAAA,uBAAA,CAAA;AAAA,EAC/K,CAAA;AACA,EAAA,MAAM,GAAA,GAAM;AAAA,IACV,SAAA,EAAW,eAAe,CAAC,CAAA;AAAA,yGAAA,CAAA;AAAA,IAE3B,MAAA,EAAQ,iBAAiB,CAAC,CAAA,mBAAA,CAAA;AAAA,IAC1B,QAAA,EAAU,CAAA,cAAA,EAAiB,CAAC,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAA;AAAA,IACnD,UAAA,EAAY,eAAe,CAAC,CAAA,kBAAA,CAAA;AAAA,IAC5B,WAAA,EAAa,eAAe,CAAC,CAAA,mBAAA,CAAA;AAAA,IAC7B,SAAA,EAAW,eAAe,CAAC,CAAA,mBAAA,CAAA;AAAA,IAC3B,SAAA,EAAW,eAAe,CAAC,CAAA;AAAA,2CAAA,CAAA;AAAA,IAE3B,SAAA,EAAW,UAAU,CAAC,CAAA,sGAAA,CAAA;AAAA,IACtB,MAAA,EAAQ,iBAAiB,CAAC,CAAA,iBAAA,CAAA;AAAA,IAC1B,QAAA,EAAU,CAAA,cAAA,EAAiB,CAAC,CAAA,6CAAA,EAAgD,GAAG,CAAA,aAAA,CAAA;AAAA,IAC/E,WAAA,EAAa,CAAA,cAAA,EAAiB,CAAC,CAAA,mDAAA,EAAsD,GAAG,CAAA,CAAA;AAAA,IACxF,QAAA,EAAU,qBAAqB,CAAC,CAAA,mBAAA,CAAA;AAAA,IAChC,QAAA,EAAU,eAAe,CAAC,CAAA,uFAAA,CAAA;AAAA;AAAA;AAAA,IAG1B,SAAA,EAAW,eAAe,CAAC,CAAA;AAAA,mEAAA,EACsC,OAAA,CAAQ,CAAA,EAAG,CAAC,CAAA,WAAA,CAAa,CAAC,CAAA,IAAA,CAAA;AAAA,IAC3F,WAAW,CAAA,OAAA,EAAU,CAAC,CAAA,sCAAA,EAAyC,OAAA,CAAQ,OAAO,CAAC,CAAA,IAAA,CAAA;AAAA;AAAA;AAAA,IAG/E,KAAA,EAAO,eAAe,CAAC,CAAA;AAAA,sDAAA,EAC6B,CAAC,wBAAwB,CAAC,CAAA,SAAA;AAAA,GAChF;AAOA,EAAA,OAAO,GAAA;AACT;AAMO,SAAS,cAAA,CAAe,OAAA,EAAkB,CAAA,EAAW,KAAA,EAAuB;AACjF,EAAA,MAAM,IAAA,GAAO,CAAA,OAAA,EAAU,CAAC,CAAA,sHAAA,EAAyH,MAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,KAAA,IAAS,MAAM,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AACpM,EAA4B,OAAO,IAAA;AAGrC;AAGO,IAAM,MAAA,GAAS;AAAA,EACpB,SAAA,EAAW,CAAC,UAAA,EAAiC,GAAA,KAAgB,CAAC,UAAA,CAAW,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,UAAU,CAAA,EAAG,GAAA,EAAK,GAAG,CAAA;AAAA,EACnH,SAAA,EAAW,CAAC,GAAA,KAAa;AAAA,IACvB,GAAA,CAAI,EAAA;AAAA,IAAI,GAAA,CAAI,GAAA;AAAA,IAAK,GAAA,CAAI,MAAA;AAAA,IAAQ,GAAA,CAAI,SAAA;AAAA,IAAW,GAAA,CAAI,UAAA;AAAA,IAAY,GAAA,CAAI,UAAA;AAAA,IAAY,GAAA,CAAI,KAAA;AAAA,IAAO,GAAA,CAAI,MAAA;AAAA,IAAQ,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAO,CAAA;AAAA,IAAG,GAAA,CAAI;AAAA,GACtI;AAAA,EACA,SAAA,EAAW,CAAC,GAAA,KAAa,CAAC,IAAI,MAAA,EAAQ,GAAA,CAAI,YAAY,GAAA,CAAI,UAAA,EAAY,IAAI,KAAA,EAAO,GAAA,CAAI,QAAQ,IAAA,CAAK,SAAA,CAAU,IAAI,OAAO,CAAA,EAAG,IAAI,EAAE,CAAA;AAAA,EAChI,WAAA,EAAa,CAAC,GAAA,EAAU,YAAA,KAAkC;AAAA,IACxD,GAAA,CAAI,MAAA;AAAA,IAAQ,GAAA,CAAI,UAAA;AAAA,IAAY,GAAA,CAAI,UAAA;AAAA,IAAY,GAAA,CAAI,KAAA;AAAA,IAAO,GAAA,CAAI,MAAA;AAAA,IAAQ,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAO,CAAA;AAAA,IAAG,GAAA,CAAI,EAAA;AAAA,IAAI,GAAG;AAAA,GAC7G;AAAA,EACA,QAAA,EAAU,CAAC,KAAA,KAAoB,CAAC,MAAM,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EAChE,SAAA,EAAW,CAAC,KAAA,KAAoB,CAAC,MAAM,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EACjE,SAAA,EAAW,CAAC,KAAA,EAAiB,eAAA,KAA4B,CAAC,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,EAAG,KAAA,CAAM,GAAA,EAAK,eAAe;AAC7G,CAAA;AAaA,IAAM,IAAA,GAAO,CAAI,CAAA,KAAmB,OAAO,MAAM,QAAA,GAAY,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAU,CAAA;AACnF,IAAM,MAAM,CAAC,CAAA,KAAkC,MAAM,IAAA,GAAO,IAAA,GAAO,OAAO,CAAC,CAAA;AAEpE,SAAS,SAAS,CAAA,EAAsB;AAC7C,EAAA,OAAO,EAAE,IAAA,EAAM,CAAA,CAAE,MAAM,UAAA,EAAY,IAAA,CAAK,EAAE,UAAU,CAAA,EAAG,SAAA,EAAW,MAAA,CAAO,EAAE,UAAU,CAAA,EAAG,WAAW,MAAA,CAAO,CAAA,CAAE,UAAU,CAAA,EAAE;AAC1H;AAEO,SAAS,SAAS,CAAA,EAAgB;AACvC,EAAA,OAAO;AAAA,IACL,IAAI,CAAA,CAAE,EAAA;AAAA,IAAI,KAAK,CAAA,CAAE,GAAA;AAAA,IAAK,QAAQ,CAAA,CAAE,MAAA;AAAA,IAAQ,SAAA,EAAW,MAAA,CAAO,CAAA,CAAE,UAAU,CAAA;AAAA,IAAG,UAAA,EAAY,GAAA,CAAI,CAAA,CAAE,WAAW,CAAA;AAAA,IACtG,UAAA,EAAY,GAAA,CAAI,CAAA,CAAE,WAAW,CAAA;AAAA,IAAG,OAAO,CAAA,CAAE,KAAA;AAAA,IAAO,QAAQ,CAAA,CAAE,MAAA;AAAA,IAAQ,OAAA,EAAS,EAAE,OAAA,KAAY,IAAA,GAAO,EAAC,GAAI,IAAA,CAAK,EAAE,OAAO,CAAA;AAAA,IAAG,SAAS,CAAA,CAAE;AAAA,GACnI;AACF;AAEO,SAAS,WAAW,CAAA,EAAuB;AAChD,EAAA,OAAO,IAAA,CAAK,EAAE,KAAK,CAAA;AACrB;;;ACxIA,IAAM,OAAA,uBAAc,OAAA,EAA6B;AAQ1C,SAAS,EAAA,CAAG,QAAA,EAA0B,OAAA,GAAqB,EAAC,EAAU;AAC3E,EAAA,IAAI,CAAC,YAAY,OAAO,QAAA,CAAS,YAAY,UAAA,IAAc,OAAO,QAAA,CAAS,KAAA,KAAU,UAAA,EAAY;AAC/F,IAAA,MAAM,IAAI,MAAM,6DAA6D,CAAA;AAAA,EAC/E;AACA,EAAA,MAAM,CAAA,GAAI,WAAA,CAAY,OAAA,CAAQ,MAAM,CAAA;AAEpC,EAAA,IAAI,CAAA,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,gCAAA,EAAmC,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA,uCAAA,CAAyC,CAAA;AACvI,EAAA,MAAM,GAAA,GAAM,UAAA,CAAW,QAAA,EAAU,CAAC,CAAA;AAClC,EAAA,MAAM,YAAA,GAAe,QAAQ,YAAA,IAAgB,IAAA;AAE7C,EAAA,MAAM,IAAA,GAAO,CAAC,IAAA,EAAc,MAAA,GAAoB,EAAC,KAAM;AACrD,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAA;AAC/B,IAAA,OAAO,OAAO,MAAA,GAAS,CAAA,GAAI,EAAE,IAAA,CAAK,GAAG,MAAM,CAAA,GAAI,CAAA;AAAA,EACjD,CAAA;AACA,EAAA,MAAM,KAAA,GAAQ,OAAU,IAAA,EAAc,MAAA,KAA2C,MAAM,KAAK,IAAA,EAAM,MAAM,CAAA,CAAE,KAAA,EAAM,IAAmB,IAAA;AACnI,EAAA,MAAM,GAAA,GAAM,OAAU,IAAA,EAAc,MAAA,GAAoB,EAAC,KAAA,CAAsB,MAAM,IAAA,CAAK,IAAA,EAAM,MAAM,CAAA,CAAE,GAAA,EAAI,EAAG,WAAW,EAAC;AAC3H,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAc,MAAA,KAAA,CAAwC,MAAM,IAAA,CAAK,IAAA,EAAM,MAAM,CAAA,CAAE,GAAA,EAAI,EAAG,IAAA,EAAM,OAAA,IAAW,CAAA;AAE9H,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,GAAO;AACX,MAAA,IAAI,CAAC,gBAAgB,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,EAAG;AAEpD,MAAA,MAAM,MAAM,MAAA,CAAO,QAAA,EAAU,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC9E,MAAA,MAAM,QAAA,CAAS,KAAA,CAAM,GAAA,CAAI,GAAA,CAAI,CAAC,MAAM,QAAA,CAAS,OAAA,CAAQ,CAAC,CAAC,CAAC,CAAA;AAGxD,MAAA,IAAI,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA;AACnC,MAAA,IAAI,CAAC,UAAU,OAAA,CAAQ,GAAA,CAAI,UAAW,QAAA,mBAAW,IAAI,KAAM,CAAA;AAC3D,MAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,MAAM,SAAA,CAAU,UAAA,EAAY,GAAA,EAAK;AAC/B,MAAA,MAAM,IAAA,CAAK,IAAI,SAAA,EAAW,MAAA,CAAO,UAAU,UAAA,EAAY,GAAG,CAAC,CAAA,CAAE,GAAA,EAAI;AAAA,IACnE,CAAA;AAAA,IACA,MAAM,OAAO,IAAA,EAAM;AACjB,MAAA,MAAM,IAAI,MAAM,KAAA,CAAc,IAAI,MAAA,EAAQ,CAAC,IAAI,CAAC,CAAA;AAChD,MAAA,OAAO,CAAA,GAAI,QAAA,CAAS,CAAC,CAAA,GAAI,IAAA;AAAA,IAC3B,CAAA;AAAA,IACA,MAAM,QAAA,GAAiC;AACrC,MAAA,OAAA,CAAQ,MAAM,GAAA,CAAY,GAAA,CAAI,QAAQ,CAAA,EAAG,IAAI,QAAQ,CAAA;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,UAAU,IAAA,EAAM;AAEpB,MAAA,MAAM,QAAA,CAAS,KAAA,CAAM,CAAC,IAAA,CAAK,GAAA,CAAI,YAAY,CAAC,IAAI,CAAC,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,aAAa,CAAC,IAAI,CAAC,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,IACjH,CAAA;AAAA,IACA,MAAM,UAAU,GAAA,EAAK;AACnB,MAAA,MAAM,IAAA,CAAK,IAAI,SAAA,EAAW,MAAA,CAAO,UAAU,GAAG,CAAC,EAAE,GAAA,EAAI;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,UAAU,GAAA,EAAK;AACnB,MAAA,MAAM,IAAA,CAAK,IAAI,SAAA,EAAW,MAAA,CAAO,UAAU,GAAG,CAAC,EAAE,GAAA,EAAI;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,WAAA,CAAY,GAAA,EAAK,YAAA,EAAc;AACnC,MAAA,IAAI,YAAA,CAAa,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AACtC,MAAA,OAAQ,MAAM,OAAA,CAAQ,cAAA,CAAe,QAAA,EAAU,CAAA,EAAG,YAAA,CAAa,MAAM,CAAA,EAAG,MAAA,CAAO,WAAA,CAAY,GAAA,EAAK,YAAY,CAAC,CAAA,GAAK,CAAA;AAAA,IACpH,CAAA;AAAA,IACA,MAAM,OAAO,EAAA,EAAI;AACf,MAAA,MAAM,IAAI,MAAM,KAAA,CAAc,IAAI,MAAA,EAAQ,CAAC,EAAE,CAAC,CAAA;AAC9C,MAAA,OAAO,CAAA,GAAI,QAAA,CAAS,CAAC,CAAA,GAAI,IAAA;AAAA,IAC3B,CAAA;AAAA,IACA,MAAM,QAAA,CAAS,GAAA,EAAK,KAAA,EAAO;AACzB,MAAA,OAAA,CAAQ,MAAM,GAAA,CAAY,GAAA,CAAI,QAAA,EAAU,CAAC,KAAK,KAAK,CAAC,CAAA,EAAG,GAAA,CAAI,QAAQ,CAAA;AAAA,IACrE,CAAA;AAAA,IACA,MAAM,QAAQ,GAAA,EAAK;AACjB,MAAA,MAAM,CAAA,GAAI,MAAM,KAAA,CAAc,GAAA,CAAI,UAAU,CAAC,GAAA,EAAK,CAAC,CAAC,CAAA;AACpD,MAAA,OAAO,CAAA,GAAI,QAAA,CAAS,CAAC,CAAA,GAAI,IAAA;AAAA,IAC3B,CAAA;AAAA,IACA,MAAM,WAAA,GAAc;AAClB,MAAA,OAAA,CAAQ,MAAM,GAAA,CAAY,GAAA,CAAI,WAAW,CAAA,EAAG,IAAI,QAAQ,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,MAAM,SAAS,GAAA,EAAK;AAClB,MAAA,MAAM,IAAI,MAAM,KAAA,CAAgB,IAAI,QAAA,EAAU,CAAC,GAAG,CAAC,CAAA;AACnD,MAAA,OAAO,CAAA,GAAI,UAAA,CAAW,CAAC,CAAA,GAAI,IAAA;AAAA,IAC7B,CAAA;AAAA,IACA,MAAM,SAAS,KAAA,EAAO;AACpB,MAAA,MAAM,IAAA,CAAK,IAAI,QAAA,EAAU,MAAA,CAAO,SAAS,KAAK,CAAC,EAAE,GAAA,EAAI;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,kBAAA,CAAmB,KAAA,EAAO,eAAA,EAAiB;AAC/C,MAAA,MAAM,OAAA,GAAU,oBAAoB,CAAA,GAChC,MAAM,QAAQ,GAAA,CAAI,SAAA,EAAW,OAAO,SAAA,CAAU,KAAK,CAAC,CAAA,GACpD,MAAM,QAAQ,GAAA,CAAI,SAAA,EAAW,OAAO,SAAA,CAAU,KAAA,EAAO,eAAe,CAAC,CAAA;AACzE,MAAA,OAAO,OAAA,GAAU,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,MAAM,MAAM,MAAA,EAAQ;AAClB,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,KAAA,EAAO,CAAC,MAAM,CAAC,CAAA;AAAA,IACpC;AAAA,GACF;AACF","file":"d1.js","sourcesContent":["import type { JobState, Run, StoredJob, StoredJobDefinition } from \"../types.js\";\n\n/**\n * The schema, statements and row mapping shared by the SQLite and Postgres\n * stores. Imports no driver, so either entry point can pull it in alone.\n * Statements are written with `?` placeholders; Postgres numbers them.\n */\n\nexport type Dialect = \"sqlite\" | \"postgres\";\n\nexport const DEFAULT_PREFIX = \"cronwatch_\";\n\n// Postgres truncates identifiers past 63 bytes; the longest name we build is the prefix plus \"runs_job_started\".\nconst MAX_PREFIX = 63 - \"runs_job_started\".length;\n\n/**\n * Table names are built from the prefix, so it must be a plain lowercase\n * identifier. Uppercase is refused rather than folded: Postgres lowercases\n * unquoted names, so \"Monitoring_\" would quietly become \"monitoring_\".\n */\nexport function tablePrefix(prefix: string = DEFAULT_PREFIX): string {\n if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {\n throw new Error(\n `cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, ` +\n `not starting with a digit, at most ${MAX_PREFIX} characters.`,\n );\n }\n return prefix;\n}\n\nexport function schema(dialect: Dialect, p: string): string {\n const pg = dialect === \"postgres\";\n const int = pg ? \"BIGINT\" : \"INTEGER\";\n const json = pg ? \"JSONB\" : \"TEXT\";\n return `\n CREATE TABLE IF NOT EXISTS ${p}jobs (\n name TEXT PRIMARY KEY,\n definition ${json} NOT NULL,\n created_at ${int} NOT NULL,\n updated_at ${int} NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${p}runs (${pg ? \"\\n seq BIGSERIAL,\" : \"\"}\n id TEXT PRIMARY KEY,\n job TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at ${int} NOT NULL,\n finished_at ${int},\n duration_ms ${int},\n error TEXT,\n output TEXT,\n metrics ${json} NOT NULL DEFAULT '{}',\n trigger TEXT NOT NULL DEFAULT 'run'\n );\n CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);\n CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';\n CREATE TABLE IF NOT EXISTS ${p}state (\n job TEXT PRIMARY KEY,\n state ${json} NOT NULL\n );\n `;\n}\n\nexport function statements(dialect: Dialect, p: string) {\n const pg = dialect === \"postgres\";\n // Insertion order, to break ties between runs that started in the same millisecond.\n const seq = pg ? \"seq\" : \"rowid\";\n // Byte order on both, so names sort the same whatever the database's collation.\n const byName = pg ? `name COLLATE \"C\"` : \"name\";\n // The version inside a state's JSON, as stateVersion() reads it: a whole\n // number from 0 to 2^53 - 1, else 0 (none, or a foreign row's 1.5 or \"x\",\n // which must neither fail the statement nor refuse every write for good).\n // Each CASE tests the JSON type before any cast.\n const version = (column: string) => {\n if (pg) {\n const v = `(${column}->>'version')::numeric`;\n return `CASE WHEN jsonb_typeof(${column}->'version') <> 'number' THEN 0 WHEN ${v} % 1 = 0 AND ${v} BETWEEN 0 AND 9007199254740991 THEN ${v}::bigint ELSE 0 END`;\n }\n const v = `json_extract(${column}, '$.version')`;\n 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`;\n };\n const sql = {\n upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,\n getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,\n listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,\n deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,\n deleteState: `DELETE FROM ${p}state WHERE job = ?`,\n deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,\n insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,\n getRun: `SELECT * FROM ${p}runs WHERE id = ?`,\n listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,\n runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,\n getState: `SELECT state FROM ${p}state WHERE job = ?`,\n setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,\n // compareAndSetState. Expecting version 0 also matches a missing row, so\n // that case inserts; any other version must find its row.\n casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)\n ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,\n casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version(\"state\")} = ?`,\n // Each job's newest run is kept whatever its age: without it, a job that\n // runs less often than the retention looks like it never ran.\n prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?\n AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`,\n };\n if (pg) {\n for (const key of Object.keys(sql) as (keyof typeof sql)[]) {\n let n = 0;\n sql[key] = sql[key].replace(/\\?/g, () => `$${++n}`);\n }\n }\n return sql;\n}\n\n/**\n * updateRunIf: the update above, only while the stored status is one of\n * `count` statuses. Built per count, since the list is bound value by value.\n */\nexport function updateRunIfSql(dialect: Dialect, p: string, count: number): string {\n const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => \"?\").join(\", \")})`;\n if (dialect !== \"postgres\") return text;\n let n = 0;\n return text.replace(/\\?/g, () => `$${++n}`);\n}\n\n// Parameters in statement order, so both drivers bind the same values.\nexport const params = {\n upsertJob: (definition: StoredJobDefinition, now: number) => [definition.name, JSON.stringify(definition), now, now],\n insertRun: (run: Run) => [\n run.id, run.job, run.status, run.startedAt, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.trigger,\n ],\n updateRun: (run: Run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],\n updateRunIf: (run: Run, fromStatuses: Run[\"status\"][]) => [\n run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id, ...fromStatuses,\n ],\n setState: (state: JobState) => [state.job, JSON.stringify(state)],\n casInsert: (state: JobState) => [state.job, JSON.stringify(state)],\n casUpdate: (state: JobState, expectedVersion: number) => [JSON.stringify(state), state.job, expectedVersion],\n};\n\n// SQLite hands back JSON as TEXT and Postgres as parsed JSONB; Postgres returns BIGINT as a string.\ntype Json<T> = T | string;\ntype Int = number | string;\n\nexport interface JobRow { name: string; definition: Json<StoredJobDefinition>; created_at: Int; updated_at: Int }\nexport interface RunRow {\n id: string; job: string; status: Run[\"status\"]; started_at: Int; finished_at: Int | null;\n duration_ms: Int | null; error: string | null; output: string | null; metrics: Json<Record<string, number>> | null; trigger: string;\n}\nexport interface StateRow { state: Json<JobState> }\n\nconst json = <T>(v: Json<T>): T => (typeof v === \"string\" ? (JSON.parse(v) as T) : v);\nconst num = (v: Int | null): number | null => (v === null ? null : Number(v));\n\nexport function rowToJob(r: JobRow): StoredJob {\n return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };\n}\n\nexport function rowToRun(r: RunRow): Run {\n return {\n id: r.id, job: r.job, status: r.status, startedAt: Number(r.started_at), finishedAt: num(r.finished_at),\n durationMs: num(r.duration_ms), error: r.error, output: r.output, metrics: r.metrics === null ? {} : json(r.metrics), trigger: r.trigger,\n };\n}\n\nexport function rowToState(r: StateRow): JobState {\n return json(r.state);\n}\n","import type { Store, StoredJob } from \"../types.js\";\nimport { params, rowToJob, rowToRun, rowToState, schema, statements, tablePrefix, updateRunIfSql, type JobRow, type RunRow, type StateRow } from \"./sql.js\";\n\n/**\n * The parts of a Cloudflare D1 binding this store uses. Written out here so\n * the SDK needs nothing from @cloudflare/workers-types: a Worker's\n * `env.DB: D1Database` fits it as it is.\n */\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first(): Promise<unknown>;\n all(): Promise<{ results?: unknown[] }>;\n run(): Promise<{ meta?: { changes?: number } }>;\n}\n\nexport interface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n}\n\nexport interface D1Options {\n /** Table name prefix: lowercase letters, digits and underscores. Default \"cronwatch_\". */\n prefix?: string;\n /**\n * Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per\n * isolate. Default true. Pass false when a D1 migration creates them\n * instead (see the Cloudflare docs page for the file).\n */\n createTables?: boolean;\n}\n\n/** Databases (and prefixes) whose tables this isolate has already made sure of. */\nconst created = new WeakMap<object, Set<string>>();\n\n/**\n * Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is\n * the SQLite store's schema, statements and row mapping over D1's\n * prepare, bind and batch. Create it where the binding is, inside the\n * handler: `d1(env.DB)`.\n */\nexport function d1(database: D1DatabaseLike, options: D1Options = {}): Store {\n if (!database || typeof database.prepare !== \"function\" || typeof database.batch !== \"function\") {\n throw new Error(\"cronwatch: d1() needs a D1 database binding, such as env.DB\");\n }\n const p = tablePrefix(options.prefix);\n // D1 keeps its own tables under _cf_ and refuses writes to them.\n if (p.startsWith(\"_cf_\")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);\n const sql = statements(\"sqlite\", p);\n const createTables = options.createTables ?? true;\n\n const stmt = (text: string, values: unknown[] = []) => {\n const s = database.prepare(text);\n return values.length > 0 ? s.bind(...values) : s;\n };\n const first = async <T>(text: string, values: unknown[]): Promise<T | null> => ((await stmt(text, values).first()) as T | null) ?? null;\n const all = async <T>(text: string, values: unknown[] = []): Promise<T[]> => ((await stmt(text, values).all()).results ?? []) as T[];\n const changes = async (text: string, values: unknown[]): Promise<number> => (await stmt(text, values).run()).meta?.changes ?? 0;\n\n return {\n async init() {\n if (!createTables || created.get(database)?.has(p)) return;\n // exec() splits on newlines, so each statement goes in on its own, in one batch.\n const ddl = schema(\"sqlite\", p).split(\";\").map((s) => s.trim()).filter(Boolean);\n await database.batch(ddl.map((s) => database.prepare(s)));\n // Remembered only once it worked, and as a flag rather than a promise,\n // so no invocation ever waits on another's unfinished I/O.\n let prefixes = created.get(database);\n if (!prefixes) created.set(database, (prefixes = new Set()));\n prefixes.add(p);\n },\n async upsertJob(definition, now) {\n await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();\n },\n async getJob(name) {\n const r = await first<JobRow>(sql.getJob, [name]);\n return r ? rowToJob(r) : null;\n },\n async listJobs(): Promise<StoredJob[]> {\n return (await all<JobRow>(sql.listJobs)).map(rowToJob);\n },\n async deleteJob(name) {\n // A batch is one transaction.\n await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);\n },\n async insertRun(run) {\n await stmt(sql.insertRun, params.insertRun(run)).run();\n },\n async updateRun(run) {\n await stmt(sql.updateRun, params.updateRun(run)).run();\n },\n async updateRunIf(run, fromStatuses) {\n if (fromStatuses.length === 0) return false;\n return (await changes(updateRunIfSql(\"sqlite\", p, fromStatuses.length), params.updateRunIf(run, fromStatuses))) > 0;\n },\n async getRun(id) {\n const r = await first<RunRow>(sql.getRun, [id]);\n return r ? rowToRun(r) : null;\n },\n async listRuns(job, limit) {\n return (await all<RunRow>(sql.listRuns, [job, limit])).map(rowToRun);\n },\n async lastRun(job) {\n const r = await first<RunRow>(sql.listRuns, [job, 1]);\n return r ? rowToRun(r) : null;\n },\n async runningRuns() {\n return (await all<RunRow>(sql.runningRuns)).map(rowToRun);\n },\n async getState(job) {\n const r = await first<StateRow>(sql.getState, [job]);\n return r ? rowToState(r) : null;\n },\n async setState(state) {\n await stmt(sql.setState, params.setState(state)).run();\n },\n async compareAndSetState(state, expectedVersion) {\n const written = expectedVersion === 0\n ? await changes(sql.casInsert, params.casInsert(state))\n : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));\n return written > 0;\n },\n async prune(before) {\n return changes(sql.prune, [before]);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/output.ts","../src/stores/sql.ts","../src/stores/d1.ts"],"names":["text","json"],"mappings":"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","file":"d1.js","sourcesContent":["/** Output is capped so a chatty job cannot fill the store. The tail is kept. */\nexport const OUTPUT_CAP = 16 * 1024;\n\n/**\n * NUL characters are removed first, since Postgres refuses them in TEXT and\n * JSONB and the whole run row would be lost. The cap then applies to what is\n * left.\n */\nexport function capOutput(text: string): string {\n const clean = stripNul(text);\n if (clean.length <= OUTPUT_CAP) return clean;\n return \"[earlier output trimmed]\\n\" + clean.slice(clean.length - OUTPUT_CAP);\n}\n\n/** Removes every U+0000. */\nexport function stripNul(text: string): string {\n return text.includes(\"\\u0000\") ? text.replace(/\\u0000/g, \"\") : text;\n}\n\n/**\n * Removes every U+0000 from JSON text, keys and strings alike, by dropping\n * each \\u0000 escape (a NUL can appear in JSON no other way). Escapes are\n * read left to right in pairs, so an escaped backslash followed by \"u0000\"\n * is left as it is.\n */\nexport function stripJsonNul(json: string): string {\n return json.includes(\"\\\\u0000\") ? json.replace(/\\\\(u0000|[\\s\\S])/g, (escape, next: string) => (next === \"u0000\" ? \"\" : escape)) : json;\n}\n\n/** \"Name: message\" and the first five stack frames, capped like output. */\nexport function errorMessage(error: unknown): string {\n return capOutput(describeError(error));\n}\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) {\n // The stack repeats the header (over several lines when the message has\n // newlines), so take only its frames.\n const frames = (error.stack ?? \"\").split(\"\\n\").filter((line) => /^\\s+at /.test(line)).slice(0, 5);\n return `${error.name}: ${error.message}${frames.length ? `\\n${frames.join(\"\\n\")}` : \"\"}`;\n }\n if (typeof error === \"string\") return error;\n try {\n return JSON.stringify(error) ?? String(error);\n } catch {\n return String(error);\n }\n}\n\nconst REDACTED = \"[redacted]\";\n\n// Bounded quantifiers throughout, so a long line cannot make these backtrack.\n// They apply in this order, each to the text the ones before it left.\nconst SECRET_PATTERNS: [RegExp, string | ((match: string, ...groups: string[]) => string)][] = [\n // A PEM private key, header to footer. Without a footer (the output was\n // trimmed) it runs to the end of the base64 body. A \"-\" that starts five\n // dashes ends the body, so the footer is never swallowed into it.\n [\n /-----BEGIN (?:[A-Z0-9]{1,20} ){0,3}PRIVATE KEY-----(?:[A-Za-z0-9+/=\\s,:]|-(?!----)){0,16384}(?:-----END (?:[A-Z0-9]{1,20} ){0,3}PRIVATE KEY-----)?/g,\n REDACTED,\n ],\n // password=..., API_KEY: ..., \"client_secret\": \"...\", TOKEN='...', token=...,\n // :password=>\"...\" (but not max_tokens: 800). A quoted value is blanked to\n // its closing quote, spaces and all, and keeps its quotes.\n [\n /\\b([A-Za-z0-9_-]{0,40}(?:secret|token|passw(?:or)?d|pwd|api[_-]?key|access[_-]?key|private[_-]?key|credential)[A-Za-z0-9_-]{0,40}(?<![Tt][Oo][Kk][Ee][Nn][Ss])\"?\\s{0,3}(?:=>|[=:])\\s{0,3})(?:(\")[^\"\\n]{1,4096}\"|(')[^'\\n]{1,4096}'|[\"']?[^\\s\"',;&]{1,4096})/gi,\n (_match, name: string, double?: string, single?: string) => {\n const quote = double ?? single ?? \"\";\n return `${name}${quote}${REDACTED}${quote}`;\n },\n ],\n // Authorization: Basic <base64> and Authorization: Token <token>, also as a JSON or hash entry.\n [/\\b((?:proxy-)?authorization[\"']?\\s{0,3}(?:=>|[=:])\\s{0,3}[\"']?\\s{0,3}(?:basic|token)\\s{1,3})[A-Za-z0-9._~+/=:-]{1,4096}/gi, `$1${REDACTED}`],\n // Credentials inside a URL: postgres://user:password@host. The password\n // runs to the last \"@\" before a \"/\" or a space, so one that contains \"@\"\n // is blanked whole.\n [/(\\b[a-z][a-z0-9+.-]{0,30}:\\/\\/[^\\s/:@]{0,256}:)[^\\s/]{1,256}@/gi, `$1${REDACTED}@`],\n // Authorization: Bearer <token>\n [/\\b(Bearer\\s{1,3})[A-Za-z0-9._~+/=-]{8,4096}/g, `$1${REDACTED}`],\n // A bare JWT: three base64url segments, the first starting eyJ.\n [/\\beyJ[A-Za-z0-9_-]{4,4096}\\.[A-Za-z0-9_-]{4,4096}\\.[A-Za-z0-9_-]{0,4096}/g, REDACTED],\n // Incoming webhook URLs carry their secret in the path.\n [/(\\bhooks\\.slack\\.com\\/(?:services|workflows|triggers)\\/)[A-Za-z0-9/_-]{1,255}/gi, `$1${REDACTED}`],\n [/(\\bdiscord(?:app)?\\.com\\/api\\/(?:v\\d{1,2}\\/)?webhooks\\/)[A-Za-z0-9/_-]{1,255}/gi, `$1${REDACTED}`],\n // Well-known token shapes: AWS, GitHub, Slack, Stripe, Anthropic, OpenAI and Google style keys.\n [/\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g, REDACTED],\n [/\\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{20,255})\\b/g, REDACTED],\n [/\\bxox[abposr]-[A-Za-z0-9-]{10,255}/g, REDACTED],\n [/\\b[rsp]k_(?:live|test)_[A-Za-z0-9]{10,255}\\b/g, REDACTED],\n [/\\bwhsec_[A-Za-z0-9+/=]{16,255}/g, REDACTED],\n [/\\bsk-[A-Za-z0-9_-]{20,255}/g, REDACTED],\n [/\\bAIza[0-9A-Za-z_-]{35}(?![0-9A-Za-z_-])/g, REDACTED],\n];\n\n/**\n * The default `redact`: blanks values that look like secrets (key=value pairs\n * with secret-ish names, Authorization headers, URL credentials, bearer\n * tokens, JWTs, PEM private keys, webhook URLs and well-known token formats)\n * before output or an error is stored, shown or sent anywhere.\n */\nexport function redactSecrets(text: string): string {\n let out = text;\n for (const [pattern, replacement] of SECRET_PATTERNS) out = out.replace(pattern, replacement as string);\n return out;\n}\n","import { stripJsonNul, stripNul } from \"../output.js\";\nimport type { JobState, Run, StoredJob, StoredJobDefinition } from \"../types.js\";\n\n/**\n * The schema, statements and row mapping shared by the SQLite and Postgres\n * stores. Imports no driver, so either entry point can pull it in alone.\n * Statements are written with `?` placeholders; Postgres numbers them.\n */\n\nexport type Dialect = \"sqlite\" | \"postgres\";\n\nexport const DEFAULT_PREFIX = \"cronwatch_\";\n\n// Postgres truncates identifiers past 63 bytes; the longest name we build is the prefix plus \"runs_job_started\".\nconst MAX_PREFIX = 63 - \"runs_job_started\".length;\n\n/**\n * Table names are built from the prefix, so it must be a plain lowercase\n * identifier. Uppercase is refused rather than folded: Postgres lowercases\n * unquoted names, so \"Monitoring_\" would quietly become \"monitoring_\".\n */\nexport function tablePrefix(prefix: string = DEFAULT_PREFIX): string {\n if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {\n throw new Error(\n `cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, ` +\n `not starting with a digit, at most ${MAX_PREFIX} characters.`,\n );\n }\n return prefix;\n}\n\nexport function schema(dialect: Dialect, p: string): string {\n const pg = dialect === \"postgres\";\n const int = pg ? \"BIGINT\" : \"INTEGER\";\n const json = pg ? \"JSONB\" : \"TEXT\";\n return `\n CREATE TABLE IF NOT EXISTS ${p}jobs (\n name TEXT PRIMARY KEY,\n definition ${json} NOT NULL,\n created_at ${int} NOT NULL,\n updated_at ${int} NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${p}runs (${pg ? \"\\n seq BIGSERIAL,\" : \"\"}\n id TEXT PRIMARY KEY,\n job TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at ${int} NOT NULL,\n finished_at ${int},\n duration_ms ${int},\n error TEXT,\n output TEXT,\n metrics ${json} NOT NULL DEFAULT '{}',\n trigger TEXT NOT NULL DEFAULT 'run'\n );\n CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);\n CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';\n CREATE TABLE IF NOT EXISTS ${p}state (\n job TEXT PRIMARY KEY,\n state ${json} NOT NULL\n );\n `;\n}\n\nexport function statements(dialect: Dialect, p: string) {\n const pg = dialect === \"postgres\";\n // Insertion order, to break ties between runs that started in the same millisecond.\n const seq = pg ? \"seq\" : \"rowid\";\n // Byte order on both, so names sort the same whatever the database's collation.\n const byName = pg ? `name COLLATE \"C\"` : \"name\";\n // The version inside a state's JSON, as stateVersion() reads it: a whole\n // number from 0 to 2^53 - 1, else 0 (none, or a foreign row's 1.5 or \"x\",\n // which must neither fail the statement nor refuse every write for good).\n // Each CASE tests the JSON type before any cast.\n const version = (column: string) => {\n if (pg) {\n const v = `(${column}->>'version')::numeric`;\n return `CASE WHEN jsonb_typeof(${column}->'version') <> 'number' THEN 0 WHEN ${v} % 1 = 0 AND ${v} BETWEEN 0 AND 9007199254740991 THEN ${v}::bigint ELSE 0 END`;\n }\n const v = `json_extract(${column}, '$.version')`;\n 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`;\n };\n const sql = {\n upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,\n getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,\n listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,\n deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,\n deleteState: `DELETE FROM ${p}state WHERE job = ?`,\n deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,\n insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,\n getRun: `SELECT * FROM ${p}runs WHERE id = ?`,\n listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,\n runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,\n getState: `SELECT state FROM ${p}state WHERE job = ?`,\n setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,\n // compareAndSetState. Expecting version 0 also matches a missing row, so\n // that case inserts; any other version must find its row.\n casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)\n ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,\n casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version(\"state\")} = ?`,\n // Each job's newest run is kept whatever its age: without it, a job that\n // runs less often than the retention looks like it never ran.\n prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?\n AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`,\n };\n if (pg) {\n for (const key of Object.keys(sql) as (keyof typeof sql)[]) {\n let n = 0;\n sql[key] = sql[key].replace(/\\?/g, () => `$${++n}`);\n }\n }\n return sql;\n}\n\n/**\n * updateRunIf: the update above, only while the stored status is one of\n * `count` statuses. Built per count, since the list is bound value by value.\n */\nexport function updateRunIfSql(dialect: Dialect, p: string, count: number): string {\n const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => \"?\").join(\", \")})`;\n if (dialect !== \"postgres\") return text;\n let n = 0;\n return text.replace(/\\?/g, () => `$${++n}`);\n}\n\n/**\n * Postgres refuses U+0000 in TEXT and JSONB, and a refused write loses the\n * whole row, so every dialect writes text without it: a run's trigger,\n * output, error and metric names, and every key and string of a definition\n * and a state. Identifiers (a job's name, a run's id) are written as given;\n * the client refuses one with a NUL before it gets here.\n */\nconst text = (value: string | null) => (value === null ? null : stripNul(value));\nconst jsonText = (value: unknown) => stripJsonNul(JSON.stringify(value));\n\n// Parameters in statement order, so both drivers bind the same values.\nexport const params = {\n upsertJob: (definition: StoredJobDefinition, now: number) => [definition.name, jsonText(definition), now, now],\n insertRun: (run: Run) => [\n run.id, run.job, run.status, run.startedAt, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), stripNul(run.trigger),\n ],\n updateRun: (run: Run) => [run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id],\n updateRunIf: (run: Run, fromStatuses: Run[\"status\"][]) => [\n run.status, run.finishedAt, run.durationMs, text(run.error), text(run.output), jsonText(run.metrics), run.id, ...fromStatuses,\n ],\n setState: (state: JobState) => [state.job, jsonText(state)],\n casInsert: (state: JobState) => [state.job, jsonText(state)],\n casUpdate: (state: JobState, expectedVersion: number) => [jsonText(state), state.job, expectedVersion],\n};\n\n// SQLite hands back JSON as TEXT and Postgres as parsed JSONB; Postgres returns BIGINT as a string.\ntype Json<T> = T | string;\ntype Int = number | string;\n\nexport interface JobRow { name: string; definition: Json<StoredJobDefinition>; created_at: Int; updated_at: Int }\nexport interface RunRow {\n id: string; job: string; status: Run[\"status\"]; started_at: Int; finished_at: Int | null;\n duration_ms: Int | null; error: string | null; output: string | null; metrics: Json<Record<string, number>> | null; trigger: string;\n}\nexport interface StateRow { state: Json<JobState> }\n\nconst json = <T>(v: Json<T>): T => (typeof v === \"string\" ? (JSON.parse(v) as T) : v);\nconst num = (v: Int | null): number | null => (v === null ? null : Number(v));\n\nexport function rowToJob(r: JobRow): StoredJob {\n return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };\n}\n\nexport function rowToRun(r: RunRow): Run {\n return {\n id: r.id, job: r.job, status: r.status, startedAt: Number(r.started_at), finishedAt: num(r.finished_at),\n durationMs: num(r.duration_ms), error: r.error, output: r.output, metrics: r.metrics === null ? {} : json(r.metrics), trigger: r.trigger,\n };\n}\n\nexport function rowToState(r: StateRow): JobState {\n return json(r.state);\n}\n","import type { Store, StoredJob } from \"../types.js\";\nimport { params, rowToJob, rowToRun, rowToState, schema, statements, tablePrefix, updateRunIfSql, type JobRow, type RunRow, type StateRow } from \"./sql.js\";\n\n/**\n * The parts of a Cloudflare D1 binding this store uses. Written out here so\n * the SDK needs nothing from @cloudflare/workers-types: a Worker's\n * `env.DB: D1Database` fits it as it is.\n */\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first(): Promise<unknown>;\n all(): Promise<{ results?: unknown[] }>;\n run(): Promise<{ meta?: { changes?: number } }>;\n}\n\nexport interface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n}\n\nexport interface D1Options {\n /** Table name prefix: lowercase letters, digits and underscores. Default \"cronwatch_\". */\n prefix?: string;\n /**\n * Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per\n * isolate. Default true. Pass false when a D1 migration creates them\n * instead (see the Cloudflare docs page for the file).\n */\n createTables?: boolean;\n}\n\n/** Databases (and prefixes) whose tables this isolate has already made sure of. */\nconst created = new WeakMap<object, Set<string>>();\n\n/**\n * Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is\n * the SQLite store's schema, statements and row mapping over D1's\n * prepare, bind and batch. Create it where the binding is, inside the\n * handler: `d1(env.DB)`.\n */\nexport function d1(database: D1DatabaseLike, options: D1Options = {}): Store {\n if (!database || typeof database.prepare !== \"function\" || typeof database.batch !== \"function\") {\n throw new Error(\"cronwatch: d1() needs a D1 database binding, such as env.DB\");\n }\n const p = tablePrefix(options.prefix);\n // D1 keeps its own tables under _cf_ and refuses writes to them.\n if (p.startsWith(\"_cf_\")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);\n const sql = statements(\"sqlite\", p);\n const createTables = options.createTables ?? true;\n\n const stmt = (text: string, values: unknown[] = []) => {\n const s = database.prepare(text);\n return values.length > 0 ? s.bind(...values) : s;\n };\n const first = async <T>(text: string, values: unknown[]): Promise<T | null> => ((await stmt(text, values).first()) as T | null) ?? null;\n const all = async <T>(text: string, values: unknown[] = []): Promise<T[]> => ((await stmt(text, values).all()).results ?? []) as T[];\n const changes = async (text: string, values: unknown[]): Promise<number> => (await stmt(text, values).run()).meta?.changes ?? 0;\n\n return {\n async init() {\n if (!createTables || created.get(database)?.has(p)) return;\n // exec() splits on newlines, so each statement goes in on its own, in one batch.\n const ddl = schema(\"sqlite\", p).split(\";\").map((s) => s.trim()).filter(Boolean);\n await database.batch(ddl.map((s) => database.prepare(s)));\n // Remembered only once it worked, and as a flag rather than a promise,\n // so no invocation ever waits on another's unfinished I/O.\n let prefixes = created.get(database);\n if (!prefixes) created.set(database, (prefixes = new Set()));\n prefixes.add(p);\n },\n async upsertJob(definition, now) {\n await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();\n },\n async getJob(name) {\n const r = await first<JobRow>(sql.getJob, [name]);\n return r ? rowToJob(r) : null;\n },\n async listJobs(): Promise<StoredJob[]> {\n return (await all<JobRow>(sql.listJobs)).map(rowToJob);\n },\n async deleteJob(name) {\n // A batch is one transaction.\n await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);\n },\n async insertRun(run) {\n await stmt(sql.insertRun, params.insertRun(run)).run();\n },\n async updateRun(run) {\n await stmt(sql.updateRun, params.updateRun(run)).run();\n },\n async updateRunIf(run, fromStatuses) {\n if (fromStatuses.length === 0) return false;\n return (await changes(updateRunIfSql(\"sqlite\", p, fromStatuses.length), params.updateRunIf(run, fromStatuses))) > 0;\n },\n async getRun(id) {\n const r = await first<RunRow>(sql.getRun, [id]);\n return r ? rowToRun(r) : null;\n },\n async listRuns(job, limit) {\n return (await all<RunRow>(sql.listRuns, [job, limit])).map(rowToRun);\n },\n async lastRun(job) {\n const r = await first<RunRow>(sql.listRuns, [job, 1]);\n return r ? rowToRun(r) : null;\n },\n async runningRuns() {\n return (await all<RunRow>(sql.runningRuns)).map(rowToRun);\n },\n async getState(job) {\n const r = await first<StateRow>(sql.getState, [job]);\n return r ? rowToState(r) : null;\n },\n async setState(state) {\n await stmt(sql.setState, params.setState(state)).run();\n },\n async compareAndSetState(state, expectedVersion) {\n const written = expectedVersion === 0\n ? await changes(sql.casInsert, params.casInsert(state))\n : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));\n return written > 0;\n },\n async prune(before) {\n return changes(sql.prune, [before]);\n },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.cjs';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.cjs';
2
2
 
3
3
  /**
4
4
  * Datadog Events API v1.
package/dist/datadog.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.js';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.js';
2
2
 
3
3
  /**
4
4
  * Datadog Events API v1.
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.cjs';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.cjs';
2
2
 
3
3
  interface DiscordOptions {
4
4
  /** A channel webhook URL from Server Settings, Integrations, Webhooks. */
package/dist/discord.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.js';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.js';
2
2
 
3
3
  interface DiscordOptions {
4
4
  /** A channel webhook URL from Server Settings, Integrations, Webhooks. */
@@ -1,4 +1,4 @@
1
- import { A as Alert } from './types-BjBbCGeJ.js';
1
+ import { A as Alert } from './types-Cz47Boks.js';
2
2
 
3
3
  /**
4
4
  * What every email channel sends: one subject, a plain text body and a small
@@ -1,4 +1,4 @@
1
- import { A as Alert } from './types-BjBbCGeJ.cjs';
1
+ import { A as Alert } from './types-Cz47Boks.cjs';
2
2
 
3
3
  /**
4
4
  * What every email channel sends: one subject, a plain text body and a small
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.cjs';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.cjs';
2
2
 
3
3
  /**
4
4
  * Honeybadger, as error notices (not Check-ins, a separate product).
@@ -1,4 +1,4 @@
1
- import { A as Alert, a as AlertChannel } from './types-BjBbCGeJ.js';
1
+ import { A as Alert, a as AlertChannel } from './types-Cz47Boks.js';
2
2
 
3
3
  /**
4
4
  * Honeybadger, as error notices (not Check-ins, a separate product).