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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sermon-js",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Service monitor: build status pages fast. Typed checks and presets, sleep-safe probes, scoring, history, a host-agnostic API and a React UI kit.",
5
5
  "license": "MIT",
6
6
  "author": "BSide-Tech",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sanitize.ts","../src/runner.ts"],"names":["error"],"mappings":"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","file":"chunk-2M4UBCDM.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 };\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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/duration.ts","../src/score.ts","../src/history.ts"],"names":[],"mappings":";AAAA,IAAM,KAAA,GAAgC;AAAA,EACpC,EAAA,EAAI,CAAA;AAAA,EACJ,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG,IAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAKO,SAAS,KAAK,QAAA,EAA4B;AAC/C,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,IAAK,QAAA,GAAW,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,QAAQ,CAAA,CAAE,CAAA;AAC/F,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,+BAAA,CAAgC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAClE,EAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,CAAA,mBAAA,EAAsB,QAAQ,CAAA,oCAAA,CAAsC,CAAA;AAChG,EAAA,OAAO,MAAA,CAAO,MAAM,CAAC,CAAC,IAAI,KAAA,CAAM,KAAA,CAAM,CAAC,CAAW,CAAA;AACpD;;;ACJO,IAAM,eAAA,GAA2B;AAAA,EACtC,cAAA,EAAgB,CAAA;AAAA,EAChB,aAAA,EAAe,GAAA;AAAA,EACf,UAAA,EAAY,EAAE,WAAA,EAAa,EAAA,EAAI,UAAU,EAAA;AAC3C;AAQO,SAAS,cAAA,CAAe,KAAA,GAAsB,EAAC,EAAY;AAChE,EAAA,OAAO;AAAA,IACL,cAAA,EAAgB,KAAA,CAAM,cAAA,IAAkB,eAAA,CAAgB,cAAA;AAAA,IACxD,aAAA,EAAe,KAAA,CAAM,aAAA,IAAiB,eAAA,CAAgB,aAAA;AAAA,IACtD,YAAY,EAAE,GAAG,gBAAgB,UAAA,EAAY,GAAG,MAAM,UAAA;AAAW,GACnE;AACF;AAEO,SAAS,aAAA,CAAc,KAAA,EAAsB,OAAA,GAAmB,eAAA,EAA6B;AAClG,EAAA,IAAI,KAAA,KAAU,MAAM,OAAO,SAAA;AAC3B,EAAA,IAAI,KAAA,IAAS,OAAA,CAAQ,UAAA,CAAW,WAAA,EAAa,OAAO,aAAA;AACpD,EAAA,IAAI,KAAA,IAAS,OAAA,CAAQ,UAAA,CAAW,QAAA,EAAU,OAAO,UAAA;AACjD,EAAA,OAAO,QAAA;AACT;AAOO,SAAS,YAAA,CAAa;AAAA,EAC3B,OAAA;AAAA,EACA,OAAA,GAAU;AACZ,CAAA,EAGgF;AAC9E,EAAA,MAAM,MAAA,GAAiC,EAAE,EAAA,EAAI,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,YAAA,EAAc,CAAA,EAAE;AACnG,EAAA,MAAM,MAAA,GAA0D;AAAA,IAC9D,EAAA,EAAI,CAAA;AAAA,IACJ,QAAA,EAAU,CAAA;AAAA,IACV,UAAU,OAAA,CAAQ,aAAA;AAAA,IAClB,IAAA,EAAM;AAAA,GACR;AACA,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,WAAA,GAAc,CAAA;AAClB,EAAA,IAAI,YAAA,GAAe,KAAA;AAEnB,EAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,QAAA,EAAS,IAAK,OAAA,EAAS;AAC1C,IAAA,MAAA,CAAO,MAAM,CAAA,IAAK,CAAA;AAClB,IAAA,IAAI,WAAW,cAAA,EAAgB;AAC/B,IAAA,MAAM,MAAA,GAAS,QAAA,GAAW,OAAA,CAAQ,cAAA,GAAiB,CAAA;AACnD,IAAA,QAAA,IAAY,MAAA,GAAS,OAAO,MAAM,CAAA;AAClC,IAAA,WAAA,IAAe,MAAA;AACf,IAAA,IAAI,QAAA,IAAY,MAAA,KAAW,MAAA,EAAQ,YAAA,GAAe,IAAA;AAAA,EACpD;AAEA,EAAA,IAAI,WAAA,KAAgB,GAAG,OAAO,EAAE,OAAO,IAAA,EAAM,KAAA,EAAO,WAAW,MAAA,EAAO;AACtE,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAO,GAAA,GAAM,WAAY,WAAW,CAAA;AACvD,EAAA,OAAO,EAAE,OAAO,KAAA,EAAO,YAAA,GAAe,WAAW,aAAA,CAAc,KAAA,EAAO,OAAO,CAAA,EAAG,MAAA,EAAO;AACzF;;;ACzEA,IAAM,MAAA,GAAS,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAEvB,IAAM,MAAA,GAAyE;AAAA,EACpF,OAAO,EAAE,UAAA,EAAY,QAAQ,QAAA,EAAU,CAAA,GAAI,KAAK,GAAA,EAAK;AAAA,EACrD,IAAA,EAAM,EAAE,UAAA,EAAY,CAAA,GAAI,QAAQ,QAAA,EAAU,EAAA,GAAK,KAAK,GAAA;AACtD;AAEA,SAAS,UAAA,CAAW,QAAkB,CAAA,EAA0B;AAC9D,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AAC3B,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,IAAA,CAAM,CAAA,GAAI,GAAA,GAAO,MAAA,CAAO,MAAM,IAAI,CAAC,CAAA;AAClF,EAAA,OAAO,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,CAAC,CAAA,IAAK,IAAA;AACvC;AAGA,SAAS,OAAO,OAAA,EAAmD;AACjE,EAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,EAAQ,OAAO,IAAA;AAC5B,EAAA,MAAM,SAAA,GAAY,QAAQ,MAAA,CAAO,CAAC,WAAW,MAAA,CAAO,MAAA,KAAW,MAAM,CAAA,CAAE,MAAA;AACvE,EAAA,OAAO,KAAK,KAAA,CAAO,SAAA,GAAY,OAAA,CAAQ,MAAA,GAAU,GAAK,CAAA,GAAI,GAAA;AAC5D;AAEA,IAAM,QAAA,GAAW,CAAC,MAAA,KAChB,MAAA,CAAO,SAAA,KAAc,WAAc,MAAA,CAAO,MAAA,KAAW,IAAA,IAAQ,MAAA,CAAO,MAAA,KAAW,UAAA,CAAA;AAG1E,SAAS,mBAAA,CAAoB;AAAA,EAClC,OAAA;AAAA,EACA,GAAA,GAAM,KAAK,GAAA;AACb,CAAA,EAIiB;AACf,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,CAAO,CAAC,WAAW,MAAA,CAAO,EAAA,IAAM,MAAM,MAAM,CAAA;AAChE,EAAA,MAAM,IAAA,GAAO,QAAQ,MAAA,CAAO,CAAC,WAAW,MAAA,CAAO,EAAA,IAAM,GAAA,GAAM,CAAA,GAAI,MAAM,CAAA;AACrE,EAAA,MAAM,YAAY,GAAA,CACf,MAAA,CAAO,QAAQ,CAAA,CACf,IAAI,CAAC,MAAA,KAAW,MAAA,CAAO,SAAmB,EAC1C,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AACvB,EAAA,MAAM,WAAA,GAAc,CAAC,GAAG,OAAO,EAC5B,OAAA,EAAQ,CACR,IAAA,CAAK,CAAC,WAAW,MAAA,CAAO,MAAA,KAAW,MAAA,IAAU,MAAA,CAAO,WAAW,UAAU,CAAA;AAG5E,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAA2D;AAClF,EAAA,KAAA,MAAW,UAAU,GAAA,EAAK;AACxB,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,CAAC,MAAA,EAAQ,SAAS,CAAC,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,SAAA,IAAa,EAAE,CAAA,EAAG;AAC/E,MAAA,UAAA,CAAW,GAAA,CAAI,GAAA,EAAK,CAAC,GAAI,WAAW,GAAA,CAAI,GAAG,CAAA,IAAK,EAAC,EAAI,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAC,CAAA;AAAA,IAC7E;AAAA,EACF;AACA,EAAA,MAAM,SAAA,GAAuD,UAAA,CAAW,IAAA,GACpE,MAAA,CAAO,WAAA;AAAA,IACL,CAAC,GAAG,UAAU,CAAA,CAAE,IAAI,CAAC,CAAC,GAAA,EAAK,IAAI,CAAA,KAAM;AAAA,MACnC,GAAA;AAAA,MACA;AAAA,QACE,SAAA,EAAW,OAAO,IAAI,CAAA;AAAA,QACtB,GAAA,EAAK,UAAA;AAAA,UACH,IAAA,CACG,MAAA,CAAO,QAAQ,CAAA,CACf,IAAI,CAAC,IAAA,KAAS,IAAA,CAAK,SAAmB,EACtC,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AAAA,UACvB;AAAA,SACF;AAAA,QACA,YAAY,IAAA,CAAK;AAAA;AACnB,KACD;AAAA,GACH,GACA,MAAA;AAEJ,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,OAAO,GAAG,CAAA;AAAA,IACrB,QAAA,EAAU,OAAO,IAAI,CAAA;AAAA,IACrB,GAAA,EAAK,UAAA,CAAW,SAAA,EAAW,EAAE,CAAA;AAAA,IAC7B,GAAA,EAAK,UAAA,CAAW,SAAA,EAAW,EAAE,CAAA;AAAA,IAC7B,YAAY,GAAA,CAAI,MAAA;AAAA,IAChB,SAAS,GAAA,CAAI,GAAA,CAAI,CAAC,MAAA,KAAW,MAAA,CAAO,aAAa,IAAI,CAAA;AAAA,IACrD,WAAA,EAAa,WAAA,GACT,EAAE,EAAA,EAAI,WAAA,CAAY,EAAA,EAAI,MAAA,EAAQ,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO,WAAA,CAAY,KAAA,EAAM,GAC3E,MAAA;AAAA,IACJ;AAAA,GACF;AACF;AAEA,IAAM,cAAA,GAA6C,EAAE,OAAA,EAAS,CAAA,EAAG,aAAa,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAE;AAGjG,SAAS,UAAA,CAAW;AAAA,EACzB,IAAA;AAAA,EACA,KAAA;AAAA,EACA,GAAA,GAAM,KAAK,GAAA,EAAI;AAAA,EACf;AACF,CAAA,EAKiB;AACf,EAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAS,GAAI,OAAO,KAAK,CAAA;AAC7C,EAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,QAAQ,IAAI,QAAA,GAAW,QAAA;AACpD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,UAAA,GAAa,QAAQ,CAAA;AAC9C,EAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,GAAQ,QAAA;AAE5B,EAAA,MAAM,IAAA,GAAqB,MAAM,IAAA,CAAK,EAAE,QAAQ,KAAA,EAAM,EAAG,CAAC,CAAA,EAAG,CAAA,MAAO;AAAA,IAClE,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA;AAAA,IACnB,KAAA,EAAO,IAAA;AAAA,IACP,KAAA,EAAO,SAAA;AAAA,IACP,IAAA,EAAM,CAAA;AAAA,IACN,SAAS;AAAC,GACZ,CAAE,CAAA;AAEF,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAM,GAAA,GAAM,KAAK,IAAA,CAAK,KAAA,CAAA,CAAO,IAAI,EAAA,GAAK,KAAA,IAAS,QAAQ,CAAC,CAAA;AACxD,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,GAAA,CAAI,IAAA,IAAQ,CAAA;AACZ,IAAA,IAAI,GAAA,CAAI,KAAA,KAAU,IAAA,KAAS,GAAA,CAAI,KAAA,KAAU,IAAA,IAAQ,GAAA,CAAI,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAA,EAAQ,GAAA,CAAI,KAAA,GAAQ,GAAA,CAAI,KAAA;AACzF,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,IAAS,aAAA,CAAc,GAAA,CAAI,OAAO,OAAO,CAAA;AAC3D,IAAA,IAAI,cAAA,CAAe,KAAK,CAAA,GAAI,cAAA,CAAe,IAAI,KAAK,CAAA,MAAO,KAAA,GAAQ,KAAA;AACnE,IAAA,KAAA,MAAW,CAAC,KAAK,MAAM,CAAA,IAAK,OAAO,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AAEvD,MAAA,IAAI,GAAA,CAAI,QAAQ,GAAG,CAAA,KAAM,QAAQ,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,IACtD;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT","file":"chunk-APC2HVSI.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\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\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}: {\n runs: RunSummary[];\n range: HistoryRange;\n now?: number;\n scoring?: Scoring;\n}): HistoryBar[] {\n const { durationMs, bucketMs } = RANGES[range];\n const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;\n const count = Math.round(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 +0,0 @@
1
- {"version":3,"sources":["../src/duration.ts","../src/score.ts","../src/history.ts"],"names":[],"mappings":"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","file":"chunk-NP3YYVRZ.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\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\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}: {\n runs: RunSummary[];\n range: HistoryRange;\n now?: number;\n scoring?: Scoring;\n}): HistoryBar[] {\n const { durationMs, bucketMs } = RANGES[range];\n const end = Math.floor(now / bucketMs) * bucketMs + bucketMs;\n const count = Math.round(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 +0,0 @@
1
- {"version":3,"sources":["../src/sanitize.ts","../src/runner.ts"],"names":["createHttp","measure","combine","normalizeReturn","error","errorMessage"],"mappings":";;;;;AAAA,IAAM,UAAA,GAAa,GAAA;AACnB,IAAM,iBAAA,GAAoB,CAAA;AAG1B,IAAM,YAAA,GAAyB;AAAA;AAAA,EAE7B,iEAAA;AAAA;AAAA,EAEA,+DAAA;AAAA;AAAA;AAAA,EAGA,uCAAA;AAAA,EACA,+CAAA;AAAA,EACA,oCAAA;AAAA,EACA,iDAAA;AAAA,EACA,4BAAA;AAAA;AAAA,EAEA,8BAAA;AAAA,EACA,4BAAA;AAAA,EACA;AACF,CAAA;AAOO,SAAS,oBAAoB,OAAA,EAAiD;AACnF,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KAAkB;AAChC,IAAA,IAAI,KAAA,CAAM,MAAA,IAAU,iBAAA,EAAmB,MAAA,CAAO,IAAI,KAAK,CAAA;AAAA,EACzD,CAAA;AACA,EAAA,MAAM,GAAA,GAAM,CAAC,KAAA,KAAmB;AAC9B,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,SAAS,iBAAA,EAAmB;AACnE,IAAA,MAAA,CAAO,KAAK,CAAA;AAEZ,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,kBAAA,CAAmB,KAAK,CAAC,CAAA;AAAA,IAClC,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,MAAA,CAAO,KAAK,SAAA,CAAU,KAAK,EAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AACzC,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,IACvC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA;AACA,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KAAmB;AAC/B,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,GAAA,CAAI,KAAK,CAAA;AAAA,SAAA,IAC/B,MAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAA,CAAM,QAAQ,IAAI,CAAA;AAAA,SAAA,IACxC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,SAAiB,MAAA,CAAO,KAAK,CAAA,CAAE,OAAA,CAAQ,IAAI,CAAA;AAAA,EAChF,CAAA;AACA,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,GAAA,CAAI,MAAM,CAAA;AACV,IAAA,IAAI,MAAA,CAAO,SAAA,EAAU,CAAE,UAAA,CAAW,GAAG,CAAA,EAAG;AACtC,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,MAAM,CAAC,CAAA;AAAA,MACzB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAEA,IAAA,MAAM,WAAA,GAAc,iDAAA,CAAkD,IAAA,CAAK,MAAM,CAAA;AACjF,IAAA,KAAA,MAAW,QAAQ,WAAA,EAAa,KAAA,CAAM,CAAC,CAAA,IAAK,EAAC,EAAG;AAC9C,MAAA,GAAA,CAAI,IAAI,CAAA;AACR,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,kBAAA,CAAmB,IAAI,CAAC,CAAA;AAAA,MAC9B,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,CAAC,GAAG,MAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAM,CAAA;AACvD;AAOO,SAAS,UAAA,CAAW,EAAE,IAAA,EAAM,YAAA,EAAa,EAAqD;AACnG,EAAA,IAAI,GAAA,GAAM,IAAA;AACV,EAAA,KAAA,MAAW,MAAA,IAAU,cAAc,GAAA,GAAM,GAAA,CAAI,MAAM,MAAM,CAAA,CAAE,KAAK,KAAK,CAAA;AACrE,EAAA,GAAA,GAAM,GAAA,CAEH,OAAA,CAAQ,uCAAA,EAAyC,QAAQ,CAAA,CAEzD,OAAA,CAAQ,8CAAA,EAAgD,QAAQ,CAAA,CAEhE,OAAA,CAAQ,mEAAA,EAAqE,QAAQ,CAAA;AACxF,EAAA,KAAA,MAAW,WAAW,YAAA,EAAc,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,SAAS,KAAK,CAAA;AACpE,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,aAAA,CAAc;AAAA,EAC5B,OAAA;AAAA,EACA;AACF,CAAA,EAGuB;AACrB,EAAA,IAAI,CAAC,SAAS,OAAO,MAAA;AACrB,EAAA,MAAM,MAAM,UAAA,CAAW,EAAE,IAAA,EAAM,OAAA,EAAS,cAAc,CAAA;AACtD,EAAA,OAAO,GAAA,CAAI,SAAS,UAAA,GAAa,CAAA,EAAG,IAAI,KAAA,CAAM,CAAA,EAAG,UAAU,CAAC,CAAA,MAAA,CAAA,GAAM,GAAA;AACpE;;;ACtGA,IAAM,QAAA,GAAW,GAAA;AAoBjB,eAAsB,UAAA,CAAW;AAAA,EAC/B,OAAA;AAAA,EACA,YAAA;AAAA,EACA,KAAA;AAAA,EACA,GAAA,GAAM,MAAM,IAAA,CAAK,GAAA,EAAI;AAAA,EACrB,KAAA,EAAO,SAAA;AAAA,EACP;AACF,CAAA,EAA4C;AAC1C,EAAA,MAAM,YAAY,GAAA,EAAI;AACtB,EAAA,IAAI,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAC9B,IAAA,OAAO,EAAE,KAAK,OAAA,CAAQ,GAAA,EAAK,QAAQ,cAAA,EAAgB,SAAA,EAAW,QAAQ,KAAA,EAAM;AAAA,EAC9E;AAEA,EAAA,MAAM,SAAS,CAAC,IAAA,KAAiB,WAAW,EAAE,IAAA,EAAM,cAAc,CAAA;AAClE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,GAAA,GAAkC;AAAA,IACtC,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,IAAA,EAAMA,4BAAA,CAAW,EAAE,SAAA,EAAW,OAAA,CAAQ,SAAA,EAAW,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,oBAAoB,CAAA;AAAA,aAC1HC,yBAAA;AAAA,aACAC,yBAAA;AAAA,IACA,QAAQ,UAAA,CAAW,MAAA;AAAA,IACnB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,KAAA;AAAA,IACA,GAAA,EAAK;AAAA,GACP;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,GAAA,EAAI;AAC9B,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,MAClC,OAAA,CAAQ,SAAQ,CAAE,IAAA,CAAK,MAAM,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAC,CAAA;AAAA,MAC/C,IAAI,OAAA,CAAe,CAAC,CAAA,EAAG,MAAA,KAAW;AAChC,QAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,UAAA,UAAA,CAAW,KAAA,CAAM,IAAI,YAAA,CAAa,WAAA,EAAa,cAAc,CAAC,CAAA;AAC9D,UAAA,MAAA,CAAO,IAAI,YAAA,CAAa,WAAA,EAAa,cAAc,CAAC,CAAA;AAAA,QACtD,CAAA,EAAG,OAAA,CAAQ,SAAA,GAAY,QAAQ,CAAA;AAAA,MACjC,CAAC;AAAA,KACF,CAAA;AACD,IAAA,OAAA,GAAUC,kCAAgB,QAAQ,CAAA;AAClC,IAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,CAAA,IAAa,OAAA,CAAQ,WAAW,KAAA,IAAS,OAAA,CAAQ,WAAW,UAAA,EAAY;AAEhG,MAAA,OAAA,GAAU,EAAE,GAAG,OAAA,EAAS,SAAA,EAAW,IAAA,CAAK,MAAM,WAAA,CAAY,GAAA,EAAI,GAAI,KAAK,CAAA,EAAE;AAAA,IAC3E;AAAA,EACF,SAASC,MAAAA,EAAO;AACd,IAAA,OAAA,GAAU,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAOC,8BAAA,CAAaD,MAAK,CAAA,EAAE;AAAA,EACzD,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB;AAEA,EAAA,IAAI,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,OAAA;AACxB,EAAA,IAAI,MAAA,KAAW,QAAQ,OAAA,CAAQ,SAAA,KAAc,UAAa,OAAA,CAAQ,SAAA,GAAY,QAAQ,MAAA,EAAQ;AAC5F,IAAA,MAAA,GAAS,UAAA;AACT,IAAA,KAAA,KAAU,CAAA,eAAA,EAAkB,OAAA,CAAQ,SAAS,CAAA,eAAA,EAAkB,QAAQ,MAAM,CAAA,IAAA,CAAA;AAAA,EAC/E;AAEA,EAAA,OAAO;AAAA,IACL,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,MAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA,EAAQ,QAAQ,MAAA,IAAU,IAAA;AAAA,IAC1B,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,YAAY,OAAA,CAAQ,UAAA;AAAA,IACpB,OAAO,aAAA,CAAc,EAAE,OAAA,EAAS,KAAA,EAAO,cAAc,CAAA;AAAA;AAAA,IAErD,OAAA,EAAS,OAAA,CAAQ,OAAA,GACb,MAAA,CAAO,WAAA;AAAA,MACL,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA,CAAE,IAAI,CAAC,CAAC,IAAA,EAAM,KAAK,CAAA,KAAM;AAAA,QACrD,OAAO,IAAI,CAAA;AAAA,QACX,OAAO,KAAA,KAAU,QAAA,GAAW,MAAA,CAAO,KAAK,CAAA,GAAI;AAAA,OAC7C;AAAA,KACH,GACA,MAAA;AAAA,IACJ,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,GAClB,QAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MAC5B,GAAG,KAAA;AAAA,MACH,KAAA,EAAO,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA;AAAA,MACzB,MAAM,KAAA,CAAM,IAAA,KAAS,SAAY,MAAA,GAAY,MAAA,CAAO,MAAM,IAAI;AAAA,MAC9D,CAAA,GACF,MAAA;AAAA,IACJ,SAAA,EAAW,QAAQ,SAAA,EAAW,MAAA,GAC1B,QAAQ,SAAA,CAAU,GAAA,CAAI,CAAC,QAAA,MAAc;AAAA,MACnC,GAAG,QAAA;AAAA,MACH,GAAA,EAAK,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA;AAAA,MACxB,QAAQ,QAAA,CAAS,MAAA,KAAW,SAAY,MAAA,GAAY,MAAA,CAAO,SAAS,MAAM,CAAA;AAAA,MAC1E,IAAI,QAAA,CAAS,EAAA,KAAO,SAAY,MAAA,GAAY,MAAA,CAAO,SAAS,EAAE,CAAA;AAAA,MAC9D,OAAO,QAAA,CAAS,KAAA,KAAU,SAAY,MAAA,GAAY,MAAA,CAAO,SAAS,KAAK,CAAA;AAAA,MACvE,OAAO,aAAA,CAAc,EAAE,SAAS,QAAA,CAAS,KAAA,EAAO,cAAc;AAAA,MAC9D,CAAA,GACF;AAAA,GACN;AACF;AAEA,eAAsB,WAAA,CAAY;AAAA,EAChC,QAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAI2B;AACzB,EAAA,OAAO,OAAA,CAAQ,GAAA;AAAA,IACb,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,KAAY,WAAW,EAAE,OAAA,EAAS,KAAA,EAAO,QAAA,CAAS,QAAQ,GAAG,CAAA,EAAG,GAAG,IAAA,EAAM,CAAC;AAAA,GAC1F;AACF","file":"chunk-PARE6EUZ.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 };\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"]}