@continuous-excellence/ze-great-dashboard-aws 0.31.53 → 0.31.55

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/dist/cli.js CHANGED
@@ -205,11 +205,11 @@ var panelSchema = z2.looseObject({
205
205
  path: ["test_results"],
206
206
  message: "test-count requires test_results artifact configuration"
207
207
  });
208
- } else if (panel.test_results) {
208
+ } else if (panel.test_results && panel.type !== "pipeline-status") {
209
209
  ctx.addIssue({
210
210
  code: "custom",
211
211
  path: ["test_results"],
212
- message: "test_results is only supported by test-count panels"
212
+ message: "test_results is only supported by test-count and pipeline-status panels"
213
213
  });
214
214
  }
215
215
  if (panel.type !== "http-value") return;
@@ -454,6 +454,12 @@ var boardConfigSchema = z2.object({
454
454
  }
455
455
  });
456
456
  }
457
+ if (panel.type === "pipeline-status" && panel.test_results && source?.type !== "github-actions")
458
+ ctx.addIssue({
459
+ code: "custom",
460
+ path: ["boards", boardName, "panels", panelIndex, "test_results"],
461
+ message: "test_results requires a github-actions source"
462
+ });
457
463
  if (panel.type !== "pipeline-status" || source?.type !== "azure-devops") return;
458
464
  if (typeof panel.pipeline !== "number" || !Number.isInteger(panel.pipeline) || panel.pipeline <= 0) {
459
465
  ctx.addIssue({
@@ -566,11 +572,29 @@ var pipelineActivitySchema = z4.object({
566
572
  name: z4.string().min(1),
567
573
  parent: z4.string().min(1).optional()
568
574
  });
575
+ var testCountSchema = z4.discriminatedUnion("status", [
576
+ z4.object({
577
+ type: z4.literal("test-count"),
578
+ status: z4.literal("reported"),
579
+ total: z4.number().int().nonnegative(),
580
+ passed: z4.number().int().nonnegative().optional(),
581
+ failed: z4.number().int().nonnegative().optional(),
582
+ skipped: z4.number().int().nonnegative().optional()
583
+ }),
584
+ z4.object({ type: z4.literal("test-count"), status: z4.literal("pending") }),
585
+ z4.object({ type: z4.literal("test-count"), status: z4.literal("not-reported") })
586
+ ]);
587
+ var testEvidenceSchema = z4.discriminatedUnion("state", [
588
+ z4.object({ state: z4.literal("ok"), observedAt: z4.iso.datetime(), signal: testCountSchema }),
589
+ z4.object({ state: z4.literal("error"), observedAt: z4.iso.datetime(), error: envelopeErrorSchema })
590
+ ]);
569
591
  var pipelineStatusSchema = z4.object({
570
592
  type: z4.literal("pipeline-status"),
571
593
  status: z4.enum(["passed", "failed", "warning", "running", "cancelled", "unknown"]),
572
594
  /** The source's unmodified status/result vocabulary, for an honest display. */
573
595
  rawStatus: z4.string(),
596
+ /** Test evidence bound to this exact run; failures do not erase build status. */
597
+ testResults: testEvidenceSchema.optional(),
574
598
  name: z4.string().min(1),
575
599
  /** The branch represented by this status, when the upstream supplies or filters by one. */
576
600
  branch: z4.string().min(1).optional(),
@@ -587,18 +611,6 @@ var pipelineStatusSchema = z4.object({
587
611
  /** When this workflow run was last updated by its source, distinct from our observation time. */
588
612
  sourceUpdatedAt: z4.iso.datetime().optional()
589
613
  });
590
- var testCountSchema = z4.discriminatedUnion("status", [
591
- z4.object({
592
- type: z4.literal("test-count"),
593
- status: z4.literal("reported"),
594
- total: z4.number().int().nonnegative(),
595
- passed: z4.number().int().nonnegative().optional(),
596
- failed: z4.number().int().nonnegative().optional(),
597
- skipped: z4.number().int().nonnegative().optional()
598
- }),
599
- z4.object({ type: z4.literal("test-count"), status: z4.literal("pending") }),
600
- z4.object({ type: z4.literal("test-count"), status: z4.literal("not-reported") })
601
- ]);
602
614
  var pullRequestHealthItemSchema = z4.object({
603
615
  label: z4.string().min(1),
604
616
  status: pipelineStatusSchema.shape.status,
@@ -1545,6 +1557,7 @@ function concat(chunks, length) {
1545
1557
 
1546
1558
  // packages/server/src/adapters/github-actions.ts
1547
1559
  var pipelinePanelSchema = z7.object({
1560
+ test_results: testResultsArtifactSchema.optional(),
1548
1561
  id: z7.string().min(1),
1549
1562
  type: z7.literal("pipeline-status"),
1550
1563
  source: z7.string().min(1),
@@ -1620,37 +1633,60 @@ function testCountCapabilities(panel, source) {
1620
1633
  async function fetchGithubActionsTestCount(args2) {
1621
1634
  const { panel, source } = testCountCapabilities(args2.panel, args2.source);
1622
1635
  const githubClient = githubClientFor(args2);
1623
- const noValidators = new Headers();
1624
- let failureLink = sourceLinkForWorkflow(source, panel.pipeline);
1625
- let failureObservedAt;
1626
1636
  try {
1627
1637
  const selected = await selectGithubWorkflowRun({
1628
1638
  ...args2,
1629
1639
  source,
1630
1640
  workflow: panel.pipeline,
1631
- requestHeaders: noValidators,
1641
+ requestHeaders: new Headers(),
1632
1642
  githubClient
1633
1643
  });
1634
1644
  if (!selected.response.ok) return testCountFailure(panel.id, source, selected.response);
1635
- const { run: run2 } = selected;
1636
- if (!run2?.id)
1645
+ if (!selected.run?.id)
1637
1646
  return testCountEnvelope(
1638
1647
  noRunEnvelope(panel.id, source, panel.pipeline, selected.response.headers.get("date"))
1639
1648
  );
1640
- const link = run2.html_url;
1641
- const runObservedAt = selected.observedAt;
1642
- failureLink = link;
1643
- failureObservedAt = runObservedAt;
1649
+ const evidence = await readGithubRunTestEvidence(
1650
+ { ...args2, source, githubClient },
1651
+ {
1652
+ run: selected.run,
1653
+ observedAt: selected.observedAt
1654
+ },
1655
+ panel.test_results
1656
+ );
1657
+ return testCountEnvelope({ ...evidence, panelId: panel.id, link: selected.run.html_url });
1658
+ } catch (error) {
1659
+ return testCountEnvelope(
1660
+ errorEnvelope(
1661
+ panel.id,
1662
+ error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
1663
+ error,
1664
+ sourceLinkForWorkflow(source, panel.pipeline)
1665
+ )
1666
+ );
1667
+ }
1668
+ }
1669
+ async function readGithubRunTestEvidence(args2, selected, artifactConfig) {
1670
+ const { run: run2, observedAt: runObservedAt } = selected;
1671
+ const { source, githubClient } = args2;
1672
+ const noValidators = new Headers();
1673
+ let failureObservedAt = runObservedAt;
1674
+ const evidenceError = (kind, at = failureObservedAt) => ({
1675
+ state: "error",
1676
+ observedAt: at,
1677
+ error: { kind, message: publicErrorMessage(kind) }
1678
+ });
1679
+ const failedResponse = (response) => evidenceError(errorKind(response.status), observedAt2(response.headers.get("date")));
1680
+ try {
1681
+ if (!run2.id) return evidenceError("upstream-error");
1644
1682
  if (run2.status !== "completed")
1645
- return testCountEnvelope({
1646
- panelId: panel.id,
1683
+ return {
1647
1684
  state: "ok",
1648
1685
  observedAt: runObservedAt,
1649
- link,
1650
1686
  signal: { type: "test-count", status: "pending" }
1651
- });
1687
+ };
1652
1688
  const artifactsResponse = await githubFetch(
1653
- githubArtifactsCall(source, run2.id, panel.test_results),
1689
+ githubArtifactsCall(source, run2.id, artifactConfig),
1654
1690
  {
1655
1691
  ...args2,
1656
1692
  source,
@@ -1658,57 +1694,37 @@ async function fetchGithubActionsTestCount(args2) {
1658
1694
  githubClient
1659
1695
  }
1660
1696
  );
1661
- if (!artifactsResponse.ok) return testCountFailure(panel.id, source, artifactsResponse, link);
1697
+ if (!artifactsResponse.ok) return failedResponse(artifactsResponse);
1662
1698
  const artifactObservedAt = observedAt2(artifactsResponse.headers.get("date"));
1663
1699
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt);
1664
1700
  const artifacts = artifactsSchema.parse(await artifactsResponse.json()).artifacts;
1665
- const matches = artifacts.filter((artifact2) => artifact2.name === panel.test_results.artifact);
1701
+ const matches = artifacts.filter((artifact2) => artifact2.name === artifactConfig.artifact);
1666
1702
  if (matches.length === 0)
1667
- return testCountEnvelope({
1668
- panelId: panel.id,
1703
+ return {
1669
1704
  state: "ok",
1670
1705
  observedAt: oldestObservation(runObservedAt, artifactObservedAt),
1671
- link,
1672
1706
  signal: { type: "test-count", status: "not-reported" }
1673
- });
1674
- if (matches.length !== 1 || matches[0]?.expired)
1675
- return testCountEnvelope(
1676
- errorEnvelope(
1677
- panel.id,
1678
- "upstream-error",
1679
- "The test-results artifact is unavailable.",
1680
- link
1681
- )
1682
- );
1707
+ };
1683
1708
  const [artifact] = matches;
1684
- if (!artifact)
1685
- return testCountEnvelope(
1686
- errorEnvelope(
1687
- panel.id,
1688
- "upstream-error",
1689
- "The test-results artifact is unavailable.",
1690
- link
1691
- )
1692
- );
1709
+ if (matches.length !== 1 || !artifact || artifact.expired)
1710
+ return evidenceError("upstream-error");
1693
1711
  const archiveResponse = await githubFetch(githubArtifactDownloadCall(source, artifact.id), {
1694
1712
  ...args2,
1695
1713
  source,
1696
1714
  requestHeaders: noValidators,
1697
1715
  githubClient
1698
1716
  });
1699
- if (!archiveResponse.ok) return testCountFailure(panel.id, source, archiveResponse, link);
1717
+ if (!archiveResponse.ok) return failedResponse(archiveResponse);
1700
1718
  const archiveObservedAt = observedAt2(archiveResponse.headers.get("date"));
1701
1719
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt);
1702
1720
  const summary = testResultsSummarySchema.parse(
1703
- JSON.parse(await readTestResultsArtifact(archiveResponse, panel.test_results.entry))
1721
+ JSON.parse(await readTestResultsArtifact(archiveResponse, artifactConfig.entry))
1704
1722
  );
1705
1723
  if (summary.runId !== run2.id || summary.runAttempt !== run2.run_attempt || summary.headSha !== run2.head_sha)
1706
1724
  throw new Error("Test-results manifest does not belong to the selected workflow run.");
1707
- return testCountEnvelope({
1708
- panelId: panel.id,
1725
+ return {
1709
1726
  state: "ok",
1710
1727
  observedAt: oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt),
1711
- link,
1712
1728
  signal: {
1713
1729
  type: "test-count",
1714
1730
  status: "reported",
@@ -1717,16 +1733,10 @@ async function fetchGithubActionsTestCount(args2) {
1717
1733
  ...summary.failed === void 0 ? {} : { failed: summary.failed },
1718
1734
  ...summary.skipped === void 0 ? {} : { skipped: summary.skipped }
1719
1735
  }
1720
- });
1736
+ };
1721
1737
  } catch (error) {
1722
- return testCountEnvelope(
1723
- errorEnvelope(
1724
- panel.id,
1725
- error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
1726
- error,
1727
- failureLink,
1728
- failureObservedAt
1729
- )
1738
+ return evidenceError(
1739
+ error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error"
1730
1740
  );
1731
1741
  }
1732
1742
  }
@@ -1767,6 +1777,7 @@ async function fetchGithubActionsPipeline(args2) {
1767
1777
  ...args2,
1768
1778
  source: parsedSource,
1769
1779
  workflow: parsedPanel.pipeline,
1780
+ requestHeaders: parsedPanel.test_results ? new Headers() : args2.requestHeaders,
1770
1781
  githubClient
1771
1782
  });
1772
1783
  } catch (error) {
@@ -1831,14 +1842,21 @@ async function fetchGithubActionsPipeline(args2) {
1831
1842
  });
1832
1843
  if (activity) signal.activity = activity;
1833
1844
  }
1845
+ if (parsedPanel.test_results) {
1846
+ signal.testResults = await readGithubRunTestEvidence(
1847
+ { ...args2, source: parsedSource, githubClient },
1848
+ { run: run2, observedAt: selected.observedAt },
1849
+ parsedPanel.test_results
1850
+ );
1851
+ }
1834
1852
  const envelope = {
1835
1853
  panelId: args2.panel.id,
1836
1854
  state: "ok",
1837
- observedAt: observedAt2(upstream.headers.get("date")),
1855
+ observedAt: signal.testResults ? oldestObservation(selected.observedAt, signal.testResults.observedAt) : selected.observedAt,
1838
1856
  link: run2.html_url,
1839
1857
  signal
1840
1858
  };
1841
- return { envelope, response: upstream };
1859
+ return { envelope, response: parsedPanel.test_results ? new Response(null) : upstream };
1842
1860
  } catch (error) {
1843
1861
  return {
1844
1862
  response: new Response(
package/dist/index.js CHANGED
@@ -887,11 +887,11 @@ var panelSchema = z2.looseObject({
887
887
  path: ["test_results"],
888
888
  message: "test-count requires test_results artifact configuration"
889
889
  });
890
- } else if (panel.test_results) {
890
+ } else if (panel.test_results && panel.type !== "pipeline-status") {
891
891
  ctx.addIssue({
892
892
  code: "custom",
893
893
  path: ["test_results"],
894
- message: "test_results is only supported by test-count panels"
894
+ message: "test_results is only supported by test-count and pipeline-status panels"
895
895
  });
896
896
  }
897
897
  if (panel.type !== "http-value") return;
@@ -1136,6 +1136,12 @@ var boardConfigSchema = z2.object({
1136
1136
  }
1137
1137
  });
1138
1138
  }
1139
+ if (panel.type === "pipeline-status" && panel.test_results && source?.type !== "github-actions")
1140
+ ctx.addIssue({
1141
+ code: "custom",
1142
+ path: ["boards", boardName, "panels", panelIndex, "test_results"],
1143
+ message: "test_results requires a github-actions source"
1144
+ });
1139
1145
  if (panel.type !== "pipeline-status" || source?.type !== "azure-devops") return;
1140
1146
  if (typeof panel.pipeline !== "number" || !Number.isInteger(panel.pipeline) || panel.pipeline <= 0) {
1141
1147
  ctx.addIssue({
@@ -1248,11 +1254,29 @@ var pipelineActivitySchema = z4.object({
1248
1254
  name: z4.string().min(1),
1249
1255
  parent: z4.string().min(1).optional()
1250
1256
  });
1257
+ var testCountSchema = z4.discriminatedUnion("status", [
1258
+ z4.object({
1259
+ type: z4.literal("test-count"),
1260
+ status: z4.literal("reported"),
1261
+ total: z4.number().int().nonnegative(),
1262
+ passed: z4.number().int().nonnegative().optional(),
1263
+ failed: z4.number().int().nonnegative().optional(),
1264
+ skipped: z4.number().int().nonnegative().optional()
1265
+ }),
1266
+ z4.object({ type: z4.literal("test-count"), status: z4.literal("pending") }),
1267
+ z4.object({ type: z4.literal("test-count"), status: z4.literal("not-reported") })
1268
+ ]);
1269
+ var testEvidenceSchema = z4.discriminatedUnion("state", [
1270
+ z4.object({ state: z4.literal("ok"), observedAt: z4.iso.datetime(), signal: testCountSchema }),
1271
+ z4.object({ state: z4.literal("error"), observedAt: z4.iso.datetime(), error: envelopeErrorSchema })
1272
+ ]);
1251
1273
  var pipelineStatusSchema = z4.object({
1252
1274
  type: z4.literal("pipeline-status"),
1253
1275
  status: z4.enum(["passed", "failed", "warning", "running", "cancelled", "unknown"]),
1254
1276
  /** The source's unmodified status/result vocabulary, for an honest display. */
1255
1277
  rawStatus: z4.string(),
1278
+ /** Test evidence bound to this exact run; failures do not erase build status. */
1279
+ testResults: testEvidenceSchema.optional(),
1256
1280
  name: z4.string().min(1),
1257
1281
  /** The branch represented by this status, when the upstream supplies or filters by one. */
1258
1282
  branch: z4.string().min(1).optional(),
@@ -1269,18 +1293,6 @@ var pipelineStatusSchema = z4.object({
1269
1293
  /** When this workflow run was last updated by its source, distinct from our observation time. */
1270
1294
  sourceUpdatedAt: z4.iso.datetime().optional()
1271
1295
  });
1272
- var testCountSchema = z4.discriminatedUnion("status", [
1273
- z4.object({
1274
- type: z4.literal("test-count"),
1275
- status: z4.literal("reported"),
1276
- total: z4.number().int().nonnegative(),
1277
- passed: z4.number().int().nonnegative().optional(),
1278
- failed: z4.number().int().nonnegative().optional(),
1279
- skipped: z4.number().int().nonnegative().optional()
1280
- }),
1281
- z4.object({ type: z4.literal("test-count"), status: z4.literal("pending") }),
1282
- z4.object({ type: z4.literal("test-count"), status: z4.literal("not-reported") })
1283
- ]);
1284
1296
  var pullRequestHealthItemSchema = z4.object({
1285
1297
  label: z4.string().min(1),
1286
1298
  status: pipelineStatusSchema.shape.status,
@@ -2227,6 +2239,7 @@ function concat(chunks, length) {
2227
2239
 
2228
2240
  // packages/server/src/adapters/github-actions.ts
2229
2241
  var pipelinePanelSchema = z7.object({
2242
+ test_results: testResultsArtifactSchema.optional(),
2230
2243
  id: z7.string().min(1),
2231
2244
  type: z7.literal("pipeline-status"),
2232
2245
  source: z7.string().min(1),
@@ -2302,37 +2315,60 @@ function testCountCapabilities(panel, source) {
2302
2315
  async function fetchGithubActionsTestCount(args) {
2303
2316
  const { panel, source } = testCountCapabilities(args.panel, args.source);
2304
2317
  const githubClient = githubClientFor(args);
2305
- const noValidators = new Headers();
2306
- let failureLink = sourceLinkForWorkflow(source, panel.pipeline);
2307
- let failureObservedAt;
2308
2318
  try {
2309
2319
  const selected = await selectGithubWorkflowRun({
2310
2320
  ...args,
2311
2321
  source,
2312
2322
  workflow: panel.pipeline,
2313
- requestHeaders: noValidators,
2323
+ requestHeaders: new Headers(),
2314
2324
  githubClient
2315
2325
  });
2316
2326
  if (!selected.response.ok) return testCountFailure(panel.id, source, selected.response);
2317
- const { run } = selected;
2318
- if (!run?.id)
2327
+ if (!selected.run?.id)
2319
2328
  return testCountEnvelope(
2320
2329
  noRunEnvelope(panel.id, source, panel.pipeline, selected.response.headers.get("date"))
2321
2330
  );
2322
- const link = run.html_url;
2323
- const runObservedAt = selected.observedAt;
2324
- failureLink = link;
2325
- failureObservedAt = runObservedAt;
2331
+ const evidence = await readGithubRunTestEvidence(
2332
+ { ...args, source, githubClient },
2333
+ {
2334
+ run: selected.run,
2335
+ observedAt: selected.observedAt
2336
+ },
2337
+ panel.test_results
2338
+ );
2339
+ return testCountEnvelope({ ...evidence, panelId: panel.id, link: selected.run.html_url });
2340
+ } catch (error) {
2341
+ return testCountEnvelope(
2342
+ errorEnvelope(
2343
+ panel.id,
2344
+ error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
2345
+ error,
2346
+ sourceLinkForWorkflow(source, panel.pipeline)
2347
+ )
2348
+ );
2349
+ }
2350
+ }
2351
+ async function readGithubRunTestEvidence(args, selected, artifactConfig) {
2352
+ const { run, observedAt: runObservedAt } = selected;
2353
+ const { source, githubClient } = args;
2354
+ const noValidators = new Headers();
2355
+ let failureObservedAt = runObservedAt;
2356
+ const evidenceError = (kind, at = failureObservedAt) => ({
2357
+ state: "error",
2358
+ observedAt: at,
2359
+ error: { kind, message: publicErrorMessage(kind) }
2360
+ });
2361
+ const failedResponse = (response) => evidenceError(errorKind(response.status), observedAt2(response.headers.get("date")));
2362
+ try {
2363
+ if (!run.id) return evidenceError("upstream-error");
2326
2364
  if (run.status !== "completed")
2327
- return testCountEnvelope({
2328
- panelId: panel.id,
2365
+ return {
2329
2366
  state: "ok",
2330
2367
  observedAt: runObservedAt,
2331
- link,
2332
2368
  signal: { type: "test-count", status: "pending" }
2333
- });
2369
+ };
2334
2370
  const artifactsResponse = await githubFetch(
2335
- githubArtifactsCall(source, run.id, panel.test_results),
2371
+ githubArtifactsCall(source, run.id, artifactConfig),
2336
2372
  {
2337
2373
  ...args,
2338
2374
  source,
@@ -2340,57 +2376,37 @@ async function fetchGithubActionsTestCount(args) {
2340
2376
  githubClient
2341
2377
  }
2342
2378
  );
2343
- if (!artifactsResponse.ok) return testCountFailure(panel.id, source, artifactsResponse, link);
2379
+ if (!artifactsResponse.ok) return failedResponse(artifactsResponse);
2344
2380
  const artifactObservedAt = observedAt2(artifactsResponse.headers.get("date"));
2345
2381
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt);
2346
2382
  const artifacts = artifactsSchema.parse(await artifactsResponse.json()).artifacts;
2347
- const matches = artifacts.filter((artifact2) => artifact2.name === panel.test_results.artifact);
2383
+ const matches = artifacts.filter((artifact2) => artifact2.name === artifactConfig.artifact);
2348
2384
  if (matches.length === 0)
2349
- return testCountEnvelope({
2350
- panelId: panel.id,
2385
+ return {
2351
2386
  state: "ok",
2352
2387
  observedAt: oldestObservation(runObservedAt, artifactObservedAt),
2353
- link,
2354
2388
  signal: { type: "test-count", status: "not-reported" }
2355
- });
2356
- if (matches.length !== 1 || matches[0]?.expired)
2357
- return testCountEnvelope(
2358
- errorEnvelope(
2359
- panel.id,
2360
- "upstream-error",
2361
- "The test-results artifact is unavailable.",
2362
- link
2363
- )
2364
- );
2389
+ };
2365
2390
  const [artifact] = matches;
2366
- if (!artifact)
2367
- return testCountEnvelope(
2368
- errorEnvelope(
2369
- panel.id,
2370
- "upstream-error",
2371
- "The test-results artifact is unavailable.",
2372
- link
2373
- )
2374
- );
2391
+ if (matches.length !== 1 || !artifact || artifact.expired)
2392
+ return evidenceError("upstream-error");
2375
2393
  const archiveResponse = await githubFetch(githubArtifactDownloadCall(source, artifact.id), {
2376
2394
  ...args,
2377
2395
  source,
2378
2396
  requestHeaders: noValidators,
2379
2397
  githubClient
2380
2398
  });
2381
- if (!archiveResponse.ok) return testCountFailure(panel.id, source, archiveResponse, link);
2399
+ if (!archiveResponse.ok) return failedResponse(archiveResponse);
2382
2400
  const archiveObservedAt = observedAt2(archiveResponse.headers.get("date"));
2383
2401
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt);
2384
2402
  const summary = testResultsSummarySchema.parse(
2385
- JSON.parse(await readTestResultsArtifact(archiveResponse, panel.test_results.entry))
2403
+ JSON.parse(await readTestResultsArtifact(archiveResponse, artifactConfig.entry))
2386
2404
  );
2387
2405
  if (summary.runId !== run.id || summary.runAttempt !== run.run_attempt || summary.headSha !== run.head_sha)
2388
2406
  throw new Error("Test-results manifest does not belong to the selected workflow run.");
2389
- return testCountEnvelope({
2390
- panelId: panel.id,
2407
+ return {
2391
2408
  state: "ok",
2392
2409
  observedAt: oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt),
2393
- link,
2394
2410
  signal: {
2395
2411
  type: "test-count",
2396
2412
  status: "reported",
@@ -2399,16 +2415,10 @@ async function fetchGithubActionsTestCount(args) {
2399
2415
  ...summary.failed === void 0 ? {} : { failed: summary.failed },
2400
2416
  ...summary.skipped === void 0 ? {} : { skipped: summary.skipped }
2401
2417
  }
2402
- });
2418
+ };
2403
2419
  } catch (error) {
2404
- return testCountEnvelope(
2405
- errorEnvelope(
2406
- panel.id,
2407
- error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
2408
- error,
2409
- failureLink,
2410
- failureObservedAt
2411
- )
2420
+ return evidenceError(
2421
+ error instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error"
2412
2422
  );
2413
2423
  }
2414
2424
  }
@@ -2449,6 +2459,7 @@ async function fetchGithubActionsPipeline(args) {
2449
2459
  ...args,
2450
2460
  source: parsedSource,
2451
2461
  workflow: parsedPanel.pipeline,
2462
+ requestHeaders: parsedPanel.test_results ? new Headers() : args.requestHeaders,
2452
2463
  githubClient
2453
2464
  });
2454
2465
  } catch (error) {
@@ -2513,14 +2524,21 @@ async function fetchGithubActionsPipeline(args) {
2513
2524
  });
2514
2525
  if (activity) signal.activity = activity;
2515
2526
  }
2527
+ if (parsedPanel.test_results) {
2528
+ signal.testResults = await readGithubRunTestEvidence(
2529
+ { ...args, source: parsedSource, githubClient },
2530
+ { run, observedAt: selected.observedAt },
2531
+ parsedPanel.test_results
2532
+ );
2533
+ }
2516
2534
  const envelope = {
2517
2535
  panelId: args.panel.id,
2518
2536
  state: "ok",
2519
- observedAt: observedAt2(upstream.headers.get("date")),
2537
+ observedAt: signal.testResults ? oldestObservation(selected.observedAt, signal.testResults.observedAt) : selected.observedAt,
2520
2538
  link: run.html_url,
2521
2539
  signal
2522
2540
  };
2523
- return { envelope, response: upstream };
2541
+ return { envelope, response: parsedPanel.test_results ? new Response(null) : upstream };
2524
2542
  } catch (error) {
2525
2543
  return {
2526
2544
  response: new Response(
package/dist/lambda.mjs CHANGED
@@ -71277,11 +71277,11 @@ var panelSchema = external_exports.looseObject({
71277
71277
  path: ["test_results"],
71278
71278
  message: "test-count requires test_results artifact configuration"
71279
71279
  });
71280
- } else if (panel.test_results) {
71280
+ } else if (panel.test_results && panel.type !== "pipeline-status") {
71281
71281
  ctx.addIssue({
71282
71282
  code: "custom",
71283
71283
  path: ["test_results"],
71284
- message: "test_results is only supported by test-count panels"
71284
+ message: "test_results is only supported by test-count and pipeline-status panels"
71285
71285
  });
71286
71286
  }
71287
71287
  if (panel.type !== "http-value") return;
@@ -71531,6 +71531,12 @@ var boardConfigSchema = external_exports.object({
71531
71531
  }
71532
71532
  });
71533
71533
  }
71534
+ if (panel.type === "pipeline-status" && panel.test_results && source?.type !== "github-actions")
71535
+ ctx.addIssue({
71536
+ code: "custom",
71537
+ path: ["boards", boardName, "panels", panelIndex, "test_results"],
71538
+ message: "test_results requires a github-actions source"
71539
+ });
71534
71540
  if (panel.type !== "pipeline-status" || source?.type !== "azure-devops") return;
71535
71541
  if (typeof panel.pipeline !== "number" || !Number.isInteger(panel.pipeline) || panel.pipeline <= 0) {
71536
71542
  ctx.addIssue({
@@ -71643,11 +71649,29 @@ var pipelineActivitySchema = external_exports.object({
71643
71649
  name: external_exports.string().min(1),
71644
71650
  parent: external_exports.string().min(1).optional()
71645
71651
  });
71652
+ var testCountSchema = external_exports.discriminatedUnion("status", [
71653
+ external_exports.object({
71654
+ type: external_exports.literal("test-count"),
71655
+ status: external_exports.literal("reported"),
71656
+ total: external_exports.number().int().nonnegative(),
71657
+ passed: external_exports.number().int().nonnegative().optional(),
71658
+ failed: external_exports.number().int().nonnegative().optional(),
71659
+ skipped: external_exports.number().int().nonnegative().optional()
71660
+ }),
71661
+ external_exports.object({ type: external_exports.literal("test-count"), status: external_exports.literal("pending") }),
71662
+ external_exports.object({ type: external_exports.literal("test-count"), status: external_exports.literal("not-reported") })
71663
+ ]);
71664
+ var testEvidenceSchema = external_exports.discriminatedUnion("state", [
71665
+ external_exports.object({ state: external_exports.literal("ok"), observedAt: external_exports.iso.datetime(), signal: testCountSchema }),
71666
+ external_exports.object({ state: external_exports.literal("error"), observedAt: external_exports.iso.datetime(), error: envelopeErrorSchema })
71667
+ ]);
71646
71668
  var pipelineStatusSchema = external_exports.object({
71647
71669
  type: external_exports.literal("pipeline-status"),
71648
71670
  status: external_exports.enum(["passed", "failed", "warning", "running", "cancelled", "unknown"]),
71649
71671
  /** The source's unmodified status/result vocabulary, for an honest display. */
71650
71672
  rawStatus: external_exports.string(),
71673
+ /** Test evidence bound to this exact run; failures do not erase build status. */
71674
+ testResults: testEvidenceSchema.optional(),
71651
71675
  name: external_exports.string().min(1),
71652
71676
  /** The branch represented by this status, when the upstream supplies or filters by one. */
71653
71677
  branch: external_exports.string().min(1).optional(),
@@ -71664,18 +71688,6 @@ var pipelineStatusSchema = external_exports.object({
71664
71688
  /** When this workflow run was last updated by its source, distinct from our observation time. */
71665
71689
  sourceUpdatedAt: external_exports.iso.datetime().optional()
71666
71690
  });
71667
- var testCountSchema = external_exports.discriminatedUnion("status", [
71668
- external_exports.object({
71669
- type: external_exports.literal("test-count"),
71670
- status: external_exports.literal("reported"),
71671
- total: external_exports.number().int().nonnegative(),
71672
- passed: external_exports.number().int().nonnegative().optional(),
71673
- failed: external_exports.number().int().nonnegative().optional(),
71674
- skipped: external_exports.number().int().nonnegative().optional()
71675
- }),
71676
- external_exports.object({ type: external_exports.literal("test-count"), status: external_exports.literal("pending") }),
71677
- external_exports.object({ type: external_exports.literal("test-count"), status: external_exports.literal("not-reported") })
71678
- ]);
71679
71691
  var pullRequestHealthItemSchema = external_exports.object({
71680
71692
  label: external_exports.string().min(1),
71681
71693
  status: pipelineStatusSchema.shape.status,
@@ -72754,6 +72766,7 @@ function concat(chunks, length) {
72754
72766
 
72755
72767
  // packages/server/src/adapters/github-actions.ts
72756
72768
  var pipelinePanelSchema = external_exports.object({
72769
+ test_results: testResultsArtifactSchema.optional(),
72757
72770
  id: external_exports.string().min(1),
72758
72771
  type: external_exports.literal("pipeline-status"),
72759
72772
  source: external_exports.string().min(1),
@@ -72829,37 +72842,60 @@ function testCountCapabilities(panel, source) {
72829
72842
  async function fetchGithubActionsTestCount(args) {
72830
72843
  const { panel, source } = testCountCapabilities(args.panel, args.source);
72831
72844
  const githubClient = githubClientFor(args);
72832
- const noValidators = new Headers();
72833
- let failureLink = sourceLinkForWorkflow(source, panel.pipeline);
72834
- let failureObservedAt;
72835
72845
  try {
72836
72846
  const selected = await selectGithubWorkflowRun({
72837
72847
  ...args,
72838
72848
  source,
72839
72849
  workflow: panel.pipeline,
72840
- requestHeaders: noValidators,
72850
+ requestHeaders: new Headers(),
72841
72851
  githubClient
72842
72852
  });
72843
72853
  if (!selected.response.ok) return testCountFailure(panel.id, source, selected.response);
72844
- const { run } = selected;
72845
- if (!run?.id)
72854
+ if (!selected.run?.id)
72846
72855
  return testCountEnvelope(
72847
72856
  noRunEnvelope(panel.id, source, panel.pipeline, selected.response.headers.get("date"))
72848
72857
  );
72849
- const link = run.html_url;
72850
- const runObservedAt = selected.observedAt;
72851
- failureLink = link;
72852
- failureObservedAt = runObservedAt;
72858
+ const evidence = await readGithubRunTestEvidence(
72859
+ { ...args, source, githubClient },
72860
+ {
72861
+ run: selected.run,
72862
+ observedAt: selected.observedAt
72863
+ },
72864
+ panel.test_results
72865
+ );
72866
+ return testCountEnvelope({ ...evidence, panelId: panel.id, link: selected.run.html_url });
72867
+ } catch (error63) {
72868
+ return testCountEnvelope(
72869
+ errorEnvelope(
72870
+ panel.id,
72871
+ error63 instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
72872
+ error63,
72873
+ sourceLinkForWorkflow(source, panel.pipeline)
72874
+ )
72875
+ );
72876
+ }
72877
+ }
72878
+ async function readGithubRunTestEvidence(args, selected, artifactConfig) {
72879
+ const { run, observedAt: runObservedAt } = selected;
72880
+ const { source, githubClient } = args;
72881
+ const noValidators = new Headers();
72882
+ let failureObservedAt = runObservedAt;
72883
+ const evidenceError = (kind, at = failureObservedAt) => ({
72884
+ state: "error",
72885
+ observedAt: at,
72886
+ error: { kind, message: publicErrorMessage(kind) }
72887
+ });
72888
+ const failedResponse = (response) => evidenceError(errorKind(response.status), observedAt2(response.headers.get("date")));
72889
+ try {
72890
+ if (!run.id) return evidenceError("upstream-error");
72853
72891
  if (run.status !== "completed")
72854
- return testCountEnvelope({
72855
- panelId: panel.id,
72892
+ return {
72856
72893
  state: "ok",
72857
72894
  observedAt: runObservedAt,
72858
- link,
72859
72895
  signal: { type: "test-count", status: "pending" }
72860
- });
72896
+ };
72861
72897
  const artifactsResponse = await githubFetch(
72862
- githubArtifactsCall(source, run.id, panel.test_results),
72898
+ githubArtifactsCall(source, run.id, artifactConfig),
72863
72899
  {
72864
72900
  ...args,
72865
72901
  source,
@@ -72867,57 +72903,37 @@ async function fetchGithubActionsTestCount(args) {
72867
72903
  githubClient
72868
72904
  }
72869
72905
  );
72870
- if (!artifactsResponse.ok) return testCountFailure(panel.id, source, artifactsResponse, link);
72906
+ if (!artifactsResponse.ok) return failedResponse(artifactsResponse);
72871
72907
  const artifactObservedAt = observedAt2(artifactsResponse.headers.get("date"));
72872
72908
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt);
72873
72909
  const artifacts = artifactsSchema.parse(await artifactsResponse.json()).artifacts;
72874
- const matches = artifacts.filter((artifact2) => artifact2.name === panel.test_results.artifact);
72910
+ const matches = artifacts.filter((artifact2) => artifact2.name === artifactConfig.artifact);
72875
72911
  if (matches.length === 0)
72876
- return testCountEnvelope({
72877
- panelId: panel.id,
72912
+ return {
72878
72913
  state: "ok",
72879
72914
  observedAt: oldestObservation(runObservedAt, artifactObservedAt),
72880
- link,
72881
72915
  signal: { type: "test-count", status: "not-reported" }
72882
- });
72883
- if (matches.length !== 1 || matches[0]?.expired)
72884
- return testCountEnvelope(
72885
- errorEnvelope(
72886
- panel.id,
72887
- "upstream-error",
72888
- "The test-results artifact is unavailable.",
72889
- link
72890
- )
72891
- );
72916
+ };
72892
72917
  const [artifact] = matches;
72893
- if (!artifact)
72894
- return testCountEnvelope(
72895
- errorEnvelope(
72896
- panel.id,
72897
- "upstream-error",
72898
- "The test-results artifact is unavailable.",
72899
- link
72900
- )
72901
- );
72918
+ if (matches.length !== 1 || !artifact || artifact.expired)
72919
+ return evidenceError("upstream-error");
72902
72920
  const archiveResponse = await githubFetch(githubArtifactDownloadCall(source, artifact.id), {
72903
72921
  ...args,
72904
72922
  source,
72905
72923
  requestHeaders: noValidators,
72906
72924
  githubClient
72907
72925
  });
72908
- if (!archiveResponse.ok) return testCountFailure(panel.id, source, archiveResponse, link);
72926
+ if (!archiveResponse.ok) return failedResponse(archiveResponse);
72909
72927
  const archiveObservedAt = observedAt2(archiveResponse.headers.get("date"));
72910
72928
  failureObservedAt = oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt);
72911
72929
  const summary = testResultsSummarySchema.parse(
72912
- JSON.parse(await readTestResultsArtifact(archiveResponse, panel.test_results.entry))
72930
+ JSON.parse(await readTestResultsArtifact(archiveResponse, artifactConfig.entry))
72913
72931
  );
72914
72932
  if (summary.runId !== run.id || summary.runAttempt !== run.run_attempt || summary.headSha !== run.head_sha)
72915
72933
  throw new Error("Test-results manifest does not belong to the selected workflow run.");
72916
- return testCountEnvelope({
72917
- panelId: panel.id,
72934
+ return {
72918
72935
  state: "ok",
72919
72936
  observedAt: oldestObservation(runObservedAt, artifactObservedAt, archiveObservedAt),
72920
- link,
72921
72937
  signal: {
72922
72938
  type: "test-count",
72923
72939
  status: "reported",
@@ -72926,16 +72942,10 @@ async function fetchGithubActionsTestCount(args) {
72926
72942
  ...summary.failed === void 0 ? {} : { failed: summary.failed },
72927
72943
  ...summary.skipped === void 0 ? {} : { skipped: summary.skipped }
72928
72944
  }
72929
- });
72945
+ };
72930
72946
  } catch (error63) {
72931
- return testCountEnvelope(
72932
- errorEnvelope(
72933
- panel.id,
72934
- error63 instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error",
72935
- error63,
72936
- failureLink,
72937
- failureObservedAt
72938
- )
72947
+ return evidenceError(
72948
+ error63 instanceof GithubAuthenticationError ? "unauthorized" : "upstream-error"
72939
72949
  );
72940
72950
  }
72941
72951
  }
@@ -73151,6 +73161,7 @@ async function fetchGithubActionsPipeline(args) {
73151
73161
  ...args,
73152
73162
  source: parsedSource,
73153
73163
  workflow: parsedPanel.pipeline,
73164
+ requestHeaders: parsedPanel.test_results ? new Headers() : args.requestHeaders,
73154
73165
  githubClient
73155
73166
  });
73156
73167
  } catch (error63) {
@@ -73215,14 +73226,21 @@ async function fetchGithubActionsPipeline(args) {
73215
73226
  });
73216
73227
  if (activity) signal.activity = activity;
73217
73228
  }
73229
+ if (parsedPanel.test_results) {
73230
+ signal.testResults = await readGithubRunTestEvidence(
73231
+ { ...args, source: parsedSource, githubClient },
73232
+ { run, observedAt: selected.observedAt },
73233
+ parsedPanel.test_results
73234
+ );
73235
+ }
73218
73236
  const envelope = {
73219
73237
  panelId: args.panel.id,
73220
73238
  state: "ok",
73221
- observedAt: observedAt2(upstream.headers.get("date")),
73239
+ observedAt: signal.testResults ? oldestObservation(selected.observedAt, signal.testResults.observedAt) : selected.observedAt,
73222
73240
  link: run.html_url,
73223
73241
  signal
73224
73242
  };
73225
- return { envelope, response: upstream };
73243
+ return { envelope, response: parsedPanel.test_results ? new Response(null) : upstream };
73226
73244
  } catch (error63) {
73227
73245
  return {
73228
73246
  response: new Response(
@@ -74241,6 +74259,15 @@ var getPathNoStrict = (request) => {
74241
74259
  const result = getPath(request);
74242
74260
  return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result;
74243
74261
  };
74262
+ var stripPathPrefix = (rawPath, decodedPath, prefixLength) => {
74263
+ if (rawPath.at(-1) === "/" && decodedPath.at(-1) !== "/") rawPath = rawPath.slice(0, -1);
74264
+ let index = 0;
74265
+ for (let i6 = decodedPath.indexOf("/"); i6 !== -1 && i6 < prefixLength; i6 = decodedPath.indexOf("/", i6 + 1)) {
74266
+ index = rawPath.indexOf("/", index + 1);
74267
+ if (index === -1) return "/";
74268
+ }
74269
+ return rawPath.slice(index) || "/";
74270
+ };
74244
74271
  var mergePath = (base, sub, ...rest) => {
74245
74272
  if (rest.length) sub = mergePath(sub, ...rest);
74246
74273
  return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
@@ -74821,7 +74848,7 @@ var Context = class {
74821
74848
  * ```
74822
74849
  */
74823
74850
  header = (name, value, options) => {
74824
- if (this.finalized) this.#res = createResponseInstance(this.#res.body, this.#res);
74851
+ if (this.finalized && !this.#res.bodyUsed) this.#res = createResponseInstance(this.#res.body, this.#res);
74825
74852
  const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers();
74826
74853
  if (value === void 0) headers.delete(name);
74827
74854
  else if (options?.append) headers.append(name, value);
@@ -75050,6 +75077,37 @@ var UnsupportedPathError = class extends Error {
75050
75077
  // node_modules/hono/dist/utils/constants.js
75051
75078
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
75052
75079
 
75080
+ // node_modules/hono/dist/utils/mount.js
75081
+ var getPathPrefixLength = (c5, pathPrefix, routePath) => {
75082
+ if (!/[:*]/.test(pathPrefix)) return pathPrefix.length;
75083
+ const path = c5.req.path;
75084
+ const matchResult = c5.req[GET_MATCH_RESULT];
75085
+ const paramNames = /* @__PURE__ */ new Set();
75086
+ let length = 0;
75087
+ for (const part of splitRoutingPath(pathPrefix)) {
75088
+ const pattern = getPattern(part);
75089
+ if (pattern && pattern !== "*") {
75090
+ const name = pattern[1];
75091
+ if (paramNames.has(name)) throw new Error(`mount(): Duplicate path parameter "${name}" in "${routePath}".`);
75092
+ paramNames.add(name);
75093
+ }
75094
+ if (pattern && pattern !== "*" && pattern[2] !== true) {
75095
+ const value = matchResult.length === 2 ? matchResult[1][matchResult[0][c5.req.routeIndex][1][pattern[1]]] : matchResult[0][c5.req.routeIndex][1][pattern[1]];
75096
+ length += value.length + 1;
75097
+ } else if (pattern || part.endsWith("*")) {
75098
+ const end2 = path.indexOf("/", length + 1);
75099
+ length = end2 === -1 ? path.length : end2;
75100
+ } else length += part.length + 1;
75101
+ }
75102
+ return length;
75103
+ };
75104
+ var createMountedRequest = (c5, pathPrefix, routePath) => {
75105
+ if (/[^/]\*$/.test(routePath)) throw new Error(`mount(): Wildcard must follow "/" in "${routePath}".`);
75106
+ const url2 = new URL(c5.req.raw.url);
75107
+ url2.pathname = stripPathPrefix(url2.pathname, c5.req.path, getPathPrefixLength(c5, pathPrefix, routePath));
75108
+ return new Request(url2, c5.req.raw);
75109
+ };
75110
+
75053
75111
  // node_modules/hono/dist/hono-base.js
75054
75112
  var notFoundHandler = (c5) => {
75055
75113
  return c5.text("404 Not Found", 404);
@@ -75271,17 +75329,10 @@ var Hono = class Hono2 {
75271
75329
  }
75272
75330
  return [c5.env, executionContext];
75273
75331
  };
75274
- replaceRequest ||= (() => {
75275
- const mergedPath = mergePath(this._basePath, path);
75276
- const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
75277
- return (request) => {
75278
- const url2 = new URL(request.url);
75279
- url2.pathname = this.getPath(request).slice(pathPrefixLength) || "/";
75280
- return new Request(url2, request);
75281
- };
75282
- })();
75332
+ const mergedPath = mergePath(this._basePath, path);
75333
+ const defaultReplaceRequest = (c5) => createMountedRequest(c5, mergedPath.replace(/\/$/, ""), mergePath(mergedPath, "*"));
75283
75334
  const handler2 = async (c5, next) => {
75284
- const res = await applicationHandler(replaceRequest(c5.req.raw), ...getOptions(c5));
75335
+ const res = await applicationHandler(replaceRequest ? replaceRequest(c5.req.raw) : defaultReplaceRequest(c5), ...getOptions(c5));
75285
75336
  if (res) return res;
75286
75337
  await next();
75287
75338
  };
@@ -75413,14 +75464,15 @@ function match(method, path) {
75413
75464
 
75414
75465
  // node_modules/hono/dist/router/reg-exp-router/node.js
75415
75466
  var LABEL_REG_EXP_STR = "[^/]+";
75416
- var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
75467
+ var ONLY_WILDCARD_REG_EXP_STR = "[^]*";
75468
+ var TAIL_WILDCARD_REG_EXP_STR = "(?:|/[^]*)";
75417
75469
  var PATH_ERROR = /* @__PURE__ */ Symbol();
75418
75470
  var regExpMetaChars = /* @__PURE__ */ new Set(".\\+*[^]$()");
75419
75471
  function compareKey(a5, b5) {
75420
75472
  if (a5.length === 1) return b5.length === 1 ? a5 < b5 ? -1 : 1 : -1;
75421
75473
  if (b5.length === 1) return 1;
75422
- if (a5 === ".*" || a5 === "(?:|/.*)") return b5 === "(?:|/.*)" ? -1 : 1;
75423
- else if (b5 === ".*" || b5 === "(?:|/.*)") return -1;
75474
+ if (a5 === "[^]*" || a5 === "(?:|/[^]*)") return b5 === "[^]*" ? -1 : 1;
75475
+ else if (b5 === "[^]*" || b5 === "(?:|/[^]*)") return -1;
75424
75476
  if (a5 === "[^/]+") return 1;
75425
75477
  else if (b5 === "[^/]+") return -1;
75426
75478
  return a5.length === b5.length ? a5 < b5 ? -1 : 1 : b5.length - a5.length;
@@ -75429,14 +75481,14 @@ var Node = class Node2 {
75429
75481
  #index;
75430
75482
  #varIndex;
75431
75483
  #children = createNullObject();
75432
- insert(tokens, index, paramMap, context, isStatic) {
75484
+ insert(tokens, index, paramMap, context, keys) {
75433
75485
  let node2 = this;
75434
75486
  for (let i6 = 0, len = tokens.length; i6 < len; i6++) {
75435
75487
  const token = tokens[i6];
75436
75488
  const pattern = token.length === 1 ? token === "*" ? i6 === len - 1 ? [
75437
75489
  "",
75438
75490
  "",
75439
- ".*"
75491
+ ONLY_WILDCARD_REG_EXP_STR
75440
75492
  ] : [
75441
75493
  "",
75442
75494
  "",
@@ -75451,15 +75503,16 @@ var Node = class Node2 {
75451
75503
  const name = pattern[1];
75452
75504
  let regexpStr = pattern[2] || "[^/]+";
75453
75505
  if (name && pattern[2]) {
75454
- if (regexpStr === ".*") throw PATH_ERROR;
75506
+ if (new RegExp(regexpStr).test("")) throw PATH_ERROR;
75455
75507
  regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
75456
75508
  if (/\((?!\?:)/.test(regexpStr)) throw PATH_ERROR;
75457
75509
  if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) throw PATH_ERROR;
75458
75510
  }
75511
+ keys.push(regexpStr);
75459
75512
  nextNode = node2.#children[regexpStr];
75460
75513
  if (!nextNode) {
75461
- if (regexpStr !== ".*" && regexpStr !== "(?:|/.*)") {
75462
- for (const k5 in node2.#children) if ((regexpStr.length > 1 || k5.length > 1) && k5 !== ".*" && k5 !== "(?:|/.*)") throw PATH_ERROR;
75514
+ if (regexpStr !== "[^]*" && regexpStr !== "(?:|/[^]*)") {
75515
+ for (const k5 in node2.#children) if ((regexpStr.length > 1 || k5.length > 1) && k5 !== "[^]*" && k5 !== "(?:|/[^]*)") throw PATH_ERROR;
75463
75516
  }
75464
75517
  nextNode = node2.#children[regexpStr] = new Node2();
75465
75518
  }
@@ -75468,16 +75521,17 @@ var Node = class Node2 {
75468
75521
  paramMap.push([name, nextNode.#varIndex]);
75469
75522
  }
75470
75523
  } else {
75524
+ keys.push(token);
75471
75525
  nextNode = node2.#children[token];
75472
75526
  if (!nextNode) {
75473
- for (const k5 in node2.#children) if (k5.length > 1 && k5 !== ".*" && k5 !== "(?:|/.*)") throw PATH_ERROR;
75527
+ for (const k5 in node2.#children) if (k5.length > 1 && k5 !== "[^]*" && k5 !== "(?:|/[^]*)") throw PATH_ERROR;
75474
75528
  nextNode = node2.#children[token] = new Node2();
75475
75529
  }
75476
75530
  }
75477
75531
  node2 = nextNode;
75478
75532
  }
75479
75533
  if (node2.#index !== void 0) throw PATH_ERROR;
75480
- node2.#index = isStatic ? -1 : index;
75534
+ node2.#index = index;
75481
75535
  }
75482
75536
  buildRegExpStr() {
75483
75537
  const strList = Object.keys(this.#children).sort(compareKey).map((k5) => {
@@ -75499,8 +75553,14 @@ var Trie = class {
75499
75553
  #index = 0;
75500
75554
  paths = createNullObject();
75501
75555
  insert(path, isStatic) {
75556
+ const keys = [];
75502
75557
  if (isStatic) {
75503
- this.#root.insert(path.split(""), 0, [], this.#context, true);
75558
+ this.#root.insert(path.split(""), -1, [], this.#context, keys);
75559
+ this.paths[path] = [
75560
+ -1,
75561
+ [],
75562
+ keys
75563
+ ];
75504
75564
  return;
75505
75565
  }
75506
75566
  const paramAssoc = [];
@@ -75508,7 +75568,7 @@ var Trie = class {
75508
75568
  let markedPath = path;
75509
75569
  for (let i6 = 0; ; ) {
75510
75570
  let replaced = false;
75511
- markedPath = markedPath.replace(/\{[^}]+\}/g, (m3) => {
75571
+ markedPath = markedPath.replace(/\{(?:\{[\d,]+\}|[^}])+\}/g, (m3) => {
75512
75572
  const mark = `@\\${i6}`;
75513
75573
  groups[i6] = [mark, m3];
75514
75574
  i6++;
@@ -75517,7 +75577,7 @@ var Trie = class {
75517
75577
  });
75518
75578
  if (!replaced) break;
75519
75579
  }
75520
- const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
75580
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./gs) || [];
75521
75581
  for (let i6 = groups.length - 1; i6 >= 0; i6--) {
75522
75582
  const [mark] = groups[i6];
75523
75583
  for (let j5 = tokens.length - 1; j5 >= 0; j5--) if (tokens[j5].indexOf(mark) !== -1) {
@@ -75525,8 +75585,12 @@ var Trie = class {
75525
75585
  break;
75526
75586
  }
75527
75587
  }
75528
- this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
75529
- this.paths[path] = [this.#index++, paramAssoc];
75588
+ this.#root.insert(tokens, this.#index, paramAssoc, this.#context, keys);
75589
+ this.paths[path] = [
75590
+ this.#index++,
75591
+ paramAssoc,
75592
+ keys
75593
+ ];
75530
75594
  }
75531
75595
  buildRegExp() {
75532
75596
  let regexp = this.#root.buildRegExpStr();
@@ -75558,21 +75622,21 @@ var Trie = class {
75558
75622
  };
75559
75623
 
75560
75624
  // node_modules/hono/dist/router/reg-exp-router/router.js
75561
- var wildcardRegExpCache = createNullObject();
75562
- function buildWildcardRegExp(path) {
75563
- return wildcardRegExpCache[path] ??= new RegExp(`^${path.replace(/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g, (match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ".*" : `/:${LABEL_REG_EXP_STR}`)}$`);
75625
+ function endsWithWildcard(keys) {
75626
+ const tail = keys[keys.length - 1];
75627
+ return tail === "[^]*" || tail === "(?:|/[^]*)";
75564
75628
  }
75565
- function findMiddleware(middleware, path) {
75566
- for (const k5 of Object.keys(middleware).sort((a5, b5) => b5.length - a5.length)) if (buildWildcardRegExp(k5).test(path)) return [...middleware[k5]];
75629
+ function covers(wildcard, target) {
75630
+ const last = wildcard.length - 1;
75631
+ for (let i6 = 0; i6 < last; i6++) if (wildcard[i6] !== target[i6]) return false;
75632
+ return wildcard[last] === "[^]*" || target.length === last || target[last] === "/";
75567
75633
  }
75568
75634
  var RegExpRouter = class {
75569
75635
  name = "RegExpRouter";
75570
- #middleware;
75571
- #routes;
75636
+ #handlers;
75572
75637
  #tries;
75573
75638
  constructor() {
75574
- this.#middleware = { ["ALL"]: createNullObject() };
75575
- this.#routes = { ["ALL"]: createNullObject() };
75639
+ this.#handlers = { ["ALL"]: [] };
75576
75640
  this.#tries = { ["ALL"]: new Trie() };
75577
75641
  }
75578
75642
  #insertPath(method, path) {
@@ -75583,65 +75647,53 @@ var RegExpRouter = class {
75583
75647
  }
75584
75648
  }
75585
75649
  add(method, path, handler2) {
75586
- const middleware = this.#middleware;
75587
- const routes = this.#routes;
75588
- if (!middleware) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
75589
- if (!middleware[method]) {
75590
- this.#tries[method] = new Trie();
75591
- for (const handlerMap of [middleware, routes]) {
75592
- handlerMap[method] = createNullObject();
75593
- for (const p3 in handlerMap["ALL"]) {
75594
- handlerMap[method][p3] = [...handlerMap["ALL"][p3]];
75595
- this.#insertPath(method, p3);
75596
- }
75597
- }
75650
+ const handlers = this.#handlers;
75651
+ if (!handlers) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
75652
+ const tries = this.#tries;
75653
+ if (!handlers[method]) {
75654
+ tries[method] = new Trie();
75655
+ handlers[method] = [...handlers["ALL"]];
75656
+ for (const p3 in tries["ALL"].paths) this.#insertPath(method, p3);
75598
75657
  }
75599
75658
  if (path === "/*") path = "*";
75600
- const methods = method === "ALL" ? Object.keys(middleware) : [method];
75601
- if (/\*$/.test(path)) {
75602
- const re = buildWildcardRegExp(path);
75603
- for (const m3 of methods) if (!middleware[m3][path]) {
75604
- this.#insertPath(m3, path);
75605
- middleware[m3][path] = findMiddleware(middleware[m3], path) || findMiddleware(middleware["ALL"], path) || [];
75606
- }
75607
- for (const handlerMap of [middleware, routes]) for (const m3 of methods) for (const p3 in handlerMap[m3]) re.test(p3) && handlerMap[m3][p3].push([handler2, path]);
75608
- return;
75609
- }
75659
+ const methods = method === "ALL" ? Object.keys(handlers) : [method];
75610
75660
  const paths = checkOptionalParameter(path) || [path];
75611
75661
  for (const path2 of paths) for (const m3 of methods) {
75612
- if (!routes[m3][path2]) {
75613
- this.#insertPath(m3, path2);
75614
- routes[m3][path2] = findMiddleware(middleware[m3], path2) || findMiddleware(middleware["ALL"], path2) || [];
75615
- }
75616
- routes[m3][path2].push([handler2, path2]);
75662
+ if (!tries[m3].paths[path2]) this.#insertPath(m3, path2);
75663
+ handlers[m3].push([handler2, path2]);
75617
75664
  }
75618
75665
  }
75619
75666
  match = match;
75620
75667
  buildAllMatchers() {
75621
75668
  const matchers = createNullObject();
75622
- for (const method of Object.keys(this.#routes)) matchers[method] = this.#buildMatcher(method);
75623
- this.#middleware = this.#routes = this.#tries = void 0;
75624
- wildcardRegExpCache = createNullObject();
75669
+ for (const method of Object.keys(this.#handlers)) matchers[method] = this.#buildMatcher(method);
75670
+ this.#handlers = this.#tries = void 0;
75625
75671
  return matchers;
75626
75672
  }
75627
75673
  #buildMatcher(method) {
75628
- const middleware = this.#middleware[method];
75629
- const routes = this.#routes[method];
75674
+ const handlers = this.#handlers[method];
75630
75675
  const trie = this.#tries[method];
75631
75676
  const staticMap = createNullObject();
75632
75677
  const handlerData = [];
75633
75678
  const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
75634
- for (const r5 of [middleware, routes]) for (const path in r5) {
75635
- const handlers = r5[path];
75636
- const pathData = trie.paths[path];
75637
- if (!pathData) {
75638
- staticMap[path] = [handlers.map(([h5]) => [h5, createNullObject()]), emptyParam];
75639
- continue;
75640
- }
75641
- handlerData[pathData[0]] = handlers.map(([h5, handlerPath]) => [h5, trie.paths[handlerPath][1].reduceRight((map4, [key], i6) => {
75642
- map4[key] = paramReplacementMap[pathData[1][i6][1]];
75679
+ const own2 = createNullObject();
75680
+ const entries = handlers.map(([h5, p3], i6) => {
75681
+ (own2[p3] ??= []).push(i6);
75682
+ return [h5, trie.paths[p3][1].reduceRight((map4, [key, varIndex]) => {
75683
+ map4[key] = paramReplacementMap[varIndex];
75643
75684
  return map4;
75644
- }, createNullObject())]);
75685
+ }, createNullObject())];
75686
+ });
75687
+ const wildcards = Object.keys(own2).filter((p3) => endsWithWildcard(trie.paths[p3][2]));
75688
+ for (const path in trie.paths) {
75689
+ const [index, , keys] = trie.paths[path];
75690
+ const indexes = [...own2[path]];
75691
+ for (const p3 of wildcards) if (p3 !== path && covers(trie.paths[p3][2], keys)) indexes.push(...own2[p3]);
75692
+ const data = indexes.sort((a5, b5) => a5 - b5).map((i6) => entries[i6]);
75693
+ if (index === -1) {
75694
+ const last = Math.max(-1, ...data.flatMap(([, map4]) => Object.values(map4)));
75695
+ staticMap[path] = [data, last === -1 ? emptyParam : path.match(regexp).slice(0, last + 1)];
75696
+ } else handlerData[index] = data;
75645
75697
  }
75646
75698
  return [
75647
75699
  regexp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuous-excellence/ze-great-dashboard-aws",
3
- "version": "0.31.53",
3
+ "version": "0.31.55",
4
4
  "type": "module",
5
5
  "description": "AWS server runtime and CloudFormation deployment tooling for Ze Great Dashboard.",
6
6
  "keywords": [