@hkdigital/lib-core 0.4.24 → 0.4.26
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.d.ts +19 -10
- package/dist/network/loaders/audio/AudioScene.svelte.js +50 -75
- package/dist/network/loaders/image/ImageScene.svelte.d.ts +13 -13
- package/dist/network/loaders/image/ImageScene.svelte.js +56 -83
- package/dist/network/states/NetworkLoader.svelte.d.ts +6 -0
- package/dist/network/states/NetworkLoader.svelte.js +15 -6
- package/dist/services/service-base/ServiceBase.d.ts +12 -8
- package/dist/services/service-base/ServiceBase.js +8 -6
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.d.ts +5 -9
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +62 -32
- package/dist/state/machines/finite-state-machine/README.md +48 -46
- 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/machines/finite-state-machine/index.d.ts +1 -0
- package/dist/state/machines/finite-state-machine/index.js +1 -0
- package/dist/state/machines/finite-state-machine/typedef.d.ts +3 -3
- package/dist/state/machines/finite-state-machine/typedef.js +21 -15
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +12 -0
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.js +27 -2
- package/dist/state/machines/loading-state-machine/README.md +89 -41
- package/dist/state/machines/loading-state-machine/constants.d.ts +2 -0
- package/dist/state/machines/loading-state-machine/constants.js +2 -0
- package/package.json +1 -1
- package/dist/logging/internal/adapters/pino.js__ +0 -260
|
@@ -8,7 +8,7 @@ A specialized finite state machine designed for managing loading operations with
|
|
|
8
8
|
|
|
9
9
|
## States
|
|
10
10
|
|
|
11
|
-
The machine defines
|
|
11
|
+
The machine defines seven primary states:
|
|
12
12
|
|
|
13
13
|
- **`INITIAL`** - Starting state, ready to begin loading
|
|
14
14
|
- **`LOADING`** - Currently loading data or resources
|
|
@@ -16,6 +16,7 @@ The machine defines six primary states:
|
|
|
16
16
|
- **`UNLOADING`** - Cleaning up or releasing resources
|
|
17
17
|
- **`CANCELLED`** - Loading operation was cancelled
|
|
18
18
|
- **`ERROR`** - An error occurred during loading
|
|
19
|
+
- **`TIMEOUT`** - Loading operation exceeded time limit
|
|
19
20
|
|
|
20
21
|
## Events
|
|
21
22
|
|
|
@@ -26,6 +27,7 @@ Available events to trigger state transitions:
|
|
|
26
27
|
- **`UNLOAD`** - Begin cleanup/unloading
|
|
27
28
|
- **`CANCEL`** - Cancel the loading operation
|
|
28
29
|
- **`ERROR`** - Signal an error occurred
|
|
30
|
+
- **`TIMEOUT`** - Signal operation has timed out
|
|
29
31
|
- **`INITIAL`** - Return to initial state
|
|
30
32
|
|
|
31
33
|
## Basic Usage
|
|
@@ -65,35 +67,36 @@ console.log(loader.current); // STATE_LOADED
|
|
|
65
67
|
|
|
66
68
|
```
|
|
67
69
|
INITIAL → LOADING
|
|
68
|
-
LOADING → LOADED | CANCELLED | ERROR
|
|
70
|
+
LOADING → LOADED | CANCELLED | ERROR | TIMEOUT
|
|
69
71
|
LOADED → LOADING | UNLOADING
|
|
70
72
|
UNLOADING → INITIAL | ERROR
|
|
71
73
|
CANCELLED → LOADING | UNLOADING
|
|
72
74
|
ERROR → LOADING | UNLOADING
|
|
75
|
+
TIMEOUT → LOADING | UNLOADING
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
### Transition Diagram
|
|
76
79
|
|
|
77
80
|
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
┌─────────┐
|
|
82
|
+
│ INITIAL │
|
|
83
|
+
└────┬────┘
|
|
84
|
+
│ LOAD
|
|
85
|
+
▼
|
|
83
86
|
┌─────────┐ CANCEL ┌───────────┐
|
|
84
87
|
│ LOADING │──────────────→│ CANCELLED │
|
|
85
88
|
└────┬────┘ └─────┬─────┘
|
|
86
89
|
│ │
|
|
87
|
-
LOADED│ ERROR
|
|
88
|
-
│ │
|
|
89
|
-
▼ ▼
|
|
90
|
-
┌────────┐ ┌───────┐
|
|
91
|
-
│ LOADED │ │ ERROR │
|
|
92
|
-
└────┬───┘ └───┬───┘
|
|
93
|
-
│ │
|
|
94
|
-
UNLOAD│ LOAD │ UNLOAD
|
|
95
|
-
▼ ▼
|
|
96
|
-
|
|
90
|
+
LOADED│ ERROR TIMEOUT LOAD │ UNLOAD
|
|
91
|
+
│ │ │ │ │
|
|
92
|
+
▼ ▼ ▼ ▼ ▼
|
|
93
|
+
┌────────┐ ┌───────┐ ┌─────────┐ ┌──────────┐
|
|
94
|
+
│ LOADED │ │ ERROR │ │ TIMEOUT │ │UNLOADING │
|
|
95
|
+
└────┬───┘ └───┬───┘ └────┬────┘ └────┬─────┘
|
|
96
|
+
│ │ │ │
|
|
97
|
+
UNLOAD│ LOAD │ UNLOAD LOAD│ UNLOAD INITIAL│ ERROR
|
|
98
|
+
▼ ▼ ▼ ▼
|
|
99
|
+
┌──────────┐◄──────────────────────────┘
|
|
97
100
|
│UNLOADING │
|
|
98
101
|
└──────────┘
|
|
99
102
|
```
|
|
@@ -105,8 +108,8 @@ The `onenter` callback provides a reliable way to react to state changes, design
|
|
|
105
108
|
```javascript
|
|
106
109
|
const loader = new LoadingStateMachine();
|
|
107
110
|
|
|
108
|
-
loader.onenter = (
|
|
109
|
-
switch (
|
|
111
|
+
loader.onenter = (currentState) => {
|
|
112
|
+
switch (currentState) {
|
|
110
113
|
case STATE_LOADING:
|
|
111
114
|
console.log('Started loading...');
|
|
112
115
|
showSpinner();
|
|
@@ -119,6 +122,10 @@ loader.onenter = (state) => {
|
|
|
119
122
|
console.log('Loading failed');
|
|
120
123
|
showError();
|
|
121
124
|
break;
|
|
125
|
+
case STATE_TIMEOUT:
|
|
126
|
+
console.log('Loading timed out');
|
|
127
|
+
showRetryOption();
|
|
128
|
+
break;
|
|
122
129
|
}
|
|
123
130
|
};
|
|
124
131
|
```
|
|
@@ -134,8 +141,8 @@ $effect(() => {
|
|
|
134
141
|
});
|
|
135
142
|
|
|
136
143
|
// onenter catches every transition
|
|
137
|
-
loader.onenter = (
|
|
138
|
-
console.log(
|
|
144
|
+
loader.onenter = (currentState) => {
|
|
145
|
+
console.log(currentState); // Sees every state change
|
|
139
146
|
};
|
|
140
147
|
```
|
|
141
148
|
|
|
@@ -167,6 +174,42 @@ if (loader.current === 'error') {
|
|
|
167
174
|
}
|
|
168
175
|
```
|
|
169
176
|
|
|
177
|
+
## Timeout Handling
|
|
178
|
+
|
|
179
|
+
The machine supports timeout functionality for managing loading operations that exceed expected duration:
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const loader = new LoadingStateMachine();
|
|
183
|
+
|
|
184
|
+
// External timeout management
|
|
185
|
+
const timeoutId = setTimeout(() => {
|
|
186
|
+
if (loader.current === STATE_LOADING) {
|
|
187
|
+
loader.doTimeout(); // Transitions to STATE_TIMEOUT
|
|
188
|
+
}
|
|
189
|
+
}, 10000); // 10 second timeout
|
|
190
|
+
|
|
191
|
+
loader.onenter = (currentState) => {
|
|
192
|
+
switch (currentState) {
|
|
193
|
+
case STATE_TIMEOUT:
|
|
194
|
+
console.log('Loading timed out');
|
|
195
|
+
showRetryButton();
|
|
196
|
+
break;
|
|
197
|
+
case STATE_LOADED:
|
|
198
|
+
clearTimeout(timeoutId); // Cancel timeout on success
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### doTimeout Method
|
|
205
|
+
|
|
206
|
+
Use `doTimeout()` to manually trigger a timeout transition:
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Only works when in STATE_LOADING
|
|
210
|
+
loader.doTimeout(); // Sends TIMEOUT signal
|
|
211
|
+
```
|
|
212
|
+
|
|
170
213
|
## Integration with Svelte Reactivity
|
|
171
214
|
|
|
172
215
|
LoadingStateMachine inherits the `onenter` callback and Svelte reactivity integration patterns from [FiniteStateMachine](../finite-state-machine/README.md#integration-with-svelte-reactivity).
|
|
@@ -177,8 +220,8 @@ LoadingStateMachine inherits the `onenter` callback and Svelte reactivity integr
|
|
|
177
220
|
```javascript
|
|
178
221
|
const loader = new LoadingStateMachine();
|
|
179
222
|
|
|
180
|
-
loader.onenter = (
|
|
181
|
-
switch (
|
|
223
|
+
loader.onenter = (currentState) => {
|
|
224
|
+
switch (currentState) {
|
|
182
225
|
case STATE_LOADING:
|
|
183
226
|
this.#startLoading(); // Start async process immediately
|
|
184
227
|
break;
|
|
@@ -224,8 +267,8 @@ export default class MultiSourceLoader {
|
|
|
224
267
|
|
|
225
268
|
constructor() {
|
|
226
269
|
// Handle immediate loading state actions
|
|
227
|
-
this.#loader.onenter = (
|
|
228
|
-
switch (
|
|
270
|
+
this.#loader.onenter = (currentState) => {
|
|
271
|
+
switch (currentState) {
|
|
229
272
|
case STATE_LOADING:
|
|
230
273
|
this.#startAllSources();
|
|
231
274
|
break;
|
|
@@ -235,8 +278,11 @@ export default class MultiSourceLoader {
|
|
|
235
278
|
case STATE_ERROR:
|
|
236
279
|
this.#handleLoadError();
|
|
237
280
|
break;
|
|
281
|
+
case STATE_TIMEOUT:
|
|
282
|
+
this.#handleTimeout();
|
|
283
|
+
break;
|
|
238
284
|
}
|
|
239
|
-
this.state =
|
|
285
|
+
this.state = currentState;
|
|
240
286
|
};
|
|
241
287
|
|
|
242
288
|
// Monitor reactive completion
|
|
@@ -273,8 +319,8 @@ export default class MultiSourceLoader {
|
|
|
273
319
|
const loader = new LoadingStateMachine();
|
|
274
320
|
let data = $state(null);
|
|
275
321
|
|
|
276
|
-
loader.onenter = (
|
|
277
|
-
if (
|
|
322
|
+
loader.onenter = (currentState) => {
|
|
323
|
+
if (currentState === STATE_LOADING) {
|
|
278
324
|
loadData();
|
|
279
325
|
}
|
|
280
326
|
};
|
|
@@ -325,8 +371,8 @@ export default class MultiSourceLoader {
|
|
|
325
371
|
const loader = new LoadingStateMachine();
|
|
326
372
|
let abortController = null;
|
|
327
373
|
|
|
328
|
-
loader.onenter = (
|
|
329
|
-
switch (
|
|
374
|
+
loader.onenter = (currentState) => {
|
|
375
|
+
switch (currentState) {
|
|
330
376
|
case STATE_LOADING:
|
|
331
377
|
startLoad();
|
|
332
378
|
break;
|
|
@@ -380,8 +426,8 @@ export default class MultiSourceLoader {
|
|
|
380
426
|
|
|
381
427
|
```javascript
|
|
382
428
|
// ✅ Good - use onenter for reliable side effects
|
|
383
|
-
loader.onenter = (
|
|
384
|
-
if (
|
|
429
|
+
loader.onenter = (currentState) => {
|
|
430
|
+
if (currentState === STATE_LOADING) {
|
|
385
431
|
analytics.track('loading_started');
|
|
386
432
|
}
|
|
387
433
|
};
|
|
@@ -397,8 +443,8 @@ $effect(() => {
|
|
|
397
443
|
### 2. Handle All Error States
|
|
398
444
|
|
|
399
445
|
```javascript
|
|
400
|
-
loader.onenter = (
|
|
401
|
-
switch (
|
|
446
|
+
loader.onenter = (currentState) => {
|
|
447
|
+
switch (currentState) {
|
|
402
448
|
case STATE_ERROR:
|
|
403
449
|
showErrorToast(loader.error.message);
|
|
404
450
|
logError(loader.error);
|
|
@@ -413,8 +459,8 @@ loader.onenter = (state) => {
|
|
|
413
459
|
### 3. Implement Proper Cleanup
|
|
414
460
|
|
|
415
461
|
```javascript
|
|
416
|
-
loader.onenter = (
|
|
417
|
-
switch (
|
|
462
|
+
loader.onenter = (currentState) => {
|
|
463
|
+
switch (currentState) {
|
|
418
464
|
case STATE_LOADING:
|
|
419
465
|
showProgressBar();
|
|
420
466
|
break;
|
|
@@ -454,8 +500,8 @@ loader.send('load');
|
|
|
454
500
|
const resourceLoader = new LoadingStateMachine();
|
|
455
501
|
let resource = null;
|
|
456
502
|
|
|
457
|
-
resourceLoader.onenter = async (
|
|
458
|
-
switch (
|
|
503
|
+
resourceLoader.onenter = async (currentState) => {
|
|
504
|
+
switch (currentState) {
|
|
459
505
|
case STATE_LOADING:
|
|
460
506
|
try {
|
|
461
507
|
resource = await loadResource();
|
|
@@ -483,8 +529,8 @@ const retryLoader = new LoadingStateMachine();
|
|
|
483
529
|
let retryCount = 0;
|
|
484
530
|
const maxRetries = 3;
|
|
485
531
|
|
|
486
|
-
retryLoader.onenter = async (
|
|
487
|
-
switch (
|
|
532
|
+
retryLoader.onenter = async (currentState) => {
|
|
533
|
+
switch (currentState) {
|
|
488
534
|
case STATE_LOADING:
|
|
489
535
|
try {
|
|
490
536
|
await performLoad();
|
|
@@ -532,6 +578,7 @@ import {
|
|
|
532
578
|
STATE_UNLOADING,
|
|
533
579
|
STATE_CANCELLED,
|
|
534
580
|
STATE_ERROR,
|
|
581
|
+
STATE_TIMEOUT,
|
|
535
582
|
|
|
536
583
|
// Events
|
|
537
584
|
LOAD,
|
|
@@ -539,6 +586,7 @@ import {
|
|
|
539
586
|
UNLOAD,
|
|
540
587
|
CANCEL,
|
|
541
588
|
ERROR,
|
|
542
|
-
INITIAL
|
|
589
|
+
INITIAL,
|
|
590
|
+
TIMEOUT
|
|
543
591
|
} from './constants.js';
|
|
544
592
|
```
|
|
@@ -4,9 +4,11 @@ export const STATE_UNLOADING: "unloading";
|
|
|
4
4
|
export const STATE_LOADED: "loaded";
|
|
5
5
|
export const STATE_CANCELLED: "cancelled";
|
|
6
6
|
export const STATE_ERROR: "error";
|
|
7
|
+
export const STATE_TIMEOUT: "timeout";
|
|
7
8
|
export const INITIAL: "initial";
|
|
8
9
|
export const LOAD: "load";
|
|
9
10
|
export const CANCEL: "cancel";
|
|
10
11
|
export const ERROR: "error";
|
|
11
12
|
export const LOADED: "loaded";
|
|
12
13
|
export const UNLOAD: "unload";
|
|
14
|
+
export const TIMEOUT: "timeout";
|
|
@@ -5,6 +5,7 @@ export const STATE_LOADED = 'loaded';
|
|
|
5
5
|
|
|
6
6
|
export const STATE_CANCELLED = 'cancelled';
|
|
7
7
|
export const STATE_ERROR = 'error';
|
|
8
|
+
export const STATE_TIMEOUT = 'timeout';
|
|
8
9
|
|
|
9
10
|
// > Signals
|
|
10
11
|
|
|
@@ -14,3 +15,4 @@ export const CANCEL = 'cancel';
|
|
|
14
15
|
export const ERROR = 'error';
|
|
15
16
|
export const LOADED = 'loaded';
|
|
16
17
|
export const UNLOAD = 'unload';
|
|
18
|
+
export const TIMEOUT = 'timeout';
|
package/package.json
CHANGED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pino adapter for server-side logging
|
|
3
|
-
*/
|
|
4
|
-
import pino from 'pino';
|
|
5
|
-
import { dev } from '$app/environment';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
detectErrorMeta,
|
|
9
|
-
findRelevantFrameIndex,
|
|
10
|
-
formatErrorDisplay
|
|
11
|
-
} from './formatting.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Pino adapter that bridges Logger events to pino
|
|
15
|
-
*/
|
|
16
|
-
export class PinoAdapter {
|
|
17
|
-
#projectRoot = null;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a new PinoAdapter
|
|
21
|
-
*
|
|
22
|
-
* @param {Object} [options] - Pino configuration options
|
|
23
|
-
*/
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
// Determine project root once for stack trace cleaning
|
|
26
|
-
this.#projectRoot = import.meta.env.VITE_PROJECT_ROOT || process.cwd();
|
|
27
|
-
const baseOptions = {
|
|
28
|
-
serializers: {
|
|
29
|
-
errors: (err) => {
|
|
30
|
-
|
|
31
|
-
/** @type {import('./typedef').ErrorSummary[]} */
|
|
32
|
-
const chain = [];
|
|
33
|
-
let loggedAt = null;
|
|
34
|
-
|
|
35
|
-
let current = err;
|
|
36
|
-
let isFirst = true;
|
|
37
|
-
|
|
38
|
-
while (current && current instanceof Error) {
|
|
39
|
-
// Check if this is the first error and it's a LoggerError - extract logging context
|
|
40
|
-
if (isFirst && current.name === 'LoggerError') {
|
|
41
|
-
if (current.stack) {
|
|
42
|
-
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
43
|
-
const cleanedStackArray = cleanedStackString
|
|
44
|
-
.split('\n')
|
|
45
|
-
.map((line) => line.trim())
|
|
46
|
-
.filter(
|
|
47
|
-
(line) =>
|
|
48
|
-
line && line !== current.name + ': ' + current.message
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// For LoggerError, we know it's a logger.error call, so find the relevant frame
|
|
52
|
-
const loggerErrorIndex = cleanedStackArray.findIndex(frame =>
|
|
53
|
-
(frame.includes('Logger.error') && frame.includes('logger/Logger.js')) ||
|
|
54
|
-
(frame.includes('error@') && frame.includes('logger/Logger.js'))
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (loggerErrorIndex >= 0 && loggerErrorIndex + 1 < cleanedStackArray.length) {
|
|
58
|
-
const relevantFrame = cleanedStackArray[loggerErrorIndex + 1];
|
|
59
|
-
|
|
60
|
-
// Extract function name from the relevant frame
|
|
61
|
-
// const functionName = parseFunctionName(relevantFrame);
|
|
62
|
-
|
|
63
|
-
// const errorType = functionName ? `logger.error in ${functionName}` : 'logger.error';
|
|
64
|
-
loggedAt = relevantFrame.slice(3); // remove "at "
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Skip the LoggerError and move to the actual error
|
|
69
|
-
current = current.cause;
|
|
70
|
-
isFirst = false;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
/** @type {import('./typedef').ErrorSummary} */
|
|
74
|
-
const serialized = {
|
|
75
|
-
name: current.name,
|
|
76
|
-
message: current.message
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Add error metadata for structured logging and terminal display
|
|
80
|
-
if (current.stack) {
|
|
81
|
-
// Convert cleaned stack string to array format expected by formatting functions
|
|
82
|
-
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
83
|
-
const cleanedStackArray = cleanedStackString
|
|
84
|
-
.split('\n')
|
|
85
|
-
.map((line) => line.trim())
|
|
86
|
-
.filter(
|
|
87
|
-
(line) =>
|
|
88
|
-
line && line !== current.name + ': ' + current.message
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const errorMeta = detectErrorMeta(current, cleanedStackArray);
|
|
92
|
-
const relevantFrameIndex = findRelevantFrameIndex(
|
|
93
|
-
current,
|
|
94
|
-
cleanedStackArray
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
serialized.meta = errorMeta;
|
|
98
|
-
serialized.errorType = formatErrorDisplay(errorMeta);
|
|
99
|
-
|
|
100
|
-
// Include stack frames for terminal display
|
|
101
|
-
serialized.stackFrames = cleanedStackArray
|
|
102
|
-
.slice(0, 9)
|
|
103
|
-
.map((frame, index) => {
|
|
104
|
-
const marker = index === relevantFrameIndex ? '→' : ' ';
|
|
105
|
-
|
|
106
|
-
return `${marker} ${frame}`;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Include HttpError-specific properties
|
|
111
|
-
const httpError = /** @type {import('$lib/network/errors.js').HttpError} */ (current);
|
|
112
|
-
if (httpError.status !== undefined) {
|
|
113
|
-
serialized.status = httpError.status;
|
|
114
|
-
}
|
|
115
|
-
if (httpError.details !== undefined) {
|
|
116
|
-
serialized.details = httpError.details;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
chain.push(serialized);
|
|
120
|
-
current = current.cause;
|
|
121
|
-
isFirst = false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return loggedAt ? { chain, loggedAt } : chain;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Add error handling for missing pino-pretty in dev
|
|
130
|
-
if ( dev) {
|
|
131
|
-
const devOptions = {
|
|
132
|
-
level: 'debug',
|
|
133
|
-
transport: {
|
|
134
|
-
target: 'pino-pretty',
|
|
135
|
-
options: {
|
|
136
|
-
colorize: true,
|
|
137
|
-
ignore: 'hostname,pid'
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
this.pino = pino({ ...baseOptions, ...devOptions, ...options });
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (
|
|
146
|
-
error.message.includes('Cannot find module') &&
|
|
147
|
-
error.message.includes('pino-pretty')
|
|
148
|
-
) {
|
|
149
|
-
const errorMessage = `
|
|
150
|
-
╭─────────────────────────────────────────────────────────────╮
|
|
151
|
-
│ Missing Dependency │
|
|
152
|
-
├─────────────────────────────────────────────────────────────┤
|
|
153
|
-
│ 'pino-pretty' is required for development logging │
|
|
154
|
-
│ Install it with: pnpm add -D pino-pretty │
|
|
155
|
-
╰─────────────────────────────────────────────────────────────╯`;
|
|
156
|
-
console.error(errorMessage);
|
|
157
|
-
throw new Error('pino-pretty is required for development mode');
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
this.pino = pino({ ...baseOptions, ...options });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Clean stack trace by removing project root path and simplifying node_modules
|
|
168
|
-
*
|
|
169
|
-
* @param {string} stack - Original stack trace
|
|
170
|
-
* @returns {string} Cleaned stack trace
|
|
171
|
-
*/
|
|
172
|
-
#cleanStackTrace(stack) {
|
|
173
|
-
if (!stack || !this.#projectRoot) {
|
|
174
|
-
return stack;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let cleaned = stack;
|
|
178
|
-
|
|
179
|
-
// Escape special regex characters in the project root path
|
|
180
|
-
const escapedRoot = this.#projectRoot.replace(
|
|
181
|
-
/[.*+?^${}()|[\]\\]/g,
|
|
182
|
-
'\\$&'
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Replace project root path with relative path, handling file:// protocol
|
|
186
|
-
// Match both regular paths and file:// URLs
|
|
187
|
-
const rootRegex = new RegExp(
|
|
188
|
-
`(\\s+at\\s+.*\\()(file://)?${escapedRoot}[\\/\\\\]`,
|
|
189
|
-
'g'
|
|
190
|
-
);
|
|
191
|
-
cleaned = cleaned.replace(rootRegex, '$1');
|
|
192
|
-
|
|
193
|
-
// Simplify pnpm paths: node_modules/.pnpm/package@version_deps/node_modules/package
|
|
194
|
-
// becomes: node_modules/package
|
|
195
|
-
const pnpmRegex =
|
|
196
|
-
/node_modules\/\.pnpm\/([^@\/]+)@[^\/]+\/node_modules\/\1/g;
|
|
197
|
-
cleaned = cleaned.replace(pnpmRegex, 'node_modules/$1');
|
|
198
|
-
|
|
199
|
-
// Also handle cases where the package name might be different in the final path
|
|
200
|
-
const pnpmRegex2 = /node_modules\/\.pnpm\/[^\/]+\/node_modules\/([^\/]+)/g;
|
|
201
|
-
cleaned = cleaned.replace(pnpmRegex2, 'node_modules/$1');
|
|
202
|
-
|
|
203
|
-
return cleaned;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Handle log events from Logger
|
|
208
|
-
*
|
|
209
|
-
* @param {Object} logEvent - Log event from Logger
|
|
210
|
-
*/
|
|
211
|
-
handleLog(logEvent) {
|
|
212
|
-
const { level, message, details, source, timestamp } = logEvent;
|
|
213
|
-
|
|
214
|
-
const logData = {
|
|
215
|
-
source,
|
|
216
|
-
timestamp
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// Check if details contains an error and promote it to err property for pino serializer
|
|
220
|
-
if (details) {
|
|
221
|
-
if (details instanceof Error) {
|
|
222
|
-
// details is directly an error
|
|
223
|
-
logData.err = details;
|
|
224
|
-
} else if (details.error instanceof Error) {
|
|
225
|
-
// details has an error property
|
|
226
|
-
logData.err = details.error;
|
|
227
|
-
// Include other details except the error
|
|
228
|
-
// eslint-disable-next-line no-unused-vars
|
|
229
|
-
const { error, ...otherDetails } = details;
|
|
230
|
-
if (Object.keys(otherDetails).length > 0) {
|
|
231
|
-
logData.details = otherDetails;
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
// No error found in details, include all details
|
|
235
|
-
logData.details = details;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Check if we have loggedAt info from the serializer
|
|
240
|
-
if (logData.err && typeof logData.err === 'object' && logData.err.loggedAt) {
|
|
241
|
-
logData.loggedAt = logData.err.loggedAt;
|
|
242
|
-
logData.err = logData.err.chain;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.pino[level](logData, message);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Create a child logger with additional context
|
|
250
|
-
*
|
|
251
|
-
* @param {Object} context - Additional context data
|
|
252
|
-
* @returns {PinoAdapter} New adapter instance with context
|
|
253
|
-
*/
|
|
254
|
-
child(context) {
|
|
255
|
-
const childPino = this.pino.child(context);
|
|
256
|
-
const adapter = new PinoAdapter();
|
|
257
|
-
adapter.pino = childPino;
|
|
258
|
-
return adapter;
|
|
259
|
-
}
|
|
260
|
-
}
|