jsgui3-server 0.0.149 → 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 (98) hide show
  1. package/.github/agents/Mobile Developer.agent.md +89 -0
  2. package/AGENTS.md +4 -0
  3. package/README.md +130 -0
  4. package/admin-ui/client.js +73 -43
  5. package/admin-ui/v1/admin_auth_service.js +197 -0
  6. package/admin-ui/v1/admin_user_store.js +71 -0
  7. package/admin-ui/v1/client.js +17 -0
  8. package/admin-ui/v1/controls/admin_shell.js +1399 -0
  9. package/admin-ui/v1/controls/group_box.js +84 -0
  10. package/admin-ui/v1/controls/stat_card.js +125 -0
  11. package/admin-ui/v1/server.js +658 -0
  12. package/admin-ui/v1/utils/formatters.js +68 -0
  13. package/docs/admin-extension-guide.md +345 -0
  14. package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
  15. package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
  16. package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
  17. package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
  18. package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
  19. package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
  20. package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
  21. package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
  22. package/docs/books/adaptive-control-improvements/README.md +66 -0
  23. package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
  24. package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
  25. package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
  26. package/docs/books/admin-ui-authentication/README.md +25 -0
  27. package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
  28. package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
  29. package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
  30. package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
  31. package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
  32. package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
  33. package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
  34. package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
  35. package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
  36. package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
  37. package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
  38. package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
  39. package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
  40. package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
  41. package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
  42. package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
  43. package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
  44. package/docs/books/creating-a-new-admin-ui/README.md +68 -0
  45. package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
  46. package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
  47. package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
  48. package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
  49. package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
  50. package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
  51. package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
  52. package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
  53. package/docs/books/device-adaptive-composition/README.md +47 -0
  54. package/docs/comparison-report-express-plex-cpanel.md +549 -0
  55. package/docs/designs/server-admin-interface-aero.svg +611 -0
  56. package/docs/troubleshooting.md +84 -53
  57. package/module.js +16 -11
  58. package/package.json +1 -1
  59. package/serve-factory.js +1 -0
  60. package/server.js +199 -0
  61. package/tests/README.md +5 -0
  62. package/tests/admin-ui-jsgui-controls.test.js +581 -0
  63. package/tests/test-runner.js +1 -0
  64. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +0 -40
  65. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +0 -39
  66. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +0 -39
  67. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +0 -39
  68. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +0 -39
  69. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +0 -40
  70. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +0 -39
  71. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +0 -40
  72. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +0 -40
  73. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +0 -39
  74. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +0 -39
  75. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +0 -44
  76. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +0 -45
  77. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +0 -39
  78. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +0 -39
  79. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +0 -42
  80. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +0 -40
  81. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +0 -43
  82. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +0 -40
  83. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +0 -40
  84. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +0 -40
  85. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +0 -39
  86. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +0 -39
  87. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +0 -39
  88. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +0 -39
  89. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +0 -39
  90. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +0 -41
  91. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +0 -44
  92. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +0 -40
  93. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +0 -40
  94. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +0 -39
  95. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +0 -39
  96. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +0 -39
  97. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +0 -39
  98. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +0 -39
@@ -197,14 +197,14 @@ input.js:1:0: ERROR: Expected identifier but found "}"
197
197
 
198
198
  2. **Verify CSS definition:**
199
199
  ```javascript
200
- MyControl.css = `
201
- .my-control {
202
- padding: 20px;
203
- background: #f0f0f0;
204
- }
205
- `;
206
- ```
207
- You can also use `MyControl.scss` or `MyControl.sass` with template literals; these compile to CSS during bundling (ensure the `sass` dependency is installed). To see inline CSS sourcemaps in devtools, enable `style.sourcemaps` (or run with `debug: true`).
200
+ MyControl.css = `
201
+ .my-control {
202
+ padding: 20px;
203
+ background: #f0f0f0;
204
+ }
205
+ `;
206
+ ```
207
+ You can also use `MyControl.scss` or `MyControl.sass` with template literals; these compile to CSS during bundling (ensure the `sass` dependency is installed). To see inline CSS sourcemaps in devtools, enable `style.sourcemaps` (or run with `debug: true`).
208
208
 
209
209
  3. **Check activation:**
210
210
  ```javascript
@@ -540,6 +540,37 @@ from origin 'http://localhost:3000' has been blocked by CORS policy
540
540
  delete require.cache[require.resolve('./client.js')];
541
541
  ```
542
542
 
543
+ ### Generated `.jsgui3-server-cache` Shim Files
544
+
545
+ **Symptoms:**
546
+ - You see many changed files under `.jsgui3-server-cache/jsgui3-html-shims/`
547
+ - Shim files contain absolute machine-specific paths
548
+ - Git status looks noisy after running examples or admin pages
549
+
550
+ **What this is:**
551
+ - These are generated cache artifacts created by the bundling/render pipeline.
552
+ - They are performance helpers, not source-of-truth project files.
553
+
554
+ **Expected behavior:**
555
+ 1. They may be regenerated when controls, environment, or module resolution changes.
556
+ 2. They can differ across machines (for example, local `node_modules` path vs NVM/global path).
557
+ 3. They are safe to delete; they will be recreated when needed.
558
+
559
+ **Recommended project hygiene:**
560
+ 1. Ignore cache directories in `.gitignore`:
561
+ ```gitignore
562
+ .jsgui3-server-cache/
563
+ **/.jsgui3-server-cache/
564
+ ```
565
+ 2. If accidentally changed, revert generated shims:
566
+ ```bash
567
+ git restore -- .jsgui3-server-cache/jsgui3-html-shims/*.js
568
+ ```
569
+ 3. If a nested example cache appears, remove it:
570
+ ```bash
571
+ rm -rf "examples/controls/1) window/.jsgui3-server-cache"
572
+ ```
573
+
543
574
  ### Source Maps Not Working
544
575
 
545
576
  **Symptoms:**
@@ -743,48 +774,48 @@ controls.MinimalControl = MinimalControl;
743
774
  module.exports = jsgui;
744
775
  ```
745
776
 
746
- This minimal setup helps isolate whether the issue is with your specific code or the framework itself.
747
-
748
- ---
749
-
750
- ## Bundling and Test Failures
751
-
752
- ### `waiting for wp_publisher ready` or Publisher Ready Timeout
753
-
754
- **Symptoms:**
755
- - Tests hang while starting servers
756
- - Logs show `waiting for wp_publisher ready`
757
- - Tests time out without a clear stack trace
758
-
759
- **Likely cause:** the JS/CSS bundler failed before emitting `ready`.
760
-
761
- **What to check:**
762
- 1. Look earlier in the log for bundler errors (esbuild, Sass, file resolution).
763
- 2. Confirm `sass` is installed if you are running Sass tests.
764
- 3. Validate the client entry path passed to `src_path_client_js`.
765
-
766
- ### esbuild platform mismatch (Windows vs WSL)
767
-
768
- **Symptoms:**
769
- - Error: `You installed esbuild for another platform than the one you're currently using`
770
-
771
- **Cause:** `node_modules` was installed on a different OS and reused in this environment.
772
-
773
- **Fix:**
774
- 1. Remove the existing `node_modules` from the current workspace.
775
- 2. Reinstall dependencies in the current environment:
776
- ```bash
777
- npm install
778
- ```
779
- 3. If you prefer to keep the lockfile untouched, use:
780
- ```bash
781
- npm ci
782
- ```
783
- 4. As a fast workaround, you can try:
784
- ```bash
785
- npm rebuild esbuild
786
- ```
787
-
788
- ---
789
-
790
- Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
777
+ This minimal setup helps isolate whether the issue is with your specific code or the framework itself.
778
+
779
+ ---
780
+
781
+ ## Bundling and Test Failures
782
+
783
+ ### `waiting for wp_publisher ready` or Publisher Ready Timeout
784
+
785
+ **Symptoms:**
786
+ - Tests hang while starting servers
787
+ - Logs show `waiting for wp_publisher ready`
788
+ - Tests time out without a clear stack trace
789
+
790
+ **Likely cause:** the JS/CSS bundler failed before emitting `ready`.
791
+
792
+ **What to check:**
793
+ 1. Look earlier in the log for bundler errors (esbuild, Sass, file resolution).
794
+ 2. Confirm `sass` is installed if you are running Sass tests.
795
+ 3. Validate the client entry path passed to `src_path_client_js`.
796
+
797
+ ### esbuild platform mismatch (Windows vs WSL)
798
+
799
+ **Symptoms:**
800
+ - Error: `You installed esbuild for another platform than the one you're currently using`
801
+
802
+ **Cause:** `node_modules` was installed on a different OS and reused in this environment.
803
+
804
+ **Fix:**
805
+ 1. Remove the existing `node_modules` from the current workspace.
806
+ 2. Reinstall dependencies in the current environment:
807
+ ```bash
808
+ npm install
809
+ ```
810
+ 3. If you prefer to keep the lockfile untouched, use:
811
+ ```bash
812
+ npm ci
813
+ ```
814
+ 4. As a fast workaround, you can try:
815
+ ```bash
816
+ npm rebuild esbuild
817
+ ```
818
+
819
+ ---
820
+
821
+ Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
package/module.js CHANGED
@@ -16,17 +16,22 @@ jsgui.controls.Active_HTML_Document = require('./controls/Active_HTML_Document')
16
16
 
17
17
  //const Resource_Publisher = require('./publishing/http-resource-publisher');
18
18
  //jsgui.Resource_Publisher = Resource_Publisher;
19
- const Server = require('./server');
20
- jsgui.Server = Server;
21
- jsgui.serve = Server.serve;
22
- jsgui.Process_Resource = Server.Process_Resource;
23
- jsgui.Remote_Process_Resource = Server.Remote_Process_Resource;
24
- jsgui.HTTP_SSE_Publisher = Server.HTTP_SSE_Publisher;
25
- if (jsgui.Resource) {
26
- jsgui.Resource.Process = Server.Process_Resource;
27
- jsgui.Resource.Remote_Process = Server.Remote_Process_Resource;
28
- }
29
- jsgui.fs2 = require('./fs2');
19
+ const Server = require('./server');
20
+ jsgui.Server = Server;
21
+ jsgui.serve = Server.serve;
22
+ jsgui.Process_Resource = Server.Process_Resource;
23
+ jsgui.Remote_Process_Resource = Server.Remote_Process_Resource;
24
+ jsgui.HTTP_SSE_Publisher = Server.HTTP_SSE_Publisher;
25
+ if (jsgui.Resource) {
26
+ jsgui.Resource.Process = Server.Process_Resource;
27
+ jsgui.Resource.Remote_Process = Server.Remote_Process_Resource;
28
+ }
29
+ jsgui.fs2 = require('./fs2');
30
+
31
+ // Admin UI extensibility exports
32
+ jsgui.Admin_Module_V1 = Server.Admin_Module_V1;
33
+ jsgui.Admin_Auth_Service = Server.Admin_Auth_Service;
34
+ jsgui.Admin_User_Store = Server.Admin_User_Store;
30
35
 
31
36
  // Port utilities for auto-port selection
32
37
  const port_utils = require('./port-utils');
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "type": "git",
44
44
  "url": "https://github.com/metabench/jsgui3-server.git"
45
45
  },
46
- "version": "0.0.149",
46
+ "version": "0.0.150",
47
47
  "scripts": {
48
48
  "cli": "node cli.js",
49
49
  "serve": "node cli.js serve",
package/serve-factory.js CHANGED
@@ -289,6 +289,7 @@ module.exports = (Server) => {
289
289
  };
290
290
  if (style_config !== undefined) server_spec.style = style_config;
291
291
  if (bundler_config !== undefined) server_spec.bundler = bundler_config;
292
+ if (serve_options.admin !== undefined) server_spec.admin = serve_options.admin;
292
293
 
293
294
  if (typeof serve_options.ctrl === 'function') {
294
295
  server_spec.Ctrl = serve_options.ctrl;
package/server.js CHANGED
@@ -134,6 +134,196 @@ class JSGUI_Single_Process_Server extends Evented_Class {
134
134
  console.warn('Skipping /admin route registration due to missing control.');
135
135
  }
136
136
 
137
+ // ─── Admin UI V1 (Dashboard with Stat Cards) ─────────
138
+ // Configurable via spec.admin:
139
+ // spec.admin === false → disable admin entirely
140
+ // spec.admin = { enabled: false } → disable admin entirely
141
+ // spec.admin = { sections: [...] } → add custom sidebar sections
142
+ // spec.admin = { endpoints: [...] } → add custom protected endpoints
143
+ const admin_config = spec.admin !== undefined ? spec.admin : {};
144
+ const admin_enabled = admin_config !== false && (admin_config.enabled !== false);
145
+
146
+ const Admin_Module_V1 = require('./admin-ui/v1/server');
147
+ if (admin_enabled) {
148
+ this.admin_v1 = new Admin_Module_V1(typeof admin_config === 'object' ? admin_config : {});
149
+ this.admin_v1.init(this);
150
+
151
+ // Register Admin V1 Page Route
152
+ let Admin_Shell_Control;
153
+ try {
154
+ Admin_Shell_Control = require('./admin-ui/v1/client').controls.Admin_Shell;
155
+ } catch (e) {
156
+ console.warn('Failed to load Admin_Shell control:', e.message || e);
157
+ }
158
+
159
+ if (Admin_Shell_Control) {
160
+ const admin_v1_app = new Webpage({
161
+ content: Admin_Shell_Control,
162
+ title: 'jsgui3 Admin Dashboard'
163
+ });
164
+
165
+ const admin_v1_publisher = new HTTP_Webpage_Publisher({
166
+ name: 'Admin_V1_Publisher',
167
+ webpage: admin_v1_app,
168
+ src_path_client_js: lib_path.join(__dirname, 'admin-ui/v1/client.js')
169
+ });
170
+ admin_v1_publisher.name = 'Admin_V1_Publisher';
171
+
172
+ const is_dev_defaults = !process.env.ADMIN_V1_PASSWORD && process.env.NODE_ENV !== 'production';
173
+ const login_hint = is_dev_defaults
174
+ ? '<div class="hint dev-creds">Dev defaults active — username: <code>admin</code> password: <code>admin</code></div><div class="hint">Set ADMIN_V1_USER / ADMIN_V1_PASSWORD env vars for production.</div>'
175
+ : '<div class="hint">Sign in with your configured credentials.</div>';
176
+ const login_html = `<!doctype html>
177
+ <html>
178
+ <head>
179
+ <meta charset="utf-8" />
180
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
181
+ <title>Admin Login</title>
182
+ <style>
183
+ body { margin:0; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:#111428; color:#e8e8f0; display:flex; align-items:center; justify-content:center; min-height:100vh; }
184
+ .card { width: 340px; background:#1b1f38; border:1px solid #2f3358; border-radius:10px; padding:20px; }
185
+ h1 { margin:0 0 16px; font-size:1rem; }
186
+ label { display:block; margin:10px 0 6px; font-size:0.8rem; color:#9aa0c8; }
187
+ input { width:100%; box-sizing:border-box; background:#13162a; border:1px solid #2e345c; color:#e8e8f0; border-radius:6px; padding:8px 10px; }
188
+ button { margin-top:14px; width:100%; border:1px solid #4facfe; background:#224267; color:#d3ebff; border-radius:6px; padding:8px 10px; cursor:pointer; }
189
+ .err { margin-top:10px; color:#ff8e9f; font-size:0.8rem; min-height:1.1rem; }
190
+ .hint { margin-top:12px; color:#7f86b3; font-size:0.75rem; }
191
+ .dev-creds { color:#43e97b; }
192
+ code { background:#13162a; padding:2px 6px; border-radius:4px; font-size:0.8rem; }
193
+ </style>
194
+ </head>
195
+ <body>
196
+ <div class="card">
197
+ <h1>jsgui3 Admin Login</h1>
198
+ <form id="login-form">
199
+ <label for="username">Username</label>
200
+ <input id="username" autocomplete="username" required />
201
+ <label for="password">Password</label>
202
+ <input id="password" type="password" autocomplete="current-password" required />
203
+ <button type="submit">Sign In</button>
204
+ <div class="err" id="err"></div>
205
+ ${login_hint}
206
+ </form>
207
+ </div>
208
+ <script>
209
+ (async () => {
210
+ try {
211
+ const session = await fetch('/api/admin/v1/auth/session', { credentials: 'same-origin' }).then(r => r.json());
212
+ if (session && session.authenticated) {
213
+ location.replace('/admin/v1');
214
+ return;
215
+ }
216
+ } catch (e) {}
217
+
218
+ const form = document.getElementById('login-form');
219
+ const err = document.getElementById('err');
220
+ form.addEventListener('submit', async (e) => {
221
+ e.preventDefault();
222
+ err.textContent = '';
223
+ const username = document.getElementById('username').value;
224
+ const password = document.getElementById('password').value;
225
+ try {
226
+ const res = await fetch('/api/admin/v1/auth/login', {
227
+ method: 'POST',
228
+ headers: { 'Content-Type': 'application/json' },
229
+ credentials: 'same-origin',
230
+ body: JSON.stringify({ username, password })
231
+ });
232
+ const data = await res.json();
233
+ if (!res.ok || !data.ok) {
234
+ err.textContent = data && data.error ? data.error : 'Login failed';
235
+ return;
236
+ }
237
+ location.replace('/admin/v1');
238
+ } catch (e2) {
239
+ err.textContent = 'Network error';
240
+ }
241
+ });
242
+ })();
243
+ </script>
244
+ </body>
245
+ </html>`;
246
+
247
+ const serve_admin_v1_page = (req, res) => {
248
+ if (req && typeof req.url === 'string' && req.url.startsWith('/admin/v1/login')) {
249
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
250
+ res.end(login_html);
251
+ return;
252
+ }
253
+
254
+ if (!this.admin_v1 || !this.admin_v1.is_admin_read_request(req)) {
255
+ res.writeHead(302, { Location: '/admin/v1/login' });
256
+ res.end();
257
+ return;
258
+ }
259
+ if (admin_v1_cached_html) {
260
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
261
+ res.end(admin_v1_cached_html);
262
+ } else {
263
+ res.writeHead(503, { 'Content-Type': 'text/plain' });
264
+ res.end('Admin UI v1 is loading, please retry...');
265
+ }
266
+ };
267
+
268
+ server_router.set_route('/admin/v1/login', null, (req, res) => {
269
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
270
+ res.end(login_html);
271
+ });
272
+
273
+ // Namespace admin v1 assets under /admin/v1/ to avoid
274
+ // colliding with the main app's /js/js.js and /css/css.css.
275
+ let admin_v1_cached_html = null;
276
+
277
+ admin_v1_publisher.on('ready', (res_ready) => {
278
+ if (res_ready && res_ready._arr) {
279
+ for (const bundle_item of res_ready._arr) {
280
+ const { type } = bundle_item;
281
+ if (type === 'HTML') {
282
+ // Rewrite asset references so browser fetches
283
+ // the admin-specific JS/CSS, not the main app's.
284
+ let html = bundle_item.text || '';
285
+ html = html.replace(
286
+ /href="\/css\/css\.css"/g,
287
+ 'href="/admin/v1/css/css.css"'
288
+ );
289
+ html = html.replace(
290
+ /src="\/js\/js\.js"/g,
291
+ 'src="/admin/v1/js/js.js"'
292
+ );
293
+ // Inject viewport meta for mobile rendering
294
+ if (!html.includes('name="viewport"')) {
295
+ html = html.replace(
296
+ /<head([^>]*)>/i,
297
+ '<head$1><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">'
298
+ );
299
+ }
300
+ admin_v1_cached_html = html;
301
+ } else {
302
+ // JS → /admin/v1/js/js.js, CSS → /admin/v1/css/css.css
303
+ const namespaced_route =
304
+ type === 'JavaScript' ? '/admin/v1/js/js.js' :
305
+ type === 'CSS' ? '/admin/v1/css/css.css' :
306
+ '/admin/v1' + bundle_item.route;
307
+ const responder = new Static_Route_HTTP_Responder(bundle_item);
308
+ server_router.set_route(namespaced_route, responder, responder.handle_http);
309
+ }
310
+ }
311
+
312
+ // Serve authenticated admin page at /admin/v1
313
+ server_router.set_route('/admin/v1', null, serve_admin_v1_page);
314
+ }
315
+ });
316
+
317
+ // Temporary handler until the publisher finishes bundling
318
+ server_router.set_route('/admin/v1', null, serve_admin_v1_page);
319
+ resource_pool.add(admin_v1_publisher);
320
+ } else {
321
+ console.warn('Skipping /admin/v1 route registration due to missing Admin_Shell control.');
322
+ }
323
+ } else {
324
+ // admin_enabled === false
325
+ this.admin_v1 = null;
326
+ }
137
327
 
138
328
  if (spec.routes) {
139
329
  throw 'NYI - will use Website class rather than Website_Resource here'
@@ -540,6 +730,9 @@ class JSGUI_Single_Process_Server extends Evented_Class {
540
730
  if (this.sse_publisher) {
541
731
  stop_targets.push(this.sse_publisher);
542
732
  }
733
+ if (this.admin_v1 && typeof this.admin_v1.destroy === 'function') {
734
+ this.admin_v1.destroy();
735
+ }
543
736
 
544
737
  if (stop_targets.length === 0) {
545
738
  close_http_servers(() => finalize_close(null));
@@ -578,6 +771,12 @@ JSGUI_Single_Process_Server.Publishers = Publishers;
578
771
  JSGUI_Single_Process_Server.Process_Resource = Process_Resource;
579
772
  JSGUI_Single_Process_Server.Remote_Process_Resource = Remote_Process_Resource;
580
773
  JSGUI_Single_Process_Server.HTTP_SSE_Publisher = HTTP_SSE_Publisher;
774
+
775
+ // Admin UI extensibility exports
776
+ JSGUI_Single_Process_Server.Admin_Module_V1 = require('./admin-ui/v1/server');
777
+ JSGUI_Single_Process_Server.Admin_Auth_Service = require('./admin-ui/v1/admin_auth_service');
778
+ JSGUI_Single_Process_Server.Admin_User_Store = require('./admin-ui/v1/admin_user_store');
779
+
581
780
  JSGUI_Single_Process_Server.serve = require('./serve-factory')(JSGUI_Single_Process_Server);
582
781
 
583
782
  module.exports = JSGUI_Single_Process_Server;
package/tests/README.md CHANGED
@@ -22,6 +22,7 @@ tests/
22
22
  ├── publishers.test.js # Component isolation tests for publishers
23
23
  ├── configuration-validation.test.js # Configuration validation tests
24
24
  ├── admin-ui-render.test.js # Admin page render regression test
25
+ ├── admin-ui-jsgui-controls.test.js # Admin shell interaction + control-first regression test
25
26
  ├── serve.test.js # Server.serve core behavior tests
26
27
  ├── serve-resources.test.js # Server.serve + resource integration tests
27
28
  ├── process-resource.test.js # Process_Resource lifecycle and restart tests
@@ -59,6 +60,9 @@ node tests/test-runner.js --test=bundlers.test.js
59
60
  # Optimizer cache behavior
60
61
  node tests/test-runner.js --test=control-optimizer-cache-behavior.test.js
61
62
 
63
+ # Admin UI shell interaction regression
64
+ node tests/test-runner.js --test=admin-ui-jsgui-controls.test.js
65
+
62
66
  # Using mocha directly
63
67
  npx mocha tests/bundlers.test.js
64
68
  ```
@@ -257,6 +261,7 @@ Browser-level interaction checks that combine controls and server resources:
257
261
  ### 12. Core Resource and Serve Reliability Tests
258
262
 
259
263
  - `admin-ui-render.test.js` validates the admin page control renders without clobbering control internals
264
+ - `admin-ui-jsgui-controls.test.js` validates admin shell interactions, custom section nav refresh, retry/logout control behavior, and SSE open/error/heartbeat handling
260
265
  - `serve.test.js` validates `Server.serve` startup/route readiness behavior
261
266
  - `serve-resources.test.js` validates in-process and process resource wiring in serve mode
262
267
  - `process-resource.test.js` validates direct process lifecycle + crash restart handling