@hkdigital/lib-core 0.4.23 → 0.4.25
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/logging/internal/adapters/pino.d.ts +7 -3
- package/dist/logging/internal/adapters/pino.js +200 -67
- package/dist/logging/internal/transports/pretty-transport.d.ts +17 -0
- package/dist/logging/internal/transports/pretty-transport.js +104 -0
- package/dist/logging/internal/transports/test-transport.d.ts +19 -0
- package/dist/logging/internal/transports/test-transport.js +79 -0
- package/dist/network/loaders/audio/AudioScene.svelte.js +2 -2
- package/dist/network/loaders/image/ImageScene.svelte.js +18 -28
- package/dist/network/states/NetworkLoader.svelte.d.ts +7 -1
- package/dist/network/states/NetworkLoader.svelte.js +17 -4
- package/dist/services/README.md +23 -0
- package/dist/services/service-base/ServiceBase.d.ts +12 -8
- package/dist/services/service-base/ServiceBase.js +8 -6
- package/dist/state/classes.d.ts +0 -2
- package/dist/state/classes.js +0 -2
- package/dist/state/{classes → machines}/finite-state-machine/FiniteStateMachine.svelte.d.ts +13 -7
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +181 -0
- package/dist/state/machines/finite-state-machine/README.md +547 -0
- package/dist/state/machines/finite-state-machine/constants.d.ts +13 -0
- package/dist/state/machines/finite-state-machine/constants.js +15 -0
- package/dist/state/{classes → machines}/finite-state-machine/index.d.ts +2 -1
- package/dist/state/{classes → machines}/finite-state-machine/index.js +2 -1
- package/dist/state/machines/finite-state-machine/typedef.d.ts +29 -0
- package/dist/state/machines/finite-state-machine/typedef.js +28 -0
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +22 -0
- package/dist/state/{classes → machines}/loading-state-machine/LoadingStateMachine.svelte.js +34 -29
- package/dist/state/machines/loading-state-machine/README.md +592 -0
- package/dist/state/{classes → machines}/loading-state-machine/constants.d.ts +2 -0
- package/dist/state/{classes → machines}/loading-state-machine/constants.js +2 -0
- package/dist/state/machines/typedef.d.ts +1 -0
- package/dist/state/machines/typedef.js +1 -0
- package/dist/state/machines.d.ts +2 -0
- package/dist/state/machines.js +2 -0
- package/dist/state/typedef.d.ts +1 -0
- package/dist/state/typedef.js +1 -0
- package/dist/ui/components/game-box/README.md +245 -0
- package/package.json +1 -1
- package/dist/logging/internal/adapters/pino.js__ +0 -260
- package/dist/state/classes/finite-state-machine/FiniteStateMachine.svelte.js +0 -133
- package/dist/state/classes/loading-state-machine/LoadingStateMachine.svelte.d.ts +0 -12
- /package/dist/state/{classes → machines}/loading-state-machine/index.d.ts +0 -0
- /package/dist/state/{classes → machines}/loading-state-machine/index.js +0 -0
|
@@ -8,13 +8,18 @@ export class PinoAdapter {
|
|
|
8
8
|
* @param {Object} [options] - Pino configuration options
|
|
9
9
|
*/
|
|
10
10
|
constructor(options?: any);
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Promise that resolves when transport is ready
|
|
13
|
+
*
|
|
14
|
+
* @returns {Promise<void>} Promise that resolves when ready
|
|
15
|
+
*/
|
|
16
|
+
ready(): Promise<void>;
|
|
12
17
|
/**
|
|
13
18
|
* Handle log events from Logger
|
|
14
19
|
*
|
|
15
20
|
* @param {Object} logEvent - Log event from Logger
|
|
16
21
|
*/
|
|
17
|
-
handleLog(logEvent: any): void
|
|
22
|
+
handleLog(logEvent: any): Promise<void>;
|
|
18
23
|
/**
|
|
19
24
|
* Create a child logger with additional context
|
|
20
25
|
*
|
|
@@ -24,4 +29,3 @@ export class PinoAdapter {
|
|
|
24
29
|
child(context: any): PinoAdapter;
|
|
25
30
|
#private;
|
|
26
31
|
}
|
|
27
|
-
import pino from 'pino';
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
import pino from 'pino';
|
|
5
5
|
import { dev } from '$app/environment';
|
|
6
6
|
|
|
7
|
+
import { HkPromise } from '../../../generic/promises.js';
|
|
8
|
+
|
|
7
9
|
import {
|
|
8
10
|
detectErrorMeta,
|
|
9
11
|
findRelevantFrameIndex,
|
|
10
|
-
formatErrorDisplay
|
|
11
|
-
parseFunctionName
|
|
12
|
+
formatErrorDisplay
|
|
12
13
|
} from './formatting.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -17,26 +18,38 @@ import {
|
|
|
17
18
|
export class PinoAdapter {
|
|
18
19
|
#projectRoot = null;
|
|
19
20
|
|
|
21
|
+
/** @type {import('pino').Logger|Object|null} */
|
|
22
|
+
#pino = null;
|
|
23
|
+
|
|
24
|
+
#options = null;
|
|
25
|
+
#messageQueue = [];
|
|
26
|
+
#isInitializing = false;
|
|
27
|
+
#isTransportReady = false;
|
|
28
|
+
#retryCount = 0;
|
|
29
|
+
#maxRetries = 3;
|
|
30
|
+
#retryDelay = 1000;
|
|
31
|
+
#readyPromise = null;
|
|
32
|
+
|
|
20
33
|
/**
|
|
21
34
|
* Create a new PinoAdapter
|
|
22
35
|
*
|
|
23
36
|
* @param {Object} [options] - Pino configuration options
|
|
24
37
|
*/
|
|
25
38
|
constructor(options = {}) {
|
|
26
|
-
// Determine project root once for stack trace cleaning
|
|
27
39
|
this.#projectRoot = import.meta.env.VITE_PROJECT_ROOT || process.cwd();
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
this.#options = options;
|
|
41
|
+
this.#readyPromise = new HkPromise();
|
|
42
|
+
}
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Get base configuration options for pino
|
|
46
|
+
*
|
|
47
|
+
* @returns {Object} Base pino configuration
|
|
48
|
+
*/
|
|
49
|
+
#getBaseOptions() {
|
|
50
|
+
return {
|
|
51
|
+
serializers: {
|
|
38
52
|
errors: (err) => {
|
|
39
|
-
|
|
40
53
|
/** @type {import('./typedef').ErrorSummary[]} */
|
|
41
54
|
const chain = [];
|
|
42
55
|
let loggedAt = null;
|
|
@@ -45,7 +58,6 @@ export class PinoAdapter {
|
|
|
45
58
|
let isFirst = true;
|
|
46
59
|
|
|
47
60
|
while (current && current instanceof Error) {
|
|
48
|
-
// Check if this is the first error and it's a LoggerError - extract logging context
|
|
49
61
|
if (isFirst && current.name === 'LoggerError') {
|
|
50
62
|
if (current.stack) {
|
|
51
63
|
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
@@ -57,24 +69,23 @@ export class PinoAdapter {
|
|
|
57
69
|
line && line !== current.name + ': ' + current.message
|
|
58
70
|
);
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
const loggerErrorIndex = cleanedStackArray.findIndex(
|
|
73
|
+
(frame) =>
|
|
74
|
+
(frame.includes('Logger.error') &&
|
|
75
|
+
frame.includes('logger/Logger.js')) ||
|
|
76
|
+
(frame.includes('error@') &&
|
|
77
|
+
frame.includes('logger/Logger.js'))
|
|
64
78
|
);
|
|
65
79
|
|
|
66
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
loggerErrorIndex >= 0 &&
|
|
82
|
+
loggerErrorIndex + 1 < cleanedStackArray.length
|
|
83
|
+
) {
|
|
67
84
|
const relevantFrame = cleanedStackArray[loggerErrorIndex + 1];
|
|
68
|
-
|
|
69
|
-
// Extract function name from the relevant frame
|
|
70
|
-
// const functionName = parseFunctionName(relevantFrame);
|
|
71
|
-
|
|
72
|
-
// const errorType = functionName ? `logger.error in ${functionName}` : 'logger.error';
|
|
73
|
-
loggedAt = relevantFrame.slice(3); // remove "at "
|
|
85
|
+
loggedAt = relevantFrame.slice(3);
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
88
|
|
|
77
|
-
// Skip the LoggerError and move to the actual error
|
|
78
89
|
current = current.cause;
|
|
79
90
|
isFirst = false;
|
|
80
91
|
continue;
|
|
@@ -85,9 +96,7 @@ export class PinoAdapter {
|
|
|
85
96
|
message: current.message
|
|
86
97
|
};
|
|
87
98
|
|
|
88
|
-
// Add error metadata for structured logging and terminal display
|
|
89
99
|
if (current.stack) {
|
|
90
|
-
// Convert cleaned stack string to array format expected by formatting functions
|
|
91
100
|
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
92
101
|
const cleanedStackArray = cleanedStackString
|
|
93
102
|
.split('\n')
|
|
@@ -106,18 +115,18 @@ export class PinoAdapter {
|
|
|
106
115
|
serialized.meta = errorMeta;
|
|
107
116
|
serialized.errorType = formatErrorDisplay(errorMeta);
|
|
108
117
|
|
|
109
|
-
// Include stack frames for terminal display
|
|
110
118
|
serialized.stackFrames = cleanedStackArray
|
|
111
119
|
.slice(0, 9)
|
|
112
120
|
.map((frame, index) => {
|
|
113
121
|
const marker = index === relevantFrameIndex ? '→' : ' ';
|
|
114
|
-
|
|
115
122
|
return `${marker} ${frame}`;
|
|
116
123
|
});
|
|
117
124
|
}
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
const httpError =
|
|
127
|
+
/** @type {import('../../../network/errors.js').HttpError} */ (
|
|
128
|
+
current
|
|
129
|
+
);
|
|
121
130
|
if (httpError.status !== undefined) {
|
|
122
131
|
serialized.status = httpError.status;
|
|
123
132
|
}
|
|
@@ -134,27 +143,50 @@ export class PinoAdapter {
|
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Initialize pino instance with retry logic for transport setup
|
|
150
|
+
*/
|
|
151
|
+
async #initializePino() {
|
|
152
|
+
if (this.#isInitializing || this.#pino) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.#isInitializing = true;
|
|
137
157
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
const baseOptions = this.#getBaseOptions();
|
|
159
|
+
|
|
160
|
+
while (this.#retryCount <= this.#maxRetries) {
|
|
161
|
+
try {
|
|
162
|
+
if (dev) {
|
|
163
|
+
// Use intermediate transport to avoid worker thread issues
|
|
164
|
+
const { default: createPrettyTransport } = await import('../transports/pretty-transport.js');
|
|
165
|
+
const prettyTransport = await createPrettyTransport({
|
|
145
166
|
colorize: true,
|
|
146
167
|
ignore: 'hostname,pid'
|
|
147
|
-
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const devOptions = {
|
|
171
|
+
level: 'debug'
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
this.#pino = pino({ ...baseOptions, ...devOptions, ...this.#options }, prettyTransport);
|
|
175
|
+
} else {
|
|
176
|
+
this.#pino = pino({ ...baseOptions, ...this.#options });
|
|
148
177
|
}
|
|
149
|
-
};
|
|
150
178
|
|
|
151
|
-
|
|
152
|
-
this
|
|
179
|
+
this.#isTransportReady = true;
|
|
180
|
+
this.#isInitializing = false;
|
|
181
|
+
this.#flushMessageQueue();
|
|
182
|
+
this.#readyPromise.tryResolve();
|
|
183
|
+
return;
|
|
184
|
+
|
|
153
185
|
} catch (error) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
186
|
+
this.#retryCount++;
|
|
187
|
+
|
|
188
|
+
if (error.message.includes('Cannot find module') &&
|
|
189
|
+
error.message.includes('pino-pretty')) {
|
|
158
190
|
const errorMessage = `
|
|
159
191
|
╭─────────────────────────────────────────────────────────────╮
|
|
160
192
|
│ Missing Dependency │
|
|
@@ -165,13 +197,82 @@ export class PinoAdapter {
|
|
|
165
197
|
console.error(errorMessage);
|
|
166
198
|
throw new Error('pino-pretty is required for development mode');
|
|
167
199
|
}
|
|
168
|
-
|
|
200
|
+
|
|
201
|
+
if (this.#retryCount > this.#maxRetries) {
|
|
202
|
+
console.error('Failed to initialize pino transport after retries:', error.message);
|
|
203
|
+
this.#fallbackToConsole();
|
|
204
|
+
this.#readyPromise.tryResolve();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await new Promise(resolve => setTimeout(resolve, this.#retryDelay * this.#retryCount));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Flush queued messages to pino transport
|
|
215
|
+
*/
|
|
216
|
+
#flushMessageQueue() {
|
|
217
|
+
if (!this.#pino || !this.#isTransportReady) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
while (this.#messageQueue.length > 0) {
|
|
222
|
+
const queuedLog = this.#messageQueue.shift();
|
|
223
|
+
try {
|
|
224
|
+
this.#pino[queuedLog.level](queuedLog.data, queuedLog.message);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('Failed to flush queued log message:', error.message);
|
|
227
|
+
this.#isTransportReady = false;
|
|
228
|
+
this.#messageQueue.unshift(queuedLog);
|
|
229
|
+
break;
|
|
169
230
|
}
|
|
170
|
-
} else {
|
|
171
|
-
this.pino = pino({ ...baseOptions, ...options });
|
|
172
231
|
}
|
|
173
232
|
}
|
|
174
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Fallback to console logging when transport fails
|
|
236
|
+
*/
|
|
237
|
+
#fallbackToConsole() {
|
|
238
|
+
this.#isTransportReady = true;
|
|
239
|
+
this.#isInitializing = false;
|
|
240
|
+
|
|
241
|
+
while (this.#messageQueue.length > 0) {
|
|
242
|
+
const queuedLog = this.#messageQueue.shift();
|
|
243
|
+
console[queuedLog.level === 'error' ? 'error' : 'log'](
|
|
244
|
+
`[${queuedLog.level.toUpperCase()}] ${queuedLog.message}`,
|
|
245
|
+
queuedLog.data
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this.#pino = {
|
|
250
|
+
debug: (data, msg) => console.log(`[DEBUG] ${msg}`, data),
|
|
251
|
+
info: (data, msg) => console.log(`[INFO] ${msg}`, data),
|
|
252
|
+
warn: (data, msg) => console.warn(`[WARN] ${msg}`, data),
|
|
253
|
+
error: (data, msg) => console.error(`[ERROR] ${msg}`, data),
|
|
254
|
+
// eslint-disable-next-line no-unused-vars
|
|
255
|
+
child: (context) => this
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Promise that resolves when transport is ready
|
|
261
|
+
*
|
|
262
|
+
* @returns {Promise<void>} Promise that resolves when ready
|
|
263
|
+
*/
|
|
264
|
+
ready() {
|
|
265
|
+
if (this.#isTransportReady) {
|
|
266
|
+
return Promise.resolve();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!this.#isInitializing) {
|
|
270
|
+
this.#initializePino();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return this.#readyPromise;
|
|
274
|
+
}
|
|
275
|
+
|
|
175
276
|
/**
|
|
176
277
|
* Clean stack trace by removing project root path and simplifying node_modules
|
|
177
278
|
*
|
|
@@ -201,8 +302,7 @@ export class PinoAdapter {
|
|
|
201
302
|
|
|
202
303
|
// Simplify pnpm paths: node_modules/.pnpm/package@version_deps/node_modules/package
|
|
203
304
|
// becomes: node_modules/package
|
|
204
|
-
const pnpmRegex =
|
|
205
|
-
/node_modules\/\.pnpm\/([^@/]+)@[^/]+\/node_modules\/\1/g;
|
|
305
|
+
const pnpmRegex = /node_modules\/\.pnpm\/([^@/]+)@[^/]+\/node_modules\/\1/g;
|
|
206
306
|
cleaned = cleaned.replace(pnpmRegex, 'node_modules/$1');
|
|
207
307
|
|
|
208
308
|
// Also handle cases where the package name might be different in the final path
|
|
@@ -211,9 +311,10 @@ export class PinoAdapter {
|
|
|
211
311
|
|
|
212
312
|
// Filter out Node.js internal modules and internal logger methods
|
|
213
313
|
const lines = cleaned.split('\n');
|
|
214
|
-
const filteredLines = lines.filter(
|
|
215
|
-
|
|
216
|
-
|
|
314
|
+
const filteredLines = lines.filter(
|
|
315
|
+
(line) =>
|
|
316
|
+
!line.includes('node:internal') &&
|
|
317
|
+
!(line.includes('#toError') && line.includes('logger/Logger.js'))
|
|
217
318
|
);
|
|
218
319
|
cleaned = filteredLines.join('\n');
|
|
219
320
|
|
|
@@ -225,7 +326,7 @@ export class PinoAdapter {
|
|
|
225
326
|
*
|
|
226
327
|
* @param {Object} logEvent - Log event from Logger
|
|
227
328
|
*/
|
|
228
|
-
handleLog(logEvent) {
|
|
329
|
+
async handleLog(logEvent) {
|
|
229
330
|
const { level, message, details, source, timestamp } = logEvent;
|
|
230
331
|
|
|
231
332
|
const logData = {
|
|
@@ -233,34 +334,58 @@ export class PinoAdapter {
|
|
|
233
334
|
timestamp
|
|
234
335
|
};
|
|
235
336
|
|
|
236
|
-
// Check if details contains an error and promote it to error property for
|
|
237
|
-
// pino serializer
|
|
238
337
|
if (details) {
|
|
239
338
|
if (details instanceof Error) {
|
|
240
|
-
// details is directly an error
|
|
241
339
|
logData.errors = details;
|
|
242
340
|
} else if (details.error instanceof Error) {
|
|
243
|
-
// details has an error property
|
|
244
341
|
logData.errors = details.error;
|
|
245
|
-
// Include other details except the error
|
|
246
342
|
// eslint-disable-next-line no-unused-vars
|
|
247
343
|
const { error, ...otherDetails } = details;
|
|
248
344
|
if (Object.keys(otherDetails).length > 0) {
|
|
249
345
|
logData.details = otherDetails;
|
|
250
346
|
}
|
|
251
347
|
} else {
|
|
252
|
-
// No error found in details, include all details
|
|
253
348
|
logData.details = details;
|
|
254
349
|
}
|
|
255
350
|
}
|
|
256
351
|
|
|
257
|
-
|
|
258
|
-
|
|
352
|
+
if (
|
|
353
|
+
logData.errors &&
|
|
354
|
+
typeof logData.errors === 'object' &&
|
|
355
|
+
logData.errors.loggedAt
|
|
356
|
+
) {
|
|
259
357
|
logData.loggedAt = logData.errors.loggedAt;
|
|
260
358
|
logData.errors = logData.errors.chain;
|
|
261
359
|
}
|
|
262
360
|
|
|
263
|
-
|
|
361
|
+
// Queue message if transport not ready
|
|
362
|
+
if (!this.#isTransportReady) {
|
|
363
|
+
this.#messageQueue.push({ level, data: logData, message });
|
|
364
|
+
|
|
365
|
+
// Limit queue size to prevent memory issues
|
|
366
|
+
if (this.#messageQueue.length > 1000) {
|
|
367
|
+
this.#messageQueue.shift();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Initialize transport if not already doing so
|
|
371
|
+
if (!this.#isInitializing) {
|
|
372
|
+
this.#initializePino();
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Try to log directly, queue on failure
|
|
378
|
+
try {
|
|
379
|
+
this.#pino[level](logData, message);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
console.error('Transport failed, queuing message:', error.message);
|
|
382
|
+
this.#isTransportReady = false;
|
|
383
|
+
this.#messageQueue.push({ level, data: logData, message });
|
|
384
|
+
|
|
385
|
+
// Retry transport initialization
|
|
386
|
+
this.#retryCount = 0;
|
|
387
|
+
setTimeout(() => this.#initializePino(), this.#retryDelay);
|
|
388
|
+
}
|
|
264
389
|
}
|
|
265
390
|
|
|
266
391
|
/**
|
|
@@ -270,9 +395,17 @@ export class PinoAdapter {
|
|
|
270
395
|
* @returns {PinoAdapter} New adapter instance with context
|
|
271
396
|
*/
|
|
272
397
|
child(context) {
|
|
273
|
-
|
|
398
|
+
if (!this.#pino) {
|
|
399
|
+
// Return new adapter that inherits parent options with context
|
|
400
|
+
return new PinoAdapter({ ...this.#options, ...context });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const childPino = this.#pino.child(context);
|
|
274
404
|
const adapter = new PinoAdapter();
|
|
275
|
-
adapter
|
|
405
|
+
adapter.#pino = childPino;
|
|
406
|
+
adapter.#isTransportReady = this.#isTransportReady;
|
|
407
|
+
adapter.#readyPromise = new HkPromise();
|
|
408
|
+
adapter.#readyPromise.tryResolve();
|
|
276
409
|
return adapter;
|
|
277
410
|
}
|
|
278
411
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom transport wrapper for pino-pretty that avoids worker threads
|
|
3
|
+
* and provides lazy loading to prevent Vite hot reload interference
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a pino-pretty transport that loads synchronously but initializes
|
|
7
|
+
* pino-pretty lazily to avoid worker thread conflicts with Vite
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} [opts] - pino-pretty options
|
|
10
|
+
* @param {boolean} [opts.colorize=true] - Enable colored output
|
|
11
|
+
* @param {string} [opts.ignore] - Fields to ignore in output
|
|
12
|
+
* @returns {Promise<Object>} Transport-compatible object
|
|
13
|
+
*/
|
|
14
|
+
export default function createPrettyTransport(opts?: {
|
|
15
|
+
colorize?: boolean;
|
|
16
|
+
ignore?: string;
|
|
17
|
+
}): Promise<any>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom transport wrapper for pino-pretty that avoids worker threads
|
|
3
|
+
* and provides lazy loading to prevent Vite hot reload interference
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a pino-pretty transport that loads synchronously but initializes
|
|
8
|
+
* pino-pretty lazily to avoid worker thread conflicts with Vite
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} [opts] - pino-pretty options
|
|
11
|
+
* @param {boolean} [opts.colorize=true] - Enable colored output
|
|
12
|
+
* @param {string} [opts.ignore] - Fields to ignore in output
|
|
13
|
+
* @returns {Promise<Object>} Transport-compatible object
|
|
14
|
+
*/
|
|
15
|
+
export default async function createPrettyTransport(opts = {}) {
|
|
16
|
+
let prettyStream = null;
|
|
17
|
+
let messageQueue = [];
|
|
18
|
+
let isInitializing = false;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize pino-pretty stream
|
|
22
|
+
*/
|
|
23
|
+
async function initializePrettyStream() {
|
|
24
|
+
if (isInitializing || prettyStream) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
isInitializing = true;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const { default: pinoPretty } = await import('pino-pretty');
|
|
32
|
+
prettyStream = pinoPretty({
|
|
33
|
+
colorize: true,
|
|
34
|
+
ignore: 'hostname,pid',
|
|
35
|
+
...opts
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Flush queued messages
|
|
39
|
+
while (messageQueue.length > 0) {
|
|
40
|
+
const chunk = messageQueue.shift();
|
|
41
|
+
prettyStream.write(chunk);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Failed to initialize pino-pretty:', error.message);
|
|
46
|
+
|
|
47
|
+
// Fallback to console output
|
|
48
|
+
prettyStream = {
|
|
49
|
+
write(chunk) {
|
|
50
|
+
try {
|
|
51
|
+
const log = JSON.parse(chunk);
|
|
52
|
+
const level = log.level >= 50 ? 'ERROR' :
|
|
53
|
+
log.level >= 40 ? 'WARN' :
|
|
54
|
+
log.level >= 30 ? 'INFO' : 'DEBUG';
|
|
55
|
+
console.log(`[${level}] ${log.msg}`, log);
|
|
56
|
+
} catch {
|
|
57
|
+
console.log(chunk);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Flush queue to console fallback
|
|
63
|
+
while (messageQueue.length > 0) {
|
|
64
|
+
const chunk = messageQueue.shift();
|
|
65
|
+
prettyStream.write(chunk);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isInitializing = false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
/**
|
|
74
|
+
* Write log message to transport
|
|
75
|
+
*
|
|
76
|
+
* @param {string} chunk - JSON log message
|
|
77
|
+
*/
|
|
78
|
+
write(chunk) {
|
|
79
|
+
if (!prettyStream) {
|
|
80
|
+
messageQueue.push(chunk);
|
|
81
|
+
|
|
82
|
+
// Limit queue size
|
|
83
|
+
if (messageQueue.length > 1000) {
|
|
84
|
+
messageQueue.shift();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Start initialization if not already started
|
|
88
|
+
if (!isInitializing) {
|
|
89
|
+
initializePrettyStream();
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
prettyStream.write(chunk);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Pretty stream write failed:', error.message);
|
|
98
|
+
messageQueue.push(chunk);
|
|
99
|
+
prettyStream = null;
|
|
100
|
+
initializePrettyStream();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test transport for validating lazy initialization behavior
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create a mock transport for testing lazy initialization scenarios
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} [opts] - Test configuration options
|
|
8
|
+
* @param {number} [opts.delay=100] - Initialization delay in milliseconds
|
|
9
|
+
* @param {boolean} [opts.shouldFail=false] - Whether initialization should fail
|
|
10
|
+
* @param {string} [opts.failureMessage] - Custom failure message
|
|
11
|
+
* @param {boolean} [opts.shouldFailOnWrite=false] - Fail on write operations
|
|
12
|
+
* @returns {Promise<Object>} Transport-compatible object
|
|
13
|
+
*/
|
|
14
|
+
export default function createTestTransport(opts?: {
|
|
15
|
+
delay?: number;
|
|
16
|
+
shouldFail?: boolean;
|
|
17
|
+
failureMessage?: string;
|
|
18
|
+
shouldFailOnWrite?: boolean;
|
|
19
|
+
}): Promise<any>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test transport for validating lazy initialization behavior
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a mock transport for testing lazy initialization scenarios
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} [opts] - Test configuration options
|
|
9
|
+
* @param {number} [opts.delay=100] - Initialization delay in milliseconds
|
|
10
|
+
* @param {boolean} [opts.shouldFail=false] - Whether initialization should fail
|
|
11
|
+
* @param {string} [opts.failureMessage] - Custom failure message
|
|
12
|
+
* @param {boolean} [opts.shouldFailOnWrite=false] - Fail on write operations
|
|
13
|
+
* @returns {Promise<Object>} Transport-compatible object
|
|
14
|
+
*/
|
|
15
|
+
export default async function createTestTransport(opts = {}) {
|
|
16
|
+
const {
|
|
17
|
+
delay = 100,
|
|
18
|
+
shouldFail = false,
|
|
19
|
+
failureMessage = 'Test transport setup failed',
|
|
20
|
+
shouldFailOnWrite = false
|
|
21
|
+
} = opts;
|
|
22
|
+
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
if (shouldFail) {
|
|
26
|
+
reject(new Error(failureMessage));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const messages = [];
|
|
31
|
+
|
|
32
|
+
resolve({
|
|
33
|
+
/**
|
|
34
|
+
* Write log message to test transport
|
|
35
|
+
*
|
|
36
|
+
* @param {string} chunk - JSON log message
|
|
37
|
+
*/
|
|
38
|
+
write(chunk) {
|
|
39
|
+
if (shouldFailOnWrite) {
|
|
40
|
+
throw new Error('Test transport write failure');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
messages.push(chunk);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get all captured messages
|
|
48
|
+
*
|
|
49
|
+
* @returns {string[]} Array of log message chunks
|
|
50
|
+
*/
|
|
51
|
+
getMessages() {
|
|
52
|
+
return [...messages];
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clear captured messages
|
|
57
|
+
*/
|
|
58
|
+
clear() {
|
|
59
|
+
messages.length = 0;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get parsed log objects from captured messages
|
|
64
|
+
*
|
|
65
|
+
* @returns {Object[]} Array of parsed log objects
|
|
66
|
+
*/
|
|
67
|
+
getParsedMessages() {
|
|
68
|
+
return messages.map(chunk => {
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(chunk);
|
|
71
|
+
} catch {
|
|
72
|
+
return { raw: chunk };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}, delay);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as expect from '../../../util/expect.js';
|
|
2
2
|
|
|
3
|
-
import { LoadingStateMachine } from '../../../state/
|
|
3
|
+
import { LoadingStateMachine } from '../../../state/machines.js';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
STATE_INITIAL,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
STATE_ERROR,
|
|
12
12
|
LOAD,
|
|
13
13
|
LOADED
|
|
14
|
-
} from '../../../state/
|
|
14
|
+
} from '../../../state/machines.js';
|
|
15
15
|
|
|
16
16
|
import AudioLoader from './AudioLoader.svelte.js';
|
|
17
17
|
|