bitwrench 2.0.15 → 2.0.17
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 +57 -21
- package/dist/bitwrench-bccl.cjs.js +3750 -0
- package/dist/bitwrench-bccl.cjs.min.js +40 -0
- package/dist/bitwrench-bccl.esm.js +3745 -0
- package/dist/bitwrench-bccl.esm.min.js +40 -0
- package/dist/bitwrench-bccl.umd.js +3756 -0
- package/dist/bitwrench-bccl.umd.min.js +40 -0
- package/dist/bitwrench-code-edit.cjs.js +57 -7
- package/dist/bitwrench-code-edit.cjs.min.js +9 -2
- package/dist/bitwrench-code-edit.es5.js +74 -11
- package/dist/bitwrench-code-edit.es5.min.js +9 -2
- package/dist/bitwrench-code-edit.esm.js +57 -7
- package/dist/bitwrench-code-edit.esm.min.js +9 -2
- package/dist/bitwrench-code-edit.umd.js +57 -7
- package/dist/bitwrench-code-edit.umd.min.js +9 -2
- package/dist/bitwrench-lean.cjs.js +905 -157
- package/dist/bitwrench-lean.cjs.min.js +7 -7
- package/dist/bitwrench-lean.es5.js +931 -157
- package/dist/bitwrench-lean.es5.min.js +5 -5
- package/dist/bitwrench-lean.esm.js +904 -157
- package/dist/bitwrench-lean.esm.min.js +7 -7
- package/dist/bitwrench-lean.umd.js +905 -157
- package/dist/bitwrench-lean.umd.min.js +7 -7
- package/dist/bitwrench.cjs.js +910 -158
- package/dist/bitwrench.cjs.min.js +8 -8
- package/dist/bitwrench.css +60 -17
- package/dist/bitwrench.es5.js +939 -158
- package/dist/bitwrench.es5.min.js +6 -6
- package/dist/bitwrench.esm.js +909 -158
- package/dist/bitwrench.esm.min.js +8 -8
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +910 -158
- package/dist/bitwrench.umd.min.js +8 -8
- package/dist/builds.json +168 -80
- package/dist/bwserve.cjs.js +660 -0
- package/dist/bwserve.esm.js +652 -0
- package/dist/sri.json +36 -28
- package/package.json +20 -3
- package/readme.html +62 -23
- package/src/bitwrench-bccl-entry.js +72 -0
- package/src/bitwrench-bccl.js +5 -1
- package/src/bitwrench-code-edit.js +56 -6
- package/src/bitwrench-color-utils.js +5 -6
- package/src/bitwrench-styles.js +20 -8
- package/src/bitwrench.js +876 -140
- package/src/bwserve/client.js +182 -0
- package/src/bwserve/index.js +363 -0
- package/src/bwserve/shell.js +106 -0
- package/src/cli/index.js +36 -15
- package/src/cli/layout-default.js +47 -32
- package/src/cli/serve.js +325 -0
- package/src/version.js +3 -3
- /package/bin/{bitwrench.js → bwcli.js} +0 -0
package/src/cli/index.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* bwcli — Main entry point for the bitwrench command-line tool
|
|
3
3
|
* Arg parsing with util.parseArgs(), help, version, dispatch
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* bwcli <file> [options] Convert a file to styled HTML
|
|
7
|
+
* bwcli serve [dir] [options] Start bwserve dev server
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
import { parseArgs } from 'node:util';
|
|
7
11
|
import { VERSION } from '../version.js';
|
|
8
12
|
import { convertFile } from './convert.js';
|
|
13
|
+
import { runServe } from './serve.js';
|
|
9
14
|
|
|
10
15
|
const USAGE = `
|
|
11
|
-
|
|
16
|
+
bwcli v${VERSION} — bitwrench command-line tool
|
|
12
17
|
|
|
13
18
|
Usage:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
bwcli <file> [options] Convert a file to styled HTML
|
|
20
|
+
bwcli serve [dir] [options] Start bwserve development server
|
|
21
|
+
bwcli --version Print version
|
|
22
|
+
bwcli --help Print this help
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
Convert options:
|
|
19
25
|
-o, --output <file> Output file path (default: input with .html extension)
|
|
20
26
|
-c, --css <file> Include external CSS file
|
|
21
27
|
-t, --theme <name> Theme preset (ocean, sunset, forest, slate) or hex colors ("#pri,#sec")
|
|
@@ -26,16 +32,26 @@ Options:
|
|
|
26
32
|
-f, --favicon <path> Favicon path or URL
|
|
27
33
|
--highlight Include highlight.js for syntax highlighting
|
|
28
34
|
-v, --verbose Verbose output
|
|
35
|
+
|
|
36
|
+
Serve options:
|
|
37
|
+
-p, --port <number> Port to listen on (default: 7902)
|
|
38
|
+
-t, --theme <name> Theme preset or hex colors
|
|
39
|
+
--open Open browser on start
|
|
40
|
+
-v, --verbose Verbose output
|
|
41
|
+
|
|
42
|
+
General:
|
|
29
43
|
-h, --help Print this help
|
|
30
44
|
--version Print version
|
|
31
45
|
|
|
32
46
|
Examples:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
bwcli README.md Convert README.md to README.html
|
|
48
|
+
bwcli README.md -o index.html Specify output file
|
|
49
|
+
bwcli README.md -o out.html --theme ocean Apply ocean theme
|
|
50
|
+
bwcli README.md -o out.html --standalone Self-contained offline HTML
|
|
51
|
+
bwcli README.md -o out.html --highlight With syntax highlighting
|
|
52
|
+
bwcli doc.md --theme "#336699,#cc6633" Custom theme colors
|
|
53
|
+
bwcli serve Serve current directory on port 7902
|
|
54
|
+
bwcli serve ./site --port 8080 Serve ./site on port 8080
|
|
39
55
|
`.trim();
|
|
40
56
|
|
|
41
57
|
/**
|
|
@@ -43,6 +59,11 @@ Examples:
|
|
|
43
59
|
* @param {string[]} argv - process.argv.slice(2)
|
|
44
60
|
*/
|
|
45
61
|
export function run(argv) {
|
|
62
|
+
// Check for subcommand before parseArgs (subcommands have different options)
|
|
63
|
+
if (argv.length > 0 && argv[0] === 'serve') {
|
|
64
|
+
return runServe(argv.slice(1));
|
|
65
|
+
}
|
|
66
|
+
|
|
46
67
|
let values, positionals;
|
|
47
68
|
|
|
48
69
|
try {
|
|
@@ -69,13 +90,13 @@ export function run(argv) {
|
|
|
69
90
|
positionals = result.positionals;
|
|
70
91
|
} catch (err) {
|
|
71
92
|
console.error(`Error: ${err.message}`);
|
|
72
|
-
console.error('Run "
|
|
93
|
+
console.error('Run "bwcli --help" for usage.');
|
|
73
94
|
process.exit(1);
|
|
74
95
|
}
|
|
75
96
|
|
|
76
97
|
// --version
|
|
77
98
|
if (values.version) {
|
|
78
|
-
console.log(`
|
|
99
|
+
console.log(`bwcli v${VERSION}`);
|
|
79
100
|
return;
|
|
80
101
|
}
|
|
81
102
|
|
|
@@ -88,7 +109,7 @@ export function run(argv) {
|
|
|
88
109
|
// No positional args → error
|
|
89
110
|
if (positionals.length === 0) {
|
|
90
111
|
console.error('Error: No input file specified.');
|
|
91
|
-
console.error('Run "
|
|
112
|
+
console.error('Run "bwcli --help" for usage.');
|
|
92
113
|
process.exit(1);
|
|
93
114
|
}
|
|
94
115
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bitwrench CLI - Default page layout
|
|
3
|
-
* Wraps converted content in a complete HTML document
|
|
3
|
+
* Wraps converted content in a complete HTML document.
|
|
4
|
+
* Delegates to bw.htmlPage() for document structure.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import bw from '../bitwrench.js';
|
|
@@ -77,7 +78,11 @@ const BASE_PAGE_CSS = `
|
|
|
77
78
|
`;
|
|
78
79
|
|
|
79
80
|
/**
|
|
80
|
-
* Build a complete HTML page from content and options
|
|
81
|
+
* Build a complete HTML page from content and options.
|
|
82
|
+
* Delegates to bw.htmlPage() for document structure, adding CLI-specific
|
|
83
|
+
* concerns: .bw_cli_page wrapper, generator meta tag, highlight.js, and
|
|
84
|
+
* pre-resolved injection strings from inject.js.
|
|
85
|
+
*
|
|
81
86
|
* @param {Object} opts
|
|
82
87
|
* @param {string} opts.title - Page title
|
|
83
88
|
* @param {string} opts.bodyHTML - Rendered HTML content for the body
|
|
@@ -99,44 +104,54 @@ export function makePageLayout(opts) {
|
|
|
99
104
|
highlight = false
|
|
100
105
|
} = opts;
|
|
101
106
|
|
|
102
|
-
const
|
|
103
|
-
const version = bw.version;
|
|
107
|
+
const version = bw.getVersion().version;
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
109
|
+
// Build extra <head> elements: generator meta, injection script, highlight.js CSS
|
|
110
|
+
const headElements = [];
|
|
111
|
+
|
|
112
|
+
// Generator meta tag (CLI-specific)
|
|
113
|
+
headElements.push({
|
|
114
|
+
t: 'meta', a: { name: 'generator', content: 'bitwrench v' + version }
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Injection script from inject.js (already pre-built HTML string)
|
|
118
|
+
if (headInjection) {
|
|
119
|
+
headElements.push(bw.raw(headInjection));
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
let highlightBodyEnd = '';
|
|
122
|
+
// Highlight.js CSS
|
|
116
123
|
if (highlight) {
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
headElements.push({
|
|
125
|
+
t: 'link', a: {
|
|
126
|
+
rel: 'stylesheet',
|
|
127
|
+
href: 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github.min.css'
|
|
128
|
+
}
|
|
129
|
+
});
|
|
119
130
|
}
|
|
120
131
|
|
|
132
|
+
// Wrap body content in .bw_cli_page div (CLI-specific)
|
|
133
|
+
const wrappedBody = '<div class="bw_cli_page">\n' + bodyHTML + '\n</div>';
|
|
134
|
+
|
|
135
|
+
// Build body-end injection (highlight.js init)
|
|
136
|
+
let fullBodyEnd = bodyEndInjection || '';
|
|
137
|
+
if (highlight) {
|
|
138
|
+
fullBodyEnd += '<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>\n<script>hljs.highlightAll();</script>';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Combine all CSS
|
|
121
142
|
const allCSS = BASE_PAGE_CSS + (css ? '\n' + css : '');
|
|
122
143
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<div class="bw_cli_page">
|
|
135
|
-
${bodyHTML}
|
|
136
|
-
</div>
|
|
137
|
-
${bodyEndInjection}${highlightBodyEnd}
|
|
138
|
-
</body>
|
|
139
|
-
</html>`;
|
|
144
|
+
// Use bw.htmlPage() with runtime:'none' since CLI handles injection itself
|
|
145
|
+
var page = bw.htmlPage({
|
|
146
|
+
title: title,
|
|
147
|
+
body: wrappedBody + (fullBodyEnd ? '\n' + fullBodyEnd : ''),
|
|
148
|
+
css: allCSS,
|
|
149
|
+
head: headElements,
|
|
150
|
+
favicon: favicon,
|
|
151
|
+
runtime: 'none'
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return page;
|
|
140
155
|
}
|
|
141
156
|
|
|
142
157
|
export { BASE_PAGE_CSS };
|
package/src/cli/serve.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bwcli serve — CLI subcommand for bwserve pipe server.
|
|
3
|
+
*
|
|
4
|
+
* Serves a directory of bitwrench pages and accepts protocol messages
|
|
5
|
+
* via an input HTTP port or stdin. Broadcasts messages to all connected
|
|
6
|
+
* browser tabs.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bwcli serve [dir] [--port N] [--listen N] [--stdin] [--theme name]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from 'node:util';
|
|
13
|
+
import { createServer } from 'node:http';
|
|
14
|
+
import { createReadStream } from 'node:fs';
|
|
15
|
+
import { VERSION } from '../version.js';
|
|
16
|
+
|
|
17
|
+
var SERVE_USAGE = `
|
|
18
|
+
bwcli serve v${VERSION} — Pipe server for browser UI
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
bwcli serve [dir] [options]
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
dir Directory to serve static files from (default: .)
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
-p, --port <number> Browser-facing web port (default: 8080)
|
|
28
|
+
-l, --listen <number> Input port for protocol messages (default: 9000)
|
|
29
|
+
--stdin Read protocol messages from stdin (newline-delimited JSON)
|
|
30
|
+
-t, --theme <name> Theme preset or hex colors ("#pri,#sec")
|
|
31
|
+
--title <string> Page title (default: "bwcli serve")
|
|
32
|
+
--open Open browser on start
|
|
33
|
+
-v, --verbose Verbose output
|
|
34
|
+
-h, --help Print this help
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
bwcli serve Serve . on :8080, listen on :9000
|
|
38
|
+
bwcli serve ./public --port 3000 Serve ./public on :3000
|
|
39
|
+
bwcli serve --stdin Read from pipe instead of input port
|
|
40
|
+
sensor-reader | bwcli serve --stdin Pipe sensor data to browser
|
|
41
|
+
curl -X POST :9000 -d '{"type":"replace","target":"#app","node":{"t":"h1","c":"Hi"}}'
|
|
42
|
+
`.trim();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse a message string — supports both strict JSON and r-prefixed relaxed JSON.
|
|
46
|
+
* @param {string} str
|
|
47
|
+
* @returns {Object|null} parsed message or null on error
|
|
48
|
+
*/
|
|
49
|
+
function parseMessage(str) {
|
|
50
|
+
str = str.trim();
|
|
51
|
+
if (!str) return null;
|
|
52
|
+
try {
|
|
53
|
+
if (str.charAt(0) === 'r') {
|
|
54
|
+
return parseRelaxedJSON(str.slice(1));
|
|
55
|
+
}
|
|
56
|
+
return JSON.parse(str);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse relaxed JSON (single-quoted strings, trailing commas).
|
|
64
|
+
* State machine — walks char by char.
|
|
65
|
+
*/
|
|
66
|
+
function parseRelaxedJSON(str) {
|
|
67
|
+
var out = [];
|
|
68
|
+
var i = 0;
|
|
69
|
+
var len = str.length;
|
|
70
|
+
|
|
71
|
+
while (i < len) {
|
|
72
|
+
var ch = str[i];
|
|
73
|
+
|
|
74
|
+
if (ch === "'") {
|
|
75
|
+
out.push('"');
|
|
76
|
+
i++;
|
|
77
|
+
while (i < len) {
|
|
78
|
+
var c = str[i];
|
|
79
|
+
if (c === '\\' && i + 1 < len) {
|
|
80
|
+
var next = str[i + 1];
|
|
81
|
+
if (next === "'") {
|
|
82
|
+
out.push("'");
|
|
83
|
+
} else {
|
|
84
|
+
out.push('\\');
|
|
85
|
+
out.push(next);
|
|
86
|
+
}
|
|
87
|
+
i += 2;
|
|
88
|
+
} else if (c === '"') {
|
|
89
|
+
out.push('\\"');
|
|
90
|
+
i++;
|
|
91
|
+
} else if (c === "'") {
|
|
92
|
+
break;
|
|
93
|
+
} else {
|
|
94
|
+
out.push(c);
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
out.push('"');
|
|
99
|
+
i++;
|
|
100
|
+
} else if (ch === '"') {
|
|
101
|
+
out.push(ch);
|
|
102
|
+
i++;
|
|
103
|
+
while (i < len) {
|
|
104
|
+
var c2 = str[i];
|
|
105
|
+
if (c2 === '\\' && i + 1 < len) {
|
|
106
|
+
out.push(c2);
|
|
107
|
+
out.push(str[i + 1]);
|
|
108
|
+
i += 2;
|
|
109
|
+
} else {
|
|
110
|
+
out.push(c2);
|
|
111
|
+
i++;
|
|
112
|
+
if (c2 === '"') break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else if (ch === ',') {
|
|
116
|
+
var j = i + 1;
|
|
117
|
+
while (j < len && (str[j] === ' ' || str[j] === '\t' || str[j] === '\n' || str[j] === '\r')) j++;
|
|
118
|
+
if (j < len && (str[j] === '}' || str[j] === ']')) {
|
|
119
|
+
i++;
|
|
120
|
+
} else {
|
|
121
|
+
out.push(ch);
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
out.push(ch);
|
|
126
|
+
i++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return JSON.parse(out.join(''));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Run the serve subcommand.
|
|
135
|
+
* @param {string[]} argv - arguments after "serve"
|
|
136
|
+
*/
|
|
137
|
+
export function runServe(argv) {
|
|
138
|
+
var values, positionals;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
var result = parseArgs({
|
|
142
|
+
args: argv,
|
|
143
|
+
strict: true,
|
|
144
|
+
allowPositionals: true,
|
|
145
|
+
options: {
|
|
146
|
+
port: { type: 'string', short: 'p' },
|
|
147
|
+
listen: { type: 'string', short: 'l' },
|
|
148
|
+
stdin: { type: 'boolean' },
|
|
149
|
+
theme: { type: 'string', short: 't' },
|
|
150
|
+
title: { type: 'string' },
|
|
151
|
+
open: { type: 'boolean' },
|
|
152
|
+
verbose: { type: 'boolean', short: 'v' },
|
|
153
|
+
help: { type: 'boolean', short: 'h' }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
values = result.values;
|
|
157
|
+
positionals = result.positionals;
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error('Error: ' + err.message);
|
|
160
|
+
console.error('Run "bwcli serve --help" for usage.');
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (values.help) {
|
|
165
|
+
console.log(SERVE_USAGE);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var dir = positionals[0] || '.';
|
|
170
|
+
var webPort = values.port ? parseInt(values.port, 10) : 8080;
|
|
171
|
+
var listenPort = values.listen ? parseInt(values.listen, 10) : 9000;
|
|
172
|
+
var useStdin = !!values.stdin;
|
|
173
|
+
var theme = values.theme || null;
|
|
174
|
+
var title = values.title || 'bwcli serve';
|
|
175
|
+
var verbose = !!values.verbose;
|
|
176
|
+
|
|
177
|
+
if (isNaN(webPort) || webPort < 1 || webPort > 65535) {
|
|
178
|
+
console.error('Error: --port must be a number between 1 and 65535.');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
if (!useStdin && (isNaN(listenPort) || listenPort < 1 || listenPort > 65535)) {
|
|
182
|
+
console.error('Error: --listen must be a number between 1 and 65535.');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Dynamic import of bwserve to avoid loading it at parse time
|
|
187
|
+
import('../../src/bwserve/index.js').then(function(bwserve) {
|
|
188
|
+
startServer(bwserve, {
|
|
189
|
+
dir: dir,
|
|
190
|
+
webPort: webPort,
|
|
191
|
+
listenPort: listenPort,
|
|
192
|
+
useStdin: useStdin,
|
|
193
|
+
theme: theme,
|
|
194
|
+
title: title,
|
|
195
|
+
verbose: verbose,
|
|
196
|
+
open: !!values.open
|
|
197
|
+
});
|
|
198
|
+
}).catch(function(err) {
|
|
199
|
+
console.error('Failed to load bwserve: ' + err.message);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Start the bwserve pipe server.
|
|
206
|
+
*/
|
|
207
|
+
function startServer(bwserve, opts) {
|
|
208
|
+
var app = bwserve.create({
|
|
209
|
+
port: opts.webPort,
|
|
210
|
+
title: opts.title,
|
|
211
|
+
static: opts.dir,
|
|
212
|
+
theme: opts.theme
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Register a passthrough page handler — just keeps clients alive
|
|
216
|
+
app.page('/', function(client) {
|
|
217
|
+
if (opts.verbose) {
|
|
218
|
+
console.error('[bwcli serve] Client connected: ' + client.id);
|
|
219
|
+
}
|
|
220
|
+
client.on('_disconnect', function() {
|
|
221
|
+
if (opts.verbose) {
|
|
222
|
+
console.error('[bwcli serve] Client disconnected: ' + client.id);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Start web server
|
|
228
|
+
app.listen(function() {
|
|
229
|
+
console.error('bwcli serve v' + VERSION);
|
|
230
|
+
console.error(' Web server: http://localhost:' + opts.webPort);
|
|
231
|
+
console.error(' Static dir: ' + opts.dir);
|
|
232
|
+
if (opts.theme) console.error(' Theme: ' + opts.theme);
|
|
233
|
+
|
|
234
|
+
if (opts.useStdin) {
|
|
235
|
+
console.error(' Input: stdin (newline-delimited JSON)');
|
|
236
|
+
startStdinReader(app, opts.verbose);
|
|
237
|
+
} else {
|
|
238
|
+
console.error(' Input port: http://localhost:' + opts.listenPort);
|
|
239
|
+
startInputServer(app, opts.listenPort, opts.verbose);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.error('');
|
|
243
|
+
console.error('Ready. Send protocol messages to push UI to browsers.');
|
|
244
|
+
|
|
245
|
+
if (opts.open) {
|
|
246
|
+
import('node:child_process').then(function(cp) {
|
|
247
|
+
var url = 'http://localhost:' + opts.webPort;
|
|
248
|
+
var cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
249
|
+
cp.exec(cmd + ' ' + url);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Start the input HTTP server for receiving protocol messages.
|
|
257
|
+
*/
|
|
258
|
+
function startInputServer(app, listenPort, verbose) {
|
|
259
|
+
var inputServer = createServer(function(req, res) {
|
|
260
|
+
if (req.method !== 'POST') {
|
|
261
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
262
|
+
res.end(JSON.stringify({ error: 'Use POST' }));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
var body = '';
|
|
267
|
+
req.on('data', function(chunk) { body += chunk; });
|
|
268
|
+
req.on('end', function() {
|
|
269
|
+
var msg = parseMessage(body);
|
|
270
|
+
if (!msg) {
|
|
271
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
272
|
+
res.end(JSON.stringify({ error: 'Invalid message' }));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
var count = app.broadcast(msg);
|
|
277
|
+
if (verbose) {
|
|
278
|
+
console.error('[input] ' + msg.type + ' -> ' + count + ' client(s)');
|
|
279
|
+
}
|
|
280
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
281
|
+
res.end(JSON.stringify({ ok: true, clients: count }));
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
inputServer.listen(listenPort);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Read protocol messages from stdin (newline-delimited).
|
|
290
|
+
*/
|
|
291
|
+
function startStdinReader(app, verbose) {
|
|
292
|
+
var buffer = '';
|
|
293
|
+
|
|
294
|
+
process.stdin.setEncoding('utf8');
|
|
295
|
+
process.stdin.on('data', function(chunk) {
|
|
296
|
+
buffer += chunk;
|
|
297
|
+
var lines = buffer.split('\n');
|
|
298
|
+
buffer = lines.pop() || '';
|
|
299
|
+
|
|
300
|
+
for (var i = 0; i < lines.length; i++) {
|
|
301
|
+
var line = lines[i].trim();
|
|
302
|
+
if (!line) continue;
|
|
303
|
+
|
|
304
|
+
var msg = parseMessage(line);
|
|
305
|
+
if (!msg) {
|
|
306
|
+
if (verbose) console.error('[stdin] Parse error: ' + line.slice(0, 80));
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
var count = app.broadcast(msg);
|
|
311
|
+
if (verbose) {
|
|
312
|
+
console.error('[stdin] ' + msg.type + ' -> ' + count + ' client(s)');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
process.stdin.on('end', function() {
|
|
318
|
+
// Flush remaining buffer
|
|
319
|
+
if (buffer.trim()) {
|
|
320
|
+
var msg = parseMessage(buffer.trim());
|
|
321
|
+
if (msg) app.broadcast(msg);
|
|
322
|
+
}
|
|
323
|
+
if (verbose) console.error('[stdin] Input stream closed. Server stays running.');
|
|
324
|
+
});
|
|
325
|
+
}
|
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.17';
|
|
7
7
|
export const VERSION_INFO = {
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.17',
|
|
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-13T23:15:10.823Z'
|
|
16
16
|
};
|
|
File without changes
|