jsgui3-server 0.0.134 → 0.0.135
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/AGENTS.md +6 -0
- package/README.md +114 -18
- package/TODO.md +81 -0
- package/cli.js +96 -0
- package/docs/simple-server-api-design.md +702 -0
- package/examples/controls/14d) window, canvas globe/Clipping.js +0 -0
- package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +319 -762
- package/examples/controls/14d) window, canvas globe/RenderingPipeline.js +43 -0
- package/examples/controls/14d) window, canvas globe/pipeline/BaseStage.js +46 -0
- package/examples/controls/14d) window, canvas globe/pipeline/ClippingStage.js +29 -0
- package/examples/controls/14d) window, canvas globe/pipeline/ComposeStage.js +15 -0
- package/examples/controls/14d) window, canvas globe/pipeline/ContinentsStage.js +116 -0
- package/examples/controls/14d) window, canvas globe/pipeline/GridStage.js +105 -0
- package/examples/controls/14d) window, canvas globe/pipeline/HUDStage.js +10 -0
- package/examples/controls/14d) window, canvas globe/pipeline/ShadeSphereStage.js +153 -0
- package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +23 -0
- package/examples/controls/14d) window, canvas globe/pipeline/clipping/FrontFaceStrategy.js +46 -0
- package/examples/controls/14d) window, canvas globe/pipeline/clipping/PlaneClipStrategy.js +339 -0
- package/module.js +3 -1
- package/package.json +9 -3
- package/resources/_old_website-resource.js +10 -2
- package/resources/jsbuilder/babel/deep_iterate/deep_iterate_babel.js +37 -0
- package/serve-factory.js +221 -0
- package/serve-helpers.js +95 -0
- package/server.js +93 -7
- package/tests/cli.test.js +66 -0
- package/tests/dummy-client.js +10 -0
- package/tests/serve.test.js +210 -0
- package/.vscode/settings.json +0 -13
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
# Simple Server API Design
|
|
2
|
+
|
|
3
|
+
**Goal:** Make jsgui3-server trivially easy to use for simple cases while preserving full power for complex scenarios.
|
|
4
|
+
|
|
5
|
+
## Design Philosophy
|
|
6
|
+
|
|
7
|
+
1. **Progressive Disclosure** – Simple things should be simple; complex things should be possible
|
|
8
|
+
2. **Convention Over Configuration** – Smart defaults; minimal required config
|
|
9
|
+
3. **Consistent Patterns** – Same API shape at every scale
|
|
10
|
+
4. **Zero-to-Hero Path** – Clear upgrade path from simple to advanced
|
|
11
|
+
5. **Composable** – Mix and match features (pages, APIs, static files)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Current Pain Points
|
|
16
|
+
|
|
17
|
+
### Boilerplate Example (Current)
|
|
18
|
+
Every example needs ~30 lines of identical code:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
const jsgui = require('./client');
|
|
22
|
+
const {Demo_UI} = jsgui.controls;
|
|
23
|
+
const Server = require('../../../server');
|
|
24
|
+
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
const server = new Server({
|
|
27
|
+
Ctrl: Demo_UI,
|
|
28
|
+
src_path_client_js: require.resolve('./client.js')
|
|
29
|
+
});
|
|
30
|
+
console.log('waiting for server ready event');
|
|
31
|
+
server.on('ready', () => {
|
|
32
|
+
console.log('server ready');
|
|
33
|
+
server.start(52000, (err) => {
|
|
34
|
+
if (err) throw err;
|
|
35
|
+
console.log('server started');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Problems:
|
|
42
|
+
- Manual path resolution (`require.resolve`)
|
|
43
|
+
- Hard-coded ports (no ENV var support)
|
|
44
|
+
- Verbose event handling (3 console.logs)
|
|
45
|
+
- No convention-based discovery
|
|
46
|
+
- Callback nesting
|
|
47
|
+
- Repetitive across all 42 examples
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Proposed API Layers
|
|
52
|
+
|
|
53
|
+
### Layer 0: Single Control (Simplest)
|
|
54
|
+
|
|
55
|
+
**Use Case:** Quick demo, single-page app, learning
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Ultra-minimal – just serve a control
|
|
59
|
+
const Server = require('jsgui3-server');
|
|
60
|
+
const { MyControl } = require('./client').controls;
|
|
61
|
+
|
|
62
|
+
Server.serve(MyControl);
|
|
63
|
+
// Runs on port 8080, auto-discovers client.js in same directory
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// With port override
|
|
68
|
+
Server.serve(MyControl, { port: 3000 });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// Explicit object notation
|
|
73
|
+
Server.serve({
|
|
74
|
+
ctrl: MyControl,
|
|
75
|
+
port: 3000,
|
|
76
|
+
host: 'localhost'
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Defaults:**
|
|
81
|
+
- Port: `8080` (or `process.env.PORT`)
|
|
82
|
+
- Host: All IPv4 interfaces (or `process.env.HOST`)
|
|
83
|
+
- Auto-discovers `client.js` in same directory as `server.js`
|
|
84
|
+
- Auto-bundles CSS/JS
|
|
85
|
+
- Routes: `/` → control, `/js/js.js` → bundled JS, `/css/css.css` → bundled CSS
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### Layer 1: Single Page with Metadata
|
|
90
|
+
|
|
91
|
+
**Use Case:** Add page title, description, favicon, custom path
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
Server.serve({
|
|
95
|
+
page: {
|
|
96
|
+
content: MyControl,
|
|
97
|
+
title: 'My Awesome App',
|
|
98
|
+
description: 'A jsgui3 application',
|
|
99
|
+
path: '/', // Default
|
|
100
|
+
favicon: './favicon.ico' // Optional
|
|
101
|
+
},
|
|
102
|
+
port: 3000
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Benefits:**
|
|
107
|
+
- Proper HTML `<title>` and meta tags
|
|
108
|
+
- Explicit page configuration
|
|
109
|
+
- Still single-file simplicity
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### Layer 2: Multiple Pages (Multi-Page App)
|
|
114
|
+
|
|
115
|
+
**Use Case:** Website with multiple routes, each with different content
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
Server.serve({
|
|
119
|
+
pages: {
|
|
120
|
+
'/': {
|
|
121
|
+
content: HomeControl,
|
|
122
|
+
title: 'Home - My Site'
|
|
123
|
+
},
|
|
124
|
+
'/about': {
|
|
125
|
+
content: AboutControl,
|
|
126
|
+
title: 'About Us'
|
|
127
|
+
},
|
|
128
|
+
'/contact': {
|
|
129
|
+
content: ContactControl,
|
|
130
|
+
title: 'Contact'
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
port: 3000
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Auto-routing:**
|
|
138
|
+
- Each key becomes a route
|
|
139
|
+
- Shared JS/CSS bundle automatically included on all pages
|
|
140
|
+
- Pages can reference different client files if needed
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### Layer 3: Pages + API Endpoints
|
|
145
|
+
|
|
146
|
+
**Use Case:** Frontend controls + backend API functions
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
Server.serve({
|
|
150
|
+
pages: {
|
|
151
|
+
'/': { content: DashboardControl, title: 'Dashboard' }
|
|
152
|
+
},
|
|
153
|
+
api: {
|
|
154
|
+
// GET/POST /api/hello
|
|
155
|
+
'hello': (name) => `Hello ${name || 'World'}!`,
|
|
156
|
+
|
|
157
|
+
// GET/POST /api/calculate
|
|
158
|
+
'calculate': ({ a, b }) => ({ sum: a + b, product: a * b }),
|
|
159
|
+
|
|
160
|
+
// Async function support
|
|
161
|
+
'users': async () => {
|
|
162
|
+
const users = await db.getUsers();
|
|
163
|
+
return users;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
port: 3000
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**API Conventions:**
|
|
171
|
+
- Routes prefixed with `/api/` automatically
|
|
172
|
+
- JSON input/output auto-parsed/serialized
|
|
173
|
+
- String returns sent as `text/plain`
|
|
174
|
+
- Object/Array returns sent as `application/json`
|
|
175
|
+
- Promises automatically awaited
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Layer 4: Pages + API + Static Files
|
|
180
|
+
|
|
181
|
+
**Use Case:** Full web application with assets
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
Server.serve({
|
|
185
|
+
pages: {
|
|
186
|
+
'/': { content: HomeControl, title: 'Home' },
|
|
187
|
+
'/gallery': { content: GalleryControl, title: 'Gallery' }
|
|
188
|
+
},
|
|
189
|
+
api: {
|
|
190
|
+
'images': () => fs.readdirSync('./images').map(f => `/images/${f}`)
|
|
191
|
+
},
|
|
192
|
+
static: {
|
|
193
|
+
'/images': './public/images',
|
|
194
|
+
'/assets': './public/assets'
|
|
195
|
+
},
|
|
196
|
+
port: 3000
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Static file handling:**
|
|
201
|
+
- Automatic MIME type detection
|
|
202
|
+
- Efficient streaming for large files
|
|
203
|
+
- Optional caching headers
|
|
204
|
+
- Directory listing (configurable)
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Layer 5: Shorthand Routes (Mixed Types)
|
|
209
|
+
|
|
210
|
+
**Use Case:** Mix controls, functions, and static content flexibly
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
Server.serve({
|
|
214
|
+
routes: {
|
|
215
|
+
'/': HomeControl, // Control at root
|
|
216
|
+
'/dashboard': DashboardControl, // Control at path
|
|
217
|
+
'/api/time': () => new Date(), // Function endpoint
|
|
218
|
+
'/docs': './documentation' // Static directory
|
|
219
|
+
},
|
|
220
|
+
port: 3000
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Auto-detection:**
|
|
225
|
+
- Control constructor → webpage
|
|
226
|
+
- Function → API endpoint
|
|
227
|
+
- String path → static directory
|
|
228
|
+
- Object with `content` → full page config
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### Layer 6: Full Website Object
|
|
233
|
+
|
|
234
|
+
**Use Case:** Complex multi-page sites, existing `Website` instances
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
const { Website, Webpage } = require('jsgui3-website');
|
|
238
|
+
|
|
239
|
+
const myWebsite = new Website({
|
|
240
|
+
name: 'My Corporate Site'
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
myWebsite.pages.push(new Webpage({
|
|
244
|
+
name: 'Home',
|
|
245
|
+
title: 'Welcome',
|
|
246
|
+
content: HomeControl,
|
|
247
|
+
path: '/'
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
myWebsite.pages.push(new Webpage({
|
|
251
|
+
name: 'Products',
|
|
252
|
+
title: 'Our Products',
|
|
253
|
+
content: ProductsControl,
|
|
254
|
+
path: '/products'
|
|
255
|
+
}));
|
|
256
|
+
|
|
257
|
+
Server.serve({
|
|
258
|
+
website: myWebsite,
|
|
259
|
+
api: { /* ... */ },
|
|
260
|
+
static: { /* ... */ },
|
|
261
|
+
port: 3000
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Benefits:**
|
|
266
|
+
- Full `Website` API available
|
|
267
|
+
- Compose complex structures programmatically
|
|
268
|
+
- Mix with API and static files
|
|
269
|
+
- Reuse website objects
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### Layer 7: Legacy/Advanced (Current API)
|
|
274
|
+
|
|
275
|
+
**Still fully supported for maximum control:**
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
const server = new Server({
|
|
279
|
+
Ctrl: MyControl,
|
|
280
|
+
src_path_client_js: require.resolve('./client.js'),
|
|
281
|
+
debug: true,
|
|
282
|
+
https_options: { /* ... */ }
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
server.on('ready', () => {
|
|
286
|
+
server.publish('endpoint', handler);
|
|
287
|
+
server.start(port, callback);
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Convention-Based Defaults
|
|
294
|
+
|
|
295
|
+
### Auto-Discovery Rules
|
|
296
|
+
|
|
297
|
+
1. **Client JS:**
|
|
298
|
+
- Look for `client.js` in same directory as `server.js`
|
|
299
|
+
- Look for `index.js` with exported controls
|
|
300
|
+
- Can be overridden with `clientPath` option
|
|
301
|
+
|
|
302
|
+
2. **Control Detection:**
|
|
303
|
+
- If `client.js` exports single control → use it
|
|
304
|
+
- If exports `controls.Demo_UI` → use Demo_UI
|
|
305
|
+
- If exports multiple → use first in `controls` object
|
|
306
|
+
- Can be overridden with `ctrl` option
|
|
307
|
+
|
|
308
|
+
3. **Port:**
|
|
309
|
+
- Use `process.env.PORT` if set
|
|
310
|
+
- Default to `8080`
|
|
311
|
+
- Override with `port` option
|
|
312
|
+
|
|
313
|
+
4. **Host:**
|
|
314
|
+
- Use `process.env.HOST` if set (single interface)
|
|
315
|
+
- Default to all IPv4 interfaces
|
|
316
|
+
- Override with `host` option
|
|
317
|
+
|
|
318
|
+
5. **Debug Mode:**
|
|
319
|
+
- Use `process.env.JSGUI_DEBUG` if set
|
|
320
|
+
- Default to `false`
|
|
321
|
+
- Override with `debug` option
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Environment Variable Support
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# Port
|
|
329
|
+
PORT=3000 node server.js
|
|
330
|
+
|
|
331
|
+
# Specific host/interface
|
|
332
|
+
HOST=127.0.0.1 node server.js
|
|
333
|
+
|
|
334
|
+
# Debug mode (verbose logging, no minification)
|
|
335
|
+
JSGUI_DEBUG=1 node server.js
|
|
336
|
+
|
|
337
|
+
# Combine
|
|
338
|
+
PORT=3000 HOST=localhost JSGUI_DEBUG=1 node server.js
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Async/Promise Support
|
|
344
|
+
|
|
345
|
+
### Callback Style (Legacy)
|
|
346
|
+
```javascript
|
|
347
|
+
Server.serve({ ctrl: MyControl, port: 3000 }, (err, server) => {
|
|
348
|
+
if (err) throw err;
|
|
349
|
+
console.log('Server running');
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Promise Style (Modern)
|
|
354
|
+
```javascript
|
|
355
|
+
const server = await Server.serve({ ctrl: MyControl, port: 3000 });
|
|
356
|
+
console.log('Server running on port', server.port);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Event Style (Current)
|
|
360
|
+
```javascript
|
|
361
|
+
const server = Server.serve({ ctrl: MyControl, port: 3000 });
|
|
362
|
+
server.on('ready', () => console.log('Ready!'));
|
|
363
|
+
server.on('started', () => console.log('Started!'));
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Configuration File Support
|
|
369
|
+
|
|
370
|
+
### `jsgui.config.js` (Optional)
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
module.exports = {
|
|
374
|
+
port: 3000,
|
|
375
|
+
host: 'localhost',
|
|
376
|
+
debug: true,
|
|
377
|
+
pages: {
|
|
378
|
+
'/': { content: require('./controls/home'), title: 'Home' },
|
|
379
|
+
'/about': { content: require('./controls/about'), title: 'About' }
|
|
380
|
+
},
|
|
381
|
+
api: {
|
|
382
|
+
'hello': name => `Hello ${name}`
|
|
383
|
+
},
|
|
384
|
+
static: {
|
|
385
|
+
'/assets': './public'
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Usage:**
|
|
391
|
+
```javascript
|
|
392
|
+
// Reads jsgui.config.js automatically
|
|
393
|
+
Server.serve();
|
|
394
|
+
|
|
395
|
+
// Or specify config file
|
|
396
|
+
Server.serve({ config: './my-config.js' });
|
|
397
|
+
|
|
398
|
+
// Config file + overrides
|
|
399
|
+
Server.serve({ config: './my-config.js', port: 4000 });
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Special Modes
|
|
405
|
+
|
|
406
|
+
### Preview Mode
|
|
407
|
+
**Opens browser automatically, uses ephemeral port:**
|
|
408
|
+
```javascript
|
|
409
|
+
await Server.serve.preview({ ctrl: MyControl });
|
|
410
|
+
// Opens http://localhost:58392/ automatically
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Development Mode
|
|
414
|
+
**Auto-restart on file changes:**
|
|
415
|
+
```javascript
|
|
416
|
+
Server.serve.dev({
|
|
417
|
+
ctrl: MyControl,
|
|
418
|
+
watch: ['./client.js', './controls/*.js']
|
|
419
|
+
});
|
|
420
|
+
// Restarts when files change
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Build Mode
|
|
424
|
+
**Pre-bundle for production (no server start):**
|
|
425
|
+
```javascript
|
|
426
|
+
await Server.serve.build({
|
|
427
|
+
ctrl: MyControl,
|
|
428
|
+
output: './dist'
|
|
429
|
+
});
|
|
430
|
+
// Creates ./dist with bundled HTML/CSS/JS
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## CLI Integration
|
|
436
|
+
|
|
437
|
+
### Serve Examples Directly
|
|
438
|
+
```powershell
|
|
439
|
+
# Serve a specific example
|
|
440
|
+
node cli.js serve-example "7) window, month_view" --port 3000
|
|
441
|
+
|
|
442
|
+
# Serve by client.js path
|
|
443
|
+
node cli.js serve-client ./examples/controls/7*/client.js --port 3000
|
|
444
|
+
|
|
445
|
+
# Serve current directory (auto-discover)
|
|
446
|
+
node cli.js serve --port 3000
|
|
447
|
+
|
|
448
|
+
# Preview mode (ephemeral port, auto-open browser)
|
|
449
|
+
node cli.js preview ./examples/controls/7*/client.js
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Error Messages
|
|
455
|
+
|
|
456
|
+
### Clear, Actionable Errors
|
|
457
|
+
|
|
458
|
+
**Before:**
|
|
459
|
+
```
|
|
460
|
+
TypeError: Cannot read property 'content' of undefined
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**After:**
|
|
464
|
+
```
|
|
465
|
+
jsgui3-server: Control not found
|
|
466
|
+
|
|
467
|
+
Looking for a control to serve but none was specified.
|
|
468
|
+
|
|
469
|
+
Did you mean to:
|
|
470
|
+
- Pass a control: Server.serve(MyControl)
|
|
471
|
+
- Specify ctrl option: Server.serve({ ctrl: MyControl })
|
|
472
|
+
- Create a client.js with exported controls?
|
|
473
|
+
|
|
474
|
+
Current directory: C:\Users\james\project
|
|
475
|
+
Searched for: client.js, index.js
|
|
476
|
+
|
|
477
|
+
See docs: https://github.com/metabench/jsgui3-server#quick-start
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Migration Path
|
|
483
|
+
|
|
484
|
+
### Current Example (30 lines)
|
|
485
|
+
```javascript
|
|
486
|
+
const jsgui = require('./client');
|
|
487
|
+
const {Demo_UI} = jsgui.controls;
|
|
488
|
+
const Server = require('../../../server');
|
|
489
|
+
|
|
490
|
+
if (require.main === module) {
|
|
491
|
+
const server = new Server({
|
|
492
|
+
Ctrl: Demo_UI,
|
|
493
|
+
src_path_client_js: require.resolve('./client.js')
|
|
494
|
+
});
|
|
495
|
+
console.log('waiting for server ready event');
|
|
496
|
+
server.on('ready', () => {
|
|
497
|
+
console.log('server ready');
|
|
498
|
+
server.start(52000, (err) => {
|
|
499
|
+
if (err) throw err;
|
|
500
|
+
console.log('server started');
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### New Example (3 lines)
|
|
507
|
+
```javascript
|
|
508
|
+
const Server = require('../../../server');
|
|
509
|
+
const { Demo_UI } = require('./client').controls;
|
|
510
|
+
|
|
511
|
+
Server.serve({ ctrl: Demo_UI, port: 52000 });
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Even Simpler (1 line)
|
|
515
|
+
```javascript
|
|
516
|
+
require('../../../server').serve(require('./client').controls.Demo_UI);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Implementation Checklist
|
|
522
|
+
|
|
523
|
+
### Phase 1: Core `serve()` Method (Complete)
|
|
524
|
+
- [x] Add `Server.serve()` static method
|
|
525
|
+
- [x] Detect input type (Control, object)
|
|
526
|
+
- [x] Support `ctrl`, `page`, `pages` options
|
|
527
|
+
- [x] Handle port/host/debug options
|
|
528
|
+
- [x] Auto-discover client.js convention
|
|
529
|
+
- [x] Return a Promise that resolves with the server instance
|
|
530
|
+
- [x] Support Promise/async and callback patterns
|
|
531
|
+
|
|
532
|
+
### Phase 2: API & Static (Partially Complete)
|
|
533
|
+
- [x] `api` option for function publishing
|
|
534
|
+
- [x] Auto-prefix `/api/` routes
|
|
535
|
+
- [x] JSON/text content-type detection
|
|
536
|
+
- [ ] `static` option for directory serving
|
|
537
|
+
- [ ] MIME type detection for static files
|
|
538
|
+
|
|
539
|
+
### Phase 3: Enhanced Features (Partially Complete)
|
|
540
|
+
- [x] ENV var support (PORT, HOST, JSGUI_DEBUG)
|
|
541
|
+
- [ ] Config file support (`jsgui.config.js`)
|
|
542
|
+
- [ ] Improved error messages
|
|
543
|
+
- [x] Consolidated console output (reduce noise)
|
|
544
|
+
|
|
545
|
+
### Phase 4: Special Modes
|
|
546
|
+
- [ ] `Server.serve.preview()` – ephemeral port + browser open
|
|
547
|
+
- [ ] `Server.serve.dev()` – file watching + auto-restart
|
|
548
|
+
- [ ] `Server.serve.build()` – pre-bundle for production
|
|
549
|
+
|
|
550
|
+
### Phase 5: CLI Integration
|
|
551
|
+
- [ ] `node cli.js serve-example <name>`
|
|
552
|
+
- [ ] `node cli.js serve-client <path>`
|
|
553
|
+
- [ ] `node cli.js preview <path>`
|
|
554
|
+
- [ ] Auto-discovery in current directory
|
|
555
|
+
|
|
556
|
+
### Phase 6: Documentation
|
|
557
|
+
- [ ] Update README with new API
|
|
558
|
+
- [ ] Create migration guide
|
|
559
|
+
- [ ] Add API reference docs
|
|
560
|
+
- [ ] Update 2-3 examples as demos
|
|
561
|
+
- [ ] Keep legacy examples as-is (still valid)
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Example Conversions
|
|
566
|
+
|
|
567
|
+
### Example 1: Simple Window + Checkbox
|
|
568
|
+
**Before:**
|
|
569
|
+
```javascript
|
|
570
|
+
const jsgui = require('./client');
|
|
571
|
+
const {Demo_UI} = jsgui.controls;
|
|
572
|
+
const Server = require('../../../server');
|
|
573
|
+
|
|
574
|
+
if (require.main === module) {
|
|
575
|
+
const server = new Server({
|
|
576
|
+
Ctrl: Demo_UI,
|
|
577
|
+
src_path_client_js: require.resolve('./client.js')
|
|
578
|
+
});
|
|
579
|
+
console.log('waiting for server ready event');
|
|
580
|
+
server.on('ready', () => {
|
|
581
|
+
console.log('server ready');
|
|
582
|
+
server.start(52000, (err) => {
|
|
583
|
+
if (err) throw err;
|
|
584
|
+
console.log('server started');
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**After:**
|
|
591
|
+
```javascript
|
|
592
|
+
require('../../../server').serve({
|
|
593
|
+
ctrl: require('./client').controls.Demo_UI,
|
|
594
|
+
port: 52000
|
|
595
|
+
});
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Example 2: Month View with Title
|
|
599
|
+
**After (with page metadata):**
|
|
600
|
+
```javascript
|
|
601
|
+
require('../../../server').serve({
|
|
602
|
+
page: {
|
|
603
|
+
content: require('./client').controls.Demo_UI,
|
|
604
|
+
title: 'JSGUI3 Month View Demo',
|
|
605
|
+
description: 'Interactive calendar control example'
|
|
606
|
+
},
|
|
607
|
+
port: 52000
|
|
608
|
+
});
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### Example 3: Multi-Control Dashboard
|
|
612
|
+
**After (multiple pages):**
|
|
613
|
+
```javascript
|
|
614
|
+
const { HomeControl, SettingsControl, ReportsControl } = require('./client').controls;
|
|
615
|
+
|
|
616
|
+
require('../../../server').serve({
|
|
617
|
+
pages: {
|
|
618
|
+
'/': { content: HomeControl, title: 'Dashboard' },
|
|
619
|
+
'/settings': { content: SettingsControl, title: 'Settings' },
|
|
620
|
+
'/reports': { content: ReportsControl, title: 'Reports' }
|
|
621
|
+
},
|
|
622
|
+
port: 52000
|
|
623
|
+
});
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Example 4: Dashboard with API
|
|
627
|
+
**After (pages + API):**
|
|
628
|
+
```javascript
|
|
629
|
+
const { DashboardControl } = require('./client').controls;
|
|
630
|
+
|
|
631
|
+
require('../../../server').serve({
|
|
632
|
+
page: {
|
|
633
|
+
content: DashboardControl,
|
|
634
|
+
title: 'Analytics Dashboard'
|
|
635
|
+
},
|
|
636
|
+
api: {
|
|
637
|
+
'metrics': () => ({
|
|
638
|
+
users: 1234,
|
|
639
|
+
revenue: 56789,
|
|
640
|
+
growth: 0.23
|
|
641
|
+
}),
|
|
642
|
+
'data': async ({ range }) => {
|
|
643
|
+
return await fetchAnalytics(range);
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
port: 52000
|
|
647
|
+
});
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## Backwards Compatibility
|
|
653
|
+
|
|
654
|
+
**All existing code continues to work unchanged:**
|
|
655
|
+
|
|
656
|
+
```javascript
|
|
657
|
+
// Current API - still fully supported
|
|
658
|
+
const server = new Server({
|
|
659
|
+
Ctrl: Demo_UI,
|
|
660
|
+
src_path_client_js: require.resolve('./client.js'),
|
|
661
|
+
debug: true
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
server.on('ready', () => {
|
|
665
|
+
server.publish('api', handler);
|
|
666
|
+
server.start(52000, (err) => {
|
|
667
|
+
if (err) throw err;
|
|
668
|
+
console.log('server started');
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**Zero breaking changes. New API is purely additive.**
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Summary
|
|
678
|
+
|
|
679
|
+
### Before (Current)
|
|
680
|
+
- 30 lines of boilerplate per example
|
|
681
|
+
- Manual path resolution
|
|
682
|
+
- Hard-coded ports
|
|
683
|
+
- Verbose console output
|
|
684
|
+
- No conventions
|
|
685
|
+
- Callback nesting
|
|
686
|
+
|
|
687
|
+
### After (New)
|
|
688
|
+
- 1-5 lines for simple cases
|
|
689
|
+
- Auto-discovery of client.js
|
|
690
|
+
- ENV var support
|
|
691
|
+
- Clean, minimal output
|
|
692
|
+
- Progressive disclosure
|
|
693
|
+
- Promise/async support
|
|
694
|
+
- Still 100% backwards compatible
|
|
695
|
+
|
|
696
|
+
### Key Benefits
|
|
697
|
+
1. **Faster prototyping** – from idea to running in seconds
|
|
698
|
+
2. **Less boilerplate** – 90% reduction for simple cases
|
|
699
|
+
3. **Better defaults** – works out of the box
|
|
700
|
+
4. **Clearer intent** – code reads like configuration
|
|
701
|
+
5. **Easy scaling** – simple projects grow naturally
|
|
702
|
+
6. **Zero breaking changes** – all existing code still works
|
|
File without changes
|