@continuous-excellence/ze-great-dashboard-aws 0.19.0 → 0.20.0

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/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-BbZhtr6c.js"></script>
15
- <link rel="stylesheet" crossorigin href="/__ASSET_PATH__/assets/index-BCZ7NCP8.css">
14
+ <script type="module" crossorigin src="/__ASSET_PATH__/assets/index-k3N7vI0h.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/__ASSET_PATH__/assets/index-Cuk6kga3.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
package/dist/cli.js CHANGED
@@ -353,7 +353,32 @@ var pullRequestHealthSchema = z4.object({
353
353
  status: pipelineStatusSchema.shape.status,
354
354
  summary: z4.string().min(1),
355
355
  workflows: z4.array(pullRequestHealthItemSchema),
356
- pullRequests: z4.array(pullRequestHealthItemSchema)
356
+ pullRequests: z4.array(pullRequestHealthItemSchema),
357
+ /** Missing component reads remain visible even when the evidence we did get is reassuring. */
358
+ incompleteObservations: z4.array(z4.object({ label: z4.string().min(1), message: z4.string().min(1) })).optional(),
359
+ /** The latest component reading, disclosed beside the oldest aggregate evidence. */
360
+ newestObservedAt: z4.iso.datetime().optional()
361
+ });
362
+ var pullRequestCandidateSchema = z4.object({
363
+ number: z4.number().int().positive(),
364
+ branch: z4.string().min(1),
365
+ link: z4.url()
366
+ });
367
+ var pullRequestCandidatesSchema = z4.object({
368
+ type: z4.literal("pull-request-candidates"),
369
+ pullRequests: z4.array(pullRequestCandidateSchema),
370
+ /** GitHub's first page is a deliberate bounded observation, not proof that no later PR exists. */
371
+ truncated: z4.boolean().optional()
372
+ });
373
+ var pullRequestWorkflowObservationSchema = z4.object({
374
+ type: z4.literal("pull-request-workflow"),
375
+ workflow: z4.string().min(1),
376
+ item: pullRequestHealthItemSchema
377
+ });
378
+ var pullRequestBuildObservationSchema = z4.object({
379
+ type: z4.literal("pull-request-build"),
380
+ branch: z4.string().min(1),
381
+ item: pullRequestHealthItemSchema
357
382
  });
358
383
  var httpValueSchema = z4.object({
359
384
  type: z4.literal("http-value"),
package/dist/index.js CHANGED
@@ -1035,7 +1035,32 @@ var pullRequestHealthSchema = z4.object({
1035
1035
  status: pipelineStatusSchema.shape.status,
1036
1036
  summary: z4.string().min(1),
1037
1037
  workflows: z4.array(pullRequestHealthItemSchema),
1038
- pullRequests: z4.array(pullRequestHealthItemSchema)
1038
+ pullRequests: z4.array(pullRequestHealthItemSchema),
1039
+ /** Missing component reads remain visible even when the evidence we did get is reassuring. */
1040
+ incompleteObservations: z4.array(z4.object({ label: z4.string().min(1), message: z4.string().min(1) })).optional(),
1041
+ /** The latest component reading, disclosed beside the oldest aggregate evidence. */
1042
+ newestObservedAt: z4.iso.datetime().optional()
1043
+ });
1044
+ var pullRequestCandidateSchema = z4.object({
1045
+ number: z4.number().int().positive(),
1046
+ branch: z4.string().min(1),
1047
+ link: z4.url()
1048
+ });
1049
+ var pullRequestCandidatesSchema = z4.object({
1050
+ type: z4.literal("pull-request-candidates"),
1051
+ pullRequests: z4.array(pullRequestCandidateSchema),
1052
+ /** GitHub's first page is a deliberate bounded observation, not proof that no later PR exists. */
1053
+ truncated: z4.boolean().optional()
1054
+ });
1055
+ var pullRequestWorkflowObservationSchema = z4.object({
1056
+ type: z4.literal("pull-request-workflow"),
1057
+ workflow: z4.string().min(1),
1058
+ item: pullRequestHealthItemSchema
1059
+ });
1060
+ var pullRequestBuildObservationSchema = z4.object({
1061
+ type: z4.literal("pull-request-build"),
1062
+ branch: z4.string().min(1),
1063
+ item: pullRequestHealthItemSchema
1039
1064
  });
1040
1065
  var httpValueSchema = z4.object({
1041
1066
  type: z4.literal("http-value"),
package/dist/lambda.mjs CHANGED
@@ -64247,7 +64247,32 @@ var pullRequestHealthSchema = external_exports.object({
64247
64247
  status: pipelineStatusSchema.shape.status,
64248
64248
  summary: external_exports.string().min(1),
64249
64249
  workflows: external_exports.array(pullRequestHealthItemSchema),
64250
- pullRequests: external_exports.array(pullRequestHealthItemSchema)
64250
+ pullRequests: external_exports.array(pullRequestHealthItemSchema),
64251
+ /** Missing component reads remain visible even when the evidence we did get is reassuring. */
64252
+ incompleteObservations: external_exports.array(external_exports.object({ label: external_exports.string().min(1), message: external_exports.string().min(1) })).optional(),
64253
+ /** The latest component reading, disclosed beside the oldest aggregate evidence. */
64254
+ newestObservedAt: external_exports.iso.datetime().optional()
64255
+ });
64256
+ var pullRequestCandidateSchema = external_exports.object({
64257
+ number: external_exports.number().int().positive(),
64258
+ branch: external_exports.string().min(1),
64259
+ link: external_exports.url()
64260
+ });
64261
+ var pullRequestCandidatesSchema = external_exports.object({
64262
+ type: external_exports.literal("pull-request-candidates"),
64263
+ pullRequests: external_exports.array(pullRequestCandidateSchema),
64264
+ /** GitHub's first page is a deliberate bounded observation, not proof that no later PR exists. */
64265
+ truncated: external_exports.boolean().optional()
64266
+ });
64267
+ var pullRequestWorkflowObservationSchema = external_exports.object({
64268
+ type: external_exports.literal("pull-request-workflow"),
64269
+ workflow: external_exports.string().min(1),
64270
+ item: pullRequestHealthItemSchema
64271
+ });
64272
+ var pullRequestBuildObservationSchema = external_exports.object({
64273
+ type: external_exports.literal("pull-request-build"),
64274
+ branch: external_exports.string().min(1),
64275
+ item: pullRequestHealthItemSchema
64251
64276
  });
64252
64277
  var httpValueSchema = external_exports.object({
64253
64278
  type: external_exports.literal("http-value"),
@@ -66927,90 +66952,117 @@ function permittedGithubActionsCalls(panel, source, _credentials) {
66927
66952
  url2.searchParams.set("per_page", "1");
66928
66953
  return [{ url: url2.toString(), headers: new Headers() }];
66929
66954
  }
66930
- function permittedGithubActionsPullRequestHealthCalls(panel, source) {
66931
- const parsedPanel = pullRequestHealthPanelSchema.parse(panel);
66932
- const parsedSource = githubActionsSourceSchema.parse(source);
66933
- const calls = parsedPanel.update_workflows.map(
66934
- ({ workflow }) => githubRunsCall(parsedSource, workflow)
66955
+ function pullRequestHealthCapabilities(panel) {
66956
+ const parsed = pullRequestHealthPanelSchema.parse(panel);
66957
+ return {
66958
+ panel: parsed,
66959
+ configuredUpdateWorkflow(workflow) {
66960
+ return parsed.update_workflows.find((candidate) => candidate.workflow === workflow);
66961
+ },
66962
+ permitsBuildBranch(branch) {
66963
+ return parsed.update_workflows.some(
66964
+ ({ branch_prefixes }) => branch_prefixes.some((prefix) => branch.startsWith(prefix))
66965
+ );
66966
+ }
66967
+ };
66968
+ }
66969
+ async function fetchGithubActionsPullRequestCandidates(args) {
66970
+ const capabilities = pullRequestHealthCapabilities(args.panel);
66971
+ const panel = capabilities.panel;
66972
+ const source = githubActionsSourceSchema.parse(args.source);
66973
+ return githubObservation(
66974
+ args,
66975
+ source,
66976
+ pullRequestsCall(source, panel.base_branch, 1),
66977
+ (value, observedAt4) => {
66978
+ const page = pullRequestsSchema.parse(value);
66979
+ const pullRequests = page.filter(
66980
+ (pullRequest) => capabilities.permitsBuildBranch(pullRequest.head.ref)
66981
+ );
66982
+ return {
66983
+ panelId: panel.id,
66984
+ state: "ok",
66985
+ observedAt: observedAt4,
66986
+ link: sourceLinkForRepository(source),
66987
+ signal: {
66988
+ type: "pull-request-candidates",
66989
+ pullRequests: pullRequests.map((pullRequest) => ({
66990
+ number: pullRequest.number,
66991
+ branch: pullRequest.head.ref,
66992
+ link: pullRequest.html_url
66993
+ })),
66994
+ ...page.length === 100 ? { truncated: true } : {}
66995
+ }
66996
+ };
66997
+ }
66935
66998
  );
66936
- calls.push({
66937
- ...pullRequestsCall(parsedSource, parsedPanel.base_branch, 1)
66938
- });
66939
- return calls;
66940
66999
  }
66941
- async function fetchGithubActionsPullRequestHealth(args) {
66942
- const panel = pullRequestHealthPanelSchema.parse(args.panel);
67000
+ async function fetchGithubActionsUpdateWorkflow(args) {
67001
+ const capabilities = pullRequestHealthCapabilities(args.panel);
67002
+ const panel = capabilities.panel;
66943
67003
  const source = githubActionsSourceSchema.parse(args.source);
67004
+ const configured = capabilities.configuredUpdateWorkflow(args.workflow);
67005
+ if (!configured) throw new Error("Unconfigured update workflow");
67006
+ return githubObservation(
67007
+ args,
67008
+ source,
67009
+ githubRunsCall(source, configured.workflow, void 0, void 0, 100),
67010
+ (value, observedAt4) => {
67011
+ const run = runsSchema.parse(value).workflow_runs.find((candidate) => {
67012
+ const branch = candidate.head_branch;
67013
+ return typeof branch === "string" && configured.branch_prefixes.some((prefix) => branch.startsWith(prefix));
67014
+ });
67015
+ return {
67016
+ panelId: panel.id,
67017
+ state: "ok",
67018
+ observedAt: observedAt4,
67019
+ link: sourceLinkForRepository(source),
67020
+ signal: {
67021
+ type: "pull-request-workflow",
67022
+ workflow: configured.workflow,
67023
+ item: healthItem(configured.workflow, run, `Update workflow ${configured.workflow}`)
67024
+ }
67025
+ };
67026
+ }
67027
+ );
67028
+ }
67029
+ async function fetchGithubActionsPullRequestBuild(args) {
67030
+ const capabilities = pullRequestHealthCapabilities(args.panel);
67031
+ const panel = capabilities.panel;
67032
+ const source = githubActionsSourceSchema.parse(args.source);
67033
+ if (!capabilities.permitsBuildBranch(args.branch))
67034
+ throw new Error("Unpermitted pull request branch");
67035
+ return githubObservation(
67036
+ args,
67037
+ source,
67038
+ githubRunsCall(source, panel.build_workflow, args.branch, "pull_request"),
67039
+ (value, observedAt4) => {
67040
+ const run = runsSchema.parse(value).workflow_runs[0];
67041
+ return {
67042
+ panelId: panel.id,
67043
+ state: "ok",
67044
+ observedAt: observedAt4,
67045
+ link: sourceLinkForRepository(source),
67046
+ signal: {
67047
+ type: "pull-request-build",
67048
+ branch: args.branch,
67049
+ item: healthItem(args.branch, run, args.branch)
67050
+ }
67051
+ };
67052
+ }
67053
+ );
67054
+ }
67055
+ async function githubObservation(args, source, call, normalize) {
67056
+ let upstream;
66944
67057
  try {
66945
- const workflowResults = await Promise.all(
66946
- panel.update_workflows.map(async ({ workflow, branch_prefixes }) => ({
66947
- workflow,
66948
- run: await latestRun({
66949
- source,
66950
- workflow,
66951
- branchPrefixes: branch_prefixes,
66952
- requestHeaders: args.requestHeaders,
66953
- fetcher: args.fetcher,
66954
- githubClient: githubClientFor(args)
66955
- })
66956
- }))
66957
- );
66958
- const pullRequests = await openPullRequests({
66959
- source,
66960
- baseBranch: panel.base_branch,
66961
- requestHeaders: args.requestHeaders,
66962
- fetcher: args.fetcher,
66963
- githubClient: githubClientFor(args)
66964
- });
66965
- const matching = pullRequests.filter(
66966
- (pullRequest) => panel.update_workflows.some(
66967
- ({ branch_prefixes }) => branch_prefixes.some((prefix) => pullRequest.head.ref.startsWith(prefix))
66968
- )
66969
- );
66970
- const pullRequestResults = await Promise.all(
66971
- matching.map(async (pullRequest) => ({
66972
- pullRequest,
66973
- run: await latestRun({
66974
- source,
66975
- workflow: panel.build_workflow,
66976
- branch: pullRequest.head.ref,
66977
- event: "pull_request",
66978
- requestHeaders: args.requestHeaders,
66979
- fetcher: args.fetcher,
66980
- githubClient: githubClientFor(args)
66981
- })
66982
- }))
66983
- );
66984
- const workflows = workflowResults.map(
66985
- ({ workflow, run }) => healthItem(workflow, run, `Update workflow ${workflow}`)
66986
- );
66987
- const pullRequestItems = pullRequestResults.map(
66988
- ({ pullRequest, run }) => healthItem(`PR #${pullRequest.number}`, run, pullRequest.head.ref, pullRequest.html_url)
66989
- );
66990
- const allItems = [...workflows, ...pullRequestItems];
66991
- const status = aggregateStatus(allItems.map((item) => item.status));
66992
- const summary = summarize(status, workflows, pullRequestItems);
66993
- const envelope = {
66994
- panelId: panel.id,
66995
- state: "ok",
66996
- observedAt: (/* @__PURE__ */ new Date()).toISOString(),
66997
- link: sourceLinkForRepository(source),
66998
- signal: {
66999
- type: "pull-request-health",
67000
- status,
67001
- summary,
67002
- workflows,
67003
- pullRequests: pullRequestItems
67004
- }
67005
- };
67006
- return { envelope, response: new Response(null, { status: 200 }) };
67058
+ upstream = await githubFetch(call, { ...args, source, githubClient: githubClientFor(args) });
67007
67059
  } catch (error52) {
67008
67060
  return {
67009
67061
  response: new Response(
67010
67062
  JSON.stringify(
67011
67063
  errorEnvelope2(
67012
- panel.id,
67013
- error52 instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
67064
+ args.panel.id,
67065
+ error52 instanceof GithubAuthenticationError ? "unauthorized" : "unreachable",
67014
67066
  error52,
67015
67067
  sourceLinkForRepository(source)
67016
67068
  )
@@ -67019,34 +67071,42 @@ async function fetchGithubActionsPullRequestHealth(args) {
67019
67071
  )
67020
67072
  };
67021
67073
  }
67022
- }
67023
- async function latestRun(args) {
67024
- const call = githubRunsCall(
67025
- args.source,
67026
- args.workflow,
67027
- args.branch,
67028
- args.event,
67029
- args.branchPrefixes ? 100 : 1
67030
- );
67031
- const response = await githubFetch(call, args);
67032
- if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
67033
- const runs = runsSchema.parse(await response.json()).workflow_runs;
67034
- const branchPrefixes = args.branchPrefixes;
67035
- if (!branchPrefixes) return runs[0];
67036
- return runs.find((run) => {
67037
- const headBranch = run.head_branch;
67038
- return headBranch !== void 0 && headBranch !== null && branchPrefixes.some((prefix) => headBranch.startsWith(prefix));
67039
- });
67040
- }
67041
- async function openPullRequests(args) {
67042
- const result = [];
67043
- for (let page = 1; ; page += 1) {
67044
- const call = pullRequestsCall(args.source, args.baseBranch, page);
67045
- const response = await githubFetch(call, args);
67046
- if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
67047
- const pageResults = pullRequestsSchema.parse(await response.json());
67048
- result.push(...pageResults);
67049
- if (pageResults.length < 100) return result;
67074
+ if (upstream.status === 304) return { response: upstream };
67075
+ if (!upstream.ok)
67076
+ return {
67077
+ response: new Response(
67078
+ JSON.stringify(
67079
+ errorEnvelope2(
67080
+ args.panel.id,
67081
+ errorKind2(upstream.status),
67082
+ `${upstream.status} ${upstream.statusText}`,
67083
+ sourceLinkForRepository(source),
67084
+ observedAt3(upstream.headers.get("date"))
67085
+ )
67086
+ ),
67087
+ { status: 200 }
67088
+ )
67089
+ };
67090
+ try {
67091
+ return {
67092
+ envelope: normalize(await upstream.json(), observedAt3(upstream.headers.get("date"))),
67093
+ response: upstream
67094
+ };
67095
+ } catch (error52) {
67096
+ return {
67097
+ response: new Response(
67098
+ JSON.stringify(
67099
+ errorEnvelope2(
67100
+ args.panel.id,
67101
+ "upstream-error",
67102
+ error52,
67103
+ sourceLinkForRepository(source),
67104
+ observedAt3(upstream.headers.get("date"))
67105
+ )
67106
+ ),
67107
+ { status: 200 }
67108
+ )
67109
+ };
67050
67110
  }
67051
67111
  }
67052
67112
  function pullRequestsCall(source, baseBranch, page) {
@@ -67074,18 +67134,6 @@ function healthItem(label, run, detail, link) {
67074
67134
  link: link ?? run?.html_url ?? null
67075
67135
  };
67076
67136
  }
67077
- function aggregateStatus(statuses) {
67078
- if (statuses.some((status) => status === "failed")) return "failed";
67079
- if (statuses.some((status) => status === "running")) return "running";
67080
- if (statuses.some((status) => status === "unknown")) return "unknown";
67081
- if (statuses.some((status) => status === "cancelled")) return "cancelled";
67082
- return "passed";
67083
- }
67084
- function summarize(status, workflows, pullRequests) {
67085
- const failed = [...workflows, ...pullRequests].find((item) => item.status === status);
67086
- if (status !== "passed" && failed) return `${failed.label}: ${failed.detail}`;
67087
- return pullRequests.length === 0 ? `${workflows.length} update workflow${workflows.length === 1 ? "" : "s"} \xB7 No open update PRs` : `${workflows.length} update workflow${workflows.length === 1 ? "" : "s"} \xB7 ${pullRequests.length} open update PR${pullRequests.length === 1 ? "" : "s"}`;
67088
- }
67089
67137
  function sourceLinkForRepository(source) {
67090
67138
  return `https://github.com/${source.repo}`;
67091
67139
  }
@@ -67361,11 +67409,20 @@ function deriveAllowlist(config2) {
67361
67409
  for (const panel of board.panels) {
67362
67410
  const source = panel.source ? config2.sources[panel.source] : void 0;
67363
67411
  const calls = callsFor(panel, source);
67364
- if (calls) allowed.set(`${boardName}/${panel.id}`, calls);
67412
+ if (calls) allowed.set(`${boardName}/${panel.id}`, /* @__PURE__ */ new Set(["read"]));
67413
+ if (panel.type === "pull-request-health" && source?.type === "github-actions") {
67414
+ allowed.set(
67415
+ `${boardName}/${panel.id}`,
67416
+ /* @__PURE__ */ new Set(["pull-requests", "update-workflow", "pull-request-build"])
67417
+ );
67418
+ }
67365
67419
  }
67366
67420
  }
67367
67421
  return allowed;
67368
67422
  }
67423
+ function permitsPanelOperation(allowlist, board, panel, operation2) {
67424
+ return allowlist.get(`${board}/${panel}`)?.has(operation2) ?? false;
67425
+ }
67369
67426
  function callsFor(panel, source) {
67370
67427
  if (panel.type === "pipeline-status" && source?.type === "github-actions") {
67371
67428
  return permittedGithubActionsCalls(panel, source);
@@ -67373,9 +67430,6 @@ function callsFor(panel, source) {
67373
67430
  if (panel.type === "pipeline-status" && source?.type === "azure-devops") {
67374
67431
  return permittedAzureDevOpsCalls(panel, source);
67375
67432
  }
67376
- if (panel.type === "pull-request-health" && source?.type === "github-actions") {
67377
- return permittedGithubActionsPullRequestHealthCalls(panel, source);
67378
- }
67379
67433
  if (panel.type === "http-value") return permittedHttpValueCalls(panel);
67380
67434
  return void 0;
67381
67435
  }
@@ -67525,7 +67579,7 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
67525
67579
  const board = deps.boardConfig?.boards[boardName];
67526
67580
  const panel = board?.panels.find((candidate) => candidate.id === panelId);
67527
67581
  const source = panel?.source ? deps.boardConfig?.sources[panel.source] : void 0;
67528
- if (!panel || panel.type !== "http-value" && !source || !allowlist.has(`${boardName}/${panelId}`))
67582
+ if (!panel || panel.type !== "http-value" && !source || !permitsPanelOperation(allowlist, boardName, panelId, "read"))
67529
67583
  return c5.notFound();
67530
67584
  if (panel.type === "pipeline-status" && source?.type === "github-actions" && source) {
67531
67585
  const result = await fetchGithubActionsPipeline({
@@ -67547,16 +67601,7 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
67547
67601
  });
67548
67602
  return adapterRouteResponse(result);
67549
67603
  }
67550
- if (panel.type === "pull-request-health" && source?.type === "github-actions" && source) {
67551
- const result = await fetchGithubActionsPullRequestHealth({
67552
- panel,
67553
- source,
67554
- requestHeaders: c5.req.raw.headers,
67555
- fetcher: deps.fetcher ?? globalThis.fetch,
67556
- githubClient
67557
- });
67558
- return adapterRouteResponse(result);
67559
- }
67604
+ if (panel.type === "pull-request-health") return c5.notFound();
67560
67605
  if (panel.type === "http-value") {
67561
67606
  const result = await fetchHttpValue({
67562
67607
  panel,
@@ -67567,6 +67612,64 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
67567
67612
  }
67568
67613
  return c5.notFound();
67569
67614
  });
67615
+ app.get("/api/panel/:board/:panelId/pull-requests", async (c5) => {
67616
+ const resolved = pullRequestPanel(c5.req.param("board"), c5.req.param("panelId"), "pull-requests");
67617
+ if (!resolved) return c5.notFound();
67618
+ return adapterRouteResponse(
67619
+ await fetchGithubActionsPullRequestCandidates({
67620
+ ...resolved,
67621
+ requestHeaders: c5.req.raw.headers,
67622
+ fetcher: deps.fetcher ?? globalThis.fetch,
67623
+ githubClient
67624
+ })
67625
+ );
67626
+ });
67627
+ app.get("/api/panel/:board/:panelId/update-workflow/:workflow", async (c5) => {
67628
+ const resolved = pullRequestPanel(
67629
+ c5.req.param("board"),
67630
+ c5.req.param("panelId"),
67631
+ "update-workflow"
67632
+ );
67633
+ const workflow = c5.req.param("workflow");
67634
+ if (!resolved?.capabilities.configuredUpdateWorkflow(workflow)) return c5.notFound();
67635
+ return adapterRouteResponse(
67636
+ await fetchGithubActionsUpdateWorkflow({
67637
+ ...resolved,
67638
+ workflow,
67639
+ requestHeaders: c5.req.raw.headers,
67640
+ fetcher: deps.fetcher ?? globalThis.fetch,
67641
+ githubClient
67642
+ })
67643
+ );
67644
+ });
67645
+ app.get("/api/panel/:board/:panelId/pull-request-build", async (c5) => {
67646
+ const resolved = pullRequestPanel(
67647
+ c5.req.param("board"),
67648
+ c5.req.param("panelId"),
67649
+ "pull-request-build"
67650
+ );
67651
+ const branch = c5.req.query("branch");
67652
+ if (!resolved || !branch || !resolved.capabilities.permitsBuildBranch(branch))
67653
+ return c5.notFound();
67654
+ return adapterRouteResponse(
67655
+ await fetchGithubActionsPullRequestBuild({
67656
+ ...resolved,
67657
+ branch,
67658
+ requestHeaders: c5.req.raw.headers,
67659
+ fetcher: deps.fetcher ?? globalThis.fetch,
67660
+ githubClient
67661
+ })
67662
+ );
67663
+ });
67664
+ function pullRequestPanel(boardName, panelId, operation2) {
67665
+ const panel = deps.boardConfig?.boards[boardName]?.panels.find(
67666
+ (candidate) => candidate.id === panelId
67667
+ );
67668
+ const source = panel?.source ? deps.boardConfig?.sources[panel.source] : void 0;
67669
+ const capabilities = panel?.type === "pull-request-health" ? pullRequestHealthCapabilities(panel) : void 0;
67670
+ if (!capabilities) return void 0;
67671
+ return panel?.type === "pull-request-health" && source?.type === "github-actions" && permitsPanelOperation(allowlist, boardName, panelId, operation2) ? { panel, source, capabilities } : void 0;
67672
+ }
67570
67673
  async function renderEntrypoint(_request, board) {
67571
67674
  const template = await templates.get(config2.assetPath);
67572
67675
  const env2 = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuous-excellence/ze-great-dashboard-aws",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "AWS Lambda and CloudFormation adapter for Ze Great Dashboard.",
6
6
  "keywords": [