jsgui3-server 0.0.151 → 0.0.152
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/README.md +21 -0
- package/admin-ui/v1/controls/admin_shell.js +33 -0
- package/admin-ui/v1/server.js +14 -1
- package/docs/api-reference.md +120 -2
- package/docs/books/website-design/01-introduction.md +73 -0
- package/docs/books/website-design/02-current-state.md +195 -0
- package/docs/books/website-design/03-base-class.md +181 -0
- package/docs/books/website-design/04-webpage.md +307 -0
- package/docs/books/website-design/05-website.md +456 -0
- package/docs/books/website-design/06-pages-storage.md +170 -0
- package/docs/books/website-design/07-api-layer.md +285 -0
- package/docs/books/website-design/08-server-integration.md +271 -0
- package/docs/books/website-design/09-cross-agent-review.md +190 -0
- package/docs/books/website-design/10-open-questions.md +196 -0
- package/docs/books/website-design/11-converged-recommendation.md +205 -0
- package/docs/books/website-design/12-content-model.md +395 -0
- package/docs/books/website-design/13-webpage-module-spec.md +404 -0
- package/docs/books/website-design/14-website-module-spec.md +541 -0
- package/docs/books/website-design/15-multi-repo-plan.md +275 -0
- package/docs/books/website-design/16-minimal-first.md +203 -0
- package/docs/books/website-design/17-implementation-report-codex.md +81 -0
- package/docs/books/website-design/README.md +43 -0
- package/docs/configuration-reference.md +54 -0
- package/docs/proposals/jsgui3-website-and-webpage-design-jsgui3-server-support.md +257 -0
- package/docs/proposals/jsgui3-website-and-webpage-design-review.md +73 -0
- package/docs/proposals/jsgui3-website-and-webpage-design.md +732 -0
- package/docs/swagger.md +316 -0
- package/examples/controls/1) window/server.js +6 -1
- package/examples/controls/21) mvvm and declarative api/check.js +94 -0
- package/examples/controls/21) mvvm and declarative api/check_output.txt +25 -0
- package/examples/controls/21) mvvm and declarative api/check_output_2.txt +27 -0
- package/examples/controls/21) mvvm and declarative api/client.js +241 -0
- declarative api/e2e-screenshot-1-name-change.png +0 -0
- declarative api/e2e-screenshot-2-toggled.png +0 -0
- declarative api/e2e-screenshot-3-final.png +0 -0
- declarative api/e2e-screenshot-final.png +0 -0
- package/examples/controls/21) mvvm and declarative api/e2e-test.js +175 -0
- package/examples/controls/21) mvvm and declarative api/out.html +1 -0
- package/examples/controls/21) mvvm and declarative api/page_out.html +1 -0
- package/examples/controls/21) mvvm and declarative api/server.js +18 -0
- package/examples/data-views/01) query-endpoint/server.js +61 -0
- package/labs/website-design/001-base-class-overhead/check.js +162 -0
- package/labs/website-design/002-pages-storage/check.js +244 -0
- package/labs/website-design/002-pages-storage/results.txt +0 -0
- package/labs/website-design/003-type-detection/check.js +193 -0
- package/labs/website-design/003-type-detection/results.txt +0 -0
- package/labs/website-design/004-two-stage-validation/check.js +314 -0
- package/labs/website-design/004-two-stage-validation/results.txt +0 -0
- package/labs/website-design/005-normalize-input/check.js +303 -0
- package/labs/website-design/006-serve-website-spike/check.js +290 -0
- package/labs/website-design/README.md +34 -0
- package/labs/website-design/manifest.json +68 -0
- package/labs/website-design/run-all.js +60 -0
- package/middleware/json-body.js +126 -0
- package/openapi.js +474 -0
- package/package.json +11 -8
- package/publishers/Publishers.js +6 -5
- package/publishers/http-function-publisher.js +135 -126
- package/publishers/http-webpage-publisher.js +89 -11
- package/publishers/query-publisher.js +116 -0
- package/publishers/swagger-publisher.js +203 -0
- package/publishers/swagger-ui.js +578 -0
- package/resources/adapters/array-adapter.js +143 -0
- package/resources/query-resource.js +131 -0
- package/serve-factory.js +728 -18
- package/server.js +421 -103
- package/tests/README.md +23 -1
- package/tests/admin-ui-jsgui-controls.test.js +16 -1
- package/tests/helpers/playwright-e2e-harness.js +326 -0
- package/tests/openapi.test.js +319 -0
- package/tests/playwright-smoke.test.js +134 -0
- package/tests/publish-enhancements.test.js +673 -0
- package/tests/query-publisher.test.js +430 -0
- package/tests/quick-json-body-test.js +169 -0
- package/tests/serve.test.js +425 -122
- package/tests/swagger-publisher.test.js +1076 -0
- package/tests/test-runner.js +1 -0
package/README.md
CHANGED
|
@@ -33,6 +33,27 @@ Server.serve(MyControl);
|
|
|
33
33
|
Server.serve(MyControl, { port: 3000 });
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Port Conflict Fallback
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
Server.serve({
|
|
40
|
+
Ctrl: MyControl,
|
|
41
|
+
port: 52000,
|
|
42
|
+
on_port_conflict: 'auto-loopback' // retry on 127.0.0.1 with a free port
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
With manual `server.start(...)`, you can print the effective endpoint:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
server.start(52000, (err) => {
|
|
50
|
+
if (err) throw err;
|
|
51
|
+
server.print_endpoints({ include_index: true });
|
|
52
|
+
}, {
|
|
53
|
+
on_port_conflict: 'auto-loopback'
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
36
57
|
### Single Page with Metadata
|
|
37
58
|
|
|
38
59
|
```javascript
|
|
@@ -647,6 +647,39 @@ class Admin_Shell extends Active_HTML_Document {
|
|
|
647
647
|
panel.add(this._create_kv_row('Node Version', node_version));
|
|
648
648
|
panel.add(this._create_kv_row('Platform', platform + ' / ' + arch));
|
|
649
649
|
|
|
650
|
+
const primary_endpoint = (status && status.server && status.server.primary_endpoint) || '—';
|
|
651
|
+
const listening_endpoints = (status && status.server && status.server.listening_endpoints) || [];
|
|
652
|
+
const startup_diagnostics = (status && status.server && status.server.startup_diagnostics) || null;
|
|
653
|
+
|
|
654
|
+
const startup_panel = this._create_panel('Startup & Network');
|
|
655
|
+
startup_panel.add(this._create_kv_row('Primary Endpoint', primary_endpoint));
|
|
656
|
+
startup_panel.add(this._create_kv_row('Listening Endpoints', listening_endpoints.length));
|
|
657
|
+
|
|
658
|
+
if (Array.isArray(listening_endpoints) && listening_endpoints.length > 0) {
|
|
659
|
+
const endpoints_list = new controls.div({ context: this.context, 'class': 'as-kv-stack' });
|
|
660
|
+
listening_endpoints.forEach((endpoint, index) => {
|
|
661
|
+
const endpoint_url = endpoint && endpoint.url ? endpoint.url : '';
|
|
662
|
+
endpoints_list.add(this._create_kv_row('Endpoint ' + (index + 1), endpoint_url));
|
|
663
|
+
});
|
|
664
|
+
startup_panel.add(endpoints_list);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (startup_diagnostics) {
|
|
668
|
+
startup_panel.add(this._create_kv_row('Requested Port', startup_diagnostics.requested_port));
|
|
669
|
+
if (startup_diagnostics.fallback_port) {
|
|
670
|
+
startup_panel.add(this._create_kv_row('Fallback Port', startup_diagnostics.fallback_port));
|
|
671
|
+
}
|
|
672
|
+
if (startup_diagnostics.fallback_host) {
|
|
673
|
+
startup_panel.add(this._create_kv_row('Fallback Host', startup_diagnostics.fallback_host));
|
|
674
|
+
}
|
|
675
|
+
const attempted = Array.isArray(startup_diagnostics.addresses_attempted)
|
|
676
|
+
? startup_diagnostics.addresses_attempted.join(', ')
|
|
677
|
+
: '';
|
|
678
|
+
startup_panel.add(this._create_kv_row('Addresses Attempted', attempted || '—'));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
panel.add(startup_panel);
|
|
682
|
+
|
|
650
683
|
const logout_btn = new controls.button({ context: this.context, 'class': 'as-logout-btn' });
|
|
651
684
|
logout_btn.dom.attributes.type = 'button';
|
|
652
685
|
logout_btn.add('Log Out');
|
package/admin-ui/v1/server.js
CHANGED
|
@@ -205,6 +205,16 @@ class Admin_Module_V1 {
|
|
|
205
205
|
const mem = process.memoryUsage();
|
|
206
206
|
const pool_summary = this._safe_pool_summary(server);
|
|
207
207
|
|
|
208
|
+
const listening_endpoints = (server && typeof server.get_listening_endpoints === 'function')
|
|
209
|
+
? server.get_listening_endpoints()
|
|
210
|
+
: [];
|
|
211
|
+
const primary_endpoint = (server && typeof server.get_primary_endpoint === 'function')
|
|
212
|
+
? server.get_primary_endpoint()
|
|
213
|
+
: null;
|
|
214
|
+
const startup_diagnostics = (server && typeof server.get_startup_diagnostics === 'function')
|
|
215
|
+
? server.get_startup_diagnostics()
|
|
216
|
+
: null;
|
|
217
|
+
|
|
208
218
|
return {
|
|
209
219
|
process: {
|
|
210
220
|
pid: process.pid,
|
|
@@ -222,7 +232,10 @@ class Admin_Module_V1 {
|
|
|
222
232
|
},
|
|
223
233
|
server: {
|
|
224
234
|
port: (server && server.port) || null,
|
|
225
|
-
name: (server && server.name) || 'jsgui3-server'
|
|
235
|
+
name: (server && server.name) || 'jsgui3-server',
|
|
236
|
+
primary_endpoint,
|
|
237
|
+
listening_endpoints,
|
|
238
|
+
startup_diagnostics
|
|
226
239
|
},
|
|
227
240
|
telemetry: {
|
|
228
241
|
request_count: this._request_count,
|
package/docs/api-reference.md
CHANGED
|
@@ -93,18 +93,32 @@ jsgui.Resource.Remote_Process;
|
|
|
93
93
|
|
|
94
94
|
## Server Instance Methods
|
|
95
95
|
|
|
96
|
-
### server.start(port, callback)
|
|
96
|
+
### server.start(port, callback, options)
|
|
97
97
|
|
|
98
98
|
Starts the HTTP server (legacy API).
|
|
99
99
|
|
|
100
100
|
**Signature:**
|
|
101
101
|
```javascript
|
|
102
|
-
server.start(
|
|
102
|
+
server.start(
|
|
103
|
+
port?: number,
|
|
104
|
+
callback?: (err?: Error) => void,
|
|
105
|
+
options?: {
|
|
106
|
+
on_port_conflict?: 'error' | 'auto-loopback'
|
|
107
|
+
}
|
|
108
|
+
): void
|
|
103
109
|
```
|
|
104
110
|
|
|
105
111
|
**Parameters:**
|
|
106
112
|
- `port` (number, optional): Port to listen on (default: 8080)
|
|
107
113
|
- `callback` (function, optional): Called when server starts or fails
|
|
114
|
+
- `options` (object, optional): Startup behavior options
|
|
115
|
+
- `on_port_conflict: 'error' | 'auto-loopback'`
|
|
116
|
+
- `error` (default): Return startup error as usual.
|
|
117
|
+
- `auto-loopback`: If all requested interface binds fail with `EADDRINUSE`,
|
|
118
|
+
retry once on `127.0.0.1` using a free port.
|
|
119
|
+
|
|
120
|
+
When startup fails, the returned error may include `error.startup_diagnostics`
|
|
121
|
+
containing attempted addresses and per-address errors.
|
|
108
122
|
|
|
109
123
|
### server.publish(route, handler)
|
|
110
124
|
|
|
@@ -151,6 +165,106 @@ server.close(callback?: (err?: Error | null) => void): void
|
|
|
151
165
|
- Stops `sse_publisher` when present
|
|
152
166
|
- Closes all HTTP listeners
|
|
153
167
|
|
|
168
|
+
### server.get_listening_endpoints()
|
|
169
|
+
|
|
170
|
+
Returns the active listener endpoints (protocol, host, port, url) for the
|
|
171
|
+
current process. Useful when startup falls back from a requested fixed port to
|
|
172
|
+
an auto-selected loopback port.
|
|
173
|
+
|
|
174
|
+
**Signature:**
|
|
175
|
+
```javascript
|
|
176
|
+
server.get_listening_endpoints(): Array<{
|
|
177
|
+
protocol: 'http' | 'https';
|
|
178
|
+
host: string;
|
|
179
|
+
port: number;
|
|
180
|
+
url: string;
|
|
181
|
+
}>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Example:**
|
|
185
|
+
```javascript
|
|
186
|
+
server.start(52000, (err) => {
|
|
187
|
+
if (err) throw err;
|
|
188
|
+
console.log('Listening at', server.get_primary_endpoint());
|
|
189
|
+
}, {
|
|
190
|
+
on_port_conflict: 'auto-loopback'
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### server.get_primary_endpoint()
|
|
195
|
+
|
|
196
|
+
Returns the primary endpoint URL string (first endpoint) or `null` if the
|
|
197
|
+
server is not listening.
|
|
198
|
+
|
|
199
|
+
**Signature:**
|
|
200
|
+
```javascript
|
|
201
|
+
server.get_primary_endpoint(): string | null
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Example:**
|
|
205
|
+
```javascript
|
|
206
|
+
const url = server.get_primary_endpoint();
|
|
207
|
+
if (url) {
|
|
208
|
+
console.log('Primary endpoint:', url);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### server.print_endpoints(options)
|
|
213
|
+
|
|
214
|
+
Prints listening endpoint URLs and returns the printed lines.
|
|
215
|
+
|
|
216
|
+
**Signature:**
|
|
217
|
+
```javascript
|
|
218
|
+
server.print_endpoints(options?: {
|
|
219
|
+
logger?: (line: string) => void;
|
|
220
|
+
include_index?: boolean;
|
|
221
|
+
prefix?: string;
|
|
222
|
+
}): string[]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Parameters:**
|
|
226
|
+
- `options.logger` (function, optional): Line logger (default: `console.log`)
|
|
227
|
+
- `options.include_index` (boolean, optional): Include endpoint index in output
|
|
228
|
+
- `options.prefix` (string, optional): Prefix text (default: `"listening endpoint"`)
|
|
229
|
+
|
|
230
|
+
**Example:**
|
|
231
|
+
```javascript
|
|
232
|
+
server.start(52000, (err) => {
|
|
233
|
+
if (err) throw err;
|
|
234
|
+
server.print_endpoints({ include_index: true });
|
|
235
|
+
}, {
|
|
236
|
+
on_port_conflict: 'auto-loopback'
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### server.get_startup_diagnostics()
|
|
241
|
+
|
|
242
|
+
Returns startup diagnostics information or `null` when unavailable.
|
|
243
|
+
|
|
244
|
+
**Signature:**
|
|
245
|
+
```javascript
|
|
246
|
+
server.get_startup_diagnostics(): {
|
|
247
|
+
requested_port: number;
|
|
248
|
+
fallback_port?: number;
|
|
249
|
+
fallback_host?: string;
|
|
250
|
+
addresses_attempted: string[];
|
|
251
|
+
errors_by_address: Record<string, { code?: string; message?: string }>;
|
|
252
|
+
} | null
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Example:**
|
|
256
|
+
```javascript
|
|
257
|
+
server.start(52000, (err) => {
|
|
258
|
+
if (err) {
|
|
259
|
+
console.error(server.get_startup_diagnostics());
|
|
260
|
+
throw err;
|
|
261
|
+
}
|
|
262
|
+
console.log(server.get_startup_diagnostics());
|
|
263
|
+
}, {
|
|
264
|
+
on_port_conflict: 'auto-loopback'
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
154
268
|
### server.use(fn)
|
|
155
269
|
|
|
156
270
|
Register middleware to run before every request is routed. Middleware is executed
|
|
@@ -992,6 +1106,10 @@ interface ServerOptions {
|
|
|
992
1106
|
src_path_client_js?: string;
|
|
993
1107
|
port?: number;
|
|
994
1108
|
host?: string;
|
|
1109
|
+
on_port_conflict?: 'error' | 'auto-loopback';
|
|
1110
|
+
start?: {
|
|
1111
|
+
on_port_conflict?: 'error' | 'auto-loopback';
|
|
1112
|
+
};
|
|
995
1113
|
debug?: boolean;
|
|
996
1114
|
name?: string;
|
|
997
1115
|
root?: string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Chapter 1: Introduction & Vision
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
`jsgui3-server` can serve web pages. You give it a control, it bundles it, wraps it in HTML, and serves it. That works well for single-page apps:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
Server.serve(My_Control);
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
But what about a website with multiple pages, API endpoints, static assets, metadata, and navigation? Today you pass a flat options object:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
Server.serve({
|
|
15
|
+
pages: {
|
|
16
|
+
'/': { content: Home },
|
|
17
|
+
'/about': { content: About }
|
|
18
|
+
},
|
|
19
|
+
api: {
|
|
20
|
+
'get-users': () => db.get_users()
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This works, but it's just configuration — a bag of options that the server interprets. There's no object you can hold, inspect, pass around, or ask questions of. You can't say "how many pages does this site have?" or "what are its API endpoints?" without knowing the internal structure of the options object.
|
|
26
|
+
|
|
27
|
+
## The Vision
|
|
28
|
+
|
|
29
|
+
Two sibling NPM packages — `jsgui3-website` and `jsgui3-webpage` — should provide **abstract representations** of websites and webpages. They describe *what* a site is, independent of *how* it gets served.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
jsgui3-webpage → "a page has a path, title, content, and metadata"
|
|
33
|
+
jsgui3-website → "a website has pages, API endpoints, assets, and metadata"
|
|
34
|
+
jsgui3-server → "I know how to serve those"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## The Core Design Tension
|
|
38
|
+
|
|
39
|
+
There's a fundamental tension in this design:
|
|
40
|
+
|
|
41
|
+
### Simplicity vs. Capability
|
|
42
|
+
|
|
43
|
+
A Webpage could be as simple as:
|
|
44
|
+
```js
|
|
45
|
+
{ path: '/about', content: About_Control, title: 'About' }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or as rich as an evented, observable, lifecycle-managed object with change tracking, validation, and introspection. The question is: how much machinery earns its keep?
|
|
49
|
+
|
|
50
|
+
### Abstraction vs. Coupling
|
|
51
|
+
|
|
52
|
+
These modules should be useful *without* jsgui3-server — maybe for a static site generator, a build tool, or just as a way to describe a site structure. But they also need to work well *with* jsgui3-server. More features means more coupling.
|
|
53
|
+
|
|
54
|
+
### Convention vs. Configuration
|
|
55
|
+
|
|
56
|
+
Should a Webpage default its path to `'/'`? Should it assume static rendering? Every default is convenient for the common case but can mask errors in complex cases.
|
|
57
|
+
|
|
58
|
+
## The Constraint
|
|
59
|
+
|
|
60
|
+
**These abstractions must remain optional.** `jsgui3-server` works today without them and must continue to do so. A developer who just wants `Server.serve(MyCtrl)` should never need to know about `Website` or `Webpage`. They are a useful layer for developers who want richer website definitions.
|
|
61
|
+
|
|
62
|
+
## How This Book is Organized
|
|
63
|
+
|
|
64
|
+
Each chapter explores a specific design dimension with multiple approaches. The goal is not to pick a winner but to lay out the tradeoffs clearly, so the best combination can emerge from informed discussion.
|
|
65
|
+
|
|
66
|
+
- **Chapter 2** documents what exists today
|
|
67
|
+
- **Chapters 3–7** explore specific design choices (base class, webpage, website, pages, API)
|
|
68
|
+
- **Chapter 8** covers server-side integration
|
|
69
|
+
- **Chapter 9** presents a cross-agent review
|
|
70
|
+
- **Chapter 10** lists open questions
|
|
71
|
+
- **Chapter 11** converges the discussion into a concrete recommended baseline
|
|
72
|
+
|
|
73
|
+
No code will be implemented until the design discussion converges.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Chapter 2: Current State
|
|
2
|
+
|
|
3
|
+
Both repos exist on NPM at version 0.0.8. Both are near-empty skeletons. This chapter documents the **exact, complete** code in each, and how `jsgui3-server` uses them today.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 2.1 jsgui3-webpage
|
|
8
|
+
|
|
9
|
+
**Repository**: `github.com/metabench/jsgui3-webpage`
|
|
10
|
+
**Version**: 0.0.8
|
|
11
|
+
**Total source files**: 3 (Webpage.js, package.json, README.md)
|
|
12
|
+
|
|
13
|
+
### Webpage.js — the entire module
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
/*
|
|
17
|
+
Probably does not need to do very much apart from hold info for the moment.
|
|
18
|
+
Could make subclasses do things like generare its specific parts from spec.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class Webpage {
|
|
22
|
+
constructor(spec) {
|
|
23
|
+
Object.assign(this, spec);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = Webpage;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. A class that copies spec properties onto itself. No methods, no validation, no dependencies.
|
|
31
|
+
|
|
32
|
+
### package.json
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"name": "jsgui3-webpage",
|
|
37
|
+
"main": "Webpage.js",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"comments": [
|
|
40
|
+
"Just depend on jsgui3-html for the moment. This would be a website in the abstract sense.",
|
|
41
|
+
{ "jsgui3-html": "^0.0.139" }
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {},
|
|
44
|
+
"version": "0.0.8"
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`jsgui3-html` is mentioned in `comments` but is NOT an actual dependency. There are zero runtime dependencies.
|
|
49
|
+
|
|
50
|
+
### README.md
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
# jsgui3-webpage
|
|
54
|
+
A class that represents a webpage.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2.2 jsgui3-website
|
|
60
|
+
|
|
61
|
+
**Repository**: `github.com/metabench/jsgui3-website`
|
|
62
|
+
**Version**: 0.0.8
|
|
63
|
+
**Total source files**: 4 (Website.js, API.js, package.json, README.md)
|
|
64
|
+
|
|
65
|
+
### Website.js
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
/*
|
|
69
|
+
Probably does not need to do very much apart from hold info for the moment.
|
|
70
|
+
Could make subclasses do things like generare its specific parts from spec.
|
|
71
|
+
*/
|
|
72
|
+
const API = require('./API');
|
|
73
|
+
|
|
74
|
+
class Website {
|
|
75
|
+
constructor(spec) {
|
|
76
|
+
Object.assign(this, spec);
|
|
77
|
+
this.api = new API({ server: this.server });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = Website;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Copies spec properties, then creates an API instance. The `this.server` reference comes from whatever was passed in the spec.
|
|
85
|
+
|
|
86
|
+
### API.js — contains a bug
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
class API {
|
|
90
|
+
constructor(spec) {
|
|
91
|
+
this.server = spec.server;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
publish(name, fn) {
|
|
95
|
+
// Need to access the appropriate resource publisher.
|
|
96
|
+
const {server} = this;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
MediaSourceHandle.exports = API
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Bug**: The last line says `MediaSourceHandle.exports` instead of `module.exports`. This is a typo (likely an autocomplete error) that will crash at runtime with `ReferenceError: MediaSourceHandle is not defined`. Any code that `require('./API')` will fail.
|
|
104
|
+
|
|
105
|
+
### package.json
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"name": "jsgui3-website",
|
|
110
|
+
"main": "Website.js",
|
|
111
|
+
"license": "MIT",
|
|
112
|
+
"comments": [
|
|
113
|
+
"Just depend on jsgui3-html for the moment. This would be a website in the abstract sense.",
|
|
114
|
+
{ "jsgui3-html": "^0.0.139" }
|
|
115
|
+
],
|
|
116
|
+
"dependencies": {},
|
|
117
|
+
"version": "0.0.8"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Same pattern — zero actual dependencies.
|
|
122
|
+
|
|
123
|
+
### README.md
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
# jsgui3-website
|
|
127
|
+
A class that represents a website. Also has functionality to deploy the website.
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 2.3 How jsgui3-server Uses Them Today
|
|
133
|
+
|
|
134
|
+
`jsgui3-server` declares both as dependencies (`^0.0.8`) and uses them in several places:
|
|
135
|
+
|
|
136
|
+
### The re-export wrappers
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
jsgui3-server/website/website.js → module.exports = require('jsgui3-website')
|
|
140
|
+
jsgui3-server/website/webpage.js → module.exports = require('jsgui3-webpage')
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Both files also contain dead code — `Obselete_Style_Website` and `Obselete_Style_Webpage` classes that are defined but never used elsewhere.
|
|
144
|
+
|
|
145
|
+
### Usage in server.js
|
|
146
|
+
|
|
147
|
+
**When a control is provided** (line 354):
|
|
148
|
+
```js
|
|
149
|
+
const wp_app = new Webpage({ content: Ctrl });
|
|
150
|
+
```
|
|
151
|
+
Creates a Webpage as a thin wrapper around a control constructor. The Webpage is then handed to `HTTP_Webpage_Publisher` for bundling.
|
|
152
|
+
|
|
153
|
+
**When no control is provided** (line 428):
|
|
154
|
+
```js
|
|
155
|
+
const ws_app = new Website(opts_website);
|
|
156
|
+
```
|
|
157
|
+
Creates a Website as a placeholder. Handed to `HTTP_Website_Publisher`.
|
|
158
|
+
|
|
159
|
+
### Usage in serve-factory.js
|
|
160
|
+
|
|
161
|
+
When multi-page serving is configured via the `pages` option:
|
|
162
|
+
```js
|
|
163
|
+
const webpage = new Webpage({ name, title, content, path });
|
|
164
|
+
```
|
|
165
|
+
Creates a Webpage per route and hands each to `prepare_webpage_route`.
|
|
166
|
+
|
|
167
|
+
### Usage in publishers
|
|
168
|
+
|
|
169
|
+
`http-website-publisher.js` type-checks:
|
|
170
|
+
```js
|
|
171
|
+
spec.website instanceof Website
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
And iterates pages via the internal `website.pages._arr` (Collection internals).
|
|
175
|
+
|
|
176
|
+
### The Admin UI pattern
|
|
177
|
+
|
|
178
|
+
The admin UI in `server.js` (lines 108–134, 167–327) shows a real-world pattern for adding a multi-route "app" to the server:
|
|
179
|
+
|
|
180
|
+
1. Create a `Webpage` with a control as content
|
|
181
|
+
2. Create an `HTTP_Webpage_Publisher` with that webpage
|
|
182
|
+
3. Listen for the publisher's `'ready'` event
|
|
183
|
+
4. Register the bundled output as static routes on the router
|
|
184
|
+
|
|
185
|
+
This pattern — Webpage → Publisher → Router — is the pipeline that any Website/Webpage design needs to work with.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 2.4 What This Tells Us
|
|
190
|
+
|
|
191
|
+
1. **The classes are property bags** — `Object.assign(this, spec)` means anything goes in, nothing is validated
|
|
192
|
+
2. **The server constructs them internally** — users don't currently create Website/Webpage objects; the server does it behind the scenes
|
|
193
|
+
3. **The publisher pipeline is the real work** — the interesting code is in the publishers, not in the domain classes
|
|
194
|
+
4. **The API.js bug means Website construction actually crashes** — the `require('./API')` in Website.js will fail due to the export typo, meaning `new Website()` in the server only works because the API module isn't actually loadable (this is masked because the server may catch or not reach that code path)
|
|
195
|
+
5. **There's significant dead code** — the `Obselete_Style_*` classes and most of `http-website-publisher.js` (580+ lines of comments/stubs) are inactive
|