@paroicms/ui-logger 1.12.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/README.md +9 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/ui-logger.d.ts +36 -0
- package/dist/ui-logger.js +192 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @paroicms/ui-logger
|
|
2
|
+
|
|
3
|
+
Logger for the BO front of a Paroi CMS.
|
|
4
|
+
|
|
5
|
+
This package is part of [ParoiCMS](https://www.npmjs.com/package/@paroicms/server).
|
|
6
|
+
|
|
7
|
+
## License
|
|
8
|
+
|
|
9
|
+
Released under the [MIT license](https://gitlab.com/paroi/opensource/paroicms/-/blob/main/LICENSE.md).
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import { createNoUiLogger } from "./ui-logger.js";
|
|
3
|
+
export * from "./ui-logger.js";
|
|
4
|
+
const UiLoggerContext = createContext(createNoUiLogger());
|
|
5
|
+
export const UiLoggerProvider = UiLoggerContext.Provider;
|
|
6
|
+
export function useUiLogger() {
|
|
7
|
+
return useContext(UiLoggerContext);
|
|
8
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface UiLogger {
|
|
2
|
+
error: LogFn;
|
|
3
|
+
warn: LogFn;
|
|
4
|
+
info: LogFn;
|
|
5
|
+
trace: LogFn;
|
|
6
|
+
debug: LogFn;
|
|
7
|
+
log(options: LogParameters): void;
|
|
8
|
+
}
|
|
9
|
+
export interface FullUiLogger extends UiLogger {
|
|
10
|
+
wrapAsync: WrapAsync;
|
|
11
|
+
lateSetup(options: Partial<UiLoggerOptions>): void;
|
|
12
|
+
}
|
|
13
|
+
export interface LogParameters {
|
|
14
|
+
level: UiLoggerStrictLevels;
|
|
15
|
+
message: any[];
|
|
16
|
+
clientOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export type LogFn = (...messages: any[]) => void;
|
|
19
|
+
export type UiLoggerStrictLevels = Exclude<keyof UiLogger, "log" | "wrapAsync">;
|
|
20
|
+
export type UiLoggerLevels = UiLoggerStrictLevels | "none";
|
|
21
|
+
export interface UiLoggerOptions {
|
|
22
|
+
minLevel?: UiLoggerLevels;
|
|
23
|
+
send?: SendToServerOptions;
|
|
24
|
+
}
|
|
25
|
+
export interface SendToServerOptions {
|
|
26
|
+
minLevel?: UiLoggerLevels;
|
|
27
|
+
sendToServer?(lines: FrontLogLine[]): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface FrontLogLine {
|
|
30
|
+
message: string[];
|
|
31
|
+
level: UiLoggerStrictLevels;
|
|
32
|
+
}
|
|
33
|
+
export declare function createNoUiLogger(): FullUiLogger;
|
|
34
|
+
export declare function createUiLogger(mainOptions?: UiLoggerOptions): FullUiLogger;
|
|
35
|
+
type WrapAsync = <T extends (...args: any[]) => Promise<any>>(fn: T) => (...args: Parameters<T>) => void;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const noOp = () => {
|
|
2
|
+
// Nothing to do.
|
|
3
|
+
};
|
|
4
|
+
export function createNoUiLogger() {
|
|
5
|
+
return {
|
|
6
|
+
error: noOp,
|
|
7
|
+
warn: noOp,
|
|
8
|
+
info: noOp,
|
|
9
|
+
trace: noOp,
|
|
10
|
+
debug: noOp,
|
|
11
|
+
log: noOp,
|
|
12
|
+
wrapAsync: makeWrapAsync({ error: noOp }),
|
|
13
|
+
lateSetup: () => {
|
|
14
|
+
throw new Error("'lateSetup' can't be called on noUiLogger.");
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function createUiLogger(mainOptions) {
|
|
19
|
+
let minLevel = mainOptions?.minLevel ?? "none";
|
|
20
|
+
let consoleLog = makeToConsole(minLevel);
|
|
21
|
+
const obj = {
|
|
22
|
+
...(mainOptions?.send ? makeToServer(mainOptions.send, consoleLog) : consoleLog),
|
|
23
|
+
log(options) {
|
|
24
|
+
if (options.clientOnly) {
|
|
25
|
+
consoleLog[options.level](...options.message);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
obj[options.level](...options.message);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
lateSetup(lateOptions) {
|
|
32
|
+
if (lateOptions.minLevel && lateOptions.minLevel !== minLevel) {
|
|
33
|
+
minLevel = lateOptions.minLevel;
|
|
34
|
+
consoleLog = makeToConsole(lateOptions.minLevel);
|
|
35
|
+
}
|
|
36
|
+
Object.assign(obj, lateOptions.send ? makeToServer(lateOptions.send, consoleLog) : consoleLog);
|
|
37
|
+
},
|
|
38
|
+
get wrapAsync() {
|
|
39
|
+
return makeWrapAsync(obj);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return obj;
|
|
43
|
+
}
|
|
44
|
+
function makeToConsole(minLevel) {
|
|
45
|
+
return {
|
|
46
|
+
error: toConsole("error", minLevel),
|
|
47
|
+
warn: toConsole("warn", minLevel),
|
|
48
|
+
info: toConsole("info", minLevel),
|
|
49
|
+
trace: toConsole("trace", minLevel),
|
|
50
|
+
debug: toConsole("debug", minLevel),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function toConsole(level, minLevel) {
|
|
54
|
+
if (!window.console || toLevelNum(level) > toLevelNum(minLevel)) {
|
|
55
|
+
return () => {
|
|
56
|
+
// Nothing to do.
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (console[level]) {
|
|
60
|
+
return (...messages) => console[level](`[${level}]`, ...messages);
|
|
61
|
+
}
|
|
62
|
+
if (console.log) {
|
|
63
|
+
return (...messages) => console.log(`[${level}]`, ...messages);
|
|
64
|
+
}
|
|
65
|
+
return () => {
|
|
66
|
+
// Nothing to do.
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function makeToServer(send, consoleLog) {
|
|
70
|
+
const middleware = send ? createSendMiddleware(send) : (_, cb) => cb;
|
|
71
|
+
return {
|
|
72
|
+
error: middleware("error", consoleLog.error),
|
|
73
|
+
warn: middleware("warn", consoleLog.warn),
|
|
74
|
+
info: middleware("info", consoleLog.info),
|
|
75
|
+
trace: middleware("trace", consoleLog.trace),
|
|
76
|
+
debug: middleware("debug", consoleLog.debug),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createSendMiddleware(options) {
|
|
80
|
+
const logErrorFromBackend = toLevelNum(options.minLevel ?? "none") >= toLevelNum("error");
|
|
81
|
+
const postLog = options.sendToServer
|
|
82
|
+
? new PostLogToBackend({
|
|
83
|
+
sendToServer: options.sendToServer,
|
|
84
|
+
logErrorFromBackend,
|
|
85
|
+
})
|
|
86
|
+
: undefined;
|
|
87
|
+
return (level, cb) => (...message) => {
|
|
88
|
+
cb(...message);
|
|
89
|
+
if (postLog) {
|
|
90
|
+
postLog.addLogLineToPost({
|
|
91
|
+
message,
|
|
92
|
+
level,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function toLevelNum(level) {
|
|
98
|
+
switch (level) {
|
|
99
|
+
case "none":
|
|
100
|
+
return 0;
|
|
101
|
+
case "error":
|
|
102
|
+
return 1;
|
|
103
|
+
case "warn":
|
|
104
|
+
return 2;
|
|
105
|
+
case "info":
|
|
106
|
+
return 3;
|
|
107
|
+
case "trace":
|
|
108
|
+
return 4;
|
|
109
|
+
case "debug":
|
|
110
|
+
return 5;
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`Invalid level: ${level}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
class PostLogToBackend {
|
|
116
|
+
options;
|
|
117
|
+
lines = [];
|
|
118
|
+
timeoutId;
|
|
119
|
+
constructor(options) {
|
|
120
|
+
this.options = options;
|
|
121
|
+
}
|
|
122
|
+
addLogLineToPost(line) {
|
|
123
|
+
this.lines.push(line);
|
|
124
|
+
this.delayedSend();
|
|
125
|
+
}
|
|
126
|
+
delayedSend() {
|
|
127
|
+
if (this.timeoutId !== undefined) {
|
|
128
|
+
if (this.lines.length >= 20)
|
|
129
|
+
return;
|
|
130
|
+
clearTimeout(this.timeoutId);
|
|
131
|
+
}
|
|
132
|
+
this.timeoutId = setTimeout(() => {
|
|
133
|
+
this.timeoutId = undefined;
|
|
134
|
+
if (this.lines.length > 0) {
|
|
135
|
+
void postLogFromFrontend({
|
|
136
|
+
...this.options,
|
|
137
|
+
lines: this.lines,
|
|
138
|
+
});
|
|
139
|
+
this.lines = [];
|
|
140
|
+
}
|
|
141
|
+
}, 50);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function postLogFromFrontend({ lines, sendToServer, logErrorFromBackend, }) {
|
|
145
|
+
try {
|
|
146
|
+
for (const line of lines)
|
|
147
|
+
line.message = line.message.map(anyMessageToStringifiable);
|
|
148
|
+
await sendToServer(lines);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (logErrorFromBackend && window.console?.error) {
|
|
152
|
+
console.error("Cannot send log to backend:", err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function anyMessageToStringifiable(message) {
|
|
157
|
+
if (message === null)
|
|
158
|
+
return null;
|
|
159
|
+
if (Number.isNaN(message))
|
|
160
|
+
return "[NaN]";
|
|
161
|
+
if (Array.isArray(message))
|
|
162
|
+
return message.map((item) => anyMessageToStringifiable(item));
|
|
163
|
+
if (message instanceof Error)
|
|
164
|
+
return message.message ?? message.name ?? "[Error]";
|
|
165
|
+
switch (typeof message) {
|
|
166
|
+
case "string":
|
|
167
|
+
case "number":
|
|
168
|
+
case "boolean":
|
|
169
|
+
return message;
|
|
170
|
+
case "undefined":
|
|
171
|
+
return null;
|
|
172
|
+
case "bigint":
|
|
173
|
+
return `${message.toString()}n`;
|
|
174
|
+
case "function":
|
|
175
|
+
return "[Function]";
|
|
176
|
+
case "symbol":
|
|
177
|
+
return "[Symbol]";
|
|
178
|
+
case "object": {
|
|
179
|
+
const obj = {};
|
|
180
|
+
for (const key of Object.keys(message))
|
|
181
|
+
obj[key] = anyMessageToStringifiable(message[key]);
|
|
182
|
+
return obj;
|
|
183
|
+
}
|
|
184
|
+
default:
|
|
185
|
+
return "[Unexpected value]";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function makeWrapAsync(logger) {
|
|
189
|
+
return (fn) => (...args) => {
|
|
190
|
+
fn(...args).catch(logger.error);
|
|
191
|
+
};
|
|
192
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paroicms/ui-logger",
|
|
3
|
+
"version": "1.12.0",
|
|
4
|
+
"description": "Logger for the Admin UI front of a Paroi CMS.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"paroicms",
|
|
7
|
+
"logger",
|
|
8
|
+
"front-log"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://gitlab.com/paroi/opensource/paroicms.git",
|
|
13
|
+
"directory": "packages/ui-logger"
|
|
14
|
+
},
|
|
15
|
+
"author": "Paroi Team",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"module": "dist/index.js",
|
|
19
|
+
"typings": "dist/index.d.ts",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"clear": "rimraf dist/*",
|
|
23
|
+
"dev": "tsc --watch --preserveWatchOutput"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"rimraf": "~6.0.1",
|
|
27
|
+
"typescript": "~5.8.3",
|
|
28
|
+
"react": "~19.1.0"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
]
|
|
33
|
+
}
|