jsgui3-server 0.0.125 → 0.0.126

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 (3) hide show
  1. package/README.md +1020 -4
  2. package/package.json +4 -4
  3. package/server.js +3 -250
package/README.md CHANGED
@@ -1,10 +1,1026 @@
1
1
  # Jsgui3 Server
2
2
 
3
- This module covers functionality that enables the application to be served to clients.
3
+ JSGUI3 Server is a comprehensive Node.js-based application server designed to serve modern JavaScript GUI applications to web clients. It provides a complete runtime environment for delivering dynamic, component-based user interfaces with integrated data binding, event handling, and real-time synchronization.
4
4
 
5
- 2022: Seems as though the server could be bundled with a few useful / essential compilers.
5
+ ## Architecture Overview
6
6
 
7
- // Just running the Server without a website set up could be a way to test the compilers and data transformers.
8
- // Would then be able to publish access to the compilers using the server mechanisms.
7
+ The server operates as a bridge between server-side JavaScript applications and browser clients, offering:
8
+
9
+ - **Component Serving:** Bundles and serves JSGUI3 controls (Windows, Date_Pickers, Checkboxes, etc.) to browsers
10
+ - **Data Model Management:** Handles shared data objects and real-time synchronization between multiple UI controls
11
+ - **CSS Bundling:** Dynamically compiles and serves CSS from component definitions
12
+ - **Event Coordination:** Manages client-server event communication and state synchronization
13
+
14
+ ## Core Components
15
+
16
+ ### Active_HTML_Document
17
+ Base class for all server-rendered applications. Provides:
18
+ - Automatic activation lifecycle management
19
+ - CSS injection and bundling
20
+ - Context-aware control composition
21
+ - Event handling infrastructure
22
+
23
+ ### Control System
24
+ JSGUI3 uses a hierarchical control system where:
25
+ - **Controls** are UI components (buttons, text fields, windows, etc.)
26
+ - **Containers** hold other controls (windows, panels, grids)
27
+ - **Data Models** provide shared state using `Data_Object` instances
28
+ - **Context** manages the runtime environment and event coordination
29
+
30
+ ### Data Binding Architecture
31
+ The framework implements a sophisticated data binding system:
32
+ - `Data_Object` instances serve as observable data models
33
+ - `field()` function from 'obext' creates reactive properties
34
+ - Controls automatically update when their bound data changes
35
+ - Multiple controls can share the same data model for real-time synchronization
36
+
37
+ ## Example Applications
38
+
39
+ The `/examples/controls/` directory contains numerous demonstrations:
40
+
41
+ - **Window + Checkbox:** Basic control composition and event handling
42
+ - **Window + Tabbed Panel:** Container controls with multiple child components
43
+ - **Window + Month View:** Calendar/date selection interfaces
44
+ - **Shared Data Model Date Pickers:** Advanced data binding with synchronized controls
45
+
46
+ Each example follows the pattern:
47
+ 1. `client.js` - Defines the UI components and their composition
48
+ 2. `server.js` - Configures and starts the application server
49
+ 3. `README.md` - Documentation specific to that example
50
+
51
+ 2022 Note: The server architecture is designed to be extensible with compilers and data transformers, allowing it to serve as a development and testing platform for various content processing pipelines.
52
+
53
+ ## Server Resources and Resource Pools
54
+
55
+ Jsgui3 Server manages various runtime resources through configurable pools to ensure high performance and scalability:
56
+
57
+ - **Connection Pool:** Reuses client sockets and HTTP connections to minimize overhead.
58
+ - **Thread/Worker Pool:** Allocates and recycles background workers for tasks like compilation and data transformation.
59
+ - **Cache Pool:** Stores compiled templates, transformed data, and static assets in memory for quick retrieval.
60
+ - **Data Model Pool:** Pre-allocates shared data models (e.g., `Data_Object`) to reduce allocation costs and speed up UI control instantiation.
61
+
62
+ Each pool can be tuned via server configuration (e.g., `server.config.js`), allowing you to adjust sizes and timeouts to match your application's workload.
63
+
64
+ ## Technical Architecture Deep Dive
65
+
66
+ ### Server Initialization and Lifecycle
67
+
68
+ The JSGUI3 server follows a structured initialization pattern:
69
+
70
+ 1. **Server Construction:**
71
+ - Takes a `Ctrl` parameter (the main UI control class)
72
+ - Accepts `src_path_client_js` pointing to the client-side entry point
73
+ - Bundles client code and prepares serving infrastructure
74
+
75
+ 2. **Event-Driven Startup:**
76
+ - Emits 'ready' event when bundling and preparation is complete
77
+ - `server.start(port, callback)` begins HTTP listening
78
+ - Multiple console.log statements track startup progress
79
+
80
+ 3. **Request Handling:**
81
+ - Serves bundled JavaScript to browsers
82
+ - Injects CSS from control definitions
83
+ - Manages WebSocket connections for real-time updates
84
+
85
+ ### Control Hierarchy and Composition
86
+
87
+ Controls in JSGUI3 follow a strict composition pattern:
88
+
89
+ ```javascript
90
+ // Basic control creation pattern
91
+ const control = new controls.ControlType({
92
+ context: context, // Required runtime context
93
+ // ... other properties
94
+ });
95
+
96
+ // Container pattern
97
+ container.inner.add(childControl); // Adding to containers
98
+ this.body.add(topLevelControl); // Adding to document body
99
+ ```
100
+
101
+ **Key Control Types:**
102
+ - **Window:** Top-level container with title bar, positioning, and dragging capabilities
103
+ - **Tabbed_Panel:** Container organizing content into tabs, supports both string labels and [label, control] pairs
104
+ - **Date_Picker:** Input control for date selection with data model binding
105
+ - **Checkbox:** Boolean input with label support
106
+ - **Month_View:** Calendar display widget
107
+
108
+ ### Data Model System Details
109
+
110
+ The data binding system is built on observable patterns:
111
+
112
+ 1. **Data_Object Creation:**
113
+ ```javascript
114
+ const model = new Data_Object({ context });
115
+ field(model, 'fieldName'); // Creates reactive property
116
+ context.register_control(model); // Registers for lifecycle management
117
+ ```
118
+
119
+ 2. **Control Binding:**
120
+ ```javascript
121
+ const control = new SomeControl({
122
+ context,
123
+ data: { model: sharedDataModel }
124
+ });
125
+ ```
126
+
127
+ 3. **Change Handler Management:**
128
+ - Controls automatically register change handlers on their data models
129
+ - `assign_data_model_value_change_handler()` method reestablishes bindings
130
+ - Multiple controls can share the same model for synchronized updates
131
+
132
+ ### File Structure and Conventions
133
+
134
+ ```
135
+ jsgui3-server/
136
+ ├── examples/
137
+ │ └── controls/
138
+ │ ├── 4) window, tabbed panel/
139
+ │ │ ├── client.js # UI definition
140
+ │ │ └── server.js # Server setup
141
+ │ ├── 7) window, month_view/
142
+ │ ├── 8) window, checkbox/
143
+ │ └── 9b) window, shared data.model mirrored date pickers/
144
+ ├── controls/
145
+ │ └── Active_HTML_Document.js # Base class for all UIs
146
+ └── server/ # Core server implementation
147
+ ```
148
+
149
+ **Naming Conventions:**
150
+ - Example directories use numbered prefixes for ordering
151
+ - `client.js` always contains UI control definitions
152
+ - `server.js` follows standard server startup pattern
153
+ - Control classes use PascalCase (e.g., `Demo_UI`, `Date_Picker`)
154
+
155
+ ### Context and Runtime Environment
156
+
157
+ The `context` object is central to JSGUI3 operation:
158
+
159
+ - **Control Registration:** Tracks all controls for lifecycle management
160
+ - **Event Coordination:** Routes events between controls and server
161
+ - **Resource Management:** Handles cleanup and memory management
162
+ - **State Synchronization:** Maintains consistency across client-server boundary
163
+
164
+ ### CSS Integration Patterns
165
+
166
+ CSS is defined as template literals within control classes:
167
+
168
+ ```javascript
169
+ Demo_UI.css = `
170
+ * { margin: 0; padding: 0; }
171
+ body {
172
+ overflow-x: hidden;
173
+ overflow-y: hidden;
174
+ background-color: #E0E0E0;
175
+ }
176
+ .demo-ui {
177
+ /* commented styles provide optional layouts */
178
+ }`;
179
+ ```
180
+
181
+ - Global resets are common across examples
182
+ - Body styling typically prevents scrollbars for desktop app feel
183
+ - Comments within CSS preserve alternative styling options
184
+ - Multi-line formatting is preserved for readability
185
+
186
+ ### Error Handling and Consistency Checks
187
+
188
+ The framework includes several consistency verification patterns:
189
+
190
+ 1. **Model Consistency:** Activation methods verify shared data models remain synchronized
191
+ 2. **Type Checking:** `typeof this.body.add_class === 'function'` guards against missing methods
192
+ 3. **Conditional Composition:** `if (!spec.el) { compose(); }` prevents double-initialization
193
+ 4. **Error Propagation:** Server startup includes error handling: `if (err) throw err;`
194
+
195
+ ### Development and Debugging Features
196
+
197
+ - **Console Logging:** Extensive logging during server startup and events
198
+ - **Port Configuration:** Standardized on port 52000 for examples
199
+ - **Hot Reloading:** Server can be restarted to pick up client.js changes
200
+ - **Activation Lifecycle:** `activate()` method provides hook for post-initialization logic
201
+
202
+ ## Dependencies and Integration
203
+
204
+ ### Core Dependencies
205
+
206
+ - **jsgui3-client:** Client-side framework providing controls and data binding
207
+ - **obext:** Object extension library providing the `field()` function for reactive properties
208
+ - **Node.js:** Server runtime environment
209
+
210
+ ### Integration Patterns
211
+
212
+ - **Require Resolution:** Uses `require.resolve('./client.js')` for reliable path resolution
213
+ - **Module Exports:** Each client.js exports the jsgui object with added controls
214
+ - **Control Registration:** `controls.Demo_UI = Demo_UI;` makes controls available globally
215
+
216
+ ## Programming Patterns and Code Structure for AI Understanding
217
+
218
+ ### Constructor Patterns
219
+
220
+ All JSGUI3 controls follow a consistent constructor pattern:
221
+
222
+ ```javascript
223
+ class Demo_UI extends Active_HTML_Document {
224
+ constructor(spec = {}) {
225
+ spec.__type_name = spec.__type_name || 'demo_ui'; // Type identification
226
+ super(spec); // Call parent constructor
227
+ const { context } = this; // Extract context reference
228
+
229
+ // Optional CSS class application
230
+ if (typeof this.body.add_class === 'function') {
231
+ this.body.add_class('demo-ui');
232
+ }
233
+
234
+ // Define composition function
235
+ const compose = () => {
236
+ // UI creation logic here
237
+ };
238
+
239
+ // Conditional composition (only if no external DOM element provided)
240
+ if (!spec.el) { compose(); }
241
+ }
242
+ }
243
+ ```
244
+
245
+ **Key Pattern Elements:**
246
+ - `spec` parameter is configuration object, always defaulted to `{}`
247
+ - `__type_name` provides runtime type identification
248
+ - `context` extraction is standard pattern for accessing framework services
249
+ - Defensive programming with `typeof` checks prevents runtime errors
250
+ - Conditional composition prevents double-initialization when control is attached to existing DOM
251
+
252
+ ### Activation Lifecycle Pattern
253
+
254
+ The activation pattern provides post-construction initialization:
255
+
256
+ ```javascript
257
+ activate() {
258
+ if (!this.__active) { // Prevent double-activation
259
+ super.activate(); // Call parent activation
260
+ const { context } = this; // Re-extract context reference
261
+
262
+ // Control-specific activation logic
263
+ context.on('window-resize', e_resize => {
264
+ // Event handler logic
265
+ });
266
+ }
267
+ }
268
+ ```
269
+
270
+ **Activation Principles:**
271
+ - `__active` flag prevents multiple activation
272
+ - Parent `activate()` must be called first
273
+ - Event listeners are typically registered during activation
274
+ - Context is re-extracted for consistency
275
+
276
+ ### Data Model Binding Patterns
277
+
278
+ JSGUI3 implements reactive data binding through several patterns:
279
+
280
+ #### 1. Shared Model Creation
281
+ ```javascript
282
+ // Create observable data model
283
+ this.data = { model: new Data_Object({ context }) };
284
+ field(this.data.model, 'value'); // Add reactive property
285
+ context.register_control(this.data.model); // Register for lifecycle management
286
+ ```
287
+
288
+ #### 2. Control Data Binding
289
+ ```javascript
290
+ const control = new Date_Picker({
291
+ context,
292
+ data: { model: this.data.model } // Bind to shared model
293
+ });
294
+ ```
295
+
296
+ #### 3. Model Consistency Verification
297
+ ```javascript
298
+ // In activate() method - verify model synchronization
299
+ if (date_picker_1.data.model !== date_picker_2.data.model) {
300
+ const dm = new Data_Object({ context });
301
+ field(dm, 'value');
302
+ date_picker_1.data.model = dm;
303
+ date_picker_2.data.model = dm;
304
+ date_picker_1.assign_data_model_value_change_handler();
305
+ date_picker_2.assign_data_model_value_change_handler();
306
+ }
307
+ ```
308
+
309
+ ### Server Startup Pattern
310
+
311
+ Every example follows identical server startup:
312
+
313
+ ```javascript
314
+ const jsgui = require('./client'); // Import client-side definition
315
+ const { Demo_UI } = jsgui.controls; // Extract main control class
316
+ const Server = require('../../../server'); // Import server framework
317
+
318
+ if (require.main === module) { // Only run if directly executed
319
+ const server = new Server({
320
+ Ctrl: Demo_UI, // Main UI control class
321
+ src_path_client_js: require.resolve('./client.js') // Client bundle path
322
+ });
323
+
324
+ console.log('waiting for server ready event');
325
+ server.on('ready', () => { // Wait for bundling completion
326
+ console.log('server ready');
327
+ server.start(52000, (err) => { // Start HTTP server
328
+ if (err) throw err; // Propagate startup errors
329
+ console.log('server started');
330
+ });
331
+ });
332
+ }
333
+ ```
334
+
335
+ ## Memory Management and Lifecycle
336
+
337
+ ### Control Reference Management
338
+
339
+ JSGUI3 maintains careful control references:
340
+
341
+ ```javascript
342
+ // Store control references for later access
343
+ this._ctrl_fields = { date_picker_1, date_picker_2 };
344
+
345
+ // Access during activation
346
+ const { _ctrl_fields } = this;
347
+ const { date_picker_1, date_picker_2 } = _ctrl_fields;
348
+ ```
349
+
350
+ ### Context Registration
351
+
352
+ The context system manages control lifecycles:
353
+
354
+ - `context.register_control(model)` - Registers data models for cleanup
355
+ - Controls automatically register with context during construction
356
+ - Context handles cleanup when UI is destroyed
357
+ - Event listeners are automatically removed during cleanup
358
+
359
+ ### CSS Memory Management
360
+
361
+ CSS is defined as static properties to prevent memory leaks:
362
+
363
+ ```javascript
364
+ Demo_UI.css = `...`; // Static property, not instance property
365
+ ```
366
+
367
+ This pattern ensures CSS definitions are shared across all instances rather than duplicated.
368
+
369
+ ## Event System Architecture
370
+
371
+ ### Event Registration Patterns
372
+
373
+ ```javascript
374
+ // Standard event listener registration
375
+ context.on('window-resize', e_resize => {
376
+ // Handler logic - note arrow function preserves 'this' context
377
+ });
378
+
379
+ // Event handler with empty body (placeholder for future functionality)
380
+ context.on('window-resize', e_resize => { });
381
+ ```
382
+
383
+ ### Event Naming Conventions
384
+
385
+ - `window-resize` - Browser window size changes
386
+ - `data-model-value-change` - Data model property updates
387
+ - Control-specific events follow `control-event` pattern
388
+
389
+ ## Control Composition Hierarchy
390
+
391
+ ### Container Control Pattern
392
+
393
+ ```javascript
394
+ // Create container
395
+ const window = new controls.Window({
396
+ context: context, // Required context
397
+ title: 'Window Title', // Window title bar text
398
+ pos: [10, 10] // Initial position [x, y]
399
+ });
400
+
401
+ // Add child controls to container's inner area
402
+ window.inner.add(childControl);
403
+
404
+ // Add top-level container to document body
405
+ this.body.add(window);
406
+ ```
407
+
408
+ ### Tabbed Panel Composition
409
+
410
+ ```javascript
411
+ const tabbed_panel = new controls.Tabbed_Panel({
412
+ context,
413
+ tabs: [
414
+ // Simple string tabs
415
+ 'tab 1', 'tab 2'
416
+
417
+ // OR complex tab definitions with controls
418
+ ['tab 1', new Control({context, size: [250, 250], background: {color: '#553311'}})],
419
+ ['tab 2', new Control({context, size: [250, 250], background: {color: '#1177AA'}})]
420
+ ]
421
+ });
422
+ ```
423
+
424
+ ## Debugging and Development Aids
425
+
426
+ ### Console Logging Strategy
427
+
428
+ JSGUI3 uses extensive console logging for development:
429
+
430
+ ```javascript
431
+ console.log('waiting for server ready event'); // Server initialization
432
+ console.log('server ready'); // Bundling complete
433
+ console.log('server started'); // HTTP server listening
434
+ ```
435
+
436
+ **Logging Conventions:**
437
+ - Present tense for ongoing states ("waiting", "starting")
438
+ - Past tense for completed actions ("ready", "started")
439
+ - No timestamps (rely on console timestamps)
440
+ - Lowercase, descriptive messages
441
+
442
+ ### Error Handling Patterns
443
+
444
+ ```javascript
445
+ // Standard error propagation
446
+ server.start(52000, (err) => {
447
+ if (err) throw err; // Re-throw errors for visibility
448
+ console.log('server started'); // Only log success if no error
449
+ });
450
+ ```
451
+
452
+ ### Type Safety Patterns
453
+
454
+ ```javascript
455
+ // Defensive method checking
456
+ if (typeof this.body.add_class === 'function') {
457
+ this.body.add_class('demo-ui');
458
+ }
459
+
460
+ // Property existence checking before use
461
+ if (!spec.el) { compose(); }
462
+
463
+ // Model existence verification
464
+ if (date_picker_1.data.model !== date_picker_2.data.model) {
465
+ // Recovery logic
466
+ }
467
+ ```
468
+
469
+ ## Framework Integration Points
470
+
471
+ ### jsgui3-client Integration
472
+
473
+ ```javascript
474
+ const jsgui = require('jsgui3-client');
475
+ const { controls, Control, mixins, Data_Object } = jsgui; // Destructure imports
476
+ ```
477
+
478
+ **Key Exports:**
479
+ - `controls` - Collection of UI control constructors
480
+ - `Control` - Base control class
481
+ - `mixins` - Behavioral mixins (e.g., `dragable`)
482
+ - `Data_Object` - Observable data model constructor
483
+
484
+ ### obext Integration
485
+
486
+ ```javascript
487
+ const { field } = require('obext'); // Object extension utilities
488
+ field(dataModel, 'propertyName'); // Creates reactive property
489
+ ```
490
+
491
+ The `field()` function creates observable properties that trigger change events when modified.
492
+
493
+ ### Module Export Pattern
494
+
495
+ ```javascript
496
+ // At end of client.js files
497
+ controls.Demo_UI = Demo_UI; // Register control globally
498
+ module.exports = jsgui; // Export entire framework
499
+ ```
500
+
501
+ This pattern extends the jsgui framework with custom controls while maintaining the complete API.
502
+
503
+ ## Performance Considerations
504
+
505
+ ### Lazy Composition
506
+
507
+ ```javascript
508
+ if (!spec.el) { compose(); } // Only compose if no external DOM
509
+ ```
510
+
511
+ This pattern allows controls to be created without immediately building their UI, enabling faster initialization and memory savings.
512
+
513
+ ### Shared Data Models
514
+
515
+ ```javascript
516
+ // Single model shared by multiple controls
517
+ const sharedModel = new Data_Object({ context });
518
+ // Multiple controls reference same model - no data duplication
519
+ ```
520
+
521
+ ### CSS Bundling
522
+
523
+ CSS is collected from all control classes and bundled once during server startup, rather than injected per-instance.
524
+
525
+ ## AI Development Guidelines
526
+
527
+ When working with JSGUI3:
528
+
529
+ 1. **Always follow the constructor pattern** - spec parameter, __type_name, super() call, context extraction
530
+ 2. **Use defensive programming** - typeof checks, property existence verification
531
+ 3. **Maintain activation lifecycle** - check __active flag, call super.activate()
532
+ 4. **Follow naming conventions** - PascalCase for classes, snake_case for properties
533
+ 5. **Preserve CSS formatting** - multi-line CSS with comments
534
+ 6. **Use consistent error handling** - throw errors, don't swallow them
535
+ 7. **Register controls properly** - context.register_control() for data models
536
+ 8. **Store control references** - use _ctrl_fields pattern for later access
537
+
538
+ ## Detailed Component Architecture
539
+
540
+ ### Active_HTML_Document Inheritance Chain
541
+
542
+ The base class hierarchy is crucial for understanding the framework:
543
+
544
+ ```javascript
545
+ class Demo_UI extends Active_HTML_Document {
546
+ // All UI classes extend this base class
547
+ }
548
+ ```
549
+
550
+ **Active_HTML_Document Responsibilities:**
551
+ - Provides `this.body` property for DOM manipulation
552
+ - Implements activation lifecycle (`activate()` method)
553
+ - Handles CSS injection and bundling
554
+ - Manages context assignment and control registration
555
+ - Provides event coordination infrastructure
556
+
557
+ ### Control Specification System
558
+
559
+ The `spec` parameter is a configuration object with standardized properties:
560
+
561
+ ```javascript
562
+ const control = new SomeControl({
563
+ context: context, // REQUIRED - Runtime environment
564
+ size: [width, height], // Optional dimensions
565
+ pos: [x, y], // Optional position
566
+ title: 'String', // Optional title (for Windows)
567
+ background: { // Optional styling
568
+ color: '#RRGGBB'
569
+ },
570
+ data: { // Optional data binding
571
+ model: dataObject
572
+ },
573
+ label: { // Optional label configuration
574
+ text: 'Label Text'
575
+ }
576
+ });
577
+ ```
578
+
579
+ **Common Spec Properties:**
580
+ - `context` - Always required, provides runtime services
581
+ - `size` - Array [width, height] for dimensioned controls
582
+ - `pos` - Array [x, y] for positioned controls (Windows)
583
+ - `data` - Object containing `model` property for data binding
584
+ - `background` - Styling object with color, image, etc.
585
+ - `label` - Label configuration for input controls
586
+
587
+ ### Window Control Deep Dive
588
+
589
+ Windows are the primary container control:
590
+
591
+ ```javascript
592
+ const window = new controls.Window({
593
+ context: context,
594
+ title: 'Window Title', // Appears in title bar
595
+ pos: [10, 10] // Initial position [x, y]
596
+ });
597
+
598
+ // Key Window properties:
599
+ window.inner // Container for child controls
600
+ window.title // Title bar text
601
+ window.pos // Current position
602
+ ```
603
+
604
+ **Window Features:**
605
+ - Draggable by title bar (via `dragable` mixin)
606
+ - Resizable borders
607
+ - Close button functionality
608
+ - Z-index management for overlapping windows
609
+ - Child control containment via `inner` property
610
+
611
+ ### Data_Object Reactive System
612
+
613
+ The observable data system is fundamental to JSGUI3:
614
+
615
+ ```javascript
616
+ // 1. Create Data_Object
617
+ const model = new Data_Object({ context });
618
+
619
+ // 2. Add reactive fields
620
+ field(model, 'value'); // Creates getter/setter with change events
621
+ field(model, 'name'); // Multiple fields supported
622
+ field(model, 'selected');
623
+
624
+ // 3. Register with context
625
+ context.register_control(model);
626
+
627
+ // 4. Bind to controls
628
+ const picker = new Date_Picker({
629
+ context,
630
+ data: { model: model } // Control automatically subscribes to changes
631
+ });
632
+
633
+ // 5. Model changes trigger UI updates
634
+ model.value = new Date(); // All bound controls update automatically
635
+ ```
636
+
637
+ **Reactive Properties:**
638
+ - Getter/setter pairs created by `field()` function
639
+ - Change events emitted when properties modified
640
+ - Controls automatically subscribe to their bound models
641
+ - Multiple controls can share single model for synchronization
642
+
643
+ ### Control Registration and Lifecycle
644
+
645
+ ```javascript
646
+ // Registration patterns
647
+ context.register_control(dataModel); // Register data models
648
+ // Controls auto-register during construction
649
+
650
+ // Lifecycle events
651
+ constructor() -> activate() -> use -> cleanup()
652
+
653
+ // Activation requirements
654
+ if (!this.__active) {
655
+ super.activate(); // MUST call parent first
656
+ // ... control-specific activation
657
+ }
658
+ ```
659
+
660
+ ### Event System Details
661
+
662
+ ```javascript
663
+ // Event registration
664
+ context.on('event-name', handler);
665
+
666
+ // Common event types:
667
+ 'window-resize' // Browser window size changes
668
+ 'data-model-value-change' // Data model property updates
669
+ 'control-activated' // Control becomes active
670
+ 'control-deactivated' // Control becomes inactive
671
+
672
+ // Event handler patterns
673
+ context.on('window-resize', e_resize => {
674
+ // e_resize contains resize event data
675
+ // 'this' context preserved with arrow functions
676
+ });
677
+ ```
678
+
679
+ ## Advanced Data Binding Scenarios
680
+
681
+ ### Model Synchronization Recovery
682
+
683
+ When shared models become desynchronized, JSGUI3 provides recovery patterns:
684
+
685
+ ```javascript
686
+ activate() {
687
+ if (!this.__active) {
688
+ super.activate();
689
+ const { _ctrl_fields } = this;
690
+ const { picker1, picker2 } = _ctrl_fields;
691
+
692
+ // Detect model desynchronization
693
+ if (picker1.data.model !== picker2.data.model) {
694
+ // Create new shared model
695
+ const dm = new Data_Object({ context });
696
+ field(dm, 'value');
697
+
698
+ // Reassign to both controls
699
+ picker1.data.model = dm;
700
+ picker2.data.model = dm;
701
+
702
+ // Re-establish change handlers
703
+ picker1.assign_data_model_value_change_handler();
704
+ picker2.assign_data_model_value_change_handler();
705
+ }
706
+ }
707
+ }
708
+ ```
709
+
710
+ ### Multi-Control Data Flows
711
+
712
+ ```javascript
713
+ // One-to-many data binding
714
+ const sharedModel = new Data_Object({ context });
715
+ field(sharedModel, 'value');
716
+
717
+ const controls = [
718
+ new Date_Picker({ context, data: { model: sharedModel } }),
719
+ new Text_Input({ context, data: { model: sharedModel } }),
720
+ new Display_Label({ context, data: { model: sharedModel } })
721
+ ];
722
+
723
+ // Any control's changes propagate to all others
724
+ ```
725
+
726
+ ### Data Model Field Types
727
+
728
+ ```javascript
729
+ // Different field types and their behaviors
730
+ field(model, 'stringField'); // String values
731
+ field(model, 'numberField'); // Numeric values
732
+ field(model, 'dateField'); // Date objects
733
+ field(model, 'booleanField'); // Boolean values
734
+ field(model, 'objectField'); // Complex objects
735
+ field(model, 'arrayField'); // Array collections
736
+
737
+ // Fields support any JavaScript type
738
+ model.stringField = "text";
739
+ model.numberField = 42;
740
+ model.dateField = new Date();
741
+ model.booleanField = true;
742
+ model.objectField = { nested: "data" };
743
+ model.arrayField = [1, 2, 3];
744
+ ```
745
+
746
+ ## Server-Side Architecture Details
747
+
748
+ ### Bundling and Serving Process
749
+
750
+ ```javascript
751
+ // Server construction phase
752
+ const server = new Server({
753
+ Ctrl: Demo_UI, // Main UI control class
754
+ src_path_client_js: require.resolve('./client.js') // Client bundle path
755
+ });
756
+
757
+ // Bundling process (happens during 'ready' event):
758
+ // 1. Parse client.js and dependencies
759
+ // 2. Collect CSS from all control classes
760
+ // 3. Bundle JavaScript for browser delivery
761
+ // 4. Prepare HTTP routes for serving
762
+ // 5. Emit 'ready' event when complete
763
+ ```
764
+
765
+ ### Request/Response Cycle
766
+
767
+ ```javascript
768
+ // HTTP request handling:
769
+ // 1. Browser requests application
770
+ // 2. Server serves bundled JavaScript
771
+ // 3. Client executes and creates UI
772
+ // 4. WebSocket connection established for real-time updates
773
+ // 5. UI events flow back to server via WebSocket
774
+ // 6. Server processes events and updates models
775
+ // 7. Model changes propagate back to client
776
+ ```
777
+
778
+ ### Multi-Client Synchronization
779
+
780
+ ```javascript
781
+ // Server maintains shared state across multiple clients
782
+ // Changes from one client propagate to all connected clients
783
+ // Data models are server-authoritative
784
+ // Client UI updates reflect server state changes
785
+ ```
786
+
787
+ ## CSS Architecture and Styling
788
+
789
+ ### CSS Definition Patterns
790
+
791
+ ```javascript
792
+ // Static CSS property on control class
793
+ ControlClass.css = `
794
+ /* Global resets - common across all examples */
795
+ * {
796
+ margin: 0;
797
+ padding: 0;
798
+ }
799
+
800
+ /* Body styling - prevents scrollbars, sets background */
801
+ body {
802
+ overflow-x: hidden;
803
+ overflow-y: hidden;
804
+ background-color: #E0E0E0;
805
+ }
806
+
807
+ /* Control-specific styling */
808
+ .control-class-name {
809
+ /* Active styles */
810
+
811
+ /* Commented alternative styles for future use */
812
+ /* display: flex; */
813
+ /* justify-content: center; */
814
+ }
815
+ `;
816
+ ```
817
+
818
+ ### CSS Bundling Process
819
+
820
+ ```javascript
821
+ // CSS collection during server startup:
822
+ // 1. Scan all control classes for .css properties
823
+ // 2. Concatenate CSS in dependency order
824
+ // 3. Minify and optimize
825
+ // 4. Serve as single stylesheet to clients
826
+ // 5. Cache for subsequent requests
827
+ ```
828
+
829
+ ### Responsive Design Patterns
830
+
831
+ ```javascript
832
+ // Window resize handling
833
+ context.on('window-resize', e_resize => {
834
+ // Update control dimensions
835
+ // Reflow layouts
836
+ // Adjust positioning
837
+ });
838
+
839
+ // CSS supports responsive breakpoints
840
+ .control-class {
841
+ /* Desktop styles */
842
+ }
843
+
844
+ @media (max-width: 768px) {
845
+ .control-class {
846
+ /* Mobile styles */
847
+ }
848
+ }
849
+ ```
850
+
851
+ ## Control-Specific Implementation Details
852
+
853
+ ### Checkbox Control
854
+
855
+ ```javascript
856
+ const checkbox = new Checkbox({
857
+ context,
858
+ label: { text: 'Checkbox Label' }, // Label configuration
859
+ // Optional data binding
860
+ data: { model: booleanModel } // Binds to boolean field
861
+ });
862
+
863
+ // Checkbox events
864
+ checkbox.on('change', (checked) => {
865
+ // Handle checkbox state changes
866
+ });
867
+ ```
868
+
869
+ ### Tabbed_Panel Control
870
+
871
+ ```javascript
872
+ const tabbed_panel = new controls.Tabbed_Panel({
873
+ context,
874
+ tabs: [
875
+ // String-only tabs (content provided separately)
876
+ 'Tab 1', 'Tab 2', 'Tab 3',
877
+
878
+ // OR control-embedded tabs
879
+ ['Tab 1', controlInstance1],
880
+ ['Tab 2', controlInstance2],
881
+ ['Tab 3', controlInstance3]
882
+ ]
883
+ });
884
+
885
+ // Tab switching events
886
+ tabbed_panel.on('tab-changed', (tabIndex) => {
887
+ // Handle tab selection
888
+ });
889
+ ```
890
+
891
+ ### Date_Picker Control
892
+
893
+ ```javascript
894
+ const date_picker = new Date_Picker({
895
+ context,
896
+ data: { model: dateModel }, // Binds to date field
897
+ // Optional configuration
898
+ format: 'YYYY-MM-DD', // Date display format
899
+ minDate: new Date('2020-01-01'), // Minimum selectable date
900
+ maxDate: new Date('2030-12-31') // Maximum selectable date
901
+ });
902
+
903
+ // Date selection events
904
+ date_picker.on('date-selected', (date) => {
905
+ // Handle date selection
906
+ });
907
+ ```
908
+
909
+ ### Month_View Control
910
+
911
+ ```javascript
912
+ const month_view = new Month_View({
913
+ context,
914
+ // Optional configuration
915
+ showToday: true, // Highlight today's date
916
+ showWeekNumbers: false, // Show week numbers
917
+ firstDayOfWeek: 0 // 0=Sunday, 1=Monday, etc.
918
+ });
919
+
920
+ // Month navigation events
921
+ month_view.on('month-changed', (year, month) => {
922
+ // Handle month navigation
923
+ });
924
+ ```
925
+
926
+ ## Error Handling and Recovery Patterns
927
+
928
+ ### Common Error Scenarios
929
+
930
+ ```javascript
931
+ // 1. Missing context
932
+ if (!context) {
933
+ throw new Error('Context required for control creation');
934
+ }
935
+
936
+ // 2. Invalid data model
937
+ if (spec.data && !spec.data.model) {
938
+ throw new Error('Data specification requires model property');
939
+ }
940
+
941
+ // 3. Missing required methods
942
+ if (typeof this.body.add_class !== 'function') {
943
+ console.warn('add_class method not available on body');
944
+ // Graceful degradation
945
+ }
946
+
947
+ // 4. Activation state errors
948
+ if (this.__active) {
949
+ console.warn('Control already activated, skipping');
950
+ return;
951
+ }
952
+ ```
953
+
954
+ ### Recovery Strategies
955
+
956
+ ```javascript
957
+ // Data model recovery
958
+ try {
959
+ model.value = newValue;
960
+ } catch (error) {
961
+ console.error('Model update failed:', error);
962
+ // Revert to previous value or default
963
+ model.value = this._previousValue || this._defaultValue;
964
+ }
965
+
966
+ // Control reference recovery
967
+ if (!this._ctrl_fields || !this._ctrl_fields.someControl) {
968
+ console.warn('Control references lost, rebuilding');
969
+ this._rebuildControls();
970
+ }
971
+
972
+ // Event handler recovery
973
+ try {
974
+ context.on('event-name', handler);
975
+ } catch (error) {
976
+ console.error('Event registration failed:', error);
977
+ // Use fallback polling or alternative event mechanism
978
+ }
979
+ ```
980
+
981
+ ## Testing and Development Utilities
982
+
983
+ ### Development Mode Features
984
+
985
+ ```javascript
986
+ // Enhanced logging in development
987
+ if (process.env.NODE_ENV === 'development') {
988
+ console.log('Control created:', this.__type_name);
989
+ console.log('Context:', context);
990
+ console.log('Specification:', spec);
991
+ }
992
+
993
+ // Debug helpers
994
+ this._debug = {
995
+ controlType: this.__type_name,
996
+ createdAt: new Date(),
997
+ context: context,
998
+ specification: spec
999
+ };
1000
+ ```
1001
+
1002
+ ### Runtime Inspection
1003
+
1004
+ ```javascript
1005
+ // Control hierarchy inspection
1006
+ function inspectControlTree(control, depth = 0) {
1007
+ const indent = ' '.repeat(depth);
1008
+ console.log(`${indent}${control.__type_name}`);
1009
+
1010
+ if (control.children) {
1011
+ control.children.forEach(child => {
1012
+ inspectControlTree(child, depth + 1);
1013
+ });
1014
+ }
1015
+ }
1016
+
1017
+ // Data model inspection
1018
+ function inspectDataModel(model) {
1019
+ console.log('Model fields:', Object.keys(model));
1020
+ Object.keys(model).forEach(key => {
1021
+ console.log(` ${key}:`, model[key]);
1022
+ });
1023
+ }
1024
+ ```
9
1025
 
10
1026
 
package/package.json CHANGED
@@ -3,15 +3,15 @@
3
3
  "main": "module.js",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
- "@babel/core": "^7.26.10",
6
+ "@babel/core": "^7.27.7",
7
7
  "@babel/generator": "^7.27.5",
8
8
  "@babel/parser": "7.27.7",
9
9
  "cookies": "^0.9.1",
10
10
  "esbuild": "^0.25.5",
11
11
  "fnl": "^0.0.36",
12
12
  "fnlfs": "^0.0.33",
13
- "jsgui3-client": "^0.0.4",
14
- "jsgui3-html": "^0.0.161",
13
+ "jsgui3-client": "^0.0.115",
14
+ "jsgui3-html": "^0.0.162",
15
15
  "jsgui3-webpage": "^0.0.8",
16
16
  "jsgui3-website": "^0.0.8",
17
17
  "lang-tools": "^0.0.36",
@@ -38,5 +38,5 @@
38
38
  "type": "git",
39
39
  "url": "https://github.com/metabench/jsgui3-server.git"
40
40
  },
41
- "version": "0.0.125"
41
+ "version": "0.0.126"
42
42
  }
package/server.js CHANGED
@@ -23,16 +23,6 @@ const HTTP_Website_Publisher = require('./publishers/http-website-publisher');
23
23
  const Webpage = require('./website/webpage');
24
24
  const HTTP_Webpage_Publisher = require('./publishers/http-webpage-publisher');
25
25
 
26
- // A class that contains resources and publishers?
27
- // Is the server itself a publisher?
28
- // Is this a Server_Process, whereby a Server could hold multiple processes?
29
-
30
- // Think this is / should be a Server. Not sure though.
31
- // Maybe Single_Process_Server ????
32
- // Could make for cleaner debugs if we have multiples of them and maybe a Multi_Process_Coordinator_Server ???
33
- // Multi_Single_Process_Server_Coordinater_Server ???
34
-
35
-
36
26
  const Static_Route_HTTP_Responder = require('./http/responders/static/Static_Route_HTTP_Responder');
37
27
 
38
28
 
@@ -91,81 +81,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
91
81
  const opts_webpage = {
92
82
  'name': this.name || 'Website'
93
83
  };
94
-
95
- // Seems like a decent mechanism already in here at the moment, but may want to make the API more powerful and flexible.
96
-
97
- // A single Ctrl that represents a page (could a Ctrl represent a site too? Could be possible with a bit of work.)
98
-
99
-
100
84
 
101
85
  if (Ctrl) {
102
- // could be a web page, not a web site.
103
- // But a site can contain one page. Easy enough default?
104
- // Though more directly serving a page seems simpler. More logical too, if we are not serving a site with it.
105
- //opts_website.content = Ctrl;
106
- //opts_webpage.content = Ctrl;
107
-
108
- // set up a web page with the ctrl, and a web page publisher.
109
-
110
- // But does that Webpage need (automatically or not) to include the necessary (built) JS client file?
111
-
112
-
113
- // May need to process / get info on the referenced JS first.
114
- // Better to make something like a JS_File_Info_Provider than to do it here.
115
- // Want a really DRY framework and to make it so slower parts can be upgraded as files and swapped as files.
116
-
117
-
118
-
119
-
120
- // Tell the webpage it needs to have a built version of the referenced client JS?
121
-
122
- // The Webpage is an app???
123
-
124
-
125
- // And give it the src_path_client_js ...?
126
- // Best to be specific with the API, but make it fairly short and simple on the top level.
127
-
128
- // But the webpage needs to have stuff in its head that references the JS.
129
- // But maybe the publisher could insert that.
130
-
131
- // Basically need to put everything in the relevant lower level abstraction and use it there.
132
- // Need to make it very clear what is being done, but concise too.
133
- // Really need to get it simply serving JS clients, extracting CSS from JS too.
134
- // Want to make it easy to make some really interesting demos with very simple idiomatic code
135
- // that is clear how it works both on client and server.
136
-
137
- // Could get more into 'undo button' type transformations, that's something React and Redux can do well,
138
- // would be worth going into UI_Action classes or similar.
139
- // UI_Action_Result too - but def want to keep it really simple on the top level, simple enough on the 2nd level,
140
- // and then it can be moderately to very complex on 3rd level down (though there could be options like choosing which
141
- // js build system to use).
142
-
143
- // Def looks like a fair bit more work to be done to get all the abstractions made and working to run simple apps,
144
- // with really simple and efficient defaults.
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
86
 
153
87
 
154
88
  const wp_app = new Webpage({content: Ctrl});
155
89
 
156
-
157
-
158
-
159
- // And maybe by default the webpage publisher should make sure that the relevant JS and CSS is built / packaged and ready to serve.
160
- // May need to give it one or two file paths.
161
- // But if we give it those file path(s) do we need to provide that Ctrl in the first place?
162
- // For the moment a single (or 2) extra properties should be fine.
163
- // a client_src_js_path property should be fine.
164
- // src_path_client_js
165
- // or src_client_js as a string.
166
-
167
-
168
-
169
90
  const opts_wp_publisher = {
170
91
  'webpage': wp_app
171
92
 
@@ -185,66 +106,16 @@ class JSGUI_Single_Process_Server extends Evented_Class {
185
106
 
186
107
 
187
108
  console.log('waiting for wp_publisher ready');
188
-
189
- // Server can (maybe just in theory) serve multiple websites at once.
190
- // Worth making that more of a feature.
191
- // For the moment, seems like new website resource inside server makes sense.
192
- // Then does the website resource contain a lot of its own resources, in a pool???
193
-
194
-
195
- // The publisher should (probably) set things up with the server itself....
196
- // Give the publisher access to the server.
197
- // Or access to a more limited number of functions that the publisher can call.
198
-
199
- // So the Publisher itself finds the Server Router and sets up routes on it, but uses very specific classes to help do that.
200
-
201
- // Maybe Http_Publisher on a lower level could put together headers and do compression.
202
- // This part will be a little more like Express (and other) middleware.
203
-
204
- // http_publisher.publish_static_content_to_routes
205
- // .publish_static_bundle_to_routes
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
109
  wp_publisher.on('ready', (wp_ready_res) => {
216
- console.log('wp publisher is ready');
217
-
218
- // The ready res on complete(res) ???
219
- // But the ready event does not (easily) carry this object.
220
-
221
-
222
- //console.log('wp_ready_res', wp_ready_res);
223
-
224
- // then go through the array....
225
-
110
+ //console.log('wp publisher is ready');
226
111
  if (wp_ready_res._arr) {
227
112
 
228
113
 
229
114
  for (const bundle_item of wp_ready_res._arr) {
230
115
  //console.log('Object.keys(bundle_item)', Object.keys(bundle_item));
231
-
232
-
233
-
234
116
  const {type, extension, text, route, response_buffers, response_headers} = bundle_item;
235
- //console.log('');
236
- //console.log('bundle_item route:', route);
237
- //console.log('bundle_item type:', type);
238
-
239
117
  const bundle_item_http_responder = new Static_Route_HTTP_Responder(bundle_item);
240
118
 
241
- //console.log('bundle_item_http_responder.handle_http', bundle_item_http_responder.handle_http);
242
-
243
- // So set_route needs to set it up with the proper context for the handle_http call.
244
- // At least it looks fairly close to being solved, though maybe Router and Routing tree
245
- // should have comprehensive fixes and improvements.
246
-
247
-
248
119
 
249
120
  server_router.set_route(route, bundle_item_http_responder, bundle_item_http_responder.handle_http);
250
121
 
@@ -259,111 +130,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
259
130
  throw 'NYI';
260
131
  }
261
132
 
262
- // But do we get a bundle from it when ready?
263
- // Maybe it should provide that bundle in the 'ready' event.
264
-
265
-
266
-
267
-
268
133
  const ws_resource = new Website_Resource({
269
134
  'name': 'Website_Resource - Single Webpage',
270
135
  'webpage': wp_app
271
136
  });
272
137
  resource_pool.add(ws_resource);
273
138
 
274
- //
275
-
276
-
277
- // Possibly set multiple routes here, with multiple response buffers depending on the encoding-type
278
- // accepted by the client.
279
-
280
- // Seems best not to rely on the Webpage_Publisher to handle the HTTP.
281
- // Better for the Publisher to create the Bundle that is ready to serve, than provide that
282
- // to the Server here, or maybe to something else.
283
-
284
- // Seems best to get that ready to serve static bundle from the publisher,
285
- // and if it helps use some kind of system to set up some more details with the server router...?
286
-
287
- // Initial_Response_Handler perhaps?
288
-
289
- // Webpage_HTTP_Response_Handler?
290
-
291
- // Static_Webpage_HTTP_Response_Handler???
292
-
293
- // Static_Webpage_Bundle_HTTP_Response_Handler ???
294
-
295
- // Static_Webpage_Bundle_HTTP_Route_Response_Handler ????
296
-
297
-
298
- // Static_Webpage_Bundle_HTTP_Route_Responder ???
299
-
300
- // new Static_Webpage_Bundle_HTTP_Route_Responder(bundle_item)
301
-
302
-
303
-
304
-
305
- // Static_Webpage_Bundle_HTTP_Responder ??? (would do routing / route checking itself perhaps?)
306
- // Seems like for the moment we should continue to use the server router.
307
-
308
-
309
-
310
-
311
- // Need to decide which encoded (compressed) buffer to return depending
312
- // on what Content-Type(s) are supported on the client.
313
-
314
-
315
-
316
-
317
- // Very explicit class names make the responsibilities very clear.
318
-
319
- // Static_Route_HTTP_Responder seems best to provide the handle_http function.
320
- // Even after routing a decision needs to be made regarding which buffer to send to the client
321
- // Should be the last thing needed to get this simple square box demo server working properly.
322
- // Hopefully the client-side activation still works fine.
323
-
324
-
325
-
326
- // HTTP_Responder class and subclasses???
327
- // Could be a helpful type of middleware.
328
- // Want to have it handle creating or using the correct compressed buffer.
329
-
330
- // go through the array of bundle items....
331
-
332
-
333
- // Need the bundle object / array here.
334
- // Would be nice to have the bundle itself hold info on what's inside.
335
- // Incl what packaging stage it is at, how ready to serve.
336
-
337
- // Though possible one object could handle the whole static bundle setup with the router...?
338
-
339
- // The code in a loop should be simple enough here though.
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
- // Set the routes of the various items in the bundle (and use the handlers provided by class
349
- // instance objects that specifically provide HTTP handlers)
350
-
351
-
352
- // Should be the very last part of serving the HTTP for this particular server type.
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
- //server_router.set_route('/', wp_publisher, wp_publisher.handle_http);
365
-
366
-
367
139
  this.raise('ready');
368
140
  });
369
141
 
@@ -501,29 +273,8 @@ class JSGUI_Single_Process_Server extends Evented_Class {
501
273
  }
502
274
  }
503
275
 
504
- // Not sure that controls should be considered server only????
505
- // Possibly makes sense, though the 'client' part may need the active HTML document.
506
-
507
- // Possibly the server could / should produce / provide such a document.
508
-
509
-
510
-
511
-
512
-
513
- // Return the 'jsgui' object???
514
-
515
- //jsgui.controls.Active_HTML_Document = require('./controls/Active_HTML_Document');
516
-
517
-
518
276
  JSGUI_Single_Process_Server.jsgui = jsgui;
519
277
 
520
-
521
-
522
- // Also want Active_HTML_Document
523
- // or Active_HTML - needs to be simple to use, putting in the active stuff automatically.
524
-
525
-
526
-
527
278
  JSGUI_Single_Process_Server.Resource = Resource;
528
279
  JSGUI_Single_Process_Server.Page_Context = Server_Page_Context;
529
280
  JSGUI_Single_Process_Server.Server_Page_Context = Server_Page_Context;
@@ -607,3 +358,5 @@ const summary = {
607
358
  ]
608
359
  }
609
360
  }
361
+
362
+ JSGUI_Single_Process_Server.summary = summary;