@continuous-excellence/ze-great-dashboard-aws 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/.vite/manifest.json +2 -2
- package/client/assets/index-C_V8l8ss.js +3 -0
- package/client/assets/index-C_V8l8ss.js.map +1 -0
- package/client/assets/index-wNv7uLY2.css +1 -0
- package/client/index.html +2 -2
- package/dist/cli.js +2 -0
- package/dist/index.js +2 -0
- package/dist/internal-board.d.ts +1 -0
- package/dist/lambda.mjs +3 -0
- package/package.json +1 -1
- package/client/assets/index-B0MmzIQ_.js +0 -3
- package/client/assets/index-B0MmzIQ_.js.map +0 -1
- package/client/assets/index-BYxWe73w.css +0 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{z as e}from"zod";import{StrictMode as t,useEffect as n,useRef as r,useState as i,useSyncExternalStore as a}from"react";import{createRoot as o}from"react-dom/client";import{jsx as s,jsxs as c}from"react/jsx-runtime";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var l=/^(\d+)(ms|s|m|h)$/,u={ms:1,s:1e3,m:6e4,h:36e5};function d(e){let t=l.exec(e);if(!t)return null;let[,n,r]=t;if(n===void 0||r===void 0)return null;let i=Number(n);return i<=0?null:i*u[r]}var f=e.string().refine(e=>d(e)!==null,{message:`must be a positive duration like "30s", "5m", or "1h"`}).brand(),ee=e.object({x:e.number().int().min(0),y:e.number().int().min(0),w:e.number().int().min(1).max(12),h:e.number().int().min(1)}),te=e.string().min(1),p=e.looseObject({id:e.string().min(1),type:e.string().min(1),source:e.string().min(1).optional(),display:te.optional(),position:ee.optional(),refresh:f.optional(),link:e.url().optional(),url:e.url().optional(),json_path:e.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/,`must be a simple JSON path`).optional()}),m=e.looseObject({type:e.string().min(1),token_env:e.string().min(1).optional()}),h=e.object({refresh:f.optional(),panels:e.array(p).min(1).superRefine((e,t)=>{let n=new Map;e.forEach((e,r)=>{let i=n.get(e.id);if(i===void 0){n.set(e.id,r);return}t.addIssue({code:`custom`,path:[r,`id`],message:`duplicate panel id "${e.id}" (already used by panel at index ${i})`})})})}),g=e.object({issuer:e.url(),client_id:e.string().min(1).optional(),allow:e.object({groups:e.array(e.string().min(1)).optional(),subjects:e.array(e.string().min(1)).optional()}).optional()});e.object({sources:e.record(e.string().min(1),m).default({}),boards:e.record(e.string().min(1),h),auth:g.optional()});function _(e){let t=e.panelOverrideMillis??e.boardDefaultMillis;return Math.max(t,e.adapterFloorMillis)}var v=e.object({assetPath:e.string().min(1),proxyPath:e.string().min(1),board:e.string().min(1),clientVersion:e.string().min(1),auth:e.object({issuer:e.string().min(1),clientId:e.string().min(1).optional()}).optional()});function y(t=globalThis.window?.env){let n=v.safeParse(t);if(!n.success)throw Error(`window.env is missing or invalid \u2014 the server did not inject usable configuration.
|
|
2
|
+
${e.prettifyError(n.error)}`);return n.data}var b=e.enum([`unreachable`,`unauthorized`,`not-found`,`no-runs`,`upstream-error`]),x=e.object({kind:b,message:e.string()}),S=e.discriminatedUnion(`state`,[e.object({panelId:e.string().min(1),state:e.literal(`ok`),observedAt:e.iso.datetime(),link:e.url().nullable(),signal:e.unknown()}),e.object({panelId:e.string().min(1),state:e.literal(`error`),observedAt:e.iso.datetime(),link:e.url().nullable(),error:x})]),C=e.object({type:e.literal(`pipeline-status`),status:e.enum([`passed`,`failed`,`running`,`cancelled`,`unknown`]),rawStatus:e.string(),name:e.string().min(1),branch:e.string().min(1).optional(),durationMs:e.number().int().nonnegative().optional(),sourceUpdatedAt:e.iso.datetime().optional()}),ne=e.object({type:e.literal(`http-value`),value:e.union([e.string(),e.number(),e.boolean()])}),w={diagnostics:`_diagnostics_1pu1f_1`,button:`_button_1pu1f_5`,area:`_area_1pu1f_14`,actions:`_actions_1pu1f_31`};function re({log:e}){let[t,n]=i(!1),r=a(e.subscribe,e.count,e.count);return c(`section`,{className:w.diagnostics,children:[c(`button`,{className:w.button,type:`button`,"aria-expanded":t,onClick:()=>n(!t),children:[`Diagnostics (`,r,`)`]}),t&&c(`div`,{className:w.area,children:[c(`p`,{children:[r,` retained browser-local events. They are never uploaded.`]}),c(`div`,{className:w.actions,children:[s(`button`,{className:w.button,type:`button`,onClick:()=>{let t=new Blob([JSON.stringify(e.export(),null,2)],{type:`application/json`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`dashboard-diagnostics-${new Date().toISOString().replaceAll(`:`,`-`)}.json`,r.click(),URL.revokeObjectURL(n)},children:`Download`}),s(`button`,{className:w.button,type:`button`,onClick:()=>{window.confirm(`Clear this browser’s retained dashboard diagnostics?`)&&e.clear()},children:`Clear`})]})]})]})}var T=`ze-great-dashboard.diagnostics.v1`,E=6048e5,D=[`session-start`,`board-fetch-start`,`board-fetch-response`,`board-fetch-parse-failure`,`board-fetch-failure`,`panel-fetch-start`,`panel-fetch-response`,`panel-fetch-parse-failure`,`panel-fetch-failure`,`panel-rendered`],O=class{env;storage;now;events=[];listeners=new Set;sessionId=crypto.randomUUID?.()??`${Date.now()}-${Math.random()}`;constructor(e,t=M(),n=()=>new Date){this.env=e,this.storage=t,this.now=n,this.events=this.read(),this.record({kind:`session-start`})}record(e){this.events=A([...this.events,{...e,schemaVersion:1,at:this.now().toISOString(),sessionId:this.sessionId,board:this.env.board}],this.now()),this.persist(),this.notify()}count=()=>this.events.length;clear(){this.events=[];try{this.storage?.removeItem(T)}catch{}this.notify()}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});export=()=>({schemaVersion:1,exportedAt:this.now().toISOString(),client:{version:this.env.clientVersion,assetPath:this.env.assetPath,board:this.env.board,sessionId:this.sessionId},events:this.events});read(){try{let e=this.storage?.getItem(T);if(!e)return[];let t=JSON.parse(e);return t.schemaVersion!==1||!Array.isArray(t.events)||!t.events.every(j)?(this.storage?.removeItem(T),[]):A(t.events.map(e=>({...e,schemaVersion:1})),this.now())}catch{try{this.storage?.removeItem(T)}catch{}return[]}}persist(){try{this.storage?.setItem(T,JSON.stringify({schemaVersion:1,events:this.events}))}catch{}}notify(){for(let e of this.listeners)e()}};function k(e){let t=Object.fromEntries([[`cacheControl`,`cache-control`],[`etag`,`etag`],[`lastModified`,`last-modified`],[`date`,`date`],[`age`,`age`]].flatMap(([t,n])=>{let r=e.get(n);return r?[[t,r]]:[]}));return Object.keys(t).length?t:void 0}function A(e,t){let n=t.valueOf()-E;return e.filter(e=>Number.isFinite(new Date(e.at).valueOf())&&new Date(e.at).valueOf()>=n).slice(-2e3)}function j(e){if(!e||typeof e!=`object`)return!1;let t=e;return(t.schemaVersion===void 0||t.schemaVersion===1)&&typeof t.at==`string`&&Number.isFinite(new Date(t.at).valueOf())&&typeof t.sessionId==`string`&&typeof t.board==`string`&&D.includes(t.kind)}function M(){try{return window.localStorage}catch{return}}function N({value:e,label:t=`As of`}){let n=new Date(e),r=n.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}),i=Date.now()-n.getTime(),a=i>3e5,o=P(i);return c(`p`,{className:`panel__hint panel__observed${a?` panel__observed--stale`:``}`,children:[s(`span`,{"aria-hidden":`true`,children:`◷`}),` `,t,` `,r,` · `,o]})}function P(e){if(e<6e4)return`just now`;let t=Math.floor(e/6e4);return t<60?`${t}m ago`:`${Math.floor(t/60)}h ${t%60}m ago`}function F(e){let t=e.position;if(t)return{"--panel-column":`${t.x+1} / span ${t.w}`,"--panel-row":`${t.y+1} / span ${t.h}`}}function I({panel:e,envelope:t,error:n=!1,children:r}){let i=e.display===`primary`||e.display===`compact`?e.display:`supporting`;return c(`section`,{className:`panel panel--${i}${n?` panel--error`:``}`,style:F(e),"aria-busy":!t||void 0,children:[s(`h2`,{className:`panel__label`,children:e.id}),r,s(L,{panelId:e.id,link:t?.link})]})}function L({panelId:e,link:t}){return t?c(`a`,{className:`panel__link`,href:t,target:`_blank`,rel:`noopener noreferrer`,"aria-label":`View source for ${e} (opens in a new tab)`,children:[`View source `,s(`span`,{"aria-hidden":`true`,children:`↗`})]}):null}function R({panel:e,envelope:t}){if(!t)return s(I,{panel:e,children:s(`p`,{className:`panel__hint`,children:`Loading…`})});if(t.state===`error`)return c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Unable to read`}),s(`p`,{className:`panel__hint`,children:t.error.message}),s(N,{value:t.observedAt})]});let n=ne.safeParse(t.signal);return n.success?c(I,{panel:e,envelope:t,children:[s(`p`,{className:`panel__status`,children:String(n.data.value)}),s(N,{value:t.observedAt})]}):c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Invalid value`}),s(N,{value:t.observedAt})]})}function z({label:e,hint:t,wide:n=!1}){return c(`section`,{className:n?`panel panel--wide`:`panel`,children:[s(`h2`,{className:`panel__label`,children:e}),s(`p`,{className:`panel__hint`,children:t})]})}function B({panel:e,envelope:t}){if(!t)return s(I,{panel:e,children:s(`p`,{className:`panel__hint`,children:`Loading…`})});if(t.state===`error`)return c(I,{panel:e,envelope:t,error:!0,children:[c(`p`,{className:`panel__status`,children:[`⚠ `,t.error.kind===`no-runs`?`No workflow runs`:`Unable to read`]}),s(`p`,{className:`panel__hint`,children:t.error.message}),s(N,{value:t.observedAt})]});let n=C.safeParse(t.signal);if(!n.success)return c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Invalid signal`}),s(N,{value:t.observedAt})]});let r=H(n.data.status);return c(I,{panel:e,envelope:t,children:[c(`p`,{className:`panel__status panel__status--${n.data.status}`,children:[r.glyph,` `,r.label]}),c(`p`,{className:`panel__hint`,children:[n.data.name,` · `,n.data.rawStatus,n.data.branch&&c(`span`,{className:`panel__branch`,title:`Branch: ${n.data.branch}`,children:[s(`span`,{"aria-hidden":`true`,children:` · ⎇ `}),s(`span`,{className:`screen-reader-only`,children:`Branch: `}),n.data.branch]})]}),n.data.status!==`running`&&n.data.durationMs!==void 0&&c(`p`,{className:`panel__hint`,children:[`Took `,V(n.data.durationMs)]}),n.data.sourceUpdatedAt&&s(N,{value:n.data.sourceUpdatedAt,label:`Run updated`}),s(N,{value:t.observedAt})]})}function V(e){let t=Math.floor(e/1e3),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60;return n>0?`${n}h ${r}m`:r>0?`${r}m ${i}s`:`${i}s`}function H(e){switch(e){case`passed`:return{glyph:`✓`,label:`Passed`};case`failed`:return{glyph:`✕`,label:`Failed`};case`running`:return{glyph:`↻`,label:`Running`};case`cancelled`:return{glyph:`⊘`,label:`Cancelled`};case`unknown`:return{glyph:`?`,label:`Unknown`}}}function U(e,t){if(t.state===`error`)return{state:t.state,link:t.link};if(e.type===`pipeline-status`){let e=C.safeParse(t.signal);return{state:t.state,status:e.success?e.data.status:void 0,link:t.link}}return{state:t.state,link:t.link}}function W(e,t,n){if(!e)return!0;let r=U(t,e),i=U(t,n);return r.state!==i.state||r.status!==i.status||r.link!==i.link}function G({board:e,env:t,diagnostics:a}){let[o,s]=i({}),c=r({});return n(()=>{if(c.current={},s({}),!e)return;let n=!1,r=[];for(let i of e.panels){if(i.type!==`pipeline-status`&&i.type!==`http-value`)continue;let o=!1,l=_({boardDefaultMillis:d(e.refresh??`60s`)??6e4,panelOverrideMillis:i.refresh?d(i.refresh)??void 0:void 0,adapterFloorMillis:0}),u=`${t.proxyPath}/panel/${encodeURIComponent(t.board)}/${encodeURIComponent(i.id)}`,f=()=>{n||o||(o=!0,a.record({kind:`panel-fetch-start`,panelId:i.id,path:u}),fetch(u).then(async e=>{if(a.record({kind:`panel-fetch-response`,panelId:i.id,path:u,status:e.status,cache:k(e.headers)}),e.status!==304)try{return await e.json()}catch(e){throw a.record({kind:`panel-fetch-parse-failure`,panelId:i.id,path:u,message:K(e)}),new q(e)}}).then(e=>{let t=J(e);if(e!==void 0&&!t&&a.record({kind:`panel-fetch-parse-failure`,panelId:i.id,path:u,message:`Response was not a valid signal envelope.`}),!n&&t){a.record({kind:`panel-fetch-response`,panelId:i.id,path:u,envelope:t});let e=c.current[i.id];W(e,i,t)&&a.record({kind:`panel-rendered`,panelId:i.id,path:u,rendered:U(i,t)}),c.current={...c.current,[i.id]:t},s(c.current)}}).catch(e=>{e instanceof q||a.record({kind:`panel-fetch-failure`,panelId:i.id,path:u,message:K(e)})}).finally(()=>{o=!1}))};f(),r.push(window.setInterval(f,l))}return()=>{n=!0;for(let e of r)window.clearInterval(e)}},[e,a,t.board,t.proxyPath]),o}function K(e){return e instanceof Error?e.message:String(e)}var q=class extends Error{constructor(e){super(K(e))}};function J(e){let t=S.safeParse(e);return t.success?t.data:void 0}function Y({env:e}){let[t,a]=i(),[o,l]=i(),u=r(null);u.current||=new O(e);let d=u.current,f=G({board:o===e.board?t:void 0,env:e,diagnostics:d});return n(()=>{let t=!1;a(void 0),l(void 0);let n=`${e.proxyPath}/boards/${encodeURIComponent(e.board)}`;return d.record({kind:`board-fetch-start`,path:n}),fetch(n).then(async e=>{if(d.record({kind:`board-fetch-response`,path:n,status:e.status,cache:k(e.headers)}),!e.ok)throw Error(`Board configuration returned ${e.status}`);try{return await e.json()}catch(e){throw d.record({kind:`board-fetch-parse-failure`,path:n,message:X(e)}),new Z(e)}}).then(r=>{if(!t){a(r),l(e.board);let t=Array.isArray(r.panels)?r.panels:[];d.record({kind:`board-fetch-response`,path:n,boardSummary:{panelCount:t.length,panelIds:t.flatMap(e=>typeof e.id==`string`?[e.id]:[])}})}}).catch(r=>{r instanceof Z||d.record({kind:`board-fetch-failure`,path:n,message:X(r)}),t||(a({panels:[]}),l(e.board))}),()=>{t=!0}},[d,e.board,e.proxyPath]),c(`div`,{className:`board`,children:[c(`header`,{className:`board__header`,children:[s(`h1`,{className:`board__title`,children:e.board}),c(`dl`,{className:`board__meta`,children:[c(`div`,{children:[s(`dt`,{children:`client`}),s(`dd`,{children:e.clientVersion})]}),c(`div`,{children:[s(`dt`,{children:`assets`}),s(`dd`,{children:e.assetPath})]})]})]}),c(`main`,{className:`board__grid`,children:[!t&&s(z,{label:`board`,hint:`Loading configuration…`,wide:!0}),t?.panels.map(e=>e.type===`pipeline-status`?s(B,{panel:e,envelope:f[e.id]},e.id):e.type===`http-value`?s(R,{panel:e,envelope:f[e.id]},e.id):s(z,{label:e.type,hint:`Not wired yet`,wide:!0},e.id))]}),c(`footer`,{className:`board__footer`,children:[s(`span`,{children:`Signals are read live from their configured authorities.`}),s(re,{log:d})]})]})}function X(e){return e instanceof Error?e.message:String(e)}var Z=class extends Error{constructor(e){super(X(e))}};function ie({message:e}){return c(`div`,{className:`config-error`,role:`alert`,children:[s(`h1`,{children:`⚠️ Dashboard misconfigured`}),s(`p`,{children:`The page loaded, but the server did not hand it usable configuration.`}),s(`pre`,{children:e})]})}var Q=document.getElementById(`root`);if(!Q)throw Error(`No #root element in the document — the template is not the one we expect.`);var $=o(Q);try{$.render(s(t,{children:s(Y,{env:y()})}))}catch(e){$.render(s(ie,{message:e instanceof Error?e.message:String(e)}))}
|
|
3
|
+
//# sourceMappingURL=index-C_V8l8ss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-C_V8l8ss.js","names":[],"sources":["../../../shared/dist/index.js","../../src/Diagnostics.module.css","../../src/Diagnostics.tsx","../../src/diagnostics.ts","../../src/ObservedAt.tsx","../../src/panel-layout.ts","../../src/PanelFrame.tsx","../../src/HttpValuePanel.tsx","../../src/PanelPlaceholder.tsx","../../src/PipelinePanel.tsx","../../src/panel-diagnostics.ts","../../src/usePanelSignals.ts","../../src/App.tsx","../../src/ConfigError.tsx","../../src/main.tsx"],"sourcesContent":["// packages/shared/src/board-config.ts\nimport { z as z2 } from \"zod\";\n\n// packages/shared/src/duration.ts\nimport { z } from \"zod\";\nvar DURATION_PATTERN = /^(\\d+)(ms|s|m|h)$/;\nvar UNIT_MILLIS = {\n ms: 1,\n s: 1e3,\n m: 6e4,\n h: 36e5\n};\nfunction parseDuration(value) {\n const match = DURATION_PATTERN.exec(value);\n if (!match) return null;\n const [, rawAmount, rawUnit] = match;\n if (rawAmount === void 0 || rawUnit === void 0) return null;\n const amount = Number(rawAmount);\n if (amount <= 0) return null;\n return amount * UNIT_MILLIS[rawUnit];\n}\nvar durationSchema = z.string().refine((value) => parseDuration(value) !== null, {\n message: 'must be a positive duration like \"30s\", \"5m\", or \"1h\"'\n}).brand();\n\n// packages/shared/src/board-config.ts\nvar positionSchema = z2.object({\n x: z2.number().int().min(0),\n y: z2.number().int().min(0),\n w: z2.number().int().min(1).max(12),\n h: z2.number().int().min(1)\n});\nvar panelDisplayRoles = [\"primary\", \"supporting\", \"compact\"];\nvar panelDisplaySchema = z2.string().min(1);\nvar panelSchema = z2.looseObject({\n id: z2.string().min(1),\n type: z2.string().min(1),\n source: z2.string().min(1).optional(),\n /** Semantic visual weight; position still controls the panel's explicit grid placement. */\n display: panelDisplaySchema.optional(),\n /** Advisory in v1 — a panel without a position renders in config order rather than not at all. */\n position: positionSchema.optional(),\n refresh: durationSchema.optional(),\n /** A deliberate override only. Adapters derive links; hand-written ones drift. */\n link: z2.url().optional(),\n /** Source-agnostic endpoint used by the http-value signal. */\n url: z2.url().optional(),\n /** Small, deliberate JSON path subset: $.version, $.deployment.version, or $.response.docs[0].latestVersion. */\n json_path: z2.string().regex(/^\\$(?:\\.[A-Za-z_][A-Za-z0-9_]*|\\[\\d+\\])*$/, \"must be a simple JSON path\").optional()\n});\nvar sourceSchema = z2.looseObject({\n type: z2.string().min(1),\n /** Credentials are never in the config — only the name of the env var holding one. */\n token_env: z2.string().min(1).optional()\n});\nvar boardSchema = z2.object({\n refresh: durationSchema.optional(),\n panels: z2.array(panelSchema).min(1).superRefine((panels, ctx) => {\n const seen = /* @__PURE__ */ new Map();\n panels.forEach((panel, index) => {\n const firstIndex = seen.get(panel.id);\n if (firstIndex === void 0) {\n seen.set(panel.id, index);\n return;\n }\n ctx.addIssue({\n code: \"custom\",\n path: [index, \"id\"],\n message: `duplicate panel id \"${panel.id}\" (already used by panel at index ${firstIndex})`\n });\n });\n })\n});\nvar authSchema = z2.object({\n issuer: z2.url(),\n /** Absent in gateway mode — the gateway does the flow, the proxy still validates. */\n client_id: z2.string().min(1).optional(),\n allow: z2.object({\n groups: z2.array(z2.string().min(1)).optional(),\n subjects: z2.array(z2.string().min(1)).optional()\n }).optional()\n});\nvar boardConfigSchema = z2.object({\n sources: z2.record(z2.string().min(1), sourceSchema).default({}),\n boards: z2.record(z2.string().min(1), boardSchema),\n auth: authSchema.optional()\n});\nfunction resolveRefreshMillis(args) {\n const requested = args.panelOverrideMillis ?? args.boardDefaultMillis;\n return Math.max(requested, args.adapterFloorMillis);\n}\n\n// packages/shared/src/client-env.ts\nimport { z as z3 } from \"zod\";\nvar clientEnvSchema = z3.object({\n /** Where the immutable client assets live. The one variable that repoints the whole client. */\n assetPath: z3.string().min(1),\n /** Same-origin path the proxy is mounted at, so there is no CORS and no cookie problem. */\n proxyPath: z3.string().min(1),\n /** Which board this is, resolved server-side so the client parses no URLs. */\n board: z3.string().min(1),\n /** The client version being served, so two published versions are visibly distinguishable. */\n clientVersion: z3.string().min(1),\n /** Omitted entirely when unauthenticated — presence is what turns auth on. */\n auth: z3.object({\n issuer: z3.string().min(1),\n clientId: z3.string().min(1).optional()\n }).optional()\n});\nfunction readClientEnv(source = globalThis.window?.env) {\n const result = clientEnvSchema.safeParse(source);\n if (!result.success) {\n throw new Error(\n `window.env is missing or invalid \\u2014 the server did not inject usable configuration.\n${z3.prettifyError(result.error)}`\n );\n }\n return result.data;\n}\n\n// packages/shared/src/envelope.ts\nimport { z as z4 } from \"zod\";\nvar errorKindSchema = z4.enum([\n \"unreachable\",\n \"unauthorized\",\n \"not-found\",\n \"no-runs\",\n \"upstream-error\"\n]);\nvar envelopeErrorSchema = z4.object({\n kind: errorKindSchema,\n message: z4.string()\n});\nvar envelopeSchema = z4.discriminatedUnion(\"state\", [\n z4.object({\n panelId: z4.string().min(1),\n state: z4.literal(\"ok\"),\n /** From the upstream response `Date`. A cached value must never be shown without its age. */\n observedAt: z4.iso.datetime(),\n /** Adapter-derived. The authority for this panel; the dashboard itself asserts nothing. */\n link: z4.url().nullable(),\n signal: z4.unknown()\n }),\n z4.object({\n panelId: z4.string().min(1),\n state: z4.literal(\"error\"),\n /**\n * Populated even on error: knowing *when* a failure was observed is the difference between\n * \"broken just now\" and \"broken all morning\", and a panel that can't reach its source is\n * exactly when a viewer most wants to click through.\n */\n observedAt: z4.iso.datetime(),\n link: z4.url().nullable(),\n error: envelopeErrorSchema\n })\n]);\nvar pipelineStatusSchema = z4.object({\n type: z4.literal(\"pipeline-status\"),\n status: z4.enum([\"passed\", \"failed\", \"running\", \"cancelled\", \"unknown\"]),\n /** The source's unmodified status/result vocabulary, for an honest display. */\n rawStatus: z4.string(),\n name: z4.string().min(1),\n /** The branch represented by this status, when the upstream supplies or filters by one. */\n branch: z4.string().min(1).optional(),\n /** Elapsed execution time, supplied only after the upstream run has completed. */\n durationMs: z4.number().int().nonnegative().optional(),\n /** When this workflow run was last updated by its source, distinct from our observation time. */\n sourceUpdatedAt: z4.iso.datetime().optional()\n});\nvar httpValueSchema = z4.object({\n type: z4.literal(\"http-value\"),\n value: z4.union([z4.string(), z4.number(), z4.boolean()])\n});\nexport {\n authSchema,\n boardConfigSchema,\n boardSchema,\n clientEnvSchema,\n durationSchema,\n envelopeSchema,\n errorKindSchema,\n httpValueSchema,\n panelDisplayRoles,\n panelDisplaySchema,\n panelSchema,\n parseDuration,\n pipelineStatusSchema,\n positionSchema,\n readClientEnv,\n resolveRefreshMillis,\n sourceSchema\n};\n",".diagnostics {\n position: relative;\n}\n\n.button {\n border: 1px solid var(--edge);\n border-radius: 0.25rem;\n background: var(--surface-raised);\n color: var(--ink-secondary);\n font: inherit;\n padding: 0.3em 0.55em;\n}\n\n.area {\n position: absolute;\n z-index: 1;\n right: 0;\n bottom: calc(100% + 0.5rem);\n width: min(24rem, 90vw);\n padding: 0.75rem;\n border: 1px solid var(--edge);\n border-radius: var(--radius);\n background: var(--surface-raised);\n box-shadow: 0 0.5rem 1.5rem #0008;\n}\n\n.area p {\n margin: 0 0 0.5rem;\n}\n\n.actions {\n display: flex;\n gap: 0.5rem;\n}\n","import { useState, useSyncExternalStore } from 'react'\nimport styles from './Diagnostics.module.css'\nimport type { BrowserDiagnosticStore } from './diagnostics.ts'\n\nexport function Diagnostics({ log }: { log: BrowserDiagnosticStore }) {\n const [open, setOpen] = useState(false)\n const count = useSyncExternalStore(log.subscribe, log.count, log.count)\n const download = () => {\n const blob = new Blob([JSON.stringify(log.export(), null, 2)], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `dashboard-diagnostics-${new Date().toISOString().replaceAll(':', '-')}.json`\n link.click()\n URL.revokeObjectURL(url)\n }\n const clear = () => {\n if (!window.confirm('Clear this browser’s retained dashboard diagnostics?')) return\n log.clear()\n }\n\n return (\n <section className={styles.diagnostics}>\n <button\n className={styles.button}\n type=\"button\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n Diagnostics ({count})\n </button>\n {open && (\n <div className={styles.area}>\n <p>{count} retained browser-local events. They are never uploaded.</p>\n <div className={styles.actions}>\n <button className={styles.button} type=\"button\" onClick={download}>\n Download\n </button>\n <button className={styles.button} type=\"button\" onClick={clear}>\n Clear\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n","import type { ClientEnv, Envelope } from '@ze-great-dashboard/shared'\n\nconst storageKey = 'ze-great-dashboard.diagnostics.v1'\nexport const diagnosticsSchemaVersion = 1\nconst maximumEvents = 2_000\nconst maximumAgeMillis = 7 * 24 * 60 * 60 * 1_000\nconst diagnosticKinds = [\n 'session-start',\n 'board-fetch-start',\n 'board-fetch-response',\n 'board-fetch-parse-failure',\n 'board-fetch-failure',\n 'panel-fetch-start',\n 'panel-fetch-response',\n 'panel-fetch-parse-failure',\n 'panel-fetch-failure',\n 'panel-rendered',\n] as const\n\nexport type CacheMetadata = Partial<\n Record<'cacheControl' | 'etag' | 'lastModified' | 'date' | 'age', string>\n>\n\ntype EventMetadata = {\n schemaVersion: typeof diagnosticsSchemaVersion\n at: string\n sessionId: string\n board: string\n}\n\nexport type RenderedPanelDiagnostic = {\n state: Envelope['state']\n status?: string\n link: string | null\n}\n\nexport type DiagnosticEventInput =\n | { kind: 'session-start' }\n | { kind: 'board-fetch-start'; path: string }\n | {\n kind: 'board-fetch-response'\n path: string\n status?: number\n cache?: CacheMetadata\n boardSummary?: { panelCount: number; panelIds: string[] }\n }\n | { kind: 'board-fetch-parse-failure'; path: string; message: string }\n | { kind: 'board-fetch-failure'; path: string; message: string }\n | { kind: 'panel-fetch-start'; panelId: string; path: string }\n | {\n kind: 'panel-fetch-response'\n panelId: string\n path: string\n status?: number\n cache?: CacheMetadata\n envelope?: Envelope\n }\n | { kind: 'panel-fetch-parse-failure'; panelId: string; path: string; message: string }\n | { kind: 'panel-fetch-failure'; panelId: string; path: string; message: string }\n | {\n kind: 'panel-rendered'\n panelId: string\n path: string\n rendered: RenderedPanelDiagnostic\n }\n\nexport type DiagnosticEvent = DiagnosticEventInput extends infer Event\n ? Event extends DiagnosticEventInput\n ? Event & EventMetadata\n : never\n : never\n\n/** The only interface client features need in order to emit browser diagnostics. */\nexport interface DiagnosticSink {\n record(event: DiagnosticEventInput): void\n}\n\ntype PersistedDiagnosticEvent = Omit<DiagnosticEvent, 'schemaVersion'> & { schemaVersion?: number }\ntype StoredDiagnostics = { schemaVersion: number; events: PersistedDiagnosticEvent[] }\n\ntype StorageLike = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>\n\n/** Browser-local diagnostic evidence. It deliberately has no server or shared-contract counterpart. */\nexport class BrowserDiagnosticStore implements DiagnosticSink {\n private events: DiagnosticEvent[] = []\n private readonly listeners = new Set<() => void>()\n private readonly sessionId = crypto.randomUUID?.() ?? `${Date.now()}-${Math.random()}`\n\n constructor(\n private readonly env: ClientEnv,\n private readonly storage: StorageLike | undefined = storageOrUndefined(),\n private readonly now: () => Date = () => new Date(),\n ) {\n this.events = this.read()\n this.record({ kind: 'session-start' })\n }\n\n record(event: DiagnosticEventInput) {\n this.events = prune(\n [\n ...this.events,\n {\n ...event,\n schemaVersion: diagnosticsSchemaVersion,\n at: this.now().toISOString(),\n sessionId: this.sessionId,\n board: this.env.board,\n },\n ],\n this.now(),\n )\n this.persist()\n this.notify()\n }\n\n count = () => {\n return this.events.length\n }\n\n clear() {\n this.events = []\n try {\n this.storage?.removeItem(storageKey)\n } catch {\n // Diagnostics must not interrupt the radiator when browser storage is unavailable.\n }\n this.notify()\n }\n\n subscribe = (listener: () => void) => {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n export = () => {\n return {\n schemaVersion: diagnosticsSchemaVersion,\n exportedAt: this.now().toISOString(),\n client: {\n version: this.env.clientVersion,\n assetPath: this.env.assetPath,\n board: this.env.board,\n sessionId: this.sessionId,\n },\n events: this.events,\n }\n }\n\n private read(): DiagnosticEvent[] {\n try {\n const raw = this.storage?.getItem(storageKey)\n if (!raw) return []\n const parsed = JSON.parse(raw) as Partial<StoredDiagnostics>\n if (parsed.schemaVersion !== diagnosticsSchemaVersion || !Array.isArray(parsed.events)) {\n this.storage?.removeItem(storageKey)\n return []\n }\n if (!parsed.events.every(isDiagnosticEvent)) {\n this.storage?.removeItem(storageKey)\n return []\n }\n // v1 retained events predate per-event versions. Their containing store was already v1, so\n // retain them and normalize on the next write rather than discarding viewer evidence.\n return prune(\n parsed.events.map(\n (event) => ({ ...event, schemaVersion: diagnosticsSchemaVersion }) as DiagnosticEvent,\n ),\n this.now(),\n )\n } catch {\n try {\n this.storage?.removeItem(storageKey)\n } catch {\n // Storage can fail on both reads and writes (for example, private browsing policies).\n }\n return []\n }\n }\n\n private persist() {\n try {\n this.storage?.setItem(\n storageKey,\n JSON.stringify({ schemaVersion: diagnosticsSchemaVersion, events: this.events }),\n )\n } catch {\n // Keep the in-memory record for this page, but never make diagnostics a rendering failure.\n }\n }\n\n private notify() {\n for (const listener of this.listeners) listener()\n }\n}\n\nexport function cacheMetadata(headers: Headers): CacheMetadata | undefined {\n const entries = [\n ['cacheControl', 'cache-control'],\n ['etag', 'etag'],\n ['lastModified', 'last-modified'],\n ['date', 'date'],\n ['age', 'age'],\n ] as const\n const metadata = Object.fromEntries(\n entries.flatMap(([key, header]) => {\n const value = headers.get(header)\n return value ? [[key, value]] : []\n }),\n ) as CacheMetadata\n return Object.keys(metadata).length ? metadata : undefined\n}\n\nfunction prune(events: DiagnosticEvent[], now: Date) {\n const cutoff = now.valueOf() - maximumAgeMillis\n return events\n .filter(\n (event) =>\n Number.isFinite(new Date(event.at).valueOf()) && new Date(event.at).valueOf() >= cutoff,\n )\n .slice(-maximumEvents)\n}\n\nfunction isDiagnosticEvent(value: unknown): value is PersistedDiagnosticEvent {\n if (!value || typeof value !== 'object') return false\n const event = value as Partial<DiagnosticEvent>\n return (\n (event.schemaVersion === undefined || event.schemaVersion === diagnosticsSchemaVersion) &&\n typeof event.at === 'string' &&\n Number.isFinite(new Date(event.at).valueOf()) &&\n typeof event.sessionId === 'string' &&\n typeof event.board === 'string' &&\n diagnosticKinds.includes(event.kind as (typeof diagnosticKinds)[number])\n )\n}\n\nfunction storageOrUndefined(): StorageLike | undefined {\n try {\n return window.localStorage\n } catch {\n return undefined\n }\n}\n","export function ObservedAt({ value, label = 'As of' }: { value: string; label?: string }) {\n const observed = new Date(value)\n const formatted = observed.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n const age = Date.now() - observed.getTime()\n const stale = age > 5 * 60 * 1000\n const ageText = formatAge(age)\n return (\n <p className={`panel__hint panel__observed${stale ? ' panel__observed--stale' : ''}`}>\n <span aria-hidden=\"true\">◷</span> {label} {formatted} · {ageText}\n </p>\n )\n}\n\nexport function formatAge(milliseconds: number) {\n if (milliseconds < 60_000) return 'just now'\n const minutes = Math.floor(milliseconds / 60_000)\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n return `${hours}h ${minutes % 60}m ago`\n}\n","import type { Panel } from '@ze-great-dashboard/shared'\nimport type { CSSProperties } from 'react'\n\n/** Convert the advisory board position into CSS grid placement. */\nexport function panelLayout(panel: Panel): CSSProperties | undefined {\n const position = panel.position\n if (!position) return undefined\n\n return {\n '--panel-column': `${position.x + 1} / span ${position.w}`,\n '--panel-row': `${position.y + 1} / span ${position.h}`,\n } as CSSProperties\n}\n","import type { Envelope, Panel } from '@ze-great-dashboard/shared'\nimport type { ReactNode } from 'react'\nimport { panelLayout } from './panel-layout.ts'\n\nexport function PanelFrame({\n panel,\n envelope,\n error = false,\n children,\n}: {\n panel: Panel\n envelope?: Envelope\n error?: boolean\n children: ReactNode\n}) {\n const display =\n panel.display === 'primary' || panel.display === 'compact' ? panel.display : 'supporting'\n return (\n <section\n className={`panel panel--${display}${error ? ' panel--error' : ''}`}\n style={panelLayout(panel)}\n aria-busy={envelope ? undefined : true}\n >\n <h2 className=\"panel__label\">{panel.id}</h2>\n {children}\n <PanelSourceLink panelId={panel.id} link={envelope?.link} />\n </section>\n )\n}\n\nfunction PanelSourceLink({ panelId, link }: { panelId: string; link?: string | null }) {\n if (!link) return null\n\n return (\n <a\n className=\"panel__link\"\n href={link}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={`View source for ${panelId} (opens in a new tab)`}\n >\n View source <span aria-hidden=\"true\">↗</span>\n </a>\n )\n}\n","import type { Panel } from '@ze-great-dashboard/shared'\nimport { type Envelope, httpValueSchema } from '@ze-great-dashboard/shared'\nimport { ObservedAt } from './ObservedAt.tsx'\nimport { PanelFrame } from './PanelFrame.tsx'\n\nexport function HttpValuePanel({\n panel,\n envelope,\n}: {\n panel: Panel\n envelope: Envelope | undefined\n}) {\n if (!envelope)\n return (\n <PanelFrame panel={panel}>\n <p className=\"panel__hint\">Loading…</p>\n </PanelFrame>\n )\n if (envelope.state === 'error')\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Unable to read</p>\n <p className=\"panel__hint\">{envelope.error.message}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n const signal = httpValueSchema.safeParse(envelope.signal)\n if (!signal.success)\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Invalid value</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n return (\n <PanelFrame panel={panel} envelope={envelope}>\n <p className=\"panel__status\">{String(signal.data.value)}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n}\n","/**\n * A panel-shaped hole, so the grid can be looked at and argued with before any signal exists.\n * Replaced by real panels in Stage 2 — nothing here is meant to survive.\n */\nexport function PanelPlaceholder({\n label,\n hint,\n wide = false,\n}: {\n label: string\n hint: string\n wide?: boolean\n}) {\n return (\n <section className={wide ? 'panel panel--wide' : 'panel'}>\n <h2 className=\"panel__label\">{label}</h2>\n <p className=\"panel__hint\">{hint}</p>\n </section>\n )\n}\n","import { type Envelope, type Panel, pipelineStatusSchema } from '@ze-great-dashboard/shared'\nimport { ObservedAt } from './ObservedAt.tsx'\nimport { PanelFrame } from './PanelFrame.tsx'\n\nexport function PipelinePanel({\n panel,\n envelope,\n}: {\n panel: Panel\n envelope: Envelope | undefined\n}) {\n if (!envelope)\n return (\n <PanelFrame panel={panel}>\n <p className=\"panel__hint\">Loading…</p>\n </PanelFrame>\n )\n if (envelope.state === 'error') {\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">\n ⚠ {envelope.error.kind === 'no-runs' ? 'No workflow runs' : 'Unable to read'}\n </p>\n <p className=\"panel__hint\">{envelope.error.message}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n }\n\n const signal = pipelineStatusSchema.safeParse(envelope.signal)\n if (!signal.success)\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Invalid signal</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n const presentation = statusPresentation(signal.data.status)\n return (\n <PanelFrame panel={panel} envelope={envelope}>\n <p className={`panel__status panel__status--${signal.data.status}`}>\n {presentation.glyph} {presentation.label}\n </p>\n <p className=\"panel__hint\">\n {signal.data.name} · {signal.data.rawStatus}\n {signal.data.branch && (\n <span className=\"panel__branch\" title={`Branch: ${signal.data.branch}`}>\n <span aria-hidden=\"true\"> · ⎇ </span>\n <span className=\"screen-reader-only\">Branch: </span>\n {signal.data.branch}\n </span>\n )}\n </p>\n {signal.data.status !== 'running' && signal.data.durationMs !== undefined && (\n <p className=\"panel__hint\">Took {formatDuration(signal.data.durationMs)}</p>\n )}\n {signal.data.sourceUpdatedAt && (\n <ObservedAt value={signal.data.sourceUpdatedAt} label=\"Run updated\" />\n )}\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n}\n\nfunction formatDuration(durationMs: number) {\n const totalSeconds = Math.floor(durationMs / 1_000)\n const hours = Math.floor(totalSeconds / 3_600)\n const minutes = Math.floor((totalSeconds % 3_600) / 60)\n const seconds = totalSeconds % 60\n\n if (hours > 0) return `${hours}h ${minutes}m`\n if (minutes > 0) return `${minutes}m ${seconds}s`\n return `${seconds}s`\n}\n\nfunction statusPresentation(status: 'passed' | 'failed' | 'running' | 'cancelled' | 'unknown') {\n switch (status) {\n case 'passed':\n return { glyph: '✓', label: 'Passed' }\n case 'failed':\n return { glyph: '✕', label: 'Failed' }\n case 'running':\n return { glyph: '↻', label: 'Running' }\n case 'cancelled':\n return { glyph: '⊘', label: 'Cancelled' }\n case 'unknown':\n return { glyph: '?', label: 'Unknown' }\n }\n}\n","import { type Envelope, type Panel, pipelineStatusSchema } from '@ze-great-dashboard/shared'\nimport type { RenderedPanelDiagnostic } from './diagnostics.ts'\n\n/**\n * The compact public fact a viewer saw. Keep new panel projections here beside their\n * presentation boundary; this is intentionally not a source or server event contract.\n */\nexport function projectPanelDiagnostic(panel: Panel, envelope: Envelope): RenderedPanelDiagnostic {\n if (envelope.state === 'error') return { state: envelope.state, link: envelope.link }\n\n if (panel.type === 'pipeline-status') {\n const signal = pipelineStatusSchema.safeParse(envelope.signal)\n return {\n state: envelope.state,\n status: signal.success ? signal.data.status : undefined,\n link: envelope.link,\n }\n }\n\n return { state: envelope.state, link: envelope.link }\n}\n\nexport function panelDiagnosticChanged(\n previous: Envelope | undefined,\n panel: Panel,\n next: Envelope,\n) {\n if (!previous) return true\n const before = projectPanelDiagnostic(panel, previous)\n const after = projectPanelDiagnostic(panel, next)\n return (\n before.state !== after.state || before.status !== after.status || before.link !== after.link\n )\n}\n","import {\n type Board,\n type ClientEnv,\n type Envelope,\n envelopeSchema,\n parseDuration,\n resolveRefreshMillis,\n} from '@ze-great-dashboard/shared'\nimport { useEffect, useRef, useState } from 'react'\nimport { cacheMetadata, type DiagnosticSink } from './diagnostics.ts'\nimport { panelDiagnosticChanged, projectPanelDiagnostic } from './panel-diagnostics.ts'\n\nexport function usePanelSignals({\n board,\n env,\n diagnostics,\n}: {\n board: Board | undefined\n env: ClientEnv\n diagnostics: DiagnosticSink\n}) {\n const [signals, setSignals] = useState<Record<string, Envelope | undefined>>({})\n const signalsRef = useRef<Record<string, Envelope | undefined>>({})\n\n useEffect(() => {\n signalsRef.current = {}\n setSignals({})\n if (!board) return\n\n let cancelled = false\n const timers: number[] = []\n for (const panel of board.panels) {\n if (panel.type !== 'pipeline-status' && panel.type !== 'http-value') continue\n let inFlight = false\n const refreshMillis = resolveRefreshMillis({\n boardDefaultMillis: parseDuration(board.refresh ?? '60s') ?? 60_000,\n panelOverrideMillis: panel.refresh\n ? (parseDuration(panel.refresh) ?? undefined)\n : undefined,\n adapterFloorMillis: 0,\n })\n const path = `${env.proxyPath}/panel/${encodeURIComponent(env.board)}/${encodeURIComponent(panel.id)}`\n const refresh = () => {\n if (cancelled || inFlight) return\n inFlight = true\n diagnostics.record({ kind: 'panel-fetch-start', panelId: panel.id, path })\n fetch(path)\n .then(async (response) => {\n diagnostics.record({\n kind: 'panel-fetch-response',\n panelId: panel.id,\n path,\n status: response.status,\n cache: cacheMetadata(response.headers),\n })\n if (response.status === 304) return undefined\n try {\n return await response.json()\n } catch (error) {\n diagnostics.record({\n kind: 'panel-fetch-parse-failure',\n panelId: panel.id,\n path,\n message: errorMessage(error),\n })\n throw new DiagnosticParseFailure(error)\n }\n })\n .then((value: unknown) => {\n const envelope = parseEnvelope(value)\n if (value !== undefined && !envelope) {\n diagnostics.record({\n kind: 'panel-fetch-parse-failure',\n panelId: panel.id,\n path,\n message: 'Response was not a valid signal envelope.',\n })\n }\n if (!cancelled && envelope) {\n diagnostics.record({\n kind: 'panel-fetch-response',\n panelId: panel.id,\n path,\n envelope,\n })\n const previous = signalsRef.current[panel.id]\n if (panelDiagnosticChanged(previous, panel, envelope)) {\n diagnostics.record({\n kind: 'panel-rendered',\n panelId: panel.id,\n path,\n rendered: projectPanelDiagnostic(panel, envelope),\n })\n }\n signalsRef.current = { ...signalsRef.current, [panel.id]: envelope }\n setSignals(signalsRef.current)\n }\n })\n .catch((error) => {\n if (!(error instanceof DiagnosticParseFailure)) {\n diagnostics.record({\n kind: 'panel-fetch-failure',\n panelId: panel.id,\n path,\n message: errorMessage(error),\n })\n }\n })\n .finally(() => {\n inFlight = false\n })\n }\n refresh()\n timers.push(window.setInterval(refresh, refreshMillis))\n }\n return () => {\n cancelled = true\n for (const timer of timers) window.clearInterval(timer)\n }\n }, [board, diagnostics, env.board, env.proxyPath])\n\n return signals\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error)\n}\n\nclass DiagnosticParseFailure extends Error {\n constructor(error: unknown) {\n super(errorMessage(error))\n }\n}\n\nfunction parseEnvelope(value: unknown): Envelope | undefined {\n const result = envelopeSchema.safeParse(value)\n return result.success ? result.data : undefined\n}\n","import type { Board, ClientEnv } from '@ze-great-dashboard/shared'\nimport { useEffect, useRef, useState } from 'react'\nimport { Diagnostics } from './Diagnostics.tsx'\nimport { BrowserDiagnosticStore, cacheMetadata } from './diagnostics.ts'\nimport { HttpValuePanel } from './HttpValuePanel.tsx'\nimport { PanelPlaceholder } from './PanelPlaceholder.tsx'\nimport { PipelinePanel } from './PipelinePanel.tsx'\nimport { usePanelSignals } from './usePanelSignals.ts'\n\n/**\n * The board shell.\n *\n * Stage 1 has no data: there is no proxy endpoint to poll yet. What this proves is the plumbing —\n * that the client was handed its configuration by the server and knows which version of itself it\n * is. The version readout is what makes two published versions visibly different, which is the\n * whole point of the Stage 1 exit criterion.\n */\nexport function App({ env }: { env: ClientEnv }) {\n const [board, setBoard] = useState<Board>()\n const [loadedBoardName, setLoadedBoardName] = useState<string>()\n const diagnosticsRef = useRef<BrowserDiagnosticStore | null>(null)\n if (!diagnosticsRef.current) diagnosticsRef.current = new BrowserDiagnosticStore(env)\n const diagnostics = diagnosticsRef.current\n const signals = usePanelSignals({\n board: loadedBoardName === env.board ? board : undefined,\n env,\n diagnostics,\n })\n\n useEffect(() => {\n let cancelled = false\n setBoard(undefined)\n setLoadedBoardName(undefined)\n const path = `${env.proxyPath}/boards/${encodeURIComponent(env.board)}`\n diagnostics.record({ kind: 'board-fetch-start', path })\n fetch(path)\n .then(async (response) => {\n diagnostics.record({\n kind: 'board-fetch-response',\n path,\n status: response.status,\n cache: cacheMetadata(response.headers),\n })\n if (!response.ok) throw new Error(`Board configuration returned ${response.status}`)\n try {\n return await response.json()\n } catch (error) {\n diagnostics.record({\n kind: 'board-fetch-parse-failure',\n path,\n message: errorMessage(error),\n })\n throw new DiagnosticParseFailure(error)\n }\n })\n .then((value: unknown) => {\n if (!cancelled) {\n setBoard(value as Board)\n setLoadedBoardName(env.board)\n const panels = Array.isArray((value as { panels?: unknown }).panels)\n ? (value as { panels: Array<{ id?: unknown }> }).panels\n : []\n diagnostics.record({\n kind: 'board-fetch-response',\n path,\n boardSummary: {\n panelCount: panels.length,\n panelIds: panels.flatMap((panel) => (typeof panel.id === 'string' ? [panel.id] : [])),\n },\n })\n }\n })\n .catch((error) => {\n if (!(error instanceof DiagnosticParseFailure)) {\n diagnostics.record({ kind: 'board-fetch-failure', path, message: errorMessage(error) })\n }\n if (!cancelled) {\n setBoard({ panels: [] })\n setLoadedBoardName(env.board)\n }\n })\n return () => {\n cancelled = true\n }\n }, [diagnostics, env.board, env.proxyPath])\n\n return (\n <div className=\"board\">\n <header className=\"board__header\">\n <h1 className=\"board__title\">{env.board}</h1>\n <dl className=\"board__meta\">\n <div>\n <dt>client</dt>\n <dd>{env.clientVersion}</dd>\n </div>\n <div>\n <dt>assets</dt>\n <dd>{env.assetPath}</dd>\n </div>\n </dl>\n </header>\n\n <main className=\"board__grid\">\n {!board && <PanelPlaceholder label=\"board\" hint=\"Loading configuration…\" wide />}\n {board?.panels.map((panel) =>\n panel.type === 'pipeline-status' ? (\n <PipelinePanel key={panel.id} panel={panel} envelope={signals[panel.id]} />\n ) : panel.type === 'http-value' ? (\n <HttpValuePanel key={panel.id} panel={panel} envelope={signals[panel.id]} />\n ) : (\n <PanelPlaceholder key={panel.id} label={panel.type} hint=\"Not wired yet\" wide />\n ),\n )}\n </main>\n\n <footer className=\"board__footer\">\n <span>Signals are read live from their configured authorities.</span>\n <Diagnostics log={diagnostics} />\n </footer>\n </div>\n )\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error)\n}\n\nclass DiagnosticParseFailure extends Error {\n constructor(error: unknown) {\n super(errorMessage(error))\n }\n}\n","export function ConfigError({ message }: { message: string }) {\n return (\n <div className=\"config-error\" role=\"alert\">\n <h1>⚠️ Dashboard misconfigured</h1>\n <p>The page loaded, but the server did not hand it usable configuration.</p>\n <pre>{message}</pre>\n </div>\n )\n}\n","import { readClientEnv } from '@ze-great-dashboard/shared'\nimport { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { App } from './App.tsx'\nimport { ConfigError } from './ConfigError.tsx'\nimport './styles.css'\n\nconst container = document.getElementById('root')\nif (!container) {\n throw new Error('No #root element in the document — the template is not the one we expect.')\n}\n\nconst root = createRoot(container)\n\ntry {\n root.render(\n <StrictMode>\n <App env={readClientEnv()} />\n </StrictMode>,\n )\n} catch (error) {\n // A radiator that fails silently is worse than one that says what's wrong. If configuration\n // never arrived, say so on screen rather than rendering an empty board.\n root.render(<ConfigError message={error instanceof Error ? error.message : String(error)} />)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._diagnostics_1pu1f_1{position:relative}._button_1pu1f_5{border:1px solid var(--edge);background:var(--surface-raised);color:var(--ink-secondary);font:inherit;border-radius:.25rem;padding:.3em .55em}._area_1pu1f_14{z-index:1;border:1px solid var(--edge);border-radius:var(--radius);background:var(--surface-raised);width:min(24rem,90vw);padding:.75rem;position:absolute;bottom:calc(100% + .5rem);right:0;box-shadow:0 .5rem 1.5rem #0008}._area_1pu1f_14 p{margin:0 0 .5rem}._actions_1pu1f_31{gap:.5rem;display:flex}:root{--surface:#12140f;--surface-raised:#1a1a19;--ink-primary:#fff;--ink-secondary:#c3c2b7;--ink-muted:#898781;--edge:#2f312b;--status-good:#0ca30c;--status-warning:#fab219;--status-serious:#ec835a;--status-critical:#d03b3b;--gap:1rem;--radius:.5rem;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}.screen-reader-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}html,body{height:100%;margin:0}body{background:var(--surface);color:var(--ink-primary);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Helvetica,Arial,sans-serif;font-size:clamp(16px,1.1vw + .5rem,28px);line-height:1.4;overflow-x:hidden}.board{gap:var(--gap);height:100dvh;min-height:0;padding:var(--gap);flex-direction:column;display:flex;overflow:hidden}.board__header{justify-content:space-between;align-items:baseline;gap:var(--gap);border-bottom:1px solid var(--edge);padding-bottom:var(--gap);flex-wrap:wrap;flex:none;display:flex}.board__title{letter-spacing:-.01em;margin:0;font-size:1.75em;font-weight:650}.board__meta{gap:calc(var(--gap) * 1.5);flex-wrap:wrap;margin:0;font-size:.7em;display:flex}.board__meta div{flex-direction:column;display:flex}.board__meta dt{color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;font-size:.8em}.board__meta dd{color:var(--ink-secondary);overflow-wrap:anywhere;margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.board__grid{gap:var(--gap);flex:1;grid-template-columns:repeat(12,1fr);grid-auto-rows:minmax(0,1fr);align-content:stretch;min-height:0;display:grid}.panel{grid-column:var(--panel-column,span 6);grid-row:var(--panel-row,auto);min-width:0;min-height:0;padding:var(--gap);border:1px solid var(--edge);border-radius:var(--radius);background:var(--surface-raised);flex-direction:column;justify-content:center;gap:.25em;display:flex;overflow:hidden}.panel--wide{grid-column:span 12}.panel__label{color:var(--ink-secondary);margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:1em;font-weight:600}.panel__hint{color:var(--ink-muted);margin:0;font-size:.75em}.panel__branch{white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.panel__observed{align-items:center;gap:.35em;display:flex}.panel__observed--stale{color:var(--status-warning)}.panel__status{margin:0;font-size:1.35em;font-weight:650}.panel__status--passed{color:var(--status-good)}.panel__status--failed{color:var(--status-critical)}.panel__status--running{color:var(--status-warning)}.panel__status--cancelled,.panel__status--unknown{color:var(--ink-secondary)}.panel--error{border-color:var(--status-serious)}.panel__link{color:var(--ink-secondary);text-underline-offset:.18em;align-self:flex-start;margin-top:.2em;font-size:.75em;font-weight:600}.panel__link:hover{color:var(--ink-primary)}.panel--primary .panel__status{font-size:1.5em}.panel--compact{gap:.1em;min-height:3.5em;padding:.65em}.panel--compact .panel__label{font-size:.8em}.panel--compact .panel__status{font-size:1.1em}.panel--compact .panel__hint,.panel--compact .panel__link{font-size:.65em}.board__footer{color:var(--ink-muted);flex-wrap:wrap;flex:none;justify-content:space-between;align-items:center;gap:.5rem;font-size:.75em;display:flex}.config-error{max-width:60ch;color:var(--ink-primary);margin:0 auto;padding:2rem}.config-error pre{border-radius:var(--radius);border:1px solid var(--status-critical);background:var(--surface-raised);color:var(--ink-secondary);white-space:pre-wrap;overflow-wrap:anywhere;padding:1rem;font-size:.75em}@media (width<=60rem){.board{height:auto;min-height:100dvh;overflow:visible}.board__grid{flex:none;grid-template-columns:1fr;grid-auto-rows:auto;min-height:auto}.panel{grid-area:auto/1/auto/-1;min-height:6em}}
|
package/client/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<title>Trust Dashboard</title>
|
|
12
12
|
|
|
13
13
|
<script type="importmap">{"imports":{"react":"https://esm.sh/react@19.2.8","react/":"https://esm.sh/react@19.2.8/","react-dom":"https://esm.sh/react-dom@19.2.8","react-dom/":"https://esm.sh/react-dom@19.2.8/","scheduler":"https://esm.sh/scheduler@0.27.0","scheduler/":"https://esm.sh/scheduler@0.27.0/","zod":"https://esm.sh/zod@4.4.3","zod/":"https://esm.sh/zod@4.4.3/"}}</script>
|
|
14
|
-
<script type="module" crossorigin src="/__ASSET_PATH__/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/__ASSET_PATH__/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/__ASSET_PATH__/assets/index-C_V8l8ss.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/__ASSET_PATH__/assets/index-wNv7uLY2.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/dist/cli.js
CHANGED
|
@@ -754,6 +754,8 @@ var panelSchema = z.looseObject({
|
|
|
754
754
|
id: z.string().min(1),
|
|
755
755
|
type: z.string().min(1),
|
|
756
756
|
source: z.string().min(1).optional(),
|
|
757
|
+
// Cosmetic roles remain open so newer board files can safely fall back on older runtimes.
|
|
758
|
+
display: z.string().min(1).optional(),
|
|
757
759
|
position: positionSchema.optional(),
|
|
758
760
|
refresh: durationSchema.optional(),
|
|
759
761
|
link: z.url().optional(),
|
package/dist/index.js
CHANGED
|
@@ -741,6 +741,8 @@ var panelSchema = z.looseObject({
|
|
|
741
741
|
id: z.string().min(1),
|
|
742
742
|
type: z.string().min(1),
|
|
743
743
|
source: z.string().min(1).optional(),
|
|
744
|
+
// Cosmetic roles remain open so newer board files can safely fall back on older runtimes.
|
|
745
|
+
display: z.string().min(1).optional(),
|
|
744
746
|
position: positionSchema.optional(),
|
|
745
747
|
refresh: durationSchema.optional(),
|
|
746
748
|
link: z.url().optional(),
|
package/dist/internal-board.d.ts
CHANGED
package/dist/lambda.mjs
CHANGED
|
@@ -24781,10 +24781,13 @@ var positionSchema = external_exports.object({
|
|
|
24781
24781
|
w: external_exports.number().int().min(1).max(12),
|
|
24782
24782
|
h: external_exports.number().int().min(1)
|
|
24783
24783
|
});
|
|
24784
|
+
var panelDisplaySchema = external_exports.string().min(1);
|
|
24784
24785
|
var panelSchema = external_exports.looseObject({
|
|
24785
24786
|
id: external_exports.string().min(1),
|
|
24786
24787
|
type: external_exports.string().min(1),
|
|
24787
24788
|
source: external_exports.string().min(1).optional(),
|
|
24789
|
+
/** Semantic visual weight; position still controls the panel's explicit grid placement. */
|
|
24790
|
+
display: panelDisplaySchema.optional(),
|
|
24788
24791
|
/** Advisory in v1 — a panel without a position renders in config order rather than not at all. */
|
|
24789
24792
|
position: positionSchema.optional(),
|
|
24790
24793
|
refresh: durationSchema.optional(),
|
package/package.json
CHANGED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{z as e}from"zod";import{StrictMode as t,useEffect as n,useRef as r,useState as i,useSyncExternalStore as a}from"react";import{createRoot as o}from"react-dom/client";import{jsx as s,jsxs as c}from"react/jsx-runtime";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var l=/^(\d+)(ms|s|m|h)$/,u={ms:1,s:1e3,m:6e4,h:36e5};function d(e){let t=l.exec(e);if(!t)return null;let[,n,r]=t;if(n===void 0||r===void 0)return null;let i=Number(n);return i<=0?null:i*u[r]}var f=e.string().refine(e=>d(e)!==null,{message:`must be a positive duration like "30s", "5m", or "1h"`}).brand(),ee=e.object({x:e.number().int().min(0),y:e.number().int().min(0),w:e.number().int().min(1).max(12),h:e.number().int().min(1)}),p=e.looseObject({id:e.string().min(1),type:e.string().min(1),source:e.string().min(1).optional(),position:ee.optional(),refresh:f.optional(),link:e.url().optional(),url:e.url().optional(),json_path:e.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/,`must be a simple JSON path`).optional()}),m=e.looseObject({type:e.string().min(1),token_env:e.string().min(1).optional()}),h=e.object({refresh:f.optional(),panels:e.array(p).min(1).superRefine((e,t)=>{let n=new Map;e.forEach((e,r)=>{let i=n.get(e.id);if(i===void 0){n.set(e.id,r);return}t.addIssue({code:`custom`,path:[r,`id`],message:`duplicate panel id "${e.id}" (already used by panel at index ${i})`})})})}),g=e.object({issuer:e.url(),client_id:e.string().min(1).optional(),allow:e.object({groups:e.array(e.string().min(1)).optional(),subjects:e.array(e.string().min(1)).optional()}).optional()});e.object({sources:e.record(e.string().min(1),m).default({}),boards:e.record(e.string().min(1),h),auth:g.optional()});function _(e){let t=e.panelOverrideMillis??e.boardDefaultMillis;return Math.max(t,e.adapterFloorMillis)}var v=e.object({assetPath:e.string().min(1),proxyPath:e.string().min(1),board:e.string().min(1),clientVersion:e.string().min(1),auth:e.object({issuer:e.string().min(1),clientId:e.string().min(1).optional()}).optional()});function y(t=globalThis.window?.env){let n=v.safeParse(t);if(!n.success)throw Error(`window.env is missing or invalid \u2014 the server did not inject usable configuration.
|
|
2
|
-
${e.prettifyError(n.error)}`);return n.data}var b=e.enum([`unreachable`,`unauthorized`,`not-found`,`no-runs`,`upstream-error`]),x=e.object({kind:b,message:e.string()}),S=e.discriminatedUnion(`state`,[e.object({panelId:e.string().min(1),state:e.literal(`ok`),observedAt:e.iso.datetime(),link:e.url().nullable(),signal:e.unknown()}),e.object({panelId:e.string().min(1),state:e.literal(`error`),observedAt:e.iso.datetime(),link:e.url().nullable(),error:x})]),C=e.object({type:e.literal(`pipeline-status`),status:e.enum([`passed`,`failed`,`running`,`cancelled`,`unknown`]),rawStatus:e.string(),name:e.string().min(1),branch:e.string().min(1).optional(),durationMs:e.number().int().nonnegative().optional(),sourceUpdatedAt:e.iso.datetime().optional()}),w=e.object({type:e.literal(`http-value`),value:e.union([e.string(),e.number(),e.boolean()])});function T({log:e}){let[t,n]=i(!1),r=a(e.subscribe,e.count,e.count);return c(`section`,{className:`diagnostics`,children:[c(`button`,{className:`diagnostics__toggle`,type:`button`,"aria-expanded":t,onClick:()=>n(!t),children:[`Diagnostics (`,r,`)`]}),t&&c(`div`,{className:`diagnostics__area`,children:[c(`p`,{children:[r,` retained browser-local events. They are never uploaded.`]}),c(`div`,{className:`diagnostics__actions`,children:[s(`button`,{type:`button`,onClick:()=>{let t=new Blob([JSON.stringify(e.export(),null,2)],{type:`application/json`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`dashboard-diagnostics-${new Date().toISOString().replaceAll(`:`,`-`)}.json`,r.click(),URL.revokeObjectURL(n)},children:`Download`}),s(`button`,{type:`button`,onClick:()=>{window.confirm(`Clear this browser’s retained dashboard diagnostics?`)&&e.clear()},children:`Clear`})]})]})]})}var E=`ze-great-dashboard.diagnostics.v1`,te=6048e5,D=[`session-start`,`board-fetch-start`,`board-fetch-response`,`board-fetch-parse-failure`,`board-fetch-failure`,`panel-fetch-start`,`panel-fetch-response`,`panel-fetch-parse-failure`,`panel-fetch-failure`,`panel-rendered`],O=class{env;storage;now;events=[];listeners=new Set;sessionId=crypto.randomUUID?.()??`${Date.now()}-${Math.random()}`;constructor(e,t=M(),n=()=>new Date){this.env=e,this.storage=t,this.now=n,this.events=this.read(),this.record({kind:`session-start`})}record(e){this.events=A([...this.events,{...e,schemaVersion:1,at:this.now().toISOString(),sessionId:this.sessionId,board:this.env.board}],this.now()),this.persist(),this.notify()}count=()=>this.events.length;clear(){this.events=[];try{this.storage?.removeItem(E)}catch{}this.notify()}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});export=()=>({schemaVersion:1,exportedAt:this.now().toISOString(),client:{version:this.env.clientVersion,assetPath:this.env.assetPath,board:this.env.board,sessionId:this.sessionId},events:this.events});read(){try{let e=this.storage?.getItem(E);if(!e)return[];let t=JSON.parse(e);return t.schemaVersion!==1||!Array.isArray(t.events)||!t.events.every(j)?(this.storage?.removeItem(E),[]):A(t.events.map(e=>({...e,schemaVersion:1})),this.now())}catch{try{this.storage?.removeItem(E)}catch{}return[]}}persist(){try{this.storage?.setItem(E,JSON.stringify({schemaVersion:1,events:this.events}))}catch{}}notify(){for(let e of this.listeners)e()}};function k(e){let t=Object.fromEntries([[`cacheControl`,`cache-control`],[`etag`,`etag`],[`lastModified`,`last-modified`],[`date`,`date`],[`age`,`age`]].flatMap(([t,n])=>{let r=e.get(n);return r?[[t,r]]:[]}));return Object.keys(t).length?t:void 0}function A(e,t){let n=t.valueOf()-te;return e.filter(e=>Number.isFinite(new Date(e.at).valueOf())&&new Date(e.at).valueOf()>=n).slice(-2e3)}function j(e){if(!e||typeof e!=`object`)return!1;let t=e;return(t.schemaVersion===void 0||t.schemaVersion===1)&&typeof t.at==`string`&&Number.isFinite(new Date(t.at).valueOf())&&typeof t.sessionId==`string`&&typeof t.board==`string`&&D.includes(t.kind)}function M(){try{return window.localStorage}catch{return}}function N({value:e,label:t=`As of`}){let n=new Date(e),r=n.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}),i=Date.now()-n.getTime(),a=i>3e5,o=P(i);return c(`p`,{className:`panel__hint panel__observed${a?` panel__observed--stale`:``}`,children:[s(`span`,{"aria-hidden":`true`,children:`◷`}),` `,t,` `,r,` · `,o]})}function P(e){if(e<6e4)return`just now`;let t=Math.floor(e/6e4);return t<60?`${t}m ago`:`${Math.floor(t/60)}h ${t%60}m ago`}function F(e){let t=e.position;if(t)return{"--panel-column":`${t.x+1} / span ${t.w}`,"--panel-row":`${t.y+1} / span ${t.h}`}}function I({panel:e,envelope:t,error:n=!1,children:r}){return c(`section`,{className:n?`panel panel--error`:`panel`,style:F(e),"aria-busy":!t||void 0,children:[s(`h2`,{className:`panel__label`,children:e.id}),r,s(L,{panelId:e.id,link:t?.link})]})}function L({panelId:e,link:t}){return t?c(`a`,{className:`panel__link`,href:t,target:`_blank`,rel:`noopener noreferrer`,"aria-label":`View source for ${e} (opens in a new tab)`,children:[`View source `,s(`span`,{"aria-hidden":`true`,children:`↗`})]}):null}function R({panel:e,envelope:t}){if(!t)return s(I,{panel:e,children:s(`p`,{className:`panel__hint`,children:`Loading…`})});if(t.state===`error`)return c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Unable to read`}),s(`p`,{className:`panel__hint`,children:t.error.message}),s(N,{value:t.observedAt})]});let n=w.safeParse(t.signal);return n.success?c(I,{panel:e,envelope:t,children:[s(`p`,{className:`panel__status`,children:String(n.data.value)}),s(N,{value:t.observedAt})]}):c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Invalid value`}),s(N,{value:t.observedAt})]})}function z({label:e,hint:t,wide:n=!1}){return c(`section`,{className:n?`panel panel--wide`:`panel`,children:[s(`h2`,{className:`panel__label`,children:e}),s(`p`,{className:`panel__hint`,children:t})]})}function B({panel:e,envelope:t}){if(!t)return s(I,{panel:e,children:s(`p`,{className:`panel__hint`,children:`Loading…`})});if(t.state===`error`)return c(I,{panel:e,envelope:t,error:!0,children:[c(`p`,{className:`panel__status`,children:[`⚠ `,t.error.kind===`no-runs`?`No workflow runs`:`Unable to read`]}),s(`p`,{className:`panel__hint`,children:t.error.message}),s(N,{value:t.observedAt})]});let n=C.safeParse(t.signal);if(!n.success)return c(I,{panel:e,envelope:t,error:!0,children:[s(`p`,{className:`panel__status`,children:`⚠ Invalid signal`}),s(N,{value:t.observedAt})]});let r=H(n.data.status);return c(I,{panel:e,envelope:t,children:[c(`p`,{className:`panel__status panel__status--${n.data.status}`,children:[r.glyph,` `,r.label]}),c(`p`,{className:`panel__hint`,children:[n.data.name,` · `,n.data.rawStatus,n.data.branch&&c(`span`,{className:`panel__branch`,title:`Branch: ${n.data.branch}`,children:[s(`span`,{"aria-hidden":`true`,children:` · ⎇ `}),s(`span`,{className:`screen-reader-only`,children:`Branch: `}),n.data.branch]})]}),n.data.status!==`running`&&n.data.durationMs!==void 0&&c(`p`,{className:`panel__hint`,children:[`Took `,V(n.data.durationMs)]}),n.data.sourceUpdatedAt&&s(N,{value:n.data.sourceUpdatedAt,label:`Run updated`}),s(N,{value:t.observedAt})]})}function V(e){let t=Math.floor(e/1e3),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60;return n>0?`${n}h ${r}m`:r>0?`${r}m ${i}s`:`${i}s`}function H(e){switch(e){case`passed`:return{glyph:`✓`,label:`Passed`};case`failed`:return{glyph:`✕`,label:`Failed`};case`running`:return{glyph:`↻`,label:`Running`};case`cancelled`:return{glyph:`⊘`,label:`Cancelled`};case`unknown`:return{glyph:`?`,label:`Unknown`}}}function U(e,t){if(t.state===`error`)return{state:t.state,link:t.link};if(e.type===`pipeline-status`){let e=C.safeParse(t.signal);return{state:t.state,status:e.success?e.data.status:void 0,link:t.link}}return{state:t.state,link:t.link}}function W(e,t,n){if(!e)return!0;let r=U(t,e),i=U(t,n);return r.state!==i.state||r.status!==i.status||r.link!==i.link}function G({board:e,env:t,diagnostics:a}){let[o,s]=i({}),c=r({});return n(()=>{if(c.current={},s({}),!e)return;let n=!1,r=[];for(let i of e.panels){if(i.type!==`pipeline-status`&&i.type!==`http-value`)continue;let o=!1,l=_({boardDefaultMillis:d(e.refresh??`60s`)??6e4,panelOverrideMillis:i.refresh?d(i.refresh)??void 0:void 0,adapterFloorMillis:0}),u=`${t.proxyPath}/panel/${encodeURIComponent(t.board)}/${encodeURIComponent(i.id)}`,f=()=>{n||o||(o=!0,a.record({kind:`panel-fetch-start`,panelId:i.id,path:u}),fetch(u).then(async e=>{if(a.record({kind:`panel-fetch-response`,panelId:i.id,path:u,status:e.status,cache:k(e.headers)}),e.status!==304)try{return await e.json()}catch(e){throw a.record({kind:`panel-fetch-parse-failure`,panelId:i.id,path:u,message:K(e)}),new q(e)}}).then(e=>{let t=J(e);if(e!==void 0&&!t&&a.record({kind:`panel-fetch-parse-failure`,panelId:i.id,path:u,message:`Response was not a valid signal envelope.`}),!n&&t){a.record({kind:`panel-fetch-response`,panelId:i.id,path:u,envelope:t});let e=c.current[i.id];W(e,i,t)&&a.record({kind:`panel-rendered`,panelId:i.id,path:u,rendered:U(i,t)}),c.current={...c.current,[i.id]:t},s(c.current)}}).catch(e=>{e instanceof q||a.record({kind:`panel-fetch-failure`,panelId:i.id,path:u,message:K(e)})}).finally(()=>{o=!1}))};f(),r.push(window.setInterval(f,l))}return()=>{n=!0;for(let e of r)window.clearInterval(e)}},[e,a,t.board,t.proxyPath]),o}function K(e){return e instanceof Error?e.message:String(e)}var q=class extends Error{constructor(e){super(K(e))}};function J(e){let t=S.safeParse(e);return t.success?t.data:void 0}function Y({env:e}){let[t,a]=i(),[o,l]=i(),u=r(null);u.current||=new O(e);let d=u.current,f=G({board:o===e.board?t:void 0,env:e,diagnostics:d});return n(()=>{let t=!1;a(void 0),l(void 0);let n=`${e.proxyPath}/boards/${encodeURIComponent(e.board)}`;return d.record({kind:`board-fetch-start`,path:n}),fetch(n).then(async e=>{if(d.record({kind:`board-fetch-response`,path:n,status:e.status,cache:k(e.headers)}),!e.ok)throw Error(`Board configuration returned ${e.status}`);try{return await e.json()}catch(e){throw d.record({kind:`board-fetch-parse-failure`,path:n,message:X(e)}),new Z(e)}}).then(r=>{if(!t){a(r),l(e.board);let t=Array.isArray(r.panels)?r.panels:[];d.record({kind:`board-fetch-response`,path:n,boardSummary:{panelCount:t.length,panelIds:t.flatMap(e=>typeof e.id==`string`?[e.id]:[])}})}}).catch(r=>{r instanceof Z||d.record({kind:`board-fetch-failure`,path:n,message:X(r)}),t||(a({panels:[]}),l(e.board))}),()=>{t=!0}},[d,e.board,e.proxyPath]),c(`div`,{className:`board`,children:[c(`header`,{className:`board__header`,children:[s(`h1`,{className:`board__title`,children:e.board}),c(`dl`,{className:`board__meta`,children:[c(`div`,{children:[s(`dt`,{children:`client`}),s(`dd`,{children:e.clientVersion})]}),c(`div`,{children:[s(`dt`,{children:`assets`}),s(`dd`,{children:e.assetPath})]})]})]}),c(`main`,{className:`board__grid`,children:[!t&&s(z,{label:`board`,hint:`Loading configuration…`,wide:!0}),t?.panels.map(e=>e.type===`pipeline-status`?s(B,{panel:e,envelope:f[e.id]},e.id):e.type===`http-value`?s(R,{panel:e,envelope:f[e.id]},e.id):s(z,{label:e.type,hint:`Not wired yet`,wide:!0},e.id))]}),c(`footer`,{className:`board__footer`,children:[s(`span`,{children:`Signals are read live from their configured authorities.`}),s(T,{log:d})]})]})}function X(e){return e instanceof Error?e.message:String(e)}var Z=class extends Error{constructor(e){super(X(e))}};function ne({message:e}){return c(`div`,{className:`config-error`,role:`alert`,children:[s(`h1`,{children:`⚠️ Dashboard misconfigured`}),s(`p`,{children:`The page loaded, but the server did not hand it usable configuration.`}),s(`pre`,{children:e})]})}var Q=document.getElementById(`root`);if(!Q)throw Error(`No #root element in the document — the template is not the one we expect.`);var $=o(Q);try{$.render(s(t,{children:s(Y,{env:y()})}))}catch(e){$.render(s(ne,{message:e instanceof Error?e.message:String(e)}))}
|
|
3
|
-
//# sourceMappingURL=index-B0MmzIQ_.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-B0MmzIQ_.js","names":[],"sources":["../../../shared/dist/index.js","../../src/Diagnostics.tsx","../../src/diagnostics.ts","../../src/ObservedAt.tsx","../../src/panel-layout.ts","../../src/PanelFrame.tsx","../../src/HttpValuePanel.tsx","../../src/PanelPlaceholder.tsx","../../src/PipelinePanel.tsx","../../src/panel-diagnostics.ts","../../src/usePanelSignals.ts","../../src/App.tsx","../../src/ConfigError.tsx","../../src/main.tsx"],"sourcesContent":["// packages/shared/src/board-config.ts\nimport { z as z2 } from \"zod\";\n\n// packages/shared/src/duration.ts\nimport { z } from \"zod\";\nvar DURATION_PATTERN = /^(\\d+)(ms|s|m|h)$/;\nvar UNIT_MILLIS = {\n ms: 1,\n s: 1e3,\n m: 6e4,\n h: 36e5\n};\nfunction parseDuration(value) {\n const match = DURATION_PATTERN.exec(value);\n if (!match) return null;\n const [, rawAmount, rawUnit] = match;\n if (rawAmount === void 0 || rawUnit === void 0) return null;\n const amount = Number(rawAmount);\n if (amount <= 0) return null;\n return amount * UNIT_MILLIS[rawUnit];\n}\nvar durationSchema = z.string().refine((value) => parseDuration(value) !== null, {\n message: 'must be a positive duration like \"30s\", \"5m\", or \"1h\"'\n}).brand();\n\n// packages/shared/src/board-config.ts\nvar positionSchema = z2.object({\n x: z2.number().int().min(0),\n y: z2.number().int().min(0),\n w: z2.number().int().min(1).max(12),\n h: z2.number().int().min(1)\n});\nvar panelSchema = z2.looseObject({\n id: z2.string().min(1),\n type: z2.string().min(1),\n source: z2.string().min(1).optional(),\n /** Advisory in v1 — a panel without a position renders in config order rather than not at all. */\n position: positionSchema.optional(),\n refresh: durationSchema.optional(),\n /** A deliberate override only. Adapters derive links; hand-written ones drift. */\n link: z2.url().optional(),\n /** Source-agnostic endpoint used by the http-value signal. */\n url: z2.url().optional(),\n /** Small, deliberate JSON path subset: $.version, $.deployment.version, or $.response.docs[0].latestVersion. */\n json_path: z2.string().regex(/^\\$(?:\\.[A-Za-z_][A-Za-z0-9_]*|\\[\\d+\\])*$/, \"must be a simple JSON path\").optional()\n});\nvar sourceSchema = z2.looseObject({\n type: z2.string().min(1),\n /** Credentials are never in the config — only the name of the env var holding one. */\n token_env: z2.string().min(1).optional()\n});\nvar boardSchema = z2.object({\n refresh: durationSchema.optional(),\n panels: z2.array(panelSchema).min(1).superRefine((panels, ctx) => {\n const seen = /* @__PURE__ */ new Map();\n panels.forEach((panel, index) => {\n const firstIndex = seen.get(panel.id);\n if (firstIndex === void 0) {\n seen.set(panel.id, index);\n return;\n }\n ctx.addIssue({\n code: \"custom\",\n path: [index, \"id\"],\n message: `duplicate panel id \"${panel.id}\" (already used by panel at index ${firstIndex})`\n });\n });\n })\n});\nvar authSchema = z2.object({\n issuer: z2.url(),\n /** Absent in gateway mode — the gateway does the flow, the proxy still validates. */\n client_id: z2.string().min(1).optional(),\n allow: z2.object({\n groups: z2.array(z2.string().min(1)).optional(),\n subjects: z2.array(z2.string().min(1)).optional()\n }).optional()\n});\nvar boardConfigSchema = z2.object({\n sources: z2.record(z2.string().min(1), sourceSchema).default({}),\n boards: z2.record(z2.string().min(1), boardSchema),\n auth: authSchema.optional()\n});\nfunction resolveRefreshMillis(args) {\n const requested = args.panelOverrideMillis ?? args.boardDefaultMillis;\n return Math.max(requested, args.adapterFloorMillis);\n}\n\n// packages/shared/src/client-env.ts\nimport { z as z3 } from \"zod\";\nvar clientEnvSchema = z3.object({\n /** Where the immutable client assets live. The one variable that repoints the whole client. */\n assetPath: z3.string().min(1),\n /** Same-origin path the proxy is mounted at, so there is no CORS and no cookie problem. */\n proxyPath: z3.string().min(1),\n /** Which board this is, resolved server-side so the client parses no URLs. */\n board: z3.string().min(1),\n /** The client version being served, so two published versions are visibly distinguishable. */\n clientVersion: z3.string().min(1),\n /** Omitted entirely when unauthenticated — presence is what turns auth on. */\n auth: z3.object({\n issuer: z3.string().min(1),\n clientId: z3.string().min(1).optional()\n }).optional()\n});\nfunction readClientEnv(source = globalThis.window?.env) {\n const result = clientEnvSchema.safeParse(source);\n if (!result.success) {\n throw new Error(\n `window.env is missing or invalid \\u2014 the server did not inject usable configuration.\n${z3.prettifyError(result.error)}`\n );\n }\n return result.data;\n}\n\n// packages/shared/src/envelope.ts\nimport { z as z4 } from \"zod\";\nvar errorKindSchema = z4.enum([\n \"unreachable\",\n \"unauthorized\",\n \"not-found\",\n \"no-runs\",\n \"upstream-error\"\n]);\nvar envelopeErrorSchema = z4.object({\n kind: errorKindSchema,\n message: z4.string()\n});\nvar envelopeSchema = z4.discriminatedUnion(\"state\", [\n z4.object({\n panelId: z4.string().min(1),\n state: z4.literal(\"ok\"),\n /** From the upstream response `Date`. A cached value must never be shown without its age. */\n observedAt: z4.iso.datetime(),\n /** Adapter-derived. The authority for this panel; the dashboard itself asserts nothing. */\n link: z4.url().nullable(),\n signal: z4.unknown()\n }),\n z4.object({\n panelId: z4.string().min(1),\n state: z4.literal(\"error\"),\n /**\n * Populated even on error: knowing *when* a failure was observed is the difference between\n * \"broken just now\" and \"broken all morning\", and a panel that can't reach its source is\n * exactly when a viewer most wants to click through.\n */\n observedAt: z4.iso.datetime(),\n link: z4.url().nullable(),\n error: envelopeErrorSchema\n })\n]);\nvar pipelineStatusSchema = z4.object({\n type: z4.literal(\"pipeline-status\"),\n status: z4.enum([\"passed\", \"failed\", \"running\", \"cancelled\", \"unknown\"]),\n /** The source's unmodified status/result vocabulary, for an honest display. */\n rawStatus: z4.string(),\n name: z4.string().min(1),\n /** The branch represented by this status, when the upstream supplies or filters by one. */\n branch: z4.string().min(1).optional(),\n /** Elapsed execution time, supplied only after the upstream run has completed. */\n durationMs: z4.number().int().nonnegative().optional(),\n /** When this workflow run was last updated by its source, distinct from our observation time. */\n sourceUpdatedAt: z4.iso.datetime().optional()\n});\nvar httpValueSchema = z4.object({\n type: z4.literal(\"http-value\"),\n value: z4.union([z4.string(), z4.number(), z4.boolean()])\n});\nexport {\n authSchema,\n boardConfigSchema,\n boardSchema,\n clientEnvSchema,\n durationSchema,\n envelopeSchema,\n errorKindSchema,\n httpValueSchema,\n panelSchema,\n parseDuration,\n pipelineStatusSchema,\n positionSchema,\n readClientEnv,\n resolveRefreshMillis,\n sourceSchema\n};\n","import { useState, useSyncExternalStore } from 'react'\nimport type { BrowserDiagnosticStore } from './diagnostics.ts'\n\nexport function Diagnostics({ log }: { log: BrowserDiagnosticStore }) {\n const [open, setOpen] = useState(false)\n const count = useSyncExternalStore(log.subscribe, log.count, log.count)\n const download = () => {\n const blob = new Blob([JSON.stringify(log.export(), null, 2)], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `dashboard-diagnostics-${new Date().toISOString().replaceAll(':', '-')}.json`\n link.click()\n URL.revokeObjectURL(url)\n }\n const clear = () => {\n if (!window.confirm('Clear this browser’s retained dashboard diagnostics?')) return\n log.clear()\n }\n\n return (\n <section className=\"diagnostics\">\n <button\n className=\"diagnostics__toggle\"\n type=\"button\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n Diagnostics ({count})\n </button>\n {open && (\n <div className=\"diagnostics__area\">\n <p>{count} retained browser-local events. They are never uploaded.</p>\n <div className=\"diagnostics__actions\">\n <button type=\"button\" onClick={download}>\n Download\n </button>\n <button type=\"button\" onClick={clear}>\n Clear\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n","import type { ClientEnv, Envelope } from '@ze-great-dashboard/shared'\n\nconst storageKey = 'ze-great-dashboard.diagnostics.v1'\nexport const diagnosticsSchemaVersion = 1\nconst maximumEvents = 2_000\nconst maximumAgeMillis = 7 * 24 * 60 * 60 * 1_000\nconst diagnosticKinds = [\n 'session-start',\n 'board-fetch-start',\n 'board-fetch-response',\n 'board-fetch-parse-failure',\n 'board-fetch-failure',\n 'panel-fetch-start',\n 'panel-fetch-response',\n 'panel-fetch-parse-failure',\n 'panel-fetch-failure',\n 'panel-rendered',\n] as const\n\nexport type CacheMetadata = Partial<\n Record<'cacheControl' | 'etag' | 'lastModified' | 'date' | 'age', string>\n>\n\ntype EventMetadata = {\n schemaVersion: typeof diagnosticsSchemaVersion\n at: string\n sessionId: string\n board: string\n}\n\nexport type RenderedPanelDiagnostic = {\n state: Envelope['state']\n status?: string\n link: string | null\n}\n\nexport type DiagnosticEventInput =\n | { kind: 'session-start' }\n | { kind: 'board-fetch-start'; path: string }\n | {\n kind: 'board-fetch-response'\n path: string\n status?: number\n cache?: CacheMetadata\n boardSummary?: { panelCount: number; panelIds: string[] }\n }\n | { kind: 'board-fetch-parse-failure'; path: string; message: string }\n | { kind: 'board-fetch-failure'; path: string; message: string }\n | { kind: 'panel-fetch-start'; panelId: string; path: string }\n | {\n kind: 'panel-fetch-response'\n panelId: string\n path: string\n status?: number\n cache?: CacheMetadata\n envelope?: Envelope\n }\n | { kind: 'panel-fetch-parse-failure'; panelId: string; path: string; message: string }\n | { kind: 'panel-fetch-failure'; panelId: string; path: string; message: string }\n | {\n kind: 'panel-rendered'\n panelId: string\n path: string\n rendered: RenderedPanelDiagnostic\n }\n\nexport type DiagnosticEvent = DiagnosticEventInput extends infer Event\n ? Event extends DiagnosticEventInput\n ? Event & EventMetadata\n : never\n : never\n\n/** The only interface client features need in order to emit browser diagnostics. */\nexport interface DiagnosticSink {\n record(event: DiagnosticEventInput): void\n}\n\ntype PersistedDiagnosticEvent = Omit<DiagnosticEvent, 'schemaVersion'> & { schemaVersion?: number }\ntype StoredDiagnostics = { schemaVersion: number; events: PersistedDiagnosticEvent[] }\n\ntype StorageLike = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>\n\n/** Browser-local diagnostic evidence. It deliberately has no server or shared-contract counterpart. */\nexport class BrowserDiagnosticStore implements DiagnosticSink {\n private events: DiagnosticEvent[] = []\n private readonly listeners = new Set<() => void>()\n private readonly sessionId = crypto.randomUUID?.() ?? `${Date.now()}-${Math.random()}`\n\n constructor(\n private readonly env: ClientEnv,\n private readonly storage: StorageLike | undefined = storageOrUndefined(),\n private readonly now: () => Date = () => new Date(),\n ) {\n this.events = this.read()\n this.record({ kind: 'session-start' })\n }\n\n record(event: DiagnosticEventInput) {\n this.events = prune(\n [\n ...this.events,\n {\n ...event,\n schemaVersion: diagnosticsSchemaVersion,\n at: this.now().toISOString(),\n sessionId: this.sessionId,\n board: this.env.board,\n },\n ],\n this.now(),\n )\n this.persist()\n this.notify()\n }\n\n count = () => {\n return this.events.length\n }\n\n clear() {\n this.events = []\n try {\n this.storage?.removeItem(storageKey)\n } catch {\n // Diagnostics must not interrupt the radiator when browser storage is unavailable.\n }\n this.notify()\n }\n\n subscribe = (listener: () => void) => {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n export = () => {\n return {\n schemaVersion: diagnosticsSchemaVersion,\n exportedAt: this.now().toISOString(),\n client: {\n version: this.env.clientVersion,\n assetPath: this.env.assetPath,\n board: this.env.board,\n sessionId: this.sessionId,\n },\n events: this.events,\n }\n }\n\n private read(): DiagnosticEvent[] {\n try {\n const raw = this.storage?.getItem(storageKey)\n if (!raw) return []\n const parsed = JSON.parse(raw) as Partial<StoredDiagnostics>\n if (parsed.schemaVersion !== diagnosticsSchemaVersion || !Array.isArray(parsed.events)) {\n this.storage?.removeItem(storageKey)\n return []\n }\n if (!parsed.events.every(isDiagnosticEvent)) {\n this.storage?.removeItem(storageKey)\n return []\n }\n // v1 retained events predate per-event versions. Their containing store was already v1, so\n // retain them and normalize on the next write rather than discarding viewer evidence.\n return prune(\n parsed.events.map(\n (event) => ({ ...event, schemaVersion: diagnosticsSchemaVersion }) as DiagnosticEvent,\n ),\n this.now(),\n )\n } catch {\n try {\n this.storage?.removeItem(storageKey)\n } catch {\n // Storage can fail on both reads and writes (for example, private browsing policies).\n }\n return []\n }\n }\n\n private persist() {\n try {\n this.storage?.setItem(\n storageKey,\n JSON.stringify({ schemaVersion: diagnosticsSchemaVersion, events: this.events }),\n )\n } catch {\n // Keep the in-memory record for this page, but never make diagnostics a rendering failure.\n }\n }\n\n private notify() {\n for (const listener of this.listeners) listener()\n }\n}\n\nexport function cacheMetadata(headers: Headers): CacheMetadata | undefined {\n const entries = [\n ['cacheControl', 'cache-control'],\n ['etag', 'etag'],\n ['lastModified', 'last-modified'],\n ['date', 'date'],\n ['age', 'age'],\n ] as const\n const metadata = Object.fromEntries(\n entries.flatMap(([key, header]) => {\n const value = headers.get(header)\n return value ? [[key, value]] : []\n }),\n ) as CacheMetadata\n return Object.keys(metadata).length ? metadata : undefined\n}\n\nfunction prune(events: DiagnosticEvent[], now: Date) {\n const cutoff = now.valueOf() - maximumAgeMillis\n return events\n .filter(\n (event) =>\n Number.isFinite(new Date(event.at).valueOf()) && new Date(event.at).valueOf() >= cutoff,\n )\n .slice(-maximumEvents)\n}\n\nfunction isDiagnosticEvent(value: unknown): value is PersistedDiagnosticEvent {\n if (!value || typeof value !== 'object') return false\n const event = value as Partial<DiagnosticEvent>\n return (\n (event.schemaVersion === undefined || event.schemaVersion === diagnosticsSchemaVersion) &&\n typeof event.at === 'string' &&\n Number.isFinite(new Date(event.at).valueOf()) &&\n typeof event.sessionId === 'string' &&\n typeof event.board === 'string' &&\n diagnosticKinds.includes(event.kind as (typeof diagnosticKinds)[number])\n )\n}\n\nfunction storageOrUndefined(): StorageLike | undefined {\n try {\n return window.localStorage\n } catch {\n return undefined\n }\n}\n","export function ObservedAt({ value, label = 'As of' }: { value: string; label?: string }) {\n const observed = new Date(value)\n const formatted = observed.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n const age = Date.now() - observed.getTime()\n const stale = age > 5 * 60 * 1000\n const ageText = formatAge(age)\n return (\n <p className={`panel__hint panel__observed${stale ? ' panel__observed--stale' : ''}`}>\n <span aria-hidden=\"true\">◷</span> {label} {formatted} · {ageText}\n </p>\n )\n}\n\nexport function formatAge(milliseconds: number) {\n if (milliseconds < 60_000) return 'just now'\n const minutes = Math.floor(milliseconds / 60_000)\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n return `${hours}h ${minutes % 60}m ago`\n}\n","import type { Panel } from '@ze-great-dashboard/shared'\nimport type { CSSProperties } from 'react'\n\n/** Convert the advisory board position into CSS grid placement. */\nexport function panelLayout(panel: Panel): CSSProperties | undefined {\n const position = panel.position\n if (!position) return undefined\n\n return {\n '--panel-column': `${position.x + 1} / span ${position.w}`,\n '--panel-row': `${position.y + 1} / span ${position.h}`,\n } as CSSProperties\n}\n","import type { Envelope, Panel } from '@ze-great-dashboard/shared'\nimport type { ReactNode } from 'react'\nimport { panelLayout } from './panel-layout.ts'\n\nexport function PanelFrame({\n panel,\n envelope,\n error = false,\n children,\n}: {\n panel: Panel\n envelope?: Envelope\n error?: boolean\n children: ReactNode\n}) {\n return (\n <section\n className={error ? 'panel panel--error' : 'panel'}\n style={panelLayout(panel)}\n aria-busy={envelope ? undefined : true}\n >\n <h2 className=\"panel__label\">{panel.id}</h2>\n {children}\n <PanelSourceLink panelId={panel.id} link={envelope?.link} />\n </section>\n )\n}\n\nfunction PanelSourceLink({ panelId, link }: { panelId: string; link?: string | null }) {\n if (!link) return null\n\n return (\n <a\n className=\"panel__link\"\n href={link}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={`View source for ${panelId} (opens in a new tab)`}\n >\n View source <span aria-hidden=\"true\">↗</span>\n </a>\n )\n}\n","import type { Panel } from '@ze-great-dashboard/shared'\nimport { type Envelope, httpValueSchema } from '@ze-great-dashboard/shared'\nimport { ObservedAt } from './ObservedAt.tsx'\nimport { PanelFrame } from './PanelFrame.tsx'\n\nexport function HttpValuePanel({\n panel,\n envelope,\n}: {\n panel: Panel\n envelope: Envelope | undefined\n}) {\n if (!envelope)\n return (\n <PanelFrame panel={panel}>\n <p className=\"panel__hint\">Loading…</p>\n </PanelFrame>\n )\n if (envelope.state === 'error')\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Unable to read</p>\n <p className=\"panel__hint\">{envelope.error.message}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n const signal = httpValueSchema.safeParse(envelope.signal)\n if (!signal.success)\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Invalid value</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n return (\n <PanelFrame panel={panel} envelope={envelope}>\n <p className=\"panel__status\">{String(signal.data.value)}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n}\n","/**\n * A panel-shaped hole, so the grid can be looked at and argued with before any signal exists.\n * Replaced by real panels in Stage 2 — nothing here is meant to survive.\n */\nexport function PanelPlaceholder({\n label,\n hint,\n wide = false,\n}: {\n label: string\n hint: string\n wide?: boolean\n}) {\n return (\n <section className={wide ? 'panel panel--wide' : 'panel'}>\n <h2 className=\"panel__label\">{label}</h2>\n <p className=\"panel__hint\">{hint}</p>\n </section>\n )\n}\n","import { type Envelope, type Panel, pipelineStatusSchema } from '@ze-great-dashboard/shared'\nimport { ObservedAt } from './ObservedAt.tsx'\nimport { PanelFrame } from './PanelFrame.tsx'\n\nexport function PipelinePanel({\n panel,\n envelope,\n}: {\n panel: Panel\n envelope: Envelope | undefined\n}) {\n if (!envelope)\n return (\n <PanelFrame panel={panel}>\n <p className=\"panel__hint\">Loading…</p>\n </PanelFrame>\n )\n if (envelope.state === 'error') {\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">\n ⚠ {envelope.error.kind === 'no-runs' ? 'No workflow runs' : 'Unable to read'}\n </p>\n <p className=\"panel__hint\">{envelope.error.message}</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n }\n\n const signal = pipelineStatusSchema.safeParse(envelope.signal)\n if (!signal.success)\n return (\n <PanelFrame panel={panel} envelope={envelope} error>\n <p className=\"panel__status\">⚠ Invalid signal</p>\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n const presentation = statusPresentation(signal.data.status)\n return (\n <PanelFrame panel={panel} envelope={envelope}>\n <p className={`panel__status panel__status--${signal.data.status}`}>\n {presentation.glyph} {presentation.label}\n </p>\n <p className=\"panel__hint\">\n {signal.data.name} · {signal.data.rawStatus}\n {signal.data.branch && (\n <span className=\"panel__branch\" title={`Branch: ${signal.data.branch}`}>\n <span aria-hidden=\"true\"> · ⎇ </span>\n <span className=\"screen-reader-only\">Branch: </span>\n {signal.data.branch}\n </span>\n )}\n </p>\n {signal.data.status !== 'running' && signal.data.durationMs !== undefined && (\n <p className=\"panel__hint\">Took {formatDuration(signal.data.durationMs)}</p>\n )}\n {signal.data.sourceUpdatedAt && (\n <ObservedAt value={signal.data.sourceUpdatedAt} label=\"Run updated\" />\n )}\n <ObservedAt value={envelope.observedAt} />\n </PanelFrame>\n )\n}\n\nfunction formatDuration(durationMs: number) {\n const totalSeconds = Math.floor(durationMs / 1_000)\n const hours = Math.floor(totalSeconds / 3_600)\n const minutes = Math.floor((totalSeconds % 3_600) / 60)\n const seconds = totalSeconds % 60\n\n if (hours > 0) return `${hours}h ${minutes}m`\n if (minutes > 0) return `${minutes}m ${seconds}s`\n return `${seconds}s`\n}\n\nfunction statusPresentation(status: 'passed' | 'failed' | 'running' | 'cancelled' | 'unknown') {\n switch (status) {\n case 'passed':\n return { glyph: '✓', label: 'Passed' }\n case 'failed':\n return { glyph: '✕', label: 'Failed' }\n case 'running':\n return { glyph: '↻', label: 'Running' }\n case 'cancelled':\n return { glyph: '⊘', label: 'Cancelled' }\n case 'unknown':\n return { glyph: '?', label: 'Unknown' }\n }\n}\n","import { type Envelope, type Panel, pipelineStatusSchema } from '@ze-great-dashboard/shared'\nimport type { RenderedPanelDiagnostic } from './diagnostics.ts'\n\n/**\n * The compact public fact a viewer saw. Keep new panel projections here beside their\n * presentation boundary; this is intentionally not a source or server event contract.\n */\nexport function projectPanelDiagnostic(panel: Panel, envelope: Envelope): RenderedPanelDiagnostic {\n if (envelope.state === 'error') return { state: envelope.state, link: envelope.link }\n\n if (panel.type === 'pipeline-status') {\n const signal = pipelineStatusSchema.safeParse(envelope.signal)\n return {\n state: envelope.state,\n status: signal.success ? signal.data.status : undefined,\n link: envelope.link,\n }\n }\n\n return { state: envelope.state, link: envelope.link }\n}\n\nexport function panelDiagnosticChanged(\n previous: Envelope | undefined,\n panel: Panel,\n next: Envelope,\n) {\n if (!previous) return true\n const before = projectPanelDiagnostic(panel, previous)\n const after = projectPanelDiagnostic(panel, next)\n return (\n before.state !== after.state || before.status !== after.status || before.link !== after.link\n )\n}\n","import {\n type Board,\n type ClientEnv,\n type Envelope,\n envelopeSchema,\n parseDuration,\n resolveRefreshMillis,\n} from '@ze-great-dashboard/shared'\nimport { useEffect, useRef, useState } from 'react'\nimport { cacheMetadata, type DiagnosticSink } from './diagnostics.ts'\nimport { panelDiagnosticChanged, projectPanelDiagnostic } from './panel-diagnostics.ts'\n\nexport function usePanelSignals({\n board,\n env,\n diagnostics,\n}: {\n board: Board | undefined\n env: ClientEnv\n diagnostics: DiagnosticSink\n}) {\n const [signals, setSignals] = useState<Record<string, Envelope | undefined>>({})\n const signalsRef = useRef<Record<string, Envelope | undefined>>({})\n\n useEffect(() => {\n signalsRef.current = {}\n setSignals({})\n if (!board) return\n\n let cancelled = false\n const timers: number[] = []\n for (const panel of board.panels) {\n if (panel.type !== 'pipeline-status' && panel.type !== 'http-value') continue\n let inFlight = false\n const refreshMillis = resolveRefreshMillis({\n boardDefaultMillis: parseDuration(board.refresh ?? '60s') ?? 60_000,\n panelOverrideMillis: panel.refresh\n ? (parseDuration(panel.refresh) ?? undefined)\n : undefined,\n adapterFloorMillis: 0,\n })\n const path = `${env.proxyPath}/panel/${encodeURIComponent(env.board)}/${encodeURIComponent(panel.id)}`\n const refresh = () => {\n if (cancelled || inFlight) return\n inFlight = true\n diagnostics.record({ kind: 'panel-fetch-start', panelId: panel.id, path })\n fetch(path)\n .then(async (response) => {\n diagnostics.record({\n kind: 'panel-fetch-response',\n panelId: panel.id,\n path,\n status: response.status,\n cache: cacheMetadata(response.headers),\n })\n if (response.status === 304) return undefined\n try {\n return await response.json()\n } catch (error) {\n diagnostics.record({\n kind: 'panel-fetch-parse-failure',\n panelId: panel.id,\n path,\n message: errorMessage(error),\n })\n throw new DiagnosticParseFailure(error)\n }\n })\n .then((value: unknown) => {\n const envelope = parseEnvelope(value)\n if (value !== undefined && !envelope) {\n diagnostics.record({\n kind: 'panel-fetch-parse-failure',\n panelId: panel.id,\n path,\n message: 'Response was not a valid signal envelope.',\n })\n }\n if (!cancelled && envelope) {\n diagnostics.record({\n kind: 'panel-fetch-response',\n panelId: panel.id,\n path,\n envelope,\n })\n const previous = signalsRef.current[panel.id]\n if (panelDiagnosticChanged(previous, panel, envelope)) {\n diagnostics.record({\n kind: 'panel-rendered',\n panelId: panel.id,\n path,\n rendered: projectPanelDiagnostic(panel, envelope),\n })\n }\n signalsRef.current = { ...signalsRef.current, [panel.id]: envelope }\n setSignals(signalsRef.current)\n }\n })\n .catch((error) => {\n if (!(error instanceof DiagnosticParseFailure)) {\n diagnostics.record({\n kind: 'panel-fetch-failure',\n panelId: panel.id,\n path,\n message: errorMessage(error),\n })\n }\n })\n .finally(() => {\n inFlight = false\n })\n }\n refresh()\n timers.push(window.setInterval(refresh, refreshMillis))\n }\n return () => {\n cancelled = true\n for (const timer of timers) window.clearInterval(timer)\n }\n }, [board, diagnostics, env.board, env.proxyPath])\n\n return signals\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error)\n}\n\nclass DiagnosticParseFailure extends Error {\n constructor(error: unknown) {\n super(errorMessage(error))\n }\n}\n\nfunction parseEnvelope(value: unknown): Envelope | undefined {\n const result = envelopeSchema.safeParse(value)\n return result.success ? result.data : undefined\n}\n","import type { Board, ClientEnv } from '@ze-great-dashboard/shared'\nimport { useEffect, useRef, useState } from 'react'\nimport { Diagnostics } from './Diagnostics.tsx'\nimport { BrowserDiagnosticStore, cacheMetadata } from './diagnostics.ts'\nimport { HttpValuePanel } from './HttpValuePanel.tsx'\nimport { PanelPlaceholder } from './PanelPlaceholder.tsx'\nimport { PipelinePanel } from './PipelinePanel.tsx'\nimport { usePanelSignals } from './usePanelSignals.ts'\n\n/**\n * The board shell.\n *\n * Stage 1 has no data: there is no proxy endpoint to poll yet. What this proves is the plumbing —\n * that the client was handed its configuration by the server and knows which version of itself it\n * is. The version readout is what makes two published versions visibly different, which is the\n * whole point of the Stage 1 exit criterion.\n */\nexport function App({ env }: { env: ClientEnv }) {\n const [board, setBoard] = useState<Board>()\n const [loadedBoardName, setLoadedBoardName] = useState<string>()\n const diagnosticsRef = useRef<BrowserDiagnosticStore | null>(null)\n if (!diagnosticsRef.current) diagnosticsRef.current = new BrowserDiagnosticStore(env)\n const diagnostics = diagnosticsRef.current\n const signals = usePanelSignals({\n board: loadedBoardName === env.board ? board : undefined,\n env,\n diagnostics,\n })\n\n useEffect(() => {\n let cancelled = false\n setBoard(undefined)\n setLoadedBoardName(undefined)\n const path = `${env.proxyPath}/boards/${encodeURIComponent(env.board)}`\n diagnostics.record({ kind: 'board-fetch-start', path })\n fetch(path)\n .then(async (response) => {\n diagnostics.record({\n kind: 'board-fetch-response',\n path,\n status: response.status,\n cache: cacheMetadata(response.headers),\n })\n if (!response.ok) throw new Error(`Board configuration returned ${response.status}`)\n try {\n return await response.json()\n } catch (error) {\n diagnostics.record({\n kind: 'board-fetch-parse-failure',\n path,\n message: errorMessage(error),\n })\n throw new DiagnosticParseFailure(error)\n }\n })\n .then((value: unknown) => {\n if (!cancelled) {\n setBoard(value as Board)\n setLoadedBoardName(env.board)\n const panels = Array.isArray((value as { panels?: unknown }).panels)\n ? (value as { panels: Array<{ id?: unknown }> }).panels\n : []\n diagnostics.record({\n kind: 'board-fetch-response',\n path,\n boardSummary: {\n panelCount: panels.length,\n panelIds: panels.flatMap((panel) => (typeof panel.id === 'string' ? [panel.id] : [])),\n },\n })\n }\n })\n .catch((error) => {\n if (!(error instanceof DiagnosticParseFailure)) {\n diagnostics.record({ kind: 'board-fetch-failure', path, message: errorMessage(error) })\n }\n if (!cancelled) {\n setBoard({ panels: [] })\n setLoadedBoardName(env.board)\n }\n })\n return () => {\n cancelled = true\n }\n }, [diagnostics, env.board, env.proxyPath])\n\n return (\n <div className=\"board\">\n <header className=\"board__header\">\n <h1 className=\"board__title\">{env.board}</h1>\n <dl className=\"board__meta\">\n <div>\n <dt>client</dt>\n <dd>{env.clientVersion}</dd>\n </div>\n <div>\n <dt>assets</dt>\n <dd>{env.assetPath}</dd>\n </div>\n </dl>\n </header>\n\n <main className=\"board__grid\">\n {!board && <PanelPlaceholder label=\"board\" hint=\"Loading configuration…\" wide />}\n {board?.panels.map((panel) =>\n panel.type === 'pipeline-status' ? (\n <PipelinePanel key={panel.id} panel={panel} envelope={signals[panel.id]} />\n ) : panel.type === 'http-value' ? (\n <HttpValuePanel key={panel.id} panel={panel} envelope={signals[panel.id]} />\n ) : (\n <PanelPlaceholder key={panel.id} label={panel.type} hint=\"Not wired yet\" wide />\n ),\n )}\n </main>\n\n <footer className=\"board__footer\">\n <span>Signals are read live from their configured authorities.</span>\n <Diagnostics log={diagnostics} />\n </footer>\n </div>\n )\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error)\n}\n\nclass DiagnosticParseFailure extends Error {\n constructor(error: unknown) {\n super(errorMessage(error))\n }\n}\n","export function ConfigError({ message }: { message: string }) {\n return (\n <div className=\"config-error\" role=\"alert\">\n <h1>⚠️ Dashboard misconfigured</h1>\n <p>The page loaded, but the server did not hand it usable configuration.</p>\n <pre>{message}</pre>\n </div>\n )\n}\n","import { readClientEnv } from '@ze-great-dashboard/shared'\nimport { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { App } from './App.tsx'\nimport { ConfigError } from './ConfigError.tsx'\nimport './styles.css'\n\nconst container = document.getElementById('root')\nif (!container) {\n throw new Error('No #root element in the document — the template is not the one we expect.')\n}\n\nconst root = createRoot(container)\n\ntry {\n root.render(\n <StrictMode>\n <App env={readClientEnv()} />\n </StrictMode>,\n )\n} catch (error) {\n // A radiator that fails silently is worse than one that says what's wrong. If configuration\n // never arrived, say so on screen rather than rendering an empty board.\n root.render(<ConfigError message={error instanceof Error ? error.message : String(error)} />)\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--surface:#12140f;--surface-raised:#1a1a19;--ink-primary:#fff;--ink-secondary:#c3c2b7;--ink-muted:#898781;--edge:#2f312b;--status-good:#0ca30c;--status-warning:#fab219;--status-serious:#ec835a;--status-critical:#d03b3b;--gap:1rem;--radius:.5rem;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}.screen-reader-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}html,body{height:100%;margin:0}body{background:var(--surface);color:var(--ink-primary);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Helvetica,Arial,sans-serif;font-size:clamp(16px,1.1vw + .5rem,28px);line-height:1.4}.board{gap:var(--gap);min-height:100%;padding:var(--gap);flex-direction:column;display:flex}.board__header{justify-content:space-between;align-items:baseline;gap:var(--gap);border-bottom:1px solid var(--edge);padding-bottom:var(--gap);flex-wrap:wrap;display:flex}.board__title{letter-spacing:-.01em;margin:0;font-size:1.75em;font-weight:650}.board__meta{gap:calc(var(--gap) * 1.5);flex-wrap:wrap;margin:0;font-size:.7em;display:flex}.board__meta div{flex-direction:column;display:flex}.board__meta dt{color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;font-size:.8em}.board__meta dd{color:var(--ink-secondary);overflow-wrap:anywhere;margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.board__grid{gap:var(--gap);flex:1;grid-template-columns:repeat(12,1fr);align-content:start;display:grid}.panel{grid-column:var(--panel-column,span 6);grid-row:var(--panel-row,auto);min-height:6em;padding:var(--gap);border:1px solid var(--edge);border-radius:var(--radius);background:var(--surface-raised);flex-direction:column;justify-content:center;gap:.25em;display:flex;overflow:hidden}.panel--wide{grid-column:span 12}.panel__label{color:var(--ink-secondary);margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:1em;font-weight:600}.panel__hint{color:var(--ink-muted);margin:0;font-size:.75em}.panel__branch{white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.panel__observed{align-items:center;gap:.35em;display:flex}.panel__observed--stale{color:var(--status-warning)}.panel__status{margin:0;font-size:1.35em;font-weight:650}.panel__status--passed{color:var(--status-good)}.panel__status--failed{color:var(--status-critical)}.panel__status--running{color:var(--status-warning)}.panel__status--cancelled,.panel__status--unknown{color:var(--ink-secondary)}.panel--error{border-color:var(--status-serious)}.panel__link{color:var(--ink-secondary);text-underline-offset:.18em;align-self:flex-start;margin-top:.2em;font-size:.75em;font-weight:600}.panel__link:hover{color:var(--ink-primary)}.board__footer{color:var(--ink-muted);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.5rem;font-size:.75em;display:flex}.diagnostics{position:relative}.diagnostics button{border:1px solid var(--edge);background:var(--surface-raised);color:var(--ink-secondary);font:inherit;border-radius:.25rem;padding:.3em .55em}.diagnostics__area{z-index:1;border:1px solid var(--edge);border-radius:var(--radius);background:var(--surface-raised);width:min(24rem,90vw);padding:.75rem;position:absolute;bottom:calc(100% + .5rem);right:0;box-shadow:0 .5rem 1.5rem #0008}.diagnostics__area p{margin:0 0 .5rem}.diagnostics__actions{gap:.5rem;display:flex}.config-error{max-width:60ch;color:var(--ink-primary);margin:0 auto;padding:2rem}.config-error pre{border-radius:var(--radius);border:1px solid var(--status-critical);background:var(--surface-raised);color:var(--ink-secondary);white-space:pre-wrap;overflow-wrap:anywhere;padding:1rem;font-size:.75em}@media (width<=60rem){.board__grid{grid-template-columns:1fr}.panel{grid-area:auto/1/auto/-1}}
|