jsgui3-server 0.0.148 → 0.0.150

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 (154) hide show
  1. package/.github/agents/Mobile Developer.agent.md +89 -0
  2. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  3. package/AGENTS.md +4 -0
  4. package/README.md +215 -3
  5. package/admin-ui/client.js +81 -51
  6. package/admin-ui/v1/admin_auth_service.js +197 -0
  7. package/admin-ui/v1/admin_user_store.js +71 -0
  8. package/admin-ui/v1/client.js +17 -0
  9. package/admin-ui/v1/controls/admin_shell.js +1399 -0
  10. package/admin-ui/v1/controls/group_box.js +84 -0
  11. package/admin-ui/v1/controls/stat_card.js +125 -0
  12. package/admin-ui/v1/server.js +658 -0
  13. package/admin-ui/v1/utils/formatters.js +68 -0
  14. package/dev-status.svg +139 -0
  15. package/docs/admin-extension-guide.md +345 -0
  16. package/docs/api-reference.md +301 -43
  17. package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
  18. package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
  19. package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
  20. package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
  21. package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
  22. package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
  23. package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
  24. package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
  25. package/docs/books/adaptive-control-improvements/README.md +66 -0
  26. package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
  27. package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
  28. package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
  29. package/docs/books/admin-ui-authentication/README.md +25 -0
  30. package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
  31. package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
  32. package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
  33. package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
  34. package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
  35. package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
  36. package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
  37. package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
  38. package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
  39. package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
  40. package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
  41. package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
  42. package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
  43. package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
  44. package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
  45. package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
  46. package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
  47. package/docs/books/creating-a-new-admin-ui/README.md +68 -0
  48. package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
  49. package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
  50. package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
  51. package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
  52. package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
  53. package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
  54. package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
  55. package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
  56. package/docs/books/device-adaptive-composition/README.md +47 -0
  57. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  58. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  59. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  60. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  61. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  62. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  63. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  64. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  65. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  66. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  67. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  68. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  69. package/docs/bundling-system-deep-dive.md +9 -4
  70. package/docs/comparison-report-express-plex-cpanel.md +549 -0
  71. package/docs/comprehensive-documentation.md +49 -18
  72. package/docs/configuration-reference.md +152 -27
  73. package/docs/core/README.md +19 -0
  74. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  75. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  76. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  77. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  78. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  79. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  80. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  81. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  82. package/docs/designs/server-admin-interface-aero.svg +611 -0
  83. package/docs/publishers-guide.md +59 -4
  84. package/docs/resources-guide.md +184 -35
  85. package/docs/simple-server-api-design.md +72 -17
  86. package/docs/system-architecture.md +18 -14
  87. package/docs/troubleshooting.md +84 -53
  88. package/examples/controls/15) window, observable SSE/server.js +6 -1
  89. package/examples/controls/19) window, auto observable ui/server.js +9 -0
  90. package/examples/controls/20) window, task manager app/README.md +133 -0
  91. package/examples/controls/20) window, task manager app/client.js +797 -0
  92. package/examples/controls/20) window, task manager app/server.js +178 -0
  93. package/examples/controls/6) window, color_palette/client.js +165 -68
  94. package/examples/controls/9) window, date picker/client.js +362 -76
  95. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  96. package/examples/jsgui3-html/06) theming/client.js +22 -1
  97. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  98. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  99. package/lab/experiments/capture-color-controls.js +196 -0
  100. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  101. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  102. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  103. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  104. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  105. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  106. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  107. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  108. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  109. package/lab/screenshot-utils.js +248 -0
  110. package/module.js +12 -0
  111. package/package.json +12 -2
  112. package/publishers/Publishers.js +4 -3
  113. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  114. package/publishers/http-sse-publisher.js +341 -0
  115. package/resources/process-resource.js +950 -0
  116. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
  117. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  118. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  119. package/resources/remote-process-resource.js +355 -0
  120. package/resources/server-resource-pool.js +354 -41
  121. package/serve-factory.js +442 -259
  122. package/server.js +288 -13
  123. package/tests/README.md +71 -4
  124. package/tests/admin-ui-jsgui-controls.test.js +581 -0
  125. package/tests/admin-ui-render.test.js +24 -0
  126. package/tests/assigners.test.js +56 -40
  127. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  128. package/tests/configuration-validation.test.js +21 -18
  129. package/tests/content-analysis.test.js +7 -6
  130. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  131. package/tests/control-scan-manifest-regression.test.js +144 -0
  132. package/tests/end-to-end.test.js +15 -14
  133. package/tests/error-handling.test.js +222 -179
  134. package/tests/fixtures/bundling-default-button-client.js +37 -0
  135. package/tests/fixtures/bundling-default-window-client.js +34 -0
  136. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  137. package/tests/fixtures/resource-monitor-client.js +319 -0
  138. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  139. package/tests/http-sse-publisher.test.js +136 -0
  140. package/tests/performance.test.js +69 -65
  141. package/tests/process-resource.test.js +138 -0
  142. package/tests/publishers.test.js +7 -7
  143. package/tests/remote-process-resource.test.js +160 -0
  144. package/tests/sass-controls.e2e.test.js +7 -1
  145. package/tests/serve-resources.test.js +270 -0
  146. package/tests/serve.test.js +120 -50
  147. package/tests/server-resource-pool.test.js +106 -0
  148. package/tests/small-controls-bundle-size.test.js +252 -0
  149. package/tests/test-runner.js +14 -1
  150. package/tests/window-examples.puppeteer.test.js +204 -1
  151. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  152. package/tests/temp_invalid.js +0 -7
  153. package/tests/temp_invalid_utf8.js +0 -1
  154. package/tests/temp_malformed.js +0 -10
@@ -0,0 +1,592 @@
1
+ # Chapter 4: The Admin Module Adapter Layer
2
+
3
+ ## Purpose
4
+
5
+ The Admin Module (`admin-ui/v1/server.js`) is the bridge between raw server internals and the clean JSON API that the admin UI controls consume. It does **not** add new platform features — it reads existing data, formats it, and exposes it through HTTP endpoints and SSE channels.
6
+
7
+ This chapter specifies every adapter function, every API endpoint, and every SSE event the Admin Module must provide.
8
+
9
+ ---
10
+
11
+ ## Design Principles
12
+
13
+ 1. **Read-mostly** — The adapter reads server properties; it does not modify control flow.
14
+ 2. **Snapshot + Stream** — Static/slow data is served via GET endpoints. Dynamic data is pushed via SSE.
15
+ 3. **Defensive** — Every property access is wrapped in try/catch. A missing property never crashes the admin module.
16
+ 4. **Self-contained** — The adapter has no external dependencies beyond what jsgui3-server already imports.
17
+
18
+ ---
19
+
20
+ ## Class Structure
21
+
22
+ ```javascript
23
+ class Admin_Module_V1 {
24
+ constructor(server) {
25
+ this.server = server;
26
+ this._start_time = Date.now();
27
+ this._request_log = []; // Circular buffer, max 1000
28
+ this._request_count = 0;
29
+ this._bundle_info = null; // Captured from ready event
30
+ this._sse_publisher = null; // For /api/admin/events
31
+ this._route_registry = []; // Tracked route registrations
32
+ }
33
+
34
+ attach_to_router(router) { ... }
35
+ instrument_request_handler() { ... }
36
+ capture_bundle_info() { ... }
37
+ track_route_registration() { ... }
38
+
39
+ // Snapshot builders
40
+ get_snapshot() { ... }
41
+ get_process_info() { ... }
42
+ get_resources_tree() { ... }
43
+ get_routes_list() { ... }
44
+ get_publishers_list() { ... }
45
+ get_build_info() { ... }
46
+ get_config() { ... }
47
+ get_request_stats() { ... }
48
+
49
+ // SSE event broadcasters
50
+ broadcast_request(req_data) { ... }
51
+ broadcast_resource_change(event_data) { ... }
52
+ broadcast_build_complete(build_data) { ... }
53
+ }
54
+ ```
55
+
56
+ ---
57
+
58
+ ## API Endpoints
59
+
60
+ ### GET `/api/admin/snapshot`
61
+
62
+ Returns the complete server state in a single response. This is what the admin UI fetches on initial load.
63
+
64
+ **Response shape:**
65
+
66
+ ```javascript
67
+ {
68
+ "server": {
69
+ "name": "jsgui3 server",
70
+ "pid": 7824,
71
+ "uptime_seconds": 15780,
72
+ "node_version": "v20.11.0",
73
+ "platform": "win32",
74
+ "debug": true,
75
+ "port": 3000,
76
+ "started": true,
77
+ "https": false
78
+ },
79
+ "memory": {
80
+ "rss": 134217728,
81
+ "heap_total": 67108864,
82
+ "heap_used": 45088768,
83
+ "external": 2097152,
84
+ "array_buffers": 1048576
85
+ },
86
+ "resources": {
87
+ "total": 5,
88
+ "running": 5,
89
+ "stopped": 0,
90
+ "crashed": 0,
91
+ "items": [
92
+ {
93
+ "name": "Local Server Info",
94
+ "type": "Local_Server_Info",
95
+ "state": "on"
96
+ },
97
+ {
98
+ "name": "Server Router",
99
+ "type": "Router",
100
+ "state": "unknown"
101
+ }
102
+ ]
103
+ },
104
+ "routes": [
105
+ {
106
+ "path": "/",
107
+ "method": "GET",
108
+ "handler_type": "Static_Route_HTTP_Responder",
109
+ "category": "auto"
110
+ },
111
+ {
112
+ "path": "/api/validateUser",
113
+ "method": "POST",
114
+ "handler_type": "HTTP_Function_Publisher",
115
+ "category": "api"
116
+ }
117
+ ],
118
+ "build": {
119
+ "js": {
120
+ "route": "/js/js.js",
121
+ "size_bytes": 250880,
122
+ "size_gzip": 62720,
123
+ "size_brotli": 48128,
124
+ "module_count": null
125
+ },
126
+ "css": {
127
+ "route": "/css/css.css",
128
+ "size_bytes": 12288,
129
+ "size_gzip": 3072,
130
+ "size_brotli": 2560
131
+ },
132
+ "built_at": 1739567000000,
133
+ "errors": 0,
134
+ "warnings": 0
135
+ },
136
+ "requests": {
137
+ "total": 42,
138
+ "per_minute": 12,
139
+ "recent": [
140
+ {
141
+ "timestamp": 1739567000000,
142
+ "method": "GET",
143
+ "url": "/",
144
+ "status": 200,
145
+ "duration_ms": 14
146
+ }
147
+ ]
148
+ },
149
+ "processes": {
150
+ "main": {
151
+ "pid": 7824,
152
+ "state": "running",
153
+ "memory_rss": 134217728,
154
+ "uptime_seconds": 15780
155
+ },
156
+ "children": []
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### GET `/api/admin/resources`
162
+
163
+ Returns just the resource pool data.
164
+
165
+ ```javascript
166
+ {
167
+ "total": 5,
168
+ "running": 5,
169
+ "stopped": 0,
170
+ "crashed": 0,
171
+ "unreachable": 0,
172
+ "by_type": {
173
+ "Local_Server_Info": [{ "name": "Local Server Info", "state": "on" }],
174
+ "Router": [{ "name": "Server Router", "state": "unknown" }]
175
+ },
176
+ "items": [
177
+ {
178
+ "name": "Local Server Info",
179
+ "type": "Local_Server_Info",
180
+ "state": "on",
181
+ "has_status": true,
182
+ "status": { ... }
183
+ }
184
+ ]
185
+ }
186
+ ```
187
+
188
+ ### GET `/api/admin/routes`
189
+
190
+ Returns the route table.
191
+
192
+ ```javascript
193
+ {
194
+ "count": 7,
195
+ "routes": [
196
+ {
197
+ "path": "/",
198
+ "method": "GET",
199
+ "handler_type": "Static_Route_HTTP_Responder",
200
+ "handler_name": null,
201
+ "category": "auto",
202
+ "description": "HTML page"
203
+ }
204
+ ]
205
+ }
206
+ ```
207
+
208
+ ### GET `/api/admin/processes`
209
+
210
+ Returns process information.
211
+
212
+ ```javascript
213
+ {
214
+ "main": {
215
+ "pid": 7824,
216
+ "state": "running",
217
+ "uptime_seconds": 15780,
218
+ "memory": {
219
+ "rss": 134217728,
220
+ "heap_total": 67108864,
221
+ "heap_used": 45088768
222
+ },
223
+ "cpu": {
224
+ "user": 45000000,
225
+ "system": 12000000
226
+ }
227
+ },
228
+ "children": [
229
+ {
230
+ "name": "Bundle Builder",
231
+ "pid": 7830,
232
+ "state": "running",
233
+ "memory": { "rss_bytes": 90177536 },
234
+ "restart_count": 0,
235
+ "type": "Process_Resource"
236
+ }
237
+ ]
238
+ }
239
+ ```
240
+
241
+ ### GET `/api/admin/build`
242
+
243
+ Returns build/bundle information.
244
+
245
+ ```javascript
246
+ {
247
+ "bundles": [
248
+ {
249
+ "type": "js",
250
+ "route": "/js/js.js",
251
+ "size_identity": 250880,
252
+ "size_gzip": 62720,
253
+ "size_brotli": 48128
254
+ },
255
+ {
256
+ "type": "css",
257
+ "route": "/css/css.css",
258
+ "size_identity": 12288,
259
+ "size_gzip": 3072,
260
+ "size_brotli": 2560
261
+ }
262
+ ],
263
+ "built_at": 1739567000000,
264
+ "source_maps": true,
265
+ "errors": 0,
266
+ "warnings": 0
267
+ }
268
+ ```
269
+
270
+ ### GET `/api/admin/config`
271
+
272
+ Returns server configuration.
273
+
274
+ ```javascript
275
+ {
276
+ "server_name": "jsgui3 server",
277
+ "port": 3000,
278
+ "debug": true,
279
+ "client_js_path": "./examples/client.js",
280
+ "https_enabled": false,
281
+ "allowed_addresses": null,
282
+ "node_version": "v20.11.0",
283
+ "jsgui_version": "0.0.143",
284
+ "platform": "win32",
285
+ "hostname": "DESKTOP-ABC123"
286
+ }
287
+ ```
288
+
289
+ ### GET `/api/admin/events` (SSE)
290
+
291
+ Opens an SSE stream for real-time updates.
292
+
293
+ **SSE events emitted:**
294
+
295
+ | Event Name | Trigger | Payload |
296
+ |-----------|---------|---------|
297
+ | `connected` | Client connects | `{ timestamp, client_id }` |
298
+ | `heartbeat` | Every 15s | `{ timestamp }` |
299
+ | `request` | HTTP request handled | `{ method, url, status, duration_ms, timestamp }` |
300
+ | `resource_state_change` | Pool event | `{ resource_name, from, to, timestamp }` |
301
+ | `resource_crashed` | Pool event | `{ resource_name, code, signal, timestamp }` |
302
+ | `resource_recovered` | Pool event | `{ resource_name, timestamp }` |
303
+ | `build_complete` | Bundle rebuilt | `{ bundles, built_at, errors, warnings }` |
304
+ | `server_stopping` | Server shutting down | `{ timestamp }` |
305
+
306
+ ### POST `/api/admin/action`
307
+
308
+ Execute an admin action.
309
+
310
+ **Request body:**
311
+
312
+ ```javascript
313
+ {
314
+ "action": "restart_resource",
315
+ "target": "Bundle Builder"
316
+ }
317
+ ```
318
+
319
+ **Supported actions:**
320
+
321
+ | Action | Target | Description |
322
+ |--------|--------|-------------|
323
+ | `restart_resource` | Resource name | Restart a Process_Resource |
324
+ | `stop_resource` | Resource name | Stop a resource |
325
+ | `start_resource` | Resource name | Start a stopped resource |
326
+ | `toggle_debug` | — | Toggle debug mode |
327
+
328
+ **Response:**
329
+
330
+ ```javascript
331
+ {
332
+ "ok": true,
333
+ "action": "restart_resource",
334
+ "target": "Bundle Builder",
335
+ "result": "restarting",
336
+ "timestamp": 1739567000000
337
+ }
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Adapter Functions — Implementation Details
343
+
344
+ ### `instrument_request_handler()`
345
+
346
+ This is the most important adapter. It wraps the request processing to capture telemetry without modifying the core request flow.
347
+
348
+ **Strategy**: The Admin Module will override or wrap the `process_request` closure inside `server.start()`. Since `process_request` is a local function, we instrument it by wrapping the router's `process` method:
349
+
350
+ ```javascript
351
+ instrument_request_handler() {
352
+ const original_process = this.server.server_router.process.bind(this.server.server_router);
353
+ const admin = this;
354
+
355
+ this.server.server_router.process = function(req, res) {
356
+ const start_time = Date.now();
357
+ const req_data = {
358
+ method: req.method,
359
+ url: req.url,
360
+ timestamp: start_time
361
+ };
362
+
363
+ // Intercept res.end to capture status and timing
364
+ const original_end = res.end.bind(res);
365
+ res.end = function(...args) {
366
+ req_data.status = res.statusCode;
367
+ req_data.duration_ms = Date.now() - start_time;
368
+ admin._log_request(req_data);
369
+ return original_end(...args);
370
+ };
371
+
372
+ return original_process(req, res);
373
+ };
374
+ }
375
+ ```
376
+
377
+ **Circular buffer for request log:**
378
+
379
+ ```javascript
380
+ _log_request(req_data) {
381
+ this._request_log.push(req_data);
382
+ if (this._request_log.length > 1000) {
383
+ this._request_log.shift();
384
+ }
385
+ this._request_count++;
386
+
387
+ // Broadcast via SSE if connected
388
+ if (this._sse_publisher) {
389
+ this._sse_publisher.broadcast('request', req_data);
390
+ }
391
+ }
392
+ ```
393
+
394
+ ### `capture_bundle_info()`
395
+
396
+ Captures bundle data from the publisher's `ready` event.
397
+
398
+ ```javascript
399
+ capture_bundle_info() {
400
+ // Listen for main webpage publisher ready
401
+ // The publisher is created in server.js constructor
402
+ // We need access to it — either through the resource pool
403
+ // or by having the server pass it to us
404
+
405
+ const pool = this.server.resource_pool;
406
+ const publishers = pool.get_resources_by_type('HTTP_Webpage_Publisher');
407
+
408
+ publishers.forEach(pub => {
409
+ // If already ready, capture from static responders
410
+ // If not, listen for ready event
411
+ pub.on('ready', (bundle) => {
412
+ this._bundle_info = {
413
+ built_at: Date.now(),
414
+ items: []
415
+ };
416
+
417
+ if (bundle && bundle._arr) {
418
+ for (const item of bundle._arr) {
419
+ this._bundle_info.items.push({
420
+ type: item.type || item.extension?.replace('.', ''),
421
+ route: item.route,
422
+ size_identity: item.response_buffers?.identity?.length || 0,
423
+ size_gzip: item.response_buffers?.gzip?.length || null,
424
+ size_brotli: item.response_buffers?.br?.length || null
425
+ });
426
+ }
427
+ }
428
+
429
+ if (this._sse_publisher) {
430
+ this._sse_publisher.broadcast('build_complete', this._bundle_info);
431
+ }
432
+ });
433
+ });
434
+ }
435
+ ```
436
+
437
+ ### `track_route_registration()`
438
+
439
+ Instruments `server_router.set_route` to maintain a route registry.
440
+
441
+ ```javascript
442
+ track_route_registration() {
443
+ const original_set_route = this.server.server_router.set_route.bind(
444
+ this.server.server_router
445
+ );
446
+ const admin = this;
447
+
448
+ this.server.server_router.set_route = function(path, responder, handler) {
449
+ // Record the route
450
+ const route_entry = {
451
+ path: path,
452
+ handler_type: responder?.constructor?.name || 'function',
453
+ handler_name: responder?.name || null,
454
+ category: admin._categorize_route(path, responder),
455
+ registered_at: Date.now()
456
+ };
457
+
458
+ admin._route_registry.push(route_entry);
459
+
460
+ // Call original
461
+ return original_set_route(path, responder, handler);
462
+ };
463
+ }
464
+ ```
465
+
466
+ ### `_categorize_route(path, responder)`
467
+
468
+ Determines the category of a route for display purposes.
469
+
470
+ ```javascript
471
+ _categorize_route(path, responder) {
472
+ const type_name = responder?.constructor?.name || '';
473
+
474
+ if (path.startsWith('/api/admin/')) return 'admin';
475
+ if (path === '/admin') return 'admin';
476
+ if (type_name === 'HTTP_Function_Publisher') return 'api';
477
+ if (type_name === 'Observable_Publisher') return 'observable';
478
+ if (type_name === 'HTTP_SSE_Publisher') return 'sse';
479
+ if (type_name === 'Static_Route_HTTP_Responder') return 'auto';
480
+ if (type_name === 'HTTP_Website_Publisher') return 'website';
481
+ if (path.startsWith('/static')) return 'static';
482
+ return 'other';
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ ## SSE Channel Setup
489
+
490
+ The Admin Module creates its own `HTTP_SSE_Publisher` for streaming real-time events to the admin UI:
491
+
492
+ ```javascript
493
+ _setup_sse() {
494
+ const HTTP_SSE_Publisher = require('../../publishers/http-sse-publisher');
495
+
496
+ this._sse_publisher = new HTTP_SSE_Publisher({
497
+ name: 'admin_events',
498
+ keepaliveIntervalMs: 15000,
499
+ maxClients: 10,
500
+ eventHistorySize: 500
501
+ });
502
+
503
+ // Wire up resource pool events
504
+ const pool = this.server.resource_pool;
505
+ const event_names = [
506
+ 'resource_state_change',
507
+ 'crashed',
508
+ 'unhealthy',
509
+ 'unreachable',
510
+ 'recovered',
511
+ 'removed'
512
+ ];
513
+
514
+ event_names.forEach(event_name => {
515
+ pool.on(event_name, (event_data) => {
516
+ this._sse_publisher.broadcast(event_name, event_data);
517
+ });
518
+ });
519
+ }
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Initialization Sequence
525
+
526
+ The Admin Module follows this startup order:
527
+
528
+ 1. **Constructor** — Store server reference, initialize buffers
529
+ 2. **`track_route_registration()`** — Instrument `set_route` before any routes are registered (must happen first!)
530
+ 3. **`_setup_sse()`** — Create SSE publisher
531
+ 4. **`attach_to_router(router)`** — Register all `/api/admin/*` endpoints
532
+ 5. **`instrument_request_handler()`** — Wrap request processing (after routes are set up, to avoid instrumenting admin route setup itself)
533
+ 6. **`capture_bundle_info()`** — Listen for bundle ready events
534
+
535
+ ```javascript
536
+ // In server.js constructor:
537
+ const Admin_Module = require('./admin-ui/v1/server');
538
+ this.admin = new Admin_Module(this);
539
+ this.admin.track_route_registration();
540
+ this.admin._setup_sse();
541
+ this.admin.attach_to_router(server_router);
542
+
543
+ // After all user routes are set up:
544
+ this.admin.instrument_request_handler();
545
+ this.admin.capture_bundle_info();
546
+ ```
547
+
548
+ ---
549
+
550
+ ## Error Handling
551
+
552
+ Every adapter function is defensive:
553
+
554
+ ```javascript
555
+ get_process_info() {
556
+ try {
557
+ return {
558
+ main: {
559
+ pid: process.pid,
560
+ state: 'running',
561
+ uptime_seconds: Math.floor(process.uptime()),
562
+ memory: process.memoryUsage(),
563
+ cpu: process.cpuUsage()
564
+ },
565
+ children: this._get_child_processes()
566
+ };
567
+ } catch (err) {
568
+ return {
569
+ main: { pid: process.pid, state: 'unknown', error: err.message },
570
+ children: []
571
+ };
572
+ }
573
+ }
574
+ ```
575
+
576
+ If a property doesn't exist or throws, the adapter returns a safe default. The admin UI must also handle missing/null values gracefully.
577
+
578
+ ---
579
+
580
+ ## Data Refresh Strategy
581
+
582
+ | Data Category | Strategy | Interval |
583
+ |--------------|----------|----------|
584
+ | Process memory | Client polls `/api/admin/snapshot` | Every 5s |
585
+ | Resource states | SSE push on state change | Instant |
586
+ | Route table | Client fetches on navigation | On demand |
587
+ | Request log | SSE push per request | Instant |
588
+ | Build info | SSE push on build | On build |
589
+ | Config | Client fetches on navigation | On demand |
590
+ | Uptime | Client-side timer from initial value | Every 1s (client-calculated) |
591
+
592
+ This minimizes server load: only memory/CPU require periodic polling. Everything else is either static or event-driven.