jsgui3-server 0.0.147 → 0.0.149

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 (145) hide show
  1. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  2. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
  3. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
  4. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
  5. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
  6. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
  7. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
  8. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
  9. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
  10. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
  11. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
  12. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
  13. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
  14. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
  15. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
  16. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
  17. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
  18. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
  19. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
  20. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
  21. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
  22. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
  23. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
  24. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
  25. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
  26. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
  27. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
  28. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
  29. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
  30. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
  31. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
  32. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
  33. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
  34. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
  35. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
  36. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
  37. package/README.md +85 -3
  38. package/admin-ui/client.js +213 -0
  39. package/admin-ui/server.js +104 -0
  40. package/client/controls/auto-observable.js +207 -0
  41. package/dev-status.svg +139 -0
  42. package/docs/api-reference.md +301 -43
  43. package/docs/books/admin-ui/01-introduction.md +32 -0
  44. package/docs/books/admin-ui/02-architecture.md +92 -0
  45. package/docs/books/admin-ui/03-controls.md +194 -0
  46. package/docs/books/admin-ui/04-implementation-plan.md +62 -0
  47. package/docs/books/admin-ui/README.md +26 -0
  48. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  49. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  50. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  51. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  52. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  53. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  54. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  55. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  56. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  57. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  58. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  59. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  60. package/docs/bundling-system-deep-dive.md +9 -4
  61. package/docs/comprehensive-documentation.md +49 -18
  62. package/docs/configuration-reference.md +152 -27
  63. package/docs/core/README.md +19 -0
  64. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  65. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  66. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  67. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  68. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  69. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  70. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  71. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  72. package/docs/publishers-guide.md +59 -4
  73. package/docs/resources-guide.md +184 -35
  74. package/docs/simple-server-api-design.md +72 -17
  75. package/docs/system-architecture.md +18 -14
  76. package/examples/controls/15) window, observable SSE/server.js +6 -1
  77. package/examples/controls/19) window, auto observable ui/client.js +125 -0
  78. package/examples/controls/19) window, auto observable ui/server.js +73 -0
  79. package/examples/controls/20) window, task manager app/README.md +133 -0
  80. package/examples/controls/20) window, task manager app/client.js +797 -0
  81. package/examples/controls/20) window, task manager app/server.js +178 -0
  82. package/examples/controls/6) window, color_palette/client.js +165 -68
  83. package/examples/controls/9) window, date picker/client.js +362 -76
  84. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  85. package/examples/jsgui3-html/06) theming/client.js +22 -1
  86. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  87. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  88. package/lab/experiments/capture-color-controls.js +196 -0
  89. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  90. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  91. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  92. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  93. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  94. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  95. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  96. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  97. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  98. package/lab/screenshot-utils.js +248 -0
  99. package/module.js +11 -4
  100. package/package.json +14 -4
  101. package/publishers/Publishers.js +4 -3
  102. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  103. package/publishers/http-observable-publisher.js +8 -0
  104. package/publishers/http-sse-publisher.js +341 -0
  105. package/publishers/http-webpage-publisher.js +13 -3
  106. package/publishers/http-webpageorsite-publisher.js +18 -0
  107. package/resources/process-resource.js +950 -0
  108. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
  109. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  110. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  111. package/resources/remote-process-resource.js +355 -0
  112. package/resources/server-resource-pool.js +354 -41
  113. package/serve-factory.js +441 -259
  114. package/server.js +161 -16
  115. package/tests/README.md +66 -4
  116. package/tests/admin-ui-render.test.js +24 -0
  117. package/tests/assigners.test.js +56 -40
  118. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  119. package/tests/configuration-validation.test.js +21 -18
  120. package/tests/content-analysis.test.js +7 -6
  121. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  122. package/tests/control-scan-manifest-regression.test.js +144 -0
  123. package/tests/end-to-end.test.js +15 -14
  124. package/tests/error-handling.test.js +222 -179
  125. package/tests/fixtures/bundling-default-button-client.js +37 -0
  126. package/tests/fixtures/bundling-default-window-client.js +34 -0
  127. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  128. package/tests/fixtures/resource-monitor-client.js +319 -0
  129. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  130. package/tests/http-sse-publisher.test.js +136 -0
  131. package/tests/performance.test.js +69 -65
  132. package/tests/process-resource.test.js +138 -0
  133. package/tests/publishers.test.js +7 -7
  134. package/tests/remote-process-resource.test.js +160 -0
  135. package/tests/sass-controls.e2e.test.js +7 -1
  136. package/tests/serve-resources.test.js +270 -0
  137. package/tests/serve.test.js +120 -50
  138. package/tests/server-resource-pool.test.js +106 -0
  139. package/tests/small-controls-bundle-size.test.js +252 -0
  140. package/tests/test-runner.js +13 -1
  141. package/tests/window-examples.puppeteer.test.js +204 -1
  142. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  143. package/tests/temp_invalid.js +0 -7
  144. package/tests/temp_invalid_utf8.js +0 -1
  145. 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
 
@@ -75,6 +78,63 @@ class JSGUI_Single_Process_Server extends Evented_Class {
75
78
  });
76
79
  resource_pool.add(server_router);
77
80
  this.https_options = spec.https_options || undefined;
81
+
82
+ // Admin Module Setup
83
+ // Admin Module Setup
84
+ const Admin_Module = require('./admin-ui/server');
85
+ this.admin = new Admin_Module(this);
86
+ this.admin.attach_to_router(server_router);
87
+
88
+ // Register Admin Page Route
89
+ let Admin_Page_Control;
90
+ try {
91
+ console.log('DEBUG: Attempting to load Admin_Page_Control...');
92
+ Admin_Page_Control = require('./admin-ui/client').controls.Admin_Page;
93
+ console.log('DEBUG: Admin_Page_Control type:', typeof Admin_Page_Control);
94
+ } catch (e) {
95
+ console.warn('DEBUG: Failed to load Admin_Page_Control', e);
96
+ }
97
+
98
+ if (Admin_Page_Control) {
99
+ console.log('DEBUG: Creating Admin Webpage...');
100
+ const admin_app = new Webpage({
101
+ content: Admin_Page_Control,
102
+ title: 'jsgui3 Admin'
103
+ });
104
+
105
+ const HTTP_Webpage_Publisher = require('./publishers/http-webpage-publisher');
106
+ console.log('DEBUG: Creating Admin Publisher...');
107
+ const admin_publisher = new HTTP_Webpage_Publisher({
108
+ name: 'Admin_Publisher',
109
+ webpage: admin_app,
110
+ //src_path_client_js: require('path').join(__dirname, 'admin-ui/client.js')
111
+ });
112
+ // Fix for Resource_Pool.add(undefined) error
113
+ admin_publisher.name = 'Admin_Publisher';
114
+
115
+ admin_publisher.on('ready', (res_ready) => {
116
+ if (res_ready._arr) {
117
+ for (const bundle_item of res_ready._arr) {
118
+ const { route } = bundle_item;
119
+ const Static_Route_HTTP_Responder = require('./http/responders/static/Static_Route_HTTP_Responder');
120
+ const responder = new Static_Route_HTTP_Responder(bundle_item);
121
+ server_router.set_route(route, responder, responder.handle_http);
122
+ }
123
+ }
124
+ });
125
+
126
+ server_router.set_route('/admin', admin_publisher, admin_publisher.handle_http);
127
+ console.log('DEBUG: Adding admin_publisher to pool:', !!admin_publisher);
128
+ if (admin_publisher) {
129
+ resource_pool.add(admin_publisher);
130
+ } else {
131
+ console.error('DEBUG: admin_publisher is undefined!');
132
+ }
133
+ } else {
134
+ console.warn('Skipping /admin route registration due to missing control.');
135
+ }
136
+
137
+
78
138
  if (spec.routes) {
79
139
  throw 'NYI - will use Website class rather than Website_Resource here'
80
140
  each(spec.routes, (app_spec, route) => {
@@ -145,8 +205,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
145
205
  'name': 'Website_Resource - Single Webpage',
146
206
  'webpage': wp_app
147
207
  });
148
- resource_pool.add(ws_resource);
149
-
208
+ console.log('DEBUG: Adding ws_resource to pool:', !!ws_resource);
209
+ if (ws_resource) {
210
+ resource_pool.add(ws_resource);
211
+ } else {
212
+ console.error('DEBUG: ws_resource is undefined!');
213
+ }
150
214
  this.raise('ready');
151
215
  });
152
216
 
@@ -206,7 +270,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
206
270
  this.function_publishers = this.function_publishers || [];
207
271
  this.function_publishers.push(fpub);
208
272
 
209
- this.server_router.set_route('/api/' + name, fpub, fpub.handle_http);
273
+ // Auto-prefix /api/ for simple names
274
+ // If name already starts with '/', use as-is for full route control
275
+ const full_route = name.startsWith('/') ? name : '/api/' + name;
276
+ this.server_router.set_route(full_route, fpub, fpub.handle_http);
210
277
  }
211
278
 
212
279
  publish_observable(route, obs, options = {}) {
@@ -214,7 +281,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
214
281
  obs,
215
282
  ...options
216
283
  });
217
- this.server_router.set_route(route, publisher, publisher.handle_http);
284
+ // Auto-prefix /api/ for simple names (like publish() does)
285
+ // If route already starts with '/', use as-is for backward compatibility
286
+ const full_route = route.startsWith('/') ? route : '/api/' + route;
287
+ this.server_router.set_route(full_route, publisher, publisher.handle_http);
218
288
  return publisher;
219
289
  }
220
290
 
@@ -227,6 +297,14 @@ class JSGUI_Single_Process_Server extends Evented_Class {
227
297
  return this.resource_pool.resource_names;
228
298
  }
229
299
  'start'(port, callback, fnProcessRequest) {
300
+ // Guard against double-start which causes EADDRINUSE
301
+ if (this._started) {
302
+ console.warn('Server.start() called but server already started. Ignoring duplicate call.');
303
+ if (callback) callback(null, true);
304
+ return;
305
+ }
306
+ this._started = true;
307
+
230
308
  if (tof(port) !== 'number') {
231
309
  console.log('Invalid port:', port);
232
310
  console.trace();
@@ -274,7 +352,7 @@ class JSGUI_Single_Process_Server extends Evented_Class {
274
352
  if (started_count > 0) {
275
353
  if (!ready_raised) {
276
354
  console.log('Server ready');
277
- this.raise('ready');
355
+ this.raise('listening'); // Changed from 'ready' to avoid double-fire
278
356
  ready_raised = true;
279
357
  }
280
358
  if (callback) callback(null, true);
@@ -409,19 +487,81 @@ class JSGUI_Single_Process_Server extends Evented_Class {
409
487
  }
410
488
 
411
489
  close(callback) {
412
- let count = this.http_servers.length;
413
- if (count === 0) {
414
- if (callback) process.nextTick(callback);
415
- return;
416
- }
417
- this.http_servers.forEach(server => {
418
- server.close(() => {
419
- count--;
420
- if (count === 0) {
421
- this.http_servers = [];
422
- if (callback) callback();
490
+ const invoke_stop = (target, done) => {
491
+ if (!target || typeof target.stop !== 'function') {
492
+ done(null);
493
+ return;
494
+ }
495
+
496
+ if (target.stop.length >= 1) {
497
+ target.stop((error) => done(error || null));
498
+ return;
499
+ }
500
+
501
+ try {
502
+ const stop_result = target.stop();
503
+ if (stop_result && typeof stop_result.then === 'function') {
504
+ stop_result.then(() => done(null), (error) => done(error || null));
505
+ return;
423
506
  }
507
+ done(null);
508
+ } catch (error) {
509
+ done(error);
510
+ }
511
+ };
512
+
513
+ const close_http_servers = (done) => {
514
+ let count = this.http_servers.length;
515
+ if (count === 0) {
516
+ this.http_servers = [];
517
+ done();
518
+ return;
519
+ }
520
+
521
+ this.http_servers.forEach(server => {
522
+ server.close(() => {
523
+ count--;
524
+ if (count === 0) {
525
+ this.http_servers = [];
526
+ done();
527
+ }
528
+ });
424
529
  });
530
+ };
531
+
532
+ const finalize_close = (error) => {
533
+ if (callback) callback(error || null);
534
+ };
535
+
536
+ const stop_targets = [];
537
+ if (this.resource_pool) {
538
+ stop_targets.push(this.resource_pool);
539
+ }
540
+ if (this.sse_publisher) {
541
+ stop_targets.push(this.sse_publisher);
542
+ }
543
+
544
+ if (stop_targets.length === 0) {
545
+ close_http_servers(() => finalize_close(null));
546
+ return;
547
+ }
548
+
549
+ let pending_stops = stop_targets.length;
550
+ const stop_errors = [];
551
+ const on_stop_complete = (error) => {
552
+ if (error) {
553
+ stop_errors.push(error);
554
+ }
555
+ pending_stops--;
556
+ if (pending_stops > 0) {
557
+ return;
558
+ }
559
+ const first_error = stop_errors.length > 0 ? stop_errors[0] : null;
560
+ close_http_servers(() => finalize_close(first_error));
561
+ };
562
+
563
+ stop_targets.forEach((target) => {
564
+ invoke_stop(target, on_stop_complete);
425
565
  });
426
566
  }
427
567
  }
@@ -429,10 +569,15 @@ class JSGUI_Single_Process_Server extends Evented_Class {
429
569
  JSGUI_Single_Process_Server.jsgui = jsgui;
430
570
 
431
571
  JSGUI_Single_Process_Server.Resource = Resource;
572
+ JSGUI_Single_Process_Server.Resource.Process = Process_Resource;
573
+ JSGUI_Single_Process_Server.Resource.Remote_Process = Remote_Process_Resource;
432
574
  JSGUI_Single_Process_Server.Page_Context = Server_Page_Context;
433
575
  JSGUI_Single_Process_Server.Server_Page_Context = Server_Page_Context;
434
576
  JSGUI_Single_Process_Server.Website_Resource = Website_Resource;
435
577
  JSGUI_Single_Process_Server.Publishers = Publishers;
578
+ JSGUI_Single_Process_Server.Process_Resource = Process_Resource;
579
+ JSGUI_Single_Process_Server.Remote_Process_Resource = Remote_Process_Resource;
580
+ JSGUI_Single_Process_Server.HTTP_SSE_Publisher = HTTP_SSE_Publisher;
436
581
  JSGUI_Single_Process_Server.serve = require('./serve-factory')(JSGUI_Single_Process_Server);
437
582
 
438
583
  module.exports = JSGUI_Single_Process_Server;
package/tests/README.md CHANGED
@@ -19,16 +19,27 @@ 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
+ ├── serve.test.js # Server.serve core behavior tests
26
+ ├── serve-resources.test.js # Server.serve + resource integration tests
27
+ ├── process-resource.test.js # Process_Resource lifecycle and restart tests
28
+ ├── remote-process-resource.test.js # Remote_Process_Resource polling/recovery tests
29
+ ├── server-resource-pool.test.js # Resource pool lifecycle and event forwarding tests
30
+ ├── http-sse-publisher.test.js # HTTP_SSE_Publisher protocol/lifecycle tests
31
+ ├── end-to-end.test.js # Full integration tests
25
32
  ├── content-analysis.test.js # Content analysis and verification
26
33
  ├── performance.test.js # Performance benchmarks
27
34
  ├── error-handling.test.js # Error handling and edge cases
35
+ ├── control-optimizer-cache-behavior.test.js # Optimizer cache enable/disable behavior
28
36
  ├── examples-controls.e2e.test.js # Example apps regression (controls)
29
37
  ├── sass-controls.e2e.test.js # Sass/CSS controls E2E coverage
30
38
  ├── jsgui3-html-examples.puppeteer.test.js # Puppeteer interaction tests (jsgui3-html examples)
39
+ ├── bundling-default-control-elimination.puppeteer.test.js # Puppeteer: default control elimination bundle checks
31
40
  ├── window-examples.puppeteer.test.js # Puppeteer interaction tests (window examples)
41
+ ├── window-resource-integration.puppeteer.test.js # Browser E2E: controls + resource APIs + SSE
42
+ ├── helpers/puppeteer-e2e-harness.js # Shared Puppeteer story runner + probes
32
43
  ├── test-runner.js # Custom test runner with reporting
33
44
  └── README.md # This file
34
45
  ```
@@ -45,6 +56,9 @@ npm test
45
56
  # Using the custom test runner
46
57
  node tests/test-runner.js --test=bundlers.test.js
47
58
 
59
+ # Optimizer cache behavior
60
+ node tests/test-runner.js --test=control-optimizer-cache-behavior.test.js
61
+
48
62
  # Using mocha directly
49
63
  npx mocha tests/bundlers.test.js
50
64
  ```
@@ -59,6 +73,16 @@ npm run test:examples:controls
59
73
  npm run test:puppeteer:windows
60
74
  ```
61
75
 
76
+ ### Run Puppeteer Bundling Elimination Tests
77
+ ```bash
78
+ npm run test:puppeteer:bundling
79
+ ```
80
+
81
+ ### Run Puppeteer Resource Integration Tests
82
+ ```bash
83
+ npm run test:puppeteer:resources
84
+ ```
85
+
62
86
  ### Run Tests with Options
63
87
  ```bash
64
88
  # Debug mode (enables sourcemaps)
@@ -76,16 +100,35 @@ node tests/test-runner.js --test=end-to-end.test.js --debug
76
100
  1. Run a focused suite first (fast feedback).
77
101
  2. Run the example regression suite next (HTML/CSS/JS smoke checks).
78
102
  3. Run Puppeteer interaction tests last (heavier, requires a browser).
79
- 4. Run the full suite only when changes are broad or before release.
103
+ 4. Run the resource integration Puppeteer suite when changing resources/SSE APIs.
104
+ 5. Run the full suite only when changes are broad or before release.
80
105
 
81
106
  Suggested sequence:
82
107
  ```bash
83
108
  node tests/test-runner.js --test=bundlers.test.js
84
109
  npm run test:examples:controls
110
+ npm run test:puppeteer:bundling
85
111
  npm run test:puppeteer:windows
112
+ npm run test:puppeteer:resources
86
113
  npm test
87
114
  ```
88
115
 
116
+ ## Advanced Puppeteer E2E Methodology
117
+
118
+ For high-value interaction and integration coverage, use the shared harness:
119
+
120
+ - `tests/helpers/puppeteer-e2e-harness.js`
121
+
122
+ Key patterns:
123
+
124
+ - Write deterministic interaction stories with `run_interaction_story(...)` and named steps.
125
+ - Add browser probes (`console`, `pageerror`, `requestfailed`) and assert they stay clean.
126
+ - Assert both UI state and server truth for integration cases.
127
+ - For resource flows, validate both:
128
+ - API actions (`/api/resource/*`) and
129
+ - SSE propagation (`/events`) reflected in client UI/debug state.
130
+ - Keep selectors stable (`id` or `data-test`) so interaction tests remain robust.
131
+
89
132
  ## Patterns That Work
90
133
 
91
134
  - Use per-test temporary client files and delete them in `finally`.
@@ -202,6 +245,25 @@ Browser-level interaction checks for jsgui3-html examples:
202
245
 
203
246
  - MVVM counter interactions, bindings, and validation state
204
247
 
248
+ ### 11. Window Resource Integration Puppeteer Tests (`window-resource-integration.puppeteer.test.js`)
249
+
250
+ Browser-level interaction checks that combine controls and server resources:
251
+
252
+ - Step-driven control interactions (date + datetime controls)
253
+ - Client actions invoking resource lifecycle APIs (`start`, `stop`, `restart`)
254
+ - SSE resource state events reflected in client UI
255
+ - Cross-checking client-observed state with server resource pool state
256
+
257
+ ### 12. Core Resource and Serve Reliability Tests
258
+
259
+ - `admin-ui-render.test.js` validates the admin page control renders without clobbering control internals
260
+ - `serve.test.js` validates `Server.serve` startup/route readiness behavior
261
+ - `serve-resources.test.js` validates in-process and process resource wiring in serve mode
262
+ - `process-resource.test.js` validates direct process lifecycle + crash restart handling
263
+ - `remote-process-resource.test.js` validates polling, unreachable, and recovered transitions
264
+ - `server-resource-pool.test.js` validates pool forwarding, remove/stop, and summaries
265
+ - `http-sse-publisher.test.js` validates SSE broadcast/send/replay/keepalive semantics
266
+
205
267
  ## Configuration Examples
206
268
 
207
269
  ### Basic Minification
@@ -0,0 +1,24 @@
1
+ const assert = require('assert');
2
+ const { describe, it } = require('mocha');
3
+ const { Page_Context } = require('jsgui3-html');
4
+
5
+ const admin_ui = require('../admin-ui/client');
6
+
7
+ describe('Admin UI rendering', function() {
8
+ it('renders Admin_Page without overriding core content collection', () => {
9
+ const Admin_Page = admin_ui.controls && admin_ui.controls.Admin_Page;
10
+ assert.strictEqual(typeof Admin_Page, 'function', 'Expected Admin_Page control export');
11
+
12
+ const page_context = new Page_Context({});
13
+ const admin_page = new Admin_Page({
14
+ context: page_context
15
+ });
16
+
17
+ assert(admin_page.content && typeof admin_page.content.length === 'function', 'Expected control content collection to remain intact');
18
+
19
+ const html_output = admin_page.all_html_render();
20
+ assert.strictEqual(typeof html_output, 'string');
21
+ assert(html_output.includes('admin-content'));
22
+ assert(html_output.includes('jsgui3 Admin'));
23
+ });
24
+ });
@@ -39,8 +39,12 @@ describe('Assigner Component Isolation Tests', function() {
39
39
  });
40
40
 
41
41
  describe('Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner', function() {
42
- it('should compress content with default gzip settings', async function() {
43
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
42
+ it('should compress content with default gzip settings', async function() {
43
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
44
+ compression: {
45
+ threshold: 0
46
+ }
47
+ });
44
48
 
45
49
  await assigner.assign(mockBundleItems);
46
50
 
@@ -56,14 +60,18 @@ describe('Assigner Component Isolation Tests', function() {
56
60
  }
57
61
  });
58
62
 
59
- // Verify compression statistics
60
- assert.strictEqual(assigner.compression_stats.total_items, 3, 'Should track 3 total items');
61
- assert.strictEqual(assigner.compression_stats.gzip_compressed, 3, 'Should have compressed 3 items with gzip');
62
- assert(assigner.compression_stats.gzip_savings >= 0, 'Gzip savings should be non-negative');
63
+ // Verify compression statistics
64
+ assert.strictEqual(assigner.compression_stats.total_items, 3, 'Should track 3 total items');
65
+ assert.strictEqual(assigner.compression_stats.gzip_compressed, 3, 'Should have compressed 3 items with gzip');
66
+ assert.strictEqual(typeof assigner.compression_stats.gzip_savings, 'number', 'Gzip savings should be numeric');
63
67
  });
64
68
 
65
- it('should compress content with default brotli settings', async function() {
66
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
69
+ it('should compress content with default brotli settings', async function() {
70
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
71
+ compression: {
72
+ threshold: 0
73
+ }
74
+ });
67
75
 
68
76
  await assigner.assign(mockBundleItems);
69
77
 
@@ -79,18 +87,19 @@ describe('Assigner Component Isolation Tests', function() {
79
87
  }
80
88
  });
81
89
 
82
- // Verify compression statistics
83
- assert.strictEqual(assigner.compression_stats.brotli_compressed, 3, 'Should have compressed 3 items with brotli');
84
- assert(assigner.compression_stats.brotli_savings >= 0, 'Brotli savings should be non-negative');
90
+ // Verify compression statistics
91
+ assert.strictEqual(assigner.compression_stats.brotli_compressed, 3, 'Should have compressed 3 items with brotli');
92
+ assert.strictEqual(typeof assigner.compression_stats.brotli_savings, 'number', 'Brotli savings should be numeric');
85
93
  });
86
94
 
87
95
  it('should respect compression configuration - gzip only', async function() {
88
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
89
- compression: {
90
- enabled: true,
91
- algorithms: ['gzip']
92
- }
93
- });
96
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
97
+ compression: {
98
+ enabled: true,
99
+ algorithms: ['gzip'],
100
+ threshold: 0
101
+ }
102
+ });
94
103
 
95
104
  await assigner.assign(mockBundleItems);
96
105
 
@@ -105,12 +114,13 @@ describe('Assigner Component Isolation Tests', function() {
105
114
  });
106
115
 
107
116
  it('should respect compression configuration - brotli only', async function() {
108
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
109
- compression: {
110
- enabled: true,
111
- algorithms: ['br']
112
- }
113
- });
117
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
118
+ compression: {
119
+ enabled: true,
120
+ algorithms: ['br'],
121
+ threshold: 0
122
+ }
123
+ });
114
124
 
115
125
  await assigner.assign(mockBundleItems);
116
126
 
@@ -125,13 +135,14 @@ describe('Assigner Component Isolation Tests', function() {
125
135
  });
126
136
 
127
137
  it('should respect compression configuration - custom gzip level', async function() {
128
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
129
- compression: {
130
- enabled: true,
131
- algorithms: ['gzip'],
132
- gzip: { level: 1 } // Fastest compression
133
- }
134
- });
138
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
139
+ compression: {
140
+ enabled: true,
141
+ algorithms: ['gzip'],
142
+ gzip: { level: 1 }, // Fastest compression
143
+ threshold: 0
144
+ }
145
+ });
135
146
 
136
147
  await assigner.assign(mockBundleItems);
137
148
 
@@ -143,13 +154,14 @@ describe('Assigner Component Isolation Tests', function() {
143
154
  });
144
155
 
145
156
  it('should respect compression configuration - custom brotli quality', async function() {
146
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
147
- compression: {
148
- enabled: true,
149
- algorithms: ['br'],
150
- brotli: { quality: 1 } // Lowest quality (fastest)
151
- }
152
- });
157
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
158
+ compression: {
159
+ enabled: true,
160
+ algorithms: ['br'],
161
+ brotli: { quality: 1 }, // Lowest quality (fastest)
162
+ threshold: 0
163
+ }
164
+ });
153
165
 
154
166
  await assigner.assign(mockBundleItems);
155
167
 
@@ -288,8 +300,12 @@ describe('Assigner Component Isolation Tests', function() {
288
300
  });
289
301
 
290
302
  describe('Compression Statistics', function() {
291
- it('should track compression statistics accurately', async function() {
292
- const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
303
+ it('should track compression statistics accurately', async function() {
304
+ const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
305
+ compression: {
306
+ threshold: 0
307
+ }
308
+ });
293
309
 
294
310
  await assigner.assign(mockBundleItems);
295
311
 
@@ -313,4 +329,4 @@ describe('Assigner Component Isolation Tests', function() {
313
329
  assert.strictEqual(assigner.compression_stats.total_items, 2);
314
330
  });
315
331
  });
316
- });
332
+ });