bitwrench 2.0.16 → 2.0.18

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 (68) hide show
  1. package/README.md +127 -38
  2. package/dist/bitwrench-bccl.cjs.js +13 -9
  3. package/dist/bitwrench-bccl.cjs.min.js +2 -2
  4. package/dist/bitwrench-bccl.esm.js +13 -9
  5. package/dist/bitwrench-bccl.esm.min.js +2 -2
  6. package/dist/bitwrench-bccl.umd.js +13 -9
  7. package/dist/bitwrench-bccl.umd.min.js +2 -2
  8. package/dist/bitwrench-code-edit.cjs.js +1 -1
  9. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  10. package/dist/bitwrench-code-edit.es5.js +1 -1
  11. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  12. package/dist/bitwrench-code-edit.esm.js +1 -1
  13. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  14. package/dist/bitwrench-code-edit.umd.js +1 -1
  15. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  16. package/dist/bitwrench-lean.cjs.js +1438 -920
  17. package/dist/bitwrench-lean.cjs.min.js +20 -20
  18. package/dist/bitwrench-lean.es5.js +1518 -1105
  19. package/dist/bitwrench-lean.es5.min.js +18 -18
  20. package/dist/bitwrench-lean.esm.js +1437 -920
  21. package/dist/bitwrench-lean.esm.min.js +20 -20
  22. package/dist/bitwrench-lean.umd.js +1438 -920
  23. package/dist/bitwrench-lean.umd.min.js +20 -20
  24. package/dist/bitwrench-util-css.cjs.js +236 -0
  25. package/dist/bitwrench-util-css.cjs.min.js +22 -0
  26. package/dist/bitwrench-util-css.es5.js +414 -0
  27. package/dist/bitwrench-util-css.es5.min.js +21 -0
  28. package/dist/bitwrench-util-css.esm.js +230 -0
  29. package/dist/bitwrench-util-css.esm.min.js +21 -0
  30. package/dist/bitwrench-util-css.umd.js +242 -0
  31. package/dist/bitwrench-util-css.umd.min.js +21 -0
  32. package/dist/bitwrench.cjs.js +1450 -928
  33. package/dist/bitwrench.cjs.min.js +21 -21
  34. package/dist/bitwrench.css +456 -132
  35. package/dist/bitwrench.es5.js +1563 -1140
  36. package/dist/bitwrench.es5.min.js +19 -19
  37. package/dist/bitwrench.esm.js +1450 -929
  38. package/dist/bitwrench.esm.min.js +21 -21
  39. package/dist/bitwrench.min.css +1 -1
  40. package/dist/bitwrench.umd.js +1450 -928
  41. package/dist/bitwrench.umd.min.js +21 -21
  42. package/dist/builds.json +178 -90
  43. package/dist/bwserve.cjs.js +528 -68
  44. package/dist/bwserve.esm.js +527 -69
  45. package/dist/sri.json +44 -36
  46. package/package.json +5 -2
  47. package/readme.html +136 -49
  48. package/src/bitwrench-bccl.js +12 -8
  49. package/src/bitwrench-color-utils.js +31 -9
  50. package/src/bitwrench-esm-entry.js +11 -0
  51. package/src/bitwrench-styles.js +439 -232
  52. package/src/bitwrench-util-css.js +229 -0
  53. package/src/bitwrench.js +979 -630
  54. package/src/bwserve/attach.js +57 -0
  55. package/src/bwserve/bwclient.js +141 -0
  56. package/src/bwserve/bwshell.js +102 -0
  57. package/src/bwserve/client.js +151 -1
  58. package/src/bwserve/index.js +139 -29
  59. package/src/cli/attach.js +555 -0
  60. package/src/cli/convert.js +2 -5
  61. package/src/cli/index.js +7 -0
  62. package/src/cli/inject.js +1 -1
  63. package/src/cli/layout-default.js +47 -32
  64. package/src/cli/serve.js +6 -2
  65. package/src/generate-css.js +11 -4
  66. package/src/vendor/html2canvas.min.js +20 -0
  67. package/src/version.js +3 -3
  68. package/src/bwserve/shell.js +0 -103
@@ -17,7 +17,9 @@
17
17
  */
18
18
 
19
19
  import { BwServeClient } from './client.js';
20
- import { generateShell } from './shell.js';
20
+ import { generateShell } from './bwshell.js';
21
+ import { generateAttachScript } from './attach.js';
22
+ import { VERSION } from '../version.js';
21
23
 
22
24
  // Resolve dist/ paths relative to the package root
23
25
  import { fileURLToPath } from 'url';
@@ -26,7 +28,16 @@ import { createServer } from 'http';
26
28
  import { readFileSync, existsSync, statSync } from 'fs';
27
29
 
28
30
  var __dirname = dirname(fileURLToPath(import.meta.url));
31
+
32
+ // Resolve dist/ — try source layout (src/bwserve/), then npm install layout,
33
+ // then dist/ itself (when running from dist/bwserve.esm.js)
29
34
  var DIST_DIR = resolve(__dirname, '..', '..', 'dist');
35
+ if (!existsSync(DIST_DIR)) {
36
+ DIST_DIR = resolve(__dirname, '..', 'dist');
37
+ }
38
+ if (!existsSync(DIST_DIR)) {
39
+ DIST_DIR = __dirname;
40
+ }
30
41
 
31
42
  // MIME type lookup for static file serving
32
43
  var MIME_TYPES = {
@@ -55,6 +66,7 @@ var MIME_TYPES = {
55
66
  * @param {string} [opts.static] - Directory to serve static files from
56
67
  * @param {boolean} [opts.injectBitwrench=true] - Auto-inject bitwrench client JS
57
68
  * @param {string|Object} [opts.theme] - Theme preset name or config object
69
+ * @param {boolean} [opts.allowScreenshot=false] - Enable client.screenshot() capability
58
70
  * @returns {BwServeApp} Application instance
59
71
  */
60
72
  export function create(opts) {
@@ -73,11 +85,13 @@ class BwServeApp {
73
85
  this.staticDir = opts.static || null;
74
86
  this.injectBitwrench = opts.injectBitwrench !== false;
75
87
  this.theme = opts.theme || null;
88
+ this.allowExec = opts.allowExec || false;
89
+ this.allowScreenshot = opts.allowScreenshot || false;
76
90
  this.keepAliveInterval = opts.keepAliveInterval || 15000;
77
91
  this._pages = new Map();
78
92
  this._clients = new Map();
79
- this._server = null;
80
93
  this._clientCounter = 0;
94
+ this._server = null;
81
95
  }
82
96
 
83
97
  /**
@@ -185,31 +199,61 @@ class BwServeApp {
185
199
  // Parse URL path (strip query string)
186
200
  var path = url.split('?')[0];
187
201
 
188
- // /__bw/bitwrench.umd.js — serve bitwrench client library
189
- if (path === '/__bw/bitwrench.umd.js' && method === 'GET') {
202
+ // /bw/attach.js — self-contained attach script for remote debugging
203
+ if (path === '/bw/attach.js' && method === 'GET') {
204
+ return this._serveAttachScript(req, res);
205
+ }
206
+
207
+ // /bw/lib/bitwrench.umd.js — serve bitwrench client library
208
+ if (path === '/bw/lib/bitwrench.umd.js' && method === 'GET') {
190
209
  return this._serveDistFile(res, 'bitwrench.umd.js');
191
210
  }
192
211
 
193
- // /__bw/bitwrench.umd.min.js — serve minified
194
- if (path === '/__bw/bitwrench.umd.min.js' && method === 'GET') {
212
+ // /bw/lib/bitwrench.umd.min.js — serve minified
213
+ if (path === '/bw/lib/bitwrench.umd.min.js' && method === 'GET') {
195
214
  return this._serveDistFile(res, 'bitwrench.umd.min.js');
196
215
  }
197
216
 
198
- // /__bw/bitwrench.css — serve bitwrench CSS
199
- if (path === '/__bw/bitwrench.css' && method === 'GET') {
217
+ // /bw/lib/bitwrench.css — serve bitwrench CSS
218
+ if (path === '/bw/lib/bitwrench.css' && method === 'GET') {
200
219
  return this._serveDistFile(res, 'bitwrench.css');
201
220
  }
202
221
 
203
- // /__bw/events/:clientId — SSE stream
204
- if (path.startsWith('/__bw/events/') && method === 'GET') {
205
- var clientId = path.slice('/__bw/events/'.length);
222
+ // /bw/events/:clientId — SSE stream
223
+ if (path.startsWith('/bw/events/') && method === 'GET') {
224
+ var clientId = path.slice('/bw/events/'.length);
206
225
  return this._handleSSE(req, res, clientId);
207
226
  }
208
227
 
209
- // /__bw/action/:clientId action POST
210
- if (path.startsWith('/__bw/action/') && method === 'POST') {
211
- var actionClientId = path.slice('/__bw/action/'.length);
212
- return this._handleAction(req, res, actionClientId);
228
+ // CORS preflight for /bw/return/ (needed for cross-origin attach)
229
+ if (method === 'OPTIONS' && path.startsWith('/bw/return/')) {
230
+ res.writeHead(204, {
231
+ 'Access-Control-Allow-Origin': '*',
232
+ 'Access-Control-Allow-Methods': 'POST',
233
+ 'Access-Control-Allow-Headers': 'Content-Type'
234
+ });
235
+ res.end();
236
+ return;
237
+ }
238
+
239
+ // /bw/return/<route>/<clientId> — unified return channel
240
+ if (method === 'POST' && path.startsWith('/bw/return/')) {
241
+ var rest = path.slice('/bw/return/'.length);
242
+ var slash = rest.indexOf('/');
243
+ if (slash === -1) {
244
+ res.writeHead(400, { 'Content-Type': 'application/json' });
245
+ res.end(JSON.stringify({ error: 'Invalid return path' }));
246
+ return;
247
+ }
248
+ var route = rest.slice(0, slash);
249
+ var returnClientId = rest.slice(slash + 1);
250
+ return this._handleReturn(req, res, route, returnClientId);
251
+ }
252
+
253
+ // /bw/lib/vendor/:filename — serve vendored libraries (allowlisted)
254
+ if (path.startsWith('/bw/lib/vendor/') && method === 'GET') {
255
+ var vendorFile = path.slice('/bw/lib/vendor/'.length);
256
+ return this._serveVendorFile(res, vendorFile);
213
257
  }
214
258
 
215
259
  // Registered page routes — serve shell HTML
@@ -219,7 +263,8 @@ class BwServeApp {
219
263
  clientId: clientId2,
220
264
  title: this.title,
221
265
  theme: this.theme,
222
- injectBitwrench: this.injectBitwrench
266
+ injectBitwrench: this.injectBitwrench,
267
+ allowExec: this.allowExec
223
268
  });
224
269
  // Store the page path for this client so SSE knows which handler to call
225
270
  this._clients.set(clientId2, { pagePath: path, client: null });
@@ -284,6 +329,7 @@ class BwServeApp {
284
329
 
285
330
  // Create client instance
286
331
  var client = new BwServeClient(clientId, res);
332
+ client._allowScreenshot = this.allowScreenshot;
287
333
 
288
334
  // Look up the pending client record (set during page serve)
289
335
  var pending = self._clients.get(clientId);
@@ -316,37 +362,101 @@ class BwServeApp {
316
362
  }
317
363
 
318
364
  /**
319
- * Handle an action POST from a client.
365
+ * Unified return channel handler.
366
+ * Handles all client-to-server POST-backs via /bw/return/<route>/<clientId>.
367
+ *
368
+ * Routes:
369
+ * action — fire-and-forget action dispatch (no requestId)
370
+ * query — resolve pending query promise
371
+ * mount — resolve pending mount promise
372
+ * screenshot — resolve pending screenshot promise
373
+ *
320
374
  * @private
321
375
  */
322
- _handleAction(req, res, clientId) {
376
+ _handleReturn(req, res, route, clientId) {
323
377
  var record = this._clients.get(clientId);
324
378
  if (!record || !record.client) {
325
- res.writeHead(404, { 'Content-Type': 'application/json' });
379
+ res.writeHead(404, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
326
380
  res.end(JSON.stringify({ error: 'Unknown client' }));
327
381
  return;
328
382
  }
329
383
 
330
384
  var body = '';
331
- req.on('data', function(chunk) {
332
- body += chunk;
333
- });
385
+ req.on('data', function(chunk) { body += chunk; });
334
386
  req.on('end', function() {
335
387
  try {
336
388
  var data = JSON.parse(body);
337
- var action = data.action;
338
- var payload = data.data || data;
339
- record.client._dispatch(action, payload);
340
- res.writeHead(200, { 'Content-Type': 'application/json' });
389
+ if (route === 'action' || route === 'event') {
390
+ // Action/event dispatch (no requestId/pending pattern)
391
+ var action = route === 'event'
392
+ ? '_bw_event'
393
+ : (data.result ? data.result.action : data.action);
394
+ var payload = route === 'event'
395
+ ? (data.result || data)
396
+ : (data.result ? data.result.data : data.data || data);
397
+ record.client._dispatch(action, payload);
398
+ } else {
399
+ // All other routes: resolve pending promise
400
+ record.client._resolvePending(data.requestId, data);
401
+ }
402
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
341
403
  res.end(JSON.stringify({ ok: true }));
342
404
  } catch (e) {
343
- res.writeHead(400, { 'Content-Type': 'application/json' });
405
+ res.writeHead(400, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
344
406
  res.end(JSON.stringify({ error: e.message }));
345
407
  }
346
408
  });
347
409
  }
410
+
411
+ /**
412
+ * Serve the self-contained attach script at /bw/attach.js.
413
+ * Loads bitwrench + bwclient and auto-connects via SSE.
414
+ * @private
415
+ */
416
+ _serveAttachScript(req, res) {
417
+ try {
418
+ var js = generateAttachScript({ origin: '' });
419
+ res.writeHead(200, {
420
+ 'Content-Type': 'application/javascript; charset=utf-8',
421
+ 'Access-Control-Allow-Origin': '*',
422
+ 'Cache-Control': 'no-cache'
423
+ });
424
+ res.end(js);
425
+ } catch (err) {
426
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
427
+ res.end('Error generating attach script: ' + err.message);
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Serve a vendored library file (allowlisted filenames only).
433
+ * @private
434
+ */
435
+ _serveVendorFile(res, filename) {
436
+ var allowed = ['html2canvas.min.js'];
437
+ if (allowed.indexOf(filename) === -1) {
438
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
439
+ res.end('Not found');
440
+ return;
441
+ }
442
+ var vendorDir = resolve(__dirname, '..', 'vendor');
443
+ var filePath = join(vendorDir, filename);
444
+ if (!existsSync(filePath)) {
445
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
446
+ res.end('Vendor file not found: ' + filename);
447
+ return;
448
+ }
449
+ var content = readFileSync(filePath);
450
+ res.writeHead(200, {
451
+ 'Content-Type': 'application/javascript; charset=utf-8',
452
+ 'Cache-Control': 'public, max-age=86400'
453
+ });
454
+ res.end(content);
455
+ }
348
456
  }
349
457
 
350
- export { BwServeApp, BwServeClient };
458
+ export var version = VERSION;
459
+
460
+ export { BwServeApp, BwServeClient, generateShell };
351
461
 
352
- export default { create, BwServeApp, BwServeClient };
462
+ export default { create, version: VERSION, BwServeApp, BwServeClient, generateShell };