@hatua/log 0.0.0 → 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +82 -0
- package/dist/index.test.d.ts +1 -0
- package/package.json +19 -5
- package/README.md +0 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pedro Gomes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Levelled, categorised diagnostics for the parts of Hatua that decide things
|
|
3
|
+
* without drawing them.
|
|
4
|
+
*
|
|
5
|
+
* The store refuses a command, narrows a publish gate, halts a save, drops a
|
|
6
|
+
* stale renewal — each for a reason it knows exactly and, until now, kept. What
|
|
7
|
+
* reaches a screen is the outcome; what is worth having while something is
|
|
8
|
+
* being worked on is the reasoning that led there, in order, with what it was
|
|
9
|
+
* looking at.
|
|
10
|
+
*
|
|
11
|
+
* ## Silent unless asked
|
|
12
|
+
*
|
|
13
|
+
* Hatua renders inside somebody else's product. A library that writes `info` to
|
|
14
|
+
* a Host's console by default is one every integrator has to go and turn off,
|
|
15
|
+
* and one whose noise buries their own logs — so nothing below `warn` is
|
|
16
|
+
* written until a caller asks for it. `warn` and `error` are always through,
|
|
17
|
+
* because a Host that has wired something wrongly should hear about it without
|
|
18
|
+
* having to opt in.
|
|
19
|
+
*
|
|
20
|
+
* ## Categories are packages
|
|
21
|
+
*
|
|
22
|
+
* A category names where the line came from — `services.editing`,
|
|
23
|
+
* `react.fields` — so a level can be turned up for the thing being chased
|
|
24
|
+
* without turning it up for everything. Dotted, and matched by prefix:
|
|
25
|
+
* `services` covers `services.editing`, and `*` covers everything.
|
|
26
|
+
*/
|
|
27
|
+
export type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
28
|
+
/**
|
|
29
|
+
* What a line is handed to.
|
|
30
|
+
*
|
|
31
|
+
* A seam rather than a hard-wired `console`, because a Host that already has
|
|
32
|
+
* somewhere for diagnostics to go should not have Hatua's arriving somewhere
|
|
33
|
+
* else — and because a test that asserts what was logged should not have to
|
|
34
|
+
* read the terminal to do it.
|
|
35
|
+
*/
|
|
36
|
+
export type Sink = (line: Line) => void;
|
|
37
|
+
export interface Line {
|
|
38
|
+
level: Level;
|
|
39
|
+
/** Where it came from, dotted: `services.editing`. */
|
|
40
|
+
category: string;
|
|
41
|
+
message: string;
|
|
42
|
+
/** Whatever the call site was looking at when it decided. */
|
|
43
|
+
detail?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
export interface LogConfig {
|
|
46
|
+
/**
|
|
47
|
+
* The level a category is written at, by prefix. The longest matching prefix
|
|
48
|
+
* wins, so `{ '*': 'warn', 'services.editing': 'trace' }` says what it looks
|
|
49
|
+
* like it says.
|
|
50
|
+
*/
|
|
51
|
+
levels?: Record<string, Level>;
|
|
52
|
+
sink?: Sink;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The settings, held on the global rather than in this module.
|
|
56
|
+
*
|
|
57
|
+
* A module holds its state once per instance, and there is no promise that this
|
|
58
|
+
* module is instantiated once. A dev server resolving symlinked workspace
|
|
59
|
+
* packages, a Host bundling `@hatua/react` while its own code imports
|
|
60
|
+
* `@hatua/log`, two copies at different versions in one tree — each gives the
|
|
61
|
+
* app one table and the packages another, so turning a category up changes a
|
|
62
|
+
* table nothing reads and the switch appears to do nothing at all.
|
|
63
|
+
*
|
|
64
|
+
* A symbol on `globalThis` is the one place every instance can agree on. It is
|
|
65
|
+
* what the settings are ABOUT — one page, one answer to "what is Hatua writing"
|
|
66
|
+
* — so sharing them there is the shape of the thing rather than a workaround
|
|
67
|
+
* for the bundler.
|
|
68
|
+
*/
|
|
69
|
+
interface Settings {
|
|
70
|
+
levels: Record<string, Level>;
|
|
71
|
+
sink: Sink;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Turn categories up or down, or send lines somewhere else.
|
|
75
|
+
*
|
|
76
|
+
* Merged into what is already set rather than replacing it, so turning one
|
|
77
|
+
* category up does not silently reset the rest. Returns what is in force, so a
|
|
78
|
+
* console can show whether the call took.
|
|
79
|
+
*/
|
|
80
|
+
export declare function configureLogging(config: LogConfig): Readonly<Settings>;
|
|
81
|
+
/**
|
|
82
|
+
* Turn a level on from code, in one line, with the spec a person types.
|
|
83
|
+
*
|
|
84
|
+
* What a `console.log` used to be for. Chasing something means putting a line
|
|
85
|
+
* where the question is, and going through `configureLogging({ levels: … })`
|
|
86
|
+
* asks for two imports and a nested object at the moment attention is
|
|
87
|
+
* elsewhere — so this is the form that gets pasted at the top of a file and
|
|
88
|
+
* deleted an hour later.
|
|
89
|
+
*
|
|
90
|
+
* import { setLogLevel } from '@hatua/log'
|
|
91
|
+
* setLogLevel('*:debug')
|
|
92
|
+
*
|
|
93
|
+
* It complains rather than doing nothing when the spec is unusable, because a
|
|
94
|
+
* diagnostic switch that fails silently is worse than none: the silence reads
|
|
95
|
+
* as "nothing is happening" when it means "nothing was turned on".
|
|
96
|
+
*/
|
|
97
|
+
export declare function setLogLevel(spec: string): Readonly<Settings>;
|
|
98
|
+
/** Back to silent-unless-asked, and back to the console. What a test resets to. */
|
|
99
|
+
export declare function resetLogging(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Levels from a short string: `*:debug`, `services.editing:trace,react:debug`.
|
|
102
|
+
*
|
|
103
|
+
* Here rather than in whatever is calling it because it is the form a person
|
|
104
|
+
* types under time pressure — into a console, into a query string, into an
|
|
105
|
+
* environment variable — and every one of those callers would otherwise write
|
|
106
|
+
* its own splitter and get a different one wrong.
|
|
107
|
+
*
|
|
108
|
+
* A bare level with no category means everything: `debug` is `*:debug`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function levelsFrom(spec: string): Record<string, Level>;
|
|
111
|
+
export interface Logger {
|
|
112
|
+
error(message: string, detail?: Record<string, unknown>): void;
|
|
113
|
+
warn(message: string, detail?: Record<string, unknown>): void;
|
|
114
|
+
info(message: string, detail?: Record<string, unknown>): void;
|
|
115
|
+
debug(message: string, detail?: Record<string, unknown>): void;
|
|
116
|
+
trace(message: string, detail?: Record<string, unknown>): void;
|
|
117
|
+
/** Whether a line at this level would be written, for a caller with work to do to build one. */
|
|
118
|
+
enabled(level: Level): boolean;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A logger for one category.
|
|
122
|
+
*
|
|
123
|
+
* Held at module scope by its caller — the category is a fact about the file,
|
|
124
|
+
* not about the call — and it reads the configuration at write time, so turning
|
|
125
|
+
* a category up affects loggers already made.
|
|
126
|
+
*/
|
|
127
|
+
export declare function logger(category: string): Logger;
|
|
128
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
var e = {
|
|
3
|
+
error: 0,
|
|
4
|
+
warn: 1,
|
|
5
|
+
info: 2,
|
|
6
|
+
debug: 3,
|
|
7
|
+
trace: 4
|
|
8
|
+
}, t = { "*": "warn" }, n = {
|
|
9
|
+
error: "#e5484d",
|
|
10
|
+
warn: "#d9a300",
|
|
11
|
+
info: "#8b8b8b",
|
|
12
|
+
debug: "#8b8b8b",
|
|
13
|
+
trace: "#8b8b8b"
|
|
14
|
+
}, r = typeof window < "u", i = ({ level: e, category: t, message: i, detail: a }) => {
|
|
15
|
+
let o = e === "error" ? console.error : e === "warn" ? console.warn : console.log, s = `[${e.toUpperCase()}][${t}]`;
|
|
16
|
+
if (!r) {
|
|
17
|
+
let e = `${s} ${i}`;
|
|
18
|
+
a === void 0 ? o(e) : o(e, a);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let c = `%c${s}%c ${i}`, l = `color:${n[e]};font-weight:bold`, u = `color:${n[e]};font-weight:normal`;
|
|
22
|
+
a === void 0 ? o(c, l, u) : o(c, l, u, a);
|
|
23
|
+
}, a = Symbol.for("hatua.log.settings"), o = globalThis, s = () => (o[a] ??= {
|
|
24
|
+
levels: { ...t },
|
|
25
|
+
sink: i
|
|
26
|
+
}, o[a]);
|
|
27
|
+
function c(e) {
|
|
28
|
+
let t = s();
|
|
29
|
+
return e.levels && (t.levels = {
|
|
30
|
+
...t.levels,
|
|
31
|
+
...e.levels
|
|
32
|
+
}), e.sink && (t.sink = e.sink), {
|
|
33
|
+
levels: { ...t.levels },
|
|
34
|
+
sink: t.sink
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function l(e) {
|
|
38
|
+
let t = f(e);
|
|
39
|
+
return Object.keys(t).length === 0 && console.warn(`[hatua] nothing usable in the log level "${e}". Try '*:debug'.`), c({ levels: t });
|
|
40
|
+
}
|
|
41
|
+
function u() {
|
|
42
|
+
o[a] = {
|
|
43
|
+
levels: { ...t },
|
|
44
|
+
sink: i
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var d = (e) => {
|
|
48
|
+
let { levels: t } = s(), n = t["*"] ?? "warn", r = -1;
|
|
49
|
+
for (let [i, a] of Object.entries(t)) i !== "*" && (e === i || e.startsWith(`${i}.`)) && i.length > r && (r = i.length, n = a);
|
|
50
|
+
return n;
|
|
51
|
+
};
|
|
52
|
+
function f(e) {
|
|
53
|
+
let t = {};
|
|
54
|
+
for (let n of e.split(",")) {
|
|
55
|
+
let e = n.trim();
|
|
56
|
+
if (!e) continue;
|
|
57
|
+
let r = e.lastIndexOf(":"), i = r < 0 ? "*" : e.slice(0, r).trim(), a = (r < 0 ? e : e.slice(r + 1)).trim();
|
|
58
|
+
p(a) && (t[i || "*"] = a);
|
|
59
|
+
}
|
|
60
|
+
return t;
|
|
61
|
+
}
|
|
62
|
+
var p = (t) => t in e;
|
|
63
|
+
function m(t) {
|
|
64
|
+
let n = (n, r, i) => {
|
|
65
|
+
e[n] > e[d(t)] || s().sink({
|
|
66
|
+
level: n,
|
|
67
|
+
category: t,
|
|
68
|
+
message: r,
|
|
69
|
+
...i === void 0 ? {} : { detail: i }
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
error: (e, t) => n("error", e, t),
|
|
74
|
+
warn: (e, t) => n("warn", e, t),
|
|
75
|
+
info: (e, t) => n("info", e, t),
|
|
76
|
+
debug: (e, t) => n("debug", e, t),
|
|
77
|
+
trace: (e, t) => n("trace", e, t),
|
|
78
|
+
enabled: (n) => e[n] <= e[d(t)]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { c as configureLogging, f as levelsFrom, m as logger, u as resetLogging, l as setLogLevel };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatua/log",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Internal to @hatua/react — published because it is an external of its build, not a supported API. Levelled, categorised diagnostics — silent unless a Host asks.",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/pedromvgomes/hatua.git",
|
|
9
10
|
"directory": "source/packages/log"
|
|
10
11
|
},
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
11
22
|
"publishConfig": {
|
|
12
23
|
"access": "public"
|
|
13
24
|
},
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "vite build",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reporter=lcovonly"
|
|
30
|
+
}
|
|
17
31
|
}
|