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
package/README.md CHANGED
@@ -110,6 +110,7 @@ type CheckOutcome = {
110
110
  metrics?: Record<string, string | number>; // small chips
111
111
  usage?: { label; used; limit; unit?; resetsAt? }[]; // quota bars
112
112
  instances?: { key; region?; state?; status; latencyMs?; error? }[]; // per machine/region
113
+ instanceLabels?: { key; detail? }; // what the instances are: "Model", "Compute"…
113
114
  probed?: boolean; // false when the target was deliberately not touched
114
115
  };
115
116
  ```
@@ -158,7 +159,7 @@ await status.tick(); // run everything, only if due (idempo
158
159
  await status.run(); // run everything now
159
160
  await status.run({ keys: ["queue-depth"] });
160
161
  await status.snapshot(); // tree + latest results + statistics + score
161
- await status.history({ range: "24h" }); // score history in buckets ("24h" | "7d")
162
+ await status.history({ range: "24h" }); // score history, one bar per interval ("24h" | "7d")
162
163
  await status.serviceHistory("queue-depth");
163
164
  ```
164
165
 
@@ -230,7 +231,7 @@ hooks and helpers to build your own. See [UI kit](docs/ui.md) and
230
231
  - [x] Host-agnostic scheduling: `tick()`, `startScheduler`, `/tick`, refresh on read
231
232
  - [x] HTTP API handler, Node adapter
232
233
  - [x] Stores: memory, Upstash REST, any Redis client
233
- - [x] Presets: Fly apps (sleep-safe, per region), GCP instances, AWS/S3, Statuspage, Pusher, AI/translation/video/email/search providers, Vercel, MongoDB, Redis, PostgreSQL
234
+ - [x] Presets: Fly apps (sleep-safe, per region), GCP instances, Neon projects (sleep-safe), AWS/S3/Bedrock, Statuspage, Pusher, Stripe, AI/translation/video/email/search providers, Vercel, MongoDB, Redis, PostgreSQL
234
235
  - [x] Stores: SQL (Postgres, MySQL/MariaDB, SQLite), MongoDB
235
236
  - [x] React UI kit (headless hooks + HeroUI components)
236
237
  - [ ] First npm release
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkNP3YYVRZ_cjs = require('../chunk-NP3YYVRZ.cjs');
3
+ var chunkZAZ6VTGV_cjs = require('../chunk-ZAZ6VTGV.cjs');
4
4
 
5
5
  // src/api/index.ts
6
6
  var RUN_ALL_LOCK = "sermon:run:all";
7
7
  function parseRange(value) {
8
- return value && value in chunkNP3YYVRZ_cjs.RANGES ? value : "24h";
8
+ return value && value in chunkZAZ6VTGV_cjs.RANGES ? value : "24h";
9
9
  }
10
10
  function safeEqual(a, b) {
11
11
  if (a.length !== b.length) return false;
@@ -30,9 +30,9 @@ function describeError(error) {
30
30
  }
31
31
  function createStatusHandler(status, options = {}) {
32
32
  const basePath = `/${(options.basePath ?? "/api").replace(/^\/+|\/+$/g, "")}`.replace(/^\/$/, "");
33
- const runAllMs = chunkNP3YYVRZ_cjs.toMs(options.rateLimits?.runAll ?? "60s");
34
- const runOneMs = chunkNP3YYVRZ_cjs.toMs(options.rateLimits?.runOne ?? "20s");
35
- const refreshBudgetMs = chunkNP3YYVRZ_cjs.toMs(options.refreshBudget ?? "8s");
33
+ const runAllMs = chunkZAZ6VTGV_cjs.toMs(options.rateLimits?.runAll ?? "60s");
34
+ const runOneMs = chunkZAZ6VTGV_cjs.toMs(options.rateLimits?.runOne ?? "20s");
35
+ const refreshBudgetMs = chunkZAZ6VTGV_cjs.toMs(options.refreshBudget ?? "8s");
36
36
  const cache = options.cache === false ? void 0 : { status: 10, history: 30, ...options.cache };
37
37
  const corsHeaders = (request) => {
38
38
  if (!options.cors) return {};
@@ -1,7 +1,7 @@
1
- import { D as Duration, S as Status } from '../status-DsWzgb9Q.cjs';
2
- import '../config-BEbP-uLf.cjs';
3
- import '../types-CctvUAs2.cjs';
4
- import '../store-DybhDphS.cjs';
1
+ import { D as Duration, S as Status } from '../status-BExBb08T.cjs';
2
+ import '../config-gro29syt.cjs';
3
+ import '../types-BVa6vIKk.cjs';
4
+ import '../store-CAIB7Esp.cjs';
5
5
 
6
6
  type HandlerContext = {
7
7
  /** Keeps the platform alive for background work after the response (Workers, Vercel, Netlify…) */
@@ -1,7 +1,7 @@
1
- import { D as Duration, S as Status } from '../status-zd04ZQ98.js';
2
- import '../config-w1tm1bdu.js';
3
- import '../types-CctvUAs2.js';
4
- import '../store-_GuzSP0z.js';
1
+ import { D as Duration, S as Status } from '../status-DmRhwvVC.js';
2
+ import '../config-iY18j9IA.js';
3
+ import '../types-BVa6vIKk.js';
4
+ import '../store-BfKP7CjW.js';
5
5
 
6
6
  type HandlerContext = {
7
7
  /** Keeps the platform alive for background work after the response (Workers, Vercel, Netlify…) */
package/dist/api/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { toMs, RANGES } from '../chunk-APC2HVSI.js';
1
+ import { toMs, RANGES } from '../chunk-NUSAZZYR.js';
2
2
 
3
3
  // src/api/index.ts
4
4
  var RUN_ALL_LOCK = "sermon:run:all";
@@ -68,6 +68,11 @@ var RANGES = {
68
68
  "24h": { durationMs: DAY_MS, bucketMs: 5 * 60 * 1e3 },
69
69
  "7d": { durationMs: 7 * DAY_MS, bucketMs: 60 * 60 * 1e3 }
70
70
  };
71
+ function bucketFor(range, intervalMs) {
72
+ const { durationMs, bucketMs } = RANGES[range];
73
+ if (!intervalMs || !Number.isFinite(intervalMs)) return bucketMs;
74
+ return Math.min(durationMs, Math.max(bucketMs, Math.round(intervalMs)));
75
+ }
71
76
  function percentile(sorted, p) {
72
77
  if (!sorted.length) return null;
73
78
  const index = Math.min(sorted.length - 1, Math.ceil(p / 100 * sorted.length) - 1);
@@ -122,11 +127,12 @@ function bucketRuns({
122
127
  runs,
123
128
  range,
124
129
  now = Date.now(),
125
- scoring
130
+ scoring,
131
+ bucketMs = RANGES[range].bucketMs
126
132
  }) {
127
- const { durationMs, bucketMs } = RANGES[range];
133
+ const { durationMs } = RANGES[range];
128
134
  const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;
129
- const count = Math.round(durationMs / bucketMs);
135
+ const count = Math.ceil(durationMs / bucketMs);
130
136
  const start = end - count * bucketMs;
131
137
  const bars = Array.from({ length: count }, (_, i) => ({
132
138
  start: start + i * bucketMs,
@@ -149,6 +155,6 @@ function bucketRuns({
149
155
  return bars;
150
156
  }
151
157
 
152
- export { DEFAULT_SCORING, RANGES, bucketRuns, computeScore, computeServiceStats, labelForScore, resolveScoring, toMs };
153
- //# sourceMappingURL=chunk-APC2HVSI.js.map
154
- //# sourceMappingURL=chunk-APC2HVSI.js.map
158
+ export { DEFAULT_SCORING, RANGES, bucketFor, bucketRuns, computeScore, computeServiceStats, labelForScore, resolveScoring, toMs };
159
+ //# sourceMappingURL=chunk-NUSAZZYR.js.map
160
+ //# sourceMappingURL=chunk-NUSAZZYR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/duration.ts","../src/score.ts","../src/history.ts"],"names":[],"mappings":"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","file":"chunk-NUSAZZYR.js","sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\n/** A number of milliseconds, or a string like \"30s\", \"5m\", \"1h\", \"7d\" */\nexport type Duration = number | `${number}${\"ms\" | \"s\" | \"m\" | \"h\" | \"d\"}`;\n\nexport function toMs(duration: Duration): number {\n if (typeof duration === \"number\") {\n if (!Number.isFinite(duration) || duration < 0) throw new Error(`Invalid duration: ${duration}`);\n return duration;\n }\n const match = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/.exec(duration.trim());\n if (!match) throw new Error(`Invalid duration: \"${duration}\" (use e.g. \"30s\", \"5m\", \"1h\", \"7d\")`);\n return Number(match[1]) * UNITS[match[2] as string]!;\n}\n","import type { ScoreLabel, Status } from \"./types\";\n\nexport type Scoring = {\n /** How much more a critical service weighs than a normal one (default 3) */\n criticalWeight: number;\n /** Value of a degraded service between 0 and 1 (default 0.6) */\n degradedValue: number;\n thresholds: {\n /** Score from which the system is \"operational\" (default 98) */\n operational: number;\n /** Score from which the system is \"degraded\" rather than \"outage\" (default 85) */\n degraded: number;\n };\n};\n\nexport const DEFAULT_SCORING: Scoring = {\n criticalWeight: 3,\n degradedValue: 0.6,\n thresholds: { operational: 98, degraded: 85 },\n};\n\nexport type ScoringInput = {\n criticalWeight?: number;\n degradedValue?: number;\n thresholds?: Partial<Scoring[\"thresholds\"]>;\n};\n\nexport function resolveScoring(input: ScoringInput = {}): Scoring {\n return {\n criticalWeight: input.criticalWeight ?? DEFAULT_SCORING.criticalWeight,\n degradedValue: input.degradedValue ?? DEFAULT_SCORING.degradedValue,\n thresholds: { ...DEFAULT_SCORING.thresholds, ...input.thresholds },\n };\n}\n\nexport function labelForScore(score: number | null, scoring: Scoring = DEFAULT_SCORING): ScoreLabel {\n if (score === null) return \"unknown\";\n if (score >= scoring.thresholds.operational) return \"operational\";\n if (score >= scoring.thresholds.degraded) return \"degraded\";\n return \"outage\";\n}\n\n/**\n * Weighted share of healthy services, 0–100. Sleeping services are expected (scale to zero)\n * and count as healthy; unconfigured ones are left out. Any critical service being down\n * forces the \"outage\" label, whatever the number.\n */\nexport function computeScore({\n results,\n scoring = DEFAULT_SCORING,\n}: {\n results: Array<{ status: Status; critical: boolean }>;\n scoring?: Scoring;\n}): { score: number | null; label: ScoreLabel; counts: Record<Status, number> } {\n const counts: Record<Status, number> = { up: 0, degraded: 0, down: 0, sleeping: 0, unconfigured: 0 };\n const values: Record<Exclude<Status, \"unconfigured\">, number> = {\n up: 1,\n sleeping: 1,\n degraded: scoring.degradedValue,\n down: 0,\n };\n let weighted = 0;\n let totalWeight = 0;\n let criticalDown = false;\n\n for (const { status, critical } of results) {\n counts[status] += 1;\n if (status === \"unconfigured\") continue;\n const weight = critical ? scoring.criticalWeight : 1;\n weighted += weight * values[status];\n totalWeight += weight;\n if (critical && status === \"down\") criticalDown = true;\n }\n\n if (totalWeight === 0) return { score: null, label: \"unknown\", counts };\n const score = Math.round((100 * weighted) / totalWeight);\n return { score, label: criticalDown ? \"outage\" : labelForScore(score, scoring), counts };\n}\n","import { labelForScore, type Scoring } from \"./score\";\nimport type { CheckSample, RunSummary } from \"./store\";\nimport type { HistoryBar, HistoryRange, InstanceStats, ScoreLabel, ServiceStats, Status } from \"./types\";\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** How far each range looks back, and its smallest bucket */\nexport const RANGES: Record<HistoryRange, { durationMs: number; bucketMs: number }> = {\n \"24h\": { durationMs: DAY_MS, bucketMs: 5 * 60 * 1000 },\n \"7d\": { durationMs: 7 * DAY_MS, bucketMs: 60 * 60 * 1000 },\n};\n\n/**\n * The width of one history bar: the check interval, so every bar holds one scheduled run\n * (a 15-minute interval gives 15-minute bars), and never less than the range's smallest\n * bucket, nor more than the range itself.\n */\nexport function bucketFor(range: HistoryRange, intervalMs?: number): number {\n const { durationMs, bucketMs } = RANGES[range];\n if (!intervalMs || !Number.isFinite(intervalMs)) return bucketMs;\n return Math.min(durationMs, Math.max(bucketMs, Math.round(intervalMs)));\n}\n\nfunction percentile(sorted: number[], p: number): number | null {\n if (!sorted.length) return null;\n const index = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);\n return sorted[Math.max(0, index)] ?? null;\n}\n\n/** Share of samples that were not \"down\", in percent with two decimals */\nfunction uptime(samples: Array<{ status: Status }>): number | null {\n if (!samples.length) return null;\n const available = samples.filter((sample) => sample.status !== \"down\").length;\n return Math.round((available / samples.length) * 10000) / 100;\n}\n\nconst measured = (sample: { status: Status; latencyMs?: number }) =>\n sample.latencyMs !== undefined && (sample.status === \"up\" || sample.status === \"degraded\");\n\n/** Uptime, latency percentiles, sparkline series and last failure of one service */\nexport function computeServiceStats({\n samples,\n now = Date.now(),\n}: {\n /** Samples of the whole retention window, oldest first */\n samples: CheckSample[];\n now?: number;\n}): ServiceStats {\n const day = samples.filter((sample) => sample.at >= now - DAY_MS);\n const week = samples.filter((sample) => sample.at >= now - 7 * DAY_MS);\n const latencies = day\n .filter(measured)\n .map((sample) => sample.latencyMs as number)\n .sort((a, b) => a - b);\n const lastFailure = [...samples]\n .reverse()\n .find((sample) => sample.status === \"down\" || sample.status === \"degraded\");\n\n // Same figures per instance, over the last 24h\n const byInstance = new Map<string, Array<{ status: Status; latencyMs?: number }>>();\n for (const sample of day) {\n for (const [key, [status, latencyMs]] of Object.entries(sample.instances ?? {})) {\n byInstance.set(key, [...(byInstance.get(key) ?? []), { status, latencyMs }]);\n }\n }\n const instances: Record<string, InstanceStats> | undefined = byInstance.size\n ? Object.fromEntries(\n [...byInstance].map(([key, list]) => [\n key,\n {\n uptime24h: uptime(list),\n p50: percentile(\n list\n .filter(measured)\n .map((item) => item.latencyMs as number)\n .sort((a, b) => a - b),\n 50\n ),\n samples24h: list.length,\n },\n ])\n )\n : undefined;\n\n return {\n uptime24h: uptime(day),\n uptime7d: uptime(week),\n p50: percentile(latencies, 50),\n p95: percentile(latencies, 95),\n samples24h: day.length,\n latency: day.map((sample) => sample.latencyMs ?? null),\n lastFailure: lastFailure\n ? { at: lastFailure.at, status: lastFailure.status, error: lastFailure.error }\n : undefined,\n instances,\n };\n}\n\nconst LABEL_SEVERITY: Record<ScoreLabel, number> = { unknown: 0, operational: 1, degraded: 2, outage: 3 };\n\n/** Group runs into fixed buckets; each bucket keeps its worst score, label and failures */\nexport function bucketRuns({\n runs,\n range,\n now = Date.now(),\n scoring,\n bucketMs = RANGES[range].bucketMs,\n}: {\n runs: RunSummary[];\n range: HistoryRange;\n now?: number;\n scoring?: Scoring;\n /** Width of one bar (default: the range's smallest bucket). See `bucketFor`. */\n bucketMs?: number;\n}): HistoryBar[] {\n const { durationMs } = RANGES[range];\n const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;\n // An interval that doesn't divide the range still covers all of it\n const count = Math.ceil(durationMs / bucketMs);\n const start = end - count * bucketMs;\n\n const bars: HistoryBar[] = Array.from({ length: count }, (_, i) => ({\n start: start + i * bucketMs,\n score: null,\n label: \"unknown\",\n runs: 0,\n failing: {},\n }));\n\n for (const run of runs) {\n const bar = bars[Math.floor((run.at - start) / bucketMs)];\n if (!bar) continue;\n bar.runs += 1;\n if (run.score !== null && (bar.score === null || run.score < bar.score)) bar.score = run.score;\n const label = run.label ?? labelForScore(run.score, scoring);\n if (LABEL_SEVERITY[label] > LABEL_SEVERITY[bar.label]) bar.label = label;\n for (const [key, status] of Object.entries(run.failing)) {\n // \"down\" wins over \"degraded\" within a bucket\n if (bar.failing[key] !== \"down\") bar.failing[key] = status;\n }\n }\n return bars;\n}\n"]}
@@ -162,7 +162,11 @@ async function runService({
162
162
  id: instance.id === void 0 ? void 0 : redact(instance.id),
163
163
  state: instance.state === void 0 ? void 0 : redact(instance.state),
164
164
  error: sanitizeError({ message: instance.error, secretValues })
165
- })) : void 0
165
+ })) : void 0,
166
+ instanceLabels: outcome.instanceLabels ? {
167
+ key: redact(outcome.instanceLabels.key),
168
+ detail: outcome.instanceLabels.detail === void 0 ? void 0 : redact(outcome.instanceLabels.detail)
169
+ } : void 0
166
170
  };
167
171
  }
168
172
  async function runServices({
@@ -180,5 +184,5 @@ exports.redactText = redactText;
180
184
  exports.runService = runService;
181
185
  exports.runServices = runServices;
182
186
  exports.sanitizeError = sanitizeError;
183
- //# sourceMappingURL=chunk-PARE6EUZ.cjs.map
184
- //# sourceMappingURL=chunk-PARE6EUZ.cjs.map
187
+ //# sourceMappingURL=chunk-NZTKXXW2.cjs.map
188
+ //# sourceMappingURL=chunk-NZTKXXW2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sanitize.ts","../src/runner.ts"],"names":["createHttp","measure","combine","normalizeReturn","error","errorMessage"],"mappings":"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","file":"chunk-NZTKXXW2.cjs","sourcesContent":["const MAX_LENGTH = 300;\nconst MIN_SECRET_LENGTH = 6;\n\n/** Shapes of well-known credentials, redacted even when the value was never declared as a secret */\nconst KEY_PATTERNS: RegExp[] = [\n // PEM blocks (private keys, certificates), whole or cut short\n /-----BEGIN [A-Z0-9 ]+-----[\\s\\S]*?(-----END [A-Z0-9 ]+-----|$)/g,\n // JSON Web Tokens (OAuth access and identity tokens)\n /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}(\\.[A-Za-z0-9_-]*)?/g,\n // Prefixed API keys: OpenAI/Anthropic/Stripe (sk-, pk_, rk_), GitHub, Slack, Fly, MailerSend.\n // \"*\" is included because providers echo keys back partly masked (sk-proj-****abcd).\n /\\b(sk|pk|rk)[-_][A-Za-z0-9_*.-]{12,}/g,\n /\\b(gh[pousr]_|github_pat_)[A-Za-z0-9_*]{12,}/g,\n /\\bxox[abeoprs]-[A-Za-z0-9*-]{10,}/g,\n /\\b(FlyV1 |fm[12]_|fo1_)[A-Za-z0-9+/=_,*-]{12,}/g,\n /\\bmlsn\\.[A-Za-z0-9*]{12,}/g,\n // AWS access key ids, Google API keys, Google OAuth tokens\n /\\b(AKIA|ASIA)[0-9A-Z*]{12,}/g,\n /\\bAIza[0-9A-Za-z_*-]{20,}/g,\n /\\bya29\\.[0-9A-Za-z_*.-]{12,}/g,\n];\n\n/**\n * Every value worth redacting from a set of secrets: the values themselves, the string fields\n * of JSON secrets (e.g. the private key inside a service-account JSON), the user and password\n * of connection strings, and the encoded forms a value takes in URLs, JSON and Basic auth.\n */\nexport function collectSecretValues(secrets: Iterable<string | undefined>): string[] {\n const values = new Set<string>();\n const addOne = (value: string) => {\n if (value.length >= MIN_SECRET_LENGTH) values.add(value);\n };\n const add = (value: unknown) => {\n if (typeof value !== \"string\" || value.length < MIN_SECRET_LENGTH) return;\n addOne(value);\n // The same value as it appears in a URL, inside JSON, and base64-encoded\n try {\n addOne(encodeURIComponent(value));\n } catch {\n // Lone surrogates can't be encoded: the raw value is still covered\n }\n addOne(JSON.stringify(value).slice(1, -1));\n try {\n addOne(btoa(value).replace(/=+$/, \"\"));\n } catch {\n // Not Latin-1: no base64 form to worry about\n }\n };\n const walk = (value: unknown) => {\n if (typeof value === \"string\") add(value);\n else if (Array.isArray(value)) value.forEach(walk);\n else if (value && typeof value === \"object\") Object.values(value).forEach(walk);\n };\n for (const secret of secrets) {\n if (!secret) continue;\n add(secret);\n if (secret.trimStart().startsWith(\"{\")) {\n try {\n walk(JSON.parse(secret));\n } catch {\n // Not JSON: the whole value is already covered\n }\n }\n // Connection strings: drivers report the user or the password on their own\n const credentials = /^[a-z][a-z0-9+.-]*:\\/\\/([^\\s/@:]*):([^\\s/@]*)@/i.exec(secret);\n for (const part of credentials?.slice(1) ?? []) {\n add(part);\n try {\n add(decodeURIComponent(part));\n } catch {\n // Not percent-encoded\n }\n }\n }\n // Longest first, so a secret containing another one is fully redacted\n return [...values].sort((a, b) => b.length - a.length);\n}\n\n/**\n * Strip secret values, URL credentials, query strings, bearer-like tokens and anything shaped\n * like a well-known credential. Safe for text of any length: nothing is cut here, so a secret\n * can never be half-removed.\n */\nexport function redactText({ text, secretValues }: { text: string; secretValues: string[] }): string {\n let out = text;\n for (const secret of secretValues) out = out.split(secret).join(\"***\");\n out = out\n // user:pass@host\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/)[^\\s/@]+@/gi, \"$1***@\")\n // query strings\n .replace(/(\\b(?:https?|wss?):\\/\\/[^\\s?]+)\\?[^\\s\"')]*/gi, \"$1?***\")\n // bearer-ish tokens that slipped through\n .replace(/\\b(Bearer|Token|Basic|DeepL-Auth-Key)\\s+[A-Za-z0-9._~+/=*-]{8,}/gi, \"$1 ***\");\n for (const pattern of KEY_PATTERNS) out = out.replace(pattern, \"***\");\n return out;\n}\n\n/** Errors are shown on public pages: redact, then truncate (in that order) */\nexport function sanitizeError({\n message,\n secretValues,\n}: {\n message: string | undefined;\n secretValues: string[];\n}): string | undefined {\n if (!message) return undefined;\n const out = redactText({ text: message, secretValues });\n return out.length > MAX_LENGTH ? `${out.slice(0, MAX_LENGTH)}…` : out;\n}\n","import type { ResolvedService } from \"./config\";\nimport { combine, createHttp, errorMessage, measure, normalizeReturn } from \"./probe\";\nimport { redactText, sanitizeError } from \"./sanitize\";\nimport type { CheckCache, CheckContext, CheckOutcome, CheckResult, SecretsInput } from \"./types\";\n\n/** Extra time after `timeoutMs` before a check that ignores its abort signal is given up on */\nconst GRACE_MS = 1000;\n\nexport type RunServiceOptions = {\n service: ResolvedService;\n /** Every secret value of the whole configuration, for redaction */\n secretValues: string[];\n cache: CheckCache;\n now?: () => number;\n fetch?: typeof fetch;\n /** Report failed HTTP answers by status code only */\n hideResponseBodies?: boolean;\n};\n\n/**\n * Run one service and apply the rules shared by all of them:\n * - missing required secrets/fields → \"unconfigured\", the check is never called\n * - a throw or a timeout → down\n * - latency above `slowMs` turns \"up\" into \"degraded\"\n * - errors and every other text of the result are redacted before they leave the runner\n */\nexport async function runService({\n service,\n secretValues,\n cache,\n now = () => Date.now(),\n fetch: fetchImpl,\n hideResponseBodies,\n}: RunServiceOptions): Promise<CheckResult> {\n const checkedAt = now();\n if (service.missing.length > 0) {\n return { key: service.key, status: \"unconfigured\", checkedAt, probed: false };\n }\n\n const redact = (text: string) => redactText({ text, secretValues });\n const controller = new AbortController();\n const ctx: CheckContext<SecretsInput> = {\n secrets: service.secrets as Record<string, string>,\n http: createHttp({ timeoutMs: service.timeoutMs, signal: controller.signal, fetch: fetchImpl, redact, hideResponseBodies }),\n measure,\n combine,\n signal: controller.signal,\n timeoutMs: service.timeoutMs,\n slowMs: service.slowMs,\n cache,\n now: checkedAt,\n };\n\n const start = performance.now();\n let outcome: CheckOutcome;\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const returned = await Promise.race([\n Promise.resolve().then(() => service.check(ctx)),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n controller.abort(new DOMException(\"Timed out\", \"TimeoutError\"));\n reject(new DOMException(\"Timed out\", \"TimeoutError\"));\n }, service.timeoutMs + GRACE_MS);\n }),\n ]);\n outcome = normalizeReturn(returned);\n if (outcome.latencyMs === undefined && outcome.probed !== false && outcome.status !== \"sleeping\") {\n // Checks that don't measure themselves get the wall time of the whole check\n outcome = { ...outcome, latencyMs: Math.round(performance.now() - start) };\n }\n } catch (error) {\n outcome = { status: \"down\", error: errorMessage(error) };\n } finally {\n clearTimeout(timer);\n }\n\n let { status, error } = outcome;\n if (status === \"up\" && outcome.latencyMs !== undefined && outcome.latencyMs > service.slowMs) {\n status = \"degraded\";\n error ??= `Slow response: ${outcome.latencyMs} ms (threshold ${service.slowMs} ms)`;\n }\n\n return {\n key: service.key,\n status,\n checkedAt,\n probed: outcome.probed ?? true,\n latencyMs: outcome.latencyMs,\n httpStatus: outcome.httpStatus,\n error: sanitizeError({ message: error, secretValues }),\n // Everything below is public too: no text leaves the runner without being redacted\n metrics: outcome.metrics\n ? Object.fromEntries(\n Object.entries(outcome.metrics).map(([name, value]) => [\n redact(name),\n typeof value === \"string\" ? redact(value) : value,\n ])\n )\n : undefined,\n usage: outcome.usage?.length\n ? outcome.usage.map((usage) => ({\n ...usage,\n label: redact(usage.label),\n unit: usage.unit === undefined ? undefined : redact(usage.unit),\n }))\n : undefined,\n instances: outcome.instances?.length\n ? outcome.instances.map((instance) => ({\n ...instance,\n key: redact(instance.key),\n region: instance.region === undefined ? undefined : redact(instance.region),\n id: instance.id === undefined ? undefined : redact(instance.id),\n state: instance.state === undefined ? undefined : redact(instance.state),\n error: sanitizeError({ message: instance.error, secretValues }),\n }))\n : undefined,\n instanceLabels: outcome.instanceLabels\n ? {\n key: redact(outcome.instanceLabels.key),\n detail: outcome.instanceLabels.detail === undefined ? undefined : redact(outcome.instanceLabels.detail),\n }\n : undefined,\n };\n}\n\nexport async function runServices({\n services,\n cacheFor,\n ...rest\n}: Omit<RunServiceOptions, \"service\" | \"cache\"> & {\n services: ResolvedService[];\n /** Each service gets its own cache namespace */\n cacheFor: (key: string) => CheckCache;\n}): Promise<CheckResult[]> {\n return Promise.all(\n services.map((service) => runService({ service, cache: cacheFor(service.key), ...rest }))\n );\n}\n"]}
@@ -160,7 +160,11 @@ async function runService({
160
160
  id: instance.id === void 0 ? void 0 : redact(instance.id),
161
161
  state: instance.state === void 0 ? void 0 : redact(instance.state),
162
162
  error: sanitizeError({ message: instance.error, secretValues })
163
- })) : void 0
163
+ })) : void 0,
164
+ instanceLabels: outcome.instanceLabels ? {
165
+ key: redact(outcome.instanceLabels.key),
166
+ detail: outcome.instanceLabels.detail === void 0 ? void 0 : redact(outcome.instanceLabels.detail)
167
+ } : void 0
164
168
  };
165
169
  }
166
170
  async function runServices({
@@ -174,5 +178,5 @@ async function runServices({
174
178
  }
175
179
 
176
180
  export { collectSecretValues, redactText, runService, runServices, sanitizeError };
177
- //# sourceMappingURL=chunk-2M4UBCDM.js.map
178
- //# sourceMappingURL=chunk-2M4UBCDM.js.map
181
+ //# sourceMappingURL=chunk-R7BDAQOM.js.map
182
+ //# sourceMappingURL=chunk-R7BDAQOM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sanitize.ts","../src/runner.ts"],"names":["error"],"mappings":"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","file":"chunk-R7BDAQOM.js","sourcesContent":["const MAX_LENGTH = 300;\nconst MIN_SECRET_LENGTH = 6;\n\n/** Shapes of well-known credentials, redacted even when the value was never declared as a secret */\nconst KEY_PATTERNS: RegExp[] = [\n // PEM blocks (private keys, certificates), whole or cut short\n /-----BEGIN [A-Z0-9 ]+-----[\\s\\S]*?(-----END [A-Z0-9 ]+-----|$)/g,\n // JSON Web Tokens (OAuth access and identity tokens)\n /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}(\\.[A-Za-z0-9_-]*)?/g,\n // Prefixed API keys: OpenAI/Anthropic/Stripe (sk-, pk_, rk_), GitHub, Slack, Fly, MailerSend.\n // \"*\" is included because providers echo keys back partly masked (sk-proj-****abcd).\n /\\b(sk|pk|rk)[-_][A-Za-z0-9_*.-]{12,}/g,\n /\\b(gh[pousr]_|github_pat_)[A-Za-z0-9_*]{12,}/g,\n /\\bxox[abeoprs]-[A-Za-z0-9*-]{10,}/g,\n /\\b(FlyV1 |fm[12]_|fo1_)[A-Za-z0-9+/=_,*-]{12,}/g,\n /\\bmlsn\\.[A-Za-z0-9*]{12,}/g,\n // AWS access key ids, Google API keys, Google OAuth tokens\n /\\b(AKIA|ASIA)[0-9A-Z*]{12,}/g,\n /\\bAIza[0-9A-Za-z_*-]{20,}/g,\n /\\bya29\\.[0-9A-Za-z_*.-]{12,}/g,\n];\n\n/**\n * Every value worth redacting from a set of secrets: the values themselves, the string fields\n * of JSON secrets (e.g. the private key inside a service-account JSON), the user and password\n * of connection strings, and the encoded forms a value takes in URLs, JSON and Basic auth.\n */\nexport function collectSecretValues(secrets: Iterable<string | undefined>): string[] {\n const values = new Set<string>();\n const addOne = (value: string) => {\n if (value.length >= MIN_SECRET_LENGTH) values.add(value);\n };\n const add = (value: unknown) => {\n if (typeof value !== \"string\" || value.length < MIN_SECRET_LENGTH) return;\n addOne(value);\n // The same value as it appears in a URL, inside JSON, and base64-encoded\n try {\n addOne(encodeURIComponent(value));\n } catch {\n // Lone surrogates can't be encoded: the raw value is still covered\n }\n addOne(JSON.stringify(value).slice(1, -1));\n try {\n addOne(btoa(value).replace(/=+$/, \"\"));\n } catch {\n // Not Latin-1: no base64 form to worry about\n }\n };\n const walk = (value: unknown) => {\n if (typeof value === \"string\") add(value);\n else if (Array.isArray(value)) value.forEach(walk);\n else if (value && typeof value === \"object\") Object.values(value).forEach(walk);\n };\n for (const secret of secrets) {\n if (!secret) continue;\n add(secret);\n if (secret.trimStart().startsWith(\"{\")) {\n try {\n walk(JSON.parse(secret));\n } catch {\n // Not JSON: the whole value is already covered\n }\n }\n // Connection strings: drivers report the user or the password on their own\n const credentials = /^[a-z][a-z0-9+.-]*:\\/\\/([^\\s/@:]*):([^\\s/@]*)@/i.exec(secret);\n for (const part of credentials?.slice(1) ?? []) {\n add(part);\n try {\n add(decodeURIComponent(part));\n } catch {\n // Not percent-encoded\n }\n }\n }\n // Longest first, so a secret containing another one is fully redacted\n return [...values].sort((a, b) => b.length - a.length);\n}\n\n/**\n * Strip secret values, URL credentials, query strings, bearer-like tokens and anything shaped\n * like a well-known credential. Safe for text of any length: nothing is cut here, so a secret\n * can never be half-removed.\n */\nexport function redactText({ text, secretValues }: { text: string; secretValues: string[] }): string {\n let out = text;\n for (const secret of secretValues) out = out.split(secret).join(\"***\");\n out = out\n // user:pass@host\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/)[^\\s/@]+@/gi, \"$1***@\")\n // query strings\n .replace(/(\\b(?:https?|wss?):\\/\\/[^\\s?]+)\\?[^\\s\"')]*/gi, \"$1?***\")\n // bearer-ish tokens that slipped through\n .replace(/\\b(Bearer|Token|Basic|DeepL-Auth-Key)\\s+[A-Za-z0-9._~+/=*-]{8,}/gi, \"$1 ***\");\n for (const pattern of KEY_PATTERNS) out = out.replace(pattern, \"***\");\n return out;\n}\n\n/** Errors are shown on public pages: redact, then truncate (in that order) */\nexport function sanitizeError({\n message,\n secretValues,\n}: {\n message: string | undefined;\n secretValues: string[];\n}): string | undefined {\n if (!message) return undefined;\n const out = redactText({ text: message, secretValues });\n return out.length > MAX_LENGTH ? `${out.slice(0, MAX_LENGTH)}…` : out;\n}\n","import type { ResolvedService } from \"./config\";\nimport { combine, createHttp, errorMessage, measure, normalizeReturn } from \"./probe\";\nimport { redactText, sanitizeError } from \"./sanitize\";\nimport type { CheckCache, CheckContext, CheckOutcome, CheckResult, SecretsInput } from \"./types\";\n\n/** Extra time after `timeoutMs` before a check that ignores its abort signal is given up on */\nconst GRACE_MS = 1000;\n\nexport type RunServiceOptions = {\n service: ResolvedService;\n /** Every secret value of the whole configuration, for redaction */\n secretValues: string[];\n cache: CheckCache;\n now?: () => number;\n fetch?: typeof fetch;\n /** Report failed HTTP answers by status code only */\n hideResponseBodies?: boolean;\n};\n\n/**\n * Run one service and apply the rules shared by all of them:\n * - missing required secrets/fields → \"unconfigured\", the check is never called\n * - a throw or a timeout → down\n * - latency above `slowMs` turns \"up\" into \"degraded\"\n * - errors and every other text of the result are redacted before they leave the runner\n */\nexport async function runService({\n service,\n secretValues,\n cache,\n now = () => Date.now(),\n fetch: fetchImpl,\n hideResponseBodies,\n}: RunServiceOptions): Promise<CheckResult> {\n const checkedAt = now();\n if (service.missing.length > 0) {\n return { key: service.key, status: \"unconfigured\", checkedAt, probed: false };\n }\n\n const redact = (text: string) => redactText({ text, secretValues });\n const controller = new AbortController();\n const ctx: CheckContext<SecretsInput> = {\n secrets: service.secrets as Record<string, string>,\n http: createHttp({ timeoutMs: service.timeoutMs, signal: controller.signal, fetch: fetchImpl, redact, hideResponseBodies }),\n measure,\n combine,\n signal: controller.signal,\n timeoutMs: service.timeoutMs,\n slowMs: service.slowMs,\n cache,\n now: checkedAt,\n };\n\n const start = performance.now();\n let outcome: CheckOutcome;\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const returned = await Promise.race([\n Promise.resolve().then(() => service.check(ctx)),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n controller.abort(new DOMException(\"Timed out\", \"TimeoutError\"));\n reject(new DOMException(\"Timed out\", \"TimeoutError\"));\n }, service.timeoutMs + GRACE_MS);\n }),\n ]);\n outcome = normalizeReturn(returned);\n if (outcome.latencyMs === undefined && outcome.probed !== false && outcome.status !== \"sleeping\") {\n // Checks that don't measure themselves get the wall time of the whole check\n outcome = { ...outcome, latencyMs: Math.round(performance.now() - start) };\n }\n } catch (error) {\n outcome = { status: \"down\", error: errorMessage(error) };\n } finally {\n clearTimeout(timer);\n }\n\n let { status, error } = outcome;\n if (status === \"up\" && outcome.latencyMs !== undefined && outcome.latencyMs > service.slowMs) {\n status = \"degraded\";\n error ??= `Slow response: ${outcome.latencyMs} ms (threshold ${service.slowMs} ms)`;\n }\n\n return {\n key: service.key,\n status,\n checkedAt,\n probed: outcome.probed ?? true,\n latencyMs: outcome.latencyMs,\n httpStatus: outcome.httpStatus,\n error: sanitizeError({ message: error, secretValues }),\n // Everything below is public too: no text leaves the runner without being redacted\n metrics: outcome.metrics\n ? Object.fromEntries(\n Object.entries(outcome.metrics).map(([name, value]) => [\n redact(name),\n typeof value === \"string\" ? redact(value) : value,\n ])\n )\n : undefined,\n usage: outcome.usage?.length\n ? outcome.usage.map((usage) => ({\n ...usage,\n label: redact(usage.label),\n unit: usage.unit === undefined ? undefined : redact(usage.unit),\n }))\n : undefined,\n instances: outcome.instances?.length\n ? outcome.instances.map((instance) => ({\n ...instance,\n key: redact(instance.key),\n region: instance.region === undefined ? undefined : redact(instance.region),\n id: instance.id === undefined ? undefined : redact(instance.id),\n state: instance.state === undefined ? undefined : redact(instance.state),\n error: sanitizeError({ message: instance.error, secretValues }),\n }))\n : undefined,\n instanceLabels: outcome.instanceLabels\n ? {\n key: redact(outcome.instanceLabels.key),\n detail: outcome.instanceLabels.detail === undefined ? undefined : redact(outcome.instanceLabels.detail),\n }\n : undefined,\n };\n}\n\nexport async function runServices({\n services,\n cacheFor,\n ...rest\n}: Omit<RunServiceOptions, \"service\" | \"cache\"> & {\n services: ResolvedService[];\n /** Each service gets its own cache namespace */\n cacheFor: (key: string) => CheckCache;\n}): Promise<CheckResult[]> {\n return Promise.all(\n services.map((service) => runService({ service, cache: cacheFor(service.key), ...rest }))\n );\n}\n"]}
@@ -70,6 +70,11 @@ var RANGES = {
70
70
  "24h": { durationMs: DAY_MS, bucketMs: 5 * 60 * 1e3 },
71
71
  "7d": { durationMs: 7 * DAY_MS, bucketMs: 60 * 60 * 1e3 }
72
72
  };
73
+ function bucketFor(range, intervalMs) {
74
+ const { durationMs, bucketMs } = RANGES[range];
75
+ if (!intervalMs || !Number.isFinite(intervalMs)) return bucketMs;
76
+ return Math.min(durationMs, Math.max(bucketMs, Math.round(intervalMs)));
77
+ }
73
78
  function percentile(sorted, p) {
74
79
  if (!sorted.length) return null;
75
80
  const index = Math.min(sorted.length - 1, Math.ceil(p / 100 * sorted.length) - 1);
@@ -124,11 +129,12 @@ function bucketRuns({
124
129
  runs,
125
130
  range,
126
131
  now = Date.now(),
127
- scoring
132
+ scoring,
133
+ bucketMs = RANGES[range].bucketMs
128
134
  }) {
129
- const { durationMs, bucketMs } = RANGES[range];
135
+ const { durationMs } = RANGES[range];
130
136
  const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;
131
- const count = Math.round(durationMs / bucketMs);
137
+ const count = Math.ceil(durationMs / bucketMs);
132
138
  const start = end - count * bucketMs;
133
139
  const bars = Array.from({ length: count }, (_, i) => ({
134
140
  start: start + i * bucketMs,
@@ -153,11 +159,12 @@ function bucketRuns({
153
159
 
154
160
  exports.DEFAULT_SCORING = DEFAULT_SCORING;
155
161
  exports.RANGES = RANGES;
162
+ exports.bucketFor = bucketFor;
156
163
  exports.bucketRuns = bucketRuns;
157
164
  exports.computeScore = computeScore;
158
165
  exports.computeServiceStats = computeServiceStats;
159
166
  exports.labelForScore = labelForScore;
160
167
  exports.resolveScoring = resolveScoring;
161
168
  exports.toMs = toMs;
162
- //# sourceMappingURL=chunk-NP3YYVRZ.cjs.map
163
- //# sourceMappingURL=chunk-NP3YYVRZ.cjs.map
169
+ //# sourceMappingURL=chunk-ZAZ6VTGV.cjs.map
170
+ //# sourceMappingURL=chunk-ZAZ6VTGV.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/duration.ts","../src/score.ts","../src/history.ts"],"names":[],"mappings":"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","file":"chunk-ZAZ6VTGV.cjs","sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\n/** A number of milliseconds, or a string like \"30s\", \"5m\", \"1h\", \"7d\" */\nexport type Duration = number | `${number}${\"ms\" | \"s\" | \"m\" | \"h\" | \"d\"}`;\n\nexport function toMs(duration: Duration): number {\n if (typeof duration === \"number\") {\n if (!Number.isFinite(duration) || duration < 0) throw new Error(`Invalid duration: ${duration}`);\n return duration;\n }\n const match = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/.exec(duration.trim());\n if (!match) throw new Error(`Invalid duration: \"${duration}\" (use e.g. \"30s\", \"5m\", \"1h\", \"7d\")`);\n return Number(match[1]) * UNITS[match[2] as string]!;\n}\n","import type { ScoreLabel, Status } from \"./types\";\n\nexport type Scoring = {\n /** How much more a critical service weighs than a normal one (default 3) */\n criticalWeight: number;\n /** Value of a degraded service between 0 and 1 (default 0.6) */\n degradedValue: number;\n thresholds: {\n /** Score from which the system is \"operational\" (default 98) */\n operational: number;\n /** Score from which the system is \"degraded\" rather than \"outage\" (default 85) */\n degraded: number;\n };\n};\n\nexport const DEFAULT_SCORING: Scoring = {\n criticalWeight: 3,\n degradedValue: 0.6,\n thresholds: { operational: 98, degraded: 85 },\n};\n\nexport type ScoringInput = {\n criticalWeight?: number;\n degradedValue?: number;\n thresholds?: Partial<Scoring[\"thresholds\"]>;\n};\n\nexport function resolveScoring(input: ScoringInput = {}): Scoring {\n return {\n criticalWeight: input.criticalWeight ?? DEFAULT_SCORING.criticalWeight,\n degradedValue: input.degradedValue ?? DEFAULT_SCORING.degradedValue,\n thresholds: { ...DEFAULT_SCORING.thresholds, ...input.thresholds },\n };\n}\n\nexport function labelForScore(score: number | null, scoring: Scoring = DEFAULT_SCORING): ScoreLabel {\n if (score === null) return \"unknown\";\n if (score >= scoring.thresholds.operational) return \"operational\";\n if (score >= scoring.thresholds.degraded) return \"degraded\";\n return \"outage\";\n}\n\n/**\n * Weighted share of healthy services, 0–100. Sleeping services are expected (scale to zero)\n * and count as healthy; unconfigured ones are left out. Any critical service being down\n * forces the \"outage\" label, whatever the number.\n */\nexport function computeScore({\n results,\n scoring = DEFAULT_SCORING,\n}: {\n results: Array<{ status: Status; critical: boolean }>;\n scoring?: Scoring;\n}): { score: number | null; label: ScoreLabel; counts: Record<Status, number> } {\n const counts: Record<Status, number> = { up: 0, degraded: 0, down: 0, sleeping: 0, unconfigured: 0 };\n const values: Record<Exclude<Status, \"unconfigured\">, number> = {\n up: 1,\n sleeping: 1,\n degraded: scoring.degradedValue,\n down: 0,\n };\n let weighted = 0;\n let totalWeight = 0;\n let criticalDown = false;\n\n for (const { status, critical } of results) {\n counts[status] += 1;\n if (status === \"unconfigured\") continue;\n const weight = critical ? scoring.criticalWeight : 1;\n weighted += weight * values[status];\n totalWeight += weight;\n if (critical && status === \"down\") criticalDown = true;\n }\n\n if (totalWeight === 0) return { score: null, label: \"unknown\", counts };\n const score = Math.round((100 * weighted) / totalWeight);\n return { score, label: criticalDown ? \"outage\" : labelForScore(score, scoring), counts };\n}\n","import { labelForScore, type Scoring } from \"./score\";\nimport type { CheckSample, RunSummary } from \"./store\";\nimport type { HistoryBar, HistoryRange, InstanceStats, ScoreLabel, ServiceStats, Status } from \"./types\";\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** How far each range looks back, and its smallest bucket */\nexport const RANGES: Record<HistoryRange, { durationMs: number; bucketMs: number }> = {\n \"24h\": { durationMs: DAY_MS, bucketMs: 5 * 60 * 1000 },\n \"7d\": { durationMs: 7 * DAY_MS, bucketMs: 60 * 60 * 1000 },\n};\n\n/**\n * The width of one history bar: the check interval, so every bar holds one scheduled run\n * (a 15-minute interval gives 15-minute bars), and never less than the range's smallest\n * bucket, nor more than the range itself.\n */\nexport function bucketFor(range: HistoryRange, intervalMs?: number): number {\n const { durationMs, bucketMs } = RANGES[range];\n if (!intervalMs || !Number.isFinite(intervalMs)) return bucketMs;\n return Math.min(durationMs, Math.max(bucketMs, Math.round(intervalMs)));\n}\n\nfunction percentile(sorted: number[], p: number): number | null {\n if (!sorted.length) return null;\n const index = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);\n return sorted[Math.max(0, index)] ?? null;\n}\n\n/** Share of samples that were not \"down\", in percent with two decimals */\nfunction uptime(samples: Array<{ status: Status }>): number | null {\n if (!samples.length) return null;\n const available = samples.filter((sample) => sample.status !== \"down\").length;\n return Math.round((available / samples.length) * 10000) / 100;\n}\n\nconst measured = (sample: { status: Status; latencyMs?: number }) =>\n sample.latencyMs !== undefined && (sample.status === \"up\" || sample.status === \"degraded\");\n\n/** Uptime, latency percentiles, sparkline series and last failure of one service */\nexport function computeServiceStats({\n samples,\n now = Date.now(),\n}: {\n /** Samples of the whole retention window, oldest first */\n samples: CheckSample[];\n now?: number;\n}): ServiceStats {\n const day = samples.filter((sample) => sample.at >= now - DAY_MS);\n const week = samples.filter((sample) => sample.at >= now - 7 * DAY_MS);\n const latencies = day\n .filter(measured)\n .map((sample) => sample.latencyMs as number)\n .sort((a, b) => a - b);\n const lastFailure = [...samples]\n .reverse()\n .find((sample) => sample.status === \"down\" || sample.status === \"degraded\");\n\n // Same figures per instance, over the last 24h\n const byInstance = new Map<string, Array<{ status: Status; latencyMs?: number }>>();\n for (const sample of day) {\n for (const [key, [status, latencyMs]] of Object.entries(sample.instances ?? {})) {\n byInstance.set(key, [...(byInstance.get(key) ?? []), { status, latencyMs }]);\n }\n }\n const instances: Record<string, InstanceStats> | undefined = byInstance.size\n ? Object.fromEntries(\n [...byInstance].map(([key, list]) => [\n key,\n {\n uptime24h: uptime(list),\n p50: percentile(\n list\n .filter(measured)\n .map((item) => item.latencyMs as number)\n .sort((a, b) => a - b),\n 50\n ),\n samples24h: list.length,\n },\n ])\n )\n : undefined;\n\n return {\n uptime24h: uptime(day),\n uptime7d: uptime(week),\n p50: percentile(latencies, 50),\n p95: percentile(latencies, 95),\n samples24h: day.length,\n latency: day.map((sample) => sample.latencyMs ?? null),\n lastFailure: lastFailure\n ? { at: lastFailure.at, status: lastFailure.status, error: lastFailure.error }\n : undefined,\n instances,\n };\n}\n\nconst LABEL_SEVERITY: Record<ScoreLabel, number> = { unknown: 0, operational: 1, degraded: 2, outage: 3 };\n\n/** Group runs into fixed buckets; each bucket keeps its worst score, label and failures */\nexport function bucketRuns({\n runs,\n range,\n now = Date.now(),\n scoring,\n bucketMs = RANGES[range].bucketMs,\n}: {\n runs: RunSummary[];\n range: HistoryRange;\n now?: number;\n scoring?: Scoring;\n /** Width of one bar (default: the range's smallest bucket). See `bucketFor`. */\n bucketMs?: number;\n}): HistoryBar[] {\n const { durationMs } = RANGES[range];\n const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;\n // An interval that doesn't divide the range still covers all of it\n const count = Math.ceil(durationMs / bucketMs);\n const start = end - count * bucketMs;\n\n const bars: HistoryBar[] = Array.from({ length: count }, (_, i) => ({\n start: start + i * bucketMs,\n score: null,\n label: \"unknown\",\n runs: 0,\n failing: {},\n }));\n\n for (const run of runs) {\n const bar = bars[Math.floor((run.at - start) / bucketMs)];\n if (!bar) continue;\n bar.runs += 1;\n if (run.score !== null && (bar.score === null || run.score < bar.score)) bar.score = run.score;\n const label = run.label ?? labelForScore(run.score, scoring);\n if (LABEL_SEVERITY[label] > LABEL_SEVERITY[bar.label]) bar.label = label;\n for (const [key, status] of Object.entries(run.failing)) {\n // \"down\" wins over \"degraded\" within a bucket\n if (bar.failing[key] !== \"down\") bar.failing[key] = status;\n }\n }\n return bars;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { l as SecretsInput, f as CheckContext, d as CheckReturn, h as CommonOptions } from './types-CctvUAs2.cjs';
1
+ import { l as SecretsInput, f as CheckContext, d as CheckReturn, h as CommonOptions } from './types-BVa6vIKk.cjs';
2
2
 
3
3
  declare const DEFAULT_SLOW_MS = 2000;
4
4
  declare const DEFAULT_TIMEOUT_MS = 10000;
@@ -1,4 +1,4 @@
1
- import { l as SecretsInput, f as CheckContext, d as CheckReturn, h as CommonOptions } from './types-CctvUAs2.js';
1
+ import { l as SecretsInput, f as CheckContext, d as CheckReturn, h as CommonOptions } from './types-BVa6vIKk.js';
2
2
 
3
3
  declare const DEFAULT_SLOW_MS = 2000;
4
4
  declare const DEFAULT_TIMEOUT_MS = 10000;
@@ -1,5 +1,5 @@
1
- import { b as PresetOptions, S as ServiceDefinition } from './config-BEbP-uLf.cjs';
2
- import { l as SecretsInput, c as HttpResult, d as CheckReturn } from './types-CctvUAs2.cjs';
1
+ import { b as PresetOptions, S as ServiceDefinition } from './config-gro29syt.cjs';
2
+ import { l as SecretsInput, c as HttpResult, d as CheckReturn } from './types-BVa6vIKk.cjs';
3
3
 
4
4
  type GenericHttpOptions<Secrets extends SecretsInput = SecretsInput> = PresetOptions & {
5
5
  /** How to call it */
@@ -1,5 +1,5 @@
1
- import { b as PresetOptions, S as ServiceDefinition } from './config-w1tm1bdu.js';
2
- import { l as SecretsInput, c as HttpResult, d as CheckReturn } from './types-CctvUAs2.js';
1
+ import { b as PresetOptions, S as ServiceDefinition } from './config-iY18j9IA.js';
2
+ import { l as SecretsInput, c as HttpResult, d as CheckReturn } from './types-BVa6vIKk.js';
3
3
 
4
4
  type GenericHttpOptions<Secrets extends SecretsInput = SecretsInput> = PresetOptions & {
5
5
  /** How to call it */
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkNP3YYVRZ_cjs = require('./chunk-NP3YYVRZ.cjs');
3
+ var chunkZAZ6VTGV_cjs = require('./chunk-ZAZ6VTGV.cjs');
4
4
  var chunkELV4ZU6G_cjs = require('./chunk-ELV4ZU6G.cjs');
5
- var chunkPARE6EUZ_cjs = require('./chunk-PARE6EUZ.cjs');
5
+ var chunkNZTKXXW2_cjs = require('./chunk-NZTKXXW2.cjs');
6
6
  var chunkXHXEKB3Q_cjs = require('./chunk-XHXEKB3Q.cjs');
7
7
 
8
8
  // src/stores/memory.ts
@@ -104,12 +104,12 @@ function defineStatus(config) {
104
104
  const resolved = chunkXHXEKB3Q_cjs.resolveTree(config.services ?? []);
105
105
  const { services, tree } = resolved;
106
106
  const store = config.store ?? memoryStore({ now });
107
- const intervalMs = chunkNP3YYVRZ_cjs.toMs(config.interval ?? "5m");
108
- const retentionMs = chunkNP3YYVRZ_cjs.toMs(config.retention ?? "7d");
109
- const scoring = chunkNP3YYVRZ_cjs.resolveScoring(config.scoring);
107
+ const intervalMs = chunkZAZ6VTGV_cjs.toMs(config.interval ?? "5m");
108
+ const retentionMs = chunkZAZ6VTGV_cjs.toMs(config.retention ?? "7d");
109
+ const scoring = chunkZAZ6VTGV_cjs.resolveScoring(config.scoring);
110
110
  const warn = config.onWarning ?? ((message) => console.warn(`[sermon-js] ${message}`));
111
111
  for (const warning of resolved.warnings) warn(warning);
112
- const secretValues = chunkPARE6EUZ_cjs.collectSecretValues(
112
+ const secretValues = chunkNZTKXXW2_cjs.collectSecretValues(
113
113
  [...services.values()].flatMap((service) => [...Object.values(service.secrets), ...service.sensitive ?? []])
114
114
  );
115
115
  const fallback = memoryStore({ now });
@@ -137,7 +137,7 @@ function defineStatus(config) {
137
137
  return latest;
138
138
  }
139
139
  function scoreOf(latest) {
140
- return chunkNP3YYVRZ_cjs.computeScore({
140
+ return chunkZAZ6VTGV_cjs.computeScore({
141
141
  scoring,
142
142
  results: [...services.values()].filter((service) => latest[service.key]).map((service) => ({ status: latest[service.key].status, critical: service.critical }))
143
143
  });
@@ -151,7 +151,7 @@ function defineStatus(config) {
151
151
  await Promise.all(
152
152
  keys.map(async (key) => {
153
153
  const samples = await store.samples(key, { since: at - retentionMs, until: at });
154
- stats[key] = chunkNP3YYVRZ_cjs.computeServiceStats({ samples, now: at });
154
+ stats[key] = chunkZAZ6VTGV_cjs.computeServiceStats({ samples, now: at });
155
155
  })
156
156
  );
157
157
  for (const key of Object.keys(stats)) if (!services.has(key)) delete stats[key];
@@ -174,7 +174,7 @@ function defineStatus(config) {
174
174
  retentionMs,
175
175
  scoring,
176
176
  warnings: resolved.warnings,
177
- redact: (text) => chunkPARE6EUZ_cjs.redactText({ text, secretValues }),
177
+ redact: (text) => chunkNZTKXXW2_cjs.redactText({ text, secretValues }),
178
178
  has: (key) => services.has(key),
179
179
  lock,
180
180
  lastFullRun,
@@ -183,7 +183,7 @@ function defineStatus(config) {
183
183
  if (unknown.length) throw new Error(`sermon-js: unknown service key(s): ${unknown.join(", ")}`);
184
184
  const selected = keys ? keys.map((key) => services.get(key)) : [...services.values()];
185
185
  const full = !keys;
186
- const results = await chunkPARE6EUZ_cjs.runServices({
186
+ const results = await chunkNZTKXXW2_cjs.runServices({
187
187
  services: selected,
188
188
  secretValues,
189
189
  cacheFor: checkCache,
@@ -277,18 +277,19 @@ function defineStatus(config) {
277
277
  },
278
278
  async history({ range = "24h" } = {}) {
279
279
  const at = now();
280
- const runs = await store.runs({ since: at - chunkNP3YYVRZ_cjs.RANGES[range].durationMs, until: at });
280
+ const runs = await store.runs({ since: at - chunkZAZ6VTGV_cjs.RANGES[range].durationMs, until: at });
281
+ const bucketMs = chunkZAZ6VTGV_cjs.bucketFor(range, intervalMs);
281
282
  return {
282
283
  schemaVersion: 1,
283
284
  range,
284
- bucketMs: chunkNP3YYVRZ_cjs.RANGES[range].bucketMs,
285
- bars: chunkNP3YYVRZ_cjs.bucketRuns({ runs, range, now: at, scoring })
285
+ bucketMs,
286
+ bars: chunkZAZ6VTGV_cjs.bucketRuns({ runs, range, now: at, scoring, bucketMs })
286
287
  };
287
288
  },
288
289
  async serviceHistory(key, { range = "24h" } = {}) {
289
290
  if (!services.has(key)) throw new Error(`sermon-js: unknown service key: ${key}`);
290
291
  const at = now();
291
- const samples = await store.samples(key, { since: at - chunkNP3YYVRZ_cjs.RANGES[range].durationMs, until: at });
292
+ const samples = await store.samples(key, { since: at - chunkZAZ6VTGV_cjs.RANGES[range].durationMs, until: at });
292
293
  return { schemaVersion: 1, key, range, samples };
293
294
  }
294
295
  };
@@ -297,7 +298,7 @@ function defineStatus(config) {
297
298
 
298
299
  // src/scheduler.ts
299
300
  function startScheduler(status, options = {}) {
300
- const periodMs = chunkNP3YYVRZ_cjs.toMs(options.every ?? status.intervalMs);
301
+ const periodMs = chunkZAZ6VTGV_cjs.toMs(options.every ?? status.intervalMs);
301
302
  const jitter = Math.max(0, options.jitter ?? 0.1);
302
303
  let timer;
303
304
  let stopped = false;
@@ -326,31 +327,35 @@ function startScheduler(status, options = {}) {
326
327
 
327
328
  Object.defineProperty(exports, "DEFAULT_SCORING", {
328
329
  enumerable: true,
329
- get: function () { return chunkNP3YYVRZ_cjs.DEFAULT_SCORING; }
330
+ get: function () { return chunkZAZ6VTGV_cjs.DEFAULT_SCORING; }
330
331
  });
331
332
  Object.defineProperty(exports, "RANGES", {
332
333
  enumerable: true,
333
- get: function () { return chunkNP3YYVRZ_cjs.RANGES; }
334
+ get: function () { return chunkZAZ6VTGV_cjs.RANGES; }
335
+ });
336
+ Object.defineProperty(exports, "bucketFor", {
337
+ enumerable: true,
338
+ get: function () { return chunkZAZ6VTGV_cjs.bucketFor; }
334
339
  });
335
340
  Object.defineProperty(exports, "bucketRuns", {
336
341
  enumerable: true,
337
- get: function () { return chunkNP3YYVRZ_cjs.bucketRuns; }
342
+ get: function () { return chunkZAZ6VTGV_cjs.bucketRuns; }
338
343
  });
339
344
  Object.defineProperty(exports, "computeScore", {
340
345
  enumerable: true,
341
- get: function () { return chunkNP3YYVRZ_cjs.computeScore; }
346
+ get: function () { return chunkZAZ6VTGV_cjs.computeScore; }
342
347
  });
343
348
  Object.defineProperty(exports, "computeServiceStats", {
344
349
  enumerable: true,
345
- get: function () { return chunkNP3YYVRZ_cjs.computeServiceStats; }
350
+ get: function () { return chunkZAZ6VTGV_cjs.computeServiceStats; }
346
351
  });
347
352
  Object.defineProperty(exports, "labelForScore", {
348
353
  enumerable: true,
349
- get: function () { return chunkNP3YYVRZ_cjs.labelForScore; }
354
+ get: function () { return chunkZAZ6VTGV_cjs.labelForScore; }
350
355
  });
351
356
  Object.defineProperty(exports, "toMs", {
352
357
  enumerable: true,
353
- get: function () { return chunkNP3YYVRZ_cjs.toMs; }
358
+ get: function () { return chunkZAZ6VTGV_cjs.toMs; }
354
359
  });
355
360
  Object.defineProperty(exports, "generic", {
356
361
  enumerable: true,
@@ -358,11 +363,11 @@ Object.defineProperty(exports, "generic", {
358
363
  });
359
364
  Object.defineProperty(exports, "collectSecretValues", {
360
365
  enumerable: true,
361
- get: function () { return chunkPARE6EUZ_cjs.collectSecretValues; }
366
+ get: function () { return chunkNZTKXXW2_cjs.collectSecretValues; }
362
367
  });
363
368
  Object.defineProperty(exports, "sanitizeError", {
364
369
  enumerable: true,
365
- get: function () { return chunkPARE6EUZ_cjs.sanitizeError; }
370
+ get: function () { return chunkNZTKXXW2_cjs.sanitizeError; }
366
371
  });
367
372
  Object.defineProperty(exports, "DEFAULT_SLOW_MS", {
368
373
  enumerable: true,