@cronwatch/sdk 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/anthropic.d.cts +1 -1
- package/dist/anthropic.d.ts +1 -1
- package/dist/bugsnag.cjs +108 -0
- package/dist/bugsnag.cjs.map +1 -0
- package/dist/bugsnag.d.cts +26 -0
- package/dist/bugsnag.d.ts +26 -0
- package/dist/bugsnag.js +106 -0
- package/dist/bugsnag.js.map +1 -0
- package/dist/client-B7bqnkI5.d.cts +450 -0
- package/dist/client-pmqH3apS.d.ts +450 -0
- package/dist/d1.cjs +214 -0
- package/dist/d1.cjs.map +1 -0
- package/dist/d1.d.cts +42 -0
- package/dist/d1.d.ts +42 -0
- package/dist/d1.js +212 -0
- package/dist/d1.js.map +1 -0
- package/dist/datadog.cjs +95 -0
- package/dist/datadog.cjs.map +1 -0
- package/dist/datadog.d.cts +24 -0
- package/dist/datadog.d.ts +24 -0
- package/dist/datadog.js +93 -0
- package/dist/datadog.js.map +1 -0
- package/dist/discord.cjs +2 -0
- package/dist/discord.cjs.map +1 -1
- package/dist/discord.d.cts +1 -1
- package/dist/discord.d.ts +1 -1
- package/dist/discord.js +2 -0
- package/dist/discord.js.map +1 -1
- package/dist/email-BJ-z_epD.d.cts +21 -0
- package/dist/email-_H0faCh3.d.ts +21 -0
- package/dist/honeybadger.cjs +101 -0
- package/dist/honeybadger.cjs.map +1 -0
- package/dist/honeybadger.d.cts +23 -0
- package/dist/honeybadger.d.ts +23 -0
- package/dist/honeybadger.js +99 -0
- package/dist/honeybadger.js.map +1 -0
- package/dist/index.cjs +462 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -260
- package/dist/index.d.ts +5 -260
- package/dist/index.js +462 -45
- package/dist/index.js.map +1 -1
- package/dist/mailgun.cjs +115 -0
- package/dist/mailgun.cjs.map +1 -0
- package/dist/mailgun.d.cts +21 -0
- package/dist/mailgun.d.ts +21 -0
- package/dist/mailgun.js +113 -0
- package/dist/mailgun.js.map +1 -0
- package/dist/newrelic.cjs +88 -0
- package/dist/newrelic.cjs.map +1 -0
- package/dist/newrelic.d.cts +25 -0
- package/dist/newrelic.d.ts +25 -0
- package/dist/newrelic.js +86 -0
- package/dist/newrelic.js.map +1 -0
- package/dist/node.cjs +191 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +97 -0
- package/dist/node.d.ts +97 -0
- package/dist/node.js +186 -0
- package/dist/node.js.map +1 -0
- package/dist/pg-cron.cjs +286 -0
- package/dist/pg-cron.cjs.map +1 -0
- package/dist/pg-cron.d.cts +93 -0
- package/dist/pg-cron.d.ts +93 -0
- package/dist/pg-cron.js +280 -0
- package/dist/pg-cron.js.map +1 -0
- package/dist/postgres.cjs +20 -0
- package/dist/postgres.cjs.map +1 -1
- package/dist/postgres.d.cts +1 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.js +20 -0
- package/dist/postgres.js.map +1 -1
- package/dist/postmark.cjs +106 -0
- package/dist/postmark.cjs.map +1 -0
- package/dist/postmark.d.cts +18 -0
- package/dist/postmark.d.ts +18 -0
- package/dist/postmark.js +104 -0
- package/dist/postmark.js.map +1 -0
- package/dist/resend.cjs +114 -0
- package/dist/resend.cjs.map +1 -0
- package/dist/resend.d.cts +16 -0
- package/dist/resend.d.ts +16 -0
- package/dist/resend.js +112 -0
- package/dist/resend.js.map +1 -0
- package/dist/rollbar.cjs +108 -0
- package/dist/rollbar.cjs.map +1 -0
- package/dist/rollbar.d.cts +20 -0
- package/dist/rollbar.d.ts +20 -0
- package/dist/rollbar.js +106 -0
- package/dist/rollbar.js.map +1 -0
- package/dist/sendgrid.cjs +114 -0
- package/dist/sendgrid.cjs.map +1 -0
- package/dist/sendgrid.d.cts +19 -0
- package/dist/sendgrid.d.ts +19 -0
- package/dist/sendgrid.js +112 -0
- package/dist/sendgrid.js.map +1 -0
- package/dist/sentry.cjs +130 -0
- package/dist/sentry.cjs.map +1 -0
- package/dist/sentry.d.cts +28 -0
- package/dist/sentry.d.ts +28 -0
- package/dist/sentry.js +127 -0
- package/dist/sentry.js.map +1 -0
- package/dist/ses.cjs +170 -0
- package/dist/ses.cjs.map +1 -0
- package/dist/ses.d.cts +25 -0
- package/dist/ses.d.ts +25 -0
- package/dist/ses.js +168 -0
- package/dist/ses.js.map +1 -0
- package/dist/slack.cjs +2 -0
- package/dist/slack.cjs.map +1 -1
- package/dist/slack.d.cts +1 -1
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +2 -0
- package/dist/slack.js.map +1 -1
- package/dist/sqlite.cjs +18 -0
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.js +18 -0
- package/dist/sqlite.js.map +1 -1
- package/dist/twilio.cjs +168 -0
- package/dist/twilio.cjs.map +1 -0
- package/dist/twilio.d.cts +52 -0
- package/dist/twilio.d.ts +52 -0
- package/dist/twilio.js +162 -0
- package/dist/twilio.js.map +1 -0
- package/dist/{types-DUt2apl0.d.cts → types-Ddq1MUPL.d.cts} +29 -2
- package/dist/{types-DUt2apl0.d.ts → types-Ddq1MUPL.d.ts} +29 -2
- package/dist/webhook.cjs +11 -5
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +6 -3
- package/dist/webhook.d.ts +6 -3
- package/dist/webhook.js +11 -6
- package/dist/webhook.js.map +1 -1
- package/package.json +208 -2
package/dist/d1.cjs.map
ADDED
|
@@ -0,0 +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, 0 when it has none.\n const version = (column: string) => (pg ? `COALESCE((${column}->>'version')::bigint, 0)` : `COALESCE(json_extract(${column}, '$.version'), 0)`);\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"]}
|
package/dist/d1.d.cts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { S as Store } from './types-Ddq1MUPL.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The parts of a Cloudflare D1 binding this store uses. Written out here so
|
|
5
|
+
* the SDK needs nothing from @cloudflare/workers-types: a Worker's
|
|
6
|
+
* `env.DB: D1Database` fits it as it is.
|
|
7
|
+
*/
|
|
8
|
+
interface D1PreparedStatementLike {
|
|
9
|
+
bind(...values: unknown[]): D1PreparedStatementLike;
|
|
10
|
+
first(): Promise<unknown>;
|
|
11
|
+
all(): Promise<{
|
|
12
|
+
results?: unknown[];
|
|
13
|
+
}>;
|
|
14
|
+
run(): Promise<{
|
|
15
|
+
meta?: {
|
|
16
|
+
changes?: number;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
interface D1DatabaseLike {
|
|
21
|
+
prepare(query: string): D1PreparedStatementLike;
|
|
22
|
+
batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;
|
|
23
|
+
}
|
|
24
|
+
interface D1Options {
|
|
25
|
+
/** Table name prefix: lowercase letters, digits and underscores. Default "cronwatch_". */
|
|
26
|
+
prefix?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per
|
|
29
|
+
* isolate. Default true. Pass false when a D1 migration creates them
|
|
30
|
+
* instead (see the Cloudflare docs page for the file).
|
|
31
|
+
*/
|
|
32
|
+
createTables?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is
|
|
36
|
+
* the SQLite store's schema, statements and row mapping over D1's
|
|
37
|
+
* prepare, bind and batch. Create it where the binding is, inside the
|
|
38
|
+
* handler: `d1(env.DB)`.
|
|
39
|
+
*/
|
|
40
|
+
declare function d1(database: D1DatabaseLike, options?: D1Options): Store;
|
|
41
|
+
|
|
42
|
+
export { type D1DatabaseLike, type D1Options, type D1PreparedStatementLike, d1 };
|
package/dist/d1.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { S as Store } from './types-Ddq1MUPL.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The parts of a Cloudflare D1 binding this store uses. Written out here so
|
|
5
|
+
* the SDK needs nothing from @cloudflare/workers-types: a Worker's
|
|
6
|
+
* `env.DB: D1Database` fits it as it is.
|
|
7
|
+
*/
|
|
8
|
+
interface D1PreparedStatementLike {
|
|
9
|
+
bind(...values: unknown[]): D1PreparedStatementLike;
|
|
10
|
+
first(): Promise<unknown>;
|
|
11
|
+
all(): Promise<{
|
|
12
|
+
results?: unknown[];
|
|
13
|
+
}>;
|
|
14
|
+
run(): Promise<{
|
|
15
|
+
meta?: {
|
|
16
|
+
changes?: number;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
interface D1DatabaseLike {
|
|
21
|
+
prepare(query: string): D1PreparedStatementLike;
|
|
22
|
+
batch(statements: D1PreparedStatementLike[]): Promise<unknown[]>;
|
|
23
|
+
}
|
|
24
|
+
interface D1Options {
|
|
25
|
+
/** Table name prefix: lowercase letters, digits and underscores. Default "cronwatch_". */
|
|
26
|
+
prefix?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Create the tables on first use (CREATE TABLE IF NOT EXISTS), once per
|
|
29
|
+
* isolate. Default true. Pass false when a D1 migration creates them
|
|
30
|
+
* instead (see the Cloudflare docs page for the file).
|
|
31
|
+
*/
|
|
32
|
+
createTables?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Keeps everything in Cloudflare D1, for Workers. D1 is SQLite, so this is
|
|
36
|
+
* the SQLite store's schema, statements and row mapping over D1's
|
|
37
|
+
* prepare, bind and batch. Create it where the binding is, inside the
|
|
38
|
+
* handler: `d1(env.DB)`.
|
|
39
|
+
*/
|
|
40
|
+
declare function d1(database: D1DatabaseLike, options?: D1Options): Store;
|
|
41
|
+
|
|
42
|
+
export { type D1DatabaseLike, type D1Options, type D1PreparedStatementLike, d1 };
|
package/dist/d1.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// src/stores/sql.ts
|
|
2
|
+
var DEFAULT_PREFIX = "cronwatch_";
|
|
3
|
+
var MAX_PREFIX = 63 - "runs_job_started".length;
|
|
4
|
+
function tablePrefix(prefix = DEFAULT_PREFIX) {
|
|
5
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(prefix) || prefix.length > MAX_PREFIX) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
`cronwatch: invalid table prefix ${JSON.stringify(prefix)}. Use lowercase letters, digits and underscores, not starting with a digit, at most ${MAX_PREFIX} characters.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return prefix;
|
|
11
|
+
}
|
|
12
|
+
function schema(dialect, p) {
|
|
13
|
+
const int = "INTEGER";
|
|
14
|
+
const json2 = "TEXT";
|
|
15
|
+
return `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS ${p}jobs (
|
|
17
|
+
name TEXT PRIMARY KEY,
|
|
18
|
+
definition ${json2} NOT NULL,
|
|
19
|
+
created_at ${int} NOT NULL,
|
|
20
|
+
updated_at ${int} NOT NULL
|
|
21
|
+
);
|
|
22
|
+
CREATE TABLE IF NOT EXISTS ${p}runs (${""}
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
job TEXT NOT NULL,
|
|
25
|
+
status TEXT NOT NULL,
|
|
26
|
+
started_at ${int} NOT NULL,
|
|
27
|
+
finished_at ${int},
|
|
28
|
+
duration_ms ${int},
|
|
29
|
+
error TEXT,
|
|
30
|
+
output TEXT,
|
|
31
|
+
metrics ${json2} NOT NULL DEFAULT '{}',
|
|
32
|
+
trigger TEXT NOT NULL DEFAULT 'run'
|
|
33
|
+
);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS ${p}runs_job_started ON ${p}runs (job, started_at DESC);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS ${p}runs_running ON ${p}runs (status) WHERE status = 'running';
|
|
36
|
+
CREATE TABLE IF NOT EXISTS ${p}state (
|
|
37
|
+
job TEXT PRIMARY KEY,
|
|
38
|
+
state ${json2} NOT NULL
|
|
39
|
+
);
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
function statements(dialect, p) {
|
|
43
|
+
const seq = "rowid";
|
|
44
|
+
const byName = "name";
|
|
45
|
+
const version = (column) => `COALESCE(json_extract(${column}, '$.version'), 0)`;
|
|
46
|
+
const sql = {
|
|
47
|
+
upsertJob: `INSERT INTO ${p}jobs (name, definition, created_at, updated_at) VALUES (?, ?, ?, ?)
|
|
48
|
+
ON CONFLICT (name) DO UPDATE SET definition = excluded.definition, updated_at = excluded.updated_at`,
|
|
49
|
+
getJob: `SELECT * FROM ${p}jobs WHERE name = ?`,
|
|
50
|
+
listJobs: `SELECT * FROM ${p}jobs ORDER BY ${byName}`,
|
|
51
|
+
deleteRuns: `DELETE FROM ${p}runs WHERE job = ?`,
|
|
52
|
+
deleteState: `DELETE FROM ${p}state WHERE job = ?`,
|
|
53
|
+
deleteJob: `DELETE FROM ${p}jobs WHERE name = ?`,
|
|
54
|
+
insertRun: `INSERT INTO ${p}runs (id, job, status, started_at, finished_at, duration_ms, error, output, metrics, trigger)
|
|
55
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
56
|
+
updateRun: `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ?`,
|
|
57
|
+
getRun: `SELECT * FROM ${p}runs WHERE id = ?`,
|
|
58
|
+
listRuns: `SELECT * FROM ${p}runs WHERE job = ? ORDER BY started_at DESC, ${seq} DESC LIMIT ?`,
|
|
59
|
+
runningRuns: `SELECT * FROM ${p}runs WHERE status = 'running' ORDER BY started_at, ${seq}`,
|
|
60
|
+
getState: `SELECT state FROM ${p}state WHERE job = ?`,
|
|
61
|
+
setState: `INSERT INTO ${p}state (job, state) VALUES (?, ?) ON CONFLICT (job) DO UPDATE SET state = excluded.state`,
|
|
62
|
+
// compareAndSetState. Expecting version 0 also matches a missing row, so
|
|
63
|
+
// that case inserts; any other version must find its row.
|
|
64
|
+
casInsert: `INSERT INTO ${p}state (job, state) VALUES (?, ?)
|
|
65
|
+
ON CONFLICT (job) DO UPDATE SET state = excluded.state WHERE ${version(`${p}state.state`)} = 0`,
|
|
66
|
+
casUpdate: `UPDATE ${p}state SET state = ? WHERE job = ? AND ${version("state")} = ?`,
|
|
67
|
+
// Each job's newest run is kept whatever its age: without it, a job that
|
|
68
|
+
// runs less often than the retention looks like it never ran.
|
|
69
|
+
prune: `DELETE FROM ${p}runs WHERE status <> 'running' AND started_at < ?
|
|
70
|
+
AND started_at < (SELECT MAX(r.started_at) FROM ${p}runs r WHERE r.job = ${p}runs.job)`
|
|
71
|
+
};
|
|
72
|
+
return sql;
|
|
73
|
+
}
|
|
74
|
+
function updateRunIfSql(dialect, p, count) {
|
|
75
|
+
const text = `UPDATE ${p}runs SET status = ?, finished_at = ?, duration_ms = ?, error = ?, output = ?, metrics = ? WHERE id = ? AND status IN (${Array.from({ length: count }, () => "?").join(", ")})`;
|
|
76
|
+
return text;
|
|
77
|
+
}
|
|
78
|
+
var params = {
|
|
79
|
+
upsertJob: (definition, now) => [definition.name, JSON.stringify(definition), now, now],
|
|
80
|
+
insertRun: (run) => [
|
|
81
|
+
run.id,
|
|
82
|
+
run.job,
|
|
83
|
+
run.status,
|
|
84
|
+
run.startedAt,
|
|
85
|
+
run.finishedAt,
|
|
86
|
+
run.durationMs,
|
|
87
|
+
run.error,
|
|
88
|
+
run.output,
|
|
89
|
+
JSON.stringify(run.metrics),
|
|
90
|
+
run.trigger
|
|
91
|
+
],
|
|
92
|
+
updateRun: (run) => [run.status, run.finishedAt, run.durationMs, run.error, run.output, JSON.stringify(run.metrics), run.id],
|
|
93
|
+
updateRunIf: (run, fromStatuses) => [
|
|
94
|
+
run.status,
|
|
95
|
+
run.finishedAt,
|
|
96
|
+
run.durationMs,
|
|
97
|
+
run.error,
|
|
98
|
+
run.output,
|
|
99
|
+
JSON.stringify(run.metrics),
|
|
100
|
+
run.id,
|
|
101
|
+
...fromStatuses
|
|
102
|
+
],
|
|
103
|
+
setState: (state) => [state.job, JSON.stringify(state)],
|
|
104
|
+
casInsert: (state) => [state.job, JSON.stringify(state)],
|
|
105
|
+
casUpdate: (state, expectedVersion) => [JSON.stringify(state), state.job, expectedVersion]
|
|
106
|
+
};
|
|
107
|
+
var json = (v) => typeof v === "string" ? JSON.parse(v) : v;
|
|
108
|
+
var num = (v) => v === null ? null : Number(v);
|
|
109
|
+
function rowToJob(r) {
|
|
110
|
+
return { name: r.name, definition: json(r.definition), createdAt: Number(r.created_at), updatedAt: Number(r.updated_at) };
|
|
111
|
+
}
|
|
112
|
+
function rowToRun(r) {
|
|
113
|
+
return {
|
|
114
|
+
id: r.id,
|
|
115
|
+
job: r.job,
|
|
116
|
+
status: r.status,
|
|
117
|
+
startedAt: Number(r.started_at),
|
|
118
|
+
finishedAt: num(r.finished_at),
|
|
119
|
+
durationMs: num(r.duration_ms),
|
|
120
|
+
error: r.error,
|
|
121
|
+
output: r.output,
|
|
122
|
+
metrics: r.metrics === null ? {} : json(r.metrics),
|
|
123
|
+
trigger: r.trigger
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function rowToState(r) {
|
|
127
|
+
return json(r.state);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/stores/d1.ts
|
|
131
|
+
var created = /* @__PURE__ */ new WeakMap();
|
|
132
|
+
function d1(database, options = {}) {
|
|
133
|
+
if (!database || typeof database.prepare !== "function" || typeof database.batch !== "function") {
|
|
134
|
+
throw new Error("cronwatch: d1() needs a D1 database binding, such as env.DB");
|
|
135
|
+
}
|
|
136
|
+
const p = tablePrefix(options.prefix);
|
|
137
|
+
if (p.startsWith("_cf_")) throw new Error(`cronwatch: invalid table prefix ${JSON.stringify(p)}. D1 reserves names starting with _cf_.`);
|
|
138
|
+
const sql = statements("sqlite", p);
|
|
139
|
+
const createTables = options.createTables ?? true;
|
|
140
|
+
const stmt = (text, values = []) => {
|
|
141
|
+
const s = database.prepare(text);
|
|
142
|
+
return values.length > 0 ? s.bind(...values) : s;
|
|
143
|
+
};
|
|
144
|
+
const first = async (text, values) => await stmt(text, values).first() ?? null;
|
|
145
|
+
const all = async (text, values = []) => (await stmt(text, values).all()).results ?? [];
|
|
146
|
+
const changes = async (text, values) => (await stmt(text, values).run()).meta?.changes ?? 0;
|
|
147
|
+
return {
|
|
148
|
+
async init() {
|
|
149
|
+
if (!createTables || created.get(database)?.has(p)) return;
|
|
150
|
+
const ddl = schema("sqlite", p).split(";").map((s) => s.trim()).filter(Boolean);
|
|
151
|
+
await database.batch(ddl.map((s) => database.prepare(s)));
|
|
152
|
+
let prefixes = created.get(database);
|
|
153
|
+
if (!prefixes) created.set(database, prefixes = /* @__PURE__ */ new Set());
|
|
154
|
+
prefixes.add(p);
|
|
155
|
+
},
|
|
156
|
+
async upsertJob(definition, now) {
|
|
157
|
+
await stmt(sql.upsertJob, params.upsertJob(definition, now)).run();
|
|
158
|
+
},
|
|
159
|
+
async getJob(name) {
|
|
160
|
+
const r = await first(sql.getJob, [name]);
|
|
161
|
+
return r ? rowToJob(r) : null;
|
|
162
|
+
},
|
|
163
|
+
async listJobs() {
|
|
164
|
+
return (await all(sql.listJobs)).map(rowToJob);
|
|
165
|
+
},
|
|
166
|
+
async deleteJob(name) {
|
|
167
|
+
await database.batch([stmt(sql.deleteRuns, [name]), stmt(sql.deleteState, [name]), stmt(sql.deleteJob, [name])]);
|
|
168
|
+
},
|
|
169
|
+
async insertRun(run) {
|
|
170
|
+
await stmt(sql.insertRun, params.insertRun(run)).run();
|
|
171
|
+
},
|
|
172
|
+
async updateRun(run) {
|
|
173
|
+
await stmt(sql.updateRun, params.updateRun(run)).run();
|
|
174
|
+
},
|
|
175
|
+
async updateRunIf(run, fromStatuses) {
|
|
176
|
+
if (fromStatuses.length === 0) return false;
|
|
177
|
+
return await changes(updateRunIfSql("sqlite", p, fromStatuses.length), params.updateRunIf(run, fromStatuses)) > 0;
|
|
178
|
+
},
|
|
179
|
+
async getRun(id) {
|
|
180
|
+
const r = await first(sql.getRun, [id]);
|
|
181
|
+
return r ? rowToRun(r) : null;
|
|
182
|
+
},
|
|
183
|
+
async listRuns(job, limit) {
|
|
184
|
+
return (await all(sql.listRuns, [job, limit])).map(rowToRun);
|
|
185
|
+
},
|
|
186
|
+
async lastRun(job) {
|
|
187
|
+
const r = await first(sql.listRuns, [job, 1]);
|
|
188
|
+
return r ? rowToRun(r) : null;
|
|
189
|
+
},
|
|
190
|
+
async runningRuns() {
|
|
191
|
+
return (await all(sql.runningRuns)).map(rowToRun);
|
|
192
|
+
},
|
|
193
|
+
async getState(job) {
|
|
194
|
+
const r = await first(sql.getState, [job]);
|
|
195
|
+
return r ? rowToState(r) : null;
|
|
196
|
+
},
|
|
197
|
+
async setState(state) {
|
|
198
|
+
await stmt(sql.setState, params.setState(state)).run();
|
|
199
|
+
},
|
|
200
|
+
async compareAndSetState(state, expectedVersion) {
|
|
201
|
+
const written = expectedVersion === 0 ? await changes(sql.casInsert, params.casInsert(state)) : await changes(sql.casUpdate, params.casUpdate(state, expectedVersion));
|
|
202
|
+
return written > 0;
|
|
203
|
+
},
|
|
204
|
+
async prune(before) {
|
|
205
|
+
return changes(sql.prune, [before]);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { d1 };
|
|
211
|
+
//# sourceMappingURL=d1.js.map
|
|
212
|
+
//# sourceMappingURL=d1.js.map
|
package/dist/d1.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/sql.ts","../src/stores/d1.ts"],"names":["json"],"mappings":"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","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, 0 when it has none.\n const version = (column: string) => (pg ? `COALESCE((${column}->>'version')::bigint, 0)` : `COALESCE(json_extract(${column}, '$.version'), 0)`);\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"]}
|
package/dist/datadog.cjs
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/alerts/shared.ts
|
|
4
|
+
var TIMEOUT_MS = 1e4;
|
|
5
|
+
function origin(url) {
|
|
6
|
+
try {
|
|
7
|
+
return new URL(url).origin;
|
|
8
|
+
} catch {
|
|
9
|
+
return "(invalid URL)";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
var ERROR_BODY_MAX = 200;
|
|
13
|
+
async function post(provider, url, init, secrets = []) {
|
|
14
|
+
const response = await fetch(url, { method: "POST", headers: init.headers, body: init.body, redirect: "error", signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
let text = "";
|
|
17
|
+
try {
|
|
18
|
+
text = await response.text();
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`${provider} ${origin(url)} answered ${response.status}${text ? `: ${errorBody(text, secrets)}` : ""}`);
|
|
22
|
+
}
|
|
23
|
+
return response;
|
|
24
|
+
}
|
|
25
|
+
function errorBody(text, secrets = []) {
|
|
26
|
+
const kept = secrets.filter((s) => typeof s === "string" && s.length >= 4);
|
|
27
|
+
const longest = kept.reduce((n, s) => Math.max(n, s.length), 0);
|
|
28
|
+
let head = cut(text, ERROR_BODY_MAX + longest);
|
|
29
|
+
for (const secret of kept) head = head.split(secret).join("[redacted]");
|
|
30
|
+
return cut(head, ERROR_BODY_MAX);
|
|
31
|
+
}
|
|
32
|
+
function trimmed(value) {
|
|
33
|
+
return typeof value === "string" ? value.trim() : "";
|
|
34
|
+
}
|
|
35
|
+
function hex(bytes) {
|
|
36
|
+
return Array.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
37
|
+
}
|
|
38
|
+
async function sha256Hex(text) {
|
|
39
|
+
return hex(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text)));
|
|
40
|
+
}
|
|
41
|
+
function cut(text, max) {
|
|
42
|
+
if (text.length <= max) return text;
|
|
43
|
+
let end = max;
|
|
44
|
+
const code = text.charCodeAt(end - 1);
|
|
45
|
+
if (code >= 55296 && code <= 56319) end -= 1;
|
|
46
|
+
return text.slice(0, end);
|
|
47
|
+
}
|
|
48
|
+
function plainText(alert, link) {
|
|
49
|
+
return [alert.title, "", alert.message, ...alert.triage ? ["", `Triage: ${alert.triage}`] : [], ...link ? ["", `Open: ${link}`] : []].join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/alerts/datadog.ts
|
|
53
|
+
var ALERT_TYPE = {
|
|
54
|
+
missed: "error",
|
|
55
|
+
failed: "error",
|
|
56
|
+
stuck: "error",
|
|
57
|
+
slow: "warning",
|
|
58
|
+
over_budget: "warning",
|
|
59
|
+
recovered: "success"
|
|
60
|
+
};
|
|
61
|
+
function datadog(options) {
|
|
62
|
+
const apiKey = trimmed(options.apiKey);
|
|
63
|
+
if (!apiKey) throw new Error("datadog() needs an apiKey");
|
|
64
|
+
const site = (options.site ?? "datadoghq.com").replace(/^https?:\/\//, "").replace(/^(api|app)\./, "").replace(/\/+$/, "");
|
|
65
|
+
if (!/^[a-z0-9.-]+$/i.test(site)) throw new Error("datadog() needs a site like datadoghq.com");
|
|
66
|
+
const url = `https://api.${site}/api/v1/events`;
|
|
67
|
+
return {
|
|
68
|
+
name: "datadog",
|
|
69
|
+
async send(alert) {
|
|
70
|
+
const link = options.link?.(alert);
|
|
71
|
+
const event = {
|
|
72
|
+
title: cut(alert.title, 500),
|
|
73
|
+
text: cut(plainText(alert, link), 4e3),
|
|
74
|
+
alert_type: ALERT_TYPE[alert.type],
|
|
75
|
+
aggregation_key: await aggregationKey(alert),
|
|
76
|
+
date_happened: Math.floor(alert.at / 1e3),
|
|
77
|
+
priority: "normal",
|
|
78
|
+
tags: ["cronwatch", `job:${alert.job}`, `alert:${alert.type}`, ...options.tags ?? []],
|
|
79
|
+
...options.host ? { host: options.host } : {}
|
|
80
|
+
};
|
|
81
|
+
await post("Datadog", url, {
|
|
82
|
+
headers: { "content-type": "application/json", accept: "application/json", "dd-api-key": apiKey },
|
|
83
|
+
body: JSON.stringify(event)
|
|
84
|
+
}, [apiKey]);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function aggregationKey(alert) {
|
|
89
|
+
const key = `cronwatch:${alert.job}:${alert.type}`;
|
|
90
|
+
return key.length <= 100 ? key : `cronwatch:${(await sha256Hex(key)).slice(0, 40)}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
exports.datadog = datadog;
|
|
94
|
+
//# sourceMappingURL=datadog.cjs.map
|
|
95
|
+
//# sourceMappingURL=datadog.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/alerts/shared.ts","../src/alerts/datadog.ts"],"names":[],"mappings":"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","file":"datadog.cjs","sourcesContent":["/**\n * Helpers shared by the provider channels. Only fetch and Web Crypto, so the\n * channels run on Node, Cloudflare Workers, Deno and Bun alike. Not an entry\n * point: each channel bundles its own copy.\n */\nimport type { Alert } from \"../types.js\";\n\nexport const TIMEOUT_MS = 10_000;\n\n/** Severity for trackers that have levels. Recovered is informational. */\nexport type Severity = \"error\" | \"warning\" | \"info\";\n\nexport function severity(type: Alert[\"type\"]): Severity {\n if (type === \"recovered\") return \"info\";\n if (type === \"slow\" || type === \"over_budget\") return \"warning\";\n return \"error\";\n}\n\n/** The scheme, host and port only. A URL's path or query can hold a credential. */\nexport function origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return \"(invalid URL)\";\n }\n}\n\n/** How much of a provider's error body goes into the error message. */\nexport const ERROR_BODY_MAX = 200;\n\n/**\n * POSTs and throws on a non-2xx answer. The error names the provider and the\n * URL's origin, plus the start of the response body with every secret the\n * channel holds cut out, in case a provider echoes one back. Secrets are cut\n * from a prefix long enough to hold one that starts inside the first\n * ERROR_BODY_MAX characters, and only then is it cut to that length, so no\n * part of a secret survives at the edge. A redirect is refused rather than\n * followed: the credential headers would go with it to wherever it points.\n */\nexport async function post(provider: string, url: string, init: { headers: Record<string, string>; body: string }, secrets: (string | undefined)[] = []): Promise<Response> {\n const response = await fetch(url, { method: \"POST\", headers: init.headers, body: init.body, redirect: \"error\", signal: AbortSignal.timeout(TIMEOUT_MS) });\n if (!response.ok) {\n let text = \"\";\n try {\n text = await response.text();\n } catch {\n // The status is enough.\n }\n throw new Error(`${provider} ${origin(url)} answered ${response.status}${text ? `: ${errorBody(text, secrets)}` : \"\"}`);\n }\n return response;\n}\n\n/** The start of an error body, secrets cut out first, then cut to ERROR_BODY_MAX on a code point. */\nexport function errorBody(text: string, secrets: (string | undefined)[] = []): string {\n const kept = secrets.filter((s): s is string => typeof s === \"string\" && s.length >= 4);\n const longest = kept.reduce((n, s) => Math.max(n, s.length), 0);\n let head = cut(text, ERROR_BODY_MAX + longest);\n for (const secret of kept) head = head.split(secret).join(\"[redacted]\");\n return cut(head, ERROR_BODY_MAX);\n}\n\n/** A credential with the spaces and newlines a paste leaves around it taken off. Anything not a string is empty. */\nexport function trimmed(value: unknown): string {\n return typeof value === \"string\" ? value.trim() : \"\";\n}\n\n/** Base64 of UTF-8, without Buffer. */\nexport function base64(text: string): string {\n let binary = \"\";\n for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\nexport function basicAuth(user: string, password: string): string {\n return `Basic ${base64(`${user}:${password}`)}`;\n}\n\nexport function hex(bytes: ArrayBuffer | Uint8Array): string {\n return Array.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport async function sha256Hex(text: string): Promise<string> {\n return hex(await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(text)));\n}\n\n/**\n * A stable 32 hex character id for one alert: the same job, type and time\n * always give the same id, so a provider that deduplicates on it drops a\n * resend of an alert it already took.\n */\nexport async function alertId(alert: Alert): Promise<string> {\n return (await sha256Hex(`${alert.job}\\n${alert.type}\\n${alert.at}`)).slice(0, 32);\n}\n\n/** The same id laid out as a UUID, for APIs that ask for one. */\nexport function asUuid(id: string): string {\n return `${id.slice(0, 8)}-${id.slice(8, 12)}-${id.slice(12, 16)}-${id.slice(16, 20)}-${id.slice(20, 32)}`;\n}\n\n/** Cuts to at most `max` UTF-16 units without splitting a surrogate pair. */\nexport function cut(text: string, max: number): string {\n if (text.length <= max) return text;\n let end = max;\n const code = text.charCodeAt(end - 1);\n if (code >= 0xd800 && code <= 0xdbff) end -= 1;\n return text.slice(0, end);\n}\n\n/** The run fields worth attaching to a tracker event. */\nexport function runSummary(alert: Alert): Record<string, unknown> | null {\n const run = alert.run;\n if (!run) return null;\n return { id: run.id, status: run.status, startedAt: new Date(run.startedAt).toISOString(), durationMs: run.durationMs, trigger: run.trigger };\n}\n\n/** Title, message, triage and link as one plain text block, the way every channel reads. */\nexport function plainText(alert: Alert, link: string | undefined): string {\n return [alert.title, \"\", alert.message, ...(alert.triage ? [\"\", `Triage: ${alert.triage}`] : []), ...(link ? [\"\", `Open: ${link}`] : [])].join(\"\\n\");\n}\n","/**\n * Datadog Events API v1.\n * API reference: https://docs.datadoghq.com/api/latest/events/ (Post an event)\n * Field limits: https://datadoghq.dev/datadog-api-client-typescript/classes/v1.EventCreateRequest.html\n * POST https://api.<site>/api/v1/events with DD-API-KEY. Answers 202.\n */\nimport type { Alert, AlertChannel } from \"../types.js\";\nimport { cut, plainText, post, sha256Hex, trimmed } from \"./shared.js\";\n\nexport interface DatadogOptions {\n /** An API key (not an application key). */\n apiKey: string;\n /** Your Datadog site: \"datadoghq.com\" (the default), \"datadoghq.eu\", \"us3.datadoghq.com\", \"us5.datadoghq.com\", \"ap1.datadoghq.com\", \"ddog-gov.com\". */\n site?: string;\n /** Extra tags, \"env:prod\" say. Every event also has cronwatch, job:<name> and alert:<type>. */\n tags?: string[];\n /** Associates the event with a host and its tags. */\n host?: string;\n link?: (alert: Alert) => string;\n}\n\nconst ALERT_TYPE: Record<Alert[\"type\"], \"error\" | \"warning\" | \"success\"> = {\n missed: \"error\",\n failed: \"error\",\n stuck: \"error\",\n slow: \"warning\",\n over_budget: \"warning\",\n recovered: \"success\",\n};\n\n/** Posts alerts to the Datadog event stream, aggregated per job and alert type. */\nexport function datadog(options: DatadogOptions): AlertChannel {\n // A pasted credential often carries a stray space or newline, which a header would refuse or send.\n const apiKey = trimmed(options.apiKey);\n if (!apiKey) throw new Error(\"datadog() needs an apiKey\");\n const site = (options.site ?? \"datadoghq.com\").replace(/^https?:\\/\\//, \"\").replace(/^(api|app)\\./, \"\").replace(/\\/+$/, \"\");\n if (!/^[a-z0-9.-]+$/i.test(site)) throw new Error(\"datadog() needs a site like datadoghq.com\");\n const url = `https://api.${site}/api/v1/events`;\n return {\n name: \"datadog\",\n async send(alert) {\n const link = options.link?.(alert);\n const event = {\n title: cut(alert.title, 500),\n text: cut(plainText(alert, link), 4000),\n alert_type: ALERT_TYPE[alert.type],\n aggregation_key: await aggregationKey(alert),\n date_happened: Math.floor(alert.at / 1000),\n priority: \"normal\",\n tags: [\"cronwatch\", `job:${alert.job}`, `alert:${alert.type}`, ...(options.tags ?? [])],\n ...(options.host ? { host: options.host } : {}),\n };\n await post(\"Datadog\", url, {\n headers: { \"content-type\": \"application/json\", accept: \"application/json\", \"dd-api-key\": apiKey },\n body: JSON.stringify(event),\n }, [apiKey]);\n },\n };\n}\n\n/** \"cronwatch:<job>:<type>\", or a hash of it when that passes Datadog's 100 characters. */\nasync function aggregationKey(alert: Alert): Promise<string> {\n const key = `cronwatch:${alert.job}:${alert.type}`;\n return key.length <= 100 ? key : `cronwatch:${(await sha256Hex(key)).slice(0, 40)}`;\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { A as Alert, a as AlertChannel } from './types-Ddq1MUPL.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Datadog Events API v1.
|
|
5
|
+
* API reference: https://docs.datadoghq.com/api/latest/events/ (Post an event)
|
|
6
|
+
* Field limits: https://datadoghq.dev/datadog-api-client-typescript/classes/v1.EventCreateRequest.html
|
|
7
|
+
* POST https://api.<site>/api/v1/events with DD-API-KEY. Answers 202.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface DatadogOptions {
|
|
11
|
+
/** An API key (not an application key). */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** Your Datadog site: "datadoghq.com" (the default), "datadoghq.eu", "us3.datadoghq.com", "us5.datadoghq.com", "ap1.datadoghq.com", "ddog-gov.com". */
|
|
14
|
+
site?: string;
|
|
15
|
+
/** Extra tags, "env:prod" say. Every event also has cronwatch, job:<name> and alert:<type>. */
|
|
16
|
+
tags?: string[];
|
|
17
|
+
/** Associates the event with a host and its tags. */
|
|
18
|
+
host?: string;
|
|
19
|
+
link?: (alert: Alert) => string;
|
|
20
|
+
}
|
|
21
|
+
/** Posts alerts to the Datadog event stream, aggregated per job and alert type. */
|
|
22
|
+
declare function datadog(options: DatadogOptions): AlertChannel;
|
|
23
|
+
|
|
24
|
+
export { type DatadogOptions, datadog };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { A as Alert, a as AlertChannel } from './types-Ddq1MUPL.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Datadog Events API v1.
|
|
5
|
+
* API reference: https://docs.datadoghq.com/api/latest/events/ (Post an event)
|
|
6
|
+
* Field limits: https://datadoghq.dev/datadog-api-client-typescript/classes/v1.EventCreateRequest.html
|
|
7
|
+
* POST https://api.<site>/api/v1/events with DD-API-KEY. Answers 202.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface DatadogOptions {
|
|
11
|
+
/** An API key (not an application key). */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** Your Datadog site: "datadoghq.com" (the default), "datadoghq.eu", "us3.datadoghq.com", "us5.datadoghq.com", "ap1.datadoghq.com", "ddog-gov.com". */
|
|
14
|
+
site?: string;
|
|
15
|
+
/** Extra tags, "env:prod" say. Every event also has cronwatch, job:<name> and alert:<type>. */
|
|
16
|
+
tags?: string[];
|
|
17
|
+
/** Associates the event with a host and its tags. */
|
|
18
|
+
host?: string;
|
|
19
|
+
link?: (alert: Alert) => string;
|
|
20
|
+
}
|
|
21
|
+
/** Posts alerts to the Datadog event stream, aggregated per job and alert type. */
|
|
22
|
+
declare function datadog(options: DatadogOptions): AlertChannel;
|
|
23
|
+
|
|
24
|
+
export { type DatadogOptions, datadog };
|