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
@@ -0,0 +1,260 @@
1
+ const assert = require('assert');
2
+ const path = require('path');
3
+ const { describe, it, before, after } = require('mocha');
4
+
5
+ const Server = require('../server');
6
+ const { get_free_port } = require('../port-utils');
7
+ const {
8
+ ensure_puppeteer_module,
9
+ launch_puppeteer_browser,
10
+ open_page,
11
+ stop_server_instance,
12
+ assert_clean_page_probe
13
+ } = require('./helpers/puppeteer-e2e-harness');
14
+
15
+ const button_fixture_client_path = path.join(__dirname, 'fixtures', 'bundling-default-button-client.js');
16
+ const window_fixture_client_path = path.join(__dirname, 'fixtures', 'bundling-default-window-client.js');
17
+
18
+ const window_markers = [
19
+ 'Minimize window',
20
+ 'window_manager',
21
+ '__type_name = "window"',
22
+ "__type_name = 'window'",
23
+ "__type_name='window'",
24
+ 'add_class("window")',
25
+ "add_class('window')"
26
+ ];
27
+
28
+ const find_window_markers = (bundle_text = '') => {
29
+ return window_markers.filter((marker) => bundle_text.includes(marker));
30
+ };
31
+
32
+ const read_js_bundle_from_page = async (page) => {
33
+ return page.evaluate(async () => {
34
+ const response = await fetch('/js/js.js', { cache: 'no-store' });
35
+ const text = await response.text();
36
+ return {
37
+ status_code: response.status,
38
+ body_text: text
39
+ };
40
+ });
41
+ };
42
+
43
+ const load_fixture_ctrl = (client_path, ctrl_name) => {
44
+ const resolved_client_path = require.resolve(client_path);
45
+ delete require.cache[resolved_client_path];
46
+
47
+ const fixture_module = require(resolved_client_path);
48
+ const ctrl_constructor = fixture_module.controls && fixture_module.controls[ctrl_name];
49
+ assert(ctrl_constructor, `Missing exported control jsgui.controls.${ctrl_name} in ${client_path}`);
50
+ return ctrl_constructor;
51
+ };
52
+
53
+ const start_fixture_server = async ({ client_path, ctrl_name, bundler }) => {
54
+ const ctrl_constructor = load_fixture_ctrl(client_path, ctrl_name);
55
+
56
+ const server_instance = new Server({
57
+ Ctrl: ctrl_constructor,
58
+ src_path_client_js: client_path,
59
+ name: `tests/bundling/${ctrl_name}`,
60
+ bundler
61
+ });
62
+
63
+ server_instance.allowed_addresses = ['127.0.0.1'];
64
+
65
+ await new Promise((resolve, reject) => {
66
+ const timeout_handle = setTimeout(() => reject(new Error('Publisher ready timeout')), 60000);
67
+ server_instance.on('ready', () => {
68
+ clearTimeout(timeout_handle);
69
+ resolve();
70
+ });
71
+ });
72
+
73
+ const port = await get_free_port();
74
+ await new Promise((resolve, reject) => {
75
+ server_instance.start(port, (error) => {
76
+ if (error) reject(error);
77
+ else resolve();
78
+ });
79
+ });
80
+
81
+ return {
82
+ server_instance,
83
+ port
84
+ };
85
+ };
86
+
87
+ const close_page_with_probe = async (page, page_probe) => {
88
+ if (page_probe && typeof page_probe.detach === 'function') {
89
+ page_probe.detach();
90
+ }
91
+ if (page) {
92
+ await page.close();
93
+ }
94
+ };
95
+
96
+ const stop_server_instance_with_timeout = async (server_instance, timeout_ms = 12000) => {
97
+ if (!server_instance) {
98
+ return;
99
+ }
100
+
101
+ await Promise.race([
102
+ stop_server_instance(server_instance),
103
+ new Promise((resolve) => setTimeout(resolve, timeout_ms))
104
+ ]);
105
+ };
106
+
107
+ describe('Bundling Default Control Elimination Puppeteer Tests', function () {
108
+ this.timeout(420000);
109
+
110
+ let puppeteer_module = null;
111
+ let browser_instance = null;
112
+
113
+ before(async function () {
114
+ this.timeout(60000);
115
+
116
+ puppeteer_module = ensure_puppeteer_module();
117
+ if (!puppeteer_module) {
118
+ this.skip();
119
+ return;
120
+ }
121
+
122
+ try {
123
+ browser_instance = await launch_puppeteer_browser(puppeteer_module);
124
+ } catch {
125
+ this.skip();
126
+ }
127
+ });
128
+
129
+ after(async function () {
130
+ if (browser_instance) {
131
+ await browser_instance.close();
132
+ browser_instance = null;
133
+ }
134
+ });
135
+
136
+ it('removes unused Window code by default and restores it when elimination is disabled', async function () {
137
+ this.timeout(360000);
138
+ let default_server_instance = null;
139
+ let disabled_server_instance = null;
140
+ let default_page = null;
141
+ let disabled_page = null;
142
+ let default_page_probe = null;
143
+ let disabled_page_probe = null;
144
+
145
+ try {
146
+ const default_server = await start_fixture_server({
147
+ client_path: button_fixture_client_path,
148
+ ctrl_name: 'Bundling_Default_Button_App'
149
+ });
150
+ default_server_instance = default_server.server_instance;
151
+
152
+ const default_open_result = await open_page(
153
+ browser_instance,
154
+ `http://127.0.0.1:${default_server.port}/`,
155
+ { wait_until: 'domcontentloaded' }
156
+ );
157
+ default_page = default_open_result.page;
158
+ default_page_probe = default_open_result.page_probe;
159
+
160
+ await default_page.waitForSelector('[data-test="bundle-test-button"]');
161
+
162
+ const default_bundle_response = await read_js_bundle_from_page(default_page);
163
+ assert.strictEqual(default_bundle_response.status_code, 200, 'Expected /js/js.js to load in default mode');
164
+ const default_markers = find_window_markers(default_bundle_response.body_text);
165
+ assert.strictEqual(
166
+ default_markers.length,
167
+ 0,
168
+ `Unexpected Window markers in default bundle: ${default_markers.join(', ')}`
169
+ );
170
+
171
+ assert_clean_page_probe(default_page_probe);
172
+
173
+ await close_page_with_probe(default_page, default_page_probe);
174
+ default_page = null;
175
+ default_page_probe = null;
176
+
177
+ const disabled_server = await start_fixture_server({
178
+ client_path: button_fixture_client_path,
179
+ ctrl_name: 'Bundling_Default_Button_App',
180
+ bundler: {
181
+ elimination: {
182
+ enabled: false,
183
+ jsgui3_html_controls: {
184
+ enabled: false
185
+ }
186
+ }
187
+ }
188
+ });
189
+ disabled_server_instance = disabled_server.server_instance;
190
+
191
+ const disabled_open_result = await open_page(
192
+ browser_instance,
193
+ `http://127.0.0.1:${disabled_server.port}/`,
194
+ { wait_until: 'domcontentloaded' }
195
+ );
196
+ disabled_page = disabled_open_result.page;
197
+ disabled_page_probe = disabled_open_result.page_probe;
198
+
199
+ await disabled_page.waitForSelector('[data-test="bundle-test-button"]');
200
+
201
+ const disabled_bundle_response = await read_js_bundle_from_page(disabled_page);
202
+ assert.strictEqual(disabled_bundle_response.status_code, 200, 'Expected /js/js.js to load in disabled mode');
203
+ const disabled_markers = find_window_markers(disabled_bundle_response.body_text);
204
+ assert(
205
+ disabled_markers.length > 0,
206
+ 'Expected Window markers when elimination is explicitly disabled'
207
+ );
208
+
209
+ assert(
210
+ default_bundle_response.body_text.length < disabled_bundle_response.body_text.length,
211
+ 'Expected default bundle to be smaller than elimination-disabled bundle'
212
+ );
213
+
214
+ assert_clean_page_probe(disabled_page_probe);
215
+ } finally {
216
+ await close_page_with_probe(default_page, default_page_probe);
217
+ await close_page_with_probe(disabled_page, disabled_page_probe);
218
+ await stop_server_instance_with_timeout(default_server_instance);
219
+ await stop_server_instance_with_timeout(disabled_server_instance);
220
+ }
221
+ });
222
+
223
+ it('keeps Window code in the default bundle when Window control is used', async () => {
224
+ let server_instance = null;
225
+ let page = null;
226
+ let page_probe = null;
227
+
228
+ try {
229
+ const started_server = await start_fixture_server({
230
+ client_path: window_fixture_client_path,
231
+ ctrl_name: 'Bundling_Default_Window_App'
232
+ });
233
+ server_instance = started_server.server_instance;
234
+
235
+ const open_result = await open_page(
236
+ browser_instance,
237
+ `http://127.0.0.1:${started_server.port}/`,
238
+ { wait_until: 'domcontentloaded' }
239
+ );
240
+ page = open_result.page;
241
+ page_probe = open_result.page_probe;
242
+
243
+ await page.waitForSelector('.bundle-test-window');
244
+ await page.waitForSelector('.bundle-test-window-content');
245
+
246
+ const window_buttons = await page.$$('.bundle-test-window .title.bar button.button');
247
+ assert(window_buttons.length >= 2, 'Expected window title bar controls to be rendered');
248
+
249
+ const bundle_response = await read_js_bundle_from_page(page);
250
+ assert.strictEqual(bundle_response.status_code, 200, 'Expected /js/js.js to load');
251
+ const markers = find_window_markers(bundle_response.body_text);
252
+ assert(markers.length > 0, 'Expected Window markers when Window control is used');
253
+
254
+ assert_clean_page_probe(page_probe);
255
+ } finally {
256
+ await close_page_with_probe(page, page_probe);
257
+ await stop_server_instance_with_timeout(server_instance);
258
+ }
259
+ });
260
+ });
@@ -309,23 +309,26 @@ describe('Configuration Validation Tests', function() {
309
309
  assert.strictEqual(bundler.minify_config.enabled, false);
310
310
  });
311
311
 
312
- it('should default to enabled minification', function() {
313
- const bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild({
314
- minify: {
315
- level: 'aggressive'
316
- }
317
- });
318
-
319
- assert.strictEqual(bundler.minify_config.enabled, true);
320
- assert.strictEqual(bundler.minify_config.level, 'aggressive');
321
- });
322
-
323
- it('should handle missing minify configuration', function() {
324
- const bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild({});
325
-
326
- assert.deepStrictEqual(bundler.minify_config, {});
327
- });
328
- });
312
+ it('should default to enabled minification', function() {
313
+ const bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild({
314
+ minify: {
315
+ level: 'aggressive'
316
+ }
317
+ });
318
+
319
+ assert.strictEqual(bundler.minify_config.level, 'aggressive');
320
+ assert.notStrictEqual(bundler.get_minify_options(), false);
321
+ });
322
+
323
+ it('should handle missing minify configuration', function() {
324
+ const bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild({});
325
+
326
+ assert.deepStrictEqual(bundler.minify_config, {
327
+ enabled: true,
328
+ level: 'normal'
329
+ });
330
+ });
331
+ });
329
332
 
330
333
  describe('Core_JS_Non_Minifying_Bundler_Using_ESBuild', function() {
331
334
  it('should accept valid sourcemap configuration', function() {
@@ -527,4 +530,4 @@ describe('Configuration Validation Tests', function() {
527
530
  assert.deepStrictEqual(minimalPublisher.bundler_config, {});
528
531
  });
529
532
  });
530
- });
533
+ });
@@ -571,12 +571,13 @@ describe('Content Analysis Tests', function() {
571
571
  })
572
572
  };
573
573
 
574
- const results = {};
575
-
576
- for (const [name, bundler] of Object.entries(bundlers)) {
577
- const startTime = Date.now();
578
- const result = await bundler.bundle(testJsFile);
579
- const endTime = Date.now();
574
+ const results = {};
575
+
576
+ for (const [name, bundler] of Object.entries(bundlers)) {
577
+ const startTime = Date.now();
578
+ const bundle_input = name === 'minifying' ? testJsContent : testJsFile;
579
+ const result = await bundler.bundle(bundle_input);
580
+ const endTime = Date.now();
580
581
 
581
582
  const bundle = result[0];
582
583
  const jsItem = bundle._arr.find(item => item.type === 'JavaScript');
@@ -0,0 +1,52 @@
1
+ const assert = require('assert');
2
+ const path = require('path');
3
+ const { describe, it } = require('mocha');
4
+
5
+ const JSGUI3_HTML_Control_Optimizer = require('../resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer');
6
+
7
+ const entry_file_path = path.join(__dirname, 'fixtures', 'bundling-default-button-client.js');
8
+
9
+ describe('Control Optimizer Cache Behavior Tests', function () {
10
+ this.timeout(60000);
11
+
12
+ it('records cache hits on repeated optimize calls when cache is enabled', async function () {
13
+ const optimizer = new JSGUI3_HTML_Control_Optimizer({
14
+ package_name: 'jsgui3-html',
15
+ cacheEnabled: true,
16
+ sharedCache: false
17
+ });
18
+
19
+ const first_result = await optimizer.optimize(entry_file_path);
20
+ const second_result = await optimizer.optimize(entry_file_path);
21
+
22
+ assert.strictEqual(first_result.enabled, true);
23
+ assert.strictEqual(second_result.enabled, true);
24
+ assert(first_result.manifest.selected_controls.includes('Button'));
25
+
26
+ const cache_stats = optimizer.cache_stats;
27
+ assert(cache_stats.entry_analysis_misses >= 1, 'Expected at least one entry analysis cache miss');
28
+ assert(cache_stats.entry_analysis_hits >= 1, 'Expected at least one entry analysis cache hit');
29
+ assert(cache_stats.controls_map_hits >= 1, 'Expected cached controls map reuse');
30
+ });
31
+
32
+ it('avoids cache hits when cache is disabled', async function () {
33
+ const optimizer = new JSGUI3_HTML_Control_Optimizer({
34
+ package_name: 'jsgui3-html',
35
+ cacheEnabled: false,
36
+ sharedCache: false
37
+ });
38
+
39
+ const first_result = await optimizer.optimize(entry_file_path);
40
+ const second_result = await optimizer.optimize(entry_file_path);
41
+
42
+ assert.strictEqual(first_result.enabled, true);
43
+ assert.strictEqual(second_result.enabled, true);
44
+
45
+ const cache_stats = optimizer.cache_stats;
46
+ assert.strictEqual(cache_stats.entry_analysis_hits, 0, 'Expected no entry analysis cache hits');
47
+ assert.strictEqual(cache_stats.file_scan_hits, 0, 'Expected no file scan cache hits');
48
+ assert.strictEqual(cache_stats.controls_map_hits, 0, 'Expected no controls map cache hits');
49
+ assert(cache_stats.file_scan_misses >= 2, 'Expected repeated uncached file scans');
50
+ assert(cache_stats.controls_map_misses >= 2, 'Expected repeated uncached controls map reads');
51
+ });
52
+ });
@@ -0,0 +1,144 @@
1
+ const assert = require('assert');
2
+ const { describe, it, before, after } = require('mocha');
3
+ const fs = require('fs').promises;
4
+ const path = require('path');
5
+
6
+ const Advanced_JS_Bundler_Using_ESBuild = require('../resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild');
7
+
8
+ const fixture_file_path = path.join(__dirname, 'fixtures', 'control_scan_manifest_expectations.json');
9
+ const update_snapshots = process.env.UPDATE_CONTROL_SCAN_MANIFEST === '1';
10
+
11
+ const normalize_manifest = (manifest) => {
12
+ const safe_array = (value) => Array.isArray(value) ? Array.from(value).sort() : [];
13
+ const safe_paths = (value) => Array.isArray(value) ? value.map(file_path => path.basename(file_path)).sort() : [];
14
+
15
+ return {
16
+ entry_file: manifest && manifest.entry_file_path ? path.basename(manifest.entry_file_path) : null,
17
+ uses_jsgui3_html: Boolean(manifest && manifest.uses_jsgui3_html),
18
+ dynamic_control_access_detected: Boolean(manifest && manifest.dynamic_control_access_detected),
19
+ reachable_files: safe_paths(manifest && manifest.reachable_files),
20
+ used_identifiers: safe_array(manifest && manifest.used_identifiers),
21
+ selected_controls: safe_array(manifest && manifest.selected_controls),
22
+ unmatched_identifiers: safe_array(manifest && manifest.unmatched_identifiers),
23
+ package_aliases: safe_array(manifest && manifest.package_aliases),
24
+ controls_aliases: safe_array(manifest && manifest.controls_aliases)
25
+ };
26
+ };
27
+
28
+ const extract_scan_manifest = (bundle_result) => {
29
+ const bundle = bundle_result[0];
30
+ const analysis = bundle && bundle.bundle_analysis && bundle.bundle_analysis.jsgui3_html_control_scan;
31
+ assert(analysis, 'Expected bundle_analysis.jsgui3_html_control_scan metadata');
32
+ return analysis;
33
+ };
34
+
35
+ describe('Control Scan Manifest Regression Tests', function () {
36
+ this.timeout(120000);
37
+
38
+ const temp_fixture_paths = [];
39
+
40
+ const write_temp_fixture = async (file_name, source_text) => {
41
+ const temp_path = path.join(__dirname, file_name);
42
+ await fs.writeFile(temp_path, source_text, 'utf8');
43
+ temp_fixture_paths.push(temp_path);
44
+ return temp_path;
45
+ };
46
+
47
+ before(async function () {
48
+ // Ensure fixture directory exists when update mode writes snapshots.
49
+ await fs.mkdir(path.dirname(fixture_file_path), { recursive: true });
50
+ });
51
+
52
+ after(async function () {
53
+ await Promise.all(temp_fixture_paths.map(async (temp_path) => {
54
+ try {
55
+ await fs.unlink(temp_path);
56
+ } catch (err) {
57
+ // Ignore missing temp files.
58
+ }
59
+ }));
60
+ });
61
+
62
+ it('should match strict manifest snapshot for static and dynamic alias usage', async function () {
63
+ const static_alias_fixture_path = await write_temp_fixture('temp_control_scan_static_alias_client.js', `
64
+ const ui = require('jsgui3-html');
65
+ const ui_controls = ui.controls;
66
+ const { Button: Button_Control } = ui_controls;
67
+
68
+ class Tiny_Static_Alias_App extends ui_controls.Active_HTML_Document {
69
+ constructor(spec = {}) {
70
+ super(spec);
71
+ if (!spec.el) {
72
+ const button = new Button_Control({ context: this.context });
73
+ button.add('ok');
74
+ this.body.add(button);
75
+ }
76
+ }
77
+ }
78
+
79
+ module.exports = { Tiny_Static_Alias_App };
80
+ `);
81
+
82
+ const dynamic_alias_fixture_path = await write_temp_fixture('temp_control_scan_dynamic_alias_client.js', `
83
+ const ui = require('jsgui3-html');
84
+ const ui_controls = ui.controls;
85
+
86
+ class Tiny_Dynamic_Alias_App extends ui_controls.Active_HTML_Document {
87
+ constructor(spec = {}) {
88
+ super(spec);
89
+ if (!spec.el) {
90
+ const control_name = 'Button';
91
+ const Dynamic_Control = ui_controls[control_name];
92
+ const button = new Dynamic_Control({ context: this.context });
93
+ button.add('ok');
94
+ this.body.add(button);
95
+ }
96
+ }
97
+ }
98
+
99
+ module.exports = { Tiny_Dynamic_Alias_App };
100
+ `);
101
+
102
+ const bundler = new Advanced_JS_Bundler_Using_ESBuild({
103
+ debug: false,
104
+ bundler: {
105
+ minify: {
106
+ enabled: true,
107
+ level: 'normal'
108
+ },
109
+ elimination: {
110
+ enabled: true,
111
+ jsgui3_html_controls: {
112
+ enabled: true
113
+ }
114
+ }
115
+ }
116
+ });
117
+
118
+ const static_manifest = extract_scan_manifest(await bundler.bundle(static_alias_fixture_path));
119
+ const dynamic_manifest = extract_scan_manifest(await bundler.bundle(dynamic_alias_fixture_path));
120
+
121
+ const snapshot = {
122
+ static_alias: normalize_manifest(static_manifest),
123
+ dynamic_alias: normalize_manifest(dynamic_manifest)
124
+ };
125
+
126
+ if (update_snapshots) {
127
+ await fs.writeFile(fixture_file_path, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
128
+ return;
129
+ }
130
+
131
+ let expected_snapshot;
132
+ try {
133
+ const expected_text = await fs.readFile(fixture_file_path, 'utf8');
134
+ expected_snapshot = JSON.parse(expected_text);
135
+ } catch (err) {
136
+ assert.fail(
137
+ `Missing manifest expectation fixture at ${fixture_file_path}. ` +
138
+ 'Run with UPDATE_CONTROL_SCAN_MANIFEST=1 to generate it.'
139
+ );
140
+ }
141
+
142
+ assert.deepStrictEqual(snapshot, expected_snapshot);
143
+ });
144
+ });
@@ -171,12 +171,13 @@ describe('End-to-End Integration Tests', function() {
171
171
  ...base_serve_options,
172
172
  debug: false,
173
173
  bundler: {
174
- compression: {
175
- enabled: true,
176
- algorithms: ['gzip', 'br']
177
- }
178
- }
179
- });
174
+ compression: {
175
+ enabled: true,
176
+ algorithms: ['gzip', 'br'],
177
+ threshold: 0
178
+ }
179
+ }
180
+ });
180
181
 
181
182
  const css_identity_response = await makeRequest(`http://localhost:${serverPort}/css/css.css`, {
182
183
  'Accept-Encoding': 'identity'
@@ -399,28 +400,28 @@ describe('End-to-End Integration Tests', function() {
399
400
  });
400
401
 
401
402
  it('should handle server port conflicts', async function() {
402
- // Start first server
403
- const server1 = await start_server({
403
+ this.timeout(60000);
404
+ const server_1 = await Server.serve({
404
405
  ...base_serve_options,
405
406
  debug: false,
406
407
  host: '127.0.0.1'
407
408
  });
408
409
 
409
- // Try to start second server on same port
410
+ let conflicting_server = null;
410
411
  try {
411
- await start_server({
412
+ conflicting_server = await Server.serve({
412
413
  ...base_serve_options,
413
- port: serverPort, // Same port
414
+ port: serverPort,
414
415
  debug: false,
415
416
  host: '127.0.0.1'
416
417
  });
417
418
  assert.fail('Should have failed to start server on occupied port');
418
419
  } catch (error) {
419
420
  assert(error, 'Should throw error for port conflict');
421
+ } finally {
422
+ await stop_server(conflicting_server);
423
+ await stop_server(server_1);
420
424
  }
421
-
422
- // Clean up
423
- await stop_server(server1);
424
425
  });
425
426
  });
426
427
  });