@monster-orange/den 0.0.0 → 0.2.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.
Files changed (255) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -1
  4. package/bin/den +68 -0
  5. package/docs/distributions.md +499 -0
  6. package/make/demo.mk +82 -0
  7. package/make/den.mk +80 -0
  8. package/make/journeys.mk +57 -0
  9. package/make/local_dev.mk +154 -0
  10. package/make/security.mk +59 -0
  11. package/make/test.mk +405 -0
  12. package/package.json +101 -4
  13. package/scripts/auth-configure.ts +415 -0
  14. package/scripts/heal-config.ts +92 -0
  15. package/scripts/jsonl-playwright-reporter.js +70 -0
  16. package/scripts/manifest-read.cjs +91 -0
  17. package/scripts/setup-1password.ts +570 -0
  18. package/scripts/setup-secrets.ts +149 -0
  19. package/scripts/view-results.js +156 -0
  20. package/service/install.sh +112 -0
  21. package/src/pillars/api/fixtures/index.ts +128 -0
  22. package/src/pillars/api/fixtures/openapi-validator.ts +111 -0
  23. package/src/pillars/api/global-setup.ts +103 -0
  24. package/src/pillars/api/playwright.config.ts +105 -0
  25. package/src/pillars/api/schemas/example-api.json +110 -0
  26. package/src/pillars/api/tests/health/health.spec.ts +19 -0
  27. package/src/pillars/api/tests/health/manifest.yml +6 -0
  28. package/src/pillars/browser/fixtures/index.ts +232 -0
  29. package/src/pillars/browser/fixtures/package.json +10 -0
  30. package/src/pillars/browser/global-setup.ts +140 -0
  31. package/src/pillars/browser/global-teardown.ts +48 -0
  32. package/src/pillars/browser/playwright.config.ts +212 -0
  33. package/src/pillars/browser/setup.ts +219 -0
  34. package/src/pillars/journeys/app-config.ts +237 -0
  35. package/src/pillars/journeys/discover.ts +160 -0
  36. package/src/pillars/journeys/hooks/deny-shell.cjs +44 -0
  37. package/src/pillars/journeys/observability/events.ts +288 -0
  38. package/src/pillars/journeys/observability/sdk-mapper.ts +306 -0
  39. package/src/pillars/journeys/report/friction.ts +304 -0
  40. package/src/pillars/journeys/report/junit.ts +105 -0
  41. package/src/pillars/journeys/report/render.ts +273 -0
  42. package/src/pillars/journeys/report/results.ts +323 -0
  43. package/src/pillars/journeys/run.ts +673 -0
  44. package/src/pillars/journeys/runner.ts +1025 -0
  45. package/src/pillars/journeys/sandbox.ts +403 -0
  46. package/src/pillars/journeys/spec/loader.ts +152 -0
  47. package/src/pillars/journeys/spec/schema.ts +208 -0
  48. package/src/pillars/journeys/teardown.ts +98 -0
  49. package/src/pillars/journeys/tools/browser-session.ts +219 -0
  50. package/src/pillars/journeys/tools/browser-tool.ts +914 -0
  51. package/src/pillars/journeys/tools/http-tool.ts +564 -0
  52. package/src/pillars/journeys/tools/ledger.ts +278 -0
  53. package/src/pillars/journeys/tools/snapshot-store.ts +117 -0
  54. package/src/pillars/journeys/verify/assertions.ts +502 -0
  55. package/src/pillars/journeys/verify/verdict.ts +71 -0
  56. package/src/pillars/perf/build-report.ts +36 -0
  57. package/src/pillars/perf/k6.config.js +112 -0
  58. package/src/pillars/perf/run.ts +345 -0
  59. package/src/pillars/perf/scenarios/load-profile.js +97 -0
  60. package/src/pillars/perf/staging.ts +73 -0
  61. package/src/pillars/security/discover.ts +124 -0
  62. package/src/pillars/security/findings.ts +151 -0
  63. package/src/pillars/security/plan.ts +355 -0
  64. package/src/pillars/security/run.ts +757 -0
  65. package/src/pillars/smoke/admin-probe.ts +68 -0
  66. package/src/pillars/smoke/base-url.ts +56 -0
  67. package/src/pillars/smoke/cleanup.ts +8 -0
  68. package/src/pillars/smoke/discover.ts +237 -0
  69. package/src/pillars/smoke/ensure-smoke-teardown.ts +40 -0
  70. package/src/pillars/smoke/fixture-manager.ts +217 -0
  71. package/src/pillars/smoke/fixtures.ts +468 -0
  72. package/src/pillars/smoke/global-setup-all.ts +262 -0
  73. package/src/pillars/smoke/global-setup-global.ts +262 -0
  74. package/src/pillars/smoke/global-setup.ts +273 -0
  75. package/src/pillars/smoke/global-teardown-all.ts +8 -0
  76. package/src/pillars/smoke/global-teardown-global.ts +8 -0
  77. package/src/pillars/smoke/global-teardown.ts +139 -0
  78. package/src/pillars/smoke/playwright-all.config.ts +147 -0
  79. package/src/pillars/smoke/playwright-global.config.ts +141 -0
  80. package/src/pillars/smoke/playwright.config.ts +162 -0
  81. package/src/pillars/smoke/setup-env.ts +132 -0
  82. package/src/pillars/smoke/setup-global.ts +126 -0
  83. package/src/pillars/smoke/setup.ts +115 -0
  84. package/src/pillars/smoke/smoke-repo.ts +16 -0
  85. package/src/pillars/smoke/staging.ts +49 -0
  86. package/src/pillars/suites/run.ts +147 -0
  87. package/src/scripts/configure-aws-profiles.ts +114 -0
  88. package/src/scripts/register-repos.ts +307 -0
  89. package/src/scripts/setup-default-workspace.ts +203 -0
  90. package/src/shared/agent-actor.ts +77 -0
  91. package/src/shared/applications.ts +138 -0
  92. package/src/shared/auth/api-token.ts +416 -0
  93. package/src/shared/auth/auth0-helper.ts +284 -0
  94. package/src/shared/auth/headless-login.ts +265 -0
  95. package/src/shared/auth/jwt-helper.ts +186 -0
  96. package/src/shared/auth/sudo-vault.ts +69 -0
  97. package/src/shared/auth/test-accounts.ts +98 -0
  98. package/src/shared/auth/token-injection.ts +184 -0
  99. package/src/shared/auth/token-providers/auth0.ts +80 -0
  100. package/src/shared/auth/token-providers/index.ts +182 -0
  101. package/src/shared/auth/token-refresh.ts +273 -0
  102. package/src/shared/banner.ts +15 -0
  103. package/src/shared/brand.ts +79 -0
  104. package/src/shared/cli-tabs.ts +131 -0
  105. package/src/shared/constants.ts +128 -0
  106. package/src/shared/den-config.ts +688 -0
  107. package/src/shared/dev-metrics/emit.ts +354 -0
  108. package/src/shared/edge/config.ts +229 -0
  109. package/src/shared/edge/render.ts +221 -0
  110. package/src/shared/edge/runtime.ts +268 -0
  111. package/src/shared/edge/status.ts +74 -0
  112. package/src/shared/edge/types.ts +62 -0
  113. package/src/shared/engine.ts +67 -0
  114. package/src/shared/environment/aws-profiles.ts +49 -0
  115. package/src/shared/environment/env-selector.ts +161 -0
  116. package/src/shared/environment/playwright-env.ts +82 -0
  117. package/src/shared/environment-manager/catalog.ts +77 -0
  118. package/src/shared/environment-manager/clone-guard.ts +163 -0
  119. package/src/shared/environment-manager/dag.ts +286 -0
  120. package/src/shared/environment-manager/default-workspace.ts +182 -0
  121. package/src/shared/environment-manager/derivation/alias.ts +90 -0
  122. package/src/shared/environment-manager/derivation/cache.ts +97 -0
  123. package/src/shared/environment-manager/derivation/derive-graph.ts +421 -0
  124. package/src/shared/environment-manager/derivation/derived-up.ts +354 -0
  125. package/src/shared/environment-manager/derivation/load-alias.ts +11 -0
  126. package/src/shared/environment-manager/derivation/rules-compose.ts +42 -0
  127. package/src/shared/environment-manager/derivation/rules-events.ts +50 -0
  128. package/src/shared/environment-manager/derivation/rules-http.ts +247 -0
  129. package/src/shared/environment-manager/derivation/rules-infra.ts +64 -0
  130. package/src/shared/environment-manager/derivation/rules-readiness.ts +96 -0
  131. package/src/shared/environment-manager/derivation/scan.ts +160 -0
  132. package/src/shared/environment-manager/derivation/text.ts +40 -0
  133. package/src/shared/environment-manager/derivation/types.ts +69 -0
  134. package/src/shared/environment-manager/health.ts +635 -0
  135. package/src/shared/environment-manager/index.ts +6 -0
  136. package/src/shared/environment-manager/load-applications.ts +108 -0
  137. package/src/shared/environment-manager/log-tail.ts +213 -0
  138. package/src/shared/environment-manager/make-targets.ts +78 -0
  139. package/src/shared/environment-manager/opctl.ts +335 -0
  140. package/src/shared/environment-manager/readiness.ts +93 -0
  141. package/src/shared/environment-manager/register-local.ts +72 -0
  142. package/src/shared/environment-manager/secrets-check.ts +260 -0
  143. package/src/shared/environment-manager/service-container.ts +47 -0
  144. package/src/shared/environment-manager/service-env.ts +110 -0
  145. package/src/shared/environment-manager/service-runner.ts +1603 -0
  146. package/src/shared/environment-manager/sync-targets.ts +66 -0
  147. package/src/shared/environment-manager/up-plan.ts +326 -0
  148. package/src/shared/environment-manager/workspace-manager.ts +905 -0
  149. package/src/shared/environment-manager/workspaces.ts +860 -0
  150. package/src/shared/manifest/deps.ts +27 -0
  151. package/src/shared/manifest/merge-override.ts +75 -0
  152. package/src/shared/manifest/migrate-manifest.ts +101 -0
  153. package/src/shared/manifest/placement.ts +321 -0
  154. package/src/shared/manifest/read-manifest.ts +278 -0
  155. package/src/shared/manifest/schema.ts +313 -0
  156. package/src/shared/manifest/service-links.ts +93 -0
  157. package/src/shared/manifest/validate-layers.ts +229 -0
  158. package/src/shared/manifest/validate-manifest.ts +200 -0
  159. package/src/shared/manifest/workspace-manifests.ts +92 -0
  160. package/src/shared/open-url.ts +122 -0
  161. package/src/shared/org.ts +44 -0
  162. package/src/shared/paths.ts +190 -0
  163. package/src/shared/perf/optional-fields.ts +105 -0
  164. package/src/shared/perf/report-normalizer.ts +404 -0
  165. package/src/shared/perf/report-paths.ts +19 -0
  166. package/src/shared/perf/report-store.ts +110 -0
  167. package/src/shared/perf/report-types.ts +70 -0
  168. package/src/shared/perf/test-paths.ts +25 -0
  169. package/src/shared/process/log-rotation.ts +147 -0
  170. package/src/shared/process/spawn-streaming.ts +229 -0
  171. package/src/shared/product.ts +92 -0
  172. package/src/shared/profile.ts +141 -0
  173. package/src/shared/redact.ts +107 -0
  174. package/src/shared/repo-kind.ts +32 -0
  175. package/src/shared/reporting/jsonl-reporter.ts +131 -0
  176. package/src/shared/reporting/slack-reporter.ts +340 -0
  177. package/src/shared/results/result-reader.ts +266 -0
  178. package/src/shared/results/result-writer.ts +168 -0
  179. package/src/shared/secrets/doppler.ts +126 -0
  180. package/src/shared/secrets/env.ts +48 -0
  181. package/src/shared/secrets/index.ts +197 -0
  182. package/src/shared/secrets/keys.ts +33 -0
  183. package/src/shared/secrets/onepassword.ts +75 -0
  184. package/src/shared/secrets/types.ts +52 -0
  185. package/src/shared/service-url.ts +119 -0
  186. package/src/shared/smoke/branch-resolver.ts +141 -0
  187. package/src/shared/smoke/git-remote.ts +70 -0
  188. package/src/shared/smoke/repo-cloner.ts +151 -0
  189. package/src/shared/stacks/config.ts +137 -0
  190. package/src/shared/stacks/manifest-commands.ts +118 -0
  191. package/src/shared/stacks/render.ts +253 -0
  192. package/src/shared/stacks/runtime.ts +334 -0
  193. package/src/shared/stacks/status.ts +99 -0
  194. package/src/shared/stacks/types.ts +53 -0
  195. package/src/shared/terminal/tabs.ts +212 -0
  196. package/src/shared/test-discovery/module-scanner.ts +569 -0
  197. package/src/shared/types/index.ts +701 -0
  198. package/src/shared/ui-marker.ts +19 -0
  199. package/src/ui/cli/applications-command.ts +148 -0
  200. package/src/ui/cli/config-command.ts +68 -0
  201. package/src/ui/cli/create-main.ts +9 -0
  202. package/src/ui/cli/create.ts +360 -0
  203. package/src/ui/cli/edge-command.ts +81 -0
  204. package/src/ui/cli/flag-parser.ts +246 -0
  205. package/src/ui/cli/graph-command.ts +570 -0
  206. package/src/ui/cli/health-display.ts +63 -0
  207. package/src/ui/cli/index.ts +3742 -0
  208. package/src/ui/cli/init.ts +855 -0
  209. package/src/ui/cli/invocation-target.ts +175 -0
  210. package/src/ui/cli/logs-command.ts +104 -0
  211. package/src/ui/cli/mcp-main.ts +13 -0
  212. package/src/ui/cli/mcp-stdout-guard.ts +10 -0
  213. package/src/ui/cli/module-selection.ts +31 -0
  214. package/src/ui/cli/prompts.ts +767 -0
  215. package/src/ui/cli/results-viewer.ts +436 -0
  216. package/src/ui/cli/secrets-preflight.ts +91 -0
  217. package/src/ui/cli/stack-command.ts +117 -0
  218. package/src/ui/cli/stalled-setup-recovery.ts +129 -0
  219. package/src/ui/cli/test-runner.ts +1319 -0
  220. package/src/ui/cli/web-command.ts +1073 -0
  221. package/src/ui/mcp/api.ts +284 -0
  222. package/src/ui/mcp/guards.ts +141 -0
  223. package/src/ui/mcp/server.ts +147 -0
  224. package/src/ui/mcp/tools.ts +565 -0
  225. package/src/ui/server/audit.ts +71 -0
  226. package/src/ui/server/changelog-parser.ts +105 -0
  227. package/src/ui/server/daemon-reaper.ts +129 -0
  228. package/src/ui/server/editor-manager.ts +301 -0
  229. package/src/ui/server/github-service.ts +223 -0
  230. package/src/ui/server/graph-prefs.ts +89 -0
  231. package/src/ui/server/health-row.ts +15 -0
  232. package/src/ui/server/http-helpers.ts +98 -0
  233. package/src/ui/server/index.ts +3350 -0
  234. package/src/ui/server/log-history.ts +453 -0
  235. package/src/ui/server/perf-report.ts +42 -0
  236. package/src/ui/server/public/environments.html +496 -0
  237. package/src/ui/server/public/favicon.ico +0 -0
  238. package/src/ui/server/public/graph-model.js +416 -0
  239. package/src/ui/server/public/graph.css +976 -0
  240. package/src/ui/server/public/graph.js +986 -0
  241. package/src/ui/server/public/header.js +1373 -0
  242. package/src/ui/server/public/index.html +2595 -0
  243. package/src/ui/server/public/local-dev.html +5344 -0
  244. package/src/ui/server/public/logs.html +1496 -0
  245. package/src/ui/server/public/panel.js +787 -0
  246. package/src/ui/server/public/perf-report.js +359 -0
  247. package/src/ui/server/public/release.html +2111 -0
  248. package/src/ui/server/public/services.html +1103 -0
  249. package/src/ui/server/public/workspace-picker.js +69 -0
  250. package/src/ui/server/readonly-routes.ts +200 -0
  251. package/src/ui/server/release-deploy-session.ts +246 -0
  252. package/src/ui/server/release-service.ts +1125 -0
  253. package/src/ui/server/sudo-store.ts +111 -0
  254. package/src/ui/server/ui-assets.ts +73 -0
  255. package/src/ui/server/workspace-routes.ts +2185 -0
@@ -0,0 +1,496 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__DEN_TITLE__ · Environments</title>
7
+ <style>
8
+ *,
9
+ *::before,
10
+ *::after {
11
+ box-sizing: border-box;
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+ :root {
16
+ --bg: #0d1117;
17
+ --surface: #161b22;
18
+ --border: #30363d;
19
+ --text: #c9d1d9;
20
+ --muted: #8b949e;
21
+ --accent: #58a6ff;
22
+ --green: #3fb950;
23
+ --red: #f85149;
24
+ --yellow: #d29922;
25
+ --mono: "SF Mono", "Cascadia Code", "Fira Code", "Menlo", monospace;
26
+ }
27
+ html,
28
+ body {
29
+ min-height: 100%;
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
33
+ font-size: 13px;
34
+ }
35
+ header {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 10px;
39
+ padding: 7px 14px;
40
+ background: var(--surface);
41
+ border-bottom: 1px solid var(--border);
42
+ flex-wrap: wrap;
43
+ }
44
+ header h1 {
45
+ font-family: var(--mono);
46
+ font-size: 14px;
47
+ font-weight: 600;
48
+ letter-spacing: -0.02em;
49
+ }
50
+ .h-spacer {
51
+ flex: 1;
52
+ }
53
+ nav {
54
+ display: flex;
55
+ gap: 2px;
56
+ flex-wrap: wrap;
57
+ }
58
+ nav a {
59
+ color: var(--muted);
60
+ text-decoration: none;
61
+ padding: 3px 10px;
62
+ border-radius: 4px;
63
+ font-size: 12px;
64
+ font-weight: 500;
65
+ }
66
+ nav a:hover {
67
+ color: var(--text);
68
+ background: var(--border);
69
+ }
70
+ nav a.active {
71
+ color: var(--text);
72
+ background: var(--bg);
73
+ }
74
+ .controls {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 10px;
78
+ padding: 6px 14px;
79
+ background: var(--surface);
80
+ border-bottom: 1px solid var(--border);
81
+ flex-wrap: wrap;
82
+ }
83
+ .controls .note {
84
+ font-size: 11px;
85
+ color: var(--muted);
86
+ }
87
+ button {
88
+ background: var(--bg);
89
+ border: 1px solid var(--border);
90
+ color: var(--text);
91
+ padding: 3px 10px;
92
+ border-radius: 6px;
93
+ font-size: 12px;
94
+ cursor: pointer;
95
+ }
96
+ button:hover {
97
+ border-color: var(--accent);
98
+ }
99
+ main {
100
+ padding: 12px 14px;
101
+ overflow-x: auto;
102
+ }
103
+ table {
104
+ border-collapse: collapse;
105
+ width: 100%;
106
+ min-width: 560px;
107
+ }
108
+ th,
109
+ td {
110
+ text-align: left;
111
+ padding: 6px 10px;
112
+ border-bottom: 1px solid var(--border);
113
+ vertical-align: middle;
114
+ }
115
+ th {
116
+ font-size: 11px;
117
+ font-weight: 600;
118
+ color: var(--muted);
119
+ text-transform: uppercase;
120
+ letter-spacing: 0.04em;
121
+ }
122
+ tr.repo td {
123
+ background: var(--surface);
124
+ font-weight: 600;
125
+ font-family: var(--mono);
126
+ font-size: 12px;
127
+ }
128
+ td.svc {
129
+ font-family: var(--mono);
130
+ font-size: 12px;
131
+ padding-left: 22px;
132
+ }
133
+ .cell {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 6px;
137
+ font-family: var(--mono);
138
+ font-size: 12px;
139
+ }
140
+ .cell a {
141
+ color: var(--text);
142
+ text-decoration: none;
143
+ }
144
+ .cell a:hover {
145
+ color: var(--accent);
146
+ text-decoration: underline;
147
+ }
148
+ .cell .ms {
149
+ color: var(--muted);
150
+ font-size: 11px;
151
+ }
152
+ .none {
153
+ color: var(--muted);
154
+ font-size: 12px;
155
+ }
156
+ .dot {
157
+ width: 8px;
158
+ height: 8px;
159
+ border-radius: 50%;
160
+ background: var(--muted);
161
+ flex-shrink: 0;
162
+ display: inline-block;
163
+ }
164
+ .dot.ok {
165
+ background: var(--green);
166
+ }
167
+ .dot.err {
168
+ background: var(--red);
169
+ }
170
+ .dot.slow {
171
+ background: var(--yellow);
172
+ }
173
+ .empty {
174
+ color: var(--muted);
175
+ padding: 24px 0;
176
+ }
177
+ tr.org td {
178
+ background: var(--bg);
179
+ font-size: 11px;
180
+ font-weight: 600;
181
+ text-transform: uppercase;
182
+ letter-spacing: 0.05em;
183
+ color: var(--muted);
184
+ padding-top: 14px;
185
+ border-bottom: 2px solid var(--org, var(--border));
186
+ }
187
+ tr.org td .swatch {
188
+ display: inline-block;
189
+ width: 8px;
190
+ height: 8px;
191
+ border-radius: 2px;
192
+ background: var(--org, var(--muted));
193
+ margin-right: 6px;
194
+ }
195
+ tr.repo td.rel {
196
+ font-weight: 400;
197
+ font-size: 11px;
198
+ color: var(--muted);
199
+ }
200
+ .rel .ok {
201
+ color: var(--green);
202
+ }
203
+ .rel .warn {
204
+ color: var(--yellow);
205
+ }
206
+ .rel .bad {
207
+ color: var(--red);
208
+ }
209
+ a.open {
210
+ color: var(--muted);
211
+ text-decoration: none;
212
+ font-size: 12px;
213
+ padding: 0 3px;
214
+ border-radius: 3px;
215
+ }
216
+ a.open:hover,
217
+ a.open:focus-visible {
218
+ color: var(--accent);
219
+ background: var(--border);
220
+ }
221
+ select {
222
+ background: var(--bg);
223
+ border: 1px solid var(--border);
224
+ color: var(--text);
225
+ padding: 2px 6px;
226
+ border-radius: 6px;
227
+ font-size: 12px;
228
+ }
229
+ </style>
230
+ </head>
231
+ <body>
232
+ <header>
233
+ <img
234
+ src="/favicon.ico"
235
+ width="16"
236
+ height="16"
237
+ alt=""
238
+ style="flex-shrink: 0"
239
+ />
240
+ <h1>__DEN_TITLE__</h1>
241
+ <div class="h-spacer"></div>
242
+ <nav>
243
+ <a href="/">Local Dev</a>
244
+ <a href="/services">Services</a>
245
+ <a href="/environments" class="active">Environments</a>
246
+ <a href="/release">Releases</a>
247
+ <a href="/logs">Logs</a>
248
+ <a href="/tests">Tests</a>
249
+ </nav>
250
+ </header>
251
+
252
+ <div class="controls">
253
+ <button id="refresh">Re-check</button>
254
+ <label class="note"
255
+ >org
256
+ <select id="org-filter">
257
+ <option value="">all</option>
258
+ </select></label
259
+ >
260
+ <span class="note" id="status">checking…</span>
261
+ <span class="note"
262
+ >Each repo's <code>__DEN_MANIFEST_YML__</code> health checks, run
263
+ against every environment. ↗ opens the service in a new tab. beta and
264
+ prod are check-only.</span
265
+ >
266
+ </div>
267
+
268
+ <main>
269
+ <table>
270
+ <thead id="head"></thead>
271
+ <tbody id="body">
272
+ <tr>
273
+ <td class="empty">Loading…</td>
274
+ </tr>
275
+ </tbody>
276
+ </table>
277
+ </main>
278
+
279
+ <script type="module">
280
+ const SLOW_MS = 1500;
281
+ const POLL_MS = 60_000;
282
+ const esc = (s) =>
283
+ String(s ?? "").replace(
284
+ /[&<>"']/g,
285
+ (c) =>
286
+ ({
287
+ "&": "&amp;",
288
+ "<": "&lt;",
289
+ ">": "&gt;",
290
+ '"': "&quot;",
291
+ "'": "&#39;",
292
+ })[c],
293
+ );
294
+
295
+ const openLink = (url, what) =>
296
+ url && /^https?:/.test(url)
297
+ ? ` <a class="open" href="${esc(url)}" target="_blank" rel="noopener" title="Open ${esc(what)} in a new tab: ${esc(url)}" aria-label="Open ${esc(what)} in a new tab">↗</a>`
298
+ : "";
299
+
300
+ function relCell(row) {
301
+ if (!row) return "";
302
+ const k = row.status?.kind;
303
+ const cls =
304
+ k === "up-to-date"
305
+ ? "ok"
306
+ : k === "behind" || k === "conflict" || k === "error"
307
+ ? "bad"
308
+ : "warn";
309
+ const sha = row.deployedShort
310
+ ? `<code>${esc(row.deployedShort)}</code> `
311
+ : "";
312
+ const link = row.runUrl
313
+ ? ` <a class="open" href="${esc(row.runUrl)}" target="_blank" rel="noopener" title="The deploy run on GitHub">run</a>`
314
+ : "";
315
+ return `<span class="${cls}" title="${esc((row.source ? row.source + ": " : "") + (row.status?.text ?? ""))}">${sha}${esc(row.status?.text ?? "")}</span>${link}`;
316
+ }
317
+
318
+ function cell(r) {
319
+ // What was checked: the URL, or a shell check's command.
320
+ const checked = r && (r.url ?? r.check);
321
+ if (!r || (!checked && r.ok == null))
322
+ return r?.openUrl
323
+ ? `<span class="cell"><span class="none">—</span>${openLink(r.openUrl, r.service)}</span>`
324
+ : '<span class="none">—</span>';
325
+ const cls =
326
+ r.ok === true
327
+ ? r.ms > SLOW_MS
328
+ ? "slow"
329
+ : "ok"
330
+ : r.ok === false
331
+ ? "err"
332
+ : "";
333
+ const what = r.status
334
+ ? String(r.status)
335
+ : r.ok
336
+ ? "up"
337
+ : r.error || "down";
338
+ const title = [r.label, checked, r.error].filter(Boolean).join(" — ");
339
+ const link =
340
+ r.url && /^https?:/.test(r.url)
341
+ ? `<a href="${esc(r.url)}" target="_blank" rel="noopener" title="${esc(title)}">${esc(what)}</a>`
342
+ : `<span title="${esc(title)}">${esc(what)}</span>`;
343
+ const ms = r.ms != null ? `<span class="ms">${r.ms} ms</span>` : "";
344
+ return `<span class="cell"><span class="dot ${cls}"></span>${link}${ms}${openLink(r.openUrl, `${r.repo}/${r.service}`)}</span>`;
345
+ }
346
+
347
+ async function load(refresh = false) {
348
+ const status = document.getElementById("status");
349
+ status.textContent = "checking…";
350
+ const envs = await fetch("/api/environments")
351
+ .then((r) => r.json())
352
+ .catch(() => ["local"]);
353
+ const [results, orgs, relCfg] = await Promise.all([
354
+ Promise.all(
355
+ envs.map((env) =>
356
+ fetch(
357
+ `/api/health?env=${encodeURIComponent(env)}${refresh ? "&refresh=1" : ""}`,
358
+ )
359
+ .then((r) => r.json())
360
+ .catch(() => []),
361
+ ),
362
+ ),
363
+ fetch("/api/orgs")
364
+ .then((r) => r.json())
365
+ .catch(() => []),
366
+ fetch("/api/release/config")
367
+ .then((r) => r.json())
368
+ .catch(() => ({ environments: [] })),
369
+ ]);
370
+ // Deployed commit per repo for the release environments (beta, prod).
371
+ const relEnvs = envs.filter((e) =>
372
+ (relCfg.environments || []).includes(e),
373
+ );
374
+ const release = {};
375
+ await Promise.all(
376
+ relEnvs.map(async (e) => {
377
+ const r = await fetch(
378
+ `/api/release/status?env=${encodeURIComponent(e)}${refresh ? "&refresh=1" : ""}`,
379
+ )
380
+ .then((x) => (x.ok ? x.json() : null))
381
+ .catch(() => null);
382
+ for (const row of r?.rows || [])
383
+ (release[row.repo] ??= {})[e] = row;
384
+ }),
385
+ );
386
+ const orgInfo = Object.fromEntries(
387
+ (Array.isArray(orgs) ? orgs : []).map((o) => [o.id, o]),
388
+ );
389
+ const filterSel = document.getElementById("org-filter");
390
+ const saved = (() => {
391
+ try {
392
+ return localStorage.getItem("__DEN_CLI__-env-org") || "";
393
+ } catch {
394
+ return "";
395
+ }
396
+ })();
397
+ filterSel.innerHTML =
398
+ '<option value="">all</option>' +
399
+ Object.values(orgInfo)
400
+ .map((o) => `<option value="${esc(o.id)}">${esc(o.label)}</option>`)
401
+ .join("");
402
+ filterSel.value = orgInfo[filterSel.dataset.value ?? saved]
403
+ ? (filterSel.dataset.value ?? saved)
404
+ : "";
405
+ const orgFilter = filterSel.value;
406
+
407
+ // One row per service, in the dependency-graph order the server ranks
408
+ // (idx); grouped under its repo.
409
+ const rows = new Map();
410
+ results.forEach((list, i) => {
411
+ for (const r of Array.isArray(list) ? list : []) {
412
+ const key = `${r.repo}/${r.service}`;
413
+ const row = rows.get(key) ?? {
414
+ repo: r.repo,
415
+ service: r.service,
416
+ label: r.label,
417
+ org: r.org || "",
418
+ isEntrypoint: r.isEntrypoint,
419
+ idx: r.idx ?? 1e9,
420
+ byEnv: {},
421
+ };
422
+ row.idx = Math.min(row.idx, r.idx ?? 1e9);
423
+ row.byEnv[envs[i]] = r;
424
+ rows.set(key, row);
425
+ }
426
+ });
427
+ // Org first (configured order), then the graph order within it.
428
+ const orgRank = (o) => {
429
+ const i = Object.keys(orgInfo).indexOf(o);
430
+ return i < 0 ? 1e6 : i;
431
+ };
432
+ const sorted = [...rows.values()]
433
+ .filter((r) => !orgFilter || r.org === orgFilter)
434
+ .sort(
435
+ (a, b) =>
436
+ orgRank(a.org) - orgRank(b.org) ||
437
+ a.idx - b.idx ||
438
+ a.service.localeCompare(b.service),
439
+ );
440
+
441
+ document.getElementById("head").innerHTML =
442
+ `<tr><th>Service</th>${envs.map((e) => `<th>${esc(e)}</th>`).join("")}</tr>`;
443
+ const body = document.getElementById("body");
444
+ if (!sorted.length) {
445
+ body.innerHTML = `<tr><td class="empty" colspan="${envs.length + 1}">No services in the active workspace have a __DEN_MANIFEST_YML__ yet.</td></tr>`;
446
+ } else {
447
+ let html = "",
448
+ lastRepo = null,
449
+ lastOrg = null;
450
+ for (const row of sorted) {
451
+ if (row.org !== lastOrg) {
452
+ const o = orgInfo[row.org];
453
+ html += `<tr class="org" style="${o?.color ? `--org:${esc(o.color)}` : ""}"><td colspan="${envs.length + 1}"><span class="swatch"></span>${esc(o?.label || row.org || "no org")}</td></tr>`;
454
+ lastOrg = row.org;
455
+ }
456
+ if (row.repo !== lastRepo) {
457
+ const entry =
458
+ sorted.find((r) => r.repo === row.repo && r.isEntrypoint) ||
459
+ row;
460
+ html += `<tr class="repo"><td>${esc(row.repo)}</td>${envs
461
+ .map(
462
+ (e) =>
463
+ `<td class="rel">${relCell(release[row.repo]?.[e])}${openLink(entry.byEnv[e]?.openUrl, `${row.repo} on ${e}`)}</td>`,
464
+ )
465
+ .join("")}</tr>`;
466
+ lastRepo = row.repo;
467
+ }
468
+ html += `<tr><td class="svc">${esc(row.service)}</td>${envs.map((e) => `<td>${cell(row.byEnv[e])}</td>`).join("")}</tr>`;
469
+ }
470
+ body.innerHTML = html;
471
+ }
472
+ const down = sorted
473
+ .flatMap((r) => Object.values(r.byEnv))
474
+ .filter((r) => r.ok === false).length;
475
+ status.textContent = `checked ${new Date().toLocaleTimeString()} · ${down ? down + " failing" : "nothing failing"}`;
476
+ }
477
+
478
+ document
479
+ .getElementById("refresh")
480
+ .addEventListener("click", () => load(true));
481
+ document.getElementById("org-filter").addEventListener("change", (e) => {
482
+ e.target.dataset.value = e.target.value;
483
+ try {
484
+ localStorage.setItem("__DEN_CLI__-env-org", e.target.value);
485
+ } catch {
486
+ /* private window */
487
+ }
488
+ load();
489
+ });
490
+ load();
491
+ setInterval(() => {
492
+ if (!document.hidden) load();
493
+ }, POLL_MS);
494
+ </script>
495
+ </body>
496
+ </html>
Binary file