@renpwn/simplelog 0.0.3 → 0.0.5
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 +145 -1
- package/package.json +1 -1
- package/src/Formatter.js +58 -6
- package/src/Logger.js +15 -16
- package/src/Time.js +29 -2
package/README.md
CHANGED
|
@@ -46,9 +46,12 @@ npm install
|
|
|
46
46
|
|
|
47
47
|
```js
|
|
48
48
|
import { simpleLog } from '@renpwn/simplelog'
|
|
49
|
-
|
|
50
49
|
const log = simpleLog()
|
|
51
50
|
|
|
51
|
+
OR
|
|
52
|
+
|
|
53
|
+
import { simpleLog as log} from '@renpwn/simplelog'
|
|
54
|
+
|
|
52
55
|
log.log('hello')
|
|
53
56
|
log.info('info message')
|
|
54
57
|
log.warn('warning')
|
|
@@ -77,12 +80,96 @@ log.debug('Debug message')
|
|
|
77
80
|
log.info('Server started')
|
|
78
81
|
log.warn('Memory usage high')
|
|
79
82
|
log.error({ code: 500, msg: 'Fatal error' })
|
|
83
|
+
|
|
84
|
+
OR just
|
|
85
|
+
...
|
|
86
|
+
color: true, // enable ANSI color (TTY only)
|
|
87
|
+
time: true, // default values locale ID & position PREFIX
|
|
88
|
+
....
|
|
80
89
|
```
|
|
81
90
|
|
|
82
91
|
**Notes**
|
|
83
92
|
- `level` → minimum log level to display
|
|
84
93
|
- `color` → auto-disabled in non-TTY / CI
|
|
85
94
|
- `time` → compact and consistent timestamp
|
|
95
|
+
- true → default { locale: 'id', position: 'prefix' }
|
|
96
|
+
- object → fully configurable (locale, position)
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
#### ⏱️ Custom Time Template
|
|
101
|
+
|
|
102
|
+
By default, `simpleLog` uses a compact, locale-based timestamp:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
[SEN|31.DES|10:15:04]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
You can now fully customize the timestamp format using a **template string**.
|
|
109
|
+
|
|
110
|
+
##### Basic Usage
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const log = simpleLog({
|
|
114
|
+
time: {
|
|
115
|
+
template: '[{HH}:{mm}:{ss}]'
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Output:
|
|
121
|
+
```
|
|
122
|
+
[10:15:04] Server started
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
##### Supported Time Tokens
|
|
128
|
+
|
|
129
|
+
Assume the following datetime:
|
|
130
|
+
```
|
|
131
|
+
Monday, 31 December 2026 — 10:15:04.123
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Token | Output | Description |
|
|
135
|
+
|------|--------|------------|
|
|
136
|
+
| `{TIME}` | `10:15:04` | Full time (HH:mm:ss) |
|
|
137
|
+
| `{HH}` | `10` | Hour |
|
|
138
|
+
| `{mm}` | `15` | Minute |
|
|
139
|
+
| `{ss}` | `04` | Second |
|
|
140
|
+
| `{ms}` | `123` | Millisecond |
|
|
141
|
+
| `{YYYY}` | `2026` | Year |
|
|
142
|
+
| `{MM}` | `12` | Month (number) |
|
|
143
|
+
| `{DD}` | `31` | Day of month |
|
|
144
|
+
| `{DAY}` | `SEN` / `MON` | Locale-based weekday |
|
|
145
|
+
| `{MON}` | `DES` / `DEC` | Locale-based month |
|
|
146
|
+
| `{iso}` | `2026-12-31T10:15:04.123Z` | ISO 8601 |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
##### Locale-aware Template
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
time: {
|
|
154
|
+
locale: 'en',
|
|
155
|
+
template: '{DAY} {DD} {MON} {TIME}'
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Output:
|
|
160
|
+
```
|
|
161
|
+
MON 31 DEC 10:15:04
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
##### Default Behavior (Unchanged)
|
|
167
|
+
|
|
168
|
+
If no template is provided, `simpleLog` keeps using the original format:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
[{DAY}|{DD}.{MON}|{TIME}]
|
|
172
|
+
```
|
|
86
173
|
|
|
87
174
|
---
|
|
88
175
|
|
|
@@ -170,6 +257,10 @@ const timer = setInterval(() => {
|
|
|
170
257
|
**Notes**
|
|
171
258
|
- Progress bars are shown only in TTY
|
|
172
259
|
- Normal logs temporarily clear progress and redraw it
|
|
260
|
+
- Each slot name acts as a unique key
|
|
261
|
+
- `log.updateProgress(name, ...)` updates the progress state for that key
|
|
262
|
+
- Only slots defined in `progress.slots` are rendered
|
|
263
|
+
- Progress is stateful and does not auto-finish or auto-remove
|
|
173
264
|
|
|
174
265
|
---
|
|
175
266
|
|
|
@@ -257,6 +348,41 @@ gold sky mint coral indigo brown olive navy maroon aqua chartreuse plum salmon s
|
|
|
257
348
|
|
|
258
349
|
---
|
|
259
350
|
|
|
351
|
+
#### 🎨 Advanced Styles & Manual Colors
|
|
352
|
+
|
|
353
|
+
The `style` object now supports **manual color definitions**, in addition to named palettes.
|
|
354
|
+
|
|
355
|
+
##### Supported Style Inputs
|
|
356
|
+
|
|
357
|
+
| Type | Example |
|
|
358
|
+
|----|--------|
|
|
359
|
+
| Named palette | `{ color: 'softBlue' }` |
|
|
360
|
+
| ANSI basic | `{ color: 31 }` |
|
|
361
|
+
| ANSI 256 | `{ color: 196 }` |
|
|
362
|
+
| HEX color | `{ color: '#ff0000' }` |
|
|
363
|
+
| RGB string | `{ color: 'rgb(255,0,0)' }` |
|
|
364
|
+
| RGB array | `{ color: [255, 0, 0] }` |
|
|
365
|
+
|
|
366
|
+
All formats also work for `bg` (background).
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
##### HEX & RGB Examples
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
log.info('HEX red', {
|
|
374
|
+
color: '#ff0000',
|
|
375
|
+
bold: true
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
log.warn('RGB blue', {
|
|
379
|
+
bg: 'rgb(30,144,255)',
|
|
380
|
+
color: '#ffffff'
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
260
386
|
### 🟢 Single Style (Always Applied)
|
|
261
387
|
|
|
262
388
|
```js
|
|
@@ -463,6 +589,24 @@ Perfect for:
|
|
|
463
589
|
- Workers / queues
|
|
464
590
|
- Base libraries (`simpleStore`, `simpleFetch`, etc.)
|
|
465
591
|
|
|
592
|
+
Works Everywhere
|
|
593
|
+
|
|
594
|
+
- Linux / macOS terminals
|
|
595
|
+
- Windows Terminal
|
|
596
|
+
- VSCode integrated terminal
|
|
597
|
+
- Termux (Android)
|
|
598
|
+
|
|
599
|
+
If truecolor is not supported, the terminal will gracefully fallback.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
### ✅ Summary
|
|
604
|
+
|
|
605
|
+
- ⏱️ Fully customizable time format with tokens
|
|
606
|
+
- 🎨 Manual color support (HEX / RGB / ANSI)
|
|
607
|
+
- 🧩 Backward compatible
|
|
608
|
+
- 🚀 Safe for minor releases
|
|
609
|
+
|
|
466
610
|
---
|
|
467
611
|
|
|
468
612
|
## 🔗 Links
|
package/package.json
CHANGED
package/src/Formatter.js
CHANGED
|
@@ -61,6 +61,60 @@ const BG = Object.assign({
|
|
|
61
61
|
brightBlue:104, brightMagenta:105, brightCyan:106, brightWhite:107
|
|
62
62
|
}, EXT)
|
|
63
63
|
|
|
64
|
+
function parseRgbString(str) {
|
|
65
|
+
const m = str.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i)
|
|
66
|
+
if (!m) return null
|
|
67
|
+
return m.slice(1).map(n => Math.max(0, Math.min(255, Number(n))))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hexToRgb(hex) {
|
|
71
|
+
hex = hex.replace('#', '')
|
|
72
|
+
if (hex.length === 3)
|
|
73
|
+
hex = hex.split('').map(c => c + c).join('')
|
|
74
|
+
const num = parseInt(hex, 16)
|
|
75
|
+
if (Number.isNaN(num)) return null
|
|
76
|
+
return [(num >> 16) & 255, (num >> 8) & 255, num & 255]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveColor(val, isBg = false) {
|
|
80
|
+
if (val == null) return null
|
|
81
|
+
const prefix = isBg ? 48 : 38
|
|
82
|
+
|
|
83
|
+
// 🔹 RGB array [r,g,b]
|
|
84
|
+
if (Array.isArray(val) && val.length === 3) {
|
|
85
|
+
const [r, g, b] = val.map(n => Math.max(0, Math.min(255, n)))
|
|
86
|
+
return `${prefix};2;${r};${g};${b}`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 🔹 rgb(255,0,0)
|
|
90
|
+
if (typeof val === 'string' && val.startsWith('rgb')) {
|
|
91
|
+
const rgb = parseRgbString(val)
|
|
92
|
+
if (rgb) return `${prefix};2;${rgb.join(';')}`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 🔹 hex '#rrggbb'
|
|
96
|
+
if (typeof val === 'string' && val.startsWith('#')) {
|
|
97
|
+
const rgb = hexToRgb(val)
|
|
98
|
+
if (rgb) return `${prefix};2;${rgb.join(';')}`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 🔹 numeric ANSI / 256-color
|
|
102
|
+
if (typeof val === 'number') {
|
|
103
|
+
return val <= 107 ? val : `${prefix};5;${val}`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 🔹 named palette
|
|
107
|
+
if (typeof val === 'string') {
|
|
108
|
+
const table = isBg ? BG : COLORS
|
|
109
|
+
if (val in table) {
|
|
110
|
+
const c = table[val]
|
|
111
|
+
return c <= 107 ? c : `${prefix};5;${c}`
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
64
118
|
export function format(text, style, tty = true) {
|
|
65
119
|
if (!tty || !style) return text
|
|
66
120
|
|
|
@@ -68,13 +122,11 @@ export function format(text, style, tty = true) {
|
|
|
68
122
|
if (style.bold) codes.push(1)
|
|
69
123
|
if (style.dim) codes.push(2)
|
|
70
124
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
style.color in COLORS && codes.push(c <= 97 ? c : `38;5;${c}`)
|
|
125
|
+
const fg = resolveColor(style.color, false)
|
|
126
|
+
const bg = resolveColor(style.bg, true)
|
|
74
127
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
style.bg in BG && codes.push(b <= 107 ? b : `48;5;${b}`)
|
|
128
|
+
if (fg) codes.push(fg)
|
|
129
|
+
if (bg) codes.push(bg)
|
|
78
130
|
|
|
79
131
|
return codes.length
|
|
80
132
|
? `\x1b[${codes.join(';')}m${text}\x1b[0m`
|
package/src/Logger.js
CHANGED
|
@@ -7,23 +7,22 @@ import { ProgressManager } from './Progress/ProgressManager.js'
|
|
|
7
7
|
|
|
8
8
|
export class Logger {
|
|
9
9
|
constructor(opts = {}) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const { level, color, truncate, maxLength, file, progress } = opts
|
|
11
|
+
const time = opts.time === true ? {} : (opts.time || {})
|
|
12
|
+
|
|
13
|
+
this.level = normalizeLevel(level)
|
|
14
|
+
this.color = !!color
|
|
12
15
|
this.tty = process.stdout.isTTY && !process.env.CI
|
|
13
16
|
|
|
14
|
-
this.time = !!
|
|
15
|
-
this.timeLocale =
|
|
16
|
-
this.
|
|
17
|
+
this.time = !!time
|
|
18
|
+
this.timeLocale = time?.locale
|
|
19
|
+
this.template = time?.template
|
|
20
|
+
this.timePos = time?.position || 'prefix'
|
|
17
21
|
|
|
18
|
-
this.stringify = createStringifier(
|
|
19
|
-
|
|
20
|
-
)
|
|
22
|
+
this.stringify = createStringifier( truncate || { maxLength } )
|
|
23
|
+
this.file = new FileSink(file || {})
|
|
21
24
|
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
this.progress = opts.progress
|
|
25
|
-
? new ProgressManager(opts.progress.slots || [], opts.progress.theme)
|
|
26
|
-
: null
|
|
25
|
+
this.progress = progress ? new ProgressManager(progress.slots || [], progress.theme) : null
|
|
27
26
|
|
|
28
27
|
this.lastProgressLines = 0
|
|
29
28
|
}
|
|
@@ -39,13 +38,13 @@ export class Logger {
|
|
|
39
38
|
|
|
40
39
|
/* ================= PROGRESS API ================= */
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
update(name, cur, total, text) {
|
|
43
42
|
if (!this.progress) return
|
|
44
43
|
this.progress.update(name, cur, total, text)
|
|
45
44
|
this.renderProgress()
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
remove(name) {
|
|
49
48
|
if (!this.progress) return
|
|
50
49
|
this.progress.remove(name)
|
|
51
50
|
this.renderProgress()
|
|
@@ -87,7 +86,7 @@ export class Logger {
|
|
|
87
86
|
if (!this.allow(type)) return
|
|
88
87
|
|
|
89
88
|
const msg = args.map(this.stringify.toStr).join(' ')
|
|
90
|
-
const t = this.time ? formatTime(this.timeLocale) : null
|
|
89
|
+
const t = this.time ? formatTime({locale: this.timeLocale, template: this.template}) : null
|
|
91
90
|
|
|
92
91
|
const out =
|
|
93
92
|
t && this.timePos === 'suffix'
|
package/src/Time.js
CHANGED
|
@@ -9,9 +9,36 @@ const LOCALES = {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
export function formatTime({
|
|
14
|
+
locale = 'id',
|
|
15
|
+
date = new Date(),
|
|
16
|
+
template ='[{DAY}|{DD}.{MON}|{TIME}]'
|
|
17
|
+
} = {}) {
|
|
13
18
|
const l = LOCALES[locale] || LOCALES.id
|
|
14
19
|
const d = date
|
|
20
|
+
|
|
21
|
+
const pad = n => String(n).padStart(2, '0')
|
|
22
|
+
|
|
23
|
+
const map = {
|
|
24
|
+
iso: d.toISOString(),
|
|
25
|
+
|
|
26
|
+
HH: pad(d.getHours()),
|
|
27
|
+
mm: pad(d.getMinutes()),
|
|
28
|
+
ss: pad(d.getSeconds()),
|
|
29
|
+
ms: d.getMilliseconds(),
|
|
30
|
+
|
|
31
|
+
TIME: d.toTimeString().split(' ')[0],
|
|
32
|
+
|
|
33
|
+
YYYY: d.getFullYear(),
|
|
34
|
+
MM: pad(d.getMonth() + 1),
|
|
35
|
+
DD: pad(d.getDate()),
|
|
36
|
+
|
|
37
|
+
// locale-based tokens
|
|
38
|
+
DAY: l.days[d.getDay()],
|
|
39
|
+
MON: l.mons[d.getMonth()]
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
|
|
16
|
-
return
|
|
43
|
+
return template.replace(/\{(\w+)\}/g, (_, k) => map[k] ?? '')
|
|
17
44
|
}
|