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
package/server.js CHANGED
@@ -25,6 +25,9 @@ const Webpage = require('./website/webpage');
25
25
  const HTTP_Webpage_Publisher = require('./publishers/http-webpage-publisher');
26
26
  const HTTP_Function_Publisher = require('./publishers/http-function-publisher');
27
27
  const HTTP_Observable_Publisher = require('./publishers/http-observable-publisher');
28
+ const HTTP_SSE_Publisher = require('./publishers/http-sse-publisher');
29
+ const Process_Resource = require('./resources/process-resource');
30
+ const Remote_Process_Resource = require('./resources/remote-process-resource');
28
31
 
29
32
  const Static_Route_HTTP_Responder = require('./http/responders/static/Static_Route_HTTP_Responder');
30
33
 
@@ -131,6 +134,196 @@ class JSGUI_Single_Process_Server extends Evented_Class {
131
134
  console.warn('Skipping /admin route registration due to missing control.');
132
135
  }
133
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
+ }
134
327
 
135
328
  if (spec.routes) {
136
329
  throw 'NYI - will use Website class rather than Website_Resource here'
@@ -267,7 +460,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
267
460
  this.function_publishers = this.function_publishers || [];
268
461
  this.function_publishers.push(fpub);
269
462
 
270
- this.server_router.set_route('/api/' + name, fpub, fpub.handle_http);
463
+ // Auto-prefix /api/ for simple names
464
+ // If name already starts with '/', use as-is for full route control
465
+ const full_route = name.startsWith('/') ? name : '/api/' + name;
466
+ this.server_router.set_route(full_route, fpub, fpub.handle_http);
271
467
  }
272
468
 
273
469
  publish_observable(route, obs, options = {}) {
@@ -275,7 +471,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
275
471
  obs,
276
472
  ...options
277
473
  });
278
- this.server_router.set_route(route, publisher, publisher.handle_http);
474
+ // Auto-prefix /api/ for simple names (like publish() does)
475
+ // If route already starts with '/', use as-is for backward compatibility
476
+ const full_route = route.startsWith('/') ? route : '/api/' + route;
477
+ this.server_router.set_route(full_route, publisher, publisher.handle_http);
279
478
  return publisher;
280
479
  }
281
480
 
@@ -478,19 +677,84 @@ class JSGUI_Single_Process_Server extends Evented_Class {
478
677
  }
479
678
 
480
679
  close(callback) {
481
- let count = this.http_servers.length;
482
- if (count === 0) {
483
- if (callback) process.nextTick(callback);
484
- return;
485
- }
486
- this.http_servers.forEach(server => {
487
- server.close(() => {
488
- count--;
489
- if (count === 0) {
490
- this.http_servers = [];
491
- if (callback) callback();
680
+ const invoke_stop = (target, done) => {
681
+ if (!target || typeof target.stop !== 'function') {
682
+ done(null);
683
+ return;
684
+ }
685
+
686
+ if (target.stop.length >= 1) {
687
+ target.stop((error) => done(error || null));
688
+ return;
689
+ }
690
+
691
+ try {
692
+ const stop_result = target.stop();
693
+ if (stop_result && typeof stop_result.then === 'function') {
694
+ stop_result.then(() => done(null), (error) => done(error || null));
695
+ return;
492
696
  }
697
+ done(null);
698
+ } catch (error) {
699
+ done(error);
700
+ }
701
+ };
702
+
703
+ const close_http_servers = (done) => {
704
+ let count = this.http_servers.length;
705
+ if (count === 0) {
706
+ this.http_servers = [];
707
+ done();
708
+ return;
709
+ }
710
+
711
+ this.http_servers.forEach(server => {
712
+ server.close(() => {
713
+ count--;
714
+ if (count === 0) {
715
+ this.http_servers = [];
716
+ done();
717
+ }
718
+ });
493
719
  });
720
+ };
721
+
722
+ const finalize_close = (error) => {
723
+ if (callback) callback(error || null);
724
+ };
725
+
726
+ const stop_targets = [];
727
+ if (this.resource_pool) {
728
+ stop_targets.push(this.resource_pool);
729
+ }
730
+ if (this.sse_publisher) {
731
+ stop_targets.push(this.sse_publisher);
732
+ }
733
+ if (this.admin_v1 && typeof this.admin_v1.destroy === 'function') {
734
+ this.admin_v1.destroy();
735
+ }
736
+
737
+ if (stop_targets.length === 0) {
738
+ close_http_servers(() => finalize_close(null));
739
+ return;
740
+ }
741
+
742
+ let pending_stops = stop_targets.length;
743
+ const stop_errors = [];
744
+ const on_stop_complete = (error) => {
745
+ if (error) {
746
+ stop_errors.push(error);
747
+ }
748
+ pending_stops--;
749
+ if (pending_stops > 0) {
750
+ return;
751
+ }
752
+ const first_error = stop_errors.length > 0 ? stop_errors[0] : null;
753
+ close_http_servers(() => finalize_close(first_error));
754
+ };
755
+
756
+ stop_targets.forEach((target) => {
757
+ invoke_stop(target, on_stop_complete);
494
758
  });
495
759
  }
496
760
  }
@@ -498,10 +762,21 @@ class JSGUI_Single_Process_Server extends Evented_Class {
498
762
  JSGUI_Single_Process_Server.jsgui = jsgui;
499
763
 
500
764
  JSGUI_Single_Process_Server.Resource = Resource;
765
+ JSGUI_Single_Process_Server.Resource.Process = Process_Resource;
766
+ JSGUI_Single_Process_Server.Resource.Remote_Process = Remote_Process_Resource;
501
767
  JSGUI_Single_Process_Server.Page_Context = Server_Page_Context;
502
768
  JSGUI_Single_Process_Server.Server_Page_Context = Server_Page_Context;
503
769
  JSGUI_Single_Process_Server.Website_Resource = Website_Resource;
504
770
  JSGUI_Single_Process_Server.Publishers = Publishers;
771
+ JSGUI_Single_Process_Server.Process_Resource = Process_Resource;
772
+ JSGUI_Single_Process_Server.Remote_Process_Resource = Remote_Process_Resource;
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
+
505
780
  JSGUI_Single_Process_Server.serve = require('./serve-factory')(JSGUI_Single_Process_Server);
506
781
 
507
782
  module.exports = JSGUI_Single_Process_Server;
package/tests/README.md CHANGED
@@ -19,16 +19,28 @@ The test suite covers all aspects of the Phase 1 implementation:
19
19
  tests/
20
20
  ├── bundlers.test.js # Component isolation tests for bundlers
21
21
  ├── assigners.test.js # Component isolation tests for assigners
22
- ├── publishers.test.js # Component isolation tests for publishers
23
- ├── configuration-validation.test.js # Configuration validation tests
24
- ├── end-to-end.test.js # Full integration tests
22
+ ├── publishers.test.js # Component isolation tests for publishers
23
+ ├── configuration-validation.test.js # Configuration validation tests
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
26
+ ├── serve.test.js # Server.serve core behavior tests
27
+ ├── serve-resources.test.js # Server.serve + resource integration tests
28
+ ├── process-resource.test.js # Process_Resource lifecycle and restart tests
29
+ ├── remote-process-resource.test.js # Remote_Process_Resource polling/recovery tests
30
+ ├── server-resource-pool.test.js # Resource pool lifecycle and event forwarding tests
31
+ ├── http-sse-publisher.test.js # HTTP_SSE_Publisher protocol/lifecycle tests
32
+ ├── end-to-end.test.js # Full integration tests
25
33
  ├── content-analysis.test.js # Content analysis and verification
26
34
  ├── performance.test.js # Performance benchmarks
27
35
  ├── error-handling.test.js # Error handling and edge cases
36
+ ├── control-optimizer-cache-behavior.test.js # Optimizer cache enable/disable behavior
28
37
  ├── examples-controls.e2e.test.js # Example apps regression (controls)
29
38
  ├── sass-controls.e2e.test.js # Sass/CSS controls E2E coverage
30
39
  ├── jsgui3-html-examples.puppeteer.test.js # Puppeteer interaction tests (jsgui3-html examples)
40
+ ├── bundling-default-control-elimination.puppeteer.test.js # Puppeteer: default control elimination bundle checks
31
41
  ├── window-examples.puppeteer.test.js # Puppeteer interaction tests (window examples)
42
+ ├── window-resource-integration.puppeteer.test.js # Browser E2E: controls + resource APIs + SSE
43
+ ├── helpers/puppeteer-e2e-harness.js # Shared Puppeteer story runner + probes
32
44
  ├── test-runner.js # Custom test runner with reporting
33
45
  └── README.md # This file
34
46
  ```
@@ -45,6 +57,12 @@ npm test
45
57
  # Using the custom test runner
46
58
  node tests/test-runner.js --test=bundlers.test.js
47
59
 
60
+ # Optimizer cache behavior
61
+ node tests/test-runner.js --test=control-optimizer-cache-behavior.test.js
62
+
63
+ # Admin UI shell interaction regression
64
+ node tests/test-runner.js --test=admin-ui-jsgui-controls.test.js
65
+
48
66
  # Using mocha directly
49
67
  npx mocha tests/bundlers.test.js
50
68
  ```
@@ -59,6 +77,16 @@ npm run test:examples:controls
59
77
  npm run test:puppeteer:windows
60
78
  ```
61
79
 
80
+ ### Run Puppeteer Bundling Elimination Tests
81
+ ```bash
82
+ npm run test:puppeteer:bundling
83
+ ```
84
+
85
+ ### Run Puppeteer Resource Integration Tests
86
+ ```bash
87
+ npm run test:puppeteer:resources
88
+ ```
89
+
62
90
  ### Run Tests with Options
63
91
  ```bash
64
92
  # Debug mode (enables sourcemaps)
@@ -76,16 +104,35 @@ node tests/test-runner.js --test=end-to-end.test.js --debug
76
104
  1. Run a focused suite first (fast feedback).
77
105
  2. Run the example regression suite next (HTML/CSS/JS smoke checks).
78
106
  3. Run Puppeteer interaction tests last (heavier, requires a browser).
79
- 4. Run the full suite only when changes are broad or before release.
107
+ 4. Run the resource integration Puppeteer suite when changing resources/SSE APIs.
108
+ 5. Run the full suite only when changes are broad or before release.
80
109
 
81
110
  Suggested sequence:
82
111
  ```bash
83
112
  node tests/test-runner.js --test=bundlers.test.js
84
113
  npm run test:examples:controls
114
+ npm run test:puppeteer:bundling
85
115
  npm run test:puppeteer:windows
116
+ npm run test:puppeteer:resources
86
117
  npm test
87
118
  ```
88
119
 
120
+ ## Advanced Puppeteer E2E Methodology
121
+
122
+ For high-value interaction and integration coverage, use the shared harness:
123
+
124
+ - `tests/helpers/puppeteer-e2e-harness.js`
125
+
126
+ Key patterns:
127
+
128
+ - Write deterministic interaction stories with `run_interaction_story(...)` and named steps.
129
+ - Add browser probes (`console`, `pageerror`, `requestfailed`) and assert they stay clean.
130
+ - Assert both UI state and server truth for integration cases.
131
+ - For resource flows, validate both:
132
+ - API actions (`/api/resource/*`) and
133
+ - SSE propagation (`/events`) reflected in client UI/debug state.
134
+ - Keep selectors stable (`id` or `data-test`) so interaction tests remain robust.
135
+
89
136
  ## Patterns That Work
90
137
 
91
138
  - Use per-test temporary client files and delete them in `finally`.
@@ -202,6 +249,26 @@ Browser-level interaction checks for jsgui3-html examples:
202
249
 
203
250
  - MVVM counter interactions, bindings, and validation state
204
251
 
252
+ ### 11. Window Resource Integration Puppeteer Tests (`window-resource-integration.puppeteer.test.js`)
253
+
254
+ Browser-level interaction checks that combine controls and server resources:
255
+
256
+ - Step-driven control interactions (date + datetime controls)
257
+ - Client actions invoking resource lifecycle APIs (`start`, `stop`, `restart`)
258
+ - SSE resource state events reflected in client UI
259
+ - Cross-checking client-observed state with server resource pool state
260
+
261
+ ### 12. Core Resource and Serve Reliability Tests
262
+
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
265
+ - `serve.test.js` validates `Server.serve` startup/route readiness behavior
266
+ - `serve-resources.test.js` validates in-process and process resource wiring in serve mode
267
+ - `process-resource.test.js` validates direct process lifecycle + crash restart handling
268
+ - `remote-process-resource.test.js` validates polling, unreachable, and recovered transitions
269
+ - `server-resource-pool.test.js` validates pool forwarding, remove/stop, and summaries
270
+ - `http-sse-publisher.test.js` validates SSE broadcast/send/replay/keepalive semantics
271
+
205
272
  ## Configuration Examples
206
273
 
207
274
  ### Basic Minification