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.
- package/.github/workflows/control-scan-manifest-check.yml +31 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
- package/README.md +85 -3
- package/admin-ui/client.js +213 -0
- package/admin-ui/server.js +104 -0
- package/client/controls/auto-observable.js +207 -0
- package/dev-status.svg +139 -0
- package/docs/api-reference.md +301 -43
- package/docs/books/admin-ui/01-introduction.md +32 -0
- package/docs/books/admin-ui/02-architecture.md +92 -0
- package/docs/books/admin-ui/03-controls.md +194 -0
- package/docs/books/admin-ui/04-implementation-plan.md +62 -0
- package/docs/books/admin-ui/README.md +26 -0
- package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
- package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
- package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
- package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
- package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
- package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
- package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
- package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
- package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
- package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
- package/docs/bundling-system-deep-dive.md +9 -4
- package/docs/comprehensive-documentation.md +49 -18
- package/docs/configuration-reference.md +152 -27
- package/docs/core/README.md +19 -0
- package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
- package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
- package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
- package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
- package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
- package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
- package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
- package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
- package/docs/publishers-guide.md +59 -4
- package/docs/resources-guide.md +184 -35
- package/docs/simple-server-api-design.md +72 -17
- package/docs/system-architecture.md +18 -14
- package/examples/controls/15) window, observable SSE/server.js +6 -1
- package/examples/controls/19) window, auto observable ui/client.js +125 -0
- package/examples/controls/19) window, auto observable ui/server.js +73 -0
- package/examples/controls/20) window, task manager app/README.md +133 -0
- package/examples/controls/20) window, task manager app/client.js +797 -0
- package/examples/controls/20) window, task manager app/server.js +178 -0
- package/examples/controls/6) window, color_palette/client.js +165 -68
- package/examples/controls/9) window, date picker/client.js +362 -76
- package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
- package/examples/jsgui3-html/06) theming/client.js +22 -1
- package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
- package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
- package/lab/experiments/capture-color-controls.js +196 -0
- package/lab/results/screenshots/color-controls/full_page.png +0 -0
- package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
- package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
- package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
- package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
- package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
- package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
- package/lab/screenshot-utils.js +248 -0
- package/module.js +11 -4
- package/package.json +14 -4
- package/publishers/Publishers.js +4 -3
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
- package/publishers/http-observable-publisher.js +8 -0
- package/publishers/http-sse-publisher.js +341 -0
- package/publishers/http-webpage-publisher.js +13 -3
- package/publishers/http-webpageorsite-publisher.js +18 -0
- package/resources/process-resource.js +950 -0
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
- package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
- package/resources/remote-process-resource.js +355 -0
- package/resources/server-resource-pool.js +354 -41
- package/serve-factory.js +441 -259
- package/server.js +161 -16
- package/tests/README.md +66 -4
- package/tests/admin-ui-render.test.js +24 -0
- package/tests/assigners.test.js +56 -40
- package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
- package/tests/configuration-validation.test.js +21 -18
- package/tests/content-analysis.test.js +7 -6
- package/tests/control-optimizer-cache-behavior.test.js +52 -0
- package/tests/control-scan-manifest-regression.test.js +144 -0
- package/tests/end-to-end.test.js +15 -14
- package/tests/error-handling.test.js +222 -179
- package/tests/fixtures/bundling-default-button-client.js +37 -0
- package/tests/fixtures/bundling-default-window-client.js +34 -0
- package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
- package/tests/fixtures/resource-monitor-client.js +319 -0
- package/tests/helpers/puppeteer-e2e-harness.js +317 -0
- package/tests/http-sse-publisher.test.js +136 -0
- package/tests/performance.test.js +69 -65
- package/tests/process-resource.test.js +138 -0
- package/tests/publishers.test.js +7 -7
- package/tests/remote-process-resource.test.js +160 -0
- package/tests/sass-controls.e2e.test.js +7 -1
- package/tests/serve-resources.test.js +270 -0
- package/tests/serve.test.js +120 -50
- package/tests/server-resource-pool.test.js +106 -0
- package/tests/small-controls-bundle-size.test.js +252 -0
- package/tests/test-runner.js +13 -1
- package/tests/window-examples.puppeteer.test.js +204 -1
- package/tests/window-resource-integration.puppeteer.test.js +585 -0
- package/tests/temp_invalid.js +0 -7
- package/tests/temp_invalid_utf8.js +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
├──
|
|
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
|
|
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
|
+
});
|
package/tests/assigners.test.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
+
});
|