@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renpwn/simplelog",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Lightweight, safe, audit-friendly logger for CLI tools and long-running Node.js processes",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
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
- // foreground
72
- const c = COLORS[style.color]
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
- // background
76
- const b = BG[style.bg]
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
- this.level = normalizeLevel(opts.level)
11
- this.color = !!opts.color
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 = !!opts.time
15
- this.timeLocale = opts.time?.locale || 'id'
16
- this.timePos = opts.time?.position || 'prefix'
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
- opts.truncate || { maxLength: opts.maxLength }
20
- )
22
+ this.stringify = createStringifier( truncate || { maxLength } )
23
+ this.file = new FileSink(file || {})
21
24
 
22
- this.file = new FileSink(opts.file || {})
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
- updateProgress(name, cur, total, text) {
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
- removeProgress(name) {
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
- export function formatTime(locale = 'id', date = new Date()) {
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 `${l.days[d.getDay()]}|${String(d.getDate()).padStart(2,'0')}.${l.mons[d.getMonth()]}|${d.toTimeString().split(' ')[0]}`
43
+ return template.replace(/\{(\w+)\}/g, (_, k) => map[k] ?? '')
17
44
  }