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
@@ -0,0 +1,197 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ class Admin_Auth_Service {
6
+ constructor(spec = {}) {
7
+ this.user_store = spec.user_store;
8
+ this.session_ttl_ms = Number.isFinite(spec.session_ttl_ms) ? spec.session_ttl_ms : (8 * 60 * 60 * 1000);
9
+ this.cookie_name = spec.cookie_name || 'jsgui_admin_v1_sid';
10
+ this.sessions = new Map();
11
+ }
12
+
13
+ _read_json_body(req) {
14
+ return new Promise((resolve, reject) => {
15
+ const chunks = [];
16
+ req.on('data', (chunk) => chunks.push(chunk));
17
+ req.on('end', () => {
18
+ if (chunks.length === 0) return resolve({});
19
+ try {
20
+ const text = Buffer.concat(chunks).toString('utf8');
21
+ resolve(text ? JSON.parse(text) : {});
22
+ } catch (error) {
23
+ reject(error);
24
+ }
25
+ });
26
+ req.on('error', reject);
27
+ });
28
+ }
29
+
30
+ _parse_cookies(req) {
31
+ const cookies = {};
32
+ const header = req && req.headers ? req.headers.cookie : '';
33
+ if (!header) return cookies;
34
+
35
+ const parts = header.split(';');
36
+ parts.forEach((part) => {
37
+ const idx = part.indexOf('=');
38
+ if (idx === -1) return;
39
+ const key = part.slice(0, idx).trim();
40
+ const value = part.slice(idx + 1).trim();
41
+ cookies[key] = decodeURIComponent(value);
42
+ });
43
+ return cookies;
44
+ }
45
+
46
+ _set_session_cookie(res, session_id) {
47
+ const secure = process.env.NODE_ENV === 'production' ? '; Secure' : '';
48
+ const cookie_value = this.cookie_name + '=' + encodeURIComponent(session_id) + '; Path=/; HttpOnly; SameSite=Lax; Max-Age=' + Math.floor(this.session_ttl_ms / 1000) + secure;
49
+ res.setHeader('Set-Cookie', cookie_value);
50
+ }
51
+
52
+ _clear_session_cookie(res) {
53
+ const secure = process.env.NODE_ENV === 'production' ? '; Secure' : '';
54
+ res.setHeader('Set-Cookie', this.cookie_name + '=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0' + secure);
55
+ }
56
+
57
+ _new_session_id() {
58
+ return crypto.randomBytes(24).toString('hex');
59
+ }
60
+
61
+ _cleanup_expired() {
62
+ const now = Date.now();
63
+ for (const [id, session] of this.sessions.entries()) {
64
+ if (!session || session.expires_at <= now) {
65
+ this.sessions.delete(id);
66
+ }
67
+ }
68
+ }
69
+
70
+ get_session(req) {
71
+ this._cleanup_expired();
72
+ const cookies = this._parse_cookies(req);
73
+ const sid = cookies[this.cookie_name];
74
+ if (!sid) return null;
75
+
76
+ const session = this.sessions.get(sid);
77
+ if (!session) return null;
78
+ if (session.expires_at <= Date.now()) {
79
+ this.sessions.delete(sid);
80
+ return null;
81
+ }
82
+ return {
83
+ session_id: sid,
84
+ user: session.user,
85
+ expires_at: session.expires_at
86
+ };
87
+ }
88
+
89
+ is_authenticated(req) {
90
+ return !!this.get_session(req);
91
+ }
92
+
93
+ has_role(req, role_name) {
94
+ const session = this.get_session(req);
95
+ if (!session || !session.user) return false;
96
+ const roles = Array.isArray(session.user.roles) ? session.user.roles : [];
97
+ return roles.includes(role_name);
98
+ }
99
+
100
+ has_any_role(req, role_names) {
101
+ const session = this.get_session(req);
102
+ if (!session || !session.user) return false;
103
+ const roles = Array.isArray(session.user.roles) ? session.user.roles : [];
104
+ if (!Array.isArray(role_names) || role_names.length === 0) return false;
105
+ return role_names.some((role_name) => roles.includes(role_name));
106
+ }
107
+
108
+ create_session(user, res) {
109
+ const session_id = this._new_session_id();
110
+ const expires_at = Date.now() + this.session_ttl_ms;
111
+ this.sessions.set(session_id, {
112
+ user,
113
+ created_at: Date.now(),
114
+ expires_at
115
+ });
116
+ this._set_session_cookie(res, session_id);
117
+ return { session_id, expires_at, user };
118
+ }
119
+
120
+ destroy_session(req, res) {
121
+ const cookies = this._parse_cookies(req);
122
+ const sid = cookies[this.cookie_name];
123
+ if (sid) this.sessions.delete(sid);
124
+ this._clear_session_cookie(res);
125
+ }
126
+
127
+ async handle_login(req, res) {
128
+ if (String(req.method || 'GET').toUpperCase() !== 'POST') {
129
+ res.writeHead(405, { 'Content-Type': 'application/json' });
130
+ res.end(JSON.stringify({ ok: false, error: 'Method Not Allowed' }));
131
+ return;
132
+ }
133
+
134
+ let body;
135
+ try {
136
+ body = await this._read_json_body(req);
137
+ } catch (error) {
138
+ res.writeHead(400, { 'Content-Type': 'application/json' });
139
+ res.end(JSON.stringify({ ok: false, error: 'Invalid JSON body' }));
140
+ return;
141
+ }
142
+
143
+ const username = body.username;
144
+ const password = body.password;
145
+ const user = this.user_store.verify_credentials(username, password);
146
+ if (!user) {
147
+ res.writeHead(401, { 'Content-Type': 'application/json' });
148
+ res.end(JSON.stringify({ ok: false, error: 'Invalid credentials' }));
149
+ return;
150
+ }
151
+
152
+ const session = this.create_session(user, res);
153
+ res.writeHead(200, { 'Content-Type': 'application/json' });
154
+ res.end(JSON.stringify({
155
+ ok: true,
156
+ user: session.user,
157
+ expires_at: session.expires_at
158
+ }));
159
+ }
160
+
161
+ handle_logout(req, res) {
162
+ if (String(req.method || 'GET').toUpperCase() !== 'POST') {
163
+ res.writeHead(405, { 'Content-Type': 'application/json' });
164
+ res.end(JSON.stringify({ ok: false, error: 'Method Not Allowed' }));
165
+ return;
166
+ }
167
+
168
+ this.destroy_session(req, res);
169
+ res.writeHead(200, { 'Content-Type': 'application/json' });
170
+ res.end(JSON.stringify({ ok: true }));
171
+ }
172
+
173
+ handle_session(req, res) {
174
+ if (String(req.method || 'GET').toUpperCase() !== 'GET') {
175
+ res.writeHead(405, { 'Content-Type': 'application/json' });
176
+ res.end(JSON.stringify({ ok: false, error: 'Method Not Allowed' }));
177
+ return;
178
+ }
179
+
180
+ const session = this.get_session(req);
181
+ if (!session) {
182
+ res.writeHead(200, { 'Content-Type': 'application/json' });
183
+ res.end(JSON.stringify({ ok: true, authenticated: false }));
184
+ return;
185
+ }
186
+
187
+ res.writeHead(200, { 'Content-Type': 'application/json' });
188
+ res.end(JSON.stringify({
189
+ ok: true,
190
+ authenticated: true,
191
+ user: session.user,
192
+ expires_at: session.expires_at
193
+ }));
194
+ }
195
+ }
196
+
197
+ module.exports = Admin_Auth_Service;
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ class Admin_User_Store {
6
+ constructor(spec = {}) {
7
+ this._users = new Map();
8
+ this._scrypt_cost = spec.scrypt_cost || 16384;
9
+ }
10
+
11
+ _hash_password(password, salt) {
12
+ const key = crypto.scryptSync(password, salt, 64, { N: this._scrypt_cost });
13
+ return key.toString('hex');
14
+ }
15
+
16
+ add_user(spec = {}) {
17
+ const username = String(spec.username || '').trim();
18
+ const password = String(spec.password || '');
19
+ const roles = Array.isArray(spec.roles) ? spec.roles : ['admin_read'];
20
+
21
+ if (!username) throw new Error('username is required');
22
+ if (!password) throw new Error('password is required');
23
+
24
+ const salt = crypto.randomBytes(16).toString('hex');
25
+ const password_hash = this._hash_password(password, salt);
26
+
27
+ this._users.set(username, {
28
+ username,
29
+ salt,
30
+ password_hash,
31
+ roles,
32
+ created_at: Date.now()
33
+ });
34
+
35
+ return { username, roles: roles.slice() };
36
+ }
37
+
38
+ has_user(username) {
39
+ return this._users.has(username);
40
+ }
41
+
42
+ get_user(username) {
43
+ const user = this._users.get(username);
44
+ if (!user) return null;
45
+ return {
46
+ username: user.username,
47
+ roles: user.roles.slice(),
48
+ created_at: user.created_at
49
+ };
50
+ }
51
+
52
+ verify_credentials(username, password) {
53
+ const user = this._users.get(String(username || ''));
54
+ if (!user) return null;
55
+
56
+ const attempted_hash = this._hash_password(String(password || ''), user.salt);
57
+ const expected = Buffer.from(user.password_hash, 'hex');
58
+ const attempted = Buffer.from(attempted_hash, 'hex');
59
+
60
+ if (expected.length !== attempted.length) return null;
61
+ const ok = crypto.timingSafeEqual(expected, attempted);
62
+ if (!ok) return null;
63
+
64
+ return {
65
+ username: user.username,
66
+ roles: user.roles.slice()
67
+ };
68
+ }
69
+ }
70
+
71
+ module.exports = Admin_User_Store;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Admin UI v1 — client entry point.
5
+ *
6
+ * This file is the ESBuild entry point that the HTTP_Webpage_Publisher
7
+ * bundles and serves. It imports the Admin_Shell control (which
8
+ * transitively pulls in Group_Box and Stat_Card) and exports the
9
+ * jsgui module with Admin_Shell registered on controls.
10
+ */
11
+ const jsgui = require('./controls/admin_shell');
12
+
13
+ // The require above already registers controls.Admin_Shell,
14
+ // controls.Stat_Card, and controls.Group_Box on the jsgui
15
+ // controls namespace.
16
+
17
+ module.exports = jsgui;