bertui 1.2.1 โ†’ 1.2.2

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.
@@ -1,18 +1,296 @@
1
- // src/utils/logger.js
2
- import { createLogger } from 'ernest-logger';
3
-
4
- // Create logger instance for BertUI
5
- const logger = createLogger({
6
- time: true,
7
- emoji: true,
8
- level: 'info',
9
- prefix: '[BertUI]',
10
- customLevels: {
11
- server: { color: 'brightCyan', emoji: '๐ŸŒ', priority: 2 },
12
- build: { color: 'brightGreen', emoji: '๐Ÿ“ฆ', priority: 2 },
13
- hmr: { color: 'brightYellow', emoji: '๐Ÿ”ฅ', priority: 2 }
1
+ // bertui/src/logger/logger.js
2
+ // Compact, progress-style CLI โ€” replaces verbose line-by-line logs
3
+
4
+ import { createWriteStream } from 'fs';
5
+ import { join } from 'path';
6
+
7
+ // โ”€โ”€ ANSI helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
8
+ const C = {
9
+ reset: '\x1b[0m',
10
+ bold: '\x1b[1m',
11
+ dim: '\x1b[2m',
12
+ red: '\x1b[31m',
13
+ green: '\x1b[32m',
14
+ yellow: '\x1b[33m',
15
+ blue: '\x1b[34m',
16
+ magenta: '\x1b[35m',
17
+ cyan: '\x1b[36m',
18
+ white: '\x1b[37m',
19
+ bgBlack: '\x1b[40m',
20
+ bgRed: '\x1b[41m',
21
+ bgGreen: '\x1b[42m',
22
+ bgBlue: '\x1b[44m',
23
+ bgCyan: '\x1b[46m',
24
+ bgWhite: '\x1b[47m',
25
+ gray: '\x1b[90m',
26
+ };
27
+
28
+ const isTTY = process.stdout.isTTY;
29
+
30
+ // โ”€โ”€ Spinner frames โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
31
+ const SPINNER = ['โ ‹','โ ™','โ น','โ ธ','โ ผ','โ ด','โ ฆ','โ ง','โ ‡','โ '];
32
+ let _spinnerFrame = 0;
33
+ let _spinnerTimer = null;
34
+ let _currentSpinnerLine = '';
35
+
36
+ // โ”€โ”€ Internal state โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
37
+ let _mode = 'idle'; // 'idle' | 'build' | 'dev'
38
+ let _totalSteps = 10;
39
+ let _stepIndex = 0;
40
+ let _stepLabel = '';
41
+ let _stepDetail = '';
42
+ let _errors = [];
43
+ let _warnings = [];
44
+ let _startTime = null;
45
+
46
+ // โ”€โ”€ Header โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
47
+ export function printHeader(mode = 'BUILD') {
48
+ _mode = mode.toLowerCase();
49
+ _startTime = Date.now();
50
+ _errors = [];
51
+ _warnings = [];
52
+
53
+ const W = 46;
54
+ const bar = 'โ–ˆ'.repeat(W);
55
+
56
+ // Big block-letter BERTUI (6 rows)
57
+ const BIG = [
58
+ ' โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•—',
59
+ ' โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘',
60
+ ' โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘',
61
+ ' โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ• โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘',
62
+ ' โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘',
63
+ ' โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•',
64
+ ];
65
+
66
+ process.stdout.write('\n');
67
+ process.stdout.write(`${C.cyan}${C.bold} ${bar}${C.reset}\n`);
68
+ for (const row of BIG) {
69
+ process.stdout.write(`${C.cyan}${C.bold}${row}${C.reset}\n`);
70
+ }
71
+ process.stdout.write(`${C.gray} by Pease Ernest${C.reset}${C.gray} ยท ${C.reset}${C.white}${C.bold}${mode.toUpperCase()}${C.reset}\n`);
72
+ process.stdout.write(`${C.cyan}${C.bold} ${bar}${C.reset}\n`);
73
+ process.stdout.write('\n');
74
+ }
75
+
76
+ // โ”€โ”€ Step progress โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
77
+ export function step(index, total, label, detail = '') {
78
+ _stepIndex = index;
79
+ _totalSteps = total;
80
+ _stepLabel = label;
81
+ _stepDetail = detail;
82
+ _stopSpinner();
83
+ _renderStep('running', detail);
84
+ _startSpinner();
85
+ }
86
+
87
+ export function stepDone(label, detail = '') {
88
+ _stopSpinner();
89
+ _clearLine();
90
+ const idx = String(_stepIndex).padStart(2, ' ');
91
+ const lbl = (label || _stepLabel).padEnd(24, ' ');
92
+ const det = detail ? `${C.gray}${_truncate(detail, 38)}${C.reset}` : '';
93
+ process.stdout.write(
94
+ ` ${C.gray}[${idx}/${_totalSteps}]${C.reset} ${C.green}โœ“${C.reset} ${C.white}${lbl}${C.reset} ${det}\n`
95
+ );
96
+ }
97
+
98
+ export function stepFail(label, detail = '') {
99
+ _stopSpinner();
100
+ _clearLine();
101
+ const idx = String(_stepIndex).padStart(2, ' ');
102
+ const lbl = (label || _stepLabel).padEnd(24, ' ');
103
+ process.stdout.write(
104
+ ` ${C.gray}[${idx}/${_totalSteps}]${C.reset} ${C.red}โœ—${C.reset} ${C.white}${lbl}${C.reset} ${C.red}${detail}${C.reset}\n`
105
+ );
106
+ }
107
+
108
+ // โ”€โ”€ Inline file progress (replaces "Progress: 2/2 (100%)") โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
109
+ export function fileProgress(current, total, filename) {
110
+ if (!isTTY) return;
111
+ _clearLine();
112
+ const pct = Math.round((current / total) * 100);
113
+ const bar = _bar(pct, 16);
114
+ const name = _truncate(filename, 30);
115
+ _currentSpinnerLine =
116
+ ` ${C.gray}[${String(_stepIndex).padStart(2,' ')}/${_totalSteps}]${C.reset}` +
117
+ ` ${C.cyan}โ ธ${C.reset} ${C.white}${_stepLabel.padEnd(24,' ')}${C.reset}` +
118
+ ` ${bar} ${C.gray}${current}/${total}${C.reset} ${C.dim}${name}${C.reset}`;
119
+ process.stdout.write('\r' + _currentSpinnerLine);
120
+ }
121
+
122
+ // โ”€โ”€ Simple log levels (used internally, suppressed in compact mode) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
123
+ export function info(msg) {
124
+ // In compact mode swallow routine info โ€” only pass through to debug log
125
+ _debugLog('INFO', msg);
126
+ }
127
+
128
+ export function success(msg) {
129
+ // Swallow โ€” stepDone() is the visual replacement
130
+ _debugLog('SUCCESS', msg);
131
+ }
132
+
133
+ export function warn(msg) {
134
+ _warnings.push(msg);
135
+ _stopSpinner();
136
+ _clearLine();
137
+ process.stdout.write(` ${C.yellow}โš ${C.reset} ${C.yellow}${msg}${C.reset}\n`);
138
+ if (_stepLabel) _startSpinner();
139
+ }
140
+
141
+ export function error(msg) {
142
+ _errors.push(msg);
143
+ _stopSpinner();
144
+ _clearLine();
145
+ process.stdout.write(` ${C.red}โœ—${C.reset} ${C.red}${msg}${C.reset}\n`);
146
+ }
147
+
148
+ export function debug(msg) {
149
+ _debugLog('DEBUG', msg);
150
+ }
151
+
152
+ // โ”€โ”€ Table (kept for route/island tables but made compact) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
153
+ export function table(rows) {
154
+ if (!rows || rows.length === 0) return;
155
+ _stopSpinner();
156
+ const keys = Object.keys(rows[0]).filter(k => k !== '');
157
+ const widths = keys.map(k =>
158
+ Math.max(k.length, ...rows.map(r => String(r[k] ?? '').length))
159
+ );
160
+
161
+ const hr = ' ' + widths.map(w => 'โ”€'.repeat(w + 2)).join('โ”ผ') ;
162
+ const header = ' ' + keys.map((k, i) => ` ${C.bold}${k.padEnd(widths[i])}${C.reset} `).join('โ”‚');
163
+
164
+ process.stdout.write(`${C.gray}${hr}${C.reset}\n`);
165
+ process.stdout.write(`${header}\n`);
166
+ process.stdout.write(`${C.gray}${hr}${C.reset}\n`);
167
+
168
+ for (const row of rows) {
169
+ const line = ' ' + keys.map((k, i) => ` ${String(row[k] ?? '').padEnd(widths[i])} `).join(`${C.gray}โ”‚${C.reset}`);
170
+ process.stdout.write(`${line}\n`);
171
+ }
172
+ process.stdout.write(`${C.gray}${hr}${C.reset}\n`);
173
+
174
+ if (_stepLabel) _startSpinner();
175
+ }
176
+
177
+ // โ”€โ”€ bigLog โ€” replaced by section headers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
178
+ export function bigLog(title, opts = {}) {
179
+ _stopSpinner();
180
+ _clearLine();
181
+ process.stdout.write(`\n ${C.bold}${C.cyan}โ”€โ”€ ${title} โ”€โ”€${C.reset}\n\n`);
182
+ if (_stepLabel) _startSpinner();
183
+ }
184
+
185
+ // โ”€โ”€ Build/Dev summary โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
186
+ export function printSummary(stats = {}) {
187
+ _stopSpinner();
188
+ process.stdout.write('\n');
189
+
190
+ const dur = _startTime ? `${((Date.now() - _startTime) / 1000).toFixed(2)}s` : '';
191
+
192
+ process.stdout.write(`${C.cyan}${C.bold} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${C.reset}\n`);
193
+ process.stdout.write(`${C.green}${C.bold} โœ“ Done${C.reset}${dur ? ` ${C.gray}${dur}${C.reset}` : ''}\n`);
194
+
195
+ if (stats.routes) _summaryLine('Routes', stats.routes);
196
+ if (stats.serverIslands)_summaryLine('Server Islands', stats.serverIslands);
197
+ if (stats.interactive) _summaryLine('Interactive', stats.interactive);
198
+ if (stats.staticRoutes) _summaryLine('Static', stats.staticRoutes);
199
+ if (stats.jsSize) _summaryLine('JS bundle', stats.jsSize);
200
+ if (stats.cssSize) _summaryLine('CSS bundle', stats.cssSize);
201
+ if (stats.outDir) _summaryLine('Output', stats.outDir);
202
+
203
+ if (_warnings.length > 0) {
204
+ process.stdout.write(`\n ${C.yellow}${_warnings.length} warning(s)${C.reset}\n`);
205
+ }
206
+ if (_errors.length > 0) {
207
+ process.stdout.write(`\n ${C.red}${_errors.length} error(s)${C.reset}\n`);
208
+ _errors.forEach(e => process.stdout.write(` ${C.red} ยท ${e}${C.reset}\n`));
209
+ }
210
+
211
+ process.stdout.write(`${C.cyan}${C.bold} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${C.reset}\n\n`);
212
+ }
213
+
214
+ function _summaryLine(label, value) {
215
+ process.stdout.write(
216
+ ` ${C.gray}${label.padEnd(18)}${C.reset}${C.white}${value}${C.reset}\n`
217
+ );
218
+ }
219
+
220
+ // โ”€โ”€ Spinner internals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
221
+ function _startSpinner() {
222
+ if (!isTTY || _spinnerTimer) return;
223
+ _spinnerTimer = setInterval(() => {
224
+ _spinnerFrame = (_spinnerFrame + 1) % SPINNER.length;
225
+ _renderStep('running', _stepDetail);
226
+ }, 80);
227
+ }
228
+
229
+ function _stopSpinner() {
230
+ if (_spinnerTimer) {
231
+ clearInterval(_spinnerTimer);
232
+ _spinnerTimer = null;
233
+ }
234
+ }
235
+
236
+ function _renderStep(state, detail = '') {
237
+ if (!isTTY) return;
238
+ _clearLine();
239
+ const spin = SPINNER[_spinnerFrame];
240
+ const idx = String(_stepIndex).padStart(2, ' ');
241
+ const lbl = _stepLabel.padEnd(24, ' ');
242
+ const det = detail ? `${C.gray}${_truncate(detail, 38)}${C.reset}` : '';
243
+ _currentSpinnerLine =
244
+ ` ${C.gray}[${idx}/${_totalSteps}]${C.reset} ${C.cyan}${spin}${C.reset} ${C.white}${lbl}${C.reset} ${det}`;
245
+ process.stdout.write('\r' + _currentSpinnerLine);
246
+ }
247
+
248
+ function _clearLine() {
249
+ if (!isTTY) return;
250
+ process.stdout.write('\r\x1b[2K');
251
+ }
252
+
253
+ // โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
254
+ function _bar(pct, width) {
255
+ const filled = Math.round((pct / 100) * width);
256
+ const empty = width - filled;
257
+ return `${C.cyan}${'โ–ˆ'.repeat(filled)}${C.gray}${'โ–‘'.repeat(empty)}${C.reset}`;
258
+ }
259
+
260
+ function _truncate(str, max) {
261
+ if (!str) return '';
262
+ str = String(str);
263
+ return str.length > max ? 'โ€ฆ' + str.slice(-(max - 1)) : str;
264
+ }
265
+
266
+ // โ”€โ”€ Debug file log (always written, never shown in terminal) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
267
+ let _logStream = null;
268
+ function _debugLog(level, msg) {
269
+ if (!_logStream) {
270
+ try {
271
+ _logStream = createWriteStream(
272
+ join(process.cwd(), '.bertui', 'dev.log'),
273
+ { flags: 'a' }
274
+ );
275
+ } catch { return; }
14
276
  }
15
- });
277
+ const ts = new Date().toISOString().substring(11, 23);
278
+ _logStream.write(`[${ts}] [${level}] ${msg}\n`);
279
+ }
16
280
 
17
- // Export the logger
18
- export default logger;
281
+ // โ”€โ”€ Default export (matches existing logger.method() call sites) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
282
+ export default {
283
+ printHeader,
284
+ step,
285
+ stepDone,
286
+ stepFail,
287
+ fileProgress,
288
+ info,
289
+ success,
290
+ warn,
291
+ error,
292
+ debug,
293
+ table,
294
+ bigLog,
295
+ printSummary,
296
+ };
@@ -99,6 +99,17 @@ export async function createDevHandler(options = {}) {
99
99
  }
100
100
  }
101
101
 
102
+ // Error overlay script
103
+ if (url.pathname === '/error-overlay.js') {
104
+ const overlayPath = join(root, 'node_modules/bertui/error-overlay.js');
105
+ const file = Bun.file(overlayPath);
106
+ if (await file.exists()) {
107
+ return new Response(file, {
108
+ headers: { 'Content-Type': 'application/javascript; charset=utf-8', 'Cache-Control': 'no-store' },
109
+ });
110
+ }
111
+ }
112
+
102
113
  // bertui-animate CSS
103
114
  if (url.pathname === '/bertui-animate.css') {
104
115
  const animPath = join(root, 'node_modules/bertui-animate/dist/bertui-animate.min.css');