@paroicms/ui-logger 1.14.2 → 1.15.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.
@@ -24,12 +24,13 @@ export interface UiLoggerOptions {
24
24
  }
25
25
  export interface SendToServerOptions {
26
26
  minLevel?: UiLoggerLevels;
27
- sendToServer?(lines: FrontLogLine[]): Promise<void>;
27
+ sendToServer?(lines: FrontLogLine[]): Promise<SendStatus>;
28
28
  }
29
29
  export interface FrontLogLine {
30
- message: string[];
30
+ message: string;
31
31
  level: UiLoggerStrictLevels;
32
32
  }
33
+ export type SendStatus = "NOT_READY" | "DONE" | "CANCEL";
33
34
  export declare function createNoUiLogger(): FullUiLogger;
34
35
  export declare function createUiLogger(mainOptions?: UiLoggerOptions): FullUiLogger;
35
36
  type WrapAsync = <T extends (...args: any[]) => Promise<any>>(fn: T) => (...args: Parameters<T>) => void;
package/dist/ui-logger.js CHANGED
@@ -79,6 +79,7 @@ function makeToServer(send, consoleLog) {
79
79
  }
80
80
  function createSendMiddleware(options) {
81
81
  const logErrorFromBackend = toLevelNum(options.minLevel ?? "none") >= toLevelNum("error");
82
+ const sendMinLevel = options.minLevel ?? "none";
82
83
  const postLog = options.sendToServer
83
84
  ? new PostLogToBackend({
84
85
  sendToServer: options.sendToServer,
@@ -87,7 +88,7 @@ function createSendMiddleware(options) {
87
88
  : undefined;
88
89
  return (level, cb) => (...message) => {
89
90
  cb(...message);
90
- if (postLog) {
91
+ if (postLog && toLevelNum(level) <= toLevelNum(sendMinLevel)) {
91
92
  postLog.addLogLineToPost({
92
93
  message,
93
94
  level,
@@ -117,6 +118,8 @@ class PostLogToBackend {
117
118
  options;
118
119
  lines = [];
119
120
  timeoutId;
121
+ sending = false;
122
+ retryDelayMs = 500;
120
123
  constructor(options) {
121
124
  this.options = options;
122
125
  }
@@ -126,44 +129,69 @@ class PostLogToBackend {
126
129
  }
127
130
  delayedSend() {
128
131
  if (this.timeoutId !== undefined) {
129
- if (this.lines.length >= 20)
130
- return;
131
132
  clearTimeout(this.timeoutId);
133
+ this.timeoutId = undefined;
134
+ if (this.lines.length >= 20) {
135
+ void this.#flush();
136
+ return;
137
+ }
132
138
  }
133
139
  this.timeoutId = setTimeout(() => {
134
140
  this.timeoutId = undefined;
135
- if (this.lines.length > 0) {
136
- void postLogFromFrontend({
137
- ...this.options,
138
- lines: this.lines,
139
- });
140
- this.lines = [];
141
- }
141
+ void this.#flush();
142
142
  }, 50);
143
143
  }
144
+ async #flush() {
145
+ if (this.sending || this.lines.length === 0)
146
+ return;
147
+ const batch = this.lines;
148
+ this.lines = [];
149
+ this.sending = true;
150
+ const status = await postLogFromFrontend({
151
+ ...this.options,
152
+ lines: batch,
153
+ });
154
+ this.sending = false;
155
+ if (status === "NOT_READY") {
156
+ this.lines = batch.concat(this.lines);
157
+ setTimeout(() => this.delayedSend(), this.retryDelayMs);
158
+ return;
159
+ }
160
+ // On CANCEL: do nothing further; messages already went to console via cb
161
+ if (this.lines.length > 0)
162
+ this.delayedSend();
163
+ }
144
164
  }
145
165
  async function postLogFromFrontend({ lines, sendToServer, logErrorFromBackend, }) {
146
166
  try {
147
167
  for (const line of lines)
148
- line.message = line.message.map(anyMessageToStringifiable);
149
- await sendToServer(lines.map((line) => ({
150
- message: line.message.map(messageOf),
168
+ line.message = line.message.map((msg) => anyMessageToStringifiable(msg));
169
+ const status = await sendToServer(lines.map((line) => ({
170
+ message: line.message.map(messageOf).join(" "),
151
171
  level: line.level,
152
172
  })));
173
+ return status;
153
174
  }
154
175
  catch (err) {
155
176
  if (logErrorFromBackend && window.console?.error) {
156
177
  console.error("Cannot send log to backend:", err);
157
178
  }
179
+ return "CANCEL";
158
180
  }
159
181
  }
160
- function anyMessageToStringifiable(message) {
182
+ function anyMessageToStringifiable(message, seen = new WeakSet()) {
161
183
  if (message === null)
162
184
  return null;
163
185
  if (Number.isNaN(message))
164
186
  return "[NaN]";
165
- if (Array.isArray(message))
166
- return message.map((item) => anyMessageToStringifiable(item));
187
+ if (Array.isArray(message)) {
188
+ if (seen.has(message))
189
+ return "[Circular Array]";
190
+ seen.add(message);
191
+ const result = message.map((item) => anyMessageToStringifiable(item, seen));
192
+ seen.delete(message);
193
+ return result;
194
+ }
167
195
  if (message instanceof Error)
168
196
  return message.message ?? message.name ?? "[Error]";
169
197
  switch (typeof message) {
@@ -176,13 +204,18 @@ function anyMessageToStringifiable(message) {
176
204
  case "bigint":
177
205
  return `${message.toString()}n`;
178
206
  case "function":
179
- return "[Function]";
207
+ return message.name ? `[Function: ${message.name}]` : "[Function]";
180
208
  case "symbol":
181
209
  return "[Symbol]";
182
210
  case "object": {
211
+ if (seen.has(message))
212
+ return "[Circular]";
213
+ seen.add(message);
183
214
  const obj = {};
184
- for (const key of Object.keys(message))
185
- obj[key] = anyMessageToStringifiable(message[key]);
215
+ for (const key of Object.keys(message)) {
216
+ obj[key] = anyMessageToStringifiable(message[key], seen);
217
+ }
218
+ seen.delete(message);
186
219
  return obj;
187
220
  }
188
221
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paroicms/ui-logger",
3
- "version": "1.14.2",
3
+ "version": "1.15.0",
4
4
  "description": "Logger for the Admin UI front of a Paroi CMS.",
5
5
  "keywords": [
6
6
  "paroicms",
@@ -20,7 +20,9 @@
20
20
  "scripts": {
21
21
  "build": "tsc",
22
22
  "clear": "rimraf dist/*",
23
- "dev": "tsc --watch --preserveWatchOutput"
23
+ "dev": "tsc --watch --preserveWatchOutput",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest"
24
26
  },
25
27
  "dependencies": {
26
28
  "@paroicms/public-anywhere-lib": "0.37.0"
@@ -28,7 +30,8 @@
28
30
  "devDependencies": {
29
31
  "rimraf": "~6.0.1",
30
32
  "typescript": "~5.9.2",
31
- "react": "~19.1.0"
33
+ "react": "~19.1.0",
34
+ "vitest": "~3.2.3"
32
35
  },
33
36
  "files": [
34
37
  "dist"