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.
Files changed (77) hide show
  1. package/README.md +21 -0
  2. package/admin-ui/v1/controls/admin_shell.js +33 -0
  3. package/admin-ui/v1/server.js +14 -1
  4. package/docs/api-reference.md +120 -2
  5. package/docs/books/website-design/01-introduction.md +73 -0
  6. package/docs/books/website-design/02-current-state.md +195 -0
  7. package/docs/books/website-design/03-base-class.md +181 -0
  8. package/docs/books/website-design/04-webpage.md +307 -0
  9. package/docs/books/website-design/05-website.md +456 -0
  10. package/docs/books/website-design/06-pages-storage.md +170 -0
  11. package/docs/books/website-design/07-api-layer.md +285 -0
  12. package/docs/books/website-design/08-server-integration.md +271 -0
  13. package/docs/books/website-design/09-cross-agent-review.md +190 -0
  14. package/docs/books/website-design/10-open-questions.md +196 -0
  15. package/docs/books/website-design/11-converged-recommendation.md +205 -0
  16. package/docs/books/website-design/12-content-model.md +395 -0
  17. package/docs/books/website-design/13-webpage-module-spec.md +404 -0
  18. package/docs/books/website-design/14-website-module-spec.md +541 -0
  19. package/docs/books/website-design/15-multi-repo-plan.md +275 -0
  20. package/docs/books/website-design/16-minimal-first.md +203 -0
  21. package/docs/books/website-design/17-implementation-report-codex.md +81 -0
  22. package/docs/books/website-design/README.md +43 -0
  23. package/docs/configuration-reference.md +54 -0
  24. package/docs/proposals/jsgui3-website-and-webpage-design-jsgui3-server-support.md +257 -0
  25. package/docs/proposals/jsgui3-website-and-webpage-design-review.md +73 -0
  26. package/docs/proposals/jsgui3-website-and-webpage-design.md +732 -0
  27. package/docs/swagger.md +316 -0
  28. package/examples/controls/1) window/server.js +6 -1
  29. package/examples/controls/21) mvvm and declarative api/check.js +94 -0
  30. package/examples/controls/21) mvvm and declarative api/check_output.txt +25 -0
  31. package/examples/controls/21) mvvm and declarative api/check_output_2.txt +27 -0
  32. package/examples/controls/21) mvvm and declarative api/client.js +241 -0
  33. declarative api/e2e-screenshot-1-name-change.png +0 -0
  34. declarative api/e2e-screenshot-2-toggled.png +0 -0
  35. declarative api/e2e-screenshot-3-final.png +0 -0
  36. declarative api/e2e-screenshot-final.png +0 -0
  37. package/examples/controls/21) mvvm and declarative api/e2e-test.js +175 -0
  38. package/examples/controls/21) mvvm and declarative api/out.html +1 -0
  39. package/examples/controls/21) mvvm and declarative api/page_out.html +1 -0
  40. package/examples/controls/21) mvvm and declarative api/server.js +18 -0
  41. package/examples/data-views/01) query-endpoint/server.js +61 -0
  42. package/labs/website-design/001-base-class-overhead/check.js +162 -0
  43. package/labs/website-design/002-pages-storage/check.js +244 -0
  44. package/labs/website-design/002-pages-storage/results.txt +0 -0
  45. package/labs/website-design/003-type-detection/check.js +193 -0
  46. package/labs/website-design/003-type-detection/results.txt +0 -0
  47. package/labs/website-design/004-two-stage-validation/check.js +314 -0
  48. package/labs/website-design/004-two-stage-validation/results.txt +0 -0
  49. package/labs/website-design/005-normalize-input/check.js +303 -0
  50. package/labs/website-design/006-serve-website-spike/check.js +290 -0
  51. package/labs/website-design/README.md +34 -0
  52. package/labs/website-design/manifest.json +68 -0
  53. package/labs/website-design/run-all.js +60 -0
  54. package/middleware/json-body.js +126 -0
  55. package/openapi.js +474 -0
  56. package/package.json +11 -8
  57. package/publishers/Publishers.js +6 -5
  58. package/publishers/http-function-publisher.js +135 -126
  59. package/publishers/http-webpage-publisher.js +89 -11
  60. package/publishers/query-publisher.js +116 -0
  61. package/publishers/swagger-publisher.js +203 -0
  62. package/publishers/swagger-ui.js +578 -0
  63. package/resources/adapters/array-adapter.js +143 -0
  64. package/resources/query-resource.js +131 -0
  65. package/serve-factory.js +728 -18
  66. package/server.js +421 -103
  67. package/tests/README.md +23 -1
  68. package/tests/admin-ui-jsgui-controls.test.js +16 -1
  69. package/tests/helpers/playwright-e2e-harness.js +326 -0
  70. package/tests/openapi.test.js +319 -0
  71. package/tests/playwright-smoke.test.js +134 -0
  72. package/tests/publish-enhancements.test.js +673 -0
  73. package/tests/query-publisher.test.js +430 -0
  74. package/tests/quick-json-body-test.js +169 -0
  75. package/tests/serve.test.js +425 -122
  76. package/tests/swagger-publisher.test.js +1076 -0
  77. 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');
@@ -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,
@@ -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(port?: number, callback?: (err?: Error) => void): void
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