jsgui3-server 0.0.150 → 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/.github/instructions/copilot.instructions.md +1 -0
- package/AGENTS.md +2 -0
- package/README.md +89 -13
- package/admin-ui/v1/controls/admin_shell.js +702 -669
- package/admin-ui/v1/server.js +14 -1
- package/docs/api-reference.md +504 -306
- package/docs/books/creating-a-new-admin-ui/README.md +20 -20
- 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/comprehensive-documentation.md +220 -220
- package/docs/configuration-reference.md +281 -204
- package/docs/middleware-guide.md +236 -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/docs/system-architecture.md +24 -18
- 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/compression.js +217 -0
- package/middleware/index.js +15 -0
- package/middleware/json-body.js +126 -0
- package/module.js +3 -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 +756 -18
- package/server.js +502 -123
- 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
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## About This Book
|
|
8
8
|
|
|
9
|
-
This book documents the design, architecture, and implementation of a rich administration interface for jsgui3-server. The admin UI is built entirely with jsgui3 controls — dogfooding the framework to create a production-quality dashboard that provides real-time visibility into server internals, process health, resource pools, routing tables, build outputs, and configuration.
|
|
10
|
-
|
|
11
|
-
The admin UI lives at `admin-ui/v1/` and is served automatically at the `/admin/v1` route of any running jsgui3-server instance.
|
|
9
|
+
This book documents the design, architecture, and implementation of a rich administration interface for jsgui3-server. The admin UI is built entirely with jsgui3 controls — dogfooding the framework to create a production-quality dashboard that provides real-time visibility into server internals, process health, resource pools, routing tables, build outputs, and configuration.
|
|
10
|
+
|
|
11
|
+
The admin UI lives at `admin-ui/v1/` and is served automatically at the `/admin/v1` route of any running jsgui3-server instance.
|
|
12
12
|
|
|
13
13
|
## Design Reference
|
|
14
14
|
|
|
@@ -30,16 +30,16 @@ The visual design is inspired by a Windows Aero-era aesthetic — glass-effect t
|
|
|
30
30
|
| 10 | [Domain Controls — Build Status & Bundle Inspector](10-domain-controls-build-status-and-bundle-inspector.md) | Bundle size cards, module count, build timing, source map status |
|
|
31
31
|
| 11 | [Domain Controls — Configuration Panel](11-domain-controls-configuration-panel.md) | Read-only and editable config fields, port/debug/entry-point display |
|
|
32
32
|
| 12 | [The Admin Shell — Layout, Sidebar, & Navigation](12-admin-shell-layout-sidebar-navigation.md) | Window chrome, sidebar navigation, toolbar, status bar, content area routing |
|
|
33
|
-
| 13 | [Integrating with the Server — Telemetry Endpoints](13-telemetry-integration.md) | Modifying `server.js` and `Admin_Module` to expose new telemetry APIs |
|
|
34
|
-
| 14 | [Real-Time Updates — SSE & Observable Integration](14-realtime-sse-observable-integration.md) | Connecting the UI to `HTTP_SSE_Publisher` and `HTTP_Observable_Publisher` for live data |
|
|
35
|
-
| 15 | [Styling & Theming — The Aero-Inspired Design System](15-styling-theming-aero-design-system.md) | Token definitions, gradient recipes, glass effects, typography, and density |
|
|
33
|
+
| 13 | [Integrating with the Server — Telemetry Endpoints](13-telemetry-integration.md) | Modifying `server.js` and `Admin_Module` to expose new telemetry APIs |
|
|
34
|
+
| 14 | [Real-Time Updates — SSE & Observable Integration](14-realtime-sse-observable-integration.md) | Connecting the UI to `HTTP_SSE_Publisher` and `HTTP_Observable_Publisher` for live data |
|
|
35
|
+
| 15 | [Styling & Theming — The Aero-Inspired Design System](15-styling-theming-aero-design-system.md) | Token definitions, gradient recipes, glass effects, typography, and density |
|
|
36
36
|
| 16 | [Testing & Quality Assurance](16-testing-and-quality-assurance.md) | Unit tests, integration tests, viewport matrix testing, and acceptance criteria |
|
|
37
37
|
| 17 | [Next Steps — Process/Resource Roadmap](17-next-steps-process-resource-roadmap.md) | Handoff-grade implementation plan for process-centric admin capabilities, auth boundaries, and test gates |
|
|
38
38
|
|
|
39
39
|
## Key Principles
|
|
40
40
|
|
|
41
41
|
1. **Dogfooding** — The admin UI is built entirely with jsgui3 controls. Every pattern used here validates the framework.
|
|
42
|
-
2. **Zero Configuration** — The admin UI is available at `/admin/v1` on every jsgui3-server instance with no extra setup.
|
|
42
|
+
2. **Zero Configuration** — The admin UI is available at `/admin/v1` on every jsgui3-server instance with no extra setup.
|
|
43
43
|
3. **Read Before Write** — The admin UI primarily reads and displays server state. Write operations (restart, config change) are guarded and opt-in.
|
|
44
44
|
4. **Incremental Delivery** — Each domain control is independently functional. The shell composes them, but each can be developed and tested in isolation.
|
|
45
45
|
5. **Real Data Only** — No mock data in production. Every number shown comes from actual server telemetry.
|
|
@@ -53,16 +53,16 @@ The visual design is inspired by a Windows Aero-era aesthetic — glass-effect t
|
|
|
53
53
|
## Directory Structure
|
|
54
54
|
|
|
55
55
|
```
|
|
56
|
-
admin-ui/
|
|
57
|
-
├── v1/
|
|
58
|
-
│ ├── client.js # Client-side entry point (registers Admin_Shell)
|
|
59
|
-
│ ├── server.js # Admin module (adapter layer + auth + telemetry)
|
|
60
|
-
│ ├── admin_auth_service.js # Session auth service
|
|
61
|
-
│ ├── admin_user_store.js # In-memory user store
|
|
62
|
-
│ ├── controls/
|
|
63
|
-
│ │ ├── admin_shell.js # Main shell with sidebar + content
|
|
64
|
-
│ │ ├── stat_card.js # Stat card control
|
|
65
|
-
│ │ └── group_box.js # Reusable grouped panel container
|
|
66
|
-
│ └── utils/
|
|
67
|
-
│ └── formatters.js # Shared formatting helpers
|
|
68
|
-
```
|
|
56
|
+
admin-ui/
|
|
57
|
+
├── v1/
|
|
58
|
+
│ ├── client.js # Client-side entry point (registers Admin_Shell)
|
|
59
|
+
│ ├── server.js # Admin module (adapter layer + auth + telemetry)
|
|
60
|
+
│ ├── admin_auth_service.js # Session auth service
|
|
61
|
+
│ ├── admin_user_store.js # In-memory user store
|
|
62
|
+
│ ├── controls/
|
|
63
|
+
│ │ ├── admin_shell.js # Main shell with sidebar + content
|
|
64
|
+
│ │ ├── stat_card.js # Stat card control
|
|
65
|
+
│ │ └── group_box.js # Reusable grouped panel container
|
|
66
|
+
│ └── utils/
|
|
67
|
+
│ └── formatters.js # Shared formatting helpers
|
|
68
|
+
```
|
|
@@ -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
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Chapter 3: The Base Class Question
|
|
2
|
+
|
|
3
|
+
The most foundational design decision for both `Webpage` and `Website` is what they inherit from. This choice shapes everything that follows — how properties work, whether events exist, what ecosystem patterns are available.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Option 1: Plain Class
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
class Webpage {
|
|
11
|
+
constructor(spec = {}) {
|
|
12
|
+
this.name = spec.name;
|
|
13
|
+
this.title = spec.title;
|
|
14
|
+
this.path = spec.path;
|
|
15
|
+
this.content = spec.content;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### The case for it
|
|
21
|
+
|
|
22
|
+
- **Maximum simplicity** — anyone reading it knows exactly what it does
|
|
23
|
+
- **Zero dependencies** — doesn't import anything from jsgui3-html
|
|
24
|
+
- **Easy to test** — no setup, no teardown, no event system to mock
|
|
25
|
+
- **Familiar** — looks like any plain JavaScript class
|
|
26
|
+
|
|
27
|
+
### The case against it
|
|
28
|
+
|
|
29
|
+
- **Inconsistent with the ecosystem** — nearly every non-trivial object in jsgui3 extends `Evented_Class`. Controls do. The server does. Resources, publishers, routers — all evented. A plain `Webpage` would be the odd one out.
|
|
30
|
+
- **Can't add events later without breaking** — if you later want `Webpage` to emit events (e.g. `'ready'`), changing the base class is a breaking change for any subclasses.
|
|
31
|
+
- **No `.on()` / `.raise()` pattern** — these are the fundamental building blocks of jsgui3's event system, widely used for lifecycle coordination.
|
|
32
|
+
|
|
33
|
+
### When it makes sense
|
|
34
|
+
|
|
35
|
+
When the object is purely a data structure with no lifecycle, no reactivity, and no expectation that it will ever need events. A config object. A DTO.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Option 2: `obext` Properties
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const { prop } = require('obext');
|
|
43
|
+
|
|
44
|
+
class Webpage {
|
|
45
|
+
constructor(spec = {}) {
|
|
46
|
+
prop(this, 'name', spec.name);
|
|
47
|
+
prop(this, 'title', spec.title);
|
|
48
|
+
prop(this, 'path', spec.path);
|
|
49
|
+
prop(this, 'content', spec.content);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### The case for it
|
|
55
|
+
|
|
56
|
+
- **Consistent with jsgui3-server** — `server.js` uses `obext` extensively for its own properties (`disk_path_client_js`, `Ctrl`, `name`)
|
|
57
|
+
- **Encapsulation** — properties go through getter/setter, so behavior can be added later without changing the public API
|
|
58
|
+
- **Already in the dependency tree** — `jsgui3-html` depends on `obext`, so it's available at zero cost
|
|
59
|
+
|
|
60
|
+
### The case against it
|
|
61
|
+
|
|
62
|
+
- **Not a base class decision** — `obext` is a property mechanism, not a class hierarchy choice. You can use `obext` properties *and* extend `Evented_Class`. They're orthogonal.
|
|
63
|
+
- **Adds a layer of indirection** — every property access goes through a getter/setter. Usually negligible, but it's hidden complexity.
|
|
64
|
+
- **Not widely understood** — `obext` is a jsgui3 ecosystem tool, not a standard JS pattern. New contributors need to learn it.
|
|
65
|
+
|
|
66
|
+
### When it makes sense
|
|
67
|
+
|
|
68
|
+
When you want encapsulated properties but don't need (or aren't sure about) full event support. Good for a "grow into it" strategy.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Option 3: Extend `Evented_Class`
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const { Evented_Class } = require('jsgui3-html');
|
|
76
|
+
|
|
77
|
+
class Webpage extends Evented_Class {
|
|
78
|
+
constructor(spec = {}) {
|
|
79
|
+
super();
|
|
80
|
+
this.name = spec.name;
|
|
81
|
+
this.title = spec.title;
|
|
82
|
+
this.path = spec.path;
|
|
83
|
+
this.content = spec.content;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### The case for it
|
|
89
|
+
|
|
90
|
+
- **It's the ecosystem standard** — nearly everything in jsgui3 extends `Evented_Class`. Using it here means `Webpage` and `Website` participate in the same patterns as controls, resources, and the server itself.
|
|
91
|
+
- **The capability is free** — extending `Evented_Class` doesn't force you to fire events on every property. You get `.on()` and `.raise()` as available tools. If nobody listens, the cost is a few extra bytes of prototype chain.
|
|
92
|
+
- **Future-proof** — once you ship without `Evented_Class`, adding it later is a breaking change. Starting with it costs almost nothing.
|
|
93
|
+
- **Real use cases exist** — a Website could raise `'page-added'`, `'ready'`, or `'error'`. A Webpage could raise `'content-changed'` for live reload. The admin UI already watches resources via events.
|
|
94
|
+
- **Doesn't require per-property events** — this is a crucial distinction. Extending `Evented_Class` does NOT mean wrapping every property in a change-event-firing setter. You can have simple `this.name = spec.name` assignments and only raise events for meaningful lifecycle moments.
|
|
95
|
+
|
|
96
|
+
### The case against it
|
|
97
|
+
|
|
98
|
+
- **Couples to jsgui3-html** — `Evented_Class` comes from `jsgui3-html`. If someone wanted to use `Webpage` without `jsgui3-html`, they couldn't.
|
|
99
|
+
- **Perceived complexity** — someone reading the code might think "events? for a webpage?" and assume it's over-engineered, even if no events are actually used.
|
|
100
|
+
- **Boilerplate concern** — if you do decide to fire change events on properties, each one needs a manual getter/setter with `this.raise()`.
|
|
101
|
+
|
|
102
|
+
### When it makes sense
|
|
103
|
+
|
|
104
|
+
When the object participates in a lifecycle, when other parts of the system might want to observe it, or when you're building within an ecosystem where `Evented_Class` is the norm.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Option 4: Combined — Evented_Class + obext
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
const { Evented_Class } = require('jsgui3-html');
|
|
112
|
+
const { prop } = require('obext');
|
|
113
|
+
|
|
114
|
+
class Webpage extends Evented_Class {
|
|
115
|
+
constructor(spec = {}) {
|
|
116
|
+
super();
|
|
117
|
+
prop(this, 'name', spec.name);
|
|
118
|
+
prop(this, 'title', spec.title);
|
|
119
|
+
prop(this, 'path', spec.path);
|
|
120
|
+
prop(this, 'content', spec.content);
|
|
121
|
+
prop(this, 'meta', spec.meta || {});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### The case for it
|
|
127
|
+
|
|
128
|
+
- **Best of both** — event system for lifecycle, encapsulated properties for cleanliness
|
|
129
|
+
- **Matches server.js exactly** — the server itself uses `Evented_Class` as a base and `obext` for properties
|
|
130
|
+
- **Maximum future flexibility** — events AND property interception available
|
|
131
|
+
|
|
132
|
+
### The case against it
|
|
133
|
+
|
|
134
|
+
- **Most complex option** — two dependency systems in play
|
|
135
|
+
- **Is the combination actually needed?** — if nobody is observing property changes on webpages, `obext` adds machinery without a use case
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Discussion: Boilerplate vs. Capability
|
|
140
|
+
|
|
141
|
+
A common concern with `Evented_Class` is the boilerplate needed for per-property change events:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
get title() { return this._title; }
|
|
145
|
+
set title(v) {
|
|
146
|
+
const old = this._title;
|
|
147
|
+
this._title = v;
|
|
148
|
+
this.raise('change', { field: 'title', old, value: v });
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
But this is a **false dilemma**. You don't have to choose between "no events" and "events on every property." The pragmatic approach:
|
|
153
|
+
|
|
154
|
+
1. **Extend `Evented_Class`** — get the capability for free
|
|
155
|
+
2. **Use simple property assignment** — `this.title = spec.title`
|
|
156
|
+
3. **Raise events only for meaningful actions** — `this.raise('page-added', page)`, `this.raise('ready')`
|
|
157
|
+
|
|
158
|
+
This is exactly how the server works. It extends `Evented_Class` and raises `'ready'`, `'listening'`, `'starting'` — not per-property change events.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Comparison Table
|
|
163
|
+
|
|
164
|
+
| Criterion | Plain | obext | Evented | Combined |
|
|
165
|
+
|---|:---:|:---:|:---:|:---:|
|
|
166
|
+
| Simplicity | ★★★ | ★★☆ | ★★☆ | ★★☆ |
|
|
167
|
+
| Ecosystem consistency | ★☆☆ | ★★☆ | ★★★ | ★★★ |
|
|
168
|
+
| Zero jsgui3-html coupling | ★★★ | ★★☆ | ☆☆☆ | ☆☆☆ |
|
|
169
|
+
| Future event support | ☆☆☆ | ★☆☆ | ★★★ | ★★★ |
|
|
170
|
+
| Property encapsulation | ☆☆☆ | ★★★ | ★☆☆ | ★★★ |
|
|
171
|
+
| Matches server.js pattern | ☆☆☆ | ★★☆ | ★★☆ | ★★★ |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## The Author's View
|
|
176
|
+
|
|
177
|
+
Both modules will depend on `jsgui3-html` (this is a stated requirement). Given that, the coupling argument against `Evented_Class` disappears — you're already importing `jsgui3-html`.
|
|
178
|
+
|
|
179
|
+
In an ecosystem where `Evented_Class` is the norm, not using it requires justification. The justification would need to be "this object will never participate in lifecycle events" — but a Website with pages, API endpoints, and a publishing pipeline absolutely will.
|
|
180
|
+
|
|
181
|
+
The recommended starting point is **Option 3** (Evented_Class with simple properties), with the option to add `obext` for specific properties if encapsulation needs arise.
|