jsgui3-server 0.0.140 → 0.0.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/agents/jsgui3-server.agent.md +699 -0
- package/.github/instructions/copilot.instructions.md +180 -0
- package/.playwright-mcp/page-2025-11-29T23-39-18-629Z.png +0 -0
- package/.playwright-mcp/page-2025-11-29T23-39-31-903Z.png +0 -0
- package/.playwright-mcp/page-2025-11-30T00-33-56-265Z.png +0 -0
- package/.playwright-mcp/page-2025-11-30T00-34-06-619Z.png +0 -0
- package/docs/agent-development-guide.md +108 -4
- package/docs/api-reference.md +116 -0
- package/docs/controls-development.md +127 -0
- package/docs/css/luxuryObsidianCss.js +1203 -0
- package/docs/css/obsidian-scrollbars.css +370 -0
- package/docs/diagrams/jsgui3-stack.svg +568 -0
- package/docs/guides/JSGUI3_UI_ARCHITECTURE_GUIDE.md +2527 -0
- package/docs/guides/OBSIDIAN_LUXURY_DESIGN_GUIDE.md +847 -0
- package/docs/jsgui3-vs-express-comparison.svg +542 -0
- package/docs/jsgui3-vs-nestjs-comparison.svg +550 -0
- package/docs/publishers-guide.md +76 -0
- package/docs/troubleshooting.md +51 -0
- package/examples/controls/15) window, observable SSE/README.md +125 -0
- package/examples/controls/15) window, observable SSE/check.js +144 -0
- package/examples/controls/15) window, observable SSE/client.js +395 -0
- package/examples/controls/15) window, observable SSE/server.js +111 -0
- package/http/responders/static/Static_Route_HTTP_Responder.js +16 -16
- package/module.js +7 -0
- package/package.json +9 -8
- package/port-utils.js +112 -0
- package/serve-factory.js +27 -5
- package/tests/README.md +40 -26
- package/tests/examples-controls.e2e.test.js +164 -0
- package/tests/observable-sse.test.js +363 -0
- package/tests/port-utils.test.js +114 -0
- package/tests/test-runner.js +13 -12
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
# jsgui3-server Agent Instructions
|
|
2
|
+
|
|
3
|
+
**Purpose**: Agent guidance for working on the jsgui3-server codebase - the ES6 JSGUI server that delivers controls to the browser.
|
|
4
|
+
|
|
5
|
+
**Core Principle**: jsgui3-server bundles and serves jsgui3 controls, handling JavaScript bundling (ESBuild), CSS extraction, and HTTP publishing. All UI controls inherit from `Active_HTML_Document` and run isomorphically (server-rendered, client-activated).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⚠️ CRITICAL: Understand the Full Stack
|
|
10
|
+
|
|
11
|
+
**You cannot effectively work on jsgui3-server without understanding the JSGUI3 ecosystem.** This server is the delivery mechanism for a multi-package framework. Changes here often involve understanding behavior defined in dependent packages.
|
|
12
|
+
|
|
13
|
+
### The JSGUI3 Package Ecosystem
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ YOUR APPLICATION │
|
|
18
|
+
│ (client.js with your Custom_Control class) │
|
|
19
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ jsgui3-server (THIS REPO) │
|
|
24
|
+
│ • HTTP server, routing, publishers │
|
|
25
|
+
│ • ESBuild bundling of client JS │
|
|
26
|
+
│ • CSS extraction from control classes │
|
|
27
|
+
│ • Server-side rendering → client activation │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
29
|
+
│ │ │ │
|
|
30
|
+
▼ ▼ ▼ ▼
|
|
31
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
32
|
+
│jsgui3-client│ │ jsgui3-html │ │jsgui3-webpage│ │jsgui3-website│
|
|
33
|
+
│ │ │ │ │ │ │ │
|
|
34
|
+
│ Browser DOM │ │HTML element │ │ Single page │ │ Multi-page │
|
|
35
|
+
│ abstraction │ │ classes │ │ abstraction │ │ site model │
|
|
36
|
+
│ & controls │ │ (div, span) │ │ │ │ │
|
|
37
|
+
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
|
38
|
+
│ │
|
|
39
|
+
▼ ▼
|
|
40
|
+
┌─────────────────────────────────┐
|
|
41
|
+
│ obext │
|
|
42
|
+
│ Observable data objects with │
|
|
43
|
+
│ field() reactive properties │
|
|
44
|
+
│ (Data_Object, Data_Value) │
|
|
45
|
+
└─────────────────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Package Responsibilities
|
|
49
|
+
|
|
50
|
+
| Package | Version | Role | Key Classes/Exports |
|
|
51
|
+
|---------|---------|------|---------------------|
|
|
52
|
+
| **jsgui3-server** | 0.0.140 | HTTP delivery, bundling | `Server`, `serve()`, Publishers |
|
|
53
|
+
| **jsgui3-client** | 0.0.120 | Browser-side control system | `Control`, DOM abstractions |
|
|
54
|
+
| **jsgui3-html** | 0.0.170 | HTML element hierarchy | `div`, `span`, `button`, `input`, etc. |
|
|
55
|
+
| **jsgui3-webpage** | 0.0.8 | Single page model | `Webpage` |
|
|
56
|
+
| **jsgui3-website** | 0.0.8 | Multi-page site model | `Website` |
|
|
57
|
+
| **obext** | 0.0.31 | Reactive data binding | `Data_Object`, `Data_Value`, `field()` |
|
|
58
|
+
| **lang-tools** | 0.0.41 | Type checking utilities | `tof()`, `get()`, `set()` |
|
|
59
|
+
| **fnl/fnlfs** | 0.0.37/34 | Functional utilities, async FS | Functional patterns |
|
|
60
|
+
|
|
61
|
+
### Why This Matters
|
|
62
|
+
|
|
63
|
+
1. **Control classes** (`Active_HTML_Document`) inherit from `jsgui3-client`, not this repo
|
|
64
|
+
2. **HTML elements** (`this.body.add.div()`) come from `jsgui3-html`
|
|
65
|
+
3. **Data binding** (`field()`, `Data_Object`) comes from `obext`
|
|
66
|
+
4. **The `context` object** is created by `jsgui3-client` and flows through everything
|
|
67
|
+
5. **CSS extraction** looks for static `.css` properties defined by `jsgui3-client` patterns
|
|
68
|
+
|
|
69
|
+
### Isomorphic Lifecycle (The Key Concept)
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
SERVER SIDE CLIENT SIDE
|
|
73
|
+
─────────── ───────────
|
|
74
|
+
1. new My_Control(spec)
|
|
75
|
+
└─ compose() builds virtual DOM
|
|
76
|
+
|
|
77
|
+
2. control.render_html()
|
|
78
|
+
└─ Serializes to HTML string
|
|
79
|
+
|
|
80
|
+
3. HTTP Response ────────────────────► Browser receives HTML
|
|
81
|
+
|
|
82
|
+
4. new My_Control({ el: dom_element })
|
|
83
|
+
└─ spec.el present = HYDRATION
|
|
84
|
+
└─ compose() SKIPPED (if (!spec.el))
|
|
85
|
+
|
|
86
|
+
5. control.activate()
|
|
87
|
+
└─ Binds events to real DOM
|
|
88
|
+
└─ Runs client-only code
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**This is why `if (!spec.el) { compose(); }` is mandatory** — without it, content duplicates on activation.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## ⚡ Quick Start (30 seconds)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Run any example:
|
|
99
|
+
cd "examples/controls/1) window"
|
|
100
|
+
node server.js
|
|
101
|
+
# → Open http://localhost:52000
|
|
102
|
+
|
|
103
|
+
# Run tests:
|
|
104
|
+
npm test # All tests
|
|
105
|
+
npm run test:mocha # Mocha tests
|
|
106
|
+
npm run test:bundlers # Bundler tests only
|
|
107
|
+
npm run test:publishers # Publisher tests
|
|
108
|
+
|
|
109
|
+
# CLI (defaults to port 8080):
|
|
110
|
+
node cli.js serve --port 8080
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Auto-Port Selection
|
|
114
|
+
|
|
115
|
+
The server supports automatic free port selection to avoid conflicts:
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
// Using Server.serve() with auto port
|
|
119
|
+
Server.serve({
|
|
120
|
+
Ctrl: My_Control,
|
|
121
|
+
src_path_client_js,
|
|
122
|
+
port: 'auto' // Auto-select a free port
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Or port: 0 for OS-assigned port
|
|
126
|
+
Server.serve({ Ctrl, port: 0 });
|
|
127
|
+
|
|
128
|
+
// Port utilities available directly
|
|
129
|
+
const { get_free_port, is_port_available } = require('jsgui3-server');
|
|
130
|
+
const port = await get_free_port();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🏗️ Architecture at a Glance
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Server.serve({ Ctrl, src_path_client_js })
|
|
139
|
+
│
|
|
140
|
+
▼
|
|
141
|
+
┌─────────────────────────────────────────┐
|
|
142
|
+
│ JSGUI_Single_Process_Server │
|
|
143
|
+
│ │
|
|
144
|
+
│ ┌──────────┐ ┌──────────┐ ┌───────┐ │
|
|
145
|
+
│ │ Bundler │ │ Router │ │ Pool │ │
|
|
146
|
+
│ │ (ESBuild)│ │ (Routes) │ │(Rsrcs)│ │
|
|
147
|
+
│ └────┬─────┘ └────┬─────┘ └───┬───┘ │
|
|
148
|
+
│ │ │ │ │
|
|
149
|
+
│ ▼ ▼ ▼ │
|
|
150
|
+
│ ┌──────────────────────────────────┐ │
|
|
151
|
+
│ │ Publishers │ │
|
|
152
|
+
│ │ • http-webpage-publisher │ │
|
|
153
|
+
│ │ • http-function-publisher │ │
|
|
154
|
+
│ │ • http-website-publisher │ │
|
|
155
|
+
│ │ • http-css-publisher │ │
|
|
156
|
+
│ │ • http-js-publisher │ │
|
|
157
|
+
│ │ • http-html-publisher │ │
|
|
158
|
+
│ │ • http-image (png/jpeg/svg) │ │
|
|
159
|
+
│ │ • + 7 more specialized types │ │
|
|
160
|
+
│ └──────────────────────────────────┘ │
|
|
161
|
+
└─────────────────────────────────────────┘
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Key Files:**
|
|
165
|
+
- `server.js` — `JSGUI_Single_Process_Server` class
|
|
166
|
+
- `serve-factory.js` — `Server.serve()` simplified API
|
|
167
|
+
- `cli.js` — Command-line interface (default port 8080)
|
|
168
|
+
- `publishers/` — HTTP content type handlers (14+ types)
|
|
169
|
+
- `controls/Active_HTML_Document.js` — Base control class
|
|
170
|
+
- `resources/` — Data resource abstractions
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 📐 Conventions (MANDATORY)
|
|
175
|
+
|
|
176
|
+
### Naming
|
|
177
|
+
```javascript
|
|
178
|
+
// Variables, functions, utilities → snake_case
|
|
179
|
+
const my_variable = 'value';
|
|
180
|
+
function process_data(input) { ... }
|
|
181
|
+
|
|
182
|
+
// Classes and constructors → PascalCase
|
|
183
|
+
class My_Custom_Control extends Active_HTML_Document { ... }
|
|
184
|
+
class HTTP_Function_Publisher extends HTTP_Publisher { ... }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Control Pattern (The Canonical Form)
|
|
188
|
+
```javascript
|
|
189
|
+
class My_Control extends Active_HTML_Document {
|
|
190
|
+
constructor(spec = {}) {
|
|
191
|
+
// 1. Set __type_name BEFORE super()
|
|
192
|
+
spec.__type_name = spec.__type_name || 'my_control';
|
|
193
|
+
super(spec);
|
|
194
|
+
|
|
195
|
+
// 2. Extract context
|
|
196
|
+
const { context } = this;
|
|
197
|
+
|
|
198
|
+
// 3. Define compose() for UI building
|
|
199
|
+
const compose = () => {
|
|
200
|
+
// Build UI here
|
|
201
|
+
const btn = this.body.add.button('Click Me');
|
|
202
|
+
btn.on('click', () => console.log('clicked'));
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// 4. Conditional compose - only if not hydrating from DOM
|
|
206
|
+
if (!spec.el) { compose(); }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
activate() {
|
|
210
|
+
// 5. Guard against double activation
|
|
211
|
+
if (!this.__active) {
|
|
212
|
+
super.activate();
|
|
213
|
+
// Event binding, DOM measurements, etc.
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 6. CSS as static property
|
|
219
|
+
My_Control.css = `
|
|
220
|
+
.my_control { padding: 16px; }
|
|
221
|
+
.my_control button { cursor: pointer; }
|
|
222
|
+
`;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Server Startup Pattern
|
|
226
|
+
```javascript
|
|
227
|
+
const Server = require('jsgui3-server');
|
|
228
|
+
const My_Control = require('./client.js');
|
|
229
|
+
const src_path_client_js = __dirname + '/client.js';
|
|
230
|
+
|
|
231
|
+
// Simple API (recommended)
|
|
232
|
+
Server.serve({
|
|
233
|
+
Ctrl: My_Control,
|
|
234
|
+
src_path_client_js,
|
|
235
|
+
port: 8080
|
|
236
|
+
}).then(server => {
|
|
237
|
+
console.log(`Server running on port ${server.port}`);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Or with manual control:
|
|
241
|
+
const server = new Server({
|
|
242
|
+
Ctrl: My_Control,
|
|
243
|
+
src_path_client_js
|
|
244
|
+
});
|
|
245
|
+
server.on('ready', () => {
|
|
246
|
+
server.start(8080);
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Data Binding Pattern
|
|
251
|
+
```javascript
|
|
252
|
+
const { Data_Object, field } = require('obext');
|
|
253
|
+
|
|
254
|
+
class Counter_Control extends Active_HTML_Document {
|
|
255
|
+
constructor(spec = {}) {
|
|
256
|
+
spec.__type_name = spec.__type_name || 'counter_control';
|
|
257
|
+
super(spec);
|
|
258
|
+
|
|
259
|
+
// Observable data model
|
|
260
|
+
const model = new Data_Object({
|
|
261
|
+
count: field(0)
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const compose = () => {
|
|
265
|
+
const display = this.body.add.span();
|
|
266
|
+
const btn = this.body.add.button('+');
|
|
267
|
+
|
|
268
|
+
// Reactive binding - updates automatically when count changes
|
|
269
|
+
model.on('change.count', (e) => {
|
|
270
|
+
display.text = `Count: ${e.value}`;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
btn.on('click', () => {
|
|
274
|
+
model.count++;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Initial render
|
|
278
|
+
display.text = `Count: ${model.count}`;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
if (!spec.el) { compose(); }
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 🔧 API Endpoints
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
// In server.js (after server created)
|
|
292
|
+
server.on('ready', () => {
|
|
293
|
+
// Simple function endpoint
|
|
294
|
+
server.publish('/api/status', () => {
|
|
295
|
+
return { status: 'ok', uptime: process.uptime() };
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Async endpoint
|
|
299
|
+
server.publish('/api/data', async (req) => {
|
|
300
|
+
const data = await fetch_data();
|
|
301
|
+
return data;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
server.start(8080);
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## ⚠️ Known Issues (Check Before Working)
|
|
311
|
+
|
|
312
|
+
| Issue | Location | Impact |
|
|
313
|
+
|-------|----------|--------|
|
|
314
|
+
| Website publisher incomplete | `publishers/http-website-publisher.js` | Multi-page sites may fail |
|
|
315
|
+
| Multiple "ready" events | `server.js` start() | Race conditions |
|
|
316
|
+
| No default holding page | Server startup | Error on misconfiguration |
|
|
317
|
+
| `/admin` route not wired | Server startup | No admin interface |
|
|
318
|
+
| Inconsistent path naming | Various | `src_path_client_js` vs `disk_path_client_js` |
|
|
319
|
+
|
|
320
|
+
**Always check** `docs/agent-development-guide.md` for the current broken functionality tracker.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 📁 Directory Structure
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
jsgui3-server/
|
|
328
|
+
├── server.js # Main server class
|
|
329
|
+
├── serve-factory.js # Server.serve() API
|
|
330
|
+
├── cli.js # CLI interface
|
|
331
|
+
├── module.js # Package entry point
|
|
332
|
+
├── page-context.js # Server-side page context
|
|
333
|
+
├── static-page-context.js # Static rendering context
|
|
334
|
+
│
|
|
335
|
+
├── controls/ # Built-in controls
|
|
336
|
+
│ ├── Active_HTML_Document.js # Base control class
|
|
337
|
+
│ ├── page/ # Page-specific controls
|
|
338
|
+
│ └── panel/ # Panel controls
|
|
339
|
+
│
|
|
340
|
+
├── publishers/ # HTTP content handlers (14+ types)
|
|
341
|
+
│ ├── http-publisher.js # Base class
|
|
342
|
+
│ ├── http-webpage-publisher.js # Single-page apps
|
|
343
|
+
│ ├── http-website-publisher.js # Multi-page sites
|
|
344
|
+
│ ├── http-function-publisher.js # API endpoints
|
|
345
|
+
│ ├── http-css-publisher.js # CSS serving
|
|
346
|
+
│ ├── http-js-publisher.js # JavaScript serving
|
|
347
|
+
│ ├── http-html-publisher.js # HTML serving
|
|
348
|
+
│ ├── http-png-publisher.js # PNG images
|
|
349
|
+
│ ├── http-jpeg-publisher.js # JPEG images
|
|
350
|
+
│ ├── http-svg-publisher.js # SVG images
|
|
351
|
+
│ └── ... # + more specialized types
|
|
352
|
+
│
|
|
353
|
+
├── resources/ # Data abstractions
|
|
354
|
+
│ └── server-resource-pool.js
|
|
355
|
+
│
|
|
356
|
+
├── website/ # Website/webpage abstractions
|
|
357
|
+
├── examples/controls/ # Example applications (numbered)
|
|
358
|
+
├── tests/ # Test suite
|
|
359
|
+
└── docs/ # Documentation
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 🧪 Testing
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# All tests
|
|
368
|
+
npm test
|
|
369
|
+
|
|
370
|
+
# Specific test suites
|
|
371
|
+
npm run test:bundlers # Bundling system
|
|
372
|
+
npm run test:publishers # Publisher system
|
|
373
|
+
npm run test:config # Configuration validation
|
|
374
|
+
npm run test:e2e # End-to-end tests
|
|
375
|
+
npm run test:errors # Error handling
|
|
376
|
+
npm run test:content # Content analysis
|
|
377
|
+
npm run test:performance # Performance tests
|
|
378
|
+
npm run test:assigners # Assigner tests
|
|
379
|
+
|
|
380
|
+
# Debug mode
|
|
381
|
+
npm run test:debug
|
|
382
|
+
npm run test:verbose
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Test patterns:**
|
|
386
|
+
- Test files: `tests/*.test.js`
|
|
387
|
+
- Use `tests/test-runner.js` as the runner
|
|
388
|
+
- Integration tests should start/stop server cleanly
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## 📚 Documentation Index
|
|
393
|
+
|
|
394
|
+
| Need | Read |
|
|
395
|
+
|------|------|
|
|
396
|
+
| Quick start, architecture | `README.md` |
|
|
397
|
+
| Comprehensive API reference | `docs/comprehensive-documentation.md` |
|
|
398
|
+
| Server API design | `docs/simple-server-api-design.md` |
|
|
399
|
+
| System architecture | `docs/system-architecture.md` |
|
|
400
|
+
| Control development | `docs/controls-development.md` |
|
|
401
|
+
| Publisher system | `docs/publishers-guide.md` |
|
|
402
|
+
| Resources system | `docs/resources-guide.md` |
|
|
403
|
+
| Agent workflow patterns | `docs/GUIDE_TO_AGENTIC_WORKFLOWS_BY_GROK.md` |
|
|
404
|
+
| **Broken functionality** | `docs/agent-development-guide.md` |
|
|
405
|
+
| CLI reference | `docs/cli-reference.md` |
|
|
406
|
+
| Troubleshooting | `docs/troubleshooting.md` |
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## ❌ Anti-Patterns
|
|
411
|
+
|
|
412
|
+
### ❌ Don't bypass the context system
|
|
413
|
+
```javascript
|
|
414
|
+
// WRONG - Direct document access
|
|
415
|
+
document.getElementById('my-element');
|
|
416
|
+
|
|
417
|
+
// RIGHT - Use control's DOM abstraction
|
|
418
|
+
this.body.add.div({ id: 'my-element' });
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### ❌ Don't forget conditional compose
|
|
422
|
+
```javascript
|
|
423
|
+
// WRONG - Always composes (breaks hydration)
|
|
424
|
+
constructor(spec = {}) {
|
|
425
|
+
super(spec);
|
|
426
|
+
compose(); // ← Will duplicate content when activating
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// RIGHT - Conditional compose
|
|
430
|
+
constructor(spec = {}) {
|
|
431
|
+
super(spec);
|
|
432
|
+
const compose = () => { ... };
|
|
433
|
+
if (!spec.el) { compose(); } // ← Only compose if not hydrating
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### ❌ Don't activate twice
|
|
438
|
+
```javascript
|
|
439
|
+
// WRONG - No guard
|
|
440
|
+
activate() {
|
|
441
|
+
super.activate();
|
|
442
|
+
// Bindings happen every time activate() is called
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// RIGHT - Guard against double activation
|
|
446
|
+
activate() {
|
|
447
|
+
if (!this.__active) {
|
|
448
|
+
super.activate();
|
|
449
|
+
// Bindings happen once
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### ❌ Don't serve without waiting for 'ready'
|
|
455
|
+
```javascript
|
|
456
|
+
// WRONG - May start before bundling complete
|
|
457
|
+
const server = new Server({ Ctrl, src_path_client_js });
|
|
458
|
+
server.start(8080);
|
|
459
|
+
|
|
460
|
+
// RIGHT - Wait for ready event
|
|
461
|
+
server.on('ready', () => {
|
|
462
|
+
server.start(8080);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// OR use Server.serve() which handles this
|
|
466
|
+
Server.serve({ Ctrl, src_path_client_js, port: 8080 });
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## 🔄 Development Workflow
|
|
472
|
+
|
|
473
|
+
1. **Check existing patterns** in `examples/controls/` before creating new features (naming: `N) description`)
|
|
474
|
+
2. **Follow the numbered example convention** (`1) window`, `2) two windows`, etc.) for new examples
|
|
475
|
+
3. **Update `docs/agent-development-guide.md`** when finding broken functionality
|
|
476
|
+
4. **Run relevant tests** before and after changes
|
|
477
|
+
5. **CSS goes on the control class** as a static `.css` property, not in separate files
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## 🚀 Quick Recipes
|
|
482
|
+
|
|
483
|
+
### Add a new control
|
|
484
|
+
```bash
|
|
485
|
+
# Create in examples/controls/ using the naming convention:
|
|
486
|
+
# "N) descriptive name" where N is the next number
|
|
487
|
+
mkdir "examples/controls/15) window, my_feature"
|
|
488
|
+
# Add client.js (the control) and server.js (bootstrap)
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Debug bundling issues
|
|
492
|
+
```bash
|
|
493
|
+
JSGUI_DEBUG=1 node server.js
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Find CSS extraction issues
|
|
497
|
+
Check `bundler.extract_css_from_ctrl()` in bundler code - CSS must be a static property.
|
|
498
|
+
|
|
499
|
+
### Test a single publisher
|
|
500
|
+
```bash
|
|
501
|
+
npm run test:publishers
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## 📝 Session Protocol
|
|
507
|
+
|
|
508
|
+
When working on this codebase:
|
|
509
|
+
|
|
510
|
+
1. **Check known issues first** — Read `docs/agent-development-guide.md` "Known Issues" section
|
|
511
|
+
2. **Document what you find** — Update the broken functionality tracker immediately
|
|
512
|
+
3. **Follow patterns** — Use examples in `examples/controls/` as reference
|
|
513
|
+
4. **Test your changes** — Run relevant test suites
|
|
514
|
+
5. **Update docs** — Keep implementation status current
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 🎯 Strategic Direction: Making jsgui3-server Well-Rounded
|
|
519
|
+
|
|
520
|
+
**Philosophy**: jsgui3-server is designed to be a *complete GUI delivery system*, not a general-purpose HTTP framework. Focus improvements on what makes it uniquely valuable: delivering interactive controls from server to browser with minimal boilerplate.
|
|
521
|
+
|
|
522
|
+
### 🔮 Observable-First Architecture (Key Differentiator)
|
|
523
|
+
|
|
524
|
+
jsgui3-server uses `fnl` observables throughout. This is not just an implementation detail — it's a **strategic advantage** that should be exposed at the API level.
|
|
525
|
+
|
|
526
|
+
#### Current Observable Infrastructure
|
|
527
|
+
|
|
528
|
+
**The `obs()` factory** from `fnl` creates observables with `next`, `complete`, `error` callbacks:
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
const {obs} = require('fnl');
|
|
532
|
+
|
|
533
|
+
// Observable pattern used throughout jsgui3-server
|
|
534
|
+
const my_operation = obs((next, complete, error) => {
|
|
535
|
+
// Emit intermediate results
|
|
536
|
+
next({ progress: 50 });
|
|
537
|
+
next({ progress: 100 });
|
|
538
|
+
|
|
539
|
+
// Signal completion with final value
|
|
540
|
+
complete({ result: 'done' });
|
|
541
|
+
|
|
542
|
+
// Or signal error
|
|
543
|
+
// error(new Error('something failed'));
|
|
544
|
+
|
|
545
|
+
return []; // cleanup functions
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Consuming observables
|
|
549
|
+
my_operation.on('next', data => console.log('Progress:', data));
|
|
550
|
+
my_operation.on('complete', result => console.log('Done:', result));
|
|
551
|
+
my_operation.on('error', err => console.error('Failed:', err));
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
#### Where Observables Are Currently Used
|
|
555
|
+
|
|
556
|
+
| Location | Purpose |
|
|
557
|
+
|----------|---------|
|
|
558
|
+
| `publishers/http-observable-publisher.js` | **SSE streaming** — already implements Server-Sent Events! |
|
|
559
|
+
| `resources/processors/bundlers/webpage-bundler.js` | Multi-stage bundling with progress |
|
|
560
|
+
| `resources/processors/bundlers/js-bundler.js` | JS compilation progress |
|
|
561
|
+
| `resources/processors/bundlers/css-bundler.js` | CSS extraction progress |
|
|
562
|
+
| `publishers/http-website-publisher.js` | Website build pipeline |
|
|
563
|
+
| ESBuild bundlers | Async compilation with status updates |
|
|
564
|
+
|
|
565
|
+
#### Observable Publisher (Already Exists!)
|
|
566
|
+
|
|
567
|
+
`HTTP_Observable_Publisher` already implements SSE:
|
|
568
|
+
|
|
569
|
+
```javascript
|
|
570
|
+
// Server-side (existing code in http-observable-publisher.js)
|
|
571
|
+
const Observable_Publisher = require('./publishers/http-observable-publisher');
|
|
572
|
+
|
|
573
|
+
// Creates SSE endpoint that streams observable events
|
|
574
|
+
// Response format: text/event-stream with chunked transfer
|
|
575
|
+
// event: message\ndata: {"key": "value"}\n\n
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
#### Strategic Opportunity: Unified Observable API
|
|
579
|
+
|
|
580
|
+
**Current gap**: No easy way to publish an observable from `Server.serve()`. The infrastructure exists but isn't wired to the simple API.
|
|
581
|
+
|
|
582
|
+
**Proposed enhancement**:
|
|
583
|
+
```javascript
|
|
584
|
+
// Goal: Make this work seamlessly
|
|
585
|
+
server.publish('/api/progress', () => {
|
|
586
|
+
return obs((next, complete, error) => {
|
|
587
|
+
// Long-running operation with progress updates
|
|
588
|
+
for (let i = 0; i <= 100; i += 10) {
|
|
589
|
+
setTimeout(() => next({ progress: i }), i * 100);
|
|
590
|
+
}
|
|
591
|
+
setTimeout(() => complete({ status: 'done' }), 1100);
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// Function publisher should detect observable return type
|
|
596
|
+
// and automatically use SSE/WebSocket transport
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Client-side consumption** (future direction):
|
|
600
|
+
```javascript
|
|
601
|
+
// In browser, jsgui3-client could provide:
|
|
602
|
+
const stream = context.subscribe('/api/progress');
|
|
603
|
+
stream.on('next', data => update_progress_bar(data.progress));
|
|
604
|
+
stream.on('complete', result => show_complete(result));
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
#### Observable vs Promise vs Callback
|
|
608
|
+
|
|
609
|
+
| Return Type | Detection | Transport | Use Case |
|
|
610
|
+
|-------------|-----------|-----------|----------|
|
|
611
|
+
| **Observable** | Has `.on('next')` | SSE or WebSocket | Streaming, progress, real-time |
|
|
612
|
+
| **Promise** | `tf(result) === 'p'` | Single HTTP response | Async one-shot |
|
|
613
|
+
| **Plain value** | Object/string/array | Single HTTP response | Sync one-shot |
|
|
614
|
+
|
|
615
|
+
The `HTTP_Function_Publisher` already detects promises (`tfr === 'p'`). Extend to detect observables.
|
|
616
|
+
|
|
617
|
+
### What's Working Well (Don't Break These)
|
|
618
|
+
|
|
619
|
+
| Feature | Status | Why It Works |
|
|
620
|
+
|---------|--------|--------------|
|
|
621
|
+
| `Server.serve({ Ctrl })` | ✅ Solid | One-liner to serve a control — the core value proposition |
|
|
622
|
+
| HTTP_Webpage_Publisher | ✅ Solid | Bundles JS/CSS/HTML automatically |
|
|
623
|
+
| ESBuild bundling | ✅ Solid | Fast, reliable JS compilation |
|
|
624
|
+
| Function publishers | ✅ Solid | `server.publish('name', fn)` for JSON APIs |
|
|
625
|
+
| Control lifecycle | ✅ Solid | Isomorphic render → activate pattern |
|
|
626
|
+
|
|
627
|
+
### What Needs Completion (Highest Impact)
|
|
628
|
+
|
|
629
|
+
These are incomplete implementations that block real use cases:
|
|
630
|
+
|
|
631
|
+
#### 1. **HTTP_Website_Publisher** — Currently Broken
|
|
632
|
+
```
|
|
633
|
+
Location: publishers/http-website-publisher.js
|
|
634
|
+
Problem: Contains "Possibly missing website publishing code" — multi-page sites don't work
|
|
635
|
+
Impact: Users can't build multi-page apps without workarounds
|
|
636
|
+
```
|
|
637
|
+
**The fix**: Complete the bundling loop for `website.pages._arr.length > 1`. The code structure exists but throws `'NYI'`.
|
|
638
|
+
|
|
639
|
+
#### 2. **Default Holding Page** — Missing
|
|
640
|
+
```
|
|
641
|
+
Location: server.js constructor (when no Ctrl provided)
|
|
642
|
+
Problem: Server fails or serves nothing when started without content
|
|
643
|
+
Impact: Confusing first-run experience
|
|
644
|
+
```
|
|
645
|
+
**The fix**: Serve a minimal HTML page with "jsgui3-server running" + links to docs. This is a ~20 line addition.
|
|
646
|
+
|
|
647
|
+
#### 3. **Single "Ready" Signal** — Fragmented
|
|
648
|
+
```
|
|
649
|
+
Location: server.js start() method
|
|
650
|
+
Problem: Multiple places emit 'ready', unclear when truly ready
|
|
651
|
+
Impact: Race conditions, startup timing issues
|
|
652
|
+
```
|
|
653
|
+
**The fix**: Consolidate to one `this.raise('ready')` after all listeners bound. Remove redundant emissions.
|
|
654
|
+
|
|
655
|
+
### What Would Be Genuinely Useful (Medium Priority)
|
|
656
|
+
|
|
657
|
+
These align with jsgui3-server's purpose of delivering GUIs:
|
|
658
|
+
|
|
659
|
+
| Feature | Why It Fits | Approach |
|
|
660
|
+
|---------|-------------|----------|
|
|
661
|
+
| **Admin panel at `/admin`** | Server should be self-documenting | Wire up existing `Web_Admin_Panel_Control` |
|
|
662
|
+
| **Graceful shutdown** | Production readiness | Handle SIGINT, close HTTP servers, print confirmation |
|
|
663
|
+
| **Watch mode** | Development ergonomics | `cli.js dev` with file watching + auto-restart |
|
|
664
|
+
|
|
665
|
+
### What NOT to Add (Framework Bloat)
|
|
666
|
+
|
|
667
|
+
Resist adding features just because other frameworks have them:
|
|
668
|
+
|
|
669
|
+
| Avoid | Reason |
|
|
670
|
+
|-------|--------|
|
|
671
|
+
| Complex middleware chains | Express already exists; jsgui3 is for GUI delivery, not generic HTTP |
|
|
672
|
+
| Database integrations | Out of scope; users can add their own |
|
|
673
|
+
| Authentication systems | Too opinionated; provide hooks instead |
|
|
674
|
+
| Template engines (EJS/Pug) | jsgui3 controls ARE the templating system |
|
|
675
|
+
| REST scaffolding | Function publishers already handle this simply |
|
|
676
|
+
|
|
677
|
+
### The Litmus Test for New Features
|
|
678
|
+
|
|
679
|
+
Before adding anything, ask:
|
|
680
|
+
|
|
681
|
+
1. **Does it make serving a JSGUI3 control easier?** If not, probably don't add it.
|
|
682
|
+
2. **Can it be done in userland with existing APIs?** If yes, document the pattern instead.
|
|
683
|
+
3. **Does it work with the isomorphic lifecycle?** Server-render → client-activate must remain seamless.
|
|
684
|
+
|
|
685
|
+
### Recommended Next Steps (In Order)
|
|
686
|
+
|
|
687
|
+
1. **Fix HTTP_Website_Publisher** — unblocks multi-page apps
|
|
688
|
+
2. **Add default holding page** — improves first-run experience
|
|
689
|
+
3. **Consolidate ready signal** — eliminates race conditions
|
|
690
|
+
4. **Wire up `/admin` route** — makes server self-documenting
|
|
691
|
+
5. **Add graceful shutdown** — production readiness
|
|
692
|
+
6. **Integrate Observable_Publisher into function publisher** — auto-detect observable returns and use SSE
|
|
693
|
+
7. **Client-side observable consumption** — `context.subscribe()` API in jsgui3-client
|
|
694
|
+
|
|
695
|
+
Each of these is a focused fix, not a large refactor. The codebase structure is sound; it just needs completion of existing patterns.
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
699
|
+
*Last updated: November 2025 - Added full stack documentation, strategic direction, and observable-first architecture*
|