flexily 0.5.2 → 0.6.0
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/dist/chunk-CBBoxR_p.mjs +26 -0
- package/dist/constants-BNURa6H7.mjs +65 -0
- package/dist/constants-BNURa6H7.mjs.map +1 -0
- package/dist/constants-D7ythAJC.d.mts +64 -0
- package/dist/constants-D7ythAJC.d.mts.map +1 -0
- package/{src/classic/node.ts → dist/index-classic.d.mts} +118 -619
- package/dist/index-classic.d.mts.map +1 -0
- package/dist/index-classic.mjs +1909 -0
- package/dist/index-classic.mjs.map +1 -0
- package/dist/index.d.mts +195 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3279 -0
- package/dist/index.mjs.map +1 -0
- package/dist/node-zero-75maLs2s.d.mts +762 -0
- package/dist/node-zero-75maLs2s.d.mts.map +1 -0
- package/dist/src-BWyhokNZ.mjs +692 -0
- package/dist/src-BWyhokNZ.mjs.map +1 -0
- package/dist/src-DdSLylRA.mjs +816 -0
- package/dist/src-DdSLylRA.mjs.map +1 -0
- package/dist/testing.d.mts +55 -0
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +154 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types--IozHd4V.mjs +283 -0
- package/dist/types--IozHd4V.mjs.map +1 -0
- package/dist/types-DG1H4DVR.d.mts +157 -0
- package/dist/types-DG1H4DVR.d.mts.map +1 -0
- package/package.json +33 -24
- package/src/CLAUDE.md +0 -527
- package/src/classic/layout.ts +0 -1843
- package/src/constants.ts +0 -82
- package/src/create-flexily.ts +0 -153
- package/src/index-classic.ts +0 -110
- package/src/index.ts +0 -133
- package/src/layout-flex-lines.ts +0 -413
- package/src/layout-helpers.ts +0 -160
- package/src/layout-measure.ts +0 -259
- package/src/layout-stats.ts +0 -41
- package/src/layout-traversal.ts +0 -70
- package/src/layout-zero.ts +0 -2219
- package/src/logger.ts +0 -68
- package/src/monospace-measurer.ts +0 -68
- package/src/node-zero.ts +0 -1508
- package/src/pretext-measurer.ts +0 -86
- package/src/test-measurer.ts +0 -219
- package/src/testing.ts +0 -215
- package/src/text-layout.ts +0 -75
- package/src/trace.ts +0 -252
- package/src/types.ts +0 -236
- package/src/utils.ts +0 -243
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-zero-75maLs2s.d.mts","names":[],"sources":["../src/node-zero.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import { closeSync, openSync, writeSync } from "node:fs";
|
|
2
|
+
//#region ../loggily/src/colors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Vendored ANSI color functions — replaces picocolors dependency.
|
|
5
|
+
* Supports NO_COLOR, FORCE_COLOR, and TTY detection.
|
|
6
|
+
*/
|
|
7
|
+
const _process$1 = typeof process !== "undefined" ? process : void 0;
|
|
8
|
+
const enabled = _process$1?.env?.["FORCE_COLOR"] !== void 0 && _process$1?.env?.["FORCE_COLOR"] !== "0" ? true : _process$1?.env?.["NO_COLOR"] !== void 0 ? false : _process$1?.stdout?.isTTY ?? false;
|
|
9
|
+
function wrap(open, close) {
|
|
10
|
+
if (!enabled) return (str) => str;
|
|
11
|
+
return (str) => open + str + close;
|
|
12
|
+
}
|
|
13
|
+
const colors = {
|
|
14
|
+
dim: wrap("\x1B[2m", "\x1B[22m"),
|
|
15
|
+
blue: wrap("\x1B[34m", "\x1B[39m"),
|
|
16
|
+
yellow: wrap("\x1B[33m", "\x1B[39m"),
|
|
17
|
+
red: wrap("\x1B[31m", "\x1B[39m"),
|
|
18
|
+
magenta: wrap("\x1B[35m", "\x1B[39m"),
|
|
19
|
+
cyan: wrap("\x1B[36m", "\x1B[39m")
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region ../loggily/src/file-writer.ts
|
|
23
|
+
/**
|
|
24
|
+
* File writer for loggily — Node.js/Bun only.
|
|
25
|
+
*
|
|
26
|
+
* Separated from core logger to allow tree-shaking in browser bundles.
|
|
27
|
+
* Uses dynamic import("node:fs") to avoid static dependency on Node APIs.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Create an async buffered file writer for log output.
|
|
31
|
+
* Buffers writes and flushes on size threshold or interval.
|
|
32
|
+
* Registers a process.on('exit') handler to flush remaining buffer.
|
|
33
|
+
*
|
|
34
|
+
* **Node.js/Bun only** — not available in browser environments.
|
|
35
|
+
*
|
|
36
|
+
* @param filePath - Path to the log file (opened in append mode)
|
|
37
|
+
* @param options - Buffer size and flush interval configuration
|
|
38
|
+
* @returns FileWriter with write, flush, and close methods
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* const writer = createFileWriter('/tmp/app.log')
|
|
42
|
+
* const unsubscribe = addWriter((formatted) => writer.write(formatted))
|
|
43
|
+
*
|
|
44
|
+
* // On shutdown:
|
|
45
|
+
* unsubscribe()
|
|
46
|
+
* writer.close()
|
|
47
|
+
*/
|
|
48
|
+
function createFileWriter(filePath, options = {}) {
|
|
49
|
+
const bufferSize = options.bufferSize ?? 4096;
|
|
50
|
+
const flushInterval = options.flushInterval ?? 100;
|
|
51
|
+
let buffer = "";
|
|
52
|
+
let fd = null;
|
|
53
|
+
let timer = null;
|
|
54
|
+
let closed = false;
|
|
55
|
+
fd = openSync(filePath, "a");
|
|
56
|
+
/** Flush buffer contents to disk synchronously */
|
|
57
|
+
function flush() {
|
|
58
|
+
if (buffer.length === 0 || fd === null) return;
|
|
59
|
+
writeSync(fd, buffer);
|
|
60
|
+
buffer = "";
|
|
61
|
+
}
|
|
62
|
+
timer = setInterval(flush, flushInterval);
|
|
63
|
+
if (timer && typeof timer === "object" && "unref" in timer) timer.unref();
|
|
64
|
+
const exitHandler = () => flush();
|
|
65
|
+
process.on("exit", exitHandler);
|
|
66
|
+
return {
|
|
67
|
+
write(line) {
|
|
68
|
+
if (closed) return;
|
|
69
|
+
buffer += line + "\n";
|
|
70
|
+
if (buffer.length >= bufferSize) flush();
|
|
71
|
+
},
|
|
72
|
+
flush,
|
|
73
|
+
close() {
|
|
74
|
+
if (closed) return;
|
|
75
|
+
closed = true;
|
|
76
|
+
if (timer !== null) {
|
|
77
|
+
clearInterval(timer);
|
|
78
|
+
timer = null;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
flush();
|
|
82
|
+
} catch {} finally {
|
|
83
|
+
if (fd !== null) {
|
|
84
|
+
closeSync(fd);
|
|
85
|
+
fd = null;
|
|
86
|
+
}
|
|
87
|
+
process.removeListener("exit", exitHandler);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region ../loggily/src/pipeline.ts
|
|
94
|
+
const LOG_LEVEL_PRIORITY = {
|
|
95
|
+
trace: 0,
|
|
96
|
+
debug: 1,
|
|
97
|
+
info: 2,
|
|
98
|
+
warn: 3,
|
|
99
|
+
error: 4,
|
|
100
|
+
silent: 5
|
|
101
|
+
};
|
|
102
|
+
const _process = typeof process !== "undefined" ? process : void 0;
|
|
103
|
+
function getEnv(key) {
|
|
104
|
+
return _process?.env?.[key];
|
|
105
|
+
}
|
|
106
|
+
function writeStderr(text) {
|
|
107
|
+
if (_process?.stderr?.write) _process.stderr.write(text + "\n");
|
|
108
|
+
else console.error(text);
|
|
109
|
+
}
|
|
110
|
+
function safeStringify(value) {
|
|
111
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
112
|
+
return JSON.stringify(value, (_key, val) => {
|
|
113
|
+
if (typeof val === "bigint") return val.toString();
|
|
114
|
+
if (typeof val === "symbol") return val.toString();
|
|
115
|
+
if (val instanceof Error) return {
|
|
116
|
+
message: val.message,
|
|
117
|
+
stack: val.stack,
|
|
118
|
+
name: val.name
|
|
119
|
+
};
|
|
120
|
+
if (typeof val === "object" && val !== null) {
|
|
121
|
+
if (seen.has(val)) return "[Circular]";
|
|
122
|
+
seen.add(val);
|
|
123
|
+
}
|
|
124
|
+
return val;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function formatConsoleEvent(event) {
|
|
128
|
+
const time = colors.dim(new Date(event.time).toISOString().split("T")[1]?.split(".")[0] || "");
|
|
129
|
+
const ns = colors.cyan(event.namespace);
|
|
130
|
+
if (event.kind === "span") {
|
|
131
|
+
const message = `(${event.duration}ms)`;
|
|
132
|
+
let output = `${time} ${colors.magenta("SPAN")} ${ns} ${message}`;
|
|
133
|
+
if (event.props && Object.keys(event.props).length > 0) output += ` ${colors.dim(safeStringify(event.props))}`;
|
|
134
|
+
return output;
|
|
135
|
+
}
|
|
136
|
+
let levelStr;
|
|
137
|
+
switch (event.level) {
|
|
138
|
+
case "trace":
|
|
139
|
+
levelStr = colors.dim("TRACE");
|
|
140
|
+
break;
|
|
141
|
+
case "debug":
|
|
142
|
+
levelStr = colors.dim("DEBUG");
|
|
143
|
+
break;
|
|
144
|
+
case "info":
|
|
145
|
+
levelStr = colors.blue("INFO");
|
|
146
|
+
break;
|
|
147
|
+
case "warn":
|
|
148
|
+
levelStr = colors.yellow("WARN");
|
|
149
|
+
break;
|
|
150
|
+
case "error":
|
|
151
|
+
levelStr = colors.red("ERROR");
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
let output = `${time} ${levelStr} ${ns} ${event.message}`;
|
|
155
|
+
if (event.props && Object.keys(event.props).length > 0) output += ` ${colors.dim(safeStringify(event.props))}`;
|
|
156
|
+
return output;
|
|
157
|
+
}
|
|
158
|
+
function formatJSONEvent(event) {
|
|
159
|
+
if (event.kind === "span") return safeStringify({
|
|
160
|
+
time: new Date(event.time).toISOString(),
|
|
161
|
+
level: "span",
|
|
162
|
+
name: event.namespace,
|
|
163
|
+
msg: `(${event.duration}ms)`,
|
|
164
|
+
duration: event.duration,
|
|
165
|
+
span_id: event.spanId,
|
|
166
|
+
trace_id: event.traceId,
|
|
167
|
+
parent_id: event.parentId,
|
|
168
|
+
...event.props
|
|
169
|
+
});
|
|
170
|
+
return safeStringify({
|
|
171
|
+
time: new Date(event.time).toISOString(),
|
|
172
|
+
level: event.level,
|
|
173
|
+
name: event.namespace,
|
|
174
|
+
msg: event.message,
|
|
175
|
+
...event.props
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function matchesPattern(namespace, pattern) {
|
|
179
|
+
if (pattern === "*") return true;
|
|
180
|
+
return namespace === pattern || namespace.startsWith(pattern + ":");
|
|
181
|
+
}
|
|
182
|
+
function parseNsFilter(ns) {
|
|
183
|
+
const patterns = typeof ns === "string" ? ns.split(",").map((s) => s.trim()) : ns;
|
|
184
|
+
const includes = [];
|
|
185
|
+
const excludes = [];
|
|
186
|
+
for (const p of patterns) if (p.startsWith("-")) excludes.push(p.slice(1));
|
|
187
|
+
else includes.push(p);
|
|
188
|
+
return (namespace) => {
|
|
189
|
+
for (const exc of excludes) if (matchesPattern(namespace, exc)) return false;
|
|
190
|
+
if (includes.length > 0) {
|
|
191
|
+
for (const inc of includes) if (matchesPattern(namespace, inc)) return true;
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function createConsoleSink(format) {
|
|
198
|
+
const formatter = format === "json" ? formatJSONEvent : formatConsoleEvent;
|
|
199
|
+
return (event) => {
|
|
200
|
+
const text = formatter(event);
|
|
201
|
+
if (event.kind === "span") {
|
|
202
|
+
writeStderr(text);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
switch (event.level) {
|
|
206
|
+
case "trace":
|
|
207
|
+
case "debug":
|
|
208
|
+
console.debug(text);
|
|
209
|
+
break;
|
|
210
|
+
case "info":
|
|
211
|
+
console.info(text);
|
|
212
|
+
break;
|
|
213
|
+
case "warn":
|
|
214
|
+
console.warn(text);
|
|
215
|
+
break;
|
|
216
|
+
case "error":
|
|
217
|
+
console.error(text);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function createFileSink(path, format) {
|
|
223
|
+
const writer = createFileWriter(path);
|
|
224
|
+
const formatter = format === "json" ? formatJSONEvent : formatConsoleEvent;
|
|
225
|
+
return {
|
|
226
|
+
write: (event) => writer.write(formatter(event)),
|
|
227
|
+
dispose: () => writer.close()
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function createWritableSink(writable, format) {
|
|
231
|
+
const formatter = format === "json" ? formatJSONEvent : formatConsoleEvent;
|
|
232
|
+
return (event) => writable.write(formatter(event) + "\n");
|
|
233
|
+
}
|
|
234
|
+
const VALID_CONFIG_KEYS = new Set([
|
|
235
|
+
"level",
|
|
236
|
+
"ns",
|
|
237
|
+
"format"
|
|
238
|
+
]);
|
|
239
|
+
const SINK_KEYS = new Set(["file", "otel"]);
|
|
240
|
+
function isPojo(obj) {
|
|
241
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
242
|
+
const proto = Object.getPrototypeOf(obj);
|
|
243
|
+
return proto === Object.prototype || proto === null;
|
|
244
|
+
}
|
|
245
|
+
function isWritable(obj) {
|
|
246
|
+
return typeof obj === "object" && obj !== null && "write" in obj && typeof obj.write === "function" && !isPojo(obj);
|
|
247
|
+
}
|
|
248
|
+
function isValidLogLevel(val) {
|
|
249
|
+
return typeof val === "string" && val in LOG_LEVEL_PRIORITY;
|
|
250
|
+
}
|
|
251
|
+
function buildPipeline(elements, parentConfig) {
|
|
252
|
+
const config = {
|
|
253
|
+
level: parentConfig?.level ?? readEnvLevel(),
|
|
254
|
+
ns: parentConfig?.ns ?? readEnvNs(),
|
|
255
|
+
format: parentConfig?.format ?? readEnvFormat()
|
|
256
|
+
};
|
|
257
|
+
const stages = [];
|
|
258
|
+
const outputs = [];
|
|
259
|
+
const branches = [];
|
|
260
|
+
const disposables = [];
|
|
261
|
+
for (const element of elements) {
|
|
262
|
+
if (Array.isArray(element)) {
|
|
263
|
+
const branch = buildPipeline(element, { ...config });
|
|
264
|
+
branches.push(branch);
|
|
265
|
+
disposables.push(() => branch.dispose());
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (typeof element === "function" && element !== console) {
|
|
269
|
+
stages.push(element);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (element === console) {
|
|
273
|
+
outputs.push({
|
|
274
|
+
levelPriority: LOG_LEVEL_PRIORITY[config.level],
|
|
275
|
+
nsFilter: config.ns,
|
|
276
|
+
write: createConsoleSink(config.format)
|
|
277
|
+
});
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (isWritable(element)) {
|
|
281
|
+
outputs.push({
|
|
282
|
+
levelPriority: LOG_LEVEL_PRIORITY[config.level],
|
|
283
|
+
nsFilter: config.ns,
|
|
284
|
+
write: createWritableSink(element, config.format)
|
|
285
|
+
});
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (isPojo(element)) {
|
|
289
|
+
const obj = element;
|
|
290
|
+
const keys = Object.keys(obj);
|
|
291
|
+
const hasSinkKey = keys.some((k) => SINK_KEYS.has(k));
|
|
292
|
+
if (keys.some((k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k))) {
|
|
293
|
+
const unknown = keys.find((k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k));
|
|
294
|
+
throw new Error(`loggily: unknown config key "${unknown}" in config object. Valid keys: ${[...VALID_CONFIG_KEYS, ...SINK_KEYS].join(", ")}`);
|
|
295
|
+
}
|
|
296
|
+
if (hasSinkKey) {
|
|
297
|
+
if (typeof obj.file === "string") {
|
|
298
|
+
const outputLevel = isValidLogLevel(obj.level) ? obj.level : config.level;
|
|
299
|
+
const outputNs = obj.ns ? parseNsFilter(obj.ns) : config.ns;
|
|
300
|
+
const outputFormat = obj.format ?? config.format;
|
|
301
|
+
const sink = createFileSink(obj.file, outputFormat);
|
|
302
|
+
disposables.push(sink.dispose);
|
|
303
|
+
outputs.push({
|
|
304
|
+
levelPriority: LOG_LEVEL_PRIORITY[outputLevel],
|
|
305
|
+
nsFilter: outputNs,
|
|
306
|
+
write: sink.write,
|
|
307
|
+
dispose: sink.dispose
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (isValidLogLevel(obj.level)) config.level = obj.level;
|
|
313
|
+
if (obj.ns !== void 0) config.ns = parseNsFilter(obj.ns);
|
|
314
|
+
if (obj.format === "console" || obj.format === "json") config.format = obj.format;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
throw new Error(`loggily: unsupported config element of type "${typeof element}". Config arrays accept: objects (config), arrays (branches), functions (stages), console, or writables ({ write }).`);
|
|
318
|
+
}
|
|
319
|
+
const dispatch = (event) => {
|
|
320
|
+
let e = event;
|
|
321
|
+
for (const stage of stages) {
|
|
322
|
+
const result = stage(e);
|
|
323
|
+
if (result === null) return;
|
|
324
|
+
if (result !== void 0) e = result;
|
|
325
|
+
}
|
|
326
|
+
for (const output of outputs) {
|
|
327
|
+
if (e.kind === "log" && LOG_LEVEL_PRIORITY[e.level] < output.levelPriority) continue;
|
|
328
|
+
if (output.nsFilter && !output.nsFilter(e.namespace)) continue;
|
|
329
|
+
output.write(e);
|
|
330
|
+
}
|
|
331
|
+
for (const branch of branches) branch.dispatch(e);
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
dispatch,
|
|
335
|
+
level: config.level,
|
|
336
|
+
dispose: () => {
|
|
337
|
+
for (const d of disposables) d();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const runtimeState = {
|
|
342
|
+
suppressConsole: false,
|
|
343
|
+
writers: []
|
|
344
|
+
};
|
|
345
|
+
function defaultPipeline() {
|
|
346
|
+
const rt = runtimeState;
|
|
347
|
+
const disposables = [];
|
|
348
|
+
let fileSink = null;
|
|
349
|
+
const logFile = getEnv("LOG_FILE");
|
|
350
|
+
if (logFile) {
|
|
351
|
+
const sink = createFileSink(logFile, "json");
|
|
352
|
+
fileSink = sink.write;
|
|
353
|
+
disposables.push(sink.dispose);
|
|
354
|
+
}
|
|
355
|
+
const dispatch = (event) => {
|
|
356
|
+
const currentLevel = readEnvLevel();
|
|
357
|
+
if (event.kind === "log") {
|
|
358
|
+
if (LOG_LEVEL_PRIORITY[event.level] < LOG_LEVEL_PRIORITY[currentLevel]) return;
|
|
359
|
+
} else if (event.kind === "span") {
|
|
360
|
+
const trace = readEnvTrace();
|
|
361
|
+
if (!trace.enabled) return;
|
|
362
|
+
if (trace.filter && !trace.filter(event.namespace)) return;
|
|
363
|
+
}
|
|
364
|
+
const currentNs = readEnvNs();
|
|
365
|
+
if (currentNs && !currentNs(event.namespace)) return;
|
|
366
|
+
const formatter = readEnvFormat() === "json" || getEnv("NODE_ENV") === "production" || getEnv("TRACE_FORMAT") === "json" ? formatJSONEvent : formatConsoleEvent;
|
|
367
|
+
if (rt.writers.length > 0) {
|
|
368
|
+
const formatted = formatter(event);
|
|
369
|
+
const lvl = event.kind === "log" ? event.level : "span";
|
|
370
|
+
for (const w of rt.writers) w(formatted, lvl);
|
|
371
|
+
}
|
|
372
|
+
if (rt.suppressConsole) {
|
|
373
|
+
fileSink?.(event);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const text = formatter(event);
|
|
377
|
+
if (event.kind === "span") writeStderr(text);
|
|
378
|
+
else switch (event.level) {
|
|
379
|
+
case "trace":
|
|
380
|
+
case "debug":
|
|
381
|
+
console.debug(text);
|
|
382
|
+
break;
|
|
383
|
+
case "info":
|
|
384
|
+
console.info(text);
|
|
385
|
+
break;
|
|
386
|
+
case "warn":
|
|
387
|
+
console.warn(text);
|
|
388
|
+
break;
|
|
389
|
+
case "error":
|
|
390
|
+
console.error(text);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
fileSink?.(event);
|
|
394
|
+
};
|
|
395
|
+
return {
|
|
396
|
+
dispatch,
|
|
397
|
+
get level() {
|
|
398
|
+
return readEnvLevel();
|
|
399
|
+
},
|
|
400
|
+
dispose: () => {
|
|
401
|
+
for (const d of disposables) d();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function readEnvLevel() {
|
|
406
|
+
const env = getEnv("LOG_LEVEL")?.toLowerCase();
|
|
407
|
+
let level = env === "trace" || env === "debug" || env === "info" || env === "warn" || env === "error" || env === "silent" ? env : "info";
|
|
408
|
+
if (getEnv("DEBUG") && LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY.debug) level = "debug";
|
|
409
|
+
return level;
|
|
410
|
+
}
|
|
411
|
+
function readEnvNs() {
|
|
412
|
+
const debugEnv = getEnv("DEBUG");
|
|
413
|
+
if (!debugEnv) return null;
|
|
414
|
+
return parseNsFilter(debugEnv.split(",").map((s) => s.trim()));
|
|
415
|
+
}
|
|
416
|
+
function readEnvFormat() {
|
|
417
|
+
const envFormat = getEnv("LOG_FORMAT")?.toLowerCase();
|
|
418
|
+
if (envFormat === "json") return "json";
|
|
419
|
+
if (envFormat === "console") return "console";
|
|
420
|
+
if (getEnv("TRACE_FORMAT") === "json") return "json";
|
|
421
|
+
if (getEnv("NODE_ENV") === "production") return "json";
|
|
422
|
+
return "console";
|
|
423
|
+
}
|
|
424
|
+
function readEnvTrace() {
|
|
425
|
+
const traceEnv = getEnv("TRACE");
|
|
426
|
+
if (!traceEnv) return {
|
|
427
|
+
enabled: false,
|
|
428
|
+
filter: null
|
|
429
|
+
};
|
|
430
|
+
if (traceEnv === "1" || traceEnv === "true") return {
|
|
431
|
+
enabled: true,
|
|
432
|
+
filter: null
|
|
433
|
+
};
|
|
434
|
+
const prefixes = traceEnv.split(",").map((s) => s.trim());
|
|
435
|
+
return {
|
|
436
|
+
enabled: true,
|
|
437
|
+
filter: (namespace) => {
|
|
438
|
+
for (const prefix of prefixes) if (matchesPattern(namespace, prefix)) return true;
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region ../loggily/src/tracing.ts
|
|
445
|
+
let currentIdFormat = "simple";
|
|
446
|
+
let simpleSpanCounter = 0;
|
|
447
|
+
let simpleTraceCounter = 0;
|
|
448
|
+
/** Generate a hex string of the given byte length using crypto.randomUUID */
|
|
449
|
+
function randomHex(bytes) {
|
|
450
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, bytes * 2);
|
|
451
|
+
}
|
|
452
|
+
/** Generate a span ID according to the current format */
|
|
453
|
+
function generateSpanId() {
|
|
454
|
+
if (currentIdFormat === "w3c") return randomHex(8);
|
|
455
|
+
return `sp_${(++simpleSpanCounter).toString(36)}`;
|
|
456
|
+
}
|
|
457
|
+
/** Generate a trace ID according to the current format */
|
|
458
|
+
function generateTraceId() {
|
|
459
|
+
if (currentIdFormat === "w3c") return randomHex(16);
|
|
460
|
+
return `tr_${(++simpleTraceCounter).toString(36)}`;
|
|
461
|
+
}
|
|
462
|
+
let sampleRate = 1;
|
|
463
|
+
/**
|
|
464
|
+
* Determine whether a new trace should be sampled.
|
|
465
|
+
* Called at trace creation time (head-based sampling).
|
|
466
|
+
*/
|
|
467
|
+
function shouldSample() {
|
|
468
|
+
if (sampleRate >= 1) return true;
|
|
469
|
+
if (sampleRate <= 0) return false;
|
|
470
|
+
return Math.random() < sampleRate;
|
|
471
|
+
}
|
|
472
|
+
//#endregion
|
|
473
|
+
//#region ../loggily/src/core.ts
|
|
474
|
+
/**
|
|
475
|
+
* loggily v2 — Structured logging with spans
|
|
476
|
+
*
|
|
477
|
+
* One import. Objects configure. Arrays branch. Values write.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* const log = createLogger('myapp')
|
|
481
|
+
* log.info?.('starting')
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* const log = createLogger('myapp', [
|
|
485
|
+
* { level: 'debug', ns: '-sql' },
|
|
486
|
+
* console,
|
|
487
|
+
* { file: '/tmp/app.log', level: 'info', format: 'json' },
|
|
488
|
+
* ])
|
|
489
|
+
* log.info?.('server started', { port: 3000 })
|
|
490
|
+
*/
|
|
491
|
+
/** @internal */
|
|
492
|
+
let _ambientRecorder = null;
|
|
493
|
+
let _getContextTags = null;
|
|
494
|
+
let _enterContext = null;
|
|
495
|
+
let _exitContext = null;
|
|
496
|
+
function createSpanDataProxy(getFields, attrs) {
|
|
497
|
+
const READONLY_KEYS = new Set([
|
|
498
|
+
"id",
|
|
499
|
+
"traceId",
|
|
500
|
+
"parentId",
|
|
501
|
+
"startTime",
|
|
502
|
+
"endTime",
|
|
503
|
+
"duration"
|
|
504
|
+
]);
|
|
505
|
+
return new Proxy(attrs, {
|
|
506
|
+
get(_target, prop) {
|
|
507
|
+
if (READONLY_KEYS.has(prop)) return getFields()[prop];
|
|
508
|
+
return attrs[prop];
|
|
509
|
+
},
|
|
510
|
+
set(_target, prop, value) {
|
|
511
|
+
if (READONLY_KEYS.has(prop)) return false;
|
|
512
|
+
attrs[prop] = value;
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
function resolveMessage(msg) {
|
|
518
|
+
return typeof msg === "function" ? msg() : msg;
|
|
519
|
+
}
|
|
520
|
+
function createLoggerImpl(name, props, pipeline, spanMeta, parentSpanId, traceId, traceSampled = true) {
|
|
521
|
+
const emitLog = (level, msgOrError, dataOrMsg, extraData) => {
|
|
522
|
+
let message;
|
|
523
|
+
let data;
|
|
524
|
+
if (msgOrError instanceof Error) {
|
|
525
|
+
const err = msgOrError;
|
|
526
|
+
if (typeof dataOrMsg === "string") {
|
|
527
|
+
message = dataOrMsg;
|
|
528
|
+
data = {
|
|
529
|
+
...props,
|
|
530
|
+
...extraData,
|
|
531
|
+
error_type: err.name,
|
|
532
|
+
error_message: err.message,
|
|
533
|
+
error_stack: err.stack,
|
|
534
|
+
error_code: err.code
|
|
535
|
+
};
|
|
536
|
+
} else {
|
|
537
|
+
message = err.message;
|
|
538
|
+
data = {
|
|
539
|
+
...props,
|
|
540
|
+
...dataOrMsg,
|
|
541
|
+
error_type: err.name,
|
|
542
|
+
error_stack: err.stack,
|
|
543
|
+
error_code: err.code
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
} else {
|
|
547
|
+
message = resolveMessage(msgOrError);
|
|
548
|
+
const contextTags = _getContextTags?.();
|
|
549
|
+
data = contextTags && Object.keys(contextTags).length > 0 ? {
|
|
550
|
+
...contextTags,
|
|
551
|
+
...props,
|
|
552
|
+
...dataOrMsg
|
|
553
|
+
} : Object.keys(props).length > 0 || dataOrMsg ? {
|
|
554
|
+
...props,
|
|
555
|
+
...dataOrMsg
|
|
556
|
+
} : void 0;
|
|
557
|
+
}
|
|
558
|
+
const event = {
|
|
559
|
+
kind: "log",
|
|
560
|
+
time: Date.now(),
|
|
561
|
+
namespace: name,
|
|
562
|
+
level,
|
|
563
|
+
message,
|
|
564
|
+
props: data
|
|
565
|
+
};
|
|
566
|
+
pipeline.dispatch(event);
|
|
567
|
+
};
|
|
568
|
+
return {
|
|
569
|
+
name,
|
|
570
|
+
props: Object.freeze({ ...props }),
|
|
571
|
+
get spanData() {
|
|
572
|
+
if (!spanMeta) return null;
|
|
573
|
+
return createSpanDataProxy(() => ({
|
|
574
|
+
id: spanMeta.id,
|
|
575
|
+
traceId: spanMeta.traceId,
|
|
576
|
+
parentId: spanMeta.parentId,
|
|
577
|
+
startTime: spanMeta.startTime,
|
|
578
|
+
endTime: spanMeta.endTime,
|
|
579
|
+
duration: spanMeta.endTime !== null ? spanMeta.endTime - spanMeta.startTime : Date.now() - spanMeta.startTime
|
|
580
|
+
}), spanMeta.attrs);
|
|
581
|
+
},
|
|
582
|
+
trace: (msg, data) => emitLog("trace", msg, data),
|
|
583
|
+
debug: (msg, data) => emitLog("debug", msg, data),
|
|
584
|
+
info: (msg, data) => emitLog("info", msg, data),
|
|
585
|
+
warn: (msg, data) => emitLog("warn", msg, data),
|
|
586
|
+
error: (msgOrError, dataOrMsg, extraData) => emitLog("error", msgOrError, dataOrMsg, extraData),
|
|
587
|
+
logger(namespace, childProps) {
|
|
588
|
+
return wrapConditional(createLoggerImpl(namespace ? `${name}:${namespace}` : name, {
|
|
589
|
+
...props,
|
|
590
|
+
...childProps
|
|
591
|
+
}, pipeline, null, parentSpanId, traceId, traceSampled), () => pipeline.level);
|
|
592
|
+
},
|
|
593
|
+
span(namespace, childProps) {
|
|
594
|
+
const childName = namespace ? `${name}:${namespace}` : name;
|
|
595
|
+
const resolvedChildProps = typeof childProps === "function" ? childProps() : childProps;
|
|
596
|
+
const mergedProps = {
|
|
597
|
+
...props,
|
|
598
|
+
...resolvedChildProps
|
|
599
|
+
};
|
|
600
|
+
const newSpanId = generateSpanId();
|
|
601
|
+
let resolvedParentId = parentSpanId;
|
|
602
|
+
let resolvedTraceId = traceId;
|
|
603
|
+
const isNewTrace = !resolvedTraceId;
|
|
604
|
+
const finalTraceId = resolvedTraceId || generateTraceId();
|
|
605
|
+
const sampled = isNewTrace ? shouldSample() : traceSampled;
|
|
606
|
+
const newSpanData = {
|
|
607
|
+
id: newSpanId,
|
|
608
|
+
traceId: finalTraceId,
|
|
609
|
+
parentId: resolvedParentId,
|
|
610
|
+
startTime: Date.now(),
|
|
611
|
+
endTime: null,
|
|
612
|
+
duration: null,
|
|
613
|
+
attrs: {}
|
|
614
|
+
};
|
|
615
|
+
const spanLogger = createLoggerImpl(childName, mergedProps, pipeline, newSpanData, newSpanId, finalTraceId, sampled);
|
|
616
|
+
_enterContext?.(newSpanId, finalTraceId, resolvedParentId);
|
|
617
|
+
spanLogger[Symbol.dispose] = () => {
|
|
618
|
+
if (newSpanData.endTime !== null) return;
|
|
619
|
+
newSpanData.endTime = Date.now();
|
|
620
|
+
newSpanData.duration = newSpanData.endTime - newSpanData.startTime;
|
|
621
|
+
_exitContext?.(newSpanId);
|
|
622
|
+
_ambientRecorder?.recordSpan({
|
|
623
|
+
name: childName,
|
|
624
|
+
durationMs: newSpanData.duration
|
|
625
|
+
});
|
|
626
|
+
if (sampled) {
|
|
627
|
+
const spanEvent = {
|
|
628
|
+
kind: "span",
|
|
629
|
+
time: newSpanData.endTime,
|
|
630
|
+
namespace: childName,
|
|
631
|
+
name: childName,
|
|
632
|
+
duration: newSpanData.duration,
|
|
633
|
+
props: {
|
|
634
|
+
...mergedProps,
|
|
635
|
+
...newSpanData.attrs
|
|
636
|
+
},
|
|
637
|
+
spanId: newSpanData.id,
|
|
638
|
+
traceId: newSpanData.traceId,
|
|
639
|
+
parentId: newSpanData.parentId
|
|
640
|
+
};
|
|
641
|
+
pipeline.dispatch(spanEvent);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
return spanLogger;
|
|
645
|
+
},
|
|
646
|
+
child(context) {
|
|
647
|
+
if (typeof context === "string") return this.logger(context);
|
|
648
|
+
return wrapConditional(createLoggerImpl(name, {
|
|
649
|
+
...props,
|
|
650
|
+
...context
|
|
651
|
+
}, pipeline, null, parentSpanId, traceId, traceSampled), () => pipeline.level);
|
|
652
|
+
},
|
|
653
|
+
end() {
|
|
654
|
+
if (spanMeta?.endTime === null) this[Symbol.dispose]?.();
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function wrapConditional(logger, getLevel) {
|
|
659
|
+
return new Proxy(logger, { get(target, prop) {
|
|
660
|
+
if (prop in LOG_LEVEL_PRIORITY && prop !== "silent") {
|
|
661
|
+
if (LOG_LEVEL_PRIORITY[prop] < LOG_LEVEL_PRIORITY[getLevel()]) return;
|
|
662
|
+
}
|
|
663
|
+
return target[prop];
|
|
664
|
+
} });
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Create a logger.
|
|
668
|
+
*
|
|
669
|
+
* @param name - Logger namespace (e.g., 'myapp', 'myapp:db')
|
|
670
|
+
* @param config - Optional config array. Objects configure, arrays branch, values write.
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* // Zero config (reads LOG_LEVEL, DEBUG, LOG_FORMAT from env)
|
|
674
|
+
* const log = createLogger('myapp')
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* // Configured pipeline
|
|
678
|
+
* const log = createLogger('myapp', [
|
|
679
|
+
* { level: 'debug', ns: '-sql' },
|
|
680
|
+
* console,
|
|
681
|
+
* { file: '/tmp/app.log', level: 'info', format: 'json' },
|
|
682
|
+
* ])
|
|
683
|
+
*/
|
|
684
|
+
function createLogger(name, config) {
|
|
685
|
+
const pipeline = config ? buildPipeline(config) : defaultPipeline();
|
|
686
|
+
return wrapConditional(createLoggerImpl(name, {}, pipeline, null, null, null), () => pipeline.level);
|
|
687
|
+
}
|
|
688
|
+
(typeof process !== "undefined" ? process : void 0)?.env;
|
|
689
|
+
//#endregion
|
|
690
|
+
export { createLogger };
|
|
691
|
+
|
|
692
|
+
//# sourceMappingURL=src-BWyhokNZ.mjs.map
|