@aglyn/tenant-data-admin 1.0.0-beta.224 → 1.0.0-beta.226

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.
@@ -46,7 +46,20 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
46
46
  *
47
47
  * A site that fails `threshold` runs in a row raises
48
48
  * `system.siteRenderFailing` once; the first passing run after that raises
49
- * `system.siteRenderRecovered` once. The state lives in
49
+ * `system.siteRenderRecovered` once.
50
+ *
51
+ * ## A challenge is not a failed render (AGL-3580)
52
+ *
53
+ * When bot protection answers instead of the site, this run saw nothing about
54
+ * the site at all. On 2026-10-05 the console had no `AGLYN_PROBE_TOKEN`, every
55
+ * `/search` probe got the 429 checkpoint for three hours, and the monitor told
56
+ * the operator "Pages are not rendering on aglyn.com" and then "rendering
57
+ * again after 3 h" about a site that was serving pages the whole time. So a
58
+ * run whose only failures are challenges is BLIND: it leaves the site's
59
+ * verdict and failure count exactly as they were, and `threshold` blind runs
60
+ * in a row raise `system.renderMonitorBlind` once, which names the setup
61
+ * problem instead of an outage. A run with a real failure beside a challenge
62
+ * is still a failure, because the real one is evidence. The state lives in
50
63
  * `operatorHealthState/render-monitor--<host>`, beside every other health
51
64
  * check, so Staff → Operator alerts lists it with its status and since when.
52
65
  *
@@ -330,7 +343,10 @@ function spellingLetters(host) {
330
343
  ];
331
344
  }
332
345
  /** The state document id for one origin. */ export function renderMonitorCheckId(origin) {
333
- return `${RENDER_MONITOR_CHECK_PREFIX}${new URL(origin).host}`;
346
+ return renderMonitorHostCheckId(new URL(origin).host);
347
+ }
348
+ /** The state document id for one host, as {@link renderMonitorCheckId} names it. */ export function renderMonitorHostCheckId(host) {
349
+ return `${RENDER_MONITOR_CHECK_PREFIX}${host}`;
334
350
  }
335
351
  /**
336
352
  * The next state for one site, given what this run saw. Pure.
@@ -339,15 +355,47 @@ function spellingLetters(host) {
339
355
  * failed, so one slow cold start or one dropped connection says nothing. It
340
356
  * turns back on the first passing run, because a render that works is
341
357
  * proof, and a recovery held back is a recovery nobody hears about.
358
+ *
359
+ * A BLIND observation (every failure was a bot-protection challenge) changes
360
+ * nothing about the site: its status, failure count and since-time carry over
361
+ * untouched, and only the blind count moves. It transitions to `blind` once,
362
+ * on the `threshold`-th blind run in a row.
342
363
  */ export function nextRenderMonitorState(prior, observation, context) {
343
364
  var _observation_detail, _ref, _ref1, _ref2, _ref3;
344
365
  const { origin, label, threshold, now } = context;
345
366
  const checkId = renderMonitorCheckId(origin);
346
367
  const detail = String((_observation_detail = observation.detail) != null ? _observation_detail : '').slice(0, 1000);
347
368
  const wasDegraded = (prior == null ? void 0 : prior.status) === 'degraded';
348
- if (observation.ok) {
369
+ if (!observation.ok && observation.blind) {
370
+ var _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10;
371
+ const blindRuns = ((_ref4 = prior == null ? void 0 : prior.consecutiveBlindRuns) != null ? _ref4 : 0) + 1;
349
372
  return {
350
373
  next: {
374
+ checkId,
375
+ label,
376
+ origin,
377
+ status: wasDegraded ? 'degraded' : 'ok',
378
+ sinceMs: (_ref5 = prior == null ? void 0 : prior.sinceMs) != null ? _ref5 : now,
379
+ detail: `The monitor cannot see this site: ${detail}`.slice(0, 1000),
380
+ updatedAtMs: now,
381
+ consecutiveFailures: (_ref6 = prior == null ? void 0 : prior.consecutiveFailures) != null ? _ref6 : 0,
382
+ failingSinceMs: (_ref7 = prior == null ? void 0 : prior.failingSinceMs) != null ? _ref7 : null,
383
+ lastOkAtMs: (_ref8 = prior == null ? void 0 : prior.lastOkAtMs) != null ? _ref8 : null,
384
+ consecutiveBlindRuns: blindRuns,
385
+ blindSinceMs: (_ref9 = prior == null ? void 0 : prior.blindSinceMs) != null ? _ref9 : now,
386
+ lastObservedAtMs: (_ref10 = prior == null ? void 0 : prior.lastObservedAtMs) != null ? _ref10 : null
387
+ },
388
+ transition: blindRuns === Math.max(1, threshold) ? 'blind' : null
389
+ };
390
+ }
391
+ const sighted = {
392
+ consecutiveBlindRuns: 0,
393
+ blindSinceMs: null,
394
+ lastObservedAtMs: now
395
+ };
396
+ if (observation.ok) {
397
+ return {
398
+ next: _extends({
351
399
  checkId,
352
400
  label,
353
401
  origin,
@@ -358,7 +406,7 @@ function spellingLetters(host) {
358
406
  consecutiveFailures: 0,
359
407
  failingSinceMs: null,
360
408
  lastOkAtMs: now
361
- },
409
+ }, sighted),
362
410
  transition: wasDegraded ? 'recovered' : null
363
411
  };
364
412
  }
@@ -366,7 +414,7 @@ function spellingLetters(host) {
366
414
  const failingSinceMs = (_ref1 = prior == null ? void 0 : prior.failingSinceMs) != null ? _ref1 : now;
367
415
  const degraded = wasDegraded || failures >= Math.max(1, threshold);
368
416
  return {
369
- next: {
417
+ next: _extends({
370
418
  checkId,
371
419
  label,
372
420
  origin,
@@ -377,7 +425,7 @@ function spellingLetters(host) {
377
425
  consecutiveFailures: failures,
378
426
  failingSinceMs,
379
427
  lastOkAtMs: (_ref3 = prior == null ? void 0 : prior.lastOkAtMs) != null ? _ref3 : null
380
- },
428
+ }, sighted),
381
429
  transition: degraded && !wasDegraded ? 'failing' : null
382
430
  };
383
431
  }
@@ -505,14 +553,16 @@ function probeLine(row) {
505
553
  ...pageProbe
506
554
  ]);
507
555
  const ok = probes.every((probe)=>probe.ok);
556
+ const failing = probes.filter((probe)=>!probe.ok);
557
+ const blind = !ok && failing.every((probe)=>probe.challenged);
508
558
  const result = {
509
559
  origin,
510
560
  ok,
561
+ blind,
511
562
  probes
512
563
  };
513
564
  if (dryRun) return result;
514
565
  const host = new URL(origin).host;
515
- const failing = probes.filter((probe)=>!probe.ok);
516
566
  const detail = ok ? 'Fresh renders pass.' : failing.map(probeLine).join('; ');
517
567
  try {
518
568
  const now = clock();
@@ -520,7 +570,8 @@ function probeLine(row) {
520
570
  var _ref, _ref1;
521
571
  const decided = nextRenderMonitorState(prior, {
522
572
  ok,
523
- detail
573
+ detail,
574
+ blind
524
575
  }, {
525
576
  origin,
526
577
  label: `Page rendering on ${host}`,
@@ -548,6 +599,16 @@ function probeLine(row) {
548
599
  detail
549
600
  }
550
601
  });
602
+ } else if (transition === 'blind') {
603
+ var _next_consecutiveBlindRuns;
604
+ await raise('system.renderMonitorBlind', {
605
+ dedupeKey: host,
606
+ context: {
607
+ site: host,
608
+ runs: (_next_consecutiveBlindRuns = next.consecutiveBlindRuns) != null ? _next_consecutiveBlindRuns : threshold,
609
+ detail
610
+ }
611
+ });
551
612
  } else if (transition === 'recovered') {
552
613
  await raise('system.siteRenderRecovered', {
553
614
  dedupeKey: host,
@@ -569,5 +630,141 @@ function probeLine(row) {
569
630
  sites
570
631
  };
571
632
  }
633
+ /**
634
+ * A verdict older than this says the monitor stopped, not that the site did.
635
+ * Four missed five-minute runs.
636
+ */ export const RENDER_PAGES_STALE_MS = 20 * 60000;
637
+ /** How many sites one `?site=` may ask about. */ export const RENDER_PAGES_MAX_SITES = 10;
638
+ /**
639
+ * Which watched hosts a `?site=` asks about. Pure.
640
+ *
641
+ * Absent or empty asks about every watched site. A host that is not watched
642
+ * is kept, so the caller can answer it red: a public monitor pointed at a
643
+ * site nobody renders is a monitor that can never go red, which is the
644
+ * mistake this door exists to undo.
645
+ */ export function requestedRenderPagesHosts(query, watchedOrigins) {
646
+ const asked = String(query != null ? query : '').split(/[\s,]+/).map((entry)=>entry.trim().toLowerCase().replace(/^https?:\/\//, '').replace(/\/.*$/, '')).filter((entry)=>/^[a-z0-9-]+(\.[a-z0-9-]+)+$/.test(entry));
647
+ const hosts = asked.length ? asked : watchedOrigins.map((origin)=>new URL(origin).host);
648
+ return [
649
+ ...new Set(hosts)
650
+ ].slice(0, RENDER_PAGES_MAX_SITES);
651
+ }
652
+ /**
653
+ * The public verdict on real pages, per site, from the render monitor's
654
+ * stored state (AGL-3580). Pure.
655
+ *
656
+ * This is what the status page's `Published sites` and `Marketing site`
657
+ * monitors read. Before it they read `/api/health/render/*`, which builds a
658
+ * node tree inside a route handler and never draws a page, and both stayed at
659
+ * 100% through two outages on 2026-10-05 in which every fresh page hung.
660
+ *
661
+ * Red is reserved for the one thing a visitor would notice:
662
+ *
663
+ * - `not-rendering` — the monitor saw the site fail to render pages it could
664
+ * not have served from a cache, `threshold` runs in a row.
665
+ * - `not-watched` — the monitor renders nothing for this host, so this row
666
+ * could never go red; fix the monitor's config or the URL.
667
+ * - `state-unavailable` — the verdicts could not be read at all.
668
+ *
669
+ * Green with a code is a verdict this door cannot improve on, and is never
670
+ * reported as an outage, because it is not one:
671
+ *
672
+ * - `awaiting-first-run` — a site added since the monitor last ran.
673
+ * - `monitor-blind` — bot protection answered the monitor; the site's last
674
+ * real verdict stands, and the operator alert says what to fix.
675
+ * - `observation-stale` — no run in {@link RENDER_PAGES_STALE_MS}; the
676
+ * monitor's own `render-monitor` row on `/api/health/crons` is what goes
677
+ * red for that, so a stopped scheduler is never told as a site outage.
678
+ */ export function renderPagesHealth(hosts, watchedOrigins, states, ms, now = Date.now()) {
679
+ const watched = new Set(watchedOrigins.map((origin)=>new URL(origin).host));
680
+ const iso = (value)=>typeof value === 'number' && Number.isFinite(value) ? new Date(value).toISOString() : null;
681
+ const checks = {};
682
+ if (!hosts.length) {
683
+ checks['sites'] = {
684
+ ok: false,
685
+ ms,
686
+ code: 'no-sites-watched',
687
+ lastObservedAt: null,
688
+ since: null
689
+ };
690
+ return checks;
691
+ }
692
+ for (const host of hosts){
693
+ var _ref, _state_lastObservedAtMs, _state_consecutiveBlindRuns, _state_updatedAtMs;
694
+ const blank = {
695
+ ms,
696
+ lastObservedAt: null,
697
+ since: null
698
+ };
699
+ if (!watched.has(host)) {
700
+ checks[host] = _extends({}, blank, {
701
+ ok: false,
702
+ code: 'not-watched'
703
+ });
704
+ continue;
705
+ }
706
+ if (states === null) {
707
+ checks[host] = _extends({}, blank, {
708
+ ok: false,
709
+ code: 'state-unavailable'
710
+ });
711
+ continue;
712
+ }
713
+ const state = states.get(renderMonitorHostCheckId(host));
714
+ if (!state) {
715
+ checks[host] = _extends({}, blank, {
716
+ ok: true,
717
+ code: 'awaiting-first-run'
718
+ });
719
+ continue;
720
+ }
721
+ const observed = (_ref = (_state_lastObservedAtMs = state.lastObservedAtMs) != null ? _state_lastObservedAtMs : state.lastOkAtMs) != null ? _ref : null;
722
+ const row = {
723
+ ms,
724
+ lastObservedAt: iso(observed),
725
+ since: iso(state.sinceMs)
726
+ };
727
+ if (state.status === 'degraded') {
728
+ checks[host] = _extends({}, row, {
729
+ ok: false,
730
+ code: 'not-rendering'
731
+ });
732
+ } else if (((_state_consecutiveBlindRuns = state.consecutiveBlindRuns) != null ? _state_consecutiveBlindRuns : 0) > 0) {
733
+ checks[host] = _extends({}, row, {
734
+ ok: true,
735
+ code: 'monitor-blind'
736
+ });
737
+ } else if (now - ((_state_updatedAtMs = state.updatedAtMs) != null ? _state_updatedAtMs : 0) > RENDER_PAGES_STALE_MS) {
738
+ checks[host] = _extends({}, row, {
739
+ ok: true,
740
+ code: 'observation-stale'
741
+ });
742
+ } else {
743
+ checks[host] = _extends({}, row, {
744
+ ok: true
745
+ });
746
+ }
747
+ }
748
+ return checks;
749
+ }
750
+ /**
751
+ * Read the stored verdicts for `hosts`, one batched read. Null when the read
752
+ * fails, which {@link renderPagesHealth} reports red rather than calm.
753
+ */ export async function readRenderMonitorStates(hosts) {
754
+ try {
755
+ const { default: firebaseAdmin } = await import("./firebase-admin.js");
756
+ const db = firebaseAdmin.app().firestore();
757
+ const refs = hosts.map((host)=>db.collection(RENDER_MONITOR_COLLECTION).doc(renderMonitorHostCheckId(host)));
758
+ const snapshots = refs.length ? await db.getAll(...refs) : [];
759
+ const states = new Map();
760
+ for (const snapshot of snapshots){
761
+ if (snapshot.exists) states.set(snapshot.id, snapshot.data());
762
+ }
763
+ return states;
764
+ } catch (error) {
765
+ console.error('[render-monitor] stored verdicts could not be read', error);
766
+ return null;
767
+ }
768
+ }
572
769
 
573
770
  //# sourceMappingURL=render-monitor.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/render-monitor.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TENANT_APEX } from '@aglyn/aglyn/app-utils/tenant-apex'\nimport type { OperatorHealthStateDoc } from './operator-health'\n\n/**\n * THE RENDER MONITOR (AGL-3568): does a published site still render a page\n * that was not already in a cache?\n *\n * On 2026-10-05 every uncached page render on the published-site runtime\n * hung to the platform's 60 s limit for 28 minutes, and nothing alerted. The\n * three monitors that existed each missed it for its own reason:\n *\n * - the GitHub uptime probe is scheduled every 15 minutes and GitHub ran it\n * four times that day, none of them during the outage;\n * - its page rows fetch `/`, which the ISR cache answers, so a visitor's\n * page that was cached still loaded and the row stayed green;\n * - `/api/health/render/site` runs the page LOADER in-process and never the\n * site layout, which is where the request was stuck.\n *\n * So this fetches pages over HTTP, from the console (a different deployment\n * from the one it watches), on Cloud Scheduler every five minutes, and asks\n * for two pages no cache can answer:\n *\n * - `dynamic` — `/search`, a route every site has that renders on every\n * request (`force-dynamic`) inside the full site layout. A 200 that is a\n * complete document proves the layout and head ran just now.\n * - `isr` — a path nobody has ever requested, under the catch-all page.\n * A never-seen path is the one request the ISR cache cannot answer, so it\n * renders in the same static context every published page renders in,\n * which is the context the 2026-09-09 outage only appeared in. It is a\n * 404 by construction, and the site's own not-found page is drawn inside\n * the same layout, so a complete 404 document is a pass.\n *\n * A site that fails `threshold` runs in a row raises\n * `system.siteRenderFailing` once; the first passing run after that raises\n * `system.siteRenderRecovered` once. The state lives in\n * `operatorHealthState/render-monitor--<host>`, beside every other health\n * check, so Staff → Operator alerts lists it with its status and since when.\n *\n * Nothing here names an Aglyn host for anyone else: the default target is the\n * install's own demonstration site label under its own tenant apex, the\n * convention the middleware and the render canaries already follow. The one\n * exception is `PLATFORM_RENDER_PAGES` below, which applies on Aglyn's own\n * console deployment and nowhere else.\n *\n * ## Real pages, rendered fresh (AGL-3571)\n *\n * The two probes above draw the layout. They do not draw a page BODY, and\n * beta.223 hung only on page bodies with an image in them (an ancestor walk\n * looped on the page root, AGL-3565): `demo` has no images and the not-found\n * page has no body, so this monitor read green through it. So a third probe,\n * `page`, renders a real published page through the full ISR path — through\n * the tenant's `*.vercel.app` production domain (`RENDER_MONITOR_RENDER_ORIGIN`),\n * which honors `?tenantHost=` and sits behind Deployment Protection, naming\n * the site with a spelling no cache holds (`freshHostSpelling`). The cache key\n * includes the `[host]` segment, and `normalizeHostAlias` resolves every\n * spelling to the same site. Without the automation bypass the render origin\n * answers its sign-in redirect, so no `page` probe is made at all.\n *\n * One page per site per run, rotating through that site's list: a platform\n * break fails every page with an image, so every run, and alerts as fast as\n * before; a break on one page alone is seen on its turn.\n */\n\n/** The state documents' collection: the health checks' own. */\nconst RENDER_MONITOR_COLLECTION = 'operatorHealthState'\n\n/** Every render monitor document id starts with this. */\nexport const RENDER_MONITOR_CHECK_PREFIX = 'render-monitor--'\n\n/** The beat id `/api/health/crons` reads for the scheduled run. */\nexport const RENDER_MONITOR_JOB_ID = 'render-monitor'\n\n/**\n * How long one page fetch may take. Well under the 60 s a platform function\n * may run, so a hung render is graded here as a timeout rather than waiting\n * for the platform's 504, and well under the route's own `maxDuration`.\n */\nexport const RENDER_MONITOR_TIMEOUT_MS = 25_000\n\n/** Failing runs in a row before the alert goes out. */\nexport const RENDER_MONITOR_DEFAULT_THRESHOLD = 2\n\n/**\n * The prefix of the never-requested path the `isr` probe asks for. A\n * lowercase slug on purpose: it must reach the catch-all page like any\n * screen slug would, not a reserved or rewritten route.\n */\nexport const RENDER_PROBE_ISR_PREFIX = '/aglyn-render-probe-'\n\nexport type RenderProbeKind = 'dynamic' | 'isr' | 'page'\n\nexport interface RenderProbeSpec {\n kind: RenderProbeKind\n /** The path to request, given a fresh nonce. */\n path: (nonce: string) => string\n /** The status a healthy render answers with. */\n expectStatus: number\n}\n\nexport const RENDER_PROBES: readonly RenderProbeSpec[] = [\n { kind: 'dynamic', path: () => '/search', expectStatus: 200 },\n {\n kind: 'isr',\n path: (nonce) => `${RENDER_PROBE_ISR_PREFIX}${nonce}`,\n expectStatus: 404,\n },\n]\n\n/** One fetched response, reduced to what grading reads. */\nexport interface RenderProbeResponse {\n status: number\n contentType?: string | null\n body?: string\n location?: string | null\n /** `x-vercel-cache` on Vercel, `x-nextjs-cache` on a plain `next start`. */\n cacheState?: string | null\n}\n\nexport interface RenderProbeVerdict {\n ok: boolean\n /** Bot protection answered instead of the site. */\n challenged: boolean\n /** A stable code; `ok` on a pass. */\n code: string\n detail: string\n}\n\n/** Vercel's challenge interstitial, matched on the body like the front doors. */\nconst CHECKPOINT_MARKER = /Vercel Security Checkpoint/i\n\n/**\n * Cache states that mean the bytes came out of a store. A probe meant to\n * prove a render cannot pass on one of these, whatever the status.\n */\nconst CACHED_STATES = new Set(['HIT', 'STALE', 'PRERENDER'])\n\n/**\n * Grade one probe response. Pure.\n *\n * The page markers are the front doors' (`tools/scripts/lib/front-door.mjs`)\n * and for their reason: structural, never site copy. `</html>` proves the\n * document finished streaming, `/_next/static/` that our build produced it.\n */\nexport function gradeRenderProbe(\n response: RenderProbeResponse,\n expectStatus: number,\n): RenderProbeVerdict {\n const body = response.body ?? ''\n if (CHECKPOINT_MARKER.test(body)) {\n return {\n ok: false,\n challenged: true,\n code: 'challenged',\n detail: `bot protection answered (HTTP ${response.status}) instead of the site; check AGLYN_PROBE_TOKEN on the console`,\n }\n }\n if (response.status >= 300 && response.status < 400) {\n return {\n ok: false,\n challenged: false,\n code: 'redirected',\n detail: `redirected to ${response.location ?? '?'}, so no page was rendered`,\n }\n }\n if (response.status !== expectStatus) {\n return {\n ok: false,\n challenged: false,\n code: `http-${response.status}`,\n detail: `HTTP ${response.status}`,\n }\n }\n if (!/text\\/html/i.test(response.contentType ?? '')) {\n return {\n ok: false,\n challenged: false,\n code: 'not-html',\n detail: `not HTML (${response.contentType || 'no content-type'})`,\n }\n }\n if (!body.includes('</html>')) {\n return {\n ok: false,\n challenged: false,\n code: 'incomplete',\n detail: 'the document never finished (no </html>)',\n }\n }\n if (!body.includes('/_next/static/')) {\n return {\n ok: false,\n challenged: false,\n code: 'not-our-render',\n detail: 'no Next asset references, so the app did not render it',\n }\n }\n const cache = String(response.cacheState ?? '').trim().toUpperCase()\n if (CACHED_STATES.has(cache)) {\n return {\n ok: false,\n challenged: false,\n code: 'served-from-cache',\n detail: `served from cache (${cache}), so it proves no render`,\n }\n }\n return { ok: true, challenged: false, code: 'ok', detail: 'rendered' }\n}\n\n/**\n * The pages Aglyn's own console renders fresh when `RENDER_MONITOR_PAGES` is\n * unset: the canary's real client pages (`tools/scripts/lib/prod-canary.mjs`\n * `DEFAULT_TENANT_HOSTS`), images, reusable components, repeats and a form\n * between them. Applied ONLY on that console deployment (`VERCEL_PROJECT_ID`),\n * so no other install ever requests them.\n */\nexport const PLATFORM_RENDER_PAGES = [\n 'ready-to-roll.aglyn.app/',\n 'edr-construction.aglyn.app/',\n 'edr-construction.aglyn.app/services',\n 'edr-construction.aglyn.app/contact',\n].join(' ')\n\n/** Aglyn's console project on Vercel, the one deployment the list above is for. */\nexport const PLATFORM_CONSOLE_VERCEL_PROJECT_ID = 'prj_gEzxEXc0Lhs81rmaXIg2a1GbsDfl'\n\n/**\n * The tenant's production project domain on Vercel: follows every promote and\n * rollback, honors `?tenantHost=`, and is reachable only with the bypass.\n */\nexport const PLATFORM_RENDER_ORIGIN = 'https://aglyn-tenant-aglyn.vercel.app'\n\n/** The middleware's custom-domain sentinel (`apps/tenant/utils/get-host.ts`). */\nconst CNAME_HOST_PREFIX = 'cname--'\n\n/** Letters past this are never recased, so the bit arithmetic stays in 32 bits. */\nconst MAX_SPELLING_BITS = 30\n\nfunction spellingLetters(host: string): number[] {\n return [...host.toLowerCase()]\n .map((char, index) => (/[a-z]/.test(char) ? index : -1))\n .filter((index) => index >= 0)\n .slice(0, MAX_SPELLING_BITS)\n}\n\n/**\n * Spelling number `n` of one site: a `?tenantHost=` value that resolves to\n * that site and is a `[host]` route segment no other number produces, so the\n * page it asks for has no cache entry to come from.\n *\n * - `{sub}.{apex}` → the full name, letters upper-cased by the bits of `n`,\n * then one trailing dot per exhausted round of cases.\n * - any other name → `cname--{name}`, recased the same way and cycling after\n * `2 ** letters` (that form keeps trailing dots), with the prefix left\n * lower-case because the canonical-domain redirect tests the raw segment.\n *\n * MUST stay identical to `freshHostSpelling` in\n * `tools/scripts/lib/prod-canary.mjs`; `apps/tenant/specs/probe-host-spelling.spec.ts`\n * pins both against `normalizeHostAlias`.\n */\nexport function freshHostSpelling(host: string, n: number, apex: string = TENANT_APEX): string {\n const subdomain = host.endsWith(`.${apex}`)\n const prefix = subdomain ? '' : CNAME_HOST_PREFIX\n const chars = [...host.toLowerCase()]\n const letters = spellingLetters(host)\n const capacity = 2 ** letters.length\n const value = Math.max(0, Math.floor(Number(n) || 0))\n const bits = value % capacity\n letters.forEach((index, bit) => {\n if (Math.floor(bits / 2 ** bit) % 2 === 1) chars[index] = chars[index].toUpperCase()\n })\n const dots = subdomain ? '.'.repeat(Math.floor(value / capacity)) : ''\n return `${prefix}${chars.join('')}${dots}`\n}\n\n/**\n * The spelling number for a run at `nowMs`: minutes since 2026-10-01, so it\n * never repeats on one deployment — a `{sub}.aglyn.app` name of 18 letters\n * gains its first trailing dot after half a year.\n */\nexport function renderSpellingNumber(nowMs: number): number {\n return Math.max(1, Math.floor((nowMs - Date.UTC(2026, 9, 1)) / 60_000))\n}\n\n/** One watched site's real pages. */\nexport interface RenderMonitorPages {\n /** The site's public origin, which keys its state document. */\n origin: string\n host: string\n paths: string[]\n}\n\n/**\n * `\"a.example.app b.example.app/services, https://b.example.app/contact\"` →\n * one entry per host, paths merged, `/` when an entry names none. Entries that\n * are not a hostname are dropped, as `RENDER_MONITOR_ORIGINS` drops them.\n */\nexport function parseRenderMonitorPages(text: string | undefined | null): RenderMonitorPages[] {\n const byHost = new Map<string, string[]>()\n for (const raw of String(text ?? '').split(/[\\s,]+/)) {\n const entry = raw.trim().replace(/^https?:\\/\\//i, '')\n if (!entry) continue\n const slash = entry.indexOf('/')\n const host = (slash === -1 ? entry : entry.slice(0, slash)).toLowerCase()\n if (!/^[a-z0-9-]+(\\.[a-z0-9-]+)+$/.test(host)) continue\n const path = slash === -1 ? '/' : entry.slice(slash).replace(/\\/+$/, '') || '/'\n const paths = byHost.get(host) ?? []\n if (!paths.includes(path)) paths.push(path)\n byHost.set(host, paths)\n }\n return [...byHost].map(([host, paths]) => ({ origin: `https://${host}`, host, paths }))\n}\n\n/**\n * Which real pages the monitor renders, and through which origin.\n *\n * `RENDER_MONITOR_PAGES` (same grammar as the canary's `CANARY_TENANT_HOSTS`)\n * and `RENDER_MONITOR_RENDER_ORIGIN`, else — on Aglyn's own console only —\n * `PLATFORM_RENDER_PAGES` through `PLATFORM_RENDER_ORIGIN`. `off` (here or in\n * `RENDER_MONITOR_ORIGINS`) renders none. No render origin, no pages.\n */\nexport function resolveRenderMonitorPages(\n env: Readonly<Record<string, string | undefined>>,\n): { renderOrigin: string; pages: RenderMonitorPages[] } {\n const off = /^(off|none|false|0)$/i\n const raw = String(env['RENDER_MONITOR_PAGES'] ?? '').trim()\n if (off.test(raw) || off.test(String(env['RENDER_MONITOR_ORIGINS'] ?? '').trim())) {\n return { renderOrigin: '', pages: [] }\n }\n const platform = env['VERCEL_PROJECT_ID'] === PLATFORM_CONSOLE_VERCEL_PROJECT_ID\n const renderOrigin =\n originOf(env['RENDER_MONITOR_RENDER_ORIGIN']) || (platform ? PLATFORM_RENDER_ORIGIN : '')\n const pages = parseRenderMonitorPages(raw || (platform ? PLATFORM_RENDER_PAGES : ''))\n return renderOrigin ? { renderOrigin, pages } : { renderOrigin: '', pages: [] }\n}\n\n/** A configured value as an origin, or '' when it is not one. */\nexport function originOf(value: string | undefined | null): string {\n const trimmed = String(value ?? '').trim().replace(/\\/+$/, '')\n return /^https?:\\/\\/[^/\\s?#]+$/i.test(trimmed) ? trimmed.toLowerCase() : ''\n}\n\n/**\n * Which sites the monitor renders, from the console's environment.\n *\n * 1. The install's demonstration site: `OPERATOR_HEALTH_TENANT_ORIGIN` when\n * set (the one published site the operator alerts tick already watches),\n * else `https://<AGLYN_TENANT_DEMO or demo>.<tenant apex>`.\n * 2. Plus every origin in `RENDER_MONITOR_ORIGINS`, comma separated.\n *\n * `RENDER_MONITOR_ORIGINS=off` watches nothing, for an install with no\n * published site to watch. Values that are not origins are dropped.\n */\nexport function resolveRenderMonitorTargets(\n env: Readonly<Record<string, string | undefined>>,\n): string[] {\n const raw = String(env['RENDER_MONITOR_ORIGINS'] ?? '').trim()\n if (/^(off|none|false|0)$/i.test(raw)) return []\n const apex =\n String(env['NEXT_PUBLIC_TENANT_DOMAIN'] ?? '').trim() || TENANT_APEX\n const label = String(env['AGLYN_TENANT_DEMO'] ?? '').trim() || 'demo'\n const demo =\n originOf(env['OPERATOR_HEALTH_TENANT_ORIGIN']) ||\n originOf(`https://${label}.${apex}`)\n const listed = raw\n .split(/[\\s,]+/)\n .map((value) => originOf(value))\n .filter(Boolean)\n // A site whose pages are rendered is watched whole: its layout probes too.\n const paged = resolveRenderMonitorPages(env).pages.map((page) => page.origin)\n return [...new Set([demo, ...listed, ...paged].filter(Boolean))]\n}\n\n/** The state document id for one origin. */\nexport function renderMonitorCheckId(origin: string): string {\n return `${RENDER_MONITOR_CHECK_PREFIX}${new URL(origin).host}`\n}\n\n/** One render monitor state document. */\nexport interface RenderMonitorStateDoc extends OperatorHealthStateDoc {\n origin: string\n /** Failing runs in a row, reset by any passing run. */\n consecutiveFailures: number\n /** When the current run of failures began; null while passing. */\n failingSinceMs: number | null\n lastOkAtMs: number | null\n}\n\nexport type RenderMonitorTransition = 'failing' | 'recovered' | null\n\n/**\n * The next state for one site, given what this run saw. Pure.\n *\n * The status turns `degraded` only once `threshold` runs in a row have\n * failed, so one slow cold start or one dropped connection says nothing. It\n * turns back on the first passing run, because a render that works is\n * proof, and a recovery held back is a recovery nobody hears about.\n */\nexport function nextRenderMonitorState(\n prior: Partial<RenderMonitorStateDoc> | null,\n observation: { ok: boolean; detail: string },\n context: { origin: string; label: string; threshold: number; now: number },\n): { next: RenderMonitorStateDoc; transition: RenderMonitorTransition } {\n const { origin, label, threshold, now } = context\n const checkId = renderMonitorCheckId(origin)\n const detail = String(observation.detail ?? '').slice(0, 1_000)\n const wasDegraded = prior?.status === 'degraded'\n if (observation.ok) {\n return {\n next: {\n checkId,\n label,\n origin,\n status: 'ok',\n sinceMs: !wasDegraded && prior?.sinceMs ? prior.sinceMs : now,\n detail,\n updatedAtMs: now,\n consecutiveFailures: 0,\n failingSinceMs: null,\n lastOkAtMs: now,\n },\n transition: wasDegraded ? 'recovered' : null,\n }\n }\n const failures = (prior?.consecutiveFailures ?? 0) + 1\n const failingSinceMs = prior?.failingSinceMs ?? now\n const degraded = wasDegraded || failures >= Math.max(1, threshold)\n return {\n next: {\n checkId,\n label,\n origin,\n status: degraded ? 'degraded' : 'ok',\n sinceMs: degraded\n ? wasDegraded && prior?.sinceMs\n ? prior.sinceMs\n : failingSinceMs\n : (prior?.sinceMs ?? now),\n detail,\n updatedAtMs: now,\n consecutiveFailures: failures,\n failingSinceMs,\n lastOkAtMs: prior?.lastOkAtMs ?? null,\n },\n transition: degraded && !wasDegraded ? 'failing' : null,\n }\n}\n\n/** One probe as the report shows it. */\nexport interface RenderProbeRow extends RenderProbeVerdict {\n kind: RenderProbeKind\n url: string\n ms: number\n httpStatus: number | null\n}\n\n/** One site's result for a run. */\nexport interface RenderMonitorSiteResult {\n origin: string\n ok: boolean\n probes: RenderProbeRow[]\n /** The state written; absent on a dry run or a failed write. */\n status?: 'ok' | 'degraded'\n consecutiveFailures?: number\n transition?: RenderMonitorTransition\n error?: string\n}\n\nexport interface RenderMonitorReport {\n dryRun: boolean\n threshold: number\n sites: RenderMonitorSiteResult[]\n}\n\n/** The read-modify-write of one site's state, as one transaction. */\nexport type RenderMonitorStore = <T>(\n checkId: string,\n work: (prior: Partial<RenderMonitorStateDoc> | null) => {\n next: RenderMonitorStateDoc\n result: T\n },\n) => Promise<T>\n\n/**\n * The default store: one Firestore transaction on the site's document, so\n * two overlapping runs cannot both decide they saw the edge.\n */\nconst firestoreStore: RenderMonitorStore = async (checkId, work) => {\n const { default: firebaseAdmin } = await import('./firebase-admin')\n const db = firebaseAdmin.app().firestore()\n const ref = db.collection(RENDER_MONITOR_COLLECTION).doc(checkId)\n return db.runTransaction(async (transaction: any) => {\n const snapshot = await transaction.get(ref)\n const prior = snapshot.exists\n ? (snapshot.data() as Partial<RenderMonitorStateDoc>)\n : null\n const { next, result } = work(prior)\n transaction.set(ref, next)\n return result\n })\n}\n\ntype RaiseAlert = (\n type: string,\n options: { dedupeKey?: string; context?: Record<string, string | number> },\n) => Promise<unknown>\n\nconst defaultRaise: RaiseAlert = async (type, options) => {\n const { raiseOperatorAlert } = await import('./operator-alerts')\n return raiseOperatorAlert(type, options)\n}\n\nfunction describeDuration(ms: number): string {\n const minutes = Math.max(1, Math.round(ms / 60_000))\n if (minutes < 60) return `${minutes} min`\n const hours = Math.floor(minutes / 60)\n const rest = minutes % 60\n return rest ? `${hours} h ${rest} min` : `${hours} h`\n}\n\n/**\n * The `page` probe's URL for one run: this run's page of the site's list\n * (rotating every five-minute slot), under this run's spelling of the site.\n */\nexport function pageProbeUrl(\n renderOrigin: string,\n site: RenderMonitorPages,\n slot: number,\n spelling: number,\n): string {\n const path = site.paths[Math.abs(slot) % site.paths.length] ?? '/'\n const url = new URL(path, renderOrigin)\n url.searchParams.set('tenantHost', freshHostSpelling(site.host, spelling))\n return url.toString()\n}\n\nfunction probeLine(row: RenderProbeRow): string {\n return `${row.kind} ${new URL(row.url).pathname}: ${row.detail} (${row.ms} ms)`\n}\n\n/** Fetch and grade one probe. Never throws. */\nasync function runProbe(\n url: string,\n spec: { kind: RenderProbeKind; expectStatus: number },\n options: {\n fetcher: typeof fetch\n headers: Record<string, string>\n timeoutMs: number\n clock: () => number\n },\n): Promise<RenderProbeRow> {\n const startedAt = options.clock()\n try {\n const response = await options.fetcher(url, {\n redirect: 'manual',\n cache: 'no-store',\n headers: {\n 'user-agent': 'aglyn-render-monitor',\n accept: 'text/html,application/xhtml+xml',\n ...options.headers,\n },\n signal: AbortSignal.timeout(options.timeoutMs),\n })\n const body = await response.text()\n const verdict = gradeRenderProbe(\n {\n status: response.status,\n contentType: response.headers.get('content-type'),\n body,\n location: response.headers.get('location'),\n cacheState:\n response.headers.get('x-vercel-cache') ??\n response.headers.get('x-nextjs-cache'),\n },\n spec.expectStatus,\n )\n return {\n kind: spec.kind,\n url,\n ms: options.clock() - startedAt,\n httpStatus: response.status,\n ...verdict,\n }\n } catch (error) {\n const name = (error as { name?: string } | null)?.name\n const timedOut = name === 'TimeoutError' || name === 'AbortError'\n return {\n kind: spec.kind,\n url,\n ms: options.clock() - startedAt,\n httpStatus: null,\n ok: false,\n challenged: false,\n code: timedOut ? 'timeout' : 'unreachable',\n detail: timedOut\n ? `no complete page in ${Math.round(options.timeoutMs / 1_000)} s`\n : `unreachable (${(error as { cause?: { code?: string } } | null)?.cause?.code ?? name ?? 'error'})`,\n }\n }\n}\n\n/**\n * One run: render every target, record each site's state, raise the edge.\n *\n * A dry run fetches and grades and writes nothing, so a person can ask\n * \"what would the monitor see right now\" without moving its state.\n */\nexport async function runRenderMonitor(\n options: {\n dryRun?: boolean\n env?: Readonly<Record<string, string | undefined>>\n fetcher?: typeof fetch\n /** Our own edge's bypass headers, from the caller's environment. */\n headers?: Record<string, string>\n now?: () => number\n nonce?: () => string\n timeoutMs?: number\n store?: RenderMonitorStore\n raise?: RaiseAlert\n } = {},\n): Promise<RenderMonitorReport> {\n const env = options.env ?? process.env\n const clock = options.now ?? Date.now\n const store = options.store ?? firestoreStore\n const raise = options.raise ?? defaultRaise\n const dryRun = Boolean(options.dryRun)\n const parsedThreshold = Number.parseInt(\n String(env['RENDER_MONITOR_FAILURE_THRESHOLD'] ?? ''),\n 10,\n )\n const threshold =\n Number.isFinite(parsedThreshold) && parsedThreshold >= 1\n ? parsedThreshold\n : RENDER_MONITOR_DEFAULT_THRESHOLD\n const nonce =\n options.nonce ??\n (() => `${clock().toString(36)}${Math.random().toString(36).slice(2, 8)}`)\n\n const probeOptions = {\n fetcher: options.fetcher ?? fetch,\n headers: options.headers ?? {},\n timeoutMs: options.timeoutMs ?? RENDER_MONITOR_TIMEOUT_MS,\n clock,\n }\n // Real pages need the render origin AND the bypass that gets past its\n // Deployment Protection; without both a `page` probe could only fail.\n const { renderOrigin, pages } = resolveRenderMonitorPages(env)\n const canRenderPages = Boolean(\n renderOrigin && probeOptions.headers['x-vercel-protection-bypass'],\n )\n const runAt = clock()\n const slot = Math.floor(runAt / 300_000)\n\n const sites = await Promise.all(\n resolveRenderMonitorTargets(env).map(\n async (origin): Promise<RenderMonitorSiteResult> => {\n const layoutProbes = RENDER_PROBES.map((spec) =>\n runProbe(`${origin}${spec.path(nonce())}`, spec, probeOptions),\n )\n const site = canRenderPages ? pages.find((page) => page.origin === origin) : undefined\n const pageProbe = site\n ? [\n runProbe(\n pageProbeUrl(renderOrigin, site, slot, renderSpellingNumber(runAt)),\n { kind: 'page', expectStatus: 200 },\n probeOptions,\n ),\n ]\n : []\n const probes = await Promise.all([...layoutProbes, ...pageProbe])\n const ok = probes.every((probe) => probe.ok)\n const result: RenderMonitorSiteResult = { origin, ok, probes }\n if (dryRun) return result\n const host = new URL(origin).host\n const failing = probes.filter((probe) => !probe.ok)\n const detail = ok\n ? 'Fresh renders pass.'\n : failing.map(probeLine).join('; ')\n try {\n const now = clock()\n const { next, transition, failingSinceMs } = await store(\n renderMonitorCheckId(origin),\n (prior) => {\n const decided = nextRenderMonitorState(\n prior,\n { ok, detail },\n {\n origin,\n label: `Page rendering on ${host}`,\n threshold,\n now,\n },\n )\n return {\n next: decided.next,\n result: {\n ...decided,\n // When the failures a recovery ends began.\n failingSinceMs: prior?.failingSinceMs ?? prior?.sinceMs ?? now,\n },\n }\n },\n )\n result.status = next.status\n result.consecutiveFailures = next.consecutiveFailures\n result.transition = transition\n if (transition === 'failing') {\n await raise('system.siteRenderFailing', {\n dedupeKey: host,\n context: {\n site: host,\n failures: next.consecutiveFailures,\n since: describeDuration(now - next.sinceMs),\n detail,\n },\n })\n } else if (transition === 'recovered') {\n await raise('system.siteRenderRecovered', {\n dedupeKey: host,\n context: {\n site: host,\n duration: describeDuration(now - failingSinceMs),\n },\n })\n }\n } catch (error) {\n result.error = error instanceof Error ? error.message : String(error)\n console.error(`[render-monitor] ${host} could not be recorded`, error)\n }\n return result\n },\n ),\n )\n return { dryRun, threshold, sites }\n}\n"],"names":["TENANT_APEX","RENDER_MONITOR_COLLECTION","RENDER_MONITOR_CHECK_PREFIX","RENDER_MONITOR_JOB_ID","RENDER_MONITOR_TIMEOUT_MS","RENDER_MONITOR_DEFAULT_THRESHOLD","RENDER_PROBE_ISR_PREFIX","RENDER_PROBES","kind","path","expectStatus","nonce","CHECKPOINT_MARKER","CACHED_STATES","Set","gradeRenderProbe","response","body","test","ok","challenged","code","detail","status","location","contentType","includes","cache","String","cacheState","trim","toUpperCase","has","PLATFORM_RENDER_PAGES","join","PLATFORM_CONSOLE_VERCEL_PROJECT_ID","PLATFORM_RENDER_ORIGIN","CNAME_HOST_PREFIX","MAX_SPELLING_BITS","spellingLetters","host","toLowerCase","map","char","index","filter","slice","freshHostSpelling","n","apex","subdomain","endsWith","prefix","chars","letters","capacity","length","value","Math","max","floor","Number","bits","forEach","bit","dots","repeat","renderSpellingNumber","nowMs","Date","UTC","parseRenderMonitorPages","text","byHost","Map","raw","split","entry","replace","slash","indexOf","paths","get","push","set","origin","resolveRenderMonitorPages","env","off","renderOrigin","pages","platform","originOf","trimmed","resolveRenderMonitorTargets","label","demo","listed","Boolean","paged","page","renderMonitorCheckId","URL","nextRenderMonitorState","prior","observation","context","threshold","now","checkId","wasDegraded","next","sinceMs","updatedAtMs","consecutiveFailures","failingSinceMs","lastOkAtMs","transition","failures","degraded","firestoreStore","work","default","firebaseAdmin","db","app","firestore","ref","collection","doc","runTransaction","transaction","snapshot","exists","data","result","defaultRaise","type","options","raiseOperatorAlert","describeDuration","ms","minutes","round","hours","rest","pageProbeUrl","site","slot","spelling","abs","url","searchParams","toString","probeLine","row","pathname","runProbe","spec","startedAt","clock","fetcher","redirect","headers","accept","signal","AbortSignal","timeout","timeoutMs","verdict","httpStatus","error","name","timedOut","cause","runRenderMonitor","process","store","raise","dryRun","parsedThreshold","parseInt","isFinite","random","probeOptions","fetch","canRenderPages","runAt","sites","Promise","all","layoutProbes","find","undefined","pageProbe","probes","every","probe","failing","decided","dedupeKey","since","duration","Error","message","console"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/render-monitor.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TENANT_APEX } from '@aglyn/aglyn/app-utils/tenant-apex'\nimport type { OperatorHealthStateDoc } from './operator-health'\n\n/**\n * THE RENDER MONITOR (AGL-3568): does a published site still render a page\n * that was not already in a cache?\n *\n * On 2026-10-05 every uncached page render on the published-site runtime\n * hung to the platform's 60 s limit for 28 minutes, and nothing alerted. The\n * three monitors that existed each missed it for its own reason:\n *\n * - the GitHub uptime probe is scheduled every 15 minutes and GitHub ran it\n * four times that day, none of them during the outage;\n * - its page rows fetch `/`, which the ISR cache answers, so a visitor's\n * page that was cached still loaded and the row stayed green;\n * - `/api/health/render/site` runs the page LOADER in-process and never the\n * site layout, which is where the request was stuck.\n *\n * So this fetches pages over HTTP, from the console (a different deployment\n * from the one it watches), on Cloud Scheduler every five minutes, and asks\n * for two pages no cache can answer:\n *\n * - `dynamic` — `/search`, a route every site has that renders on every\n * request (`force-dynamic`) inside the full site layout. A 200 that is a\n * complete document proves the layout and head ran just now.\n * - `isr` — a path nobody has ever requested, under the catch-all page.\n * A never-seen path is the one request the ISR cache cannot answer, so it\n * renders in the same static context every published page renders in,\n * which is the context the 2026-09-09 outage only appeared in. It is a\n * 404 by construction, and the site's own not-found page is drawn inside\n * the same layout, so a complete 404 document is a pass.\n *\n * A site that fails `threshold` runs in a row raises\n * `system.siteRenderFailing` once; the first passing run after that raises\n * `system.siteRenderRecovered` once.\n *\n * ## A challenge is not a failed render (AGL-3580)\n *\n * When bot protection answers instead of the site, this run saw nothing about\n * the site at all. On 2026-10-05 the console had no `AGLYN_PROBE_TOKEN`, every\n * `/search` probe got the 429 checkpoint for three hours, and the monitor told\n * the operator \"Pages are not rendering on aglyn.com\" and then \"rendering\n * again after 3 h\" about a site that was serving pages the whole time. So a\n * run whose only failures are challenges is BLIND: it leaves the site's\n * verdict and failure count exactly as they were, and `threshold` blind runs\n * in a row raise `system.renderMonitorBlind` once, which names the setup\n * problem instead of an outage. A run with a real failure beside a challenge\n * is still a failure, because the real one is evidence. The state lives in\n * `operatorHealthState/render-monitor--<host>`, beside every other health\n * check, so Staff → Operator alerts lists it with its status and since when.\n *\n * Nothing here names an Aglyn host for anyone else: the default target is the\n * install's own demonstration site label under its own tenant apex, the\n * convention the middleware and the render canaries already follow. The one\n * exception is `PLATFORM_RENDER_PAGES` below, which applies on Aglyn's own\n * console deployment and nowhere else.\n *\n * ## Real pages, rendered fresh (AGL-3571)\n *\n * The two probes above draw the layout. They do not draw a page BODY, and\n * beta.223 hung only on page bodies with an image in them (an ancestor walk\n * looped on the page root, AGL-3565): `demo` has no images and the not-found\n * page has no body, so this monitor read green through it. So a third probe,\n * `page`, renders a real published page through the full ISR path — through\n * the tenant's `*.vercel.app` production domain (`RENDER_MONITOR_RENDER_ORIGIN`),\n * which honors `?tenantHost=` and sits behind Deployment Protection, naming\n * the site with a spelling no cache holds (`freshHostSpelling`). The cache key\n * includes the `[host]` segment, and `normalizeHostAlias` resolves every\n * spelling to the same site. Without the automation bypass the render origin\n * answers its sign-in redirect, so no `page` probe is made at all.\n *\n * One page per site per run, rotating through that site's list: a platform\n * break fails every page with an image, so every run, and alerts as fast as\n * before; a break on one page alone is seen on its turn.\n */\n\n/** The state documents' collection: the health checks' own. */\nconst RENDER_MONITOR_COLLECTION = 'operatorHealthState'\n\n/** Every render monitor document id starts with this. */\nexport const RENDER_MONITOR_CHECK_PREFIX = 'render-monitor--'\n\n/** The beat id `/api/health/crons` reads for the scheduled run. */\nexport const RENDER_MONITOR_JOB_ID = 'render-monitor'\n\n/**\n * How long one page fetch may take. Well under the 60 s a platform function\n * may run, so a hung render is graded here as a timeout rather than waiting\n * for the platform's 504, and well under the route's own `maxDuration`.\n */\nexport const RENDER_MONITOR_TIMEOUT_MS = 25_000\n\n/** Failing runs in a row before the alert goes out. */\nexport const RENDER_MONITOR_DEFAULT_THRESHOLD = 2\n\n/**\n * The prefix of the never-requested path the `isr` probe asks for. A\n * lowercase slug on purpose: it must reach the catch-all page like any\n * screen slug would, not a reserved or rewritten route.\n */\nexport const RENDER_PROBE_ISR_PREFIX = '/aglyn-render-probe-'\n\nexport type RenderProbeKind = 'dynamic' | 'isr' | 'page'\n\nexport interface RenderProbeSpec {\n kind: RenderProbeKind\n /** The path to request, given a fresh nonce. */\n path: (nonce: string) => string\n /** The status a healthy render answers with. */\n expectStatus: number\n}\n\nexport const RENDER_PROBES: readonly RenderProbeSpec[] = [\n { kind: 'dynamic', path: () => '/search', expectStatus: 200 },\n {\n kind: 'isr',\n path: (nonce) => `${RENDER_PROBE_ISR_PREFIX}${nonce}`,\n expectStatus: 404,\n },\n]\n\n/** One fetched response, reduced to what grading reads. */\nexport interface RenderProbeResponse {\n status: number\n contentType?: string | null\n body?: string\n location?: string | null\n /** `x-vercel-cache` on Vercel, `x-nextjs-cache` on a plain `next start`. */\n cacheState?: string | null\n}\n\nexport interface RenderProbeVerdict {\n ok: boolean\n /** Bot protection answered instead of the site. */\n challenged: boolean\n /** A stable code; `ok` on a pass. */\n code: string\n detail: string\n}\n\n/** Vercel's challenge interstitial, matched on the body like the front doors. */\nconst CHECKPOINT_MARKER = /Vercel Security Checkpoint/i\n\n/**\n * Cache states that mean the bytes came out of a store. A probe meant to\n * prove a render cannot pass on one of these, whatever the status.\n */\nconst CACHED_STATES = new Set(['HIT', 'STALE', 'PRERENDER'])\n\n/**\n * Grade one probe response. Pure.\n *\n * The page markers are the front doors' (`tools/scripts/lib/front-door.mjs`)\n * and for their reason: structural, never site copy. `</html>` proves the\n * document finished streaming, `/_next/static/` that our build produced it.\n */\nexport function gradeRenderProbe(\n response: RenderProbeResponse,\n expectStatus: number,\n): RenderProbeVerdict {\n const body = response.body ?? ''\n if (CHECKPOINT_MARKER.test(body)) {\n return {\n ok: false,\n challenged: true,\n code: 'challenged',\n detail: `bot protection answered (HTTP ${response.status}) instead of the site; check AGLYN_PROBE_TOKEN on the console`,\n }\n }\n if (response.status >= 300 && response.status < 400) {\n return {\n ok: false,\n challenged: false,\n code: 'redirected',\n detail: `redirected to ${response.location ?? '?'}, so no page was rendered`,\n }\n }\n if (response.status !== expectStatus) {\n return {\n ok: false,\n challenged: false,\n code: `http-${response.status}`,\n detail: `HTTP ${response.status}`,\n }\n }\n if (!/text\\/html/i.test(response.contentType ?? '')) {\n return {\n ok: false,\n challenged: false,\n code: 'not-html',\n detail: `not HTML (${response.contentType || 'no content-type'})`,\n }\n }\n if (!body.includes('</html>')) {\n return {\n ok: false,\n challenged: false,\n code: 'incomplete',\n detail: 'the document never finished (no </html>)',\n }\n }\n if (!body.includes('/_next/static/')) {\n return {\n ok: false,\n challenged: false,\n code: 'not-our-render',\n detail: 'no Next asset references, so the app did not render it',\n }\n }\n const cache = String(response.cacheState ?? '').trim().toUpperCase()\n if (CACHED_STATES.has(cache)) {\n return {\n ok: false,\n challenged: false,\n code: 'served-from-cache',\n detail: `served from cache (${cache}), so it proves no render`,\n }\n }\n return { ok: true, challenged: false, code: 'ok', detail: 'rendered' }\n}\n\n/**\n * The pages Aglyn's own console renders fresh when `RENDER_MONITOR_PAGES` is\n * unset: the canary's real client pages (`tools/scripts/lib/prod-canary.mjs`\n * `DEFAULT_TENANT_HOSTS`), images, reusable components, repeats and a form\n * between them. Applied ONLY on that console deployment (`VERCEL_PROJECT_ID`),\n * so no other install ever requests them.\n */\nexport const PLATFORM_RENDER_PAGES = [\n 'ready-to-roll.aglyn.app/',\n 'edr-construction.aglyn.app/',\n 'edr-construction.aglyn.app/services',\n 'edr-construction.aglyn.app/contact',\n].join(' ')\n\n/** Aglyn's console project on Vercel, the one deployment the list above is for. */\nexport const PLATFORM_CONSOLE_VERCEL_PROJECT_ID = 'prj_gEzxEXc0Lhs81rmaXIg2a1GbsDfl'\n\n/**\n * The tenant's production project domain on Vercel: follows every promote and\n * rollback, honors `?tenantHost=`, and is reachable only with the bypass.\n */\nexport const PLATFORM_RENDER_ORIGIN = 'https://aglyn-tenant-aglyn.vercel.app'\n\n/** The middleware's custom-domain sentinel (`apps/tenant/utils/get-host.ts`). */\nconst CNAME_HOST_PREFIX = 'cname--'\n\n/** Letters past this are never recased, so the bit arithmetic stays in 32 bits. */\nconst MAX_SPELLING_BITS = 30\n\nfunction spellingLetters(host: string): number[] {\n return [...host.toLowerCase()]\n .map((char, index) => (/[a-z]/.test(char) ? index : -1))\n .filter((index) => index >= 0)\n .slice(0, MAX_SPELLING_BITS)\n}\n\n/**\n * Spelling number `n` of one site: a `?tenantHost=` value that resolves to\n * that site and is a `[host]` route segment no other number produces, so the\n * page it asks for has no cache entry to come from.\n *\n * - `{sub}.{apex}` → the full name, letters upper-cased by the bits of `n`,\n * then one trailing dot per exhausted round of cases.\n * - any other name → `cname--{name}`, recased the same way and cycling after\n * `2 ** letters` (that form keeps trailing dots), with the prefix left\n * lower-case because the canonical-domain redirect tests the raw segment.\n *\n * MUST stay identical to `freshHostSpelling` in\n * `tools/scripts/lib/prod-canary.mjs`; `apps/tenant/specs/probe-host-spelling.spec.ts`\n * pins both against `normalizeHostAlias`.\n */\nexport function freshHostSpelling(host: string, n: number, apex: string = TENANT_APEX): string {\n const subdomain = host.endsWith(`.${apex}`)\n const prefix = subdomain ? '' : CNAME_HOST_PREFIX\n const chars = [...host.toLowerCase()]\n const letters = spellingLetters(host)\n const capacity = 2 ** letters.length\n const value = Math.max(0, Math.floor(Number(n) || 0))\n const bits = value % capacity\n letters.forEach((index, bit) => {\n if (Math.floor(bits / 2 ** bit) % 2 === 1) chars[index] = chars[index].toUpperCase()\n })\n const dots = subdomain ? '.'.repeat(Math.floor(value / capacity)) : ''\n return `${prefix}${chars.join('')}${dots}`\n}\n\n/**\n * The spelling number for a run at `nowMs`: minutes since 2026-10-01, so it\n * never repeats on one deployment — a `{sub}.aglyn.app` name of 18 letters\n * gains its first trailing dot after half a year.\n */\nexport function renderSpellingNumber(nowMs: number): number {\n return Math.max(1, Math.floor((nowMs - Date.UTC(2026, 9, 1)) / 60_000))\n}\n\n/** One watched site's real pages. */\nexport interface RenderMonitorPages {\n /** The site's public origin, which keys its state document. */\n origin: string\n host: string\n paths: string[]\n}\n\n/**\n * `\"a.example.app b.example.app/services, https://b.example.app/contact\"` →\n * one entry per host, paths merged, `/` when an entry names none. Entries that\n * are not a hostname are dropped, as `RENDER_MONITOR_ORIGINS` drops them.\n */\nexport function parseRenderMonitorPages(text: string | undefined | null): RenderMonitorPages[] {\n const byHost = new Map<string, string[]>()\n for (const raw of String(text ?? '').split(/[\\s,]+/)) {\n const entry = raw.trim().replace(/^https?:\\/\\//i, '')\n if (!entry) continue\n const slash = entry.indexOf('/')\n const host = (slash === -1 ? entry : entry.slice(0, slash)).toLowerCase()\n if (!/^[a-z0-9-]+(\\.[a-z0-9-]+)+$/.test(host)) continue\n const path = slash === -1 ? '/' : entry.slice(slash).replace(/\\/+$/, '') || '/'\n const paths = byHost.get(host) ?? []\n if (!paths.includes(path)) paths.push(path)\n byHost.set(host, paths)\n }\n return [...byHost].map(([host, paths]) => ({ origin: `https://${host}`, host, paths }))\n}\n\n/**\n * Which real pages the monitor renders, and through which origin.\n *\n * `RENDER_MONITOR_PAGES` (same grammar as the canary's `CANARY_TENANT_HOSTS`)\n * and `RENDER_MONITOR_RENDER_ORIGIN`, else — on Aglyn's own console only —\n * `PLATFORM_RENDER_PAGES` through `PLATFORM_RENDER_ORIGIN`. `off` (here or in\n * `RENDER_MONITOR_ORIGINS`) renders none. No render origin, no pages.\n */\nexport function resolveRenderMonitorPages(\n env: Readonly<Record<string, string | undefined>>,\n): { renderOrigin: string; pages: RenderMonitorPages[] } {\n const off = /^(off|none|false|0)$/i\n const raw = String(env['RENDER_MONITOR_PAGES'] ?? '').trim()\n if (off.test(raw) || off.test(String(env['RENDER_MONITOR_ORIGINS'] ?? '').trim())) {\n return { renderOrigin: '', pages: [] }\n }\n const platform = env['VERCEL_PROJECT_ID'] === PLATFORM_CONSOLE_VERCEL_PROJECT_ID\n const renderOrigin =\n originOf(env['RENDER_MONITOR_RENDER_ORIGIN']) || (platform ? PLATFORM_RENDER_ORIGIN : '')\n const pages = parseRenderMonitorPages(raw || (platform ? PLATFORM_RENDER_PAGES : ''))\n return renderOrigin ? { renderOrigin, pages } : { renderOrigin: '', pages: [] }\n}\n\n/** A configured value as an origin, or '' when it is not one. */\nexport function originOf(value: string | undefined | null): string {\n const trimmed = String(value ?? '').trim().replace(/\\/+$/, '')\n return /^https?:\\/\\/[^/\\s?#]+$/i.test(trimmed) ? trimmed.toLowerCase() : ''\n}\n\n/**\n * Which sites the monitor renders, from the console's environment.\n *\n * 1. The install's demonstration site: `OPERATOR_HEALTH_TENANT_ORIGIN` when\n * set (the one published site the operator alerts tick already watches),\n * else `https://<AGLYN_TENANT_DEMO or demo>.<tenant apex>`.\n * 2. Plus every origin in `RENDER_MONITOR_ORIGINS`, comma separated.\n *\n * `RENDER_MONITOR_ORIGINS=off` watches nothing, for an install with no\n * published site to watch. Values that are not origins are dropped.\n */\nexport function resolveRenderMonitorTargets(\n env: Readonly<Record<string, string | undefined>>,\n): string[] {\n const raw = String(env['RENDER_MONITOR_ORIGINS'] ?? '').trim()\n if (/^(off|none|false|0)$/i.test(raw)) return []\n const apex =\n String(env['NEXT_PUBLIC_TENANT_DOMAIN'] ?? '').trim() || TENANT_APEX\n const label = String(env['AGLYN_TENANT_DEMO'] ?? '').trim() || 'demo'\n const demo =\n originOf(env['OPERATOR_HEALTH_TENANT_ORIGIN']) ||\n originOf(`https://${label}.${apex}`)\n const listed = raw\n .split(/[\\s,]+/)\n .map((value) => originOf(value))\n .filter(Boolean)\n // A site whose pages are rendered is watched whole: its layout probes too.\n const paged = resolveRenderMonitorPages(env).pages.map((page) => page.origin)\n return [...new Set([demo, ...listed, ...paged].filter(Boolean))]\n}\n\n/** The state document id for one origin. */\nexport function renderMonitorCheckId(origin: string): string {\n return renderMonitorHostCheckId(new URL(origin).host)\n}\n\n/** The state document id for one host, as {@link renderMonitorCheckId} names it. */\nexport function renderMonitorHostCheckId(host: string): string {\n return `${RENDER_MONITOR_CHECK_PREFIX}${host}`\n}\n\n/** One render monitor state document. */\nexport interface RenderMonitorStateDoc extends OperatorHealthStateDoc {\n origin: string\n /** Failing runs in a row, reset by any passing run. */\n consecutiveFailures: number\n /** When the current run of failures began; null while passing. */\n failingSinceMs: number | null\n lastOkAtMs: number | null\n /** Runs in a row where bot protection answered instead of the site. */\n consecutiveBlindRuns?: number\n /** When the current blind stretch began; null while the monitor can see. */\n blindSinceMs?: number | null\n /** When a run last saw the site itself: a pass or a real failure. */\n lastObservedAtMs?: number | null\n}\n\nexport type RenderMonitorTransition = 'failing' | 'recovered' | 'blind' | null\n\n/**\n * The next state for one site, given what this run saw. Pure.\n *\n * The status turns `degraded` only once `threshold` runs in a row have\n * failed, so one slow cold start or one dropped connection says nothing. It\n * turns back on the first passing run, because a render that works is\n * proof, and a recovery held back is a recovery nobody hears about.\n *\n * A BLIND observation (every failure was a bot-protection challenge) changes\n * nothing about the site: its status, failure count and since-time carry over\n * untouched, and only the blind count moves. It transitions to `blind` once,\n * on the `threshold`-th blind run in a row.\n */\nexport function nextRenderMonitorState(\n prior: Partial<RenderMonitorStateDoc> | null,\n observation: { ok: boolean; detail: string; blind?: boolean },\n context: { origin: string; label: string; threshold: number; now: number },\n): { next: RenderMonitorStateDoc; transition: RenderMonitorTransition } {\n const { origin, label, threshold, now } = context\n const checkId = renderMonitorCheckId(origin)\n const detail = String(observation.detail ?? '').slice(0, 1_000)\n const wasDegraded = prior?.status === 'degraded'\n if (!observation.ok && observation.blind) {\n const blindRuns = (prior?.consecutiveBlindRuns ?? 0) + 1\n return {\n next: {\n checkId,\n label,\n origin,\n status: wasDegraded ? 'degraded' : 'ok',\n sinceMs: prior?.sinceMs ?? now,\n detail: `The monitor cannot see this site: ${detail}`.slice(0, 1_000),\n updatedAtMs: now,\n consecutiveFailures: prior?.consecutiveFailures ?? 0,\n failingSinceMs: prior?.failingSinceMs ?? null,\n lastOkAtMs: prior?.lastOkAtMs ?? null,\n consecutiveBlindRuns: blindRuns,\n blindSinceMs: prior?.blindSinceMs ?? now,\n lastObservedAtMs: prior?.lastObservedAtMs ?? null,\n },\n transition: blindRuns === Math.max(1, threshold) ? 'blind' : null,\n }\n }\n const sighted = { consecutiveBlindRuns: 0, blindSinceMs: null, lastObservedAtMs: now }\n if (observation.ok) {\n return {\n next: {\n checkId,\n label,\n origin,\n status: 'ok',\n sinceMs: !wasDegraded && prior?.sinceMs ? prior.sinceMs : now,\n detail,\n updatedAtMs: now,\n consecutiveFailures: 0,\n failingSinceMs: null,\n lastOkAtMs: now,\n ...sighted,\n },\n transition: wasDegraded ? 'recovered' : null,\n }\n }\n const failures = (prior?.consecutiveFailures ?? 0) + 1\n const failingSinceMs = prior?.failingSinceMs ?? now\n const degraded = wasDegraded || failures >= Math.max(1, threshold)\n return {\n next: {\n checkId,\n label,\n origin,\n status: degraded ? 'degraded' : 'ok',\n sinceMs: degraded\n ? wasDegraded && prior?.sinceMs\n ? prior.sinceMs\n : failingSinceMs\n : (prior?.sinceMs ?? now),\n detail,\n updatedAtMs: now,\n consecutiveFailures: failures,\n failingSinceMs,\n lastOkAtMs: prior?.lastOkAtMs ?? null,\n ...sighted,\n },\n transition: degraded && !wasDegraded ? 'failing' : null,\n }\n}\n\n/** One probe as the report shows it. */\nexport interface RenderProbeRow extends RenderProbeVerdict {\n kind: RenderProbeKind\n url: string\n ms: number\n httpStatus: number | null\n}\n\n/** One site's result for a run. */\nexport interface RenderMonitorSiteResult {\n origin: string\n ok: boolean\n /** Every failure this run was a challenge, so it saw nothing of the site. */\n blind: boolean\n probes: RenderProbeRow[]\n /** The state written; absent on a dry run or a failed write. */\n status?: 'ok' | 'degraded'\n consecutiveFailures?: number\n transition?: RenderMonitorTransition\n error?: string\n}\n\nexport interface RenderMonitorReport {\n dryRun: boolean\n threshold: number\n sites: RenderMonitorSiteResult[]\n}\n\n/** The read-modify-write of one site's state, as one transaction. */\nexport type RenderMonitorStore = <T>(\n checkId: string,\n work: (prior: Partial<RenderMonitorStateDoc> | null) => {\n next: RenderMonitorStateDoc\n result: T\n },\n) => Promise<T>\n\n/**\n * The default store: one Firestore transaction on the site's document, so\n * two overlapping runs cannot both decide they saw the edge.\n */\nconst firestoreStore: RenderMonitorStore = async (checkId, work) => {\n const { default: firebaseAdmin } = await import('./firebase-admin')\n const db = firebaseAdmin.app().firestore()\n const ref = db.collection(RENDER_MONITOR_COLLECTION).doc(checkId)\n return db.runTransaction(async (transaction: any) => {\n const snapshot = await transaction.get(ref)\n const prior = snapshot.exists\n ? (snapshot.data() as Partial<RenderMonitorStateDoc>)\n : null\n const { next, result } = work(prior)\n transaction.set(ref, next)\n return result\n })\n}\n\ntype RaiseAlert = (\n type: string,\n options: { dedupeKey?: string; context?: Record<string, string | number> },\n) => Promise<unknown>\n\nconst defaultRaise: RaiseAlert = async (type, options) => {\n const { raiseOperatorAlert } = await import('./operator-alerts')\n return raiseOperatorAlert(type, options)\n}\n\nfunction describeDuration(ms: number): string {\n const minutes = Math.max(1, Math.round(ms / 60_000))\n if (minutes < 60) return `${minutes} min`\n const hours = Math.floor(minutes / 60)\n const rest = minutes % 60\n return rest ? `${hours} h ${rest} min` : `${hours} h`\n}\n\n/**\n * The `page` probe's URL for one run: this run's page of the site's list\n * (rotating every five-minute slot), under this run's spelling of the site.\n */\nexport function pageProbeUrl(\n renderOrigin: string,\n site: RenderMonitorPages,\n slot: number,\n spelling: number,\n): string {\n const path = site.paths[Math.abs(slot) % site.paths.length] ?? '/'\n const url = new URL(path, renderOrigin)\n url.searchParams.set('tenantHost', freshHostSpelling(site.host, spelling))\n return url.toString()\n}\n\nfunction probeLine(row: RenderProbeRow): string {\n return `${row.kind} ${new URL(row.url).pathname}: ${row.detail} (${row.ms} ms)`\n}\n\n/** Fetch and grade one probe. Never throws. */\nasync function runProbe(\n url: string,\n spec: { kind: RenderProbeKind; expectStatus: number },\n options: {\n fetcher: typeof fetch\n headers: Record<string, string>\n timeoutMs: number\n clock: () => number\n },\n): Promise<RenderProbeRow> {\n const startedAt = options.clock()\n try {\n const response = await options.fetcher(url, {\n redirect: 'manual',\n cache: 'no-store',\n headers: {\n 'user-agent': 'aglyn-render-monitor',\n accept: 'text/html,application/xhtml+xml',\n ...options.headers,\n },\n signal: AbortSignal.timeout(options.timeoutMs),\n })\n const body = await response.text()\n const verdict = gradeRenderProbe(\n {\n status: response.status,\n contentType: response.headers.get('content-type'),\n body,\n location: response.headers.get('location'),\n cacheState:\n response.headers.get('x-vercel-cache') ??\n response.headers.get('x-nextjs-cache'),\n },\n spec.expectStatus,\n )\n return {\n kind: spec.kind,\n url,\n ms: options.clock() - startedAt,\n httpStatus: response.status,\n ...verdict,\n }\n } catch (error) {\n const name = (error as { name?: string } | null)?.name\n const timedOut = name === 'TimeoutError' || name === 'AbortError'\n return {\n kind: spec.kind,\n url,\n ms: options.clock() - startedAt,\n httpStatus: null,\n ok: false,\n challenged: false,\n code: timedOut ? 'timeout' : 'unreachable',\n detail: timedOut\n ? `no complete page in ${Math.round(options.timeoutMs / 1_000)} s`\n : `unreachable (${(error as { cause?: { code?: string } } | null)?.cause?.code ?? name ?? 'error'})`,\n }\n }\n}\n\n/**\n * One run: render every target, record each site's state, raise the edge.\n *\n * A dry run fetches and grades and writes nothing, so a person can ask\n * \"what would the monitor see right now\" without moving its state.\n */\nexport async function runRenderMonitor(\n options: {\n dryRun?: boolean\n env?: Readonly<Record<string, string | undefined>>\n fetcher?: typeof fetch\n /** Our own edge's bypass headers, from the caller's environment. */\n headers?: Record<string, string>\n now?: () => number\n nonce?: () => string\n timeoutMs?: number\n store?: RenderMonitorStore\n raise?: RaiseAlert\n } = {},\n): Promise<RenderMonitorReport> {\n const env = options.env ?? process.env\n const clock = options.now ?? Date.now\n const store = options.store ?? firestoreStore\n const raise = options.raise ?? defaultRaise\n const dryRun = Boolean(options.dryRun)\n const parsedThreshold = Number.parseInt(\n String(env['RENDER_MONITOR_FAILURE_THRESHOLD'] ?? ''),\n 10,\n )\n const threshold =\n Number.isFinite(parsedThreshold) && parsedThreshold >= 1\n ? parsedThreshold\n : RENDER_MONITOR_DEFAULT_THRESHOLD\n const nonce =\n options.nonce ??\n (() => `${clock().toString(36)}${Math.random().toString(36).slice(2, 8)}`)\n\n const probeOptions = {\n fetcher: options.fetcher ?? fetch,\n headers: options.headers ?? {},\n timeoutMs: options.timeoutMs ?? RENDER_MONITOR_TIMEOUT_MS,\n clock,\n }\n // Real pages need the render origin AND the bypass that gets past its\n // Deployment Protection; without both a `page` probe could only fail.\n const { renderOrigin, pages } = resolveRenderMonitorPages(env)\n const canRenderPages = Boolean(\n renderOrigin && probeOptions.headers['x-vercel-protection-bypass'],\n )\n const runAt = clock()\n const slot = Math.floor(runAt / 300_000)\n\n const sites = await Promise.all(\n resolveRenderMonitorTargets(env).map(\n async (origin): Promise<RenderMonitorSiteResult> => {\n const layoutProbes = RENDER_PROBES.map((spec) =>\n runProbe(`${origin}${spec.path(nonce())}`, spec, probeOptions),\n )\n const site = canRenderPages ? pages.find((page) => page.origin === origin) : undefined\n const pageProbe = site\n ? [\n runProbe(\n pageProbeUrl(renderOrigin, site, slot, renderSpellingNumber(runAt)),\n { kind: 'page', expectStatus: 200 },\n probeOptions,\n ),\n ]\n : []\n const probes = await Promise.all([...layoutProbes, ...pageProbe])\n const ok = probes.every((probe) => probe.ok)\n const failing = probes.filter((probe) => !probe.ok)\n const blind = !ok && failing.every((probe) => probe.challenged)\n const result: RenderMonitorSiteResult = { origin, ok, blind, probes }\n if (dryRun) return result\n const host = new URL(origin).host\n const detail = ok\n ? 'Fresh renders pass.'\n : failing.map(probeLine).join('; ')\n try {\n const now = clock()\n const { next, transition, failingSinceMs } = await store(\n renderMonitorCheckId(origin),\n (prior) => {\n const decided = nextRenderMonitorState(\n prior,\n { ok, detail, blind },\n {\n origin,\n label: `Page rendering on ${host}`,\n threshold,\n now,\n },\n )\n return {\n next: decided.next,\n result: {\n ...decided,\n // When the failures a recovery ends began.\n failingSinceMs: prior?.failingSinceMs ?? prior?.sinceMs ?? now,\n },\n }\n },\n )\n result.status = next.status\n result.consecutiveFailures = next.consecutiveFailures\n result.transition = transition\n if (transition === 'failing') {\n await raise('system.siteRenderFailing', {\n dedupeKey: host,\n context: {\n site: host,\n failures: next.consecutiveFailures,\n since: describeDuration(now - next.sinceMs),\n detail,\n },\n })\n } else if (transition === 'blind') {\n await raise('system.renderMonitorBlind', {\n dedupeKey: host,\n context: {\n site: host,\n runs: next.consecutiveBlindRuns ?? threshold,\n detail,\n },\n })\n } else if (transition === 'recovered') {\n await raise('system.siteRenderRecovered', {\n dedupeKey: host,\n context: {\n site: host,\n duration: describeDuration(now - failingSinceMs),\n },\n })\n }\n } catch (error) {\n result.error = error instanceof Error ? error.message : String(error)\n console.error(`[render-monitor] ${host} could not be recorded`, error)\n }\n return result\n },\n ),\n )\n return { dryRun, threshold, sites }\n}\n\n/**\n * One watched site as `/api/health/pages` reports it (AGL-3580).\n *\n * The shape of every other health check — `ok`, `ms`, a stable `code` — plus\n * when the monitor last saw the site, so a reader can tell a fresh verdict\n * from an old one without a second request.\n */\nexport interface RenderPagesCheck {\n ok: boolean\n ms: number\n code?: string\n /** When a run last rendered or failed this site's pages, ISO. */\n lastObservedAt: string | null\n /** When the site's current status began, ISO. */\n since: string | null\n}\n\n/**\n * A verdict older than this says the monitor stopped, not that the site did.\n * Four missed five-minute runs.\n */\nexport const RENDER_PAGES_STALE_MS = 20 * 60_000\n\n/** How many sites one `?site=` may ask about. */\nexport const RENDER_PAGES_MAX_SITES = 10\n\n/**\n * Which watched hosts a `?site=` asks about. Pure.\n *\n * Absent or empty asks about every watched site. A host that is not watched\n * is kept, so the caller can answer it red: a public monitor pointed at a\n * site nobody renders is a monitor that can never go red, which is the\n * mistake this door exists to undo.\n */\nexport function requestedRenderPagesHosts(\n query: string | null | undefined,\n watchedOrigins: readonly string[],\n): string[] {\n const asked = String(query ?? '')\n .split(/[\\s,]+/)\n .map((entry) => entry.trim().toLowerCase().replace(/^https?:\\/\\//, '').replace(/\\/.*$/, ''))\n .filter((entry) => /^[a-z0-9-]+(\\.[a-z0-9-]+)+$/.test(entry))\n const hosts = asked.length ? asked : watchedOrigins.map((origin) => new URL(origin).host)\n return [...new Set(hosts)].slice(0, RENDER_PAGES_MAX_SITES)\n}\n\n/**\n * The public verdict on real pages, per site, from the render monitor's\n * stored state (AGL-3580). Pure.\n *\n * This is what the status page's `Published sites` and `Marketing site`\n * monitors read. Before it they read `/api/health/render/*`, which builds a\n * node tree inside a route handler and never draws a page, and both stayed at\n * 100% through two outages on 2026-10-05 in which every fresh page hung.\n *\n * Red is reserved for the one thing a visitor would notice:\n *\n * - `not-rendering` — the monitor saw the site fail to render pages it could\n * not have served from a cache, `threshold` runs in a row.\n * - `not-watched` — the monitor renders nothing for this host, so this row\n * could never go red; fix the monitor's config or the URL.\n * - `state-unavailable` — the verdicts could not be read at all.\n *\n * Green with a code is a verdict this door cannot improve on, and is never\n * reported as an outage, because it is not one:\n *\n * - `awaiting-first-run` — a site added since the monitor last ran.\n * - `monitor-blind` — bot protection answered the monitor; the site's last\n * real verdict stands, and the operator alert says what to fix.\n * - `observation-stale` — no run in {@link RENDER_PAGES_STALE_MS}; the\n * monitor's own `render-monitor` row on `/api/health/crons` is what goes\n * red for that, so a stopped scheduler is never told as a site outage.\n */\nexport function renderPagesHealth(\n hosts: readonly string[],\n watchedOrigins: readonly string[],\n states: ReadonlyMap<string, Partial<RenderMonitorStateDoc>> | null,\n ms: number,\n now: number = Date.now(),\n): Record<string, RenderPagesCheck> {\n const watched = new Set(watchedOrigins.map((origin) => new URL(origin).host))\n const iso = (value: number | null | undefined) =>\n typeof value === 'number' && Number.isFinite(value) ? new Date(value).toISOString() : null\n const checks: Record<string, RenderPagesCheck> = {}\n if (!hosts.length) {\n checks['sites'] = { ok: false, ms, code: 'no-sites-watched', lastObservedAt: null, since: null }\n return checks\n }\n for (const host of hosts) {\n const blank = { ms, lastObservedAt: null, since: null }\n if (!watched.has(host)) {\n checks[host] = { ...blank, ok: false, code: 'not-watched' }\n continue\n }\n if (states === null) {\n checks[host] = { ...blank, ok: false, code: 'state-unavailable' }\n continue\n }\n const state = states.get(renderMonitorHostCheckId(host))\n if (!state) {\n checks[host] = { ...blank, ok: true, code: 'awaiting-first-run' }\n continue\n }\n const observed = state.lastObservedAtMs ?? state.lastOkAtMs ?? null\n const row = { ms, lastObservedAt: iso(observed), since: iso(state.sinceMs) }\n if (state.status === 'degraded') {\n checks[host] = { ...row, ok: false, code: 'not-rendering' }\n } else if ((state.consecutiveBlindRuns ?? 0) > 0) {\n checks[host] = { ...row, ok: true, code: 'monitor-blind' }\n } else if (now - (state.updatedAtMs ?? 0) > RENDER_PAGES_STALE_MS) {\n checks[host] = { ...row, ok: true, code: 'observation-stale' }\n } else {\n checks[host] = { ...row, ok: true }\n }\n }\n return checks\n}\n\n/**\n * Read the stored verdicts for `hosts`, one batched read. Null when the read\n * fails, which {@link renderPagesHealth} reports red rather than calm.\n */\nexport async function readRenderMonitorStates(\n hosts: readonly string[],\n): Promise<Map<string, Partial<RenderMonitorStateDoc>> | null> {\n try {\n const { default: firebaseAdmin } = await import('./firebase-admin')\n const db = firebaseAdmin.app().firestore()\n const refs = hosts.map((host) =>\n db.collection(RENDER_MONITOR_COLLECTION).doc(renderMonitorHostCheckId(host)),\n )\n const snapshots = refs.length ? await db.getAll(...refs) : []\n const states = new Map<string, Partial<RenderMonitorStateDoc>>()\n for (const snapshot of snapshots) {\n if (snapshot.exists) states.set(snapshot.id, snapshot.data() as Partial<RenderMonitorStateDoc>)\n }\n return states\n } catch (error) {\n console.error('[render-monitor] stored verdicts could not be read', error)\n return null\n }\n}\n"],"names":["TENANT_APEX","RENDER_MONITOR_COLLECTION","RENDER_MONITOR_CHECK_PREFIX","RENDER_MONITOR_JOB_ID","RENDER_MONITOR_TIMEOUT_MS","RENDER_MONITOR_DEFAULT_THRESHOLD","RENDER_PROBE_ISR_PREFIX","RENDER_PROBES","kind","path","expectStatus","nonce","CHECKPOINT_MARKER","CACHED_STATES","Set","gradeRenderProbe","response","body","test","ok","challenged","code","detail","status","location","contentType","includes","cache","String","cacheState","trim","toUpperCase","has","PLATFORM_RENDER_PAGES","join","PLATFORM_CONSOLE_VERCEL_PROJECT_ID","PLATFORM_RENDER_ORIGIN","CNAME_HOST_PREFIX","MAX_SPELLING_BITS","spellingLetters","host","toLowerCase","map","char","index","filter","slice","freshHostSpelling","n","apex","subdomain","endsWith","prefix","chars","letters","capacity","length","value","Math","max","floor","Number","bits","forEach","bit","dots","repeat","renderSpellingNumber","nowMs","Date","UTC","parseRenderMonitorPages","text","byHost","Map","raw","split","entry","replace","slash","indexOf","paths","get","push","set","origin","resolveRenderMonitorPages","env","off","renderOrigin","pages","platform","originOf","trimmed","resolveRenderMonitorTargets","label","demo","listed","Boolean","paged","page","renderMonitorCheckId","renderMonitorHostCheckId","URL","nextRenderMonitorState","prior","observation","context","threshold","now","checkId","wasDegraded","blind","blindRuns","consecutiveBlindRuns","next","sinceMs","updatedAtMs","consecutiveFailures","failingSinceMs","lastOkAtMs","blindSinceMs","lastObservedAtMs","transition","sighted","failures","degraded","firestoreStore","work","default","firebaseAdmin","db","app","firestore","ref","collection","doc","runTransaction","transaction","snapshot","exists","data","result","defaultRaise","type","options","raiseOperatorAlert","describeDuration","ms","minutes","round","hours","rest","pageProbeUrl","site","slot","spelling","abs","url","searchParams","toString","probeLine","row","pathname","runProbe","spec","startedAt","clock","fetcher","redirect","headers","accept","signal","AbortSignal","timeout","timeoutMs","verdict","httpStatus","error","name","timedOut","cause","runRenderMonitor","process","store","raise","dryRun","parsedThreshold","parseInt","isFinite","random","probeOptions","fetch","canRenderPages","runAt","sites","Promise","all","layoutProbes","find","undefined","pageProbe","probes","every","probe","failing","decided","dedupeKey","since","runs","duration","Error","message","console","RENDER_PAGES_STALE_MS","RENDER_PAGES_MAX_SITES","requestedRenderPagesHosts","query","watchedOrigins","asked","hosts","renderPagesHealth","states","watched","iso","toISOString","checks","lastObservedAt","state","blank","observed","readRenderMonitorStates","refs","snapshots","getAll","id"],"mappings":"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"}
@@ -137,6 +137,13 @@ export declare const MEDIA_CDN_STABLE_EDGE_BYPASS_CACHE_CONTROL = "private, max-
137
137
  * the stable policy, for the AGL-1515 reasons above.
138
138
  */
139
139
  export declare const MEDIA_CDN_VERSIONED_CACHE_CONTROL = "public, max-age=31536000, s-maxage=3600, stale-while-revalidate=86400, immutable";
140
+ /**
141
+ * The generated width that answers a `?w=` request (AGL-3486): the width
142
+ * itself when the asset has it, otherwise the smallest larger one, and `null`
143
+ * when every variant is narrower than asked — then the display copy or the
144
+ * original is the closest picture there is.
145
+ */
146
+ export declare function mediaCdnVariantFor(variants: readonly unknown[], width: number): number | null;
140
147
  /**
141
148
  * Whether a versioned request may be answered with
142
149
  * {@link MEDIA_CDN_VERSIONED_CACHE_CONTROL} (AGL-3485).