jsgui3-server 0.0.138 → 0.0.140

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 (57) hide show
  1. package/AGENTS.md +87 -0
  2. package/README.md +12 -0
  3. package/docs/GUIDE_TO_AGENTIC_WORKFLOWS_BY_GROK.md +19 -0
  4. package/docs/advanced-usage-examples.md +1360 -0
  5. package/docs/agent-development-guide.md +386 -0
  6. package/docs/api-reference.md +916 -0
  7. package/docs/broken-functionality-tracker.md +285 -0
  8. package/docs/bundling-system-deep-dive.md +525 -0
  9. package/docs/cli-reference.md +393 -0
  10. package/docs/comprehensive-documentation.md +1403 -0
  11. package/docs/configuration-reference.md +808 -0
  12. package/docs/controls-development.md +859 -0
  13. package/docs/documentation-review/CURRENT_REVIEW.md +95 -0
  14. package/docs/function-publishers-json-apis.md +847 -0
  15. package/docs/getting-started-with-json.md +518 -0
  16. package/docs/minification-compression-sourcemaps-status.md +482 -0
  17. package/docs/minification-compression-sourcemaps-test-results.md +205 -0
  18. package/docs/publishers-guide.md +313 -0
  19. package/docs/resources-guide.md +615 -0
  20. package/docs/serve-helpers.md +406 -0
  21. package/docs/simple-server-api-design.md +13 -0
  22. package/docs/system-architecture.md +275 -0
  23. package/docs/troubleshooting.md +698 -0
  24. package/examples/json/README.md +115 -0
  25. package/examples/json/basic-api/README.md +345 -0
  26. package/examples/json/basic-api/server.js +199 -0
  27. package/examples/json/simple-api/README.md +125 -0
  28. package/examples/json/simple-api/diagnostic-report.json +73 -0
  29. package/examples/json/simple-api/diagnostic-test.js +433 -0
  30. package/examples/json/simple-api/server-debug.md +58 -0
  31. package/examples/json/simple-api/server.js +91 -0
  32. package/examples/json/simple-api/test.js +215 -0
  33. package/http/responders/static/Static_Route_HTTP_Responder.js +1 -2
  34. package/package.json +19 -8
  35. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +65 -12
  36. package/publishers/helpers/preparers/static/bundle/Static_Routes_Responses_Webpage_Bundle_Preparer.js +6 -1
  37. package/publishers/http-function-publisher.js +59 -38
  38. package/publishers/http-webpage-publisher.js +48 -1
  39. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +38 -146
  40. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +54 -5
  41. package/resources/processors/bundlers/js/esbuild/Core_JS_Single_File_Minifying_Bundler_Using_ESBuild.js +36 -4
  42. package/serve-factory.js +36 -9
  43. package/server.js +10 -4
  44. package/test-report.json +0 -0
  45. package/tests/README.md +250 -0
  46. package/tests/assigners.test.js +316 -0
  47. package/tests/bundlers.test.js +329 -0
  48. package/tests/configuration-validation.test.js +530 -0
  49. package/tests/content-analysis.test.js +641 -0
  50. package/tests/end-to-end.test.js +496 -0
  51. package/tests/error-handling.test.js +746 -0
  52. package/tests/performance.test.js +653 -0
  53. package/tests/publishers.test.js +395 -0
  54. package/tests/temp_invalid.js +7 -0
  55. package/tests/temp_invalid_utf8.js +1 -0
  56. package/tests/temp_malformed.js +10 -0
  57. package/tests/test-runner.js +261 -0
@@ -23,7 +23,54 @@ class HTTP_Webpage_Publisher extends HTTP_Webpageorsite_Publisher {
23
23
  return webpage;
24
24
  }
25
25
  });
26
- this.static_routes_responses_webpage_bundle_preparer = new Static_Routes_Responses_Webpage_Bundle_Preparer();
26
+
27
+ // Store bundler configuration for passing to preparers
28
+ this.bundler_config = spec.bundler || {};
29
+
30
+ // Add input validation for bundler configuration
31
+ if (spec.bundler !== undefined && typeof spec.bundler !== 'object') {
32
+ throw new Error('bundler must be an object');
33
+ }
34
+
35
+ // Add input validation for compression settings
36
+ if (this.bundler_config.compression) {
37
+ const compression = this.bundler_config.compression;
38
+ if (compression.enabled !== undefined && typeof compression.enabled !== 'boolean') {
39
+ throw new Error('bundler.compression.enabled must be a boolean');
40
+ }
41
+ if (compression.algorithms && !Array.isArray(compression.algorithms)) {
42
+ throw new Error('bundler.compression.algorithms must be an array');
43
+ }
44
+ if (compression.algorithms) {
45
+ const validAlgorithms = ['gzip', 'br'];
46
+ for (const alg of compression.algorithms) {
47
+ if (!validAlgorithms.includes(alg)) {
48
+ throw new Error(`Invalid compression algorithm: ${alg}. Must be one of: ${validAlgorithms.join(', ')}`);
49
+ }
50
+ }
51
+ }
52
+ if (compression.threshold !== undefined && (typeof compression.threshold !== 'number' || compression.threshold < 0)) {
53
+ throw new Error('bundler.compression.threshold must be a non-negative number');
54
+ }
55
+ }
56
+
57
+ // Add input validation for minification settings
58
+ if (this.bundler_config.minify) {
59
+ const minify = this.bundler_config.minify;
60
+ if (minify.level !== undefined && typeof minify.level !== 'string') {
61
+ throw new Error('bundler.minify.level must be a string');
62
+ }
63
+ if (minify.level !== undefined) {
64
+ const validLevels = ['conservative', 'normal', 'aggressive'];
65
+ if (!validLevels.includes(minify.level)) {
66
+ throw new Error(`Invalid minification level: ${minify.level}. Must be one of: ${validLevels.join(', ')}`);
67
+ }
68
+ }
69
+ }
70
+
71
+ this.static_routes_responses_webpage_bundle_preparer = new Static_Routes_Responses_Webpage_Bundle_Preparer({
72
+ bundler_config: this.bundler_config
73
+ });
27
74
  (async() => {
28
75
  const res_get_ready = await this.get_ready();
29
76
  this.raise('ready', res_get_ready);
@@ -18,16 +18,20 @@ const CSS_And_JS_From_JS_String_Extractor = require('../../../extractors/js/css_
18
18
 
19
19
 
20
20
  class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
21
- constructor(spec) {
21
+ constructor(spec = {}) {
22
22
  super(spec);
23
23
 
24
24
  if (spec.debug !== undefined) this.debug = spec.debug;
25
25
 
26
+ // Store bundler configuration
27
+ this.bundler_config = spec.bundler || {};
28
+
26
29
  //this.css_extractor = new CSS_Extractor();
27
30
 
28
31
 
29
32
  this.non_minifying_bundler = new Core_JS_Non_Minifying_Bundler_Using_ESBuild({
30
- debug: this.debug
33
+ debug: this.debug,
34
+ sourcemaps: this.bundler_config.sourcemaps
31
35
  });
32
36
 
33
37
 
@@ -35,20 +39,27 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
35
39
 
36
40
 
37
41
  // Probably don't use that minifying bundler in debug mode.
38
- this.minifying_js_single_file_bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild();
42
+ this.minifying_js_single_file_bundler = new Core_JS_Single_File_Minifying_Bundler_Using_ESBuild({
43
+ minify: this.bundler_config.minify
44
+ });
39
45
 
40
46
  }
41
47
 
42
48
  bundle(js_file_path) {
43
49
  const {non_minifying_bundler, css_and_js_from_js_string_extractor, minifying_js_single_file_bundler} = this;
44
50
 
51
+ // Validate input
52
+ if (typeof js_file_path !== 'string' || js_file_path.trim() === '') {
53
+ throw new Error('bundle() expects a valid file path string');
54
+ }
55
+
45
56
  // Maybe this should get them positioned absolutely when removed from the grid?
46
57
  // But then what about the space they leave?
47
58
 
48
59
  // This is just a simple principle demo though.
49
60
  // Maybe want a simple and explicit option to change behaviour like I specify.
50
61
 
51
-
62
+
52
63
 
53
64
  const res_obs = obs(async(next, complete, error) => {
54
65
 
@@ -78,30 +89,22 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
78
89
  const {css, js} = res_extract_css_and_js_from_js;
79
90
 
80
91
  if (this.debug) {
81
-
82
- // sourcemap: 'inline'
83
-
84
- // Don't minify the js.
85
-
86
- // Make res bundle items that include the CSS and the non-minified JS.
87
-
88
- // Including the source map would be better still.
92
+ // Generate source maps for CSS-free JS
93
+ const css_free_bundle_result = await non_minifying_bundler.bundle_js_string(js);
94
+ const css_free_bundle_item = css_free_bundle_result[0]._arr[0];
89
95
 
90
96
  const res_bundle = new Bundle();
91
- // Add the non-minified JS (as a bundle item object)
92
-
93
97
  const o_js_bundle_item = {
94
98
  type: 'JavaScript',
95
- extension: 'JS',
96
- text: js
99
+ extension: 'js',
100
+ text: css_free_bundle_item.text
97
101
  }
98
- res_bundle.add(o_js_bundle_item);
102
+ res_bundle.push(o_js_bundle_item);
99
103
  const o_css_bundle_item = {
100
104
  type: 'CSS',
101
105
  extension: 'css',
102
106
  text: css
103
107
  }
104
-
105
108
  res_bundle.push(o_css_bundle_item);
106
109
  next(res_bundle);
107
110
  complete(res_bundle);
@@ -112,125 +115,30 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
112
115
 
113
116
 
114
117
 
115
-
116
118
  } else {
117
-
118
-
119
- const minified_js = await minifying_js_single_file_bundler.bundle(js);
119
+ // Generate source maps for CSS-free JS and minify
120
+ const css_free_bundle_result = await non_minifying_bundler.bundle_js_string(js);
121
+ const css_free_bundle_item = css_free_bundle_result[0]._arr[0];
122
+ const minified_js = await minifying_js_single_file_bundler.bundle(css_free_bundle_item.text);
120
123
 
121
124
  //console.log('minified_js', minified_js);
122
125
  //console.log('minified_js.length', minified_js.length);
123
126
 
124
127
  // it's an array....
125
128
 
126
- if (is_array(minified_js)) {
127
-
128
- if (minified_js.length === 1) {
129
-
130
-
131
- // Should put it all in a single res bundle though....
132
- // Though merging / concating bundles will be fine too.
133
-
134
-
135
-
136
- const minified_js_bundle_collection = minified_js[0];
137
-
138
-
139
-
140
- const o_css_bundle_item = {
141
- type: 'CSS',
142
- extension: 'css',
143
- text: css
144
- }
145
-
146
- minified_js_bundle_collection.push(o_css_bundle_item);
147
-
148
-
149
- // Maybe will provide the class / class instance that processes CSS or SASS/SCSS / whatever else.
150
-
151
-
152
-
153
- // Could add the extracted CSS here.
154
-
155
-
156
-
157
- next(minified_js_bundle_collection);
158
-
159
- // But create a CSS bundle item...
160
-
161
-
162
-
163
-
164
-
165
- // And also the CSS...
166
-
167
-
168
-
169
-
170
-
171
- complete(minified_js_bundle_collection);
172
-
173
-
174
-
175
- const unneeded_looking_into_the_js_bundle = () => {
176
- if (minified_js_bundle_collection._arr) {
177
-
178
- if (minified_js_bundle_collection._arr.length === 1) {
179
-
180
- const minified_js_bundle_item = minified_js_bundle_collection._arr[0];
181
- console.log('minified_js_bundle_item', minified_js_bundle_item);
182
-
183
-
184
- if (minified_js_bundle_item.type === 'JavaScript') {
185
-
186
- //const str_minified_js = minified_js_bundle_item.text;
187
-
188
- //const res =
189
-
190
-
191
-
192
-
193
-
194
- } else {
195
-
196
- console.trace();
197
- throw 'NYI';
198
-
199
- }
200
-
201
-
202
- // Could even check it's js here...?
203
-
204
-
205
- console.trace();
206
- throw 'NYI';
207
-
208
-
209
- } else {
210
- console.trace();
211
- throw 'NYI';
212
- }
213
-
214
- } else {
215
-
216
- console.trace();
217
- throw 'NYI';
218
-
219
- }
220
-
221
- }
222
-
223
-
224
-
225
- } else {
226
- console.trace();
227
- throw 'stop';
129
+ if (is_array(minified_js) && minified_js.length === 1) {
130
+ const minified_bundle = minified_js[0];
131
+ const o_css_bundle_item = {
132
+ type: 'CSS',
133
+ extension: 'css',
134
+ text: css
228
135
  }
229
-
136
+ minified_bundle.push(o_css_bundle_item);
137
+ next(minified_bundle);
138
+ complete(minified_bundle);
230
139
  } else {
231
-
232
140
  console.trace();
233
- throw 'stop';
141
+ throw 'Unexpected minified JS structure';
234
142
  }
235
143
 
236
144
 
@@ -240,12 +148,8 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
240
148
 
241
149
 
242
150
 
243
-
244
-
245
151
 
246
-
247
-
248
-
152
+
249
153
  //console.trace();
250
154
  //throw 'stop';
251
155
 
@@ -259,13 +163,11 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
259
163
 
260
164
 
261
165
 
262
-
263
166
  // Maybe that will be a Ready_To_Serve_Static_Bundle
264
167
 
265
168
 
266
169
 
267
170
 
268
-
269
171
  // Though it needs to provide (a bundle of?) both JS and CSS.
270
172
  // Better to use a standard bundle class (collection) to transfer these objects.
271
173
 
@@ -291,17 +193,6 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
291
193
 
292
194
 
293
195
 
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
196
  //console.log('res_extract_css_and_js_from_js', res_extract_css_and_js_from_js);
306
197
 
307
198
  //console.trace();
@@ -372,7 +263,6 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
372
263
 
373
264
 
374
265
 
375
-
376
266
  });
377
267
  return res_obs;
378
268
 
@@ -382,8 +272,10 @@ class Advanced_JS_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
382
272
 
383
273
  }
384
274
 
275
+
385
276
 
386
277
 
387
278
  }
388
279
 
280
+
389
281
  module.exports = Advanced_JS_Bundler_Using_ESBuild;
@@ -14,11 +14,13 @@ const Bundler_Using_ESBuild = require('./Bundler_Using_ESBuild');
14
14
 
15
15
 
16
16
  class Core_JS_Non_Minifying_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
17
- constructor(spec) {
17
+ constructor(spec = {}) {
18
18
  super(spec);
19
19
 
20
20
  if (spec.debug !== undefined) this.debug = spec.debug;
21
21
 
22
+ // Store sourcemap configuration
23
+ this.sourcemap_config = spec.sourcemaps || {};
22
24
 
23
25
  }
24
26
 
@@ -42,11 +44,56 @@ class Core_JS_Non_Minifying_Bundler_Using_ESBuild extends Bundler_Using_ESBuild
42
44
 
43
45
 
44
46
 
47
+ bundle_js_string(js_string) {
48
+ const res_obs = obs(async(next, complete, error) => {
49
+ const o_build = {
50
+ stdin: {
51
+ contents: js_string,
52
+ resolveDir: process.cwd()
53
+ },
54
+ bundle: true,
55
+ treeShaking: true,
56
+ write: false,
57
+ // Remove outdir since we're keeping in memory
58
+ }
59
+
60
+ // Configure sourcemaps based on configuration
61
+ const sourcemapsEnabled = this.sourcemap_config.enabled !== false; // Default: true in debug, false in production
62
+ if (sourcemapsEnabled && (this.debug || this.sourcemap_config.includeInProduction)) {
63
+ o_build.sourcemap = this.sourcemap_config.format || 'inline';
64
+ }
65
+
66
+ let result = await esbuild.build(o_build);
67
+
68
+ if (result.outputFiles.length === 1) {
69
+ const output_file = result.outputFiles[0];
70
+ const res_bundle = new Bundle();
71
+ const o_bundle_item = {
72
+ type: 'JavaScript',
73
+ extension: 'js',
74
+ text: output_file.text
75
+ }
76
+ res_bundle.push(o_bundle_item);
77
+ next(res_bundle);
78
+ complete();
79
+ } else {
80
+ console.trace();
81
+ throw 'NYI';
82
+ }
83
+ });
84
+ return res_obs;
85
+ }
86
+
45
87
  bundle(js_file_path) {
46
88
 
47
89
 
48
90
  const res_obs = obs(async(next, complete, error) => {
49
91
 
92
+ // Validate input
93
+ if (typeof js_file_path !== 'string' || js_file_path.trim() === '') {
94
+ throw new Error('bundle() expects a valid file path string');
95
+ }
96
+
50
97
  //console.log('Core_JS_Bundler_Using_ESBuild bundle js_file_path:', js_file_path);
51
98
 
52
99
  // Looks like we need better linking build options....
@@ -57,7 +104,7 @@ class Core_JS_Non_Minifying_Bundler_Using_ESBuild extends Bundler_Using_ESBuild
57
104
 
58
105
  //format: 'iife',
59
106
  bundle: true,
60
-
107
+
61
108
  // Possibly no minification here....
62
109
  // Want to use non-minified or only partially minified version to separate the JS and the CSS.
63
110
 
@@ -65,11 +112,13 @@ class Core_JS_Non_Minifying_Bundler_Using_ESBuild extends Bundler_Using_ESBuild
65
112
 
66
113
  //sourcemap: 'external',
67
114
  write: false,
68
- outdir: 'out',
115
+ // Remove outdir since we're keeping in memory
69
116
  }
70
117
 
71
- if (this.debug) {
72
- o_build.sourcemap = 'inline';
118
+ // Configure sourcemaps based on configuration
119
+ const sourcemapsEnabled = this.sourcemap_config.enabled !== false; // Default: true in debug, false in production
120
+ if (sourcemapsEnabled && (this.debug || this.sourcemap_config.includeInProduction)) {
121
+ o_build.sourcemap = this.sourcemap_config.format || 'inline';
73
122
  }
74
123
 
75
124
 
@@ -16,8 +16,11 @@ const Bundler_Using_ESBuild = require('./Bundler_Using_ESBuild');
16
16
 
17
17
 
18
18
  class Core_JS_Single_File_Minifying_Bundler_Using_ESBuild extends Bundler_Using_ESBuild {
19
- constructor(spec) {
19
+ constructor(spec = {}) {
20
20
  super(spec);
21
+
22
+ // Store minification configuration
23
+ this.minify_config = spec.minify || this.get_default_minify_config();
21
24
  }
22
25
 
23
26
  // And the options....
@@ -30,10 +33,35 @@ class Core_JS_Single_File_Minifying_Bundler_Using_ESBuild extends Bundler_Using_
30
33
  // Core_JS_Non_Minifying_Bundler_Using_ESBuild for example....
31
34
 
32
35
  // Really specific class names for specific functionality to call reqlly quickly and interchange really quickly.
33
-
36
+
34
37
  // Probably use the non-minifying bundler, and then use a minifier afterwards.
35
38
  // And here the minifier is ESBuild, as is the bundler.
36
39
 
40
+ get_minify_options() {
41
+ const level = this.minify_config.level || 'normal';
42
+ const enabled = this.minify_config.enabled !== false; // Default: true
43
+
44
+ if (!enabled) {
45
+ return false; // Disable minification
46
+ }
47
+
48
+ const baseOptions = {
49
+ conservative: { mangle: false, compress: { sequences: false } },
50
+ normal: { mangle: true, compress: true },
51
+ aggressive: { mangle: true, compress: { drop_console: true, drop_debugger: true } }
52
+ };
53
+
54
+ const options = { ...baseOptions[level], ...this.minify_config.options };
55
+ return options;
56
+ }
57
+
58
+ get_default_minify_config() {
59
+ return {
60
+ enabled: true,
61
+ level: 'normal'
62
+ };
63
+ }
64
+
37
65
 
38
66
 
39
67
 
@@ -43,6 +71,10 @@ class Core_JS_Single_File_Minifying_Bundler_Using_ESBuild extends Bundler_Using_
43
71
  bundle(str_js) {
44
72
 
45
73
 
74
+ // Validate input
75
+ if (typeof str_js !== 'string') {
76
+ throw new Error('bundle() expects a string parameter');
77
+ }
46
78
  const res_obs = obs(async(next, complete, error) => {
47
79
 
48
80
 
@@ -60,7 +92,7 @@ class Core_JS_Single_File_Minifying_Bundler_Using_ESBuild extends Bundler_Using_
60
92
  //sourcefile: 'imaginary-file.js',
61
93
  //loader: 'ts',
62
94
  },
63
- //bundle: true,
95
+ bundle: true,
64
96
 
65
97
  // Possibly no minification here....
66
98
  // Want to use non-minified or only partially minified version to separate the JS and the CSS.
@@ -71,7 +103,7 @@ class Core_JS_Single_File_Minifying_Bundler_Using_ESBuild extends Bundler_Using_
71
103
 
72
104
  //sourcemap: 'external',
73
105
  write: false,
74
- outdir: 'out',
106
+ // Remove outdir since we're keeping in memory
75
107
  })
76
108
  //console.log('result.outputFiles:\n\n');
77
109
  for (let out of result.outputFiles) {
package/serve-factory.js CHANGED
@@ -144,6 +144,9 @@ module.exports = (Server) => {
144
144
  };
145
145
  if (typeof serve_options.ctrl === 'function') {
146
146
  server_spec.Ctrl = serve_options.ctrl;
147
+ } else if (serve_options.api && typeof serve_options.api === 'object') {
148
+ // API-only server: explicitly disable website setup
149
+ server_spec.website = false;
147
150
  }
148
151
  if (root_client_path) {
149
152
  server_spec.src_path_client_js = root_client_path;
@@ -153,7 +156,7 @@ module.exports = (Server) => {
153
156
  if (host) {
154
157
  server_instance.allowed_addresses = Array.isArray(host) ? host : [host];
155
158
  }
156
-
159
+
157
160
  const settle = (resolver, value) => {
158
161
  if (callback) {
159
162
  try {
@@ -164,9 +167,9 @@ module.exports = (Server) => {
164
167
  }
165
168
  return resolver(value);
166
169
  };
167
-
170
+
168
171
  let has_started = false;
169
-
172
+
170
173
  const extra_page_promises = additional_pages.map(([route, cfg]) => prepare_webpage_route(server_instance, route, cfg, {
171
174
  caller_dir,
172
175
  debug: debug_enabled
@@ -177,41 +180,65 @@ module.exports = (Server) => {
177
180
  debug: debug_enabled
178
181
  }));
179
182
  }
180
-
183
+
181
184
  if (serve_options.api && typeof serve_options.api === 'object') {
185
+ console.log('🔍 DEBUG: Setting up API routes');
182
186
  for (const [name, handler] of Object.entries(serve_options.api)) {
183
187
  if (typeof handler === 'function') {
188
+ console.log(`🔍 DEBUG: Publishing API route: ${name}`);
184
189
  server_instance.publish(name, handler);
185
190
  }
186
191
  }
192
+ console.log('🔍 DEBUG: API routes setup complete');
187
193
  }
188
-
194
+
189
195
  return new Promise((resolve, reject) => {
190
196
  const start_server = () => {
191
197
  if (has_started) return;
192
198
  has_started = true;
199
+ console.log('🔍 DEBUG: Calling server_instance.start()');
193
200
  server_instance.start(port, (err) => {
194
- if (err) return settle(reject, err);
201
+ if (err) {
202
+ console.log('🔍 DEBUG: server_instance.start() failed:', err);
203
+ return settle(reject, err);
204
+ }
205
+ console.log('🔍 DEBUG: server_instance.start() succeeded');
195
206
  const message = host ? `Serving on http://${Array.isArray(host) ? host[0] : host}:${port || 0}/` : `Serving on port ${port || 0} (all IPv4 interfaces)`;
196
207
  console.log(message);
197
208
  console.log('Server ready');
198
209
  settle(resolve, server_instance);
199
210
  });
200
211
  };
201
-
212
+
213
+ console.log('🔍 DEBUG: Setting up ready event listener');
202
214
  server_instance.on('ready', () => {
215
+ console.log('🔍 DEBUG: Ready event fired');
203
216
  Promise.allSettled(extra_page_promises).then(results => {
204
217
  const rejected_entry = results.find(result => result.status === 'rejected');
205
218
  if (rejected_entry) {
219
+ console.log('🔍 DEBUG: Extra page promise rejected:', rejected_entry.reason);
206
220
  return settle(reject, rejected_entry.reason);
207
221
  }
222
+ console.log('🔍 DEBUG: All extra page promises resolved, calling start_server()');
208
223
  start_server();
209
- }).catch(err => settle(reject, err));
224
+ }).catch(err => {
225
+ console.log('🔍 DEBUG: Extra page promises error:', err);
226
+ settle(reject, err);
227
+ });
210
228
  });
211
-
229
+
230
+ // For API-only servers, trigger ready immediately after API setup
231
+ if (serve_options.api && typeof serve_options.api === 'object' && !serve_options.ctrl) {
232
+ console.log('🔍 DEBUG: API-only server detected, triggering ready event');
233
+ server_instance.raise('ready');
234
+ }
235
+
236
+ console.log('🔍 DEBUG: Setting up fallback timeout');
212
237
  setTimeout(() => {
213
238
  // Fallback in case ready event never fires (should not happen, but guard just in case)
239
+ console.log('🔍 DEBUG: Fallback timeout triggered, has_started:', has_started);
214
240
  if (!has_started) {
241
+ console.log('🔍 DEBUG: Calling start_server() from fallback');
215
242
  start_server();
216
243
  }
217
244
  }, 2000).unref?.();
package/server.js CHANGED
@@ -85,7 +85,7 @@ class JSGUI_Single_Process_Server extends Evented_Class {
85
85
  const opts_webpage = {
86
86
  'name': this.name || 'Website'
87
87
  };
88
-
88
+
89
89
  if (Ctrl) {
90
90
 
91
91
 
@@ -102,7 +102,7 @@ class JSGUI_Single_Process_Server extends Evented_Class {
102
102
 
103
103
  if (disk_path_client_js) opts_wp_publisher.src_path_client_js = disk_path_client_js;
104
104
 
105
-
105
+
106
106
 
107
107
  // HTTP_Webpage_Publisher probably needs to build the JavaScript. Possibly other assets too.
108
108
  const wp_publisher = new HTTP_Webpage_Publisher(opts_wp_publisher);
@@ -125,7 +125,8 @@ class JSGUI_Single_Process_Server extends Evented_Class {
125
125
 
126
126
 
127
127
  }
128
-
128
+
129
+
129
130
  //console.trace();
130
131
  //throw 'stop';
131
132
 
@@ -145,6 +146,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
145
146
 
146
147
 
147
148
  } else {
149
+ // Check if this is an API-only server (no website needed)
150
+ if (spec.website === false) {
151
+ // API-only server: emit ready immediately after router setup
152
+ this.raise('ready');
153
+ return;
154
+ }
148
155
 
149
156
  // Ahhh the web page publisher may be used instead of the website publisher.
150
157
  // See about making use of relevant shared abstractions.
@@ -173,7 +180,6 @@ class JSGUI_Single_Process_Server extends Evented_Class {
173
180
  });
174
181
  }
175
182
 
176
-
177
183
 
178
184
  Object.defineProperty(this, 'router', { get: () => server_router })
179
185
  }
File without changes