@mmutils/logger 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +1038 -2
- package/dist/cjs/index.cjs +72 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/levels.cjs +82 -0
- package/dist/cjs/levels.cjs.map +1 -0
- package/dist/cjs/logger.cjs +576 -0
- package/dist/cjs/logger.cjs.map +1 -0
- package/dist/cjs/outputs.cjs +24 -0
- package/dist/cjs/outputs.cjs.map +1 -0
- package/dist/cjs/plugins/columns.cjs +782 -0
- package/dist/cjs/plugins/columns.cjs.map +1 -0
- package/dist/cjs/plugins/graph.cjs +578 -0
- package/dist/cjs/plugins/graph.cjs.map +1 -0
- package/dist/cjs/plugins/json.cjs +644 -0
- package/dist/cjs/plugins/json.cjs.map +1 -0
- package/dist/cjs/plugins/message.cjs +108 -0
- package/dist/cjs/plugins/message.cjs.map +1 -0
- package/dist/cjs/plugins/shared.cjs +194 -0
- package/dist/cjs/plugins/shared.cjs.map +1 -0
- package/dist/cjs/time.cjs +234 -0
- package/dist/cjs/time.cjs.map +1 -0
- package/dist/cjs/utils/text.cjs +646 -0
- package/dist/cjs/utils/text.cjs.map +1 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/levels.js +72 -0
- package/dist/esm/levels.js.map +1 -0
- package/dist/esm/logger.js +572 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/outputs.js +21 -0
- package/dist/esm/outputs.js.map +1 -0
- package/dist/esm/plugins/columns.js +778 -0
- package/dist/esm/plugins/columns.js.map +1 -0
- package/dist/esm/plugins/graph.js +572 -0
- package/dist/esm/plugins/graph.js.map +1 -0
- package/dist/esm/plugins/json.js +641 -0
- package/dist/esm/plugins/json.js.map +1 -0
- package/dist/esm/plugins/message.js +105 -0
- package/dist/esm/plugins/message.js.map +1 -0
- package/dist/esm/plugins/shared.js +187 -0
- package/dist/esm/plugins/shared.js.map +1 -0
- package/dist/esm/time.js +231 -0
- package/dist/esm/time.js.map +1 -0
- package/dist/esm/utils/text.js +621 -0
- package/dist/esm/utils/text.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/levels.d.ts +50 -0
- package/dist/types/logger.d.ts +245 -0
- package/dist/types/outputs.d.ts +12 -0
- package/dist/types/plugins/columns.d.ts +151 -0
- package/dist/types/plugins/graph.d.ts +243 -0
- package/dist/types/plugins/json.d.ts +169 -0
- package/dist/types/plugins/message.d.ts +181 -0
- package/dist/types/plugins/shared.d.ts +74 -0
- package/dist/types/time.d.ts +48 -0
- package/dist/types/types.d.ts +176 -0
- package/dist/types/utils/text.d.ts +231 -0
- package/package.json +85 -4
package/dist/esm/time.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { formatHighResTime } from '@mmutils/time';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default timestamp pattern, local time: `2026/03/16 15:35:00`. Change the
|
|
5
|
+
* default here; see `compileTimestamp` for the tokens.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_TIMESTAMP_FORMAT = 'YYYY/MM/DD HH:mm:ss';
|
|
8
|
+
const MONTHS = [
|
|
9
|
+
'January',
|
|
10
|
+
'February',
|
|
11
|
+
'March',
|
|
12
|
+
'April',
|
|
13
|
+
'May',
|
|
14
|
+
'June',
|
|
15
|
+
'July',
|
|
16
|
+
'August',
|
|
17
|
+
'September',
|
|
18
|
+
'October',
|
|
19
|
+
'November',
|
|
20
|
+
'December',
|
|
21
|
+
];
|
|
22
|
+
const WEEKDAYS = [
|
|
23
|
+
'Sunday',
|
|
24
|
+
'Monday',
|
|
25
|
+
'Tuesday',
|
|
26
|
+
'Wednesday',
|
|
27
|
+
'Thursday',
|
|
28
|
+
'Friday',
|
|
29
|
+
'Saturday',
|
|
30
|
+
];
|
|
31
|
+
const pad2 = (value) => value < 10 ? `0${String(value)}` : String(value);
|
|
32
|
+
const pad3 = (value) => value < 10 ? `00${String(value)}`
|
|
33
|
+
: value < 100 ? `0${String(value)}`
|
|
34
|
+
: String(value);
|
|
35
|
+
const pad6 = (value) => String(value).padStart(6, '0');
|
|
36
|
+
const readSecond = (date, utc) => utc ?
|
|
37
|
+
{
|
|
38
|
+
year: date.getUTCFullYear(),
|
|
39
|
+
month: date.getUTCMonth(),
|
|
40
|
+
day: date.getUTCDate(),
|
|
41
|
+
weekday: date.getUTCDay(),
|
|
42
|
+
hour: date.getUTCHours(),
|
|
43
|
+
minute: date.getUTCMinutes(),
|
|
44
|
+
second: date.getUTCSeconds(),
|
|
45
|
+
offset: 0,
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
year: date.getFullYear(),
|
|
49
|
+
month: date.getMonth(),
|
|
50
|
+
day: date.getDate(),
|
|
51
|
+
weekday: date.getDay(),
|
|
52
|
+
hour: date.getHours(),
|
|
53
|
+
minute: date.getMinutes(),
|
|
54
|
+
second: date.getSeconds(),
|
|
55
|
+
offset: -date.getTimezoneOffset(),
|
|
56
|
+
};
|
|
57
|
+
const formatOffset = (offset, separator) => {
|
|
58
|
+
const sign = offset < 0 ? '-' : '+';
|
|
59
|
+
const minutes = Math.abs(offset);
|
|
60
|
+
return `${sign}${pad2(Math.floor(minutes / 60))}${separator}${pad2(minutes % 60)}`;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Tokens that change at most once per second: rendered once per second and
|
|
64
|
+
* cached.
|
|
65
|
+
*/
|
|
66
|
+
const SECOND_TOKENS = {
|
|
67
|
+
// years outside 0-9999 like ISO 8601 (toISOString): -000001, +010000
|
|
68
|
+
YYYY: ({ year }) => year >= 0 && year <= 9999 ?
|
|
69
|
+
String(year).padStart(4, '0')
|
|
70
|
+
: `${year < 0 ? '-' : '+'}${String(Math.abs(year)).padStart(6, '0')}`,
|
|
71
|
+
YY: ({ year }) => pad2(Math.abs(year) % 100),
|
|
72
|
+
MMMM: ({ month }) => MONTHS[month] ?? '',
|
|
73
|
+
MMM: ({ month }) => (MONTHS[month] ?? '').slice(0, 3),
|
|
74
|
+
MM: ({ month }) => pad2(month + 1),
|
|
75
|
+
M: ({ month }) => String(month + 1),
|
|
76
|
+
DD: ({ day }) => pad2(day),
|
|
77
|
+
D: ({ day }) => String(day),
|
|
78
|
+
dddd: ({ weekday }) => WEEKDAYS[weekday] ?? '',
|
|
79
|
+
ddd: ({ weekday }) => (WEEKDAYS[weekday] ?? '').slice(0, 3),
|
|
80
|
+
HH: ({ hour }) => pad2(hour),
|
|
81
|
+
H: ({ hour }) => String(hour),
|
|
82
|
+
hh: ({ hour }) => pad2(hour % 12 || 12),
|
|
83
|
+
h: ({ hour }) => String(hour % 12 || 12),
|
|
84
|
+
A: ({ hour }) => (hour < 12 ? 'AM' : 'PM'),
|
|
85
|
+
a: ({ hour }) => (hour < 12 ? 'am' : 'pm'),
|
|
86
|
+
mm: ({ minute }) => pad2(minute),
|
|
87
|
+
m: ({ minute }) => String(minute),
|
|
88
|
+
ss: ({ second }) => pad2(second),
|
|
89
|
+
s: ({ second }) => String(second),
|
|
90
|
+
ZZ: ({ offset }) => formatOffset(offset, ''),
|
|
91
|
+
Z: ({ offset }) => formatOffset(offset, ':'),
|
|
92
|
+
};
|
|
93
|
+
const LOG_TOKENS = {
|
|
94
|
+
SSSSSSSSS: (ms, timeNs) => `${pad3(ms)}${pad6(timeNs)}`,
|
|
95
|
+
SSSSSS: (ms, timeNs) => `${pad3(ms)}${pad3(Math.floor(timeNs / 1000))}`,
|
|
96
|
+
SSS: (ms) => pad3(ms),
|
|
97
|
+
EEE: (_ms, _timeNs, elapsedNs) => `+${(elapsedNs / 1e6).toFixed(6)}ms`,
|
|
98
|
+
E: (_ms, _timeNs, elapsedNs) => `+${formatHighResTime(Math.max(0, elapsedNs), 2, 'ns')}`,
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Tokens, longest first, or `[literal]` text.
|
|
102
|
+
*/
|
|
103
|
+
const TOKEN = /\[([^\]]*)]|YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|A|a|mm|m|SSSSSSSSS|SSSSSS|SSS|ss|s|ZZ|Z|EEE|E/g;
|
|
104
|
+
const parse = (pattern) => {
|
|
105
|
+
const parts = [];
|
|
106
|
+
let index = 0;
|
|
107
|
+
for (const match of pattern.matchAll(TOKEN)) {
|
|
108
|
+
if (match.index > index)
|
|
109
|
+
parts.push({ kind: 'text', text: pattern.slice(index, match.index) });
|
|
110
|
+
const [token, literal] = match;
|
|
111
|
+
const second = SECOND_TOKENS[token];
|
|
112
|
+
const log = LOG_TOKENS[token];
|
|
113
|
+
parts.push(literal !== undefined ? { kind: 'text', text: literal }
|
|
114
|
+
: second !== undefined ? { kind: 'second', render: second }
|
|
115
|
+
// every other match is a log token
|
|
116
|
+
: {
|
|
117
|
+
kind: 'log',
|
|
118
|
+
render: log,
|
|
119
|
+
elapsed: token === 'E' || token === 'EEE',
|
|
120
|
+
});
|
|
121
|
+
index = match.index + token.length;
|
|
122
|
+
}
|
|
123
|
+
if (index < pattern.length)
|
|
124
|
+
parts.push({ kind: 'text', text: pattern.slice(index) });
|
|
125
|
+
return parts;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Parsed patterns, shared: parsing costs about a microsecond per logger,
|
|
129
|
+
* and loggers usually share a few patterns. Parts are immutable.
|
|
130
|
+
*/
|
|
131
|
+
const parsedPatterns = new Map();
|
|
132
|
+
const parseCached = (pattern) => {
|
|
133
|
+
let parts = parsedPatterns.get(pattern);
|
|
134
|
+
if (parts === undefined) {
|
|
135
|
+
parts = parse(pattern);
|
|
136
|
+
parsedPatterns.set(pattern, parts);
|
|
137
|
+
}
|
|
138
|
+
return parts;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Compiles a timestamp pattern once, dayjs-style:
|
|
142
|
+
*
|
|
143
|
+
* | Token | Output |
|
|
144
|
+
* | --- | --- |
|
|
145
|
+
* | `YYYY` `YY` | `2026` `26` |
|
|
146
|
+
* | `MMMM` `MMM` `MM` `M` | `March` `Mar` `03` `3` |
|
|
147
|
+
* | `DD` `D` | `16` `16` |
|
|
148
|
+
* | `dddd` `ddd` | `Monday` `Mon` |
|
|
149
|
+
* | `HH` `H` | `15` `15` (24 hours) |
|
|
150
|
+
* | `hh` `h` `A` `a` | `03` `3` `PM` `pm` (12 hours) |
|
|
151
|
+
* | `mm` `m`, `ss` `s` | minutes, seconds |
|
|
152
|
+
* | `SSS` `SSSSSS` `SSSSSSSSS` | milliseconds, microseconds, nanoseconds |
|
|
153
|
+
* | `Z` `ZZ` | `+02:00` `+0200` |
|
|
154
|
+
* | `E` | elapsed since the start: `+1s 32ms 400µs` |
|
|
155
|
+
* | `EEE` | elapsed in milliseconds: `+1032.400123ms` |
|
|
156
|
+
* | `[text]` | `text` as is |
|
|
157
|
+
*
|
|
158
|
+
* Other characters are written as is. Text that changes at most once per
|
|
159
|
+
* second is rendered with `Date` getters and cached, so a log only pays a
|
|
160
|
+
* comparison plus its fraction and elapsed tokens.
|
|
161
|
+
*
|
|
162
|
+
* @throws RangeError when formatting a time that is not a finite number.
|
|
163
|
+
*/
|
|
164
|
+
const compileTimestamp = (pattern, { utc = false, start } = {}) => {
|
|
165
|
+
const parts = parseCached(pattern);
|
|
166
|
+
// cached text between the per-log tokens: chunks.length === tokens.length + 1
|
|
167
|
+
const chunkParts = [[]];
|
|
168
|
+
const tokens = [];
|
|
169
|
+
let needsElapsed = false;
|
|
170
|
+
for (const part of parts)
|
|
171
|
+
if (part.kind === 'log') {
|
|
172
|
+
tokens.push(part.render);
|
|
173
|
+
needsElapsed ||= part.elapsed;
|
|
174
|
+
chunkParts.push([]);
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
chunkParts[chunkParts.length - 1]?.push(part);
|
|
178
|
+
const [startTime, startNs] = start ?? [Date.now(), 0];
|
|
179
|
+
const chunks = chunkParts.map(() => '');
|
|
180
|
+
// the first two chunks, for the common patterns' fast paths
|
|
181
|
+
let first = '';
|
|
182
|
+
let second = '';
|
|
183
|
+
let cachedSecond = NaN;
|
|
184
|
+
const renderSecond = (time, epochSecond) => {
|
|
185
|
+
// NaN and Infinity never match the cache
|
|
186
|
+
if (!Number.isFinite(time))
|
|
187
|
+
throw new RangeError('Invalid time value');
|
|
188
|
+
const values = readSecond(new Date(epochSecond * 1000), utc);
|
|
189
|
+
for (const [index, chunk] of chunkParts.entries()) {
|
|
190
|
+
let text = '';
|
|
191
|
+
for (const part of chunk)
|
|
192
|
+
text += part.kind === 'text' ? part.text : part.render(values);
|
|
193
|
+
chunks[index] = text;
|
|
194
|
+
}
|
|
195
|
+
first = chunks[0] ?? '';
|
|
196
|
+
second = chunks[1] ?? '';
|
|
197
|
+
cachedSecond = epochSecond;
|
|
198
|
+
};
|
|
199
|
+
const elapsed = (time, timeNs) => needsElapsed ? (time - startTime) * 1e6 + (timeNs - startNs) : 0;
|
|
200
|
+
// no per-log tokens, e.g. the default pattern: the cached text
|
|
201
|
+
if (!tokens.length)
|
|
202
|
+
return (time) => {
|
|
203
|
+
const epochSecond = Math.floor(time / 1000);
|
|
204
|
+
if (epochSecond !== cachedSecond)
|
|
205
|
+
renderSecond(time, epochSecond);
|
|
206
|
+
return first;
|
|
207
|
+
};
|
|
208
|
+
// one per-log token, e.g. milliseconds: no loop
|
|
209
|
+
const [token] = tokens;
|
|
210
|
+
if (tokens.length === 1)
|
|
211
|
+
return (time, timeNs) => {
|
|
212
|
+
const epochSecond = Math.floor(time / 1000);
|
|
213
|
+
if (epochSecond !== cachedSecond)
|
|
214
|
+
renderSecond(time, epochSecond);
|
|
215
|
+
return `${first}${token(time - epochSecond * 1000, timeNs, elapsed(time, timeNs))}${second}`;
|
|
216
|
+
};
|
|
217
|
+
return (time, timeNs) => {
|
|
218
|
+
const epochSecond = Math.floor(time / 1000);
|
|
219
|
+
if (epochSecond !== cachedSecond)
|
|
220
|
+
renderSecond(time, epochSecond);
|
|
221
|
+
const ms = time - epochSecond * 1000;
|
|
222
|
+
const elapsedNs = elapsed(time, timeNs);
|
|
223
|
+
let text = first;
|
|
224
|
+
for (let index = 0; index < tokens.length; index++)
|
|
225
|
+
text += `${tokens[index](ms, timeNs, elapsedNs)}${chunks[index + 1] ?? ''}`;
|
|
226
|
+
return text;
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export { DEFAULT_TIMESTAMP_FORMAT, compileTimestamp };
|
|
231
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sources":["../../src/time.ts"],"sourcesContent":["import { formatHighResTime } from '@mmutils/time';\n\n/**\n * Default timestamp pattern, local time: `2026/03/16 15:35:00`. Change the\n * default here; see `compileTimestamp` for the tokens.\n */\nexport const DEFAULT_TIMESTAMP_FORMAT = 'YYYY/MM/DD HH:mm:ss';\n\n/**\n * Formats the time of a log.\n *\n * @param time Epoch time in whole milliseconds.\n * @param timeNs Nanoseconds within the millisecond (0-999,999); `0` unless\n * the logger's clock is `'precise'`.\n */\nexport type TimestampFormatter = (time: number, timeNs: number) => string;\n\nexport interface CompileTimestampOptions {\n /** Format the time in UTC instead of local time. Default: `false`. */\n readonly utc?: boolean;\n /**\n * Start of the elapsed time tokens (`E`, `EEE`), as epoch milliseconds and\n * nanoseconds within the millisecond. Default: when the pattern is\n * compiled, from `Date.now()`.\n */\n readonly start?: readonly [time: number, timeNs: number];\n}\n\nconst MONTHS = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n] as const;\n\nconst WEEKDAYS = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n] as const;\n\nconst pad2 = (value: number): string =>\n value < 10 ? `0${String(value)}` : String(value);\n\nconst pad3 = (value: number): string =>\n value < 10 ? `00${String(value)}`\n : value < 100 ? `0${String(value)}`\n : String(value);\n\nconst pad6 = (value: number): string => String(value).padStart(6, '0');\n\n/**\n * Date and time parts of one second, read once per second with `Date`\n * getters (local time) or their UTC versions.\n */\ninterface SecondParts {\n readonly year: number;\n readonly month: number;\n readonly day: number;\n readonly weekday: number;\n readonly hour: number;\n readonly minute: number;\n readonly second: number;\n /** Minutes east of UTC. */\n readonly offset: number;\n}\n\nconst readSecond = (date: Date, utc: boolean): SecondParts =>\n utc ?\n {\n year: date.getUTCFullYear(),\n month: date.getUTCMonth(),\n day: date.getUTCDate(),\n weekday: date.getUTCDay(),\n hour: date.getUTCHours(),\n minute: date.getUTCMinutes(),\n second: date.getUTCSeconds(),\n offset: 0,\n }\n : {\n year: date.getFullYear(),\n month: date.getMonth(),\n day: date.getDate(),\n weekday: date.getDay(),\n hour: date.getHours(),\n minute: date.getMinutes(),\n second: date.getSeconds(),\n offset: -date.getTimezoneOffset(),\n };\n\nconst formatOffset = (offset: number, separator: string): string => {\n const sign = offset < 0 ? '-' : '+';\n const minutes = Math.abs(offset);\n return `${sign}${pad2(Math.floor(minutes / 60))}${separator}${pad2(minutes % 60)}`;\n};\n\n/**\n * Tokens that change at most once per second: rendered once per second and\n * cached.\n */\nconst SECOND_TOKENS: Readonly<Record<string, (parts: SecondParts) => string>> =\n {\n // years outside 0-9999 like ISO 8601 (toISOString): -000001, +010000\n YYYY: ({ year }) =>\n year >= 0 && year <= 9999 ?\n String(year).padStart(4, '0')\n : `${year < 0 ? '-' : '+'}${String(Math.abs(year)).padStart(6, '0')}`,\n YY: ({ year }) => pad2(Math.abs(year) % 100),\n MMMM: ({ month }) => MONTHS[month] ?? '',\n MMM: ({ month }) => (MONTHS[month] ?? '').slice(0, 3),\n MM: ({ month }) => pad2(month + 1),\n M: ({ month }) => String(month + 1),\n DD: ({ day }) => pad2(day),\n D: ({ day }) => String(day),\n dddd: ({ weekday }) => WEEKDAYS[weekday] ?? '',\n ddd: ({ weekday }) => (WEEKDAYS[weekday] ?? '').slice(0, 3),\n HH: ({ hour }) => pad2(hour),\n H: ({ hour }) => String(hour),\n hh: ({ hour }) => pad2(hour % 12 || 12),\n h: ({ hour }) => String(hour % 12 || 12),\n A: ({ hour }) => (hour < 12 ? 'AM' : 'PM'),\n a: ({ hour }) => (hour < 12 ? 'am' : 'pm'),\n mm: ({ minute }) => pad2(minute),\n m: ({ minute }) => String(minute),\n ss: ({ second }) => pad2(second),\n s: ({ second }) => String(second),\n ZZ: ({ offset }) => formatOffset(offset, ''),\n Z: ({ offset }) => formatOffset(offset, ':'),\n };\n\n/**\n * Tokens that change on every log: fractions of the second and elapsed\n * time, computed per log after the cached text.\n *\n * @param ms Milliseconds within the second.\n * @param timeNs Nanoseconds within the millisecond.\n * @param elapsedNs Nanoseconds since the start.\n */\ntype LogToken = (ms: number, timeNs: number, elapsedNs: number) => string;\n\nconst LOG_TOKENS: Readonly<Record<string, LogToken>> = {\n SSSSSSSSS: (ms, timeNs) => `${pad3(ms)}${pad6(timeNs)}`,\n SSSSSS: (ms, timeNs) => `${pad3(ms)}${pad3(Math.floor(timeNs / 1000))}`,\n SSS: (ms) => pad3(ms),\n EEE: (_ms, _timeNs, elapsedNs) => `+${(elapsedNs / 1e6).toFixed(6)}ms`,\n E: (_ms, _timeNs, elapsedNs) =>\n `+${formatHighResTime(Math.max(0, elapsedNs), 2, 'ns')}`,\n};\n\n/**\n * Tokens, longest first, or `[literal]` text.\n */\nconst TOKEN =\n /\\[([^\\]]*)]|YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|A|a|mm|m|SSSSSSSSS|SSSSSS|SSS|ss|s|ZZ|Z|EEE|E/g;\n\ntype Part =\n | { readonly kind: 'text'; readonly text: string }\n | { readonly kind: 'second'; readonly render: (parts: SecondParts) => string }\n | {\n readonly kind: 'log';\n readonly render: LogToken;\n /** Needs the elapsed time, which is computed only for such tokens. */\n readonly elapsed: boolean;\n };\n\n/**\n * Parts of the cached text: everything but the per-log tokens.\n */\ntype CachedPart = Exclude<Part, { readonly kind: 'log' }>;\n\nconst parse = (pattern: string): Part[] => {\n const parts: Part[] = [];\n let index = 0;\n for (const match of pattern.matchAll(TOKEN)) {\n if (match.index > index)\n parts.push({ kind: 'text', text: pattern.slice(index, match.index) });\n const [token, literal] = match;\n const second = SECOND_TOKENS[token];\n const log = LOG_TOKENS[token];\n parts.push(\n literal !== undefined ? { kind: 'text', text: literal }\n : second !== undefined ? { kind: 'second', render: second }\n // every other match is a log token\n : {\n kind: 'log',\n render: log as LogToken,\n elapsed: token === 'E' || token === 'EEE',\n }\n );\n index = match.index + token.length;\n }\n if (index < pattern.length)\n parts.push({ kind: 'text', text: pattern.slice(index) });\n return parts;\n};\n\n/**\n * Parsed patterns, shared: parsing costs about a microsecond per logger,\n * and loggers usually share a few patterns. Parts are immutable.\n */\nconst parsedPatterns = new Map<string, readonly Part[]>();\n\nconst parseCached = (pattern: string): readonly Part[] => {\n let parts = parsedPatterns.get(pattern);\n if (parts === undefined) {\n parts = parse(pattern);\n parsedPatterns.set(pattern, parts);\n }\n return parts;\n};\n\n/**\n * Compiles a timestamp pattern once, dayjs-style:\n *\n * | Token | Output |\n * | --- | --- |\n * | `YYYY` `YY` | `2026` `26` |\n * | `MMMM` `MMM` `MM` `M` | `March` `Mar` `03` `3` |\n * | `DD` `D` | `16` `16` |\n * | `dddd` `ddd` | `Monday` `Mon` |\n * | `HH` `H` | `15` `15` (24 hours) |\n * | `hh` `h` `A` `a` | `03` `3` `PM` `pm` (12 hours) |\n * | `mm` `m`, `ss` `s` | minutes, seconds |\n * | `SSS` `SSSSSS` `SSSSSSSSS` | milliseconds, microseconds, nanoseconds |\n * | `Z` `ZZ` | `+02:00` `+0200` |\n * | `E` | elapsed since the start: `+1s 32ms 400µs` |\n * | `EEE` | elapsed in milliseconds: `+1032.400123ms` |\n * | `[text]` | `text` as is |\n *\n * Other characters are written as is. Text that changes at most once per\n * second is rendered with `Date` getters and cached, so a log only pays a\n * comparison plus its fraction and elapsed tokens.\n *\n * @throws RangeError when formatting a time that is not a finite number.\n */\nexport const compileTimestamp = (\n pattern: string,\n { utc = false, start }: CompileTimestampOptions = {}\n): TimestampFormatter => {\n const parts = parseCached(pattern);\n // cached text between the per-log tokens: chunks.length === tokens.length + 1\n const chunkParts: CachedPart[][] = [[]];\n const tokens: LogToken[] = [];\n let needsElapsed = false;\n for (const part of parts)\n if (part.kind === 'log') {\n tokens.push(part.render);\n needsElapsed ||= part.elapsed;\n chunkParts.push([]);\n } else chunkParts[chunkParts.length - 1]?.push(part);\n\n const [startTime, startNs] = start ?? [Date.now(), 0];\n const chunks: string[] = chunkParts.map(() => '');\n // the first two chunks, for the common patterns' fast paths\n let first = '';\n let second = '';\n let cachedSecond = NaN;\n const renderSecond = (time: number, epochSecond: number): void => {\n // NaN and Infinity never match the cache\n if (!Number.isFinite(time)) throw new RangeError('Invalid time value');\n const values = readSecond(new Date(epochSecond * 1000), utc);\n for (const [index, chunk] of chunkParts.entries()) {\n let text = '';\n for (const part of chunk)\n text += part.kind === 'text' ? part.text : part.render(values);\n chunks[index] = text;\n }\n first = chunks[0] ?? '';\n second = chunks[1] ?? '';\n cachedSecond = epochSecond;\n };\n const elapsed = (time: number, timeNs: number): number =>\n needsElapsed ? (time - startTime) * 1e6 + (timeNs - startNs) : 0;\n\n // no per-log tokens, e.g. the default pattern: the cached text\n if (!tokens.length)\n return (time) => {\n const epochSecond = Math.floor(time / 1000);\n if (epochSecond !== cachedSecond) renderSecond(time, epochSecond);\n return first;\n };\n\n // one per-log token, e.g. milliseconds: no loop\n const [token] = tokens as [LogToken];\n if (tokens.length === 1)\n return (time, timeNs) => {\n const epochSecond = Math.floor(time / 1000);\n if (epochSecond !== cachedSecond) renderSecond(time, epochSecond);\n return `${first}${token(time - epochSecond * 1000, timeNs, elapsed(time, timeNs))}${second}`;\n };\n\n return (time, timeNs) => {\n const epochSecond = Math.floor(time / 1000);\n if (epochSecond !== cachedSecond) renderSecond(time, epochSecond);\n const ms = time - epochSecond * 1000;\n const elapsedNs = elapsed(time, timeNs);\n let text = first;\n for (let index = 0; index < tokens.length; index++)\n text += `${(tokens[index] as LogToken)(ms, timeNs, elapsedNs)}${chunks[index + 1] ?? ''}`;\n return text;\n };\n};\n"],"names":[],"mappings":"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"}
|