sermon-js 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +3 -2
  2. package/dist/api/index.cjs +5 -5
  3. package/dist/api/index.d.cts +4 -4
  4. package/dist/api/index.d.ts +4 -4
  5. package/dist/api/index.js +1 -1
  6. package/dist/{chunk-APC2HVSI.js → chunk-NUSAZZYR.js} +12 -6
  7. package/dist/chunk-NUSAZZYR.js.map +1 -0
  8. package/dist/{chunk-PARE6EUZ.cjs → chunk-NZTKXXW2.cjs} +7 -3
  9. package/dist/chunk-NZTKXXW2.cjs.map +1 -0
  10. package/dist/{chunk-2M4UBCDM.js → chunk-R7BDAQOM.js} +7 -3
  11. package/dist/chunk-R7BDAQOM.js.map +1 -0
  12. package/dist/{chunk-NP3YYVRZ.cjs → chunk-ZAZ6VTGV.cjs} +12 -5
  13. package/dist/chunk-ZAZ6VTGV.cjs.map +1 -0
  14. package/dist/{config-BEbP-uLf.d.cts → config-gro29syt.d.cts} +1 -1
  15. package/dist/{config-w1tm1bdu.d.ts → config-iY18j9IA.d.ts} +1 -1
  16. package/dist/{generic-lmrGxMDC.d.cts → generic-BUiaKpNp.d.cts} +2 -2
  17. package/dist/{generic-BJmlgtUM.d.ts → generic-CV2O4aNH.d.ts} +2 -2
  18. package/dist/index.cjs +29 -24
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +19 -10
  21. package/dist/index.d.ts +19 -10
  22. package/dist/index.js +7 -6
  23. package/dist/index.js.map +1 -1
  24. package/dist/presets/index.cjs +407 -47
  25. package/dist/presets/index.cjs.map +1 -1
  26. package/dist/presets/index.d.cts +173 -14
  27. package/dist/presets/index.d.ts +173 -14
  28. package/dist/presets/index.js +402 -48
  29. package/dist/presets/index.js.map +1 -1
  30. package/dist/react/heroui/index.cjs +27 -18
  31. package/dist/react/heroui/index.cjs.map +1 -1
  32. package/dist/react/heroui/index.d.cts +7 -2
  33. package/dist/react/heroui/index.d.ts +7 -2
  34. package/dist/react/heroui/index.js +13 -4
  35. package/dist/react/heroui/index.js.map +1 -1
  36. package/dist/react/index.d.cts +2 -2
  37. package/dist/react/index.d.ts +2 -2
  38. package/dist/{status-DsWzgb9Q.d.cts → status-BExBb08T.d.cts} +3 -3
  39. package/dist/{status-zd04ZQ98.d.ts → status-DmRhwvVC.d.ts} +3 -3
  40. package/dist/{store-_GuzSP0z.d.ts → store-BfKP7CjW.d.ts} +1 -1
  41. package/dist/{store-DybhDphS.d.cts → store-CAIB7Esp.d.cts} +1 -1
  42. package/dist/stores/mongodb.d.cts +2 -2
  43. package/dist/stores/mongodb.d.ts +2 -2
  44. package/dist/stores/redis.d.cts +2 -2
  45. package/dist/stores/redis.d.ts +2 -2
  46. package/dist/stores/sql.d.cts +2 -2
  47. package/dist/stores/sql.d.ts +2 -2
  48. package/dist/stores/upstash.d.cts +2 -2
  49. package/dist/stores/upstash.d.ts +2 -2
  50. package/dist/testing/index.cjs +3 -3
  51. package/dist/testing/index.d.cts +3 -3
  52. package/dist/testing/index.d.ts +3 -3
  53. package/dist/testing/index.js +1 -1
  54. package/dist/{tree-BXbXfjF1.d.cts → tree-J3riUyI9.d.cts} +9 -0
  55. package/dist/{tree-BXbXfjF1.d.ts → tree-J3riUyI9.d.ts} +9 -0
  56. package/dist/{types-CctvUAs2.d.cts → types-BVa6vIKk.d.cts} +9 -0
  57. package/dist/{types-CctvUAs2.d.ts → types-BVa6vIKk.d.ts} +9 -0
  58. package/package.json +1 -1
  59. package/dist/chunk-2M4UBCDM.js.map +0 -1
  60. package/dist/chunk-APC2HVSI.js.map +0 -1
  61. package/dist/chunk-NP3YYVRZ.cjs.map +0 -1
  62. package/dist/chunk-PARE6EUZ.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stores/memory.ts","../src/status.ts","../src/scheduler.ts"],"names":["resolveTree","toMs","resolveScoring","collectSecretValues","computeScore","computeServiceStats","redactText","runServices","RANGES","bucketRuns"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { CheckSample, LockResult, RunSummary, StatusStore } from \"../store\";\nimport type { CheckResult } from \"../types\";\n\n/**\n * In-memory store: the default when no store is given. History lives only as long as the\n * process, so use a persistent store on serverless platforms.\n */\nexport function memoryStore({ now = () => Date.now() }: { now?: () => number } = {}): StatusStore {\n let latest: Record<string, CheckResult> = {};\n let runs: RunSummary[] = [];\n const samples = new Map<string, CheckSample[]>();\n const locks = new Map<string, number>();\n const cache = new Map<string, { value: string; expiresAt: number }>();\n\n return {\n async saveRun(run) {\n for (const result of run.results) latest[result.key] = result;\n for (const sample of run.samples) {\n const list = samples.get(sample.key) ?? [];\n list.push(sample);\n samples.set(sample.key, list);\n }\n runs.push(run.summary);\n },\n\n async latest() {\n return { ...latest };\n },\n\n async runs({ since, until }) {\n return runs.filter((run) => run.at >= since && run.at <= until).sort((a, b) => a.at - b.at);\n },\n\n async samples(key, { since, until }) {\n return (samples.get(key) ?? [])\n .filter((sample) => sample.at >= since && sample.at <= until)\n .sort((a, b) => a.at - b.at);\n },\n\n async prune({ before, keepKeys }) {\n runs = runs.filter((run) => run.at >= before);\n for (const [key, list] of samples) {\n const kept = list.filter((sample) => sample.at >= before);\n if (kept.length) samples.set(key, kept);\n else samples.delete(key);\n }\n if (keepKeys) {\n const keep = new Set(keepKeys);\n latest = Object.fromEntries(Object.entries(latest).filter(([key]) => keep.has(key)));\n for (const key of [...samples.keys()]) if (!keep.has(key)) samples.delete(key);\n }\n },\n\n async lock(name, ttlMs): Promise<LockResult> {\n const current = now();\n const expiresAt = locks.get(name);\n if (expiresAt !== undefined && expiresAt > current) {\n return { ok: false, retryAfterMs: expiresAt - current };\n }\n locks.set(name, current + ttlMs);\n return { ok: true };\n },\n\n async unlock(name) {\n locks.delete(name);\n },\n\n cache: {\n async get(key) {\n const entry = cache.get(key);\n if (!entry) return null;\n if (entry.expiresAt <= now()) {\n cache.delete(key);\n return null;\n }\n return entry.value;\n },\n async set(key, value, ttlMs) {\n cache.set(key, { value, expiresAt: now() + ttlMs });\n },\n },\n };\n}\n","import { resolveTree, type ResolvedNode, type ResolvedService, type ServiceNode } from \"./config\";\nimport { toMs, type Duration } from \"./duration\";\nimport { bucketRuns, computeServiceStats, RANGES } from \"./history\";\nimport { runServices } from \"./runner\";\nimport { collectSecretValues, redactText } from \"./sanitize\";\nimport { computeScore, resolveScoring, type Scoring, type ScoringInput } from \"./score\";\nimport type { CheckSample, LockResult, RunSummary, StatusStore } from \"./store\";\nimport { memoryStore } from \"./stores/memory\";\nimport type {\n CheckCache,\n CheckResult,\n HistoryPayload,\n HistoryRange,\n ScoreLabel,\n ServiceStats,\n SnapshotNode,\n StatusSnapshot,\n Trigger,\n} from \"./types\";\n\nexport type StatusConfig = {\n /** What to supervise: services built with presets or `custom()`, optionally nested in `group()`s */\n services: ServiceNode[];\n /** Where history is kept. Defaults to memory, which lasts as long as the process. */\n store?: StatusStore;\n /** How often a full run is due (default \"5m\"). Used by `tick()`, the scheduler and staleness. */\n interval?: Duration;\n /** How long history is kept (default \"7d\") */\n retention?: Duration;\n scoring?: ScoringInput;\n /**\n * Never quote what a service replied in public errors: a failed HTTP answer is reported by\n * its status code only (\"HTTP 500\"). Turn it on when a checked service could answer with\n * something sensitive, such as a debug page. Default false. Errors returned by `custom()`\n * checks are yours and are kept (redacted) either way.\n */\n hideResponseBodies?: boolean;\n /** Receives configuration warnings (default: `console.warn`) */\n onWarning?: (message: string) => void;\n /** Override `fetch` (tests, custom agents) */\n fetch?: typeof fetch;\n /** Override the clock (tests) */\n now?: () => number;\n};\n\nexport type RunReport = {\n at: number;\n trigger: Trigger;\n /** True when every service was checked */\n full: boolean;\n score: number | null;\n label: ScoreLabel;\n results: CheckResult[];\n};\n\nexport type ServiceHistory = { schemaVersion: 1; key: string; range: HistoryRange; samples: CheckSample[] };\n\nexport type TickResult =\n | { ran: true; report: RunReport }\n | {\n ran: false;\n /** \"not-due\": the last full run is recent enough. \"locked\": another tick is running. */\n reason: \"not-due\" | \"locked\";\n /** When the next full run is due (ms epoch) */\n nextDueAt: number;\n };\n\nexport type Status = {\n /** Check every service (or only `keys`) now, and record the results */\n run(options?: { keys?: string[]; trigger?: Trigger }): Promise<RunReport>;\n /**\n * Run every service only if a full run is due (the last one is older than `interval`), and\n * never twice at once. Safe to call as often as you like, from any number of triggers:\n * a cron, a platform scheduler, a timer, a page view.\n */\n tick(options?: { force?: boolean }): Promise<TickResult>;\n /** Everything a status page needs: the tree with latest results, statistics and the score */\n snapshot(): Promise<StatusSnapshot>;\n /** Score history in fixed buckets */\n history(options?: { range?: HistoryRange }): Promise<HistoryPayload>;\n /** Raw samples of one service */\n serviceHistory(key: string, options?: { range?: HistoryRange }): Promise<ServiceHistory>;\n /**\n * Remove every configured secret (and anything shaped like a credential) from a text.\n * Use it before logging errors that come from checks, stores or drivers.\n */\n redact(text: string): string;\n /** Whether a service with this key exists */\n has(key: string): boolean;\n /** Take a named lock for `ttlMs` (the store's when it has one, otherwise in-process) */\n lock(name: string, ttlMs: number): Promise<LockResult>;\n /** When the last full run happened, if any within the retention window */\n lastFullRun(): Promise<{ at: number; trigger: Trigger } | null>;\n readonly services: ReadonlyMap<string, ResolvedService>;\n readonly tree: readonly ResolvedNode[];\n readonly store: StatusStore;\n readonly intervalMs: number;\n readonly retentionMs: number;\n readonly scoring: Scoring;\n readonly warnings: readonly string[];\n};\n\nconst TICK_LOCK = \"sermon:tick\";\nconst STATS_KEY = \"sermon:stats\";\nconst LAST_RUN_KEY = \"sermon:last-full-run\";\n\nfunction sampleOf(result: CheckResult): CheckSample {\n const healthy = result.status === \"up\" || result.status === \"sleeping\";\n return {\n key: result.key,\n at: result.checkedAt,\n status: result.status,\n latencyMs: result.latencyMs,\n httpStatus: result.httpStatus,\n error: healthy ? undefined : result.error,\n instances: result.instances?.length\n ? Object.fromEntries(\n result.instances.map((instance) => [instance.key, [instance.status, instance.latencyMs]])\n )\n : undefined,\n };\n}\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe what to supervise and get a status engine: run checks, read snapshots and history.\n * Nothing here depends on a host or a framework; see \"sermon-js/api\" to expose it over HTTP.\n */\nexport function defineStatus(config: StatusConfig): Status {\n const now = config.now ?? (() => Date.now());\n const resolved = resolveTree(config.services ?? []);\n const { services, tree } = resolved;\n const store = config.store ?? memoryStore({ now });\n const intervalMs = toMs(config.interval ?? \"5m\");\n const retentionMs = toMs(config.retention ?? \"7d\");\n const scoring = resolveScoring(config.scoring);\n const warn = config.onWarning ?? ((message: string) => console.warn(`[sermon-js] ${message}`));\n for (const warning of resolved.warnings) warn(warning);\n\n const secretValues = collectSecretValues(\n [...services.values()].flatMap((service) => [...Object.values(service.secrets), ...(service.sensitive ?? [])])\n );\n\n // Fallbacks for stores without a cache or a lock: fine for a single process\n const fallback = memoryStore({ now });\n const cache = store.cache ?? fallback.cache!;\n const lock = (name: string, ttlMs: number) =>\n store.lock ? store.lock(name, ttlMs) : fallback.lock!(name, ttlMs);\n const unlock = async (name: string) => {\n if (store.lock) await store.unlock?.(name);\n else await fallback.unlock!(name);\n };\n\n // A full run can't outlast its slowest check by much: that bounds how long the tick lock is held\n const longestCheckMs = Math.max(0, ...[...services.values()].map((service) => service.timeoutMs));\n const tickLockMs = longestCheckMs + 6_000;\n\n const checkCache = (key: string): CheckCache => ({\n get: (name) => cache.get(`sermon:check:${key}:${name}`),\n set: (name, value, ttlMs) => cache.set(`sermon:check:${key}:${name}`, value, ttlMs),\n });\n\n /** Latest results of the services that still exist; a service that lost its configuration drops its old result */\n async function currentLatest() {\n const stored = await store.latest();\n const latest: Record<string, CheckResult> = {};\n for (const [key, service] of services) {\n const result = stored[key];\n if (!result) continue;\n if (service.missing.length > 0 && result.status !== \"unconfigured\") continue;\n latest[key] = result;\n }\n return latest;\n }\n\n function scoreOf(latest: Record<string, CheckResult>) {\n return computeScore({\n scoring,\n results: [...services.values()]\n .filter((service) => latest[service.key])\n .map((service) => ({ status: latest[service.key]!.status, critical: service.critical })),\n });\n }\n\n async function readStats() {\n return parseJson<Record<string, ServiceStats>>(await cache.get(STATS_KEY)) ?? {};\n }\n\n /** Recompute the statistics of some services from their samples and cache them all together */\n async function refreshStats(keys: string[], known?: Record<string, ServiceStats>) {\n const at = now();\n const stats = known ?? (await readStats());\n await Promise.all(\n keys.map(async (key) => {\n const samples = await store.samples(key, { since: at - retentionMs, until: at });\n stats[key] = computeServiceStats({ samples, now: at });\n })\n );\n // Forget services that no longer exist\n for (const key of Object.keys(stats)) if (!services.has(key)) delete stats[key];\n await cache.set(STATS_KEY, JSON.stringify(stats), retentionMs);\n return stats;\n }\n\n async function lastFullRun() {\n const cached = parseJson<{ at: number; trigger: Trigger }>(await cache.get(LAST_RUN_KEY));\n if (cached) return cached;\n const at = now();\n const runs = await store.runs({ since: at - retentionMs, until: at });\n const last = [...runs].reverse().find((run) => run.full);\n return last ? { at: last.at, trigger: last.trigger } : null;\n }\n\n const status: Status = {\n services,\n tree,\n store,\n intervalMs,\n retentionMs,\n scoring,\n warnings: resolved.warnings,\n redact: (text) => redactText({ text, secretValues }),\n has: (key) => services.has(key),\n lock,\n lastFullRun,\n\n async run({ keys, trigger = \"manual\" } = {}) {\n const unknown = (keys ?? []).filter((key) => !services.has(key));\n if (unknown.length) throw new Error(`sermon-js: unknown service key(s): ${unknown.join(\", \")}`);\n const selected = keys ? keys.map((key) => services.get(key)!) : [...services.values()];\n const full = !keys;\n\n const results = await runServices({\n services: selected,\n secretValues,\n cacheFor: checkCache,\n now,\n fetch: config.fetch,\n hideResponseBodies: config.hideResponseBodies,\n });\n\n // The overall score always reflects the whole system: merge with what we already know\n const latest = full ? {} : await currentLatest();\n for (const result of results) latest[result.key] = result;\n const { score, label } = scoreOf(latest);\n\n const at = now();\n const failing: RunSummary[\"failing\"] = {};\n for (const result of results) {\n if (result.status === \"down\" || result.status === \"degraded\") failing[result.key] = result.status;\n }\n const summary: RunSummary = { at, trigger, full, score, label, failing };\n const samples = results.filter((result) => result.status !== \"unconfigured\").map(sampleOf);\n\n await store.saveRun({ summary, results, samples });\n if (full) {\n await store.prune({ before: at - retentionMs, keepKeys: [...services.keys()] });\n await cache.set(LAST_RUN_KEY, JSON.stringify({ at, trigger }), retentionMs);\n }\n await refreshStats(samples.map((sample) => sample.key));\n\n return { at, trigger, full, score, label, results };\n },\n\n async tick({ force = false } = {}) {\n const at = now();\n // Schedulers fire a little early or late: a run counts as due slightly before the interval ends\n const tolerance = Math.min(intervalMs * 0.1, 30_000);\n const last = await lastFullRun();\n if (!force && last && at - last.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: last.at + intervalMs };\n }\n // Only one run at a time. The lock expires by itself if a run dies, and is released as soon\n // as a run ends, so the \"due\" check above is what paces the runs.\n const held = await lock(TICK_LOCK, tickLockMs);\n if (!held.ok) {\n return { ran: false, reason: \"locked\", nextDueAt: at + held.retryAfterMs };\n }\n try {\n // Another instance may have finished a run between our check and taking the lock\n const latest = force ? null : await lastFullRun();\n if (latest && now() - latest.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: latest.at + intervalMs };\n }\n return { ran: true, report: await status.run({ trigger: \"scheduled\" }) };\n } finally {\n await unlock(TICK_LOCK).catch(() => undefined);\n }\n },\n\n async snapshot() {\n const at = now();\n const [latest, cachedStats, lastRun] = await Promise.all([currentLatest(), readStats(), lastFullRun()]);\n\n // Statistics are normally cached at run time; rebuild whatever is missing (cold cache)\n const missing = Object.values(latest)\n .filter((result) => result.status !== \"unconfigured\" && !cachedStats[result.key])\n .map((result) => result.key);\n const stats = missing.length ? await refreshStats(missing, cachedStats) : cachedStats;\n\n const toSnapshot = (node: ResolvedNode): SnapshotNode => {\n if (node.type === \"group\") {\n return {\n type: \"group\",\n key: node.key,\n title: node.title,\n description: node.description,\n children: node.children.map(toSnapshot),\n };\n }\n const service = services.get(node.key)!;\n const configured = service.missing.length === 0;\n return {\n type: \"service\",\n key: service.key,\n kind: service.kind,\n name: service.name,\n description: service.description,\n critical: service.critical,\n slowMs: service.slowMs,\n configured,\n missing: configured ? undefined : service.missing,\n result: latest[service.key],\n stats: configured ? stats[service.key] : undefined,\n };\n };\n\n const checkedAts = Object.values(latest).map((result) => result.checkedAt);\n return {\n schemaVersion: 1,\n generatedAt: at,\n lastCheckedAt: checkedAts.length ? Math.max(...checkedAts) : null,\n lastRunAt: lastRun?.at ?? null,\n lastRunTrigger: lastRun?.trigger ?? null,\n nextDueAt: lastRun ? lastRun.at + intervalMs : null,\n intervalMs,\n retentionMs,\n ...scoreOf(latest),\n tree: tree.map(toSnapshot),\n };\n },\n\n async history({ range = \"24h\" } = {}) {\n const at = now();\n const runs = await store.runs({ since: at - RANGES[range].durationMs, until: at });\n return {\n schemaVersion: 1,\n range,\n bucketMs: RANGES[range].bucketMs,\n bars: bucketRuns({ runs, range, now: at, scoring }),\n };\n },\n\n async serviceHistory(key, { range = \"24h\" } = {}) {\n if (!services.has(key)) throw new Error(`sermon-js: unknown service key: ${key}`);\n const at = now();\n const samples = await store.samples(key, { since: at - RANGES[range].durationMs, until: at });\n return { schemaVersion: 1, key, range, samples };\n },\n };\n\n return status;\n}\n","import { toMs, type Duration } from \"./duration\";\nimport type { Status, TickResult } from \"./status\";\n\nexport type SchedulerOptions = {\n /** How often to call `tick()` (default: the status `interval`) */\n every?: Duration;\n /** Run a first tick right away instead of waiting one period (default true) */\n immediate?: boolean;\n /** Random extra delay per period, as a share of it, so replicas don't all fire together (default 0.1) */\n jitter?: number;\n onTick?: (result: TickResult) => void;\n /** Called when a tick throws (e.g. the store is unreachable). The scheduler keeps going. */\n onError?: (error: unknown) => void;\n};\n\n/**\n * In-process scheduler for long-running servers (Node, Bun, Deno, containers).\n * It only calls `status.tick()`, which is idempotent, so several replicas sharing one store\n * still run the checks once per interval.\n *\n * On serverless platforms use the platform's scheduler, or the `/tick` endpoint, instead.\n */\nexport function startScheduler(status: Status, options: SchedulerOptions = {}): { stop: () => void } {\n const periodMs = toMs(options.every ?? status.intervalMs);\n const jitter = Math.max(0, options.jitter ?? 0.1);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const schedule = (delayMs: number) => {\n if (stopped) return;\n timer = setTimeout(run, delayMs);\n // Don't keep a Node process alive just for the scheduler\n (timer as { unref?: () => void }).unref?.();\n };\n\n const run = async () => {\n try {\n // Tick first: `onTick?.(await …)` would skip the call entirely when there is no listener\n const result = await status.tick();\n options.onTick?.(result);\n } catch (error) {\n options.onError?.(error);\n }\n schedule(periodMs + Math.random() * jitter * periodMs);\n };\n\n schedule(options.immediate === false ? periodMs : 0);\n\n return {\n stop() {\n stopped = true;\n clearTimeout(timer);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/stores/memory.ts","../src/status.ts","../src/scheduler.ts"],"names":["resolveTree","toMs","resolveScoring","collectSecretValues","computeScore","computeServiceStats","redactText","runServices","RANGES","bucketFor","bucketRuns"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { CheckSample, LockResult, RunSummary, StatusStore } from \"../store\";\nimport type { CheckResult } from \"../types\";\n\n/**\n * In-memory store: the default when no store is given. History lives only as long as the\n * process, so use a persistent store on serverless platforms.\n */\nexport function memoryStore({ now = () => Date.now() }: { now?: () => number } = {}): StatusStore {\n let latest: Record<string, CheckResult> = {};\n let runs: RunSummary[] = [];\n const samples = new Map<string, CheckSample[]>();\n const locks = new Map<string, number>();\n const cache = new Map<string, { value: string; expiresAt: number }>();\n\n return {\n async saveRun(run) {\n for (const result of run.results) latest[result.key] = result;\n for (const sample of run.samples) {\n const list = samples.get(sample.key) ?? [];\n list.push(sample);\n samples.set(sample.key, list);\n }\n runs.push(run.summary);\n },\n\n async latest() {\n return { ...latest };\n },\n\n async runs({ since, until }) {\n return runs.filter((run) => run.at >= since && run.at <= until).sort((a, b) => a.at - b.at);\n },\n\n async samples(key, { since, until }) {\n return (samples.get(key) ?? [])\n .filter((sample) => sample.at >= since && sample.at <= until)\n .sort((a, b) => a.at - b.at);\n },\n\n async prune({ before, keepKeys }) {\n runs = runs.filter((run) => run.at >= before);\n for (const [key, list] of samples) {\n const kept = list.filter((sample) => sample.at >= before);\n if (kept.length) samples.set(key, kept);\n else samples.delete(key);\n }\n if (keepKeys) {\n const keep = new Set(keepKeys);\n latest = Object.fromEntries(Object.entries(latest).filter(([key]) => keep.has(key)));\n for (const key of [...samples.keys()]) if (!keep.has(key)) samples.delete(key);\n }\n },\n\n async lock(name, ttlMs): Promise<LockResult> {\n const current = now();\n const expiresAt = locks.get(name);\n if (expiresAt !== undefined && expiresAt > current) {\n return { ok: false, retryAfterMs: expiresAt - current };\n }\n locks.set(name, current + ttlMs);\n return { ok: true };\n },\n\n async unlock(name) {\n locks.delete(name);\n },\n\n cache: {\n async get(key) {\n const entry = cache.get(key);\n if (!entry) return null;\n if (entry.expiresAt <= now()) {\n cache.delete(key);\n return null;\n }\n return entry.value;\n },\n async set(key, value, ttlMs) {\n cache.set(key, { value, expiresAt: now() + ttlMs });\n },\n },\n };\n}\n","import { resolveTree, type ResolvedNode, type ResolvedService, type ServiceNode } from \"./config\";\nimport { toMs, type Duration } from \"./duration\";\nimport { bucketFor, bucketRuns, computeServiceStats, RANGES } from \"./history\";\nimport { runServices } from \"./runner\";\nimport { collectSecretValues, redactText } from \"./sanitize\";\nimport { computeScore, resolveScoring, type Scoring, type ScoringInput } from \"./score\";\nimport type { CheckSample, LockResult, RunSummary, StatusStore } from \"./store\";\nimport { memoryStore } from \"./stores/memory\";\nimport type {\n CheckCache,\n CheckResult,\n HistoryPayload,\n HistoryRange,\n ScoreLabel,\n ServiceStats,\n SnapshotNode,\n StatusSnapshot,\n Trigger,\n} from \"./types\";\n\nexport type StatusConfig = {\n /** What to supervise: services built with presets or `custom()`, optionally nested in `group()`s */\n services: ServiceNode[];\n /** Where history is kept. Defaults to memory, which lasts as long as the process. */\n store?: StatusStore;\n /** How often a full run is due (default \"5m\"). Used by `tick()`, the scheduler and staleness. */\n interval?: Duration;\n /** How long history is kept (default \"7d\") */\n retention?: Duration;\n scoring?: ScoringInput;\n /**\n * Never quote what a service replied in public errors: a failed HTTP answer is reported by\n * its status code only (\"HTTP 500\"). Turn it on when a checked service could answer with\n * something sensitive, such as a debug page. Default false. Errors returned by `custom()`\n * checks are yours and are kept (redacted) either way.\n */\n hideResponseBodies?: boolean;\n /** Receives configuration warnings (default: `console.warn`) */\n onWarning?: (message: string) => void;\n /** Override `fetch` (tests, custom agents) */\n fetch?: typeof fetch;\n /** Override the clock (tests) */\n now?: () => number;\n};\n\nexport type RunReport = {\n at: number;\n trigger: Trigger;\n /** True when every service was checked */\n full: boolean;\n score: number | null;\n label: ScoreLabel;\n results: CheckResult[];\n};\n\nexport type ServiceHistory = { schemaVersion: 1; key: string; range: HistoryRange; samples: CheckSample[] };\n\nexport type TickResult =\n | { ran: true; report: RunReport }\n | {\n ran: false;\n /** \"not-due\": the last full run is recent enough. \"locked\": another tick is running. */\n reason: \"not-due\" | \"locked\";\n /** When the next full run is due (ms epoch) */\n nextDueAt: number;\n };\n\nexport type Status = {\n /** Check every service (or only `keys`) now, and record the results */\n run(options?: { keys?: string[]; trigger?: Trigger }): Promise<RunReport>;\n /**\n * Run every service only if a full run is due (the last one is older than `interval`), and\n * never twice at once. Safe to call as often as you like, from any number of triggers:\n * a cron, a platform scheduler, a timer, a page view.\n */\n tick(options?: { force?: boolean }): Promise<TickResult>;\n /** Everything a status page needs: the tree with latest results, statistics and the score */\n snapshot(): Promise<StatusSnapshot>;\n /** Score history in fixed buckets */\n history(options?: { range?: HistoryRange }): Promise<HistoryPayload>;\n /** Raw samples of one service */\n serviceHistory(key: string, options?: { range?: HistoryRange }): Promise<ServiceHistory>;\n /**\n * Remove every configured secret (and anything shaped like a credential) from a text.\n * Use it before logging errors that come from checks, stores or drivers.\n */\n redact(text: string): string;\n /** Whether a service with this key exists */\n has(key: string): boolean;\n /** Take a named lock for `ttlMs` (the store's when it has one, otherwise in-process) */\n lock(name: string, ttlMs: number): Promise<LockResult>;\n /** When the last full run happened, if any within the retention window */\n lastFullRun(): Promise<{ at: number; trigger: Trigger } | null>;\n readonly services: ReadonlyMap<string, ResolvedService>;\n readonly tree: readonly ResolvedNode[];\n readonly store: StatusStore;\n readonly intervalMs: number;\n readonly retentionMs: number;\n readonly scoring: Scoring;\n readonly warnings: readonly string[];\n};\n\nconst TICK_LOCK = \"sermon:tick\";\nconst STATS_KEY = \"sermon:stats\";\nconst LAST_RUN_KEY = \"sermon:last-full-run\";\n\nfunction sampleOf(result: CheckResult): CheckSample {\n const healthy = result.status === \"up\" || result.status === \"sleeping\";\n return {\n key: result.key,\n at: result.checkedAt,\n status: result.status,\n latencyMs: result.latencyMs,\n httpStatus: result.httpStatus,\n error: healthy ? undefined : result.error,\n instances: result.instances?.length\n ? Object.fromEntries(\n result.instances.map((instance) => [instance.key, [instance.status, instance.latencyMs]])\n )\n : undefined,\n };\n}\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe what to supervise and get a status engine: run checks, read snapshots and history.\n * Nothing here depends on a host or a framework; see \"sermon-js/api\" to expose it over HTTP.\n */\nexport function defineStatus(config: StatusConfig): Status {\n const now = config.now ?? (() => Date.now());\n const resolved = resolveTree(config.services ?? []);\n const { services, tree } = resolved;\n const store = config.store ?? memoryStore({ now });\n const intervalMs = toMs(config.interval ?? \"5m\");\n const retentionMs = toMs(config.retention ?? \"7d\");\n const scoring = resolveScoring(config.scoring);\n const warn = config.onWarning ?? ((message: string) => console.warn(`[sermon-js] ${message}`));\n for (const warning of resolved.warnings) warn(warning);\n\n const secretValues = collectSecretValues(\n [...services.values()].flatMap((service) => [...Object.values(service.secrets), ...(service.sensitive ?? [])])\n );\n\n // Fallbacks for stores without a cache or a lock: fine for a single process\n const fallback = memoryStore({ now });\n const cache = store.cache ?? fallback.cache!;\n const lock = (name: string, ttlMs: number) =>\n store.lock ? store.lock(name, ttlMs) : fallback.lock!(name, ttlMs);\n const unlock = async (name: string) => {\n if (store.lock) await store.unlock?.(name);\n else await fallback.unlock!(name);\n };\n\n // A full run can't outlast its slowest check by much: that bounds how long the tick lock is held\n const longestCheckMs = Math.max(0, ...[...services.values()].map((service) => service.timeoutMs));\n const tickLockMs = longestCheckMs + 6_000;\n\n const checkCache = (key: string): CheckCache => ({\n get: (name) => cache.get(`sermon:check:${key}:${name}`),\n set: (name, value, ttlMs) => cache.set(`sermon:check:${key}:${name}`, value, ttlMs),\n });\n\n /** Latest results of the services that still exist; a service that lost its configuration drops its old result */\n async function currentLatest() {\n const stored = await store.latest();\n const latest: Record<string, CheckResult> = {};\n for (const [key, service] of services) {\n const result = stored[key];\n if (!result) continue;\n if (service.missing.length > 0 && result.status !== \"unconfigured\") continue;\n latest[key] = result;\n }\n return latest;\n }\n\n function scoreOf(latest: Record<string, CheckResult>) {\n return computeScore({\n scoring,\n results: [...services.values()]\n .filter((service) => latest[service.key])\n .map((service) => ({ status: latest[service.key]!.status, critical: service.critical })),\n });\n }\n\n async function readStats() {\n return parseJson<Record<string, ServiceStats>>(await cache.get(STATS_KEY)) ?? {};\n }\n\n /** Recompute the statistics of some services from their samples and cache them all together */\n async function refreshStats(keys: string[], known?: Record<string, ServiceStats>) {\n const at = now();\n const stats = known ?? (await readStats());\n await Promise.all(\n keys.map(async (key) => {\n const samples = await store.samples(key, { since: at - retentionMs, until: at });\n stats[key] = computeServiceStats({ samples, now: at });\n })\n );\n // Forget services that no longer exist\n for (const key of Object.keys(stats)) if (!services.has(key)) delete stats[key];\n await cache.set(STATS_KEY, JSON.stringify(stats), retentionMs);\n return stats;\n }\n\n async function lastFullRun() {\n const cached = parseJson<{ at: number; trigger: Trigger }>(await cache.get(LAST_RUN_KEY));\n if (cached) return cached;\n const at = now();\n const runs = await store.runs({ since: at - retentionMs, until: at });\n const last = [...runs].reverse().find((run) => run.full);\n return last ? { at: last.at, trigger: last.trigger } : null;\n }\n\n const status: Status = {\n services,\n tree,\n store,\n intervalMs,\n retentionMs,\n scoring,\n warnings: resolved.warnings,\n redact: (text) => redactText({ text, secretValues }),\n has: (key) => services.has(key),\n lock,\n lastFullRun,\n\n async run({ keys, trigger = \"manual\" } = {}) {\n const unknown = (keys ?? []).filter((key) => !services.has(key));\n if (unknown.length) throw new Error(`sermon-js: unknown service key(s): ${unknown.join(\", \")}`);\n const selected = keys ? keys.map((key) => services.get(key)!) : [...services.values()];\n const full = !keys;\n\n const results = await runServices({\n services: selected,\n secretValues,\n cacheFor: checkCache,\n now,\n fetch: config.fetch,\n hideResponseBodies: config.hideResponseBodies,\n });\n\n // The overall score always reflects the whole system: merge with what we already know\n const latest = full ? {} : await currentLatest();\n for (const result of results) latest[result.key] = result;\n const { score, label } = scoreOf(latest);\n\n const at = now();\n const failing: RunSummary[\"failing\"] = {};\n for (const result of results) {\n if (result.status === \"down\" || result.status === \"degraded\") failing[result.key] = result.status;\n }\n const summary: RunSummary = { at, trigger, full, score, label, failing };\n const samples = results.filter((result) => result.status !== \"unconfigured\").map(sampleOf);\n\n await store.saveRun({ summary, results, samples });\n if (full) {\n await store.prune({ before: at - retentionMs, keepKeys: [...services.keys()] });\n await cache.set(LAST_RUN_KEY, JSON.stringify({ at, trigger }), retentionMs);\n }\n await refreshStats(samples.map((sample) => sample.key));\n\n return { at, trigger, full, score, label, results };\n },\n\n async tick({ force = false } = {}) {\n const at = now();\n // Schedulers fire a little early or late: a run counts as due slightly before the interval ends\n const tolerance = Math.min(intervalMs * 0.1, 30_000);\n const last = await lastFullRun();\n if (!force && last && at - last.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: last.at + intervalMs };\n }\n // Only one run at a time. The lock expires by itself if a run dies, and is released as soon\n // as a run ends, so the \"due\" check above is what paces the runs.\n const held = await lock(TICK_LOCK, tickLockMs);\n if (!held.ok) {\n return { ran: false, reason: \"locked\", nextDueAt: at + held.retryAfterMs };\n }\n try {\n // Another instance may have finished a run between our check and taking the lock\n const latest = force ? null : await lastFullRun();\n if (latest && now() - latest.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: latest.at + intervalMs };\n }\n return { ran: true, report: await status.run({ trigger: \"scheduled\" }) };\n } finally {\n await unlock(TICK_LOCK).catch(() => undefined);\n }\n },\n\n async snapshot() {\n const at = now();\n const [latest, cachedStats, lastRun] = await Promise.all([currentLatest(), readStats(), lastFullRun()]);\n\n // Statistics are normally cached at run time; rebuild whatever is missing (cold cache)\n const missing = Object.values(latest)\n .filter((result) => result.status !== \"unconfigured\" && !cachedStats[result.key])\n .map((result) => result.key);\n const stats = missing.length ? await refreshStats(missing, cachedStats) : cachedStats;\n\n const toSnapshot = (node: ResolvedNode): SnapshotNode => {\n if (node.type === \"group\") {\n return {\n type: \"group\",\n key: node.key,\n title: node.title,\n description: node.description,\n children: node.children.map(toSnapshot),\n };\n }\n const service = services.get(node.key)!;\n const configured = service.missing.length === 0;\n return {\n type: \"service\",\n key: service.key,\n kind: service.kind,\n name: service.name,\n description: service.description,\n critical: service.critical,\n slowMs: service.slowMs,\n configured,\n missing: configured ? undefined : service.missing,\n result: latest[service.key],\n stats: configured ? stats[service.key] : undefined,\n };\n };\n\n const checkedAts = Object.values(latest).map((result) => result.checkedAt);\n return {\n schemaVersion: 1,\n generatedAt: at,\n lastCheckedAt: checkedAts.length ? Math.max(...checkedAts) : null,\n lastRunAt: lastRun?.at ?? null,\n lastRunTrigger: lastRun?.trigger ?? null,\n nextDueAt: lastRun ? lastRun.at + intervalMs : null,\n intervalMs,\n retentionMs,\n ...scoreOf(latest),\n tree: tree.map(toSnapshot),\n };\n },\n\n async history({ range = \"24h\" } = {}) {\n const at = now();\n const runs = await store.runs({ since: at - RANGES[range].durationMs, until: at });\n // One bar per scheduled run: bars as wide as the interval\n const bucketMs = bucketFor(range, intervalMs);\n return {\n schemaVersion: 1,\n range,\n bucketMs,\n bars: bucketRuns({ runs, range, now: at, scoring, bucketMs }),\n };\n },\n\n async serviceHistory(key, { range = \"24h\" } = {}) {\n if (!services.has(key)) throw new Error(`sermon-js: unknown service key: ${key}`);\n const at = now();\n const samples = await store.samples(key, { since: at - RANGES[range].durationMs, until: at });\n return { schemaVersion: 1, key, range, samples };\n },\n };\n\n return status;\n}\n","import { toMs, type Duration } from \"./duration\";\nimport type { Status, TickResult } from \"./status\";\n\nexport type SchedulerOptions = {\n /** How often to call `tick()` (default: the status `interval`) */\n every?: Duration;\n /** Run a first tick right away instead of waiting one period (default true) */\n immediate?: boolean;\n /** Random extra delay per period, as a share of it, so replicas don't all fire together (default 0.1) */\n jitter?: number;\n onTick?: (result: TickResult) => void;\n /** Called when a tick throws (e.g. the store is unreachable). The scheduler keeps going. */\n onError?: (error: unknown) => void;\n};\n\n/**\n * In-process scheduler for long-running servers (Node, Bun, Deno, containers).\n * It only calls `status.tick()`, which is idempotent, so several replicas sharing one store\n * still run the checks once per interval.\n *\n * On serverless platforms use the platform's scheduler, or the `/tick` endpoint, instead.\n */\nexport function startScheduler(status: Status, options: SchedulerOptions = {}): { stop: () => void } {\n const periodMs = toMs(options.every ?? status.intervalMs);\n const jitter = Math.max(0, options.jitter ?? 0.1);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const schedule = (delayMs: number) => {\n if (stopped) return;\n timer = setTimeout(run, delayMs);\n // Don't keep a Node process alive just for the scheduler\n (timer as { unref?: () => void }).unref?.();\n };\n\n const run = async () => {\n try {\n // Tick first: `onTick?.(await …)` would skip the call entirely when there is no listener\n const result = await status.tick();\n options.onTick?.(result);\n } catch (error) {\n options.onError?.(error);\n }\n schedule(periodMs + Math.random() * jitter * periodMs);\n };\n\n schedule(options.immediate === false ? periodMs : 0);\n\n return {\n stop() {\n stopped = true;\n clearTimeout(timer);\n },\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,11 +1,11 @@
1
- import { D as Duration, T as TickResult, S as Status, a as Scoring } from './status-DsWzgb9Q.cjs';
2
- export { b as DEFAULT_SCORING, R as RunReport, c as ScoringInput, d as ServiceHistory, e as StatusConfig, f as computeScore, g as defineStatus, l as labelForScore, t as toMs } from './status-DsWzgb9Q.cjs';
3
- export { C as CustomOptions, D as DEFAULT_SLOW_MS, a as DEFAULT_TIMEOUT_MS, G as GroupDefinition, P as PresetContext, b as PresetOptions, c as PresetSpec, R as ResolvedNode, d as ResolvedService, S as ServiceDefinition, e as ServiceNode, f as custom, g as definePreset, h as group } from './config-BEbP-uLf.cjs';
4
- export { G as GenericHttpOptions, g as generic } from './generic-lmrGxMDC.cjs';
5
- import { S as StatusStore, R as RunSummary, C as CheckSample } from './store-DybhDphS.cjs';
6
- export { L as LockResult, T as TimeRange } from './store-DybhDphS.cjs';
7
- import { H as HistoryRange, a as HistoryBar, S as ServiceStats, C as CheckOutcome, b as HttpInit, c as HttpResult, d as CheckReturn } from './types-CctvUAs2.cjs';
8
- export { e as CheckCache, f as CheckContext, g as CheckResult, h as CommonOptions, G as GroupSnapshot, i as HistoryPayload, I as Instance, j as InstanceStats, M as Metrics, O as OutcomeStatus, P as ProbeHelpers, k as ScoreLabel, l as SecretsInput, m as ServiceSnapshot, n as SnapshotNode, o as Status, p as StatusSnapshot, T as Trigger, U as Usage } from './types-CctvUAs2.cjs';
1
+ import { D as Duration, T as TickResult, S as Status, a as Scoring } from './status-BExBb08T.cjs';
2
+ export { b as DEFAULT_SCORING, R as RunReport, c as ScoringInput, d as ServiceHistory, e as StatusConfig, f as computeScore, g as defineStatus, l as labelForScore, t as toMs } from './status-BExBb08T.cjs';
3
+ export { C as CustomOptions, D as DEFAULT_SLOW_MS, a as DEFAULT_TIMEOUT_MS, G as GroupDefinition, P as PresetContext, b as PresetOptions, c as PresetSpec, R as ResolvedNode, d as ResolvedService, S as ServiceDefinition, e as ServiceNode, f as custom, g as definePreset, h as group } from './config-gro29syt.cjs';
4
+ export { G as GenericHttpOptions, g as generic } from './generic-BUiaKpNp.cjs';
5
+ import { S as StatusStore, R as RunSummary, C as CheckSample } from './store-CAIB7Esp.cjs';
6
+ export { L as LockResult, T as TimeRange } from './store-CAIB7Esp.cjs';
7
+ import { H as HistoryRange, a as HistoryBar, S as ServiceStats, C as CheckOutcome, b as HttpInit, c as HttpResult, d as CheckReturn } from './types-BVa6vIKk.cjs';
8
+ export { e as CheckCache, f as CheckContext, g as CheckResult, h as CommonOptions, G as GroupSnapshot, i as HistoryPayload, I as Instance, j as InstanceStats, M as Metrics, O as OutcomeStatus, P as ProbeHelpers, k as ScoreLabel, l as SecretsInput, m as ServiceSnapshot, n as SnapshotNode, o as Status, p as StatusSnapshot, T as Trigger, U as Usage } from './types-BVa6vIKk.cjs';
9
9
 
10
10
  type SchedulerOptions = {
11
11
  /** How often to call `tick()` (default: the status `interval`) */
@@ -37,10 +37,17 @@ declare function memoryStore({ now }?: {
37
37
  now?: () => number;
38
38
  }): StatusStore;
39
39
 
40
+ /** How far each range looks back, and its smallest bucket */
40
41
  declare const RANGES: Record<HistoryRange, {
41
42
  durationMs: number;
42
43
  bucketMs: number;
43
44
  }>;
45
+ /**
46
+ * The width of one history bar: the check interval, so every bar holds one scheduled run
47
+ * (a 15-minute interval gives 15-minute bars), and never less than the range's smallest
48
+ * bucket, nor more than the range itself.
49
+ */
50
+ declare function bucketFor(range: HistoryRange, intervalMs?: number): number;
44
51
  /** Uptime, latency percentiles, sparkline series and last failure of one service */
45
52
  declare function computeServiceStats({ samples, now, }: {
46
53
  /** Samples of the whole retention window, oldest first */
@@ -48,11 +55,13 @@ declare function computeServiceStats({ samples, now, }: {
48
55
  now?: number;
49
56
  }): ServiceStats;
50
57
  /** Group runs into fixed buckets; each bucket keeps its worst score, label and failures */
51
- declare function bucketRuns({ runs, range, now, scoring, }: {
58
+ declare function bucketRuns({ runs, range, now, scoring, bucketMs, }: {
52
59
  runs: RunSummary[];
53
60
  range: HistoryRange;
54
61
  now?: number;
55
62
  scoring?: Scoring;
63
+ /** Width of one bar (default: the range's smallest bucket). See `bucketFor`. */
64
+ bucketMs?: number;
56
65
  }): HistoryBar[];
57
66
 
58
67
  /**
@@ -90,4 +99,4 @@ declare function measure(fn: () => Promise<CheckReturn>): Promise<CheckOutcome>;
90
99
  */
91
100
  declare function combine(outcomes: CheckOutcome[]): CheckOutcome;
92
101
 
93
- export { CheckOutcome, CheckReturn, CheckSample, Duration, HistoryBar, HistoryRange, HttpInit, HttpResult, RANGES, RunSummary, type SchedulerOptions, Scoring, ServiceStats, Status as StatusEngine, StatusStore, TickResult, bucketRuns, collectSecretValues, combine, computeServiceStats, createHttp, errorMessage, measure, memoryStore, sanitizeError, startScheduler };
102
+ export { CheckOutcome, CheckReturn, CheckSample, Duration, HistoryBar, HistoryRange, HttpInit, HttpResult, RANGES, RunSummary, type SchedulerOptions, Scoring, ServiceStats, Status as StatusEngine, StatusStore, TickResult, bucketFor, bucketRuns, collectSecretValues, combine, computeServiceStats, createHttp, errorMessage, measure, memoryStore, sanitizeError, startScheduler };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { D as Duration, T as TickResult, S as Status, a as Scoring } from './status-zd04ZQ98.js';
2
- export { b as DEFAULT_SCORING, R as RunReport, c as ScoringInput, d as ServiceHistory, e as StatusConfig, f as computeScore, g as defineStatus, l as labelForScore, t as toMs } from './status-zd04ZQ98.js';
3
- export { C as CustomOptions, D as DEFAULT_SLOW_MS, a as DEFAULT_TIMEOUT_MS, G as GroupDefinition, P as PresetContext, b as PresetOptions, c as PresetSpec, R as ResolvedNode, d as ResolvedService, S as ServiceDefinition, e as ServiceNode, f as custom, g as definePreset, h as group } from './config-w1tm1bdu.js';
4
- export { G as GenericHttpOptions, g as generic } from './generic-BJmlgtUM.js';
5
- import { S as StatusStore, R as RunSummary, C as CheckSample } from './store-_GuzSP0z.js';
6
- export { L as LockResult, T as TimeRange } from './store-_GuzSP0z.js';
7
- import { H as HistoryRange, a as HistoryBar, S as ServiceStats, C as CheckOutcome, b as HttpInit, c as HttpResult, d as CheckReturn } from './types-CctvUAs2.js';
8
- export { e as CheckCache, f as CheckContext, g as CheckResult, h as CommonOptions, G as GroupSnapshot, i as HistoryPayload, I as Instance, j as InstanceStats, M as Metrics, O as OutcomeStatus, P as ProbeHelpers, k as ScoreLabel, l as SecretsInput, m as ServiceSnapshot, n as SnapshotNode, o as Status, p as StatusSnapshot, T as Trigger, U as Usage } from './types-CctvUAs2.js';
1
+ import { D as Duration, T as TickResult, S as Status, a as Scoring } from './status-DmRhwvVC.js';
2
+ export { b as DEFAULT_SCORING, R as RunReport, c as ScoringInput, d as ServiceHistory, e as StatusConfig, f as computeScore, g as defineStatus, l as labelForScore, t as toMs } from './status-DmRhwvVC.js';
3
+ export { C as CustomOptions, D as DEFAULT_SLOW_MS, a as DEFAULT_TIMEOUT_MS, G as GroupDefinition, P as PresetContext, b as PresetOptions, c as PresetSpec, R as ResolvedNode, d as ResolvedService, S as ServiceDefinition, e as ServiceNode, f as custom, g as definePreset, h as group } from './config-iY18j9IA.js';
4
+ export { G as GenericHttpOptions, g as generic } from './generic-CV2O4aNH.js';
5
+ import { S as StatusStore, R as RunSummary, C as CheckSample } from './store-BfKP7CjW.js';
6
+ export { L as LockResult, T as TimeRange } from './store-BfKP7CjW.js';
7
+ import { H as HistoryRange, a as HistoryBar, S as ServiceStats, C as CheckOutcome, b as HttpInit, c as HttpResult, d as CheckReturn } from './types-BVa6vIKk.js';
8
+ export { e as CheckCache, f as CheckContext, g as CheckResult, h as CommonOptions, G as GroupSnapshot, i as HistoryPayload, I as Instance, j as InstanceStats, M as Metrics, O as OutcomeStatus, P as ProbeHelpers, k as ScoreLabel, l as SecretsInput, m as ServiceSnapshot, n as SnapshotNode, o as Status, p as StatusSnapshot, T as Trigger, U as Usage } from './types-BVa6vIKk.js';
9
9
 
10
10
  type SchedulerOptions = {
11
11
  /** How often to call `tick()` (default: the status `interval`) */
@@ -37,10 +37,17 @@ declare function memoryStore({ now }?: {
37
37
  now?: () => number;
38
38
  }): StatusStore;
39
39
 
40
+ /** How far each range looks back, and its smallest bucket */
40
41
  declare const RANGES: Record<HistoryRange, {
41
42
  durationMs: number;
42
43
  bucketMs: number;
43
44
  }>;
45
+ /**
46
+ * The width of one history bar: the check interval, so every bar holds one scheduled run
47
+ * (a 15-minute interval gives 15-minute bars), and never less than the range's smallest
48
+ * bucket, nor more than the range itself.
49
+ */
50
+ declare function bucketFor(range: HistoryRange, intervalMs?: number): number;
44
51
  /** Uptime, latency percentiles, sparkline series and last failure of one service */
45
52
  declare function computeServiceStats({ samples, now, }: {
46
53
  /** Samples of the whole retention window, oldest first */
@@ -48,11 +55,13 @@ declare function computeServiceStats({ samples, now, }: {
48
55
  now?: number;
49
56
  }): ServiceStats;
50
57
  /** Group runs into fixed buckets; each bucket keeps its worst score, label and failures */
51
- declare function bucketRuns({ runs, range, now, scoring, }: {
58
+ declare function bucketRuns({ runs, range, now, scoring, bucketMs, }: {
52
59
  runs: RunSummary[];
53
60
  range: HistoryRange;
54
61
  now?: number;
55
62
  scoring?: Scoring;
63
+ /** Width of one bar (default: the range's smallest bucket). See `bucketFor`. */
64
+ bucketMs?: number;
56
65
  }): HistoryBar[];
57
66
 
58
67
  /**
@@ -90,4 +99,4 @@ declare function measure(fn: () => Promise<CheckReturn>): Promise<CheckOutcome>;
90
99
  */
91
100
  declare function combine(outcomes: CheckOutcome[]): CheckOutcome;
92
101
 
93
- export { CheckOutcome, CheckReturn, CheckSample, Duration, HistoryBar, HistoryRange, HttpInit, HttpResult, RANGES, RunSummary, type SchedulerOptions, Scoring, ServiceStats, Status as StatusEngine, StatusStore, TickResult, bucketRuns, collectSecretValues, combine, computeServiceStats, createHttp, errorMessage, measure, memoryStore, sanitizeError, startScheduler };
102
+ export { CheckOutcome, CheckReturn, CheckSample, Duration, HistoryBar, HistoryRange, HttpInit, HttpResult, RANGES, RunSummary, type SchedulerOptions, Scoring, ServiceStats, Status as StatusEngine, StatusStore, TickResult, bucketFor, bucketRuns, collectSecretValues, combine, computeServiceStats, createHttp, errorMessage, measure, memoryStore, sanitizeError, startScheduler };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { toMs, resolveScoring, RANGES, bucketRuns, computeServiceStats, computeScore } from './chunk-APC2HVSI.js';
2
- export { DEFAULT_SCORING, RANGES, bucketRuns, computeScore, computeServiceStats, labelForScore, toMs } from './chunk-APC2HVSI.js';
1
+ import { toMs, resolveScoring, RANGES, bucketFor, bucketRuns, computeServiceStats, computeScore } from './chunk-NUSAZZYR.js';
2
+ export { DEFAULT_SCORING, RANGES, bucketFor, bucketRuns, computeScore, computeServiceStats, labelForScore, toMs } from './chunk-NUSAZZYR.js';
3
3
  export { generic } from './chunk-I3346MUL.js';
4
- import { collectSecretValues, runServices, redactText } from './chunk-2M4UBCDM.js';
5
- export { collectSecretValues, sanitizeError } from './chunk-2M4UBCDM.js';
4
+ import { collectSecretValues, runServices, redactText } from './chunk-R7BDAQOM.js';
5
+ export { collectSecretValues, sanitizeError } from './chunk-R7BDAQOM.js';
6
6
  import { resolveTree } from './chunk-XPXIDJUU.js';
7
7
  export { DEFAULT_SLOW_MS, DEFAULT_TIMEOUT_MS, combine, createHttp, custom, definePreset, errorMessage, group, measure } from './chunk-XPXIDJUU.js';
8
8
 
@@ -279,11 +279,12 @@ function defineStatus(config) {
279
279
  async history({ range = "24h" } = {}) {
280
280
  const at = now();
281
281
  const runs = await store.runs({ since: at - RANGES[range].durationMs, until: at });
282
+ const bucketMs = bucketFor(range, intervalMs);
282
283
  return {
283
284
  schemaVersion: 1,
284
285
  range,
285
- bucketMs: RANGES[range].bucketMs,
286
- bars: bucketRuns({ runs, range, now: at, scoring })
286
+ bucketMs,
287
+ bars: bucketRuns({ runs, range, now: at, scoring, bucketMs })
287
288
  };
288
289
  },
289
290
  async serviceHistory(key, { range = "24h" } = {}) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stores/memory.ts","../src/status.ts","../src/scheduler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { CheckSample, LockResult, RunSummary, StatusStore } from \"../store\";\nimport type { CheckResult } from \"../types\";\n\n/**\n * In-memory store: the default when no store is given. History lives only as long as the\n * process, so use a persistent store on serverless platforms.\n */\nexport function memoryStore({ now = () => Date.now() }: { now?: () => number } = {}): StatusStore {\n let latest: Record<string, CheckResult> = {};\n let runs: RunSummary[] = [];\n const samples = new Map<string, CheckSample[]>();\n const locks = new Map<string, number>();\n const cache = new Map<string, { value: string; expiresAt: number }>();\n\n return {\n async saveRun(run) {\n for (const result of run.results) latest[result.key] = result;\n for (const sample of run.samples) {\n const list = samples.get(sample.key) ?? [];\n list.push(sample);\n samples.set(sample.key, list);\n }\n runs.push(run.summary);\n },\n\n async latest() {\n return { ...latest };\n },\n\n async runs({ since, until }) {\n return runs.filter((run) => run.at >= since && run.at <= until).sort((a, b) => a.at - b.at);\n },\n\n async samples(key, { since, until }) {\n return (samples.get(key) ?? [])\n .filter((sample) => sample.at >= since && sample.at <= until)\n .sort((a, b) => a.at - b.at);\n },\n\n async prune({ before, keepKeys }) {\n runs = runs.filter((run) => run.at >= before);\n for (const [key, list] of samples) {\n const kept = list.filter((sample) => sample.at >= before);\n if (kept.length) samples.set(key, kept);\n else samples.delete(key);\n }\n if (keepKeys) {\n const keep = new Set(keepKeys);\n latest = Object.fromEntries(Object.entries(latest).filter(([key]) => keep.has(key)));\n for (const key of [...samples.keys()]) if (!keep.has(key)) samples.delete(key);\n }\n },\n\n async lock(name, ttlMs): Promise<LockResult> {\n const current = now();\n const expiresAt = locks.get(name);\n if (expiresAt !== undefined && expiresAt > current) {\n return { ok: false, retryAfterMs: expiresAt - current };\n }\n locks.set(name, current + ttlMs);\n return { ok: true };\n },\n\n async unlock(name) {\n locks.delete(name);\n },\n\n cache: {\n async get(key) {\n const entry = cache.get(key);\n if (!entry) return null;\n if (entry.expiresAt <= now()) {\n cache.delete(key);\n return null;\n }\n return entry.value;\n },\n async set(key, value, ttlMs) {\n cache.set(key, { value, expiresAt: now() + ttlMs });\n },\n },\n };\n}\n","import { resolveTree, type ResolvedNode, type ResolvedService, type ServiceNode } from \"./config\";\nimport { toMs, type Duration } from \"./duration\";\nimport { bucketRuns, computeServiceStats, RANGES } from \"./history\";\nimport { runServices } from \"./runner\";\nimport { collectSecretValues, redactText } from \"./sanitize\";\nimport { computeScore, resolveScoring, type Scoring, type ScoringInput } from \"./score\";\nimport type { CheckSample, LockResult, RunSummary, StatusStore } from \"./store\";\nimport { memoryStore } from \"./stores/memory\";\nimport type {\n CheckCache,\n CheckResult,\n HistoryPayload,\n HistoryRange,\n ScoreLabel,\n ServiceStats,\n SnapshotNode,\n StatusSnapshot,\n Trigger,\n} from \"./types\";\n\nexport type StatusConfig = {\n /** What to supervise: services built with presets or `custom()`, optionally nested in `group()`s */\n services: ServiceNode[];\n /** Where history is kept. Defaults to memory, which lasts as long as the process. */\n store?: StatusStore;\n /** How often a full run is due (default \"5m\"). Used by `tick()`, the scheduler and staleness. */\n interval?: Duration;\n /** How long history is kept (default \"7d\") */\n retention?: Duration;\n scoring?: ScoringInput;\n /**\n * Never quote what a service replied in public errors: a failed HTTP answer is reported by\n * its status code only (\"HTTP 500\"). Turn it on when a checked service could answer with\n * something sensitive, such as a debug page. Default false. Errors returned by `custom()`\n * checks are yours and are kept (redacted) either way.\n */\n hideResponseBodies?: boolean;\n /** Receives configuration warnings (default: `console.warn`) */\n onWarning?: (message: string) => void;\n /** Override `fetch` (tests, custom agents) */\n fetch?: typeof fetch;\n /** Override the clock (tests) */\n now?: () => number;\n};\n\nexport type RunReport = {\n at: number;\n trigger: Trigger;\n /** True when every service was checked */\n full: boolean;\n score: number | null;\n label: ScoreLabel;\n results: CheckResult[];\n};\n\nexport type ServiceHistory = { schemaVersion: 1; key: string; range: HistoryRange; samples: CheckSample[] };\n\nexport type TickResult =\n | { ran: true; report: RunReport }\n | {\n ran: false;\n /** \"not-due\": the last full run is recent enough. \"locked\": another tick is running. */\n reason: \"not-due\" | \"locked\";\n /** When the next full run is due (ms epoch) */\n nextDueAt: number;\n };\n\nexport type Status = {\n /** Check every service (or only `keys`) now, and record the results */\n run(options?: { keys?: string[]; trigger?: Trigger }): Promise<RunReport>;\n /**\n * Run every service only if a full run is due (the last one is older than `interval`), and\n * never twice at once. Safe to call as often as you like, from any number of triggers:\n * a cron, a platform scheduler, a timer, a page view.\n */\n tick(options?: { force?: boolean }): Promise<TickResult>;\n /** Everything a status page needs: the tree with latest results, statistics and the score */\n snapshot(): Promise<StatusSnapshot>;\n /** Score history in fixed buckets */\n history(options?: { range?: HistoryRange }): Promise<HistoryPayload>;\n /** Raw samples of one service */\n serviceHistory(key: string, options?: { range?: HistoryRange }): Promise<ServiceHistory>;\n /**\n * Remove every configured secret (and anything shaped like a credential) from a text.\n * Use it before logging errors that come from checks, stores or drivers.\n */\n redact(text: string): string;\n /** Whether a service with this key exists */\n has(key: string): boolean;\n /** Take a named lock for `ttlMs` (the store's when it has one, otherwise in-process) */\n lock(name: string, ttlMs: number): Promise<LockResult>;\n /** When the last full run happened, if any within the retention window */\n lastFullRun(): Promise<{ at: number; trigger: Trigger } | null>;\n readonly services: ReadonlyMap<string, ResolvedService>;\n readonly tree: readonly ResolvedNode[];\n readonly store: StatusStore;\n readonly intervalMs: number;\n readonly retentionMs: number;\n readonly scoring: Scoring;\n readonly warnings: readonly string[];\n};\n\nconst TICK_LOCK = \"sermon:tick\";\nconst STATS_KEY = \"sermon:stats\";\nconst LAST_RUN_KEY = \"sermon:last-full-run\";\n\nfunction sampleOf(result: CheckResult): CheckSample {\n const healthy = result.status === \"up\" || result.status === \"sleeping\";\n return {\n key: result.key,\n at: result.checkedAt,\n status: result.status,\n latencyMs: result.latencyMs,\n httpStatus: result.httpStatus,\n error: healthy ? undefined : result.error,\n instances: result.instances?.length\n ? Object.fromEntries(\n result.instances.map((instance) => [instance.key, [instance.status, instance.latencyMs]])\n )\n : undefined,\n };\n}\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe what to supervise and get a status engine: run checks, read snapshots and history.\n * Nothing here depends on a host or a framework; see \"sermon-js/api\" to expose it over HTTP.\n */\nexport function defineStatus(config: StatusConfig): Status {\n const now = config.now ?? (() => Date.now());\n const resolved = resolveTree(config.services ?? []);\n const { services, tree } = resolved;\n const store = config.store ?? memoryStore({ now });\n const intervalMs = toMs(config.interval ?? \"5m\");\n const retentionMs = toMs(config.retention ?? \"7d\");\n const scoring = resolveScoring(config.scoring);\n const warn = config.onWarning ?? ((message: string) => console.warn(`[sermon-js] ${message}`));\n for (const warning of resolved.warnings) warn(warning);\n\n const secretValues = collectSecretValues(\n [...services.values()].flatMap((service) => [...Object.values(service.secrets), ...(service.sensitive ?? [])])\n );\n\n // Fallbacks for stores without a cache or a lock: fine for a single process\n const fallback = memoryStore({ now });\n const cache = store.cache ?? fallback.cache!;\n const lock = (name: string, ttlMs: number) =>\n store.lock ? store.lock(name, ttlMs) : fallback.lock!(name, ttlMs);\n const unlock = async (name: string) => {\n if (store.lock) await store.unlock?.(name);\n else await fallback.unlock!(name);\n };\n\n // A full run can't outlast its slowest check by much: that bounds how long the tick lock is held\n const longestCheckMs = Math.max(0, ...[...services.values()].map((service) => service.timeoutMs));\n const tickLockMs = longestCheckMs + 6_000;\n\n const checkCache = (key: string): CheckCache => ({\n get: (name) => cache.get(`sermon:check:${key}:${name}`),\n set: (name, value, ttlMs) => cache.set(`sermon:check:${key}:${name}`, value, ttlMs),\n });\n\n /** Latest results of the services that still exist; a service that lost its configuration drops its old result */\n async function currentLatest() {\n const stored = await store.latest();\n const latest: Record<string, CheckResult> = {};\n for (const [key, service] of services) {\n const result = stored[key];\n if (!result) continue;\n if (service.missing.length > 0 && result.status !== \"unconfigured\") continue;\n latest[key] = result;\n }\n return latest;\n }\n\n function scoreOf(latest: Record<string, CheckResult>) {\n return computeScore({\n scoring,\n results: [...services.values()]\n .filter((service) => latest[service.key])\n .map((service) => ({ status: latest[service.key]!.status, critical: service.critical })),\n });\n }\n\n async function readStats() {\n return parseJson<Record<string, ServiceStats>>(await cache.get(STATS_KEY)) ?? {};\n }\n\n /** Recompute the statistics of some services from their samples and cache them all together */\n async function refreshStats(keys: string[], known?: Record<string, ServiceStats>) {\n const at = now();\n const stats = known ?? (await readStats());\n await Promise.all(\n keys.map(async (key) => {\n const samples = await store.samples(key, { since: at - retentionMs, until: at });\n stats[key] = computeServiceStats({ samples, now: at });\n })\n );\n // Forget services that no longer exist\n for (const key of Object.keys(stats)) if (!services.has(key)) delete stats[key];\n await cache.set(STATS_KEY, JSON.stringify(stats), retentionMs);\n return stats;\n }\n\n async function lastFullRun() {\n const cached = parseJson<{ at: number; trigger: Trigger }>(await cache.get(LAST_RUN_KEY));\n if (cached) return cached;\n const at = now();\n const runs = await store.runs({ since: at - retentionMs, until: at });\n const last = [...runs].reverse().find((run) => run.full);\n return last ? { at: last.at, trigger: last.trigger } : null;\n }\n\n const status: Status = {\n services,\n tree,\n store,\n intervalMs,\n retentionMs,\n scoring,\n warnings: resolved.warnings,\n redact: (text) => redactText({ text, secretValues }),\n has: (key) => services.has(key),\n lock,\n lastFullRun,\n\n async run({ keys, trigger = \"manual\" } = {}) {\n const unknown = (keys ?? []).filter((key) => !services.has(key));\n if (unknown.length) throw new Error(`sermon-js: unknown service key(s): ${unknown.join(\", \")}`);\n const selected = keys ? keys.map((key) => services.get(key)!) : [...services.values()];\n const full = !keys;\n\n const results = await runServices({\n services: selected,\n secretValues,\n cacheFor: checkCache,\n now,\n fetch: config.fetch,\n hideResponseBodies: config.hideResponseBodies,\n });\n\n // The overall score always reflects the whole system: merge with what we already know\n const latest = full ? {} : await currentLatest();\n for (const result of results) latest[result.key] = result;\n const { score, label } = scoreOf(latest);\n\n const at = now();\n const failing: RunSummary[\"failing\"] = {};\n for (const result of results) {\n if (result.status === \"down\" || result.status === \"degraded\") failing[result.key] = result.status;\n }\n const summary: RunSummary = { at, trigger, full, score, label, failing };\n const samples = results.filter((result) => result.status !== \"unconfigured\").map(sampleOf);\n\n await store.saveRun({ summary, results, samples });\n if (full) {\n await store.prune({ before: at - retentionMs, keepKeys: [...services.keys()] });\n await cache.set(LAST_RUN_KEY, JSON.stringify({ at, trigger }), retentionMs);\n }\n await refreshStats(samples.map((sample) => sample.key));\n\n return { at, trigger, full, score, label, results };\n },\n\n async tick({ force = false } = {}) {\n const at = now();\n // Schedulers fire a little early or late: a run counts as due slightly before the interval ends\n const tolerance = Math.min(intervalMs * 0.1, 30_000);\n const last = await lastFullRun();\n if (!force && last && at - last.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: last.at + intervalMs };\n }\n // Only one run at a time. The lock expires by itself if a run dies, and is released as soon\n // as a run ends, so the \"due\" check above is what paces the runs.\n const held = await lock(TICK_LOCK, tickLockMs);\n if (!held.ok) {\n return { ran: false, reason: \"locked\", nextDueAt: at + held.retryAfterMs };\n }\n try {\n // Another instance may have finished a run between our check and taking the lock\n const latest = force ? null : await lastFullRun();\n if (latest && now() - latest.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: latest.at + intervalMs };\n }\n return { ran: true, report: await status.run({ trigger: \"scheduled\" }) };\n } finally {\n await unlock(TICK_LOCK).catch(() => undefined);\n }\n },\n\n async snapshot() {\n const at = now();\n const [latest, cachedStats, lastRun] = await Promise.all([currentLatest(), readStats(), lastFullRun()]);\n\n // Statistics are normally cached at run time; rebuild whatever is missing (cold cache)\n const missing = Object.values(latest)\n .filter((result) => result.status !== \"unconfigured\" && !cachedStats[result.key])\n .map((result) => result.key);\n const stats = missing.length ? await refreshStats(missing, cachedStats) : cachedStats;\n\n const toSnapshot = (node: ResolvedNode): SnapshotNode => {\n if (node.type === \"group\") {\n return {\n type: \"group\",\n key: node.key,\n title: node.title,\n description: node.description,\n children: node.children.map(toSnapshot),\n };\n }\n const service = services.get(node.key)!;\n const configured = service.missing.length === 0;\n return {\n type: \"service\",\n key: service.key,\n kind: service.kind,\n name: service.name,\n description: service.description,\n critical: service.critical,\n slowMs: service.slowMs,\n configured,\n missing: configured ? undefined : service.missing,\n result: latest[service.key],\n stats: configured ? stats[service.key] : undefined,\n };\n };\n\n const checkedAts = Object.values(latest).map((result) => result.checkedAt);\n return {\n schemaVersion: 1,\n generatedAt: at,\n lastCheckedAt: checkedAts.length ? Math.max(...checkedAts) : null,\n lastRunAt: lastRun?.at ?? null,\n lastRunTrigger: lastRun?.trigger ?? null,\n nextDueAt: lastRun ? lastRun.at + intervalMs : null,\n intervalMs,\n retentionMs,\n ...scoreOf(latest),\n tree: tree.map(toSnapshot),\n };\n },\n\n async history({ range = \"24h\" } = {}) {\n const at = now();\n const runs = await store.runs({ since: at - RANGES[range].durationMs, until: at });\n return {\n schemaVersion: 1,\n range,\n bucketMs: RANGES[range].bucketMs,\n bars: bucketRuns({ runs, range, now: at, scoring }),\n };\n },\n\n async serviceHistory(key, { range = \"24h\" } = {}) {\n if (!services.has(key)) throw new Error(`sermon-js: unknown service key: ${key}`);\n const at = now();\n const samples = await store.samples(key, { since: at - RANGES[range].durationMs, until: at });\n return { schemaVersion: 1, key, range, samples };\n },\n };\n\n return status;\n}\n","import { toMs, type Duration } from \"./duration\";\nimport type { Status, TickResult } from \"./status\";\n\nexport type SchedulerOptions = {\n /** How often to call `tick()` (default: the status `interval`) */\n every?: Duration;\n /** Run a first tick right away instead of waiting one period (default true) */\n immediate?: boolean;\n /** Random extra delay per period, as a share of it, so replicas don't all fire together (default 0.1) */\n jitter?: number;\n onTick?: (result: TickResult) => void;\n /** Called when a tick throws (e.g. the store is unreachable). The scheduler keeps going. */\n onError?: (error: unknown) => void;\n};\n\n/**\n * In-process scheduler for long-running servers (Node, Bun, Deno, containers).\n * It only calls `status.tick()`, which is idempotent, so several replicas sharing one store\n * still run the checks once per interval.\n *\n * On serverless platforms use the platform's scheduler, or the `/tick` endpoint, instead.\n */\nexport function startScheduler(status: Status, options: SchedulerOptions = {}): { stop: () => void } {\n const periodMs = toMs(options.every ?? status.intervalMs);\n const jitter = Math.max(0, options.jitter ?? 0.1);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const schedule = (delayMs: number) => {\n if (stopped) return;\n timer = setTimeout(run, delayMs);\n // Don't keep a Node process alive just for the scheduler\n (timer as { unref?: () => void }).unref?.();\n };\n\n const run = async () => {\n try {\n // Tick first: `onTick?.(await …)` would skip the call entirely when there is no listener\n const result = await status.tick();\n options.onTick?.(result);\n } catch (error) {\n options.onError?.(error);\n }\n schedule(periodMs + Math.random() * jitter * periodMs);\n };\n\n schedule(options.immediate === false ? periodMs : 0);\n\n return {\n stop() {\n stopped = true;\n clearTimeout(timer);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/stores/memory.ts","../src/status.ts","../src/scheduler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { CheckSample, LockResult, RunSummary, StatusStore } from \"../store\";\nimport type { CheckResult } from \"../types\";\n\n/**\n * In-memory store: the default when no store is given. History lives only as long as the\n * process, so use a persistent store on serverless platforms.\n */\nexport function memoryStore({ now = () => Date.now() }: { now?: () => number } = {}): StatusStore {\n let latest: Record<string, CheckResult> = {};\n let runs: RunSummary[] = [];\n const samples = new Map<string, CheckSample[]>();\n const locks = new Map<string, number>();\n const cache = new Map<string, { value: string; expiresAt: number }>();\n\n return {\n async saveRun(run) {\n for (const result of run.results) latest[result.key] = result;\n for (const sample of run.samples) {\n const list = samples.get(sample.key) ?? [];\n list.push(sample);\n samples.set(sample.key, list);\n }\n runs.push(run.summary);\n },\n\n async latest() {\n return { ...latest };\n },\n\n async runs({ since, until }) {\n return runs.filter((run) => run.at >= since && run.at <= until).sort((a, b) => a.at - b.at);\n },\n\n async samples(key, { since, until }) {\n return (samples.get(key) ?? [])\n .filter((sample) => sample.at >= since && sample.at <= until)\n .sort((a, b) => a.at - b.at);\n },\n\n async prune({ before, keepKeys }) {\n runs = runs.filter((run) => run.at >= before);\n for (const [key, list] of samples) {\n const kept = list.filter((sample) => sample.at >= before);\n if (kept.length) samples.set(key, kept);\n else samples.delete(key);\n }\n if (keepKeys) {\n const keep = new Set(keepKeys);\n latest = Object.fromEntries(Object.entries(latest).filter(([key]) => keep.has(key)));\n for (const key of [...samples.keys()]) if (!keep.has(key)) samples.delete(key);\n }\n },\n\n async lock(name, ttlMs): Promise<LockResult> {\n const current = now();\n const expiresAt = locks.get(name);\n if (expiresAt !== undefined && expiresAt > current) {\n return { ok: false, retryAfterMs: expiresAt - current };\n }\n locks.set(name, current + ttlMs);\n return { ok: true };\n },\n\n async unlock(name) {\n locks.delete(name);\n },\n\n cache: {\n async get(key) {\n const entry = cache.get(key);\n if (!entry) return null;\n if (entry.expiresAt <= now()) {\n cache.delete(key);\n return null;\n }\n return entry.value;\n },\n async set(key, value, ttlMs) {\n cache.set(key, { value, expiresAt: now() + ttlMs });\n },\n },\n };\n}\n","import { resolveTree, type ResolvedNode, type ResolvedService, type ServiceNode } from \"./config\";\nimport { toMs, type Duration } from \"./duration\";\nimport { bucketFor, bucketRuns, computeServiceStats, RANGES } from \"./history\";\nimport { runServices } from \"./runner\";\nimport { collectSecretValues, redactText } from \"./sanitize\";\nimport { computeScore, resolveScoring, type Scoring, type ScoringInput } from \"./score\";\nimport type { CheckSample, LockResult, RunSummary, StatusStore } from \"./store\";\nimport { memoryStore } from \"./stores/memory\";\nimport type {\n CheckCache,\n CheckResult,\n HistoryPayload,\n HistoryRange,\n ScoreLabel,\n ServiceStats,\n SnapshotNode,\n StatusSnapshot,\n Trigger,\n} from \"./types\";\n\nexport type StatusConfig = {\n /** What to supervise: services built with presets or `custom()`, optionally nested in `group()`s */\n services: ServiceNode[];\n /** Where history is kept. Defaults to memory, which lasts as long as the process. */\n store?: StatusStore;\n /** How often a full run is due (default \"5m\"). Used by `tick()`, the scheduler and staleness. */\n interval?: Duration;\n /** How long history is kept (default \"7d\") */\n retention?: Duration;\n scoring?: ScoringInput;\n /**\n * Never quote what a service replied in public errors: a failed HTTP answer is reported by\n * its status code only (\"HTTP 500\"). Turn it on when a checked service could answer with\n * something sensitive, such as a debug page. Default false. Errors returned by `custom()`\n * checks are yours and are kept (redacted) either way.\n */\n hideResponseBodies?: boolean;\n /** Receives configuration warnings (default: `console.warn`) */\n onWarning?: (message: string) => void;\n /** Override `fetch` (tests, custom agents) */\n fetch?: typeof fetch;\n /** Override the clock (tests) */\n now?: () => number;\n};\n\nexport type RunReport = {\n at: number;\n trigger: Trigger;\n /** True when every service was checked */\n full: boolean;\n score: number | null;\n label: ScoreLabel;\n results: CheckResult[];\n};\n\nexport type ServiceHistory = { schemaVersion: 1; key: string; range: HistoryRange; samples: CheckSample[] };\n\nexport type TickResult =\n | { ran: true; report: RunReport }\n | {\n ran: false;\n /** \"not-due\": the last full run is recent enough. \"locked\": another tick is running. */\n reason: \"not-due\" | \"locked\";\n /** When the next full run is due (ms epoch) */\n nextDueAt: number;\n };\n\nexport type Status = {\n /** Check every service (or only `keys`) now, and record the results */\n run(options?: { keys?: string[]; trigger?: Trigger }): Promise<RunReport>;\n /**\n * Run every service only if a full run is due (the last one is older than `interval`), and\n * never twice at once. Safe to call as often as you like, from any number of triggers:\n * a cron, a platform scheduler, a timer, a page view.\n */\n tick(options?: { force?: boolean }): Promise<TickResult>;\n /** Everything a status page needs: the tree with latest results, statistics and the score */\n snapshot(): Promise<StatusSnapshot>;\n /** Score history in fixed buckets */\n history(options?: { range?: HistoryRange }): Promise<HistoryPayload>;\n /** Raw samples of one service */\n serviceHistory(key: string, options?: { range?: HistoryRange }): Promise<ServiceHistory>;\n /**\n * Remove every configured secret (and anything shaped like a credential) from a text.\n * Use it before logging errors that come from checks, stores or drivers.\n */\n redact(text: string): string;\n /** Whether a service with this key exists */\n has(key: string): boolean;\n /** Take a named lock for `ttlMs` (the store's when it has one, otherwise in-process) */\n lock(name: string, ttlMs: number): Promise<LockResult>;\n /** When the last full run happened, if any within the retention window */\n lastFullRun(): Promise<{ at: number; trigger: Trigger } | null>;\n readonly services: ReadonlyMap<string, ResolvedService>;\n readonly tree: readonly ResolvedNode[];\n readonly store: StatusStore;\n readonly intervalMs: number;\n readonly retentionMs: number;\n readonly scoring: Scoring;\n readonly warnings: readonly string[];\n};\n\nconst TICK_LOCK = \"sermon:tick\";\nconst STATS_KEY = \"sermon:stats\";\nconst LAST_RUN_KEY = \"sermon:last-full-run\";\n\nfunction sampleOf(result: CheckResult): CheckSample {\n const healthy = result.status === \"up\" || result.status === \"sleeping\";\n return {\n key: result.key,\n at: result.checkedAt,\n status: result.status,\n latencyMs: result.latencyMs,\n httpStatus: result.httpStatus,\n error: healthy ? undefined : result.error,\n instances: result.instances?.length\n ? Object.fromEntries(\n result.instances.map((instance) => [instance.key, [instance.status, instance.latencyMs]])\n )\n : undefined,\n };\n}\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe what to supervise and get a status engine: run checks, read snapshots and history.\n * Nothing here depends on a host or a framework; see \"sermon-js/api\" to expose it over HTTP.\n */\nexport function defineStatus(config: StatusConfig): Status {\n const now = config.now ?? (() => Date.now());\n const resolved = resolveTree(config.services ?? []);\n const { services, tree } = resolved;\n const store = config.store ?? memoryStore({ now });\n const intervalMs = toMs(config.interval ?? \"5m\");\n const retentionMs = toMs(config.retention ?? \"7d\");\n const scoring = resolveScoring(config.scoring);\n const warn = config.onWarning ?? ((message: string) => console.warn(`[sermon-js] ${message}`));\n for (const warning of resolved.warnings) warn(warning);\n\n const secretValues = collectSecretValues(\n [...services.values()].flatMap((service) => [...Object.values(service.secrets), ...(service.sensitive ?? [])])\n );\n\n // Fallbacks for stores without a cache or a lock: fine for a single process\n const fallback = memoryStore({ now });\n const cache = store.cache ?? fallback.cache!;\n const lock = (name: string, ttlMs: number) =>\n store.lock ? store.lock(name, ttlMs) : fallback.lock!(name, ttlMs);\n const unlock = async (name: string) => {\n if (store.lock) await store.unlock?.(name);\n else await fallback.unlock!(name);\n };\n\n // A full run can't outlast its slowest check by much: that bounds how long the tick lock is held\n const longestCheckMs = Math.max(0, ...[...services.values()].map((service) => service.timeoutMs));\n const tickLockMs = longestCheckMs + 6_000;\n\n const checkCache = (key: string): CheckCache => ({\n get: (name) => cache.get(`sermon:check:${key}:${name}`),\n set: (name, value, ttlMs) => cache.set(`sermon:check:${key}:${name}`, value, ttlMs),\n });\n\n /** Latest results of the services that still exist; a service that lost its configuration drops its old result */\n async function currentLatest() {\n const stored = await store.latest();\n const latest: Record<string, CheckResult> = {};\n for (const [key, service] of services) {\n const result = stored[key];\n if (!result) continue;\n if (service.missing.length > 0 && result.status !== \"unconfigured\") continue;\n latest[key] = result;\n }\n return latest;\n }\n\n function scoreOf(latest: Record<string, CheckResult>) {\n return computeScore({\n scoring,\n results: [...services.values()]\n .filter((service) => latest[service.key])\n .map((service) => ({ status: latest[service.key]!.status, critical: service.critical })),\n });\n }\n\n async function readStats() {\n return parseJson<Record<string, ServiceStats>>(await cache.get(STATS_KEY)) ?? {};\n }\n\n /** Recompute the statistics of some services from their samples and cache them all together */\n async function refreshStats(keys: string[], known?: Record<string, ServiceStats>) {\n const at = now();\n const stats = known ?? (await readStats());\n await Promise.all(\n keys.map(async (key) => {\n const samples = await store.samples(key, { since: at - retentionMs, until: at });\n stats[key] = computeServiceStats({ samples, now: at });\n })\n );\n // Forget services that no longer exist\n for (const key of Object.keys(stats)) if (!services.has(key)) delete stats[key];\n await cache.set(STATS_KEY, JSON.stringify(stats), retentionMs);\n return stats;\n }\n\n async function lastFullRun() {\n const cached = parseJson<{ at: number; trigger: Trigger }>(await cache.get(LAST_RUN_KEY));\n if (cached) return cached;\n const at = now();\n const runs = await store.runs({ since: at - retentionMs, until: at });\n const last = [...runs].reverse().find((run) => run.full);\n return last ? { at: last.at, trigger: last.trigger } : null;\n }\n\n const status: Status = {\n services,\n tree,\n store,\n intervalMs,\n retentionMs,\n scoring,\n warnings: resolved.warnings,\n redact: (text) => redactText({ text, secretValues }),\n has: (key) => services.has(key),\n lock,\n lastFullRun,\n\n async run({ keys, trigger = \"manual\" } = {}) {\n const unknown = (keys ?? []).filter((key) => !services.has(key));\n if (unknown.length) throw new Error(`sermon-js: unknown service key(s): ${unknown.join(\", \")}`);\n const selected = keys ? keys.map((key) => services.get(key)!) : [...services.values()];\n const full = !keys;\n\n const results = await runServices({\n services: selected,\n secretValues,\n cacheFor: checkCache,\n now,\n fetch: config.fetch,\n hideResponseBodies: config.hideResponseBodies,\n });\n\n // The overall score always reflects the whole system: merge with what we already know\n const latest = full ? {} : await currentLatest();\n for (const result of results) latest[result.key] = result;\n const { score, label } = scoreOf(latest);\n\n const at = now();\n const failing: RunSummary[\"failing\"] = {};\n for (const result of results) {\n if (result.status === \"down\" || result.status === \"degraded\") failing[result.key] = result.status;\n }\n const summary: RunSummary = { at, trigger, full, score, label, failing };\n const samples = results.filter((result) => result.status !== \"unconfigured\").map(sampleOf);\n\n await store.saveRun({ summary, results, samples });\n if (full) {\n await store.prune({ before: at - retentionMs, keepKeys: [...services.keys()] });\n await cache.set(LAST_RUN_KEY, JSON.stringify({ at, trigger }), retentionMs);\n }\n await refreshStats(samples.map((sample) => sample.key));\n\n return { at, trigger, full, score, label, results };\n },\n\n async tick({ force = false } = {}) {\n const at = now();\n // Schedulers fire a little early or late: a run counts as due slightly before the interval ends\n const tolerance = Math.min(intervalMs * 0.1, 30_000);\n const last = await lastFullRun();\n if (!force && last && at - last.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: last.at + intervalMs };\n }\n // Only one run at a time. The lock expires by itself if a run dies, and is released as soon\n // as a run ends, so the \"due\" check above is what paces the runs.\n const held = await lock(TICK_LOCK, tickLockMs);\n if (!held.ok) {\n return { ran: false, reason: \"locked\", nextDueAt: at + held.retryAfterMs };\n }\n try {\n // Another instance may have finished a run between our check and taking the lock\n const latest = force ? null : await lastFullRun();\n if (latest && now() - latest.at < intervalMs - tolerance) {\n return { ran: false, reason: \"not-due\", nextDueAt: latest.at + intervalMs };\n }\n return { ran: true, report: await status.run({ trigger: \"scheduled\" }) };\n } finally {\n await unlock(TICK_LOCK).catch(() => undefined);\n }\n },\n\n async snapshot() {\n const at = now();\n const [latest, cachedStats, lastRun] = await Promise.all([currentLatest(), readStats(), lastFullRun()]);\n\n // Statistics are normally cached at run time; rebuild whatever is missing (cold cache)\n const missing = Object.values(latest)\n .filter((result) => result.status !== \"unconfigured\" && !cachedStats[result.key])\n .map((result) => result.key);\n const stats = missing.length ? await refreshStats(missing, cachedStats) : cachedStats;\n\n const toSnapshot = (node: ResolvedNode): SnapshotNode => {\n if (node.type === \"group\") {\n return {\n type: \"group\",\n key: node.key,\n title: node.title,\n description: node.description,\n children: node.children.map(toSnapshot),\n };\n }\n const service = services.get(node.key)!;\n const configured = service.missing.length === 0;\n return {\n type: \"service\",\n key: service.key,\n kind: service.kind,\n name: service.name,\n description: service.description,\n critical: service.critical,\n slowMs: service.slowMs,\n configured,\n missing: configured ? undefined : service.missing,\n result: latest[service.key],\n stats: configured ? stats[service.key] : undefined,\n };\n };\n\n const checkedAts = Object.values(latest).map((result) => result.checkedAt);\n return {\n schemaVersion: 1,\n generatedAt: at,\n lastCheckedAt: checkedAts.length ? Math.max(...checkedAts) : null,\n lastRunAt: lastRun?.at ?? null,\n lastRunTrigger: lastRun?.trigger ?? null,\n nextDueAt: lastRun ? lastRun.at + intervalMs : null,\n intervalMs,\n retentionMs,\n ...scoreOf(latest),\n tree: tree.map(toSnapshot),\n };\n },\n\n async history({ range = \"24h\" } = {}) {\n const at = now();\n const runs = await store.runs({ since: at - RANGES[range].durationMs, until: at });\n // One bar per scheduled run: bars as wide as the interval\n const bucketMs = bucketFor(range, intervalMs);\n return {\n schemaVersion: 1,\n range,\n bucketMs,\n bars: bucketRuns({ runs, range, now: at, scoring, bucketMs }),\n };\n },\n\n async serviceHistory(key, { range = \"24h\" } = {}) {\n if (!services.has(key)) throw new Error(`sermon-js: unknown service key: ${key}`);\n const at = now();\n const samples = await store.samples(key, { since: at - RANGES[range].durationMs, until: at });\n return { schemaVersion: 1, key, range, samples };\n },\n };\n\n return status;\n}\n","import { toMs, type Duration } from \"./duration\";\nimport type { Status, TickResult } from \"./status\";\n\nexport type SchedulerOptions = {\n /** How often to call `tick()` (default: the status `interval`) */\n every?: Duration;\n /** Run a first tick right away instead of waiting one period (default true) */\n immediate?: boolean;\n /** Random extra delay per period, as a share of it, so replicas don't all fire together (default 0.1) */\n jitter?: number;\n onTick?: (result: TickResult) => void;\n /** Called when a tick throws (e.g. the store is unreachable). The scheduler keeps going. */\n onError?: (error: unknown) => void;\n};\n\n/**\n * In-process scheduler for long-running servers (Node, Bun, Deno, containers).\n * It only calls `status.tick()`, which is idempotent, so several replicas sharing one store\n * still run the checks once per interval.\n *\n * On serverless platforms use the platform's scheduler, or the `/tick` endpoint, instead.\n */\nexport function startScheduler(status: Status, options: SchedulerOptions = {}): { stop: () => void } {\n const periodMs = toMs(options.every ?? status.intervalMs);\n const jitter = Math.max(0, options.jitter ?? 0.1);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const schedule = (delayMs: number) => {\n if (stopped) return;\n timer = setTimeout(run, delayMs);\n // Don't keep a Node process alive just for the scheduler\n (timer as { unref?: () => void }).unref?.();\n };\n\n const run = async () => {\n try {\n // Tick first: `onTick?.(await …)` would skip the call entirely when there is no listener\n const result = await status.tick();\n options.onTick?.(result);\n } catch (error) {\n options.onError?.(error);\n }\n schedule(periodMs + Math.random() * jitter * periodMs);\n };\n\n schedule(options.immediate === false ? periodMs : 0);\n\n return {\n stop() {\n stopped = true;\n clearTimeout(timer);\n },\n };\n}\n"]}