bitwrench 2.0.18 → 2.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -81
- package/dist/bitwrench-bccl.cjs.js +221 -48
- package/dist/bitwrench-bccl.cjs.min.js +3 -3
- package/dist/bitwrench-bccl.esm.js +221 -48
- package/dist/bitwrench-bccl.esm.min.js +3 -3
- package/dist/bitwrench-bccl.umd.js +221 -48
- package/dist/bitwrench-bccl.umd.min.js +3 -3
- package/dist/bitwrench-code-edit.cjs.js +7 -9
- package/dist/bitwrench-code-edit.cjs.min.js +5 -7
- package/dist/bitwrench-code-edit.es5.js +6 -8
- package/dist/bitwrench-code-edit.es5.min.js +5 -7
- package/dist/bitwrench-code-edit.esm.js +7 -9
- package/dist/bitwrench-code-edit.esm.min.js +5 -7
- package/dist/bitwrench-code-edit.umd.js +7 -9
- package/dist/bitwrench-code-edit.umd.min.js +5 -7
- package/dist/bitwrench-debug.js +268 -0
- package/dist/bitwrench-debug.min.js +3 -0
- package/dist/bitwrench-lean.cjs.js +250 -1574
- package/dist/bitwrench-lean.cjs.min.js +6 -6
- package/dist/bitwrench-lean.es5.js +344 -1661
- package/dist/bitwrench-lean.es5.min.js +4 -4
- package/dist/bitwrench-lean.esm.js +250 -1574
- package/dist/bitwrench-lean.esm.min.js +6 -6
- package/dist/bitwrench-lean.umd.js +250 -1574
- package/dist/bitwrench-lean.umd.min.js +6 -6
- package/dist/bitwrench-util-css.cjs.js +1 -1
- package/dist/bitwrench-util-css.cjs.min.js +1 -1
- package/dist/bitwrench-util-css.es5.js +1 -1
- package/dist/bitwrench-util-css.es5.min.js +1 -1
- package/dist/bitwrench-util-css.esm.js +1 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -1
- package/dist/bitwrench-util-css.umd.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js +1 -1
- package/dist/bitwrench.cjs.js +510 -1660
- package/dist/bitwrench.cjs.min.js +7 -7
- package/dist/bitwrench.css +80 -33
- package/dist/bitwrench.es5.js +569 -1694
- package/dist/bitwrench.es5.min.js +5 -5
- package/dist/bitwrench.esm.js +510 -1660
- package/dist/bitwrench.esm.min.js +7 -7
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +510 -1660
- package/dist/bitwrench.umd.min.js +7 -7
- package/dist/builds.json +133 -111
- package/dist/bwserve.cjs.js +2 -2
- package/dist/bwserve.esm.js +2 -2
- package/dist/sri.json +46 -44
- package/package.json +5 -3
- package/readme.html +86 -75
- package/src/bitwrench-bccl-entry.js +3 -4
- package/src/bitwrench-bccl.js +217 -43
- package/src/bitwrench-code-edit.js +6 -8
- package/src/bitwrench-debug.js +245 -0
- package/src/bitwrench-styles.js +35 -8
- package/src/bitwrench.js +212 -1563
- package/src/cli/attach.js +53 -21
- package/src/cli/serve.js +179 -3
- package/src/version.js +3 -3
package/src/cli/attach.js
CHANGED
|
@@ -70,6 +70,14 @@ Examples:
|
|
|
70
70
|
bw> /screenshot body page.png
|
|
71
71
|
bw> /listen .bw-btn click
|
|
72
72
|
bw> /mount #app card {"title":"Hello","content":"World"}
|
|
73
|
+
|
|
74
|
+
Workflow — build a dashboard from your terminal:
|
|
75
|
+
bw> /render #app {"t":"div","c":[{"t":"h2","c":"Dashboard"},{"t":"div","a":{"id":"stats"},"c":[{"t":"span","a":{"id":"users"},"c":"Users: 0"},{"t":"span","a":{"id":"orders"},"c":"Orders: 0"}]}]}
|
|
76
|
+
bw> /patch users "Users: 342"
|
|
77
|
+
bw> /patch orders "Orders: 28"
|
|
78
|
+
bw> /mount #app card {"title":"Status","content":"All systems go"}
|
|
79
|
+
bw> /tree #app 2
|
|
80
|
+
bw> /listen .bw-btn click
|
|
73
81
|
`.trim();
|
|
74
82
|
|
|
75
83
|
/**
|
|
@@ -91,8 +99,9 @@ export function wrapExpression(code) {
|
|
|
91
99
|
/**
|
|
92
100
|
* Run the attach subcommand.
|
|
93
101
|
* @param {string[]} argv - arguments after "attach"
|
|
102
|
+
* @param {object} [ioOpts] - optional input/output streams for testing
|
|
94
103
|
*/
|
|
95
|
-
export function runAttach(argv) {
|
|
104
|
+
export function runAttach(argv, ioOpts) {
|
|
96
105
|
var values;
|
|
97
106
|
|
|
98
107
|
try {
|
|
@@ -129,19 +138,25 @@ export function runAttach(argv) {
|
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
// Dynamic import of bwserve
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
var io = ioOpts || {};
|
|
142
|
+
var importPath = io._importPath || '../bwserve/index.js';
|
|
143
|
+
var importPromise = import(importPath).then(function(bwserve) {
|
|
144
|
+
return startAttach(bwserve, { port: port, allowScreenshot: allowScreenshot, verbose: verbose, input: io.input, output: io.output });
|
|
134
145
|
}).catch(function(err) {
|
|
135
146
|
console.error('Failed to load bwserve: ' + err.message);
|
|
136
147
|
process.exit(1);
|
|
137
148
|
});
|
|
149
|
+
|
|
150
|
+
return importPromise;
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
/**
|
|
141
154
|
* Start the attach server and REPL.
|
|
142
|
-
* @
|
|
155
|
+
* @param {object} bwserve - The bwserve module (or mock)
|
|
156
|
+
* @param {object} opts - { port, allowScreenshot, verbose, input }
|
|
157
|
+
* @returns {{ rl: object, app: object }} readline interface and app for testing
|
|
143
158
|
*/
|
|
144
|
-
function startAttach(bwserve, opts) {
|
|
159
|
+
export function startAttach(bwserve, opts) {
|
|
145
160
|
var app = bwserve.create({
|
|
146
161
|
port: opts.port,
|
|
147
162
|
title: 'bwcli attach',
|
|
@@ -180,7 +195,7 @@ function startAttach(bwserve, opts) {
|
|
|
180
195
|
// Print connection message
|
|
181
196
|
process.stdout.write('\r\x1b[K');
|
|
182
197
|
console.log('[connected] client ' + clientId);
|
|
183
|
-
|
|
198
|
+
safePrompt(true);
|
|
184
199
|
|
|
185
200
|
// Listen for events from _bw_listen
|
|
186
201
|
client.on('_bw_event', function(data) {
|
|
@@ -188,7 +203,7 @@ function startAttach(bwserve, opts) {
|
|
|
188
203
|
console.log('[event] ' + data.event + ' on ' + data.selector +
|
|
189
204
|
' \u2192 ' + data.tagName + (data.id ? '#' + data.id : '') +
|
|
190
205
|
(data.text ? ' "' + data.text.slice(0, 50) + '"' : ''));
|
|
191
|
-
|
|
206
|
+
safePrompt(true);
|
|
192
207
|
});
|
|
193
208
|
|
|
194
209
|
// Handle disconnect
|
|
@@ -205,7 +220,7 @@ function startAttach(bwserve, opts) {
|
|
|
205
220
|
} else {
|
|
206
221
|
console.log('Waiting for connection...');
|
|
207
222
|
}
|
|
208
|
-
|
|
223
|
+
safePrompt(true);
|
|
209
224
|
});
|
|
210
225
|
}
|
|
211
226
|
};
|
|
@@ -223,20 +238,25 @@ function startAttach(bwserve, opts) {
|
|
|
223
238
|
console.log('Waiting for connection...');
|
|
224
239
|
console.log('Type /help for commands, /quit to exit.');
|
|
225
240
|
console.log('');
|
|
226
|
-
|
|
241
|
+
safePrompt();
|
|
227
242
|
});
|
|
228
243
|
|
|
229
244
|
// Create readline REPL
|
|
230
245
|
var rl = createInterface({
|
|
231
|
-
input: process.stdin,
|
|
232
|
-
output: process.stdout,
|
|
246
|
+
input: opts.input || process.stdin,
|
|
247
|
+
output: opts.output || process.stdout,
|
|
233
248
|
prompt: 'bw> '
|
|
234
249
|
});
|
|
250
|
+
var rlClosed = false;
|
|
251
|
+
|
|
252
|
+
function safePrompt(preserveCursor) {
|
|
253
|
+
if (!rlClosed) rl.prompt(preserveCursor);
|
|
254
|
+
}
|
|
235
255
|
|
|
236
256
|
rl.on('line', function(line) {
|
|
237
257
|
line = line.trim();
|
|
238
258
|
if (!line) {
|
|
239
|
-
|
|
259
|
+
safePrompt();
|
|
240
260
|
return;
|
|
241
261
|
}
|
|
242
262
|
|
|
@@ -249,7 +269,7 @@ function startAttach(bwserve, opts) {
|
|
|
249
269
|
// JS expression — requires active client
|
|
250
270
|
if (!activeClient) {
|
|
251
271
|
console.log('No client connected. Add the attach script to a page first.');
|
|
252
|
-
|
|
272
|
+
safePrompt();
|
|
253
273
|
return;
|
|
254
274
|
}
|
|
255
275
|
|
|
@@ -268,26 +288,33 @@ function startAttach(bwserve, opts) {
|
|
|
268
288
|
} else {
|
|
269
289
|
console.log('undefined');
|
|
270
290
|
}
|
|
271
|
-
|
|
291
|
+
safePrompt();
|
|
272
292
|
}).catch(function(err) {
|
|
273
293
|
console.error('[error] ' + err.message);
|
|
274
|
-
|
|
294
|
+
safePrompt();
|
|
275
295
|
});
|
|
276
296
|
});
|
|
277
297
|
|
|
278
298
|
rl.on('close', function() {
|
|
299
|
+
rlClosed = true;
|
|
279
300
|
console.log('\nExiting.');
|
|
280
301
|
app.close().then(function() {
|
|
281
302
|
process.exit(0);
|
|
282
303
|
});
|
|
283
304
|
});
|
|
305
|
+
|
|
306
|
+
return { rl: rl, app: app };
|
|
284
307
|
}
|
|
285
308
|
|
|
286
309
|
/**
|
|
287
310
|
* Handle slash commands in the REPL.
|
|
288
|
-
* @
|
|
311
|
+
* @param {string} line - The full command line (e.g., "/tree #app 2")
|
|
312
|
+
* @param {object|null} activeClient - The active BwServeClient, or null
|
|
313
|
+
* @param {Map} clients - Map of clientId -> client
|
|
314
|
+
* @param {object} opts - { allowScreenshot, verbose }
|
|
315
|
+
* @param {object} rl - readline interface with prompt() method
|
|
289
316
|
*/
|
|
290
|
-
function handleSlashCommand(line, activeClient, clients, opts, rl) {
|
|
317
|
+
export function handleSlashCommand(line, activeClient, clients, opts, rl) {
|
|
291
318
|
var parts = line.split(/\s+/);
|
|
292
319
|
var cmd = parts[0].toLowerCase();
|
|
293
320
|
|
|
@@ -505,9 +532,10 @@ function handleSlashCommand(line, activeClient, clients, opts, rl) {
|
|
|
505
532
|
|
|
506
533
|
/**
|
|
507
534
|
* Pretty-print a DOM tree from _bw_tree.
|
|
508
|
-
* @
|
|
535
|
+
* @param {object} node - Tree node with tag, id, cls, children
|
|
536
|
+
* @param {number} indent - Current indentation level
|
|
509
537
|
*/
|
|
510
|
-
function printTree(node, indent) {
|
|
538
|
+
export function printTree(node, indent) {
|
|
511
539
|
if (!node) return;
|
|
512
540
|
var prefix = ' '.repeat(indent);
|
|
513
541
|
var label = node.tag || '?';
|
|
@@ -523,9 +551,8 @@ function printTree(node, indent) {
|
|
|
523
551
|
|
|
524
552
|
/**
|
|
525
553
|
* Print the REPL help reference.
|
|
526
|
-
* @private
|
|
527
554
|
*/
|
|
528
|
-
function printHelp() {
|
|
555
|
+
export function printHelp() {
|
|
529
556
|
console.log([
|
|
530
557
|
'',
|
|
531
558
|
'bwcli attach — REPL Commands',
|
|
@@ -550,6 +577,11 @@ function printHelp() {
|
|
|
550
577
|
' /unlisten <sel> <event> Remove a listener',
|
|
551
578
|
' /exec <code> Execute JS without capturing return value',
|
|
552
579
|
' /clients List connected clients',
|
|
580
|
+
'',
|
|
581
|
+
' Workflow — build and push a component:',
|
|
582
|
+
' /render #app {"t":"div","c":[{"t":"h2","c":"Hello"},{"t":"span","a":{"id":"msg"},"c":"..."}]}',
|
|
583
|
+
' /patch msg "Component pushed!"',
|
|
584
|
+
' /mount #app card {"title":"Status","content":"OK"}',
|
|
553
585
|
''
|
|
554
586
|
].join('\n'));
|
|
555
587
|
}
|
package/src/cli/serve.js
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* via an input HTTP port or stdin. Broadcasts messages to all connected
|
|
6
6
|
* browser tabs.
|
|
7
7
|
*
|
|
8
|
+
* The input HTTP port also supports interactive commands: POST a JSON
|
|
9
|
+
* object with a `command` field and receive the result as the HTTP
|
|
10
|
+
* response. This is the programmatic equivalent of bwcli attach's REPL.
|
|
11
|
+
*
|
|
8
12
|
* Usage:
|
|
9
13
|
* bwcli serve [dir] [--port N] [--listen N] [--stdin] [--theme name]
|
|
10
14
|
*/
|
|
@@ -40,6 +44,8 @@ Examples:
|
|
|
40
44
|
bwcli serve --stdin Read from pipe instead of input port
|
|
41
45
|
sensor-reader | bwcli serve --stdin Pipe sensor data to browser
|
|
42
46
|
curl -X POST :9000 -d '{"type":"replace","target":"#app","node":{"t":"h1","c":"Hi"}}'
|
|
47
|
+
curl -X POST :9000 -d '{"command":"query","code":"document.title"}'
|
|
48
|
+
curl -X POST :9000 -d '{"command":"clients"}'
|
|
43
49
|
`.trim();
|
|
44
50
|
|
|
45
51
|
/**
|
|
@@ -131,11 +137,151 @@ function parseRelaxedJSON(str) {
|
|
|
131
137
|
return JSON.parse(out.join(''));
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
// Required fields per command (async commands also listed)
|
|
141
|
+
var _COMMAND_REQUIRED = {
|
|
142
|
+
query: ['code'],
|
|
143
|
+
screenshot: [],
|
|
144
|
+
tree: [],
|
|
145
|
+
mount: ['selector', 'factory'],
|
|
146
|
+
exec: ['code'],
|
|
147
|
+
render: ['selector', 'taco'],
|
|
148
|
+
patch: ['id'],
|
|
149
|
+
listen: ['selector', 'event'],
|
|
150
|
+
unlisten: ['selector', 'event'],
|
|
151
|
+
clients: []
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Commands that return a result via promise
|
|
155
|
+
var _ASYNC_COMMANDS = { query: 1, screenshot: 1, tree: 1, mount: 1 };
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Handle an interactive command from the listen port.
|
|
159
|
+
*
|
|
160
|
+
* @param {Object} msg - Parsed message with `command` field
|
|
161
|
+
* @param {Object} app - BwServeApp instance (has _clients Map)
|
|
162
|
+
* @param {boolean} verbose - Log details to stderr
|
|
163
|
+
* @returns {Promise<Object>} Response object ({ok, result, clientId} or {error})
|
|
164
|
+
*/
|
|
165
|
+
function handleCommand(msg, app, verbose) {
|
|
166
|
+
var cmd = msg.command;
|
|
167
|
+
|
|
168
|
+
// clients: no client needed
|
|
169
|
+
if (cmd === 'clients') {
|
|
170
|
+
var ids = [];
|
|
171
|
+
for (var entry of app._clients) {
|
|
172
|
+
if (entry[1] && entry[1].client) ids.push(entry[0]);
|
|
173
|
+
}
|
|
174
|
+
return Promise.resolve({ ok: true, clients: ids });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Validate command name
|
|
178
|
+
if (!_COMMAND_REQUIRED.hasOwnProperty(cmd)) {
|
|
179
|
+
return Promise.resolve({ error: 'Unknown command: ' + cmd });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Validate required fields
|
|
183
|
+
var required = _COMMAND_REQUIRED[cmd];
|
|
184
|
+
for (var k = 0; k < required.length; k++) {
|
|
185
|
+
if (msg[required[k]] === undefined) {
|
|
186
|
+
return Promise.resolve({ error: 'Missing required field: ' + required[k] });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Select client
|
|
191
|
+
var client = null;
|
|
192
|
+
var clientId = null;
|
|
193
|
+
|
|
194
|
+
if (msg.clientId) {
|
|
195
|
+
var record = app._clients.get(msg.clientId);
|
|
196
|
+
if (record && record.client) {
|
|
197
|
+
client = record.client;
|
|
198
|
+
clientId = msg.clientId;
|
|
199
|
+
} else {
|
|
200
|
+
return Promise.resolve({ error: 'Client not found: ' + msg.clientId });
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
// Pick first connected client
|
|
204
|
+
for (var pair of app._clients) {
|
|
205
|
+
if (pair[1] && pair[1].client && !pair[1].client._closed) {
|
|
206
|
+
client = pair[1].client;
|
|
207
|
+
clientId = pair[0];
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!client) {
|
|
214
|
+
return Promise.resolve({ error: 'No clients connected' });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
var timeout = msg.timeout;
|
|
218
|
+
|
|
219
|
+
if (verbose) {
|
|
220
|
+
console.error('[command] ' + cmd + ' -> client ' + clientId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Dispatch
|
|
224
|
+
try {
|
|
225
|
+
switch (cmd) {
|
|
226
|
+
case 'query':
|
|
227
|
+
return client.query(msg.code, { timeout: timeout || 5000 }).then(function(result) {
|
|
228
|
+
return { ok: true, result: result, clientId: clientId };
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
case 'screenshot':
|
|
232
|
+
return client.screenshot(msg.selector || 'body', { timeout: timeout || 10000 }).then(function(result) {
|
|
233
|
+
// Convert Buffer to base64 for JSON response
|
|
234
|
+
var data = result && result.data ? result.data.toString('base64') : null;
|
|
235
|
+
return { ok: true, result: { data: data, width: result.width, height: result.height, format: result.format }, clientId: clientId };
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
case 'tree':
|
|
239
|
+
var pend = client._pend(timeout || 10000);
|
|
240
|
+
client.call('_bw_tree', {
|
|
241
|
+
selector: msg.selector || 'body',
|
|
242
|
+
depth: msg.depth || 3,
|
|
243
|
+
requestId: pend.requestId
|
|
244
|
+
});
|
|
245
|
+
return pend.promise.then(function(result) {
|
|
246
|
+
return { ok: true, result: result, clientId: clientId };
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
case 'mount':
|
|
250
|
+
return client.mount(msg.selector, msg.factory, msg.props || {}, { timeout: timeout || 10000 }).then(function(result) {
|
|
251
|
+
return { ok: true, result: result, clientId: clientId };
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
case 'exec':
|
|
255
|
+
client.exec(msg.code);
|
|
256
|
+
return Promise.resolve({ ok: true, clientId: clientId });
|
|
257
|
+
|
|
258
|
+
case 'render':
|
|
259
|
+
client.render(msg.selector, msg.taco);
|
|
260
|
+
return Promise.resolve({ ok: true, clientId: clientId });
|
|
261
|
+
|
|
262
|
+
case 'patch':
|
|
263
|
+
client.patch(msg.id, msg.content, msg.attr);
|
|
264
|
+
return Promise.resolve({ ok: true, clientId: clientId });
|
|
265
|
+
|
|
266
|
+
case 'listen':
|
|
267
|
+
client.call('_bw_listen', { selector: msg.selector, event: msg.event });
|
|
268
|
+
return Promise.resolve({ ok: true, clientId: clientId });
|
|
269
|
+
|
|
270
|
+
case 'unlisten':
|
|
271
|
+
client.call('_bw_unlisten', { selector: msg.selector, event: msg.event });
|
|
272
|
+
return Promise.resolve({ ok: true, clientId: clientId });
|
|
273
|
+
}
|
|
274
|
+
} catch (err) {
|
|
275
|
+
return Promise.resolve({ error: err.message });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
134
279
|
/**
|
|
135
280
|
* Run the serve subcommand.
|
|
136
281
|
* @param {string[]} argv - arguments after "serve"
|
|
282
|
+
* @param {object} [ioOpts] - optional overrides for testing
|
|
137
283
|
*/
|
|
138
|
-
export function runServe(argv) {
|
|
284
|
+
export function runServe(argv, ioOpts) {
|
|
139
285
|
var values, positionals;
|
|
140
286
|
|
|
141
287
|
try {
|
|
@@ -186,7 +332,9 @@ export function runServe(argv) {
|
|
|
186
332
|
}
|
|
187
333
|
|
|
188
334
|
// Dynamic import of bwserve to avoid loading it at parse time
|
|
189
|
-
|
|
335
|
+
var io = ioOpts || {};
|
|
336
|
+
var importPath = io._importPath || '../../src/bwserve/index.js';
|
|
337
|
+
var importPromise = import(importPath).then(function(bwserve) {
|
|
190
338
|
startServer(bwserve, {
|
|
191
339
|
dir: dir,
|
|
192
340
|
webPort: webPort,
|
|
@@ -202,6 +350,8 @@ export function runServe(argv) {
|
|
|
202
350
|
console.error('Failed to load bwserve: ' + err.message);
|
|
203
351
|
process.exit(1);
|
|
204
352
|
});
|
|
353
|
+
|
|
354
|
+
return importPromise;
|
|
205
355
|
}
|
|
206
356
|
|
|
207
357
|
/**
|
|
@@ -257,7 +407,11 @@ function startServer(bwserve, opts) {
|
|
|
257
407
|
}
|
|
258
408
|
|
|
259
409
|
/**
|
|
260
|
-
* Start the input HTTP server for receiving protocol messages.
|
|
410
|
+
* Start the input HTTP server for receiving protocol messages and interactive commands.
|
|
411
|
+
*
|
|
412
|
+
* Messages with a `command` field are treated as interactive commands --
|
|
413
|
+
* routed to a specific client and the result returned in the HTTP response.
|
|
414
|
+
* Messages with a `type` field (no `command`) are broadcast to all clients.
|
|
261
415
|
*/
|
|
262
416
|
function startInputServer(app, listenPort, verbose) {
|
|
263
417
|
var inputServer = createServer(function(req, res) {
|
|
@@ -277,6 +431,25 @@ function startInputServer(app, listenPort, verbose) {
|
|
|
277
431
|
return;
|
|
278
432
|
}
|
|
279
433
|
|
|
434
|
+
// Interactive command path
|
|
435
|
+
if (msg.command) {
|
|
436
|
+
handleCommand(msg, app, verbose).then(function(result) {
|
|
437
|
+
var status = result.error ? 400 : 200;
|
|
438
|
+
// Unknown command and client-not-found get 400; timeout also 400
|
|
439
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
440
|
+
res.end(JSON.stringify(result));
|
|
441
|
+
}).catch(function(err) {
|
|
442
|
+
var errMsg = err && err.message ? err.message : String(err);
|
|
443
|
+
if (verbose) {
|
|
444
|
+
console.error('[command] error: ' + errMsg);
|
|
445
|
+
}
|
|
446
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
447
|
+
res.end(JSON.stringify({ error: errMsg }));
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Broadcast path (existing behavior)
|
|
280
453
|
var count = app.broadcast(msg);
|
|
281
454
|
if (verbose) {
|
|
282
455
|
console.error('[input] ' + msg.type + ' -> ' + count + ' client(s)');
|
|
@@ -327,3 +500,6 @@ function startStdinReader(app, verbose) {
|
|
|
327
500
|
if (verbose) console.error('[stdin] Input stream closed. Server stays running.');
|
|
328
501
|
});
|
|
329
502
|
}
|
|
503
|
+
|
|
504
|
+
// Export private functions for testability
|
|
505
|
+
export { parseMessage, parseRelaxedJSON, startServer, startInputServer, startStdinReader, handleCommand };
|
package/src/version.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* DO NOT EDIT DIRECTLY - Use npm run generate-version
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export const VERSION = '2.0.
|
|
6
|
+
export const VERSION = '2.0.19';
|
|
7
7
|
export const VERSION_INFO = {
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.19',
|
|
9
9
|
name: 'bitwrench',
|
|
10
10
|
description: 'A library for javascript UI functions.',
|
|
11
11
|
license: 'BSD-2-Clause',
|
|
12
12
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
13
13
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
14
14
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
15
|
-
buildDate: '2026-03-
|
|
15
|
+
buildDate: '2026-03-22T19:09:32.608Z'
|
|
16
16
|
};
|