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
@@ -0,0 +1,91 @@
1
+ // Simple JSON API Example
2
+ // Demonstrates basic JSON API functionality with minimal code
3
+
4
+ const Server = require('../../../server');
5
+
6
+ // Simple in-memory data store
7
+ let messages = [
8
+ { id: 1, text: 'Hello, World!', author: 'System', timestamp: new Date() },
9
+ { id: 2, text: 'Welcome to JSGUI3 Server JSON API', author: 'API', timestamp: new Date() }
10
+ ];
11
+
12
+ let nextId = 3;
13
+
14
+ Server.serve({
15
+ // No UI control - pure JSON API server
16
+ // ctrl: undefined,
17
+
18
+ api: {
19
+ // GET /api/messages - Get all messages
20
+ 'messages': () => ({
21
+ messages: messages,
22
+ count: messages.length
23
+ }),
24
+
25
+ // GET /api/status - Server status
26
+ 'status': () => ({
27
+ status: 'running',
28
+ timestamp: new Date(),
29
+ version: '1.0.0',
30
+ uptime: process.uptime()
31
+ }),
32
+
33
+ // POST /api/add-message - Add a new message
34
+ 'add-message': (data) => {
35
+ if (!data.text || typeof data.text !== 'string') {
36
+ throw new Error('Message text is required');
37
+ }
38
+
39
+ if (data.text.length > 500) {
40
+ throw new Error('Message text must be 500 characters or less');
41
+ }
42
+
43
+ const newMessage = {
44
+ id: nextId++,
45
+ text: data.text.trim(),
46
+ author: data.author || 'Anonymous',
47
+ timestamp: new Date()
48
+ };
49
+
50
+ messages.push(newMessage);
51
+
52
+ return {
53
+ success: true,
54
+ message: newMessage
55
+ };
56
+ },
57
+
58
+ // POST /api/clear-messages - Clear all messages
59
+ 'clear-messages': () => {
60
+ const count = messages.length;
61
+ messages = [];
62
+ nextId = 1;
63
+
64
+ return {
65
+ success: true,
66
+ clearedCount: count,
67
+ message: `Cleared ${count} messages`
68
+ };
69
+ }
70
+ },
71
+
72
+ port: 3002
73
+ }).then(server => {
74
+ console.log('🚀 Simple JSON API Server started!');
75
+ console.log(`📡 Running at: http://localhost:${server.port}`);
76
+ console.log('\n📋 Available endpoints:');
77
+ console.log(' GET /api/messages - Get all messages');
78
+ console.log(' GET /api/status - Server status');
79
+ console.log(' POST /api/add-message - Add new message');
80
+ console.log(' POST /api/clear-messages - Clear all messages');
81
+ console.log('\n🧪 Test with:');
82
+ console.log(' curl http://localhost:3002/api/status');
83
+ console.log(' curl -X POST http://localhost:3002/api/add-message \\');
84
+ console.log(' -H "Content-Type: application/json" \\');
85
+ console.log(' -d \'{"text":"Hello from curl!","author":"Tester"}\'');
86
+
87
+ }).catch(err => {
88
+ console.error('❌ Failed to start server:', err.message);
89
+ console.error('❌ Full error:', err);
90
+ process.exit(1);
91
+ });
@@ -0,0 +1,215 @@
1
+ // Test file for the simple JSON API example
2
+ // Runs the server and tests all endpoints
3
+
4
+ const { spawn } = require('child_process');
5
+ const http = require('http');
6
+
7
+ async function testSimpleAPI() {
8
+ console.log('🧪 Testing Simple JSON API Example');
9
+ console.log('=====================================\n');
10
+
11
+ let serverProcess;
12
+
13
+ try {
14
+ // Start the server
15
+ console.log('🚀 Starting server...');
16
+ serverProcess = spawn('node', ['server.js'], {
17
+ cwd: __dirname,
18
+ stdio: ['pipe', 'pipe', 'pipe']
19
+ });
20
+
21
+ // Wait for server to start
22
+ await new Promise((resolve, reject) => {
23
+ let output = '';
24
+ const timeout = setTimeout(() => {
25
+ reject(new Error('Server startup timeout'));
26
+ }, 10000);
27
+
28
+ serverProcess.stdout.on('data', (data) => {
29
+ output += data.toString();
30
+ console.log('Server output:', data.toString().trim());
31
+ // Look for server started message indicating server is listening
32
+ if (output.includes('🚀 Simple JSON API Server started!')) {
33
+ clearTimeout(timeout);
34
+ console.log('✅ Server started successfully and is listening');
35
+ // Wait a moment for server to be fully ready
36
+ setTimeout(() => {
37
+ console.log('⏳ Waiting for server to be ready...');
38
+ setTimeout(resolve, 0); // Brief additional wait
39
+ }, 0);
40
+ }
41
+ });
42
+
43
+ serverProcess.stderr.on('data', (data) => {
44
+ console.error('Server error:', data.toString());
45
+ });
46
+
47
+ serverProcess.on('error', reject);
48
+ });
49
+
50
+ const baseUrl = 'http://localhost:3002';
51
+
52
+ // Test 1: GET /api/status
53
+ console.log('\n📋 Test 1: GET /api/status');
54
+ let statusResponse;
55
+ try {
56
+ statusResponse = await makeRequest('GET', '/api/status');
57
+ console.log('Response:', JSON.stringify(statusResponse, null, 2));
58
+ } catch (error) {
59
+ console.error('Request failed:', error.message);
60
+ console.error('Full error:', error);
61
+ throw error;
62
+ }
63
+
64
+ if (!statusResponse.status || statusResponse.status !== 'running') {
65
+ throw new Error('Status endpoint failed - expected status: "running"');
66
+ }
67
+ console.log('✅ Status test passed - server is running');
68
+
69
+ // Test 2: GET /api/messages
70
+ console.log('\n📋 Test 2: GET /api/messages');
71
+ const messagesResponse = await makeRequest('GET', '/api/messages');
72
+ console.log('Response:', JSON.stringify(messagesResponse, null, 2));
73
+
74
+ if (!messagesResponse.messages || !Array.isArray(messagesResponse.messages)) {
75
+ throw new Error('Messages endpoint failed - expected messages array');
76
+ }
77
+ console.log(`✅ Messages test passed - found ${messagesResponse.messages.length} initial messages`);
78
+
79
+ // Test 3: POST /api/add-message
80
+ console.log('\n📋 Test 3: POST /api/add-message');
81
+ const addMessageData = {
82
+ text: 'Test message from automated test',
83
+ author: 'Test Suite'
84
+ };
85
+ const addResponse = await makeRequest('POST', '/api/add-message', addMessageData);
86
+ console.log('Response:', JSON.stringify(addResponse, null, 2));
87
+
88
+ if (!addResponse.success || !addResponse.message) {
89
+ throw new Error('Add message endpoint failed - expected success response with message');
90
+ }
91
+ console.log('✅ Add message test passed - message added successfully');
92
+
93
+ // Test 4: Verify message was added
94
+ console.log('\n📋 Test 4: Verify message was added');
95
+ const messagesAfterAdd = await makeRequest('GET', '/api/messages');
96
+ const hasNewMessage = messagesAfterAdd.messages.some(msg =>
97
+ msg.text === 'Test message from automated test'
98
+ );
99
+
100
+ if (!hasNewMessage) {
101
+ throw new Error('Message was not added correctly - test message not found in messages list');
102
+ }
103
+ console.log('✅ Message verification test passed - test message found in list');
104
+
105
+ // Test 5: POST /api/clear-messages
106
+ console.log('\n📋 Test 5: POST /api/clear-messages');
107
+ const clearResponse = await makeRequest('POST', '/api/clear-messages');
108
+ console.log('Response:', JSON.stringify(clearResponse, null, 2));
109
+
110
+ if (!clearResponse.success) {
111
+ throw new Error('Clear messages endpoint failed - expected success response');
112
+ }
113
+ console.log('✅ Clear messages test passed - messages cleared successfully');
114
+
115
+ // Test 6: Verify messages were cleared
116
+ console.log('\n📋 Test 6: Verify messages were cleared');
117
+ const messagesAfterClear = await makeRequest('GET', '/api/messages');
118
+
119
+ if (messagesAfterClear.count !== 0) {
120
+ throw new Error('Messages were not cleared correctly - expected count to be 0');
121
+ }
122
+ console.log('✅ Message clearing verification test passed - all messages cleared');
123
+
124
+ // Test 7: Error handling
125
+ console.log('\n📋 Test 7: Error handling test');
126
+ try {
127
+ const errorResponse = await makeRequest('POST', '/api/add-message', { text: '' });
128
+ console.log('Error response:', JSON.stringify(errorResponse, null, 2));
129
+ if (!errorResponse.error || !errorResponse.error.includes('Message text is required')) {
130
+ throw new Error('Error handling test failed - expected error response with "Message text is required"');
131
+ }
132
+ console.log('✅ Error handling test passed - validation working correctly');
133
+ } catch (error) {
134
+ throw new Error('Error handling test failed: ' + error.message);
135
+ }
136
+
137
+ console.log('\n🎉 All tests passed! Simple JSON API is working correctly.');
138
+ console.log('\n📊 Test Summary:');
139
+ console.log(' ✅ Server startup');
140
+ console.log(' ✅ GET /api/status');
141
+ console.log(' ✅ GET /api/messages');
142
+ console.log(' ✅ POST /api/add-message');
143
+ console.log(' ✅ Message persistence');
144
+ console.log(' ✅ POST /api/clear-messages');
145
+ console.log(' ✅ Message clearing');
146
+ console.log(' ✅ Error handling');
147
+
148
+ } catch (error) {
149
+ console.error('\n❌ Test failed:', error.message);
150
+ process.exit(1);
151
+ } finally {
152
+ // Clean up server process
153
+ if (serverProcess) {
154
+ console.log('\n🧹 Shutting down server...');
155
+ serverProcess.kill('SIGTERM');
156
+
157
+ // Wait a bit for graceful shutdown
158
+ setTimeout(() => {
159
+ if (!serverProcess.killed) {
160
+ serverProcess.kill('SIGKILL');
161
+ }
162
+ console.log('✅ Server shut down');
163
+ }, 2000);
164
+ }
165
+ }
166
+ }
167
+
168
+ // Helper function to make HTTP requests
169
+ function makeRequest(method, path, data = null) {
170
+ return new Promise((resolve, reject) => {
171
+ const options = {
172
+ hostname: 'localhost',
173
+ port: 3002,
174
+ path: path,
175
+ method: method,
176
+ headers: {
177
+ 'Content-Type': 'application/json'
178
+ }
179
+ };
180
+
181
+ const req = http.request(options, (res) => {
182
+ let body = '';
183
+
184
+ res.on('data', (chunk) => {
185
+ body += chunk;
186
+ });
187
+
188
+ res.on('end', () => {
189
+ try {
190
+ const response = JSON.parse(body);
191
+ resolve(response);
192
+ } catch (error) {
193
+ reject(new Error(`Failed to parse response: ${body}`));
194
+ }
195
+ });
196
+ });
197
+
198
+ req.on('error', (error) => {
199
+ reject(error);
200
+ });
201
+
202
+ if (data) {
203
+ req.write(JSON.stringify(data));
204
+ }
205
+
206
+ req.end();
207
+ });
208
+ }
209
+
210
+ // Run the tests
211
+ if (require.main === module) {
212
+ testSimpleAPI().catch(console.error);
213
+ }
214
+
215
+ module.exports = { testSimpleAPI };
@@ -84,8 +84,7 @@ class Static_Route_HTTP_Responder extends HTTP_Responder {
84
84
  // Then write the (hopefully compressed) response bodies...
85
85
 
86
86
  if (supported_encodings.br === true) {
87
-
88
- res.write(response_buffers.br);
87
+
89
88
  res.write(response_buffers.br);
90
89
  } else if (supported_encodings.gzip === true) {
91
90
  //console.log('should write gzipped buffer...');
package/package.json CHANGED
@@ -7,19 +7,19 @@
7
7
  "@babel/generator": "^7.28.5",
8
8
  "@babel/parser": "^7.28.5",
9
9
  "cookies": "^0.9.1",
10
- "esbuild": "^0.25.11",
11
- "fnl": "^0.0.36",
12
- "fnlfs": "^0.0.33",
10
+ "esbuild": "^0.25.12",
11
+ "fnl": "^0.0.37",
12
+ "fnlfs": "^0.0.34",
13
13
  "jsgui3-client": "^0.0.120",
14
- "jsgui3-html": "^0.0.168",
14
+ "jsgui3-html": "^0.0.170",
15
15
  "jsgui3-webpage": "^0.0.8",
16
16
  "jsgui3-website": "^0.0.8",
17
- "lang-tools": "^0.0.39",
17
+ "lang-tools": "^0.0.41",
18
18
  "mocha": "^11.7.4",
19
19
  "multiparty": "^4.2.3",
20
20
  "ncp": "^2.0.0",
21
21
  "obext": "^0.0.31",
22
- "rimraf": "^6.0.1",
22
+ "rimraf": "^6.1.0",
23
23
  "stream-to-array": "^2.3.0",
24
24
  "url-parse": "^1.5.10"
25
25
  },
@@ -39,10 +39,21 @@
39
39
  "type": "git",
40
40
  "url": "https://github.com/metabench/jsgui3-server.git"
41
41
  },
42
- "version": "0.0.138",
42
+ "version": "0.0.140",
43
43
  "scripts": {
44
44
  "cli": "node cli.js",
45
45
  "serve": "node cli.js serve",
46
- "test": "mocha tests/**/*.test.js"
46
+ "test": "node tests/test-runner.js",
47
+ "test:mocha": "mocha tests/**/*.test.js",
48
+ "test:bundlers": "node tests/test-runner.js --test=bundlers.test.js",
49
+ "test:assigners": "node tests/test-runner.js --test=assigners.test.js",
50
+ "test:publishers": "node tests/test-runner.js --test=publishers.test.js",
51
+ "test:config": "node tests/test-runner.js --test=configuration-validation.test.js",
52
+ "test:e2e": "node tests/test-runner.js --test=end-to-end.test.js",
53
+ "test:content": "node tests/test-runner.js --test=content-analysis.test.js",
54
+ "test:performance": "node tests/test-runner.js --test=performance.test.js",
55
+ "test:errors": "node tests/test-runner.js --test=error-handling.test.js",
56
+ "test:debug": "node tests/test-runner.js --debug",
57
+ "test:verbose": "node tests/test-runner.js --verbose"
47
58
  }
48
59
  }
@@ -43,6 +43,16 @@ class Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner
43
43
 
44
44
  constructor(spec = {}) {
45
45
  super(spec);
46
+
47
+ // Store compression configuration
48
+ this.compression_config = spec.compression || {};
49
+ this.compression_stats = {
50
+ total_items: 0,
51
+ gzip_compressed: 0,
52
+ brotli_compressed: 0,
53
+ gzip_savings: 0,
54
+ brotli_savings: 0
55
+ };
46
56
  }
47
57
 
48
58
  // assign to bundle....
@@ -54,6 +64,18 @@ class Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner
54
64
  // These assign functions should (all probably) be async
55
65
 
56
66
  async assign(arr_bundled_items) {
67
+ // Get compression configuration with defaults
68
+ const enabled = this.compression_config.enabled !== false; // Default: true
69
+ const algorithms = this.compression_config.algorithms || ['gzip', 'br'];
70
+ const gzipLevel = this.compression_config.gzip?.level || 6;
71
+ const brotliQuality = this.compression_config.brotli?.quality || 6;
72
+ const threshold = this.compression_config.threshold || 1024;
73
+
74
+ if (!enabled) {
75
+ console.log('Compression disabled, skipping compression assignment');
76
+ return;
77
+ }
78
+
57
79
  // go through them....
58
80
 
59
81
  // Maybe check that the correct items are in the bundle.
@@ -84,20 +106,42 @@ class Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner
84
106
  // // response_buffers.identity I think....
85
107
 
86
108
  if (item.text) {
109
+ const originalSize = item.response_buffers.identity.length;
110
+ this.compression_stats.total_items++;
111
+
112
+ // Skip compression if below threshold
113
+ if (originalSize < threshold) {
114
+ console.log(`Skipping compression for ${item.type} (${originalSize} bytes < ${threshold} threshold)`);
115
+ continue;
116
+ }
87
117
 
88
118
  // Async compression definitely seems much better here.
89
- const buf_gzipped = await gzip_compress(item.response_buffers.identity);
90
- item.response_buffers.gzip = buf_gzipped;
91
-
92
- const buf_br = await br_compress(item.response_buffers.identity, {
93
- params: {
94
- //[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
95
- [zlib.constants.BROTLI_PARAM_QUALITY]: 10,
96
- [zlib.constants.BROTLI_PARAM_SIZE_HINT]: item.response_buffers.identity.length,
97
- }
98
- });
99
-
100
- item.response_buffers.br = buf_br;
119
+ if (algorithms.includes('gzip')) {
120
+ const buf_gzipped = await gzip_compress(item.response_buffers.identity, { level: gzipLevel });
121
+ item.response_buffers.gzip = buf_gzipped;
122
+ const compressedSize = buf_gzipped.length;
123
+ const savings = originalSize - compressedSize;
124
+ this.compression_stats.gzip_compressed++;
125
+ this.compression_stats.gzip_savings += savings;
126
+ console.log(`Gzip compressed ${item.type}: ${originalSize} → ${compressedSize} bytes (${Math.round(savings/originalSize*100)}% savings)`);
127
+ }
128
+
129
+ if (algorithms.includes('br')) {
130
+ const buf_br = await br_compress(item.response_buffers.identity, {
131
+ params: {
132
+ //[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
133
+ [zlib.constants.BROTLI_PARAM_QUALITY]: brotliQuality,
134
+ [zlib.constants.BROTLI_PARAM_SIZE_HINT]: item.response_buffers.identity.length,
135
+ }
136
+ });
137
+
138
+ item.response_buffers.br = buf_br;
139
+ const compressedSize = buf_br.length;
140
+ const savings = originalSize - compressedSize;
141
+ this.compression_stats.brotli_compressed++;
142
+ this.compression_stats.brotli_savings += savings;
143
+ console.log(`Brotli compressed ${item.type}: ${originalSize} → ${compressedSize} bytes (${Math.round(savings/originalSize*100)}% savings)`);
144
+ }
101
145
  } else {
102
146
 
103
147
  }
@@ -105,6 +149,15 @@ class Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner
105
149
  //throw 'stop';
106
150
  }
107
151
 
152
+ // Log compression statistics
153
+ console.log('Compression Statistics:', {
154
+ total_items: this.compression_stats.total_items,
155
+ gzip_compressed: this.compression_stats.gzip_compressed,
156
+ brotli_compressed: this.compression_stats.brotli_compressed,
157
+ total_gzip_savings: `${Math.round(this.compression_stats.gzip_savings/1024)}KB`,
158
+ total_brotli_savings: `${Math.round(this.compression_stats.brotli_savings/1024)}KB`
159
+ });
160
+
108
161
  } else {
109
162
  console.trace();
110
163
  throw 'stop';
@@ -17,12 +17,17 @@ class Static_Routes_Responses_Webpage_Bundle_Preparer {
17
17
 
18
18
  //if (spec.debug !== undefined) this.debug = spec.debug;
19
19
 
20
+ // Store bundler configuration to pass to compression assigner
21
+ this.bundler_config = spec.bundler_config || {};
22
+
20
23
  this.routes_assigner = new Single_Control_Webpage_Server_Static_Routes_Assigner();
21
24
 
22
25
  // And the uncompressed response buffer(s) assigner....?
23
26
 
24
27
  this.uncompressed_response_buffers_assigner = new Single_Control_Webpage_Server_Static_Uncompressed_Response_Buffers_Assigner();
25
- this.compressed_response_buffers_assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
28
+ this.compressed_response_buffers_assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
29
+ compression: this.bundler_config.compression
30
+ });
26
31
  this.headers_assigner = new Single_Control_Webpage_Server_Static_Headers_Assigner();
27
32
 
28
33
 
@@ -110,7 +110,12 @@ class Function_Publisher extends HTTP_Publisher {
110
110
  } else {
111
111
 
112
112
  if (content_type === 'application/json') {
113
- obj_input = JSON.parse(buf_input.toString());
113
+ const inputStr = buf_input.toString();
114
+ if (inputStr.trim() === '') {
115
+ obj_input = null;
116
+ } else {
117
+ obj_input = JSON.parse(inputStr);
118
+ }
114
119
  } else {
115
120
  console.trace();
116
121
  throw 'NYI';
@@ -141,57 +146,73 @@ class Function_Publisher extends HTTP_Publisher {
141
146
  // And the function to call may be async.
142
147
  // Can test to see if we get a promise (or observable) back from it.
143
148
 
144
- const fn_res = fn(obj_input);
145
- const tfr = tf(fn_res);
146
- //console.log('fn_res', fn_res);
147
-
148
- //
149
+ try {
150
+ const fn_res = fn(obj_input);
151
+ const tfr = tf(fn_res);
152
+ //console.log('fn_res', fn_res);
149
153
 
150
- if (tfr === 'p') {
151
- // promise
152
- console.log('need to await promise resolution');
154
+ //
153
155
 
154
- fn_res.then(call_res => {
155
- console.log('fn_res then happened, call_res', call_res);
156
- output_all(call_res);
156
+ if (tfr === 'p') {
157
+ // promise
158
+ console.log('need to await promise resolution');
157
159
 
158
- }, err => {
160
+ fn_res.then(call_res => {
161
+ console.log('fn_res then happened, call_res', call_res);
162
+ output_all(call_res);
159
163
 
160
- });
164
+ }, err => {
165
+ console.error('Function execution error:', err);
166
+ if (!res.headersSent) {
167
+ res.writeHead(500, {
168
+ 'Content-Type': 'application/json'
169
+ });
170
+ res.end(JSON.stringify({ error: err.message }));
171
+ }
172
+ });
161
173
 
162
174
 
163
- } else if (tfr === 's') {
164
- // Just write it as a string for the moment I think?
165
- // Or always encode as JSON?
175
+ } else if (tfr === 's') {
176
+ // Just write it as a string for the moment I think?
177
+ // Or always encode as JSON?
166
178
 
167
- // text/plain;charset=UTF-8
179
+ // text/plain;charset=UTF-8
168
180
 
169
- res.writeHead(200, {
170
- 'Content-Type': 'text/plain;charset=UTF-8'//,
171
- //'Transfer-Encoding': 'chunked',
172
- //'Trailer': 'Content-MD5'
173
- });
174
- res.end(fn_res);
181
+ res.writeHead(200, {
182
+ 'Content-Type': 'text/plain;charset=UTF-8'//,
183
+ //'Transfer-Encoding': 'chunked',
184
+ //'Trailer': 'Content-MD5'
185
+ });
186
+ res.end(fn_res);
175
187
 
176
188
 
177
- } else if (tfr === 'o' || tfr === 'a') {
178
- // Just write it as a string for the moment I think?
179
- // Or always encode as JSON?
189
+ } else if (tfr === 'o' || tfr === 'a') {
190
+ // Just write it as a string for the moment I think?
191
+ // Or always encode as JSON?
180
192
 
181
- // text/plain;charset=UTF-8
193
+ // text/plain;charset=UTF-8
182
194
 
183
- res.writeHead(200, {
184
- 'Content-Type': 'application/json'//,
185
- //'Transfer-Encoding': 'chunked',
186
- //'Trailer': 'Content-MD5'
187
- });
188
- res.end(JSON.stringify(fn_res));
195
+ res.writeHead(200, {
196
+ 'Content-Type': 'application/json'//,
197
+ //'Transfer-Encoding': 'chunked',
198
+ //'Trailer': 'Content-MD5'
199
+ });
200
+ res.end(JSON.stringify(fn_res));
189
201
 
190
202
 
191
- } else {
192
- console.log('tfr', tfr);
193
- console.trace();
194
- throw 'NYI';
203
+ } else {
204
+ console.log('tfr', tfr);
205
+ console.trace();
206
+ throw 'NYI';
207
+ }
208
+ } catch (err) {
209
+ console.error('Function execution error:', err);
210
+ if (!res.headersSent) {
211
+ res.writeHead(500, {
212
+ 'Content-Type': 'application/json'
213
+ });
214
+ res.end(JSON.stringify({ error: err.message }));
215
+ }
195
216
  }
196
217
 
197
218