jsgui3-server 0.0.146 → 0.0.148

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 (35) hide show
  1. package/README.md +16 -0
  2. package/admin-ui/client.js +213 -0
  3. package/admin-ui/server.js +104 -0
  4. package/client/controls/auto-observable.js +207 -0
  5. package/docs/agent-development-guide.md +92 -9
  6. package/docs/books/admin-ui/01-introduction.md +32 -0
  7. package/docs/books/admin-ui/02-architecture.md +92 -0
  8. package/docs/books/admin-ui/03-controls.md +194 -0
  9. package/docs/books/admin-ui/04-implementation-plan.md +62 -0
  10. package/docs/books/admin-ui/README.md +26 -0
  11. package/docs/books/jsgui3-mvvm-fullstack/00-overview.md +17 -0
  12. package/docs/books/jsgui3-mvvm-fullstack/01-stack-map.md +25 -0
  13. package/docs/books/jsgui3-mvvm-fullstack/02-control-lifecycle.md +32 -0
  14. package/docs/books/jsgui3-mvvm-fullstack/03-mvvm-basics.md +57 -0
  15. package/docs/books/jsgui3-mvvm-fullstack/04-bindings-and-validation.md +51 -0
  16. package/docs/books/jsgui3-mvvm-fullstack/05-full-stack-example.md +50 -0
  17. package/docs/books/jsgui3-mvvm-fullstack/06-testing.md +23 -0
  18. package/docs/books/jsgui3-mvvm-fullstack/07-troubleshooting.md +20 -0
  19. package/docs/books/jsgui3-mvvm-fullstack/08-agent-playbooks.md +30 -0
  20. package/docs/books/jsgui3-mvvm-fullstack/README.md +30 -0
  21. package/docs/comprehensive-documentation.md +145 -34
  22. package/docs/diagrams/jsgui3-progress-update.svg +181 -0
  23. package/examples/controls/18) window, observable bindRemote/README.md +28 -0
  24. package/examples/controls/18) window, observable bindRemote/check.js +144 -0
  25. package/examples/controls/18) window, observable bindRemote/client.js +387 -0
  26. package/examples/controls/18) window, observable bindRemote/server.js +107 -0
  27. package/examples/controls/19) window, auto observable ui/client.js +125 -0
  28. package/examples/controls/19) window, auto observable ui/server.js +64 -0
  29. package/package.json +4 -4
  30. package/publishers/http-observable-publisher.js +8 -0
  31. package/publishers/http-webpage-publisher.js +52 -19
  32. package/publishers/http-webpageorsite-publisher.js +65 -34
  33. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +54 -32
  34. package/server.js +270 -187
  35. package/tests/server-publish-observable.test.js +99 -0
@@ -27,6 +27,9 @@ class Observable_Publisher extends HTTP_Publisher {
27
27
  this.type = 'observable';
28
28
  this.obs = obs;
29
29
 
30
+ // Expose schema if provided (either in spec or on the observable itself)
31
+ this.schema = spec.schema || obs.schema || null;
32
+
30
33
  this.keep_alive_interval_ms = (spec && spec.keep_alive_interval_ms !== undefined)
31
34
  ? spec.keep_alive_interval_ms
32
35
  : default_keep_alive_interval_ms;
@@ -301,6 +304,11 @@ class Observable_Publisher extends HTTP_Publisher {
301
304
  this._write_sse(res, `event: paused\ndata: ${this._stringify_sse_data({ status: 'paused' })}\n\n`);
302
305
  }
303
306
 
307
+ // Send schema if available
308
+ if (this.schema) {
309
+ this._write_sse(res, `event: schema\ndata: ${this._stringify_sse_data(this.schema)}\n\n`);
310
+ }
311
+
304
312
  let removed = false;
305
313
  const remove_connection = () => {
306
314
  if (removed) return;
@@ -5,7 +5,7 @@ const HTTP_Publisher = require('./http-publisher');
5
5
  const Server_Static_Page_Context = require('../static-page-context');
6
6
 
7
7
  const HTTP_Webpageorsite_Publisher = require('./http-webpageorsite-publisher');
8
- const {obs} = require('fnl');
8
+ const { obs } = require('fnl');
9
9
 
10
10
  const Static_Routes_Responses_Webpage_Bundle_Preparer = require('./helpers/preparers/static/bundle/Static_Routes_Responses_Webpage_Bundle_Preparer');
11
11
 
@@ -71,10 +71,20 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
71
71
  this.static_routes_responses_webpage_bundle_preparer = new Static_Routes_Responses_Webpage_Bundle_Preparer({
72
72
  bundler_config: this.bundler_config
73
73
  });
74
- (async() => {
75
- const res_get_ready = await this.get_ready();
76
- this.raise('ready', res_get_ready);
77
-
74
+ (async () => {
75
+ try {
76
+ const res_get_ready = await this.get_ready();
77
+ this.raise('ready', res_get_ready);
78
+ } catch (e) {
79
+ console.error('HTTP_Webpage_Publisher error: Failed to get ready (bundling or preparation failed).');
80
+ console.error(e);
81
+ // Can't just ignore it if it means the server won't handle requests correctly,
82
+ // but at least it won't crash the whole process.
83
+ // We might want to emit an error event.
84
+ this.raise('error', e);
85
+ // Also raise 'ready' so the pool/server can continue starting up other things
86
+ this.raise('ready', {});
87
+ }
78
88
  })();
79
89
 
80
90
  }
@@ -82,7 +92,7 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
82
92
  async get_ready() {
83
93
  //await super.get_ready();
84
94
 
85
- const {static_routes_responses_webpage_bundle_preparer} = this;
95
+ const { static_routes_responses_webpage_bundle_preparer } = this;
86
96
 
87
97
 
88
98
  // Its a bundle....
@@ -91,7 +101,7 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
91
101
 
92
102
  const render_webpage = async () => {
93
103
 
94
- const {webpage} = this;
104
+ const { webpage } = this;
95
105
  const Ctrl = webpage.content;
96
106
 
97
107
  // In business activating it with the page context.
@@ -112,25 +122,48 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
112
122
  ctrl_css_link.dom.attributes.rel = 'stylesheet';
113
123
  ctrl_css_link.dom.attributes.href = '/css/css.css';
114
124
  ctrl.head.add(ctrl_css_link);
115
-
125
+
116
126
  const ctrl_js_script_link = new jsgui_client.controls.script({
117
127
  context: static_page_context
118
128
  });
119
129
 
120
130
  ctrl_js_script_link.dom.attributes.src = '/js/js.js';
121
131
  ctrl.body.add(ctrl_js_script_link);
122
-
132
+
123
133
  ctrl.active();
124
134
  const html = await ctrl.all_html_render();
125
135
  return html;
126
136
  } else {
137
+ // Control is not a document - wrap it in an Active_HTML_Document
138
+ const Active_HTML_Document = require('../controls/Active_HTML_Document');
139
+
140
+ const doc = new Active_HTML_Document({
141
+ context: static_page_context
142
+ });
143
+
144
+ // Add CSS link to head
145
+ const ctrl_css_link = new jsgui_client.controls.link({
146
+ context: static_page_context
147
+ });
148
+ ctrl_css_link.dom.attributes.rel = 'stylesheet';
149
+ ctrl_css_link.dom.attributes.href = '/css/css.css';
150
+ doc.head.add(ctrl_css_link);
127
151
 
128
- // C reate doc and put control inside that?
152
+ // Add the control to body
153
+ doc.body.add(ctrl);
129
154
 
130
- console.trace();
131
- throw 'NYI';
155
+ // Add JS script link to body
156
+ const ctrl_js_script_link = new jsgui_client.controls.script({
157
+ context: static_page_context
158
+ });
159
+ ctrl_js_script_link.dom.attributes.src = '/js/js.js';
160
+ doc.body.add(ctrl_js_script_link);
161
+
162
+ doc.active();
163
+ const html = await doc.all_html_render();
164
+ return html;
132
165
  }
133
-
166
+
134
167
  }
135
168
 
136
169
  // Maybe a Webpage_Rendering_Preparer could do this even?
@@ -157,7 +190,7 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
157
190
  // Then publish it to the router...?
158
191
  // server.serve_prepared_static_routes_bundle ?????
159
192
  return webpage_or_website_res_get_ready;
160
-
193
+
161
194
  }
162
195
 
163
196
 
@@ -165,17 +198,17 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
165
198
  console.log('HTTP_Webpage_Publisher handle_http');
166
199
  console.log('req.url', req.url);
167
200
 
168
- const {webpage} = this;
169
-
201
+ const { webpage } = this;
202
+
170
203
  const Ctrl = webpage.content;
171
204
  const ctrl = new Ctrl();
172
-
205
+
173
206
  res.writeHead(200, {
174
207
  'Content-Type': 'text/html'
175
- });
208
+ });
176
209
 
177
210
  res.end(ctrl.all_html_render());
178
-
211
+
179
212
  }
180
213
  }
181
214
 
@@ -1,9 +1,9 @@
1
1
 
2
2
 
3
3
 
4
- const {each, Router, tof} = require('jsgui3-html');
4
+ const { each, Router, tof } = require('jsgui3-html');
5
5
  const HTTP_Publisher = require('./http-publisher');
6
- const {obs} = require('fnl');
6
+ const { obs } = require('fnl');
7
7
 
8
8
  // The Webpage bundler should be able to come up with the compiled JS and CSS, maybe even a favicon.
9
9
 
@@ -97,9 +97,9 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
97
97
  constructor(spec) {
98
98
  super(spec);
99
99
 
100
- if (spec.debug !== undefined) this.debug = spec.debug;
101
- this.style_config = spec.style || {};
102
- this.bundler_config = spec.bundler || {};
100
+ if (spec.debug !== undefined) this.debug = spec.debug;
101
+ this.style_config = spec.style || {};
102
+ this.bundler_config = spec.bundler || {};
103
103
 
104
104
  // But then some properties to do with the js client(s?) file path.
105
105
 
@@ -109,37 +109,37 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
109
109
  // js_client_file_path ??? client_js_file_path???
110
110
 
111
111
  let src_path_client_js;
112
- if (spec.disk_path_client_js) {
113
- src_path_client_js = spec.disk_path_client_js;
114
- } else if (spec.src_path_client_js) {
115
- src_path_client_js = spec.src_path_client_js;
116
- } else if (spec.source_path_client_js) {
117
- src_path_client_js = spec.source_path_client_js;
118
- };
112
+ if (spec.disk_path_client_js) {
113
+ src_path_client_js = spec.disk_path_client_js;
114
+ } else if (spec.src_path_client_js) {
115
+ src_path_client_js = spec.src_path_client_js;
116
+ } else if (spec.source_path_client_js) {
117
+ src_path_client_js = spec.source_path_client_js;
118
+ };
119
119
 
120
- // or src_path_client_js as well...
120
+ // or src_path_client_js as well...
121
121
 
122
- Object.defineProperty(this, 'disk_path_client_js', {get: () => src_path_client_js, set: (value) => src_path_client_js = value});
123
- Object.defineProperty(this, 'src_path_client_js', {get: () => src_path_client_js, set: (value) => src_path_client_js = value});
124
- Object.defineProperty(this, 'source_path_client_js', {get: () => src_path_client_js, set: (value) => src_path_client_js = value});
122
+ Object.defineProperty(this, 'disk_path_client_js', { get: () => src_path_client_js, set: (value) => src_path_client_js = value });
123
+ Object.defineProperty(this, 'src_path_client_js', { get: () => src_path_client_js, set: (value) => src_path_client_js = value });
124
+ Object.defineProperty(this, 'source_path_client_js', { get: () => src_path_client_js, set: (value) => src_path_client_js = value });
125
125
 
126
126
 
127
127
  // But then need to have some things that get it ready on this level...
128
128
 
129
129
  // Maybe need a get_ready (async or obs) function....
130
130
 
131
- this.js_bundler = new JS_Bundler({
132
- 'debug': this.debug || false,
133
- 'style': this.style_config,
134
- 'bundler': this.bundler_config
135
- });
131
+ this.js_bundler = new JS_Bundler({
132
+ 'debug': this.debug || false,
133
+ 'style': this.style_config,
134
+ 'bundler': this.bundler_config
135
+ });
136
136
 
137
137
 
138
138
  }
139
139
 
140
140
  async get_ready() {
141
141
 
142
- const {js_bundler} = this;
142
+ const { js_bundler } = this;
143
143
  //await super.get_ready(); // Does not have one
144
144
 
145
145
  // Then bundle (extract (process) everything in and referenced by the JS file (s) into a bundle object)
@@ -158,20 +158,51 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
158
158
  // Single js client for whole website??? Could work, not so sure.
159
159
  // Appropriate defalt setting (using v explicit classes) will help it to work.
160
160
 
161
+ const { src_path_client_js } = this;
162
+
163
+ console.log('[HTTP_Webpageorsite_Publisher] get_ready called, src_path_client_js:', src_path_client_js);
164
+
165
+ // Defensive programming: Handle missing client JS path
166
+ if (!src_path_client_js) {
167
+ console.warn('[HTTP_Webpageorsite_Publisher] No src_path_client_js provided. Returning empty bundle.');
168
+ const bundle = new Bundle();
169
+ // Could add empty CSS/JS placeholders if needed to prevent errors downstream
170
+ bundle.push({
171
+ type: 'CSS',
172
+ extension: 'css',
173
+ text: '/* No client CSS - Server Start Mode */'
174
+ });
175
+ bundle.push({
176
+ type: 'JavaScript',
177
+ extension: 'js',
178
+ text: '/* No client JS - Server Start Mode */'
179
+ });
180
+ return bundle;
181
+ }
161
182
 
162
-
163
- const {src_path_client_js} = this;
164
-
165
- //console.log('src_path_client_js', src_path_client_js);
166
-
183
+ // Skip bundling if no client.js path is provided
184
+ // This allows Server.serve() to work with SSR-only controls
185
+ if (!src_path_client_js) {
186
+ console.log('[HTTP_Webpageorsite_Publisher] No src_path_client_js provided - skipping JS bundling');
187
+ // Return an empty bundle with minimal CSS and JS
188
+ const empty_bundle = new Bundle();
189
+ empty_bundle.push({
190
+ type: 'CSS',
191
+ extension: 'css',
192
+ text: '/* No client CSS */'
193
+ });
194
+ empty_bundle.push({
195
+ type: 'JavaScript',
196
+ extension: 'js',
197
+ text: '/* No client JS - SSR only */'
198
+ });
199
+ return empty_bundle;
200
+ }
167
201
 
168
202
  // The js_bundler may need to operate in 'debug' mode.
169
-
170
203
  const js_bundler_res = await js_bundler.bundle(src_path_client_js);
171
204
  // Should also get the CSS from it....
172
205
 
173
-
174
-
175
206
  //console.log('js_bundler_res', js_bundler_res);
176
207
 
177
208
  // Res of length 2 - with a css item and a js item.
@@ -182,7 +213,7 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
182
213
 
183
214
  const css_item = bundle._arr.find((item) => item.type === 'CSS');
184
215
  const js_item = bundle._arr.find((item) => item.type === 'JavaScript');
185
-
216
+
186
217
  if (css_item && js_item) {
187
218
 
188
219
  return bundle;
@@ -261,7 +292,7 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
261
292
  // This part here is just getting ready to publish.
262
293
 
263
294
 
264
-
295
+
265
296
 
266
297
 
267
298
 
@@ -312,7 +343,7 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
312
343
  console.trace();
313
344
  throw 'stop';
314
345
 
315
-
346
+
316
347
 
317
348
  } else {
318
349
  console.log('js_bundler_res', js_bundler_res);
@@ -330,7 +361,7 @@ class HTTP_Webpageorsite_Publisher extends HTTP_Publisher {
330
361
 
331
362
 
332
363
 
333
-
364
+
334
365
 
335
366
 
336
367
 
@@ -5,17 +5,17 @@ const Core_JS_Non_Minifying_Bundler_Using_ESBuild = require('./Core_JS_Non_Minif
5
5
  const Bundler_Using_ESBuild = require('./Bundler_Using_ESBuild');
6
6
  const {is_array} = require('lang-tools');
7
7
 
8
- const Core_JS_Single_File_Minifying_Bundler_Using_ESBuild = require('./Core_JS_Single_File_Minifying_Bundler_Using_ESBuild');
8
+ const Core_JS_Single_File_Minifying_Bundler_Using_ESBuild = require('./Core_JS_Single_File_Minifying_Bundler_Using_ESBuild');
9
9
 
10
10
  //const CSS_Extractor = require('./_Old_CSS_Extractor');
11
11
 
12
12
  // Use a different CSS extractor.
13
13
 
14
14
 
15
- const Bundle = require('../../bundle');
16
-
17
- const CSS_And_JS_From_JS_String_Extractor = require('../../../extractors/js/css_and_js/CSS_And_JS_From_JS_String_Extractor');
18
- const {compile_styles} = require('../../style-bundler');
15
+ const Bundle = require('../../bundle');
16
+
17
+ const CSS_And_JS_From_JS_String_Extractor = require('../../../extractors/js/css_and_js/CSS_And_JS_From_JS_String_Extractor');
18
+ const {compile_styles} = require('../../style-bundler');
19
19
 
20
20
 
21
21
  class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
@@ -24,10 +24,10 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
24
24
 
25
25
  if (spec.debug !== undefined) this.debug = spec.debug;
26
26
 
27
- // Store bundler configuration
28
- this.bundler_config = spec.bundler || {};
29
- const style_config = spec.style || this.bundler_config.style || {};
30
- this.style_config = Object.assign({debug: this.debug === true}, style_config);
27
+ // Store bundler configuration
28
+ this.bundler_config = spec.bundler || {};
29
+ const style_config = spec.style || this.bundler_config.style || {};
30
+ this.style_config = Object.assign({debug: this.debug === true}, style_config);
31
31
 
32
32
  //this.css_extractor = new CSS_Extractor();
33
33
 
@@ -49,7 +49,7 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
49
49
  }
50
50
 
51
51
  bundle(js_file_path) {
52
- const {non_minifying_bundler, css_and_js_from_js_string_extractor, minifying_js_single_file_bundler, style_config} = this;
52
+ const {non_minifying_bundler, css_and_js_from_js_string_extractor, minifying_js_single_file_bundler, style_config} = this;
53
53
 
54
54
  // Validate input
55
55
  if (typeof js_file_path !== 'string' || js_file_path.trim() === '') {
@@ -66,6 +66,7 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
66
66
 
67
67
  const res_obs = obs(async(next, complete, error) => {
68
68
 
69
+ try {
69
70
  //console.log('Advanced_JS_Bundler_Using_ESBuild bundle js_file_path:', js_file_path);
70
71
 
71
72
  const res_nmb = await non_minifying_bundler.bundle(js_file_path);
@@ -88,17 +89,17 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
88
89
  const {text} = bundle_item;
89
90
  // Then use the css and js from js extractor.
90
91
 
91
- const res_extract_styles_and_js = await css_and_js_from_js_string_extractor.extract(text);
92
- const {
93
- css = '',
94
- scss = '',
95
- sass = '',
96
- style_segments = [],
97
- js = text
98
- } = res_extract_styles_and_js || {};
99
-
100
- const style_bundle = compile_styles({css, scss, sass, style_segments}, style_config);
101
- const compiled_css = style_bundle.css || '';
92
+ const res_extract_styles_and_js = await css_and_js_from_js_string_extractor.extract(text);
93
+ const {
94
+ css = '',
95
+ scss = '',
96
+ sass = '',
97
+ style_segments = [],
98
+ js = text
99
+ } = res_extract_styles_and_js || {};
100
+
101
+ const style_bundle = compile_styles({css, scss, sass, style_segments}, style_config);
102
+ const compiled_css = style_bundle.css || '';
102
103
 
103
104
  if (this.debug) {
104
105
  // Generate source maps for CSS-free JS
@@ -112,11 +113,11 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
112
113
  text: css_free_bundle_item.text
113
114
  }
114
115
  res_bundle.push(o_js_bundle_item);
115
- const o_css_bundle_item = {
116
- type: 'CSS',
117
- extension: 'css',
118
- text: compiled_css
119
- }
116
+ const o_css_bundle_item = {
117
+ type: 'CSS',
118
+ extension: 'css',
119
+ text: compiled_css
120
+ }
120
121
  res_bundle.push(o_css_bundle_item);
121
122
  next(res_bundle);
122
123
  complete(res_bundle);
@@ -140,11 +141,11 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
140
141
 
141
142
  if (is_array(minified_js) && minified_js.length === 1) {
142
143
  const minified_bundle = minified_js[0];
143
- const o_css_bundle_item = {
144
- type: 'CSS',
145
- extension: 'css',
146
- text: compiled_css
147
- }
144
+ const o_css_bundle_item = {
145
+ type: 'CSS',
146
+ extension: 'css',
147
+ text: compiled_css
148
+ }
148
149
  minified_bundle.push(o_css_bundle_item);
149
150
  next(minified_bundle);
150
151
  complete(minified_bundle);
@@ -241,6 +242,27 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
241
242
  // Need to use a non minifying bundler (first).
242
243
 
243
244
 
245
+ } catch (bundleError) {
246
+ // Defensive programming: Log error but don't crash the server
247
+ console.error('[Advanced_JS_Bundler_Using_ESBuild] Bundling failed:', bundleError.message || bundleError);
248
+ console.error('[Advanced_JS_Bundler_Using_ESBuild] Returning empty bundle to allow server startup.');
249
+
250
+ // Return an empty bundle so the server can continue
251
+ const fallback_bundle = new Bundle();
252
+ fallback_bundle.push({
253
+ type: 'JavaScript',
254
+ extension: 'js',
255
+ text: '/* Bundling failed - see server logs */'
256
+ });
257
+ fallback_bundle.push({
258
+ type: 'CSS',
259
+ extension: 'css',
260
+ text: '/* Bundling failed - see server logs */'
261
+ });
262
+ next(fallback_bundle);
263
+ complete(fallback_bundle);
264
+ }
265
+
244
266
  const _old_code = async() => {
245
267
 
246
268
  const css_extractor_res = await css_extractor.separate_css_and_js(js_file_path)
@@ -290,4 +312,4 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
290
312
  }
291
313
 
292
314
 
293
- module.exports = Advanced_JS_Bundler_Using_ESBuild;
315
+ module.exports = Advanced_JS_Bundler_Using_ESBuild;