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.
- package/.github/agents/Mobile Developer.agent.md +89 -0
- package/.github/workflows/control-scan-manifest-check.yml +31 -0
- package/AGENTS.md +4 -0
- package/README.md +215 -3
- package/admin-ui/client.js +81 -51
- package/admin-ui/v1/admin_auth_service.js +197 -0
- package/admin-ui/v1/admin_user_store.js +71 -0
- package/admin-ui/v1/client.js +17 -0
- package/admin-ui/v1/controls/admin_shell.js +1399 -0
- package/admin-ui/v1/controls/group_box.js +84 -0
- package/admin-ui/v1/controls/stat_card.js +125 -0
- package/admin-ui/v1/server.js +658 -0
- package/admin-ui/v1/utils/formatters.js +68 -0
- package/dev-status.svg +139 -0
- package/docs/admin-extension-guide.md +345 -0
- package/docs/api-reference.md +301 -43
- package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
- package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
- package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
- package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
- package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
- package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
- package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
- package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
- package/docs/books/adaptive-control-improvements/README.md +66 -0
- package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
- package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
- package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
- package/docs/books/admin-ui-authentication/README.md +25 -0
- package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
- package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
- package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
- package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
- package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
- package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
- package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
- package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
- package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
- package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
- package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
- package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
- package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
- package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
- package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
- package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
- package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
- package/docs/books/creating-a-new-admin-ui/README.md +68 -0
- package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
- package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
- package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
- package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
- package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
- package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
- package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
- package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
- package/docs/books/device-adaptive-composition/README.md +47 -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/comparison-report-express-plex-cpanel.md +549 -0
- 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/designs/server-admin-interface-aero.svg +611 -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/docs/troubleshooting.md +84 -53
- package/examples/controls/15) window, observable SSE/server.js +6 -1
- package/examples/controls/19) window, auto observable ui/server.js +9 -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 +12 -0
- package/package.json +12 -2
- 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-sse-publisher.js +341 -0
- package/resources/process-resource.js +950 -0
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
- 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 +442 -259
- package/server.js +288 -13
- package/tests/README.md +71 -4
- package/tests/admin-ui-jsgui-controls.test.js +581 -0
- 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 +14 -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
|
|
|
@@ -131,6 +134,196 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
131
134
|
console.warn('Skipping /admin route registration due to missing control.');
|
|
132
135
|
}
|
|
133
136
|
|
|
137
|
+
// ─── Admin UI V1 (Dashboard with Stat Cards) ─────────
|
|
138
|
+
// Configurable via spec.admin:
|
|
139
|
+
// spec.admin === false → disable admin entirely
|
|
140
|
+
// spec.admin = { enabled: false } → disable admin entirely
|
|
141
|
+
// spec.admin = { sections: [...] } → add custom sidebar sections
|
|
142
|
+
// spec.admin = { endpoints: [...] } → add custom protected endpoints
|
|
143
|
+
const admin_config = spec.admin !== undefined ? spec.admin : {};
|
|
144
|
+
const admin_enabled = admin_config !== false && (admin_config.enabled !== false);
|
|
145
|
+
|
|
146
|
+
const Admin_Module_V1 = require('./admin-ui/v1/server');
|
|
147
|
+
if (admin_enabled) {
|
|
148
|
+
this.admin_v1 = new Admin_Module_V1(typeof admin_config === 'object' ? admin_config : {});
|
|
149
|
+
this.admin_v1.init(this);
|
|
150
|
+
|
|
151
|
+
// Register Admin V1 Page Route
|
|
152
|
+
let Admin_Shell_Control;
|
|
153
|
+
try {
|
|
154
|
+
Admin_Shell_Control = require('./admin-ui/v1/client').controls.Admin_Shell;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.warn('Failed to load Admin_Shell control:', e.message || e);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (Admin_Shell_Control) {
|
|
160
|
+
const admin_v1_app = new Webpage({
|
|
161
|
+
content: Admin_Shell_Control,
|
|
162
|
+
title: 'jsgui3 Admin Dashboard'
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const admin_v1_publisher = new HTTP_Webpage_Publisher({
|
|
166
|
+
name: 'Admin_V1_Publisher',
|
|
167
|
+
webpage: admin_v1_app,
|
|
168
|
+
src_path_client_js: lib_path.join(__dirname, 'admin-ui/v1/client.js')
|
|
169
|
+
});
|
|
170
|
+
admin_v1_publisher.name = 'Admin_V1_Publisher';
|
|
171
|
+
|
|
172
|
+
const is_dev_defaults = !process.env.ADMIN_V1_PASSWORD && process.env.NODE_ENV !== 'production';
|
|
173
|
+
const login_hint = is_dev_defaults
|
|
174
|
+
? '<div class="hint dev-creds">Dev defaults active — username: <code>admin</code> password: <code>admin</code></div><div class="hint">Set ADMIN_V1_USER / ADMIN_V1_PASSWORD env vars for production.</div>'
|
|
175
|
+
: '<div class="hint">Sign in with your configured credentials.</div>';
|
|
176
|
+
const login_html = `<!doctype html>
|
|
177
|
+
<html>
|
|
178
|
+
<head>
|
|
179
|
+
<meta charset="utf-8" />
|
|
180
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
181
|
+
<title>Admin Login</title>
|
|
182
|
+
<style>
|
|
183
|
+
body { margin:0; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:#111428; color:#e8e8f0; display:flex; align-items:center; justify-content:center; min-height:100vh; }
|
|
184
|
+
.card { width: 340px; background:#1b1f38; border:1px solid #2f3358; border-radius:10px; padding:20px; }
|
|
185
|
+
h1 { margin:0 0 16px; font-size:1rem; }
|
|
186
|
+
label { display:block; margin:10px 0 6px; font-size:0.8rem; color:#9aa0c8; }
|
|
187
|
+
input { width:100%; box-sizing:border-box; background:#13162a; border:1px solid #2e345c; color:#e8e8f0; border-radius:6px; padding:8px 10px; }
|
|
188
|
+
button { margin-top:14px; width:100%; border:1px solid #4facfe; background:#224267; color:#d3ebff; border-radius:6px; padding:8px 10px; cursor:pointer; }
|
|
189
|
+
.err { margin-top:10px; color:#ff8e9f; font-size:0.8rem; min-height:1.1rem; }
|
|
190
|
+
.hint { margin-top:12px; color:#7f86b3; font-size:0.75rem; }
|
|
191
|
+
.dev-creds { color:#43e97b; }
|
|
192
|
+
code { background:#13162a; padding:2px 6px; border-radius:4px; font-size:0.8rem; }
|
|
193
|
+
</style>
|
|
194
|
+
</head>
|
|
195
|
+
<body>
|
|
196
|
+
<div class="card">
|
|
197
|
+
<h1>jsgui3 Admin Login</h1>
|
|
198
|
+
<form id="login-form">
|
|
199
|
+
<label for="username">Username</label>
|
|
200
|
+
<input id="username" autocomplete="username" required />
|
|
201
|
+
<label for="password">Password</label>
|
|
202
|
+
<input id="password" type="password" autocomplete="current-password" required />
|
|
203
|
+
<button type="submit">Sign In</button>
|
|
204
|
+
<div class="err" id="err"></div>
|
|
205
|
+
${login_hint}
|
|
206
|
+
</form>
|
|
207
|
+
</div>
|
|
208
|
+
<script>
|
|
209
|
+
(async () => {
|
|
210
|
+
try {
|
|
211
|
+
const session = await fetch('/api/admin/v1/auth/session', { credentials: 'same-origin' }).then(r => r.json());
|
|
212
|
+
if (session && session.authenticated) {
|
|
213
|
+
location.replace('/admin/v1');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
} catch (e) {}
|
|
217
|
+
|
|
218
|
+
const form = document.getElementById('login-form');
|
|
219
|
+
const err = document.getElementById('err');
|
|
220
|
+
form.addEventListener('submit', async (e) => {
|
|
221
|
+
e.preventDefault();
|
|
222
|
+
err.textContent = '';
|
|
223
|
+
const username = document.getElementById('username').value;
|
|
224
|
+
const password = document.getElementById('password').value;
|
|
225
|
+
try {
|
|
226
|
+
const res = await fetch('/api/admin/v1/auth/login', {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
credentials: 'same-origin',
|
|
230
|
+
body: JSON.stringify({ username, password })
|
|
231
|
+
});
|
|
232
|
+
const data = await res.json();
|
|
233
|
+
if (!res.ok || !data.ok) {
|
|
234
|
+
err.textContent = data && data.error ? data.error : 'Login failed';
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
location.replace('/admin/v1');
|
|
238
|
+
} catch (e2) {
|
|
239
|
+
err.textContent = 'Network error';
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
})();
|
|
243
|
+
</script>
|
|
244
|
+
</body>
|
|
245
|
+
</html>`;
|
|
246
|
+
|
|
247
|
+
const serve_admin_v1_page = (req, res) => {
|
|
248
|
+
if (req && typeof req.url === 'string' && req.url.startsWith('/admin/v1/login')) {
|
|
249
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
250
|
+
res.end(login_html);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!this.admin_v1 || !this.admin_v1.is_admin_read_request(req)) {
|
|
255
|
+
res.writeHead(302, { Location: '/admin/v1/login' });
|
|
256
|
+
res.end();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (admin_v1_cached_html) {
|
|
260
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
261
|
+
res.end(admin_v1_cached_html);
|
|
262
|
+
} else {
|
|
263
|
+
res.writeHead(503, { 'Content-Type': 'text/plain' });
|
|
264
|
+
res.end('Admin UI v1 is loading, please retry...');
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
server_router.set_route('/admin/v1/login', null, (req, res) => {
|
|
269
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
270
|
+
res.end(login_html);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Namespace admin v1 assets under /admin/v1/ to avoid
|
|
274
|
+
// colliding with the main app's /js/js.js and /css/css.css.
|
|
275
|
+
let admin_v1_cached_html = null;
|
|
276
|
+
|
|
277
|
+
admin_v1_publisher.on('ready', (res_ready) => {
|
|
278
|
+
if (res_ready && res_ready._arr) {
|
|
279
|
+
for (const bundle_item of res_ready._arr) {
|
|
280
|
+
const { type } = bundle_item;
|
|
281
|
+
if (type === 'HTML') {
|
|
282
|
+
// Rewrite asset references so browser fetches
|
|
283
|
+
// the admin-specific JS/CSS, not the main app's.
|
|
284
|
+
let html = bundle_item.text || '';
|
|
285
|
+
html = html.replace(
|
|
286
|
+
/href="\/css\/css\.css"/g,
|
|
287
|
+
'href="/admin/v1/css/css.css"'
|
|
288
|
+
);
|
|
289
|
+
html = html.replace(
|
|
290
|
+
/src="\/js\/js\.js"/g,
|
|
291
|
+
'src="/admin/v1/js/js.js"'
|
|
292
|
+
);
|
|
293
|
+
// Inject viewport meta for mobile rendering
|
|
294
|
+
if (!html.includes('name="viewport"')) {
|
|
295
|
+
html = html.replace(
|
|
296
|
+
/<head([^>]*)>/i,
|
|
297
|
+
'<head$1><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">'
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
admin_v1_cached_html = html;
|
|
301
|
+
} else {
|
|
302
|
+
// JS → /admin/v1/js/js.js, CSS → /admin/v1/css/css.css
|
|
303
|
+
const namespaced_route =
|
|
304
|
+
type === 'JavaScript' ? '/admin/v1/js/js.js' :
|
|
305
|
+
type === 'CSS' ? '/admin/v1/css/css.css' :
|
|
306
|
+
'/admin/v1' + bundle_item.route;
|
|
307
|
+
const responder = new Static_Route_HTTP_Responder(bundle_item);
|
|
308
|
+
server_router.set_route(namespaced_route, responder, responder.handle_http);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Serve authenticated admin page at /admin/v1
|
|
313
|
+
server_router.set_route('/admin/v1', null, serve_admin_v1_page);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Temporary handler until the publisher finishes bundling
|
|
318
|
+
server_router.set_route('/admin/v1', null, serve_admin_v1_page);
|
|
319
|
+
resource_pool.add(admin_v1_publisher);
|
|
320
|
+
} else {
|
|
321
|
+
console.warn('Skipping /admin/v1 route registration due to missing Admin_Shell control.');
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
// admin_enabled === false
|
|
325
|
+
this.admin_v1 = null;
|
|
326
|
+
}
|
|
134
327
|
|
|
135
328
|
if (spec.routes) {
|
|
136
329
|
throw 'NYI - will use Website class rather than Website_Resource here'
|
|
@@ -267,7 +460,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
267
460
|
this.function_publishers = this.function_publishers || [];
|
|
268
461
|
this.function_publishers.push(fpub);
|
|
269
462
|
|
|
270
|
-
|
|
463
|
+
// Auto-prefix /api/ for simple names
|
|
464
|
+
// If name already starts with '/', use as-is for full route control
|
|
465
|
+
const full_route = name.startsWith('/') ? name : '/api/' + name;
|
|
466
|
+
this.server_router.set_route(full_route, fpub, fpub.handle_http);
|
|
271
467
|
}
|
|
272
468
|
|
|
273
469
|
publish_observable(route, obs, options = {}) {
|
|
@@ -275,7 +471,10 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
275
471
|
obs,
|
|
276
472
|
...options
|
|
277
473
|
});
|
|
278
|
-
|
|
474
|
+
// Auto-prefix /api/ for simple names (like publish() does)
|
|
475
|
+
// If route already starts with '/', use as-is for backward compatibility
|
|
476
|
+
const full_route = route.startsWith('/') ? route : '/api/' + route;
|
|
477
|
+
this.server_router.set_route(full_route, publisher, publisher.handle_http);
|
|
279
478
|
return publisher;
|
|
280
479
|
}
|
|
281
480
|
|
|
@@ -478,19 +677,84 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
478
677
|
}
|
|
479
678
|
|
|
480
679
|
close(callback) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
680
|
+
const invoke_stop = (target, done) => {
|
|
681
|
+
if (!target || typeof target.stop !== 'function') {
|
|
682
|
+
done(null);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (target.stop.length >= 1) {
|
|
687
|
+
target.stop((error) => done(error || null));
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
try {
|
|
692
|
+
const stop_result = target.stop();
|
|
693
|
+
if (stop_result && typeof stop_result.then === 'function') {
|
|
694
|
+
stop_result.then(() => done(null), (error) => done(error || null));
|
|
695
|
+
return;
|
|
492
696
|
}
|
|
697
|
+
done(null);
|
|
698
|
+
} catch (error) {
|
|
699
|
+
done(error);
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const close_http_servers = (done) => {
|
|
704
|
+
let count = this.http_servers.length;
|
|
705
|
+
if (count === 0) {
|
|
706
|
+
this.http_servers = [];
|
|
707
|
+
done();
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
this.http_servers.forEach(server => {
|
|
712
|
+
server.close(() => {
|
|
713
|
+
count--;
|
|
714
|
+
if (count === 0) {
|
|
715
|
+
this.http_servers = [];
|
|
716
|
+
done();
|
|
717
|
+
}
|
|
718
|
+
});
|
|
493
719
|
});
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
const finalize_close = (error) => {
|
|
723
|
+
if (callback) callback(error || null);
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
const stop_targets = [];
|
|
727
|
+
if (this.resource_pool) {
|
|
728
|
+
stop_targets.push(this.resource_pool);
|
|
729
|
+
}
|
|
730
|
+
if (this.sse_publisher) {
|
|
731
|
+
stop_targets.push(this.sse_publisher);
|
|
732
|
+
}
|
|
733
|
+
if (this.admin_v1 && typeof this.admin_v1.destroy === 'function') {
|
|
734
|
+
this.admin_v1.destroy();
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (stop_targets.length === 0) {
|
|
738
|
+
close_http_servers(() => finalize_close(null));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
let pending_stops = stop_targets.length;
|
|
743
|
+
const stop_errors = [];
|
|
744
|
+
const on_stop_complete = (error) => {
|
|
745
|
+
if (error) {
|
|
746
|
+
stop_errors.push(error);
|
|
747
|
+
}
|
|
748
|
+
pending_stops--;
|
|
749
|
+
if (pending_stops > 0) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const first_error = stop_errors.length > 0 ? stop_errors[0] : null;
|
|
753
|
+
close_http_servers(() => finalize_close(first_error));
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
stop_targets.forEach((target) => {
|
|
757
|
+
invoke_stop(target, on_stop_complete);
|
|
494
758
|
});
|
|
495
759
|
}
|
|
496
760
|
}
|
|
@@ -498,10 +762,21 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
498
762
|
JSGUI_Single_Process_Server.jsgui = jsgui;
|
|
499
763
|
|
|
500
764
|
JSGUI_Single_Process_Server.Resource = Resource;
|
|
765
|
+
JSGUI_Single_Process_Server.Resource.Process = Process_Resource;
|
|
766
|
+
JSGUI_Single_Process_Server.Resource.Remote_Process = Remote_Process_Resource;
|
|
501
767
|
JSGUI_Single_Process_Server.Page_Context = Server_Page_Context;
|
|
502
768
|
JSGUI_Single_Process_Server.Server_Page_Context = Server_Page_Context;
|
|
503
769
|
JSGUI_Single_Process_Server.Website_Resource = Website_Resource;
|
|
504
770
|
JSGUI_Single_Process_Server.Publishers = Publishers;
|
|
771
|
+
JSGUI_Single_Process_Server.Process_Resource = Process_Resource;
|
|
772
|
+
JSGUI_Single_Process_Server.Remote_Process_Resource = Remote_Process_Resource;
|
|
773
|
+
JSGUI_Single_Process_Server.HTTP_SSE_Publisher = HTTP_SSE_Publisher;
|
|
774
|
+
|
|
775
|
+
// Admin UI extensibility exports
|
|
776
|
+
JSGUI_Single_Process_Server.Admin_Module_V1 = require('./admin-ui/v1/server');
|
|
777
|
+
JSGUI_Single_Process_Server.Admin_Auth_Service = require('./admin-ui/v1/admin_auth_service');
|
|
778
|
+
JSGUI_Single_Process_Server.Admin_User_Store = require('./admin-ui/v1/admin_user_store');
|
|
779
|
+
|
|
505
780
|
JSGUI_Single_Process_Server.serve = require('./serve-factory')(JSGUI_Single_Process_Server);
|
|
506
781
|
|
|
507
782
|
module.exports = JSGUI_Single_Process_Server;
|
package/tests/README.md
CHANGED
|
@@ -19,16 +19,28 @@ The test suite covers all aspects of the Phase 1 implementation:
|
|
|
19
19
|
tests/
|
|
20
20
|
├── bundlers.test.js # Component isolation tests for bundlers
|
|
21
21
|
├── assigners.test.js # Component isolation tests for assigners
|
|
22
|
-
├── publishers.test.js # Component isolation tests for publishers
|
|
23
|
-
├── configuration-validation.test.js # Configuration validation tests
|
|
24
|
-
├──
|
|
22
|
+
├── publishers.test.js # Component isolation tests for publishers
|
|
23
|
+
├── configuration-validation.test.js # Configuration validation tests
|
|
24
|
+
├── admin-ui-render.test.js # Admin page render regression test
|
|
25
|
+
├── admin-ui-jsgui-controls.test.js # Admin shell interaction + control-first regression test
|
|
26
|
+
├── serve.test.js # Server.serve core behavior tests
|
|
27
|
+
├── serve-resources.test.js # Server.serve + resource integration tests
|
|
28
|
+
├── process-resource.test.js # Process_Resource lifecycle and restart tests
|
|
29
|
+
├── remote-process-resource.test.js # Remote_Process_Resource polling/recovery tests
|
|
30
|
+
├── server-resource-pool.test.js # Resource pool lifecycle and event forwarding tests
|
|
31
|
+
├── http-sse-publisher.test.js # HTTP_SSE_Publisher protocol/lifecycle tests
|
|
32
|
+
├── end-to-end.test.js # Full integration tests
|
|
25
33
|
├── content-analysis.test.js # Content analysis and verification
|
|
26
34
|
├── performance.test.js # Performance benchmarks
|
|
27
35
|
├── error-handling.test.js # Error handling and edge cases
|
|
36
|
+
├── control-optimizer-cache-behavior.test.js # Optimizer cache enable/disable behavior
|
|
28
37
|
├── examples-controls.e2e.test.js # Example apps regression (controls)
|
|
29
38
|
├── sass-controls.e2e.test.js # Sass/CSS controls E2E coverage
|
|
30
39
|
├── jsgui3-html-examples.puppeteer.test.js # Puppeteer interaction tests (jsgui3-html examples)
|
|
40
|
+
├── bundling-default-control-elimination.puppeteer.test.js # Puppeteer: default control elimination bundle checks
|
|
31
41
|
├── window-examples.puppeteer.test.js # Puppeteer interaction tests (window examples)
|
|
42
|
+
├── window-resource-integration.puppeteer.test.js # Browser E2E: controls + resource APIs + SSE
|
|
43
|
+
├── helpers/puppeteer-e2e-harness.js # Shared Puppeteer story runner + probes
|
|
32
44
|
├── test-runner.js # Custom test runner with reporting
|
|
33
45
|
└── README.md # This file
|
|
34
46
|
```
|
|
@@ -45,6 +57,12 @@ npm test
|
|
|
45
57
|
# Using the custom test runner
|
|
46
58
|
node tests/test-runner.js --test=bundlers.test.js
|
|
47
59
|
|
|
60
|
+
# Optimizer cache behavior
|
|
61
|
+
node tests/test-runner.js --test=control-optimizer-cache-behavior.test.js
|
|
62
|
+
|
|
63
|
+
# Admin UI shell interaction regression
|
|
64
|
+
node tests/test-runner.js --test=admin-ui-jsgui-controls.test.js
|
|
65
|
+
|
|
48
66
|
# Using mocha directly
|
|
49
67
|
npx mocha tests/bundlers.test.js
|
|
50
68
|
```
|
|
@@ -59,6 +77,16 @@ npm run test:examples:controls
|
|
|
59
77
|
npm run test:puppeteer:windows
|
|
60
78
|
```
|
|
61
79
|
|
|
80
|
+
### Run Puppeteer Bundling Elimination Tests
|
|
81
|
+
```bash
|
|
82
|
+
npm run test:puppeteer:bundling
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Run Puppeteer Resource Integration Tests
|
|
86
|
+
```bash
|
|
87
|
+
npm run test:puppeteer:resources
|
|
88
|
+
```
|
|
89
|
+
|
|
62
90
|
### Run Tests with Options
|
|
63
91
|
```bash
|
|
64
92
|
# Debug mode (enables sourcemaps)
|
|
@@ -76,16 +104,35 @@ node tests/test-runner.js --test=end-to-end.test.js --debug
|
|
|
76
104
|
1. Run a focused suite first (fast feedback).
|
|
77
105
|
2. Run the example regression suite next (HTML/CSS/JS smoke checks).
|
|
78
106
|
3. Run Puppeteer interaction tests last (heavier, requires a browser).
|
|
79
|
-
4. Run the
|
|
107
|
+
4. Run the resource integration Puppeteer suite when changing resources/SSE APIs.
|
|
108
|
+
5. Run the full suite only when changes are broad or before release.
|
|
80
109
|
|
|
81
110
|
Suggested sequence:
|
|
82
111
|
```bash
|
|
83
112
|
node tests/test-runner.js --test=bundlers.test.js
|
|
84
113
|
npm run test:examples:controls
|
|
114
|
+
npm run test:puppeteer:bundling
|
|
85
115
|
npm run test:puppeteer:windows
|
|
116
|
+
npm run test:puppeteer:resources
|
|
86
117
|
npm test
|
|
87
118
|
```
|
|
88
119
|
|
|
120
|
+
## Advanced Puppeteer E2E Methodology
|
|
121
|
+
|
|
122
|
+
For high-value interaction and integration coverage, use the shared harness:
|
|
123
|
+
|
|
124
|
+
- `tests/helpers/puppeteer-e2e-harness.js`
|
|
125
|
+
|
|
126
|
+
Key patterns:
|
|
127
|
+
|
|
128
|
+
- Write deterministic interaction stories with `run_interaction_story(...)` and named steps.
|
|
129
|
+
- Add browser probes (`console`, `pageerror`, `requestfailed`) and assert they stay clean.
|
|
130
|
+
- Assert both UI state and server truth for integration cases.
|
|
131
|
+
- For resource flows, validate both:
|
|
132
|
+
- API actions (`/api/resource/*`) and
|
|
133
|
+
- SSE propagation (`/events`) reflected in client UI/debug state.
|
|
134
|
+
- Keep selectors stable (`id` or `data-test`) so interaction tests remain robust.
|
|
135
|
+
|
|
89
136
|
## Patterns That Work
|
|
90
137
|
|
|
91
138
|
- Use per-test temporary client files and delete them in `finally`.
|
|
@@ -202,6 +249,26 @@ Browser-level interaction checks for jsgui3-html examples:
|
|
|
202
249
|
|
|
203
250
|
- MVVM counter interactions, bindings, and validation state
|
|
204
251
|
|
|
252
|
+
### 11. Window Resource Integration Puppeteer Tests (`window-resource-integration.puppeteer.test.js`)
|
|
253
|
+
|
|
254
|
+
Browser-level interaction checks that combine controls and server resources:
|
|
255
|
+
|
|
256
|
+
- Step-driven control interactions (date + datetime controls)
|
|
257
|
+
- Client actions invoking resource lifecycle APIs (`start`, `stop`, `restart`)
|
|
258
|
+
- SSE resource state events reflected in client UI
|
|
259
|
+
- Cross-checking client-observed state with server resource pool state
|
|
260
|
+
|
|
261
|
+
### 12. Core Resource and Serve Reliability Tests
|
|
262
|
+
|
|
263
|
+
- `admin-ui-render.test.js` validates the admin page control renders without clobbering control internals
|
|
264
|
+
- `admin-ui-jsgui-controls.test.js` validates admin shell interactions, custom section nav refresh, retry/logout control behavior, and SSE open/error/heartbeat handling
|
|
265
|
+
- `serve.test.js` validates `Server.serve` startup/route readiness behavior
|
|
266
|
+
- `serve-resources.test.js` validates in-process and process resource wiring in serve mode
|
|
267
|
+
- `process-resource.test.js` validates direct process lifecycle + crash restart handling
|
|
268
|
+
- `remote-process-resource.test.js` validates polling, unreachable, and recovered transitions
|
|
269
|
+
- `server-resource-pool.test.js` validates pool forwarding, remove/stop, and summaries
|
|
270
|
+
- `http-sse-publisher.test.js` validates SSE broadcast/send/replay/keepalive semantics
|
|
271
|
+
|
|
205
272
|
## Configuration Examples
|
|
206
273
|
|
|
207
274
|
### Basic Minification
|