jsgui3-server 0.0.148 → 0.0.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.github/agents/Mobile Developer.agent.md +89 -0
  2. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  3. package/AGENTS.md +4 -0
  4. package/README.md +215 -3
  5. package/admin-ui/client.js +81 -51
  6. package/admin-ui/v1/admin_auth_service.js +197 -0
  7. package/admin-ui/v1/admin_user_store.js +71 -0
  8. package/admin-ui/v1/client.js +17 -0
  9. package/admin-ui/v1/controls/admin_shell.js +1399 -0
  10. package/admin-ui/v1/controls/group_box.js +84 -0
  11. package/admin-ui/v1/controls/stat_card.js +125 -0
  12. package/admin-ui/v1/server.js +658 -0
  13. package/admin-ui/v1/utils/formatters.js +68 -0
  14. package/dev-status.svg +139 -0
  15. package/docs/admin-extension-guide.md +345 -0
  16. package/docs/api-reference.md +301 -43
  17. package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
  18. package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
  19. package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
  20. package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
  21. package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
  22. package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
  23. package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
  24. package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
  25. package/docs/books/adaptive-control-improvements/README.md +66 -0
  26. package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
  27. package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
  28. package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
  29. package/docs/books/admin-ui-authentication/README.md +25 -0
  30. package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
  31. package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
  32. package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
  33. package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
  34. package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
  35. package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
  36. package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
  37. package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
  38. package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
  39. package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
  40. package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
  41. package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
  42. package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
  43. package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
  44. package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
  45. package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
  46. package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
  47. package/docs/books/creating-a-new-admin-ui/README.md +68 -0
  48. package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
  49. package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
  50. package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
  51. package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
  52. package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
  53. package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
  54. package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
  55. package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
  56. package/docs/books/device-adaptive-composition/README.md +47 -0
  57. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  58. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  59. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  60. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  61. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  62. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  63. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  64. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  65. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  66. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  67. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  68. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  69. package/docs/bundling-system-deep-dive.md +9 -4
  70. package/docs/comparison-report-express-plex-cpanel.md +549 -0
  71. package/docs/comprehensive-documentation.md +49 -18
  72. package/docs/configuration-reference.md +152 -27
  73. package/docs/core/README.md +19 -0
  74. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  75. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  76. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  77. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  78. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  79. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  80. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  81. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  82. package/docs/designs/server-admin-interface-aero.svg +611 -0
  83. package/docs/publishers-guide.md +59 -4
  84. package/docs/resources-guide.md +184 -35
  85. package/docs/simple-server-api-design.md +72 -17
  86. package/docs/system-architecture.md +18 -14
  87. package/docs/troubleshooting.md +84 -53
  88. package/examples/controls/15) window, observable SSE/server.js +6 -1
  89. package/examples/controls/19) window, auto observable ui/server.js +9 -0
  90. package/examples/controls/20) window, task manager app/README.md +133 -0
  91. package/examples/controls/20) window, task manager app/client.js +797 -0
  92. package/examples/controls/20) window, task manager app/server.js +178 -0
  93. package/examples/controls/6) window, color_palette/client.js +165 -68
  94. package/examples/controls/9) window, date picker/client.js +362 -76
  95. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  96. package/examples/jsgui3-html/06) theming/client.js +22 -1
  97. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  98. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  99. package/lab/experiments/capture-color-controls.js +196 -0
  100. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  101. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  102. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  103. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  104. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  105. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  106. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  107. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  108. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  109. package/lab/screenshot-utils.js +248 -0
  110. package/module.js +12 -0
  111. package/package.json +12 -2
  112. package/publishers/Publishers.js +4 -3
  113. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  114. package/publishers/http-sse-publisher.js +341 -0
  115. package/resources/process-resource.js +950 -0
  116. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
  117. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  118. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  119. package/resources/remote-process-resource.js +355 -0
  120. package/resources/server-resource-pool.js +354 -41
  121. package/serve-factory.js +442 -259
  122. package/server.js +288 -13
  123. package/tests/README.md +71 -4
  124. package/tests/admin-ui-jsgui-controls.test.js +581 -0
  125. package/tests/admin-ui-render.test.js +24 -0
  126. package/tests/assigners.test.js +56 -40
  127. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  128. package/tests/configuration-validation.test.js +21 -18
  129. package/tests/content-analysis.test.js +7 -6
  130. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  131. package/tests/control-scan-manifest-regression.test.js +144 -0
  132. package/tests/end-to-end.test.js +15 -14
  133. package/tests/error-handling.test.js +222 -179
  134. package/tests/fixtures/bundling-default-button-client.js +37 -0
  135. package/tests/fixtures/bundling-default-window-client.js +34 -0
  136. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  137. package/tests/fixtures/resource-monitor-client.js +319 -0
  138. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  139. package/tests/http-sse-publisher.test.js +136 -0
  140. package/tests/performance.test.js +69 -65
  141. package/tests/process-resource.test.js +138 -0
  142. package/tests/publishers.test.js +7 -7
  143. package/tests/remote-process-resource.test.js +160 -0
  144. package/tests/sass-controls.e2e.test.js +7 -1
  145. package/tests/serve-resources.test.js +270 -0
  146. package/tests/serve.test.js +120 -50
  147. package/tests/server-resource-pool.test.js +106 -0
  148. package/tests/small-controls-bundle-size.test.js +252 -0
  149. package/tests/test-runner.js +14 -1
  150. package/tests/window-examples.puppeteer.test.js +204 -1
  151. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  152. package/tests/temp_invalid.js +0 -7
  153. package/tests/temp_invalid_utf8.js +0 -1
  154. package/tests/temp_malformed.js +0 -10
@@ -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
+ });
@@ -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
+ });