@continuous-excellence/ze-great-dashboard-aws 0.25.0 → 0.26.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/dist/cli.js CHANGED
@@ -83,6 +83,26 @@ var positionSchema = z2.union([
83
83
  ]);
84
84
  var panelDensities = ["auto", "comfortable", "compact"];
85
85
  var panelDensitySchema = z2.enum(panelDensities);
86
+ var httpValueJsonPathSchema = z2.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path");
87
+ var httpValueFactSchema = z2.object({
88
+ /** Stable address below the panel; labels and ordering may change without repointing it. */
89
+ id: z2.string().min(1),
90
+ label: z2.string().min(1),
91
+ url: z2.url(),
92
+ json_path: httpValueJsonPathSchema.optional()
93
+ });
94
+ var httpValuePanelIdentitySchema = z2.object({
95
+ id: z2.string().min(1),
96
+ type: z2.literal("http-value")
97
+ });
98
+ var httpValueScalarPanelSchema = httpValuePanelIdentitySchema.extend({
99
+ url: z2.url(),
100
+ json_path: httpValueJsonPathSchema.optional(),
101
+ link: z2.url().optional()
102
+ });
103
+ var httpValueGroupedPanelSchema = httpValuePanelIdentitySchema.extend({
104
+ facts: z2.array(httpValueFactSchema).min(1).max(4)
105
+ });
86
106
  var visibleRunningAnimations = [
87
107
  "radial",
88
108
  "runway",
@@ -125,7 +145,9 @@ var panelSchema = z2.looseObject({
125
145
  /** Source-agnostic endpoint used by the http-value signal. */
126
146
  url: z2.url().optional(),
127
147
  /** Small, deliberate JSON path subset: $.version, $.deployment.version, or $.response.docs[0].latestVersion. */
128
- json_path: z2.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path").optional()
148
+ json_path: httpValueJsonPathSchema.optional(),
149
+ /** Up to four independently fetched source-agnostic readings in one visual panel. */
150
+ facts: httpValueGroupedPanelSchema.shape.facts.optional()
129
151
  }).superRefine((panel, ctx) => {
130
152
  if ("display" in panel) {
131
153
  ctx.addIssue({
@@ -134,6 +156,32 @@ var panelSchema = z2.looseObject({
134
156
  message: "display was removed; use density"
135
157
  });
136
158
  }
159
+ if (panel.type !== "http-value") return;
160
+ if (panel.facts) {
161
+ if (panel.url !== void 0)
162
+ ctx.addIssue({
163
+ code: "custom",
164
+ path: ["url"],
165
+ message: "use facts URLs for grouped panels"
166
+ });
167
+ if (panel.json_path !== void 0)
168
+ ctx.addIssue({
169
+ code: "custom",
170
+ path: ["json_path"],
171
+ message: "use facts json_path values for grouped panels"
172
+ });
173
+ const seen = /* @__PURE__ */ new Map();
174
+ panel.facts.forEach((fact, index) => {
175
+ const first = seen.get(fact.id);
176
+ if (first === void 0) seen.set(fact.id, index);
177
+ else
178
+ ctx.addIssue({
179
+ code: "custom",
180
+ path: ["facts", index, "id"],
181
+ message: `duplicate fact id "${fact.id}" (already used by fact at index ${first})`
182
+ });
183
+ });
184
+ }
137
185
  });
138
186
  var githubAppSchema = z2.object({
139
187
  app_id_env: z2.string().min(1),
package/dist/index.js CHANGED
@@ -764,6 +764,26 @@ var positionSchema = z2.union([
764
764
  ]);
765
765
  var panelDensities = ["auto", "comfortable", "compact"];
766
766
  var panelDensitySchema = z2.enum(panelDensities);
767
+ var httpValueJsonPathSchema = z2.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path");
768
+ var httpValueFactSchema = z2.object({
769
+ /** Stable address below the panel; labels and ordering may change without repointing it. */
770
+ id: z2.string().min(1),
771
+ label: z2.string().min(1),
772
+ url: z2.url(),
773
+ json_path: httpValueJsonPathSchema.optional()
774
+ });
775
+ var httpValuePanelIdentitySchema = z2.object({
776
+ id: z2.string().min(1),
777
+ type: z2.literal("http-value")
778
+ });
779
+ var httpValueScalarPanelSchema = httpValuePanelIdentitySchema.extend({
780
+ url: z2.url(),
781
+ json_path: httpValueJsonPathSchema.optional(),
782
+ link: z2.url().optional()
783
+ });
784
+ var httpValueGroupedPanelSchema = httpValuePanelIdentitySchema.extend({
785
+ facts: z2.array(httpValueFactSchema).min(1).max(4)
786
+ });
767
787
  var visibleRunningAnimations = [
768
788
  "radial",
769
789
  "runway",
@@ -806,7 +826,9 @@ var panelSchema = z2.looseObject({
806
826
  /** Source-agnostic endpoint used by the http-value signal. */
807
827
  url: z2.url().optional(),
808
828
  /** Small, deliberate JSON path subset: $.version, $.deployment.version, or $.response.docs[0].latestVersion. */
809
- json_path: z2.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path").optional()
829
+ json_path: httpValueJsonPathSchema.optional(),
830
+ /** Up to four independently fetched source-agnostic readings in one visual panel. */
831
+ facts: httpValueGroupedPanelSchema.shape.facts.optional()
810
832
  }).superRefine((panel, ctx) => {
811
833
  if ("display" in panel) {
812
834
  ctx.addIssue({
@@ -815,6 +837,32 @@ var panelSchema = z2.looseObject({
815
837
  message: "display was removed; use density"
816
838
  });
817
839
  }
840
+ if (panel.type !== "http-value") return;
841
+ if (panel.facts) {
842
+ if (panel.url !== void 0)
843
+ ctx.addIssue({
844
+ code: "custom",
845
+ path: ["url"],
846
+ message: "use facts URLs for grouped panels"
847
+ });
848
+ if (panel.json_path !== void 0)
849
+ ctx.addIssue({
850
+ code: "custom",
851
+ path: ["json_path"],
852
+ message: "use facts json_path values for grouped panels"
853
+ });
854
+ const seen = /* @__PURE__ */ new Map();
855
+ panel.facts.forEach((fact, index) => {
856
+ const first = seen.get(fact.id);
857
+ if (first === void 0) seen.set(fact.id, index);
858
+ else
859
+ ctx.addIssue({
860
+ code: "custom",
861
+ path: ["facts", index, "id"],
862
+ message: `duplicate fact id "${fact.id}" (already used by fact at index ${first})`
863
+ });
864
+ });
865
+ }
818
866
  });
819
867
  var githubAppSchema = z2.object({
820
868
  app_id_env: z2.string().min(1),
package/dist/lambda.mjs CHANGED
@@ -68320,6 +68320,26 @@ var positionSchema = external_exports.union([
68320
68320
  ]);
68321
68321
  var panelDensities = ["auto", "comfortable", "compact"];
68322
68322
  var panelDensitySchema = external_exports.enum(panelDensities);
68323
+ var httpValueJsonPathSchema = external_exports.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path");
68324
+ var httpValueFactSchema = external_exports.object({
68325
+ /** Stable address below the panel; labels and ordering may change without repointing it. */
68326
+ id: external_exports.string().min(1),
68327
+ label: external_exports.string().min(1),
68328
+ url: external_exports.url(),
68329
+ json_path: httpValueJsonPathSchema.optional()
68330
+ });
68331
+ var httpValuePanelIdentitySchema = external_exports.object({
68332
+ id: external_exports.string().min(1),
68333
+ type: external_exports.literal("http-value")
68334
+ });
68335
+ var httpValueScalarPanelSchema = httpValuePanelIdentitySchema.extend({
68336
+ url: external_exports.url(),
68337
+ json_path: httpValueJsonPathSchema.optional(),
68338
+ link: external_exports.url().optional()
68339
+ });
68340
+ var httpValueGroupedPanelSchema = httpValuePanelIdentitySchema.extend({
68341
+ facts: external_exports.array(httpValueFactSchema).min(1).max(4)
68342
+ });
68323
68343
  var visibleRunningAnimations = [
68324
68344
  "radial",
68325
68345
  "runway",
@@ -68362,7 +68382,9 @@ var panelSchema = external_exports.looseObject({
68362
68382
  /** Source-agnostic endpoint used by the http-value signal. */
68363
68383
  url: external_exports.url().optional(),
68364
68384
  /** Small, deliberate JSON path subset: $.version, $.deployment.version, or $.response.docs[0].latestVersion. */
68365
- json_path: external_exports.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/, "must be a simple JSON path").optional()
68385
+ json_path: httpValueJsonPathSchema.optional(),
68386
+ /** Up to four independently fetched source-agnostic readings in one visual panel. */
68387
+ facts: httpValueGroupedPanelSchema.shape.facts.optional()
68366
68388
  }).superRefine((panel, ctx) => {
68367
68389
  if ("display" in panel) {
68368
68390
  ctx.addIssue({
@@ -68371,6 +68393,32 @@ var panelSchema = external_exports.looseObject({
68371
68393
  message: "display was removed; use density"
68372
68394
  });
68373
68395
  }
68396
+ if (panel.type !== "http-value") return;
68397
+ if (panel.facts) {
68398
+ if (panel.url !== void 0)
68399
+ ctx.addIssue({
68400
+ code: "custom",
68401
+ path: ["url"],
68402
+ message: "use facts URLs for grouped panels"
68403
+ });
68404
+ if (panel.json_path !== void 0)
68405
+ ctx.addIssue({
68406
+ code: "custom",
68407
+ path: ["json_path"],
68408
+ message: "use facts json_path values for grouped panels"
68409
+ });
68410
+ const seen = /* @__PURE__ */ new Map();
68411
+ panel.facts.forEach((fact, index) => {
68412
+ const first = seen.get(fact.id);
68413
+ if (first === void 0) seen.set(fact.id, index);
68414
+ else
68415
+ ctx.addIssue({
68416
+ code: "custom",
68417
+ path: ["facts", index, "id"],
68418
+ message: `duplicate fact id "${fact.id}" (already used by fact at index ${first})`
68419
+ });
68420
+ });
68421
+ }
68374
68422
  });
68375
68423
  var githubAppSchema = external_exports.object({
68376
68424
  app_id_env: external_exports.string().min(1),
@@ -69805,23 +69853,17 @@ function failure(panelId, kind, error62, link, date6, failure2) {
69805
69853
  }
69806
69854
 
69807
69855
  // packages/server/src/adapters/http-value.ts
69808
- var httpValuePanelSchema = external_exports.object({
69809
- id: external_exports.string().min(1),
69810
- type: external_exports.literal("http-value"),
69811
- url: external_exports.url(),
69812
- json_path: external_exports.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/).optional(),
69813
- link: external_exports.url().optional()
69814
- });
69815
69856
  function permittedHttpValueCalls(panel) {
69816
- const parsed = httpValuePanelSchema.parse(panel);
69817
- const url2 = new URL(parsed.url);
69818
- if (url2.protocol !== "http:" && url2.protocol !== "https:") {
69819
- throw new Error("http-value URLs must use http or https");
69820
- }
69821
- return [{ url: url2.toString(), headers: new Headers({ accept: "application/json, text/plain" }) }];
69857
+ const grouped = httpValueGroupedPanelSchema.safeParse(panel);
69858
+ const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [httpValueScalarPanelSchema.parse(panel).url];
69859
+ return urls.map(permittedCall);
69860
+ }
69861
+ function httpValueFact(panel, factId) {
69862
+ const grouped = httpValueGroupedPanelSchema.parse(panel);
69863
+ return grouped.facts.find((fact) => fact.id === factId);
69822
69864
  }
69823
69865
  async function fetchHttpValue(args) {
69824
- const panel = httpValuePanelSchema.parse(args.panel);
69866
+ const panel = httpValueScalarPanelSchema.parse(args.panel);
69825
69867
  const [call] = permittedHttpValueCalls(args.panel);
69826
69868
  if (!call) throw new Error("http-value adapter declared no permitted call");
69827
69869
  forwardValidators(args.requestHeaders, call.headers);
@@ -69870,6 +69912,21 @@ async function fetchHttpValue(args) {
69870
69912
  };
69871
69913
  }
69872
69914
  }
69915
+ async function fetchHttpValueFact(args) {
69916
+ const fact = httpValueFact(args.panel, args.factId);
69917
+ if (!fact) throw new Error(`http-value fact "${args.factId}" is not configured`);
69918
+ return fetchHttpValue({
69919
+ panel: { id: args.panel.id, type: "http-value", url: fact.url, json_path: fact.json_path },
69920
+ requestHeaders: args.requestHeaders,
69921
+ fetcher: args.fetcher
69922
+ });
69923
+ }
69924
+ function permittedCall(value) {
69925
+ const url2 = new URL(value);
69926
+ if (url2.protocol !== "http:" && url2.protocol !== "https:")
69927
+ throw new Error("http-value URLs must use http or https");
69928
+ return { url: url2.toString(), headers: new Headers({ accept: "application/json, text/plain" }) };
69929
+ }
69873
69930
  function parseValue(text) {
69874
69931
  const trimmed = text.trim();
69875
69932
  try {
@@ -72305,6 +72362,27 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
72305
72362
  };
72306
72363
  app.get("/api/boards/:board/rendered", (c5) => layoutDownload(c5, "rendered"));
72307
72364
  app.get("/api/boards/:board/authored", (c5) => layoutDownload(c5, "authored"));
72365
+ app.get("/api/panel/:board/:panelId/facts/:factId", async (c5) => {
72366
+ const boardName = c5.req.param("board");
72367
+ const panelId = c5.req.param("panelId");
72368
+ const factId = c5.req.param("factId");
72369
+ const panel = deps.boardConfig?.boards[boardName]?.panels.find(
72370
+ (candidate) => candidate.id === panelId
72371
+ );
72372
+ const fact = panel?.type === "http-value" && panel.facts ? httpValueFact(panel, factId) : void 0;
72373
+ if (!panel || !fact || !permitsPanelOperation(allowlist, boardName, panelId, "read"))
72374
+ return rejected(c5, { boardId: boardName, panelId, operation: "read" });
72375
+ return observation(
72376
+ c5,
72377
+ fetchHttpValueFact({
72378
+ panel,
72379
+ factId,
72380
+ requestHeaders: c5.req.raw.headers,
72381
+ fetcher: deps.fetcher ?? globalThis.fetch
72382
+ }),
72383
+ { boardId: boardName, panelId, operation: "read", destination: fact.url }
72384
+ );
72385
+ });
72308
72386
  app.get("/api/panel/:board/:panelId", async (c5) => {
72309
72387
  const boardName = c5.req.param("board");
72310
72388
  const panelId = c5.req.param("panelId");
@@ -72367,6 +72445,7 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
72367
72445
  if (panel.type === "pull-request-health")
72368
72446
  return rejected(c5, { boardId: boardName, panelId, operation: "read" });
72369
72447
  if (panel.type === "http-value") {
72448
+ if (panel.facts) return rejected(c5, { boardId: boardName, panelId, operation: "read" });
72370
72449
  const result = fetchHttpValue({
72371
72450
  panel,
72372
72451
  requestHeaders: c5.req.raw.headers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuous-excellence/ze-great-dashboard-aws",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "description": "AWS server runtime and CloudFormation deployment tooling for Ze Great Dashboard.",
6
6
  "keywords": [