@qlover/scripts-context 1.2.3 → 2.0.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/dist/index.cjs +4 -21
- package/dist/index.d.ts +1544 -275
- package/dist/index.js +110 -2481
- package/package.json +7 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { LoggerInterface, FormatterInterface, LogEvent } from '@qlover/logger';
|
|
1
|
+
import { UserConfig } from '@commitlint/types';
|
|
3
2
|
import { Env } from '@qlover/env-loader';
|
|
4
|
-
import {
|
|
3
|
+
import { ExecutorContextImpl, LifecyclePluginInterface, ExecutorContextInterface, ExecutorError } from '@qlover/fe-corekit';
|
|
5
4
|
|
|
6
5
|
declare const defaultFeConfig: FeConfig;
|
|
7
|
-
|
|
6
|
+
interface FeConfig {
|
|
8
7
|
/**
|
|
9
8
|
* Run `fe-clean-branch` to exclude branches
|
|
10
9
|
*
|
|
@@ -40,7 +39,7 @@ type FeConfig = {
|
|
|
40
39
|
*
|
|
41
40
|
* @default { "extends": ["@commitlint/config-conventional"] }
|
|
42
41
|
*/
|
|
43
|
-
commitlint?:
|
|
42
|
+
commitlint?: UserConfig;
|
|
44
43
|
/**
|
|
45
44
|
* config of CI release
|
|
46
45
|
*
|
|
@@ -51,7 +50,24 @@ type FeConfig = {
|
|
|
51
50
|
* @default ['.env.local', '.env']
|
|
52
51
|
*/
|
|
53
52
|
envOrder?: string[];
|
|
54
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Allow additional custom properties for script-specific configurations
|
|
55
|
+
*
|
|
56
|
+
* This enables users to add custom configuration properties for their
|
|
57
|
+
* specific scripts without modifying the core FeConfig type definition.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const config: FeConfig = {
|
|
62
|
+
* protectedBranches: ['main'],
|
|
63
|
+
* 'my-script': {
|
|
64
|
+
* customOption: 'value'
|
|
65
|
+
* }
|
|
66
|
+
* };
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
[key: string]: unknown;
|
|
70
|
+
}
|
|
55
71
|
/**
|
|
56
72
|
* Configuration interface for automated release process management
|
|
57
73
|
*
|
|
@@ -343,205 +359,1535 @@ interface FeReleaseConfig {
|
|
|
343
359
|
* - `${changelog}`: Generated changelog content
|
|
344
360
|
* - `${pkgName}`: Package name
|
|
345
361
|
*
|
|
346
|
-
* @optional
|
|
347
|
-
* @default Comprehensive PR body template with changelog
|
|
348
|
-
* @example Simple PR body
|
|
362
|
+
* @optional
|
|
363
|
+
* @default Comprehensive PR body template with changelog
|
|
364
|
+
* @example Simple PR body
|
|
365
|
+
* ```typescript
|
|
366
|
+
* const config: FeReleaseConfig = {
|
|
367
|
+
* PRBody: 'Release ${pkgName} v${tagName}\n\n${changelog}'
|
|
368
|
+
* };
|
|
369
|
+
* ```
|
|
370
|
+
*
|
|
371
|
+
* @example Detailed PR body
|
|
372
|
+
* ```typescript
|
|
373
|
+
* const config: FeReleaseConfig = {
|
|
374
|
+
* PRBody: `
|
|
375
|
+
* ## Release ${pkgName} v${tagName}
|
|
376
|
+
*
|
|
377
|
+
* **Environment:** ${env}
|
|
378
|
+
* **Branch:** ${branch}
|
|
379
|
+
*
|
|
380
|
+
* ### Changes
|
|
381
|
+
* ${changelog}
|
|
382
|
+
*
|
|
383
|
+
* ### Checklist
|
|
384
|
+
* - [ ] Version number is correct
|
|
385
|
+
* - [ ] All tests pass
|
|
386
|
+
* - [ ] Documentation updated
|
|
387
|
+
* `
|
|
388
|
+
* };
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
PRBody?: string;
|
|
392
|
+
/**
|
|
393
|
+
* Configuration for release pull request labels
|
|
394
|
+
*
|
|
395
|
+
* Core concept:
|
|
396
|
+
* Defines the label configuration for release pull requests,
|
|
397
|
+
* enabling automated categorization and visual identification
|
|
398
|
+
* of release-related PRs.
|
|
399
|
+
*
|
|
400
|
+
* Label features:
|
|
401
|
+
* - Automated label application
|
|
402
|
+
* - Customizable label appearance
|
|
403
|
+
* - Consistent release identification
|
|
404
|
+
* - Integration with GitHub labeling system
|
|
405
|
+
* - Support for custom label descriptions
|
|
406
|
+
*
|
|
407
|
+
* Label properties:
|
|
408
|
+
* - name: Label identifier and display name
|
|
409
|
+
* - color: Hexadecimal color code for visual distinction
|
|
410
|
+
* - description: Label description for documentation
|
|
411
|
+
*
|
|
412
|
+
* @optional
|
|
413
|
+
* @example Basic label configuration
|
|
414
|
+
* ```typescript
|
|
415
|
+
* const config: FeReleaseConfig = {
|
|
416
|
+
* label: {
|
|
417
|
+
* name: 'release',
|
|
418
|
+
* color: '1A7F37',
|
|
419
|
+
* description: 'Automated release PR'
|
|
420
|
+
* }
|
|
421
|
+
* };
|
|
422
|
+
* ```
|
|
423
|
+
*
|
|
424
|
+
* @example Custom label
|
|
425
|
+
* ```typescript
|
|
426
|
+
* const config: FeReleaseConfig = {
|
|
427
|
+
* label: {
|
|
428
|
+
* name: 'CI-Release',
|
|
429
|
+
* color: '0366D6',
|
|
430
|
+
* description: 'Release created by CI/CD'
|
|
431
|
+
* }
|
|
432
|
+
* };
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
label?: {
|
|
436
|
+
/**
|
|
437
|
+
* Hexadecimal color code for label appearance
|
|
438
|
+
*
|
|
439
|
+
* Color format: 6-character hex string without '#'
|
|
440
|
+
* Used for visual distinction in GitHub interface
|
|
441
|
+
* Supports standard web color codes
|
|
442
|
+
*
|
|
443
|
+
* @optional
|
|
444
|
+
* @default `'1A7F37'`
|
|
445
|
+
* @example Green color
|
|
446
|
+
* ```typescript
|
|
447
|
+
* color: '1A7F37'
|
|
448
|
+
* ```
|
|
449
|
+
*
|
|
450
|
+
* @example Blue color
|
|
451
|
+
* ```typescript
|
|
452
|
+
* color: '0366D6'
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
color?: string;
|
|
456
|
+
/**
|
|
457
|
+
* Descriptive text for label documentation
|
|
458
|
+
*
|
|
459
|
+
* Provides context about the label's purpose
|
|
460
|
+
* Used in GitHub label management interface
|
|
461
|
+
* Helps team members understand label usage
|
|
462
|
+
*
|
|
463
|
+
* @optional
|
|
464
|
+
* @default `'Release PR'`
|
|
465
|
+
* @example
|
|
466
|
+
* ```typescript
|
|
467
|
+
* description: 'Automated release pull request'
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
description?: string;
|
|
471
|
+
/**
|
|
472
|
+
* Label name for identification and display
|
|
473
|
+
*
|
|
474
|
+
* Used as the primary identifier for the label
|
|
475
|
+
* Displayed in GitHub PR interface
|
|
476
|
+
* Should be descriptive and consistent
|
|
477
|
+
*
|
|
478
|
+
* @optional
|
|
479
|
+
* @default `'CI-Release'`
|
|
480
|
+
* @example
|
|
481
|
+
* ```typescript
|
|
482
|
+
* name: 'release'
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
name?: string;
|
|
486
|
+
};
|
|
487
|
+
/**
|
|
488
|
+
* Template for package change labels in monorepos
|
|
489
|
+
*
|
|
490
|
+
* Core concept:
|
|
491
|
+
* Defines the naming pattern for labels that identify
|
|
492
|
+
* which packages have changed in monorepo releases,
|
|
493
|
+
* enabling targeted review and deployment.
|
|
494
|
+
*
|
|
495
|
+
* Label usage:
|
|
496
|
+
* - Applied to PRs when specific packages change
|
|
497
|
+
* - Enables package-specific review processes
|
|
498
|
+
* - Supports selective deployment strategies
|
|
499
|
+
* - Improves monorepo change tracking
|
|
500
|
+
* - Facilitates team collaboration and review
|
|
501
|
+
*
|
|
502
|
+
* Template variables:
|
|
503
|
+
* - `${name}`: Package name for label identification
|
|
504
|
+
*
|
|
505
|
+
* @optional
|
|
506
|
+
* @default `'changes:${name}'`
|
|
507
|
+
* @example Basic change label
|
|
508
|
+
* ```typescript
|
|
509
|
+
* const config: FeReleaseConfig = {
|
|
510
|
+
* changePackagesLabel: 'changes:${name}'
|
|
511
|
+
* };
|
|
512
|
+
* ```
|
|
513
|
+
*
|
|
514
|
+
* @example Custom change label
|
|
515
|
+
* ```typescript
|
|
516
|
+
* const config: FeReleaseConfig = {
|
|
517
|
+
* changePackagesLabel: 'package:${name}'
|
|
518
|
+
* };
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
changePackagesLabel?: string;
|
|
522
|
+
/**
|
|
523
|
+
* Directories containing packages for monorepo releases
|
|
524
|
+
*
|
|
525
|
+
* Core concept:
|
|
526
|
+
* Specifies the directories that contain packages for
|
|
527
|
+
* monorepo release management, enabling selective
|
|
528
|
+
* package discovery and release coordination.
|
|
529
|
+
*
|
|
530
|
+
* Directory patterns:
|
|
531
|
+
* - Supports glob patterns for flexible matching
|
|
532
|
+
* - Enables selective package inclusion
|
|
533
|
+
* - Supports nested directory structures
|
|
534
|
+
* - Facilitates monorepo organization
|
|
535
|
+
* - Enables workspace-specific configurations
|
|
536
|
+
*
|
|
537
|
+
* Use cases:
|
|
538
|
+
* - Monorepo package discovery
|
|
539
|
+
* - Selective package releases
|
|
540
|
+
* - Workspace-specific configurations
|
|
541
|
+
* - Multi-package coordination
|
|
542
|
+
* - Dependency-aware releases
|
|
543
|
+
*
|
|
544
|
+
* @optional
|
|
545
|
+
* @default `[]`
|
|
546
|
+
* @example Basic package directories
|
|
547
|
+
* ```typescript
|
|
548
|
+
* const config: FeReleaseConfig = {
|
|
549
|
+
* packagesDirectories: ['packages/*']
|
|
550
|
+
* };
|
|
551
|
+
* ```
|
|
552
|
+
*
|
|
553
|
+
* @example Multiple package directories
|
|
554
|
+
* ```typescript
|
|
555
|
+
* const config: FeReleaseConfig = {
|
|
556
|
+
* packagesDirectories: ['packages/*', 'apps/*', 'libs/*']
|
|
557
|
+
* };
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
packagesDirectories?: string[];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Represents a log event in the logging system
|
|
565
|
+
*
|
|
566
|
+
* This class encapsulates all information about a single log event,
|
|
567
|
+
* including the log level, message arguments, timestamp, logger name,
|
|
568
|
+
* and optional typed context data.
|
|
569
|
+
*
|
|
570
|
+
* Core features:
|
|
571
|
+
* - Automatic timestamp generation
|
|
572
|
+
* - Type-safe context support
|
|
573
|
+
* - Flexible argument handling
|
|
574
|
+
* - Logger identification
|
|
575
|
+
*
|
|
576
|
+
* @typeParam Ctx - Type of the context value, defaults to unknown
|
|
577
|
+
*
|
|
578
|
+
* @example Basic usage
|
|
579
|
+
* ```typescript
|
|
580
|
+
* const event = new LogEvent(
|
|
581
|
+
* 'info',
|
|
582
|
+
* ['Application started'],
|
|
583
|
+
* 'app'
|
|
584
|
+
* );
|
|
585
|
+
* // event.timestamp is automatically set
|
|
586
|
+
* console.log(event);
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @example With context
|
|
590
|
+
* ```typescript
|
|
591
|
+
* interface RequestContext {
|
|
592
|
+
* requestId: string;
|
|
593
|
+
* userId: string;
|
|
594
|
+
* path: string;
|
|
595
|
+
* }
|
|
596
|
+
*
|
|
597
|
+
* const event = new LogEvent<RequestContext>(
|
|
598
|
+
* 'info',
|
|
599
|
+
* ['Request processed', { duration: 150 }],
|
|
600
|
+
* 'http',
|
|
601
|
+
* new LogContext({
|
|
602
|
+
* requestId: 'req-123',
|
|
603
|
+
* userId: 'user-456',
|
|
604
|
+
* path: '/api/users'
|
|
605
|
+
* })
|
|
606
|
+
* );
|
|
607
|
+
* ```
|
|
608
|
+
*
|
|
609
|
+
* @example Error event
|
|
610
|
+
* ```typescript
|
|
611
|
+
* const error = new Error('Database connection failed');
|
|
612
|
+
* const event = new LogEvent(
|
|
613
|
+
* 'error',
|
|
614
|
+
* [
|
|
615
|
+
* 'Failed to connect to database',
|
|
616
|
+
* {
|
|
617
|
+
* error: error.message,
|
|
618
|
+
* stack: error.stack,
|
|
619
|
+
* code: 'DB_CONN_ERROR'
|
|
620
|
+
* }
|
|
621
|
+
* ],
|
|
622
|
+
* 'database'
|
|
623
|
+
* );
|
|
624
|
+
* ```
|
|
625
|
+
*
|
|
626
|
+
* @example Performance monitoring
|
|
627
|
+
* ```typescript
|
|
628
|
+
* interface PerformanceContext {
|
|
629
|
+
* operation: string;
|
|
630
|
+
* startTime: number;
|
|
631
|
+
* endTime: number;
|
|
632
|
+
* metadata: Record<string, unknown>;
|
|
633
|
+
* }
|
|
634
|
+
*
|
|
635
|
+
* const event = new LogEvent<PerformanceContext>(
|
|
636
|
+
* 'debug',
|
|
637
|
+
* ['Operation completed'],
|
|
638
|
+
* 'performance',
|
|
639
|
+
* new LogContext({
|
|
640
|
+
* operation: 'data-processing',
|
|
641
|
+
* startTime: 1679395845000,
|
|
642
|
+
* endTime: 1679395846000,
|
|
643
|
+
* metadata: {
|
|
644
|
+
* itemsProcessed: 1000,
|
|
645
|
+
* batchSize: 100,
|
|
646
|
+
* cacheHits: 850
|
|
647
|
+
* }
|
|
648
|
+
* })
|
|
649
|
+
* );
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
declare class LogEvent<Ctx = unknown> {
|
|
653
|
+
/**
|
|
654
|
+
* Log level indicating the severity or importance
|
|
655
|
+
*
|
|
656
|
+
* Common values:
|
|
657
|
+
* - 'fatal': System is unusable
|
|
658
|
+
* - 'error': Error conditions
|
|
659
|
+
* - 'warn': Warning conditions
|
|
660
|
+
* - 'info': Informational messages
|
|
661
|
+
* - 'debug': Debug-level messages
|
|
662
|
+
* - 'trace': Trace-level messages
|
|
663
|
+
*
|
|
664
|
+
* @example
|
|
665
|
+
* ```typescript
|
|
666
|
+
* new LogEvent('error', ['Database connection failed'], 'db');
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
level: string;
|
|
670
|
+
/**
|
|
671
|
+
* Array of log message arguments
|
|
672
|
+
*
|
|
673
|
+
* The first argument is typically the main message string,
|
|
674
|
+
* followed by optional data objects, error instances, or
|
|
675
|
+
* other relevant information.
|
|
676
|
+
*
|
|
677
|
+
* @example
|
|
678
|
+
* ```typescript
|
|
679
|
+
* // Simple message
|
|
680
|
+
* new LogEvent('info', ['User logged in'], 'auth');
|
|
681
|
+
*
|
|
682
|
+
* // Message with data
|
|
683
|
+
* new LogEvent('info', [
|
|
684
|
+
* 'User logged in',
|
|
685
|
+
* { userId: 123, role: 'admin' }
|
|
686
|
+
* ], 'auth');
|
|
687
|
+
*
|
|
688
|
+
* // Error with details
|
|
689
|
+
* new LogEvent('error', [
|
|
690
|
+
* 'Operation failed',
|
|
691
|
+
* new Error('Invalid input'),
|
|
692
|
+
* { operation: 'validate', input: data }
|
|
693
|
+
* ], 'validator');
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
args: unknown[];
|
|
697
|
+
/**
|
|
698
|
+
* Name of the logger that created this event
|
|
699
|
+
*
|
|
700
|
+
* Used to identify the source or category of the log event.
|
|
701
|
+
* This can be a module name, service name, or any other
|
|
702
|
+
* identifier that helps categorize log events.
|
|
703
|
+
*
|
|
704
|
+
* @example
|
|
705
|
+
* ```typescript
|
|
706
|
+
* // Module-based names
|
|
707
|
+
* new LogEvent('info', ['Starting'], 'auth.service');
|
|
708
|
+
* new LogEvent('debug', ['Cache miss'], 'data.cache');
|
|
709
|
+
*
|
|
710
|
+
* // Component-based names
|
|
711
|
+
* new LogEvent('info', ['Render complete'], 'ui.dashboard');
|
|
712
|
+
* new LogEvent('error', ['API error'], 'api.users');
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
715
|
+
loggerName: string;
|
|
716
|
+
/**
|
|
717
|
+
* Optional typed context data for the log event
|
|
718
|
+
*
|
|
719
|
+
* Provides additional structured data that can be used by
|
|
720
|
+
* formatters and handlers. The context type is controlled
|
|
721
|
+
* by the Ctx type parameter.
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* ```typescript
|
|
725
|
+
* // Request context
|
|
726
|
+
* interface RequestContext {
|
|
727
|
+
* requestId: string;
|
|
728
|
+
* path: string;
|
|
729
|
+
* method: string;
|
|
730
|
+
* }
|
|
731
|
+
*
|
|
732
|
+
* new LogEvent<RequestContext>(
|
|
733
|
+
* 'info',
|
|
734
|
+
* ['Request received'],
|
|
735
|
+
* 'http',
|
|
736
|
+
* new LogContext({
|
|
737
|
+
* requestId: 'req-123',
|
|
738
|
+
* path: '/api/users',
|
|
739
|
+
* method: 'GET'
|
|
740
|
+
* })
|
|
741
|
+
* );
|
|
742
|
+
*
|
|
743
|
+
* // Performance context
|
|
744
|
+
* interface PerfContext {
|
|
745
|
+
* duration: number;
|
|
746
|
+
* memoryUsage: number;
|
|
747
|
+
* }
|
|
748
|
+
*
|
|
749
|
+
* new LogEvent<PerfContext>(
|
|
750
|
+
* 'debug',
|
|
751
|
+
* ['Operation completed'],
|
|
752
|
+
* 'perf',
|
|
753
|
+
* new LogContext({
|
|
754
|
+
* duration: 150,
|
|
755
|
+
* memoryUsage: process.memoryUsage().heapUsed
|
|
756
|
+
* })
|
|
757
|
+
* );
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
context?: Ctx | undefined;
|
|
761
|
+
/**
|
|
762
|
+
* Timestamp when the log event was created
|
|
763
|
+
*
|
|
764
|
+
* Automatically set to the current time in milliseconds since the Unix epoch
|
|
765
|
+
* when the event is constructed. This ensures accurate timing information
|
|
766
|
+
* for each log entry.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```typescript
|
|
770
|
+
* const event = new LogEvent('info', ['message'], 'app');
|
|
771
|
+
* console.log(new Date(event.timestamp).toISOString());
|
|
772
|
+
* // Output: "2024-03-21T14:30:45.123Z"
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
timestamp: number;
|
|
776
|
+
constructor(
|
|
777
|
+
/**
|
|
778
|
+
* Log level indicating the severity or importance
|
|
779
|
+
*
|
|
780
|
+
* Common values:
|
|
781
|
+
* - 'fatal': System is unusable
|
|
782
|
+
* - 'error': Error conditions
|
|
783
|
+
* - 'warn': Warning conditions
|
|
784
|
+
* - 'info': Informational messages
|
|
785
|
+
* - 'debug': Debug-level messages
|
|
786
|
+
* - 'trace': Trace-level messages
|
|
787
|
+
*
|
|
788
|
+
* @example
|
|
789
|
+
* ```typescript
|
|
790
|
+
* new LogEvent('error', ['Database connection failed'], 'db');
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
level: string,
|
|
794
|
+
/**
|
|
795
|
+
* Array of log message arguments
|
|
796
|
+
*
|
|
797
|
+
* The first argument is typically the main message string,
|
|
798
|
+
* followed by optional data objects, error instances, or
|
|
799
|
+
* other relevant information.
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```typescript
|
|
803
|
+
* // Simple message
|
|
804
|
+
* new LogEvent('info', ['User logged in'], 'auth');
|
|
805
|
+
*
|
|
806
|
+
* // Message with data
|
|
807
|
+
* new LogEvent('info', [
|
|
808
|
+
* 'User logged in',
|
|
809
|
+
* { userId: 123, role: 'admin' }
|
|
810
|
+
* ], 'auth');
|
|
811
|
+
*
|
|
812
|
+
* // Error with details
|
|
813
|
+
* new LogEvent('error', [
|
|
814
|
+
* 'Operation failed',
|
|
815
|
+
* new Error('Invalid input'),
|
|
816
|
+
* { operation: 'validate', input: data }
|
|
817
|
+
* ], 'validator');
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
args: unknown[],
|
|
821
|
+
/**
|
|
822
|
+
* Name of the logger that created this event
|
|
823
|
+
*
|
|
824
|
+
* Used to identify the source or category of the log event.
|
|
825
|
+
* This can be a module name, service name, or any other
|
|
826
|
+
* identifier that helps categorize log events.
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* ```typescript
|
|
830
|
+
* // Module-based names
|
|
831
|
+
* new LogEvent('info', ['Starting'], 'auth.service');
|
|
832
|
+
* new LogEvent('debug', ['Cache miss'], 'data.cache');
|
|
833
|
+
*
|
|
834
|
+
* // Component-based names
|
|
835
|
+
* new LogEvent('info', ['Render complete'], 'ui.dashboard');
|
|
836
|
+
* new LogEvent('error', ['API error'], 'api.users');
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
loggerName: string,
|
|
840
|
+
/**
|
|
841
|
+
* Optional typed context data for the log event
|
|
842
|
+
*
|
|
843
|
+
* Provides additional structured data that can be used by
|
|
844
|
+
* formatters and handlers. The context type is controlled
|
|
845
|
+
* by the Ctx type parameter.
|
|
846
|
+
*
|
|
847
|
+
* @example
|
|
848
|
+
* ```typescript
|
|
849
|
+
* // Request context
|
|
850
|
+
* interface RequestContext {
|
|
851
|
+
* requestId: string;
|
|
852
|
+
* path: string;
|
|
853
|
+
* method: string;
|
|
854
|
+
* }
|
|
855
|
+
*
|
|
856
|
+
* new LogEvent<RequestContext>(
|
|
857
|
+
* 'info',
|
|
858
|
+
* ['Request received'],
|
|
859
|
+
* 'http',
|
|
860
|
+
* new LogContext({
|
|
861
|
+
* requestId: 'req-123',
|
|
862
|
+
* path: '/api/users',
|
|
863
|
+
* method: 'GET'
|
|
864
|
+
* })
|
|
865
|
+
* );
|
|
866
|
+
*
|
|
867
|
+
* // Performance context
|
|
868
|
+
* interface PerfContext {
|
|
869
|
+
* duration: number;
|
|
870
|
+
* memoryUsage: number;
|
|
871
|
+
* }
|
|
872
|
+
*
|
|
873
|
+
* new LogEvent<PerfContext>(
|
|
874
|
+
* 'debug',
|
|
875
|
+
* ['Operation completed'],
|
|
876
|
+
* 'perf',
|
|
877
|
+
* new LogContext({
|
|
878
|
+
* duration: 150,
|
|
879
|
+
* memoryUsage: process.memoryUsage().heapUsed
|
|
880
|
+
* })
|
|
881
|
+
* );
|
|
882
|
+
* ```
|
|
883
|
+
*/
|
|
884
|
+
context?: Ctx | undefined);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Interface for log event formatters
|
|
889
|
+
*
|
|
890
|
+
* Formatters are responsible for converting log events into specific output
|
|
891
|
+
* formats. They can transform the event data into strings, objects, or arrays
|
|
892
|
+
* suitable for the target output medium.
|
|
893
|
+
*
|
|
894
|
+
* Core responsibilities:
|
|
895
|
+
* - Format log events into desired output format
|
|
896
|
+
* - Support type-safe context handling
|
|
897
|
+
* - Maintain consistent formatting across log entries
|
|
898
|
+
* - Handle different log levels appropriately
|
|
899
|
+
*
|
|
900
|
+
* @typeParam Ctx - Type of the context value, defaults to unknown
|
|
901
|
+
*
|
|
902
|
+
* @example Basic string formatter
|
|
903
|
+
* ```typescript
|
|
904
|
+
* class SimpleFormatter implements FormatterInterface {
|
|
905
|
+
* format(event: LogEvent): string[] {
|
|
906
|
+
* const timestamp = new Date(event.timestamp).toISOString();
|
|
907
|
+
* const level = event.level.toUpperCase();
|
|
908
|
+
* const message = event.args.join(' ');
|
|
909
|
+
*
|
|
910
|
+
* return [`[${timestamp}] ${level}: ${message}`];
|
|
911
|
+
* }
|
|
912
|
+
* }
|
|
913
|
+
* ```
|
|
914
|
+
*
|
|
915
|
+
* @example JSON formatter
|
|
916
|
+
* ```typescript
|
|
917
|
+
* class JSONFormatter implements FormatterInterface {
|
|
918
|
+
* format(event: LogEvent): string {
|
|
919
|
+
* return JSON.stringify({
|
|
920
|
+
* timestamp: event.timestamp,
|
|
921
|
+
* level: event.level,
|
|
922
|
+
* message: event.args[0],
|
|
923
|
+
* data: event.args.slice(1),
|
|
924
|
+
* context: event.context?.value
|
|
925
|
+
* });
|
|
926
|
+
* }
|
|
927
|
+
* }
|
|
928
|
+
* ```
|
|
929
|
+
*
|
|
930
|
+
* @example Type-safe context formatter
|
|
931
|
+
* ```typescript
|
|
932
|
+
* interface RequestContext {
|
|
933
|
+
* requestId: string;
|
|
934
|
+
* userId?: string;
|
|
935
|
+
* path: string;
|
|
936
|
+
* method: string;
|
|
937
|
+
* }
|
|
938
|
+
*
|
|
939
|
+
* class RequestFormatter implements FormatterInterface<RequestContext> {
|
|
940
|
+
* format(event: LogEvent<RequestContext>): string[] {
|
|
941
|
+
* const ctx = event.context?.value;
|
|
942
|
+
* const prefix = ctx
|
|
943
|
+
* ? `[${ctx.method} ${ctx.path}] [${ctx.requestId}]`
|
|
944
|
+
* : '';
|
|
945
|
+
*
|
|
946
|
+
* return [
|
|
947
|
+
* prefix,
|
|
948
|
+
* event.level.toUpperCase(),
|
|
949
|
+
* ...event.args
|
|
950
|
+
* ];
|
|
951
|
+
* }
|
|
952
|
+
* }
|
|
953
|
+
* ```
|
|
954
|
+
*
|
|
955
|
+
* @example Colored console formatter
|
|
956
|
+
* ```typescript
|
|
957
|
+
* class ColoredFormatter implements FormatterInterface {
|
|
958
|
+
* private colors = {
|
|
959
|
+
* error: '\x1b[31m', // Red
|
|
960
|
+
* warn: '\x1b[33m', // Yellow
|
|
961
|
+
* info: '\x1b[36m', // Cyan
|
|
962
|
+
* debug: '\x1b[90m', // Gray
|
|
963
|
+
* reset: '\x1b[0m' // Reset
|
|
964
|
+
* };
|
|
965
|
+
*
|
|
966
|
+
* format(event: LogEvent): unknown[] {
|
|
967
|
+
* const color = this.colors[event.level] || this.colors.reset;
|
|
968
|
+
* const timestamp = new Date(event.timestamp).toISOString();
|
|
969
|
+
*
|
|
970
|
+
* return [
|
|
971
|
+
* `${color}[${timestamp}] ${event.level.toUpperCase()}:${this.colors.reset}`,
|
|
972
|
+
* ...event.args
|
|
973
|
+
* ];
|
|
974
|
+
* }
|
|
975
|
+
* }
|
|
976
|
+
* ```
|
|
977
|
+
*/
|
|
978
|
+
interface FormatterInterface<Ctx = unknown> {
|
|
979
|
+
/**
|
|
980
|
+
* Formats a log event into the desired output format
|
|
981
|
+
*
|
|
982
|
+
* This method transforms a log event into a format suitable for output.
|
|
983
|
+
* The return value can be:
|
|
984
|
+
* - A single value (string, object, etc.)
|
|
985
|
+
* - An array of values (for handlers that support multiple arguments)
|
|
986
|
+
*
|
|
987
|
+
* Implementation considerations:
|
|
988
|
+
* - Handle all log levels consistently
|
|
989
|
+
* - Process context data appropriately
|
|
990
|
+
* - Maintain type safety with generic context
|
|
991
|
+
* - Consider performance for high-volume logging
|
|
992
|
+
*
|
|
993
|
+
* @param event - The log event to format
|
|
994
|
+
* @returns Formatted output as a single value or array of values
|
|
995
|
+
*
|
|
996
|
+
* @example String output
|
|
997
|
+
* ```typescript
|
|
998
|
+
* format(event: LogEvent): string {
|
|
999
|
+
* const { timestamp, level, args } = event;
|
|
1000
|
+
* return `[${new Date(timestamp).toISOString()}] ${
|
|
1001
|
+
* level.toUpperCase()
|
|
1002
|
+
* }: ${args.join(' ')}`;
|
|
1003
|
+
* }
|
|
1004
|
+
* ```
|
|
1005
|
+
*
|
|
1006
|
+
* @example Array output for console
|
|
1007
|
+
* ```typescript
|
|
1008
|
+
* format(event: LogEvent): unknown[] {
|
|
1009
|
+
* const prefix = `[${event.level.toUpperCase()}]`;
|
|
1010
|
+
* return [prefix, ...event.args];
|
|
1011
|
+
* }
|
|
1012
|
+
* ```
|
|
1013
|
+
*
|
|
1014
|
+
* @example Structured output
|
|
1015
|
+
* ```typescript
|
|
1016
|
+
* format(event: LogEvent): object {
|
|
1017
|
+
* return {
|
|
1018
|
+
* '@timestamp': event.timestamp,
|
|
1019
|
+
* level: event.level,
|
|
1020
|
+
* message: event.args[0],
|
|
1021
|
+
* metadata: {
|
|
1022
|
+
* args: event.args.slice(1),
|
|
1023
|
+
* context: event.context?.value
|
|
1024
|
+
* }
|
|
1025
|
+
* };
|
|
1026
|
+
* }
|
|
1027
|
+
* ```
|
|
1028
|
+
*
|
|
1029
|
+
* @example Type-safe context handling
|
|
1030
|
+
* ```typescript
|
|
1031
|
+
* interface UserContext {
|
|
1032
|
+
* userId: string;
|
|
1033
|
+
* sessionId: string;
|
|
1034
|
+
* }
|
|
1035
|
+
*
|
|
1036
|
+
* format(event: LogEvent<UserContext>): object {
|
|
1037
|
+
* const ctx = event.context?.value;
|
|
1038
|
+
* return {
|
|
1039
|
+
* timestamp: event.timestamp,
|
|
1040
|
+
* level: event.level,
|
|
1041
|
+
* message: event.args[0],
|
|
1042
|
+
* user: ctx ? {
|
|
1043
|
+
* id: ctx.userId,
|
|
1044
|
+
* session: ctx.sessionId
|
|
1045
|
+
* } : undefined
|
|
1046
|
+
* };
|
|
1047
|
+
* }
|
|
1048
|
+
* ```
|
|
1049
|
+
*/
|
|
1050
|
+
format(event: LogEvent<Ctx>): unknown | unknown[];
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Interface for log event handlers (appenders)
|
|
1055
|
+
*
|
|
1056
|
+
* Handlers are responsible for processing and outputting log events to
|
|
1057
|
+
* various destinations (console, file, network, etc.). They can optionally
|
|
1058
|
+
* use formatters to customize the output format.
|
|
1059
|
+
*
|
|
1060
|
+
* Core responsibilities:
|
|
1061
|
+
* - Process log events
|
|
1062
|
+
* - Output logs to specific destinations
|
|
1063
|
+
* - Support optional formatting
|
|
1064
|
+
* - Handle different log levels
|
|
1065
|
+
*
|
|
1066
|
+
* @example Basic console handler
|
|
1067
|
+
* ```typescript
|
|
1068
|
+
* class ConsoleHandler implements HandlerInterface {
|
|
1069
|
+
* private formatter: FormatterInterface | null = null;
|
|
1070
|
+
*
|
|
1071
|
+
* append(event: LogEvent): void {
|
|
1072
|
+
* const args = this.formatter
|
|
1073
|
+
* ? this.formatter.format(event)
|
|
1074
|
+
* : event.args;
|
|
1075
|
+
*
|
|
1076
|
+
* console.log(...args);
|
|
1077
|
+
* }
|
|
1078
|
+
*
|
|
1079
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1080
|
+
* this.formatter = formatter;
|
|
1081
|
+
* }
|
|
1082
|
+
* }
|
|
1083
|
+
* ```
|
|
1084
|
+
*
|
|
1085
|
+
* @example File handler with rotation
|
|
1086
|
+
* ```typescript
|
|
1087
|
+
* class RotatingFileHandler implements HandlerInterface {
|
|
1088
|
+
* constructor(
|
|
1089
|
+
* private filename: string,
|
|
1090
|
+
* private maxSize: number = 10 * 1024 * 1024
|
|
1091
|
+
* ) {}
|
|
1092
|
+
*
|
|
1093
|
+
* append(event: LogEvent): void {
|
|
1094
|
+
* const message = this.formatter
|
|
1095
|
+
* ? this.formatter.format(event)
|
|
1096
|
+
* : event.args.join(' ');
|
|
1097
|
+
*
|
|
1098
|
+
* if (this.shouldRotate()) {
|
|
1099
|
+
* this.rotate();
|
|
1100
|
+
* }
|
|
1101
|
+
* fs.appendFileSync(this.filename, message + '\n');
|
|
1102
|
+
* }
|
|
1103
|
+
*
|
|
1104
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1105
|
+
* this.formatter = formatter;
|
|
1106
|
+
* }
|
|
1107
|
+
*
|
|
1108
|
+
* private shouldRotate(): boolean {
|
|
1109
|
+
* return fs.statSync(this.filename).size > this.maxSize;
|
|
1110
|
+
* }
|
|
1111
|
+
*
|
|
1112
|
+
* private rotate(): void {
|
|
1113
|
+
* const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1114
|
+
* fs.renameSync(
|
|
1115
|
+
* this.filename,
|
|
1116
|
+
* `${this.filename}.${timestamp}.backup`
|
|
1117
|
+
* );
|
|
1118
|
+
* }
|
|
1119
|
+
* }
|
|
1120
|
+
* ```
|
|
1121
|
+
*
|
|
1122
|
+
* @example Network handler with buffering
|
|
1123
|
+
* ```typescript
|
|
1124
|
+
* class NetworkHandler implements HandlerInterface {
|
|
1125
|
+
* private buffer: LogEvent[] = [];
|
|
1126
|
+
* private timer: NodeJS.Timer | null = null;
|
|
1127
|
+
*
|
|
1128
|
+
* constructor(
|
|
1129
|
+
* private endpoint: string,
|
|
1130
|
+
* private batchSize: number = 100,
|
|
1131
|
+
* private flushInterval: number = 5000
|
|
1132
|
+
* ) {
|
|
1133
|
+
* this.startTimer();
|
|
1134
|
+
* }
|
|
1135
|
+
*
|
|
1136
|
+
* append(event: LogEvent): void {
|
|
1137
|
+
* this.buffer.push(event);
|
|
1138
|
+
* if (this.buffer.length >= this.batchSize) {
|
|
1139
|
+
* this.flush();
|
|
1140
|
+
* }
|
|
1141
|
+
* }
|
|
1142
|
+
*
|
|
1143
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1144
|
+
* this.formatter = formatter;
|
|
1145
|
+
* }
|
|
1146
|
+
*
|
|
1147
|
+
* private async flush(): Promise<void> {
|
|
1148
|
+
* if (this.buffer.length === 0) return;
|
|
1149
|
+
*
|
|
1150
|
+
* const events = this.buffer.map(event => ({
|
|
1151
|
+
* level: event.level,
|
|
1152
|
+
* message: this.formatter
|
|
1153
|
+
* ? this.formatter.format(event)
|
|
1154
|
+
* : event.args.join(' '),
|
|
1155
|
+
* timestamp: event.timestamp
|
|
1156
|
+
* }));
|
|
1157
|
+
*
|
|
1158
|
+
* try {
|
|
1159
|
+
* await fetch(this.endpoint, {
|
|
1160
|
+
* method: 'POST',
|
|
1161
|
+
* body: JSON.stringify(events)
|
|
1162
|
+
* });
|
|
1163
|
+
* this.buffer = [];
|
|
1164
|
+
* } catch (error) {
|
|
1165
|
+
* console.error('Failed to send logs:', error);
|
|
1166
|
+
* }
|
|
1167
|
+
* }
|
|
1168
|
+
*
|
|
1169
|
+
* private startTimer(): void {
|
|
1170
|
+
* this.timer = setInterval(() => this.flush(), this.flushInterval);
|
|
1171
|
+
* }
|
|
1172
|
+
* }
|
|
1173
|
+
* ```
|
|
1174
|
+
*/
|
|
1175
|
+
interface HandlerInterface<Ctx = unknown> {
|
|
1176
|
+
/**
|
|
1177
|
+
* Processes and outputs a log event
|
|
1178
|
+
*
|
|
1179
|
+
* This method is called by the logger for each log event that needs
|
|
1180
|
+
* to be processed. The handler can format the event using its formatter
|
|
1181
|
+
* (if set) and output it to its destination.
|
|
1182
|
+
*
|
|
1183
|
+
* Implementation considerations:
|
|
1184
|
+
* - Handle all log levels appropriately
|
|
1185
|
+
* - Apply formatting if a formatter is set
|
|
1186
|
+
* - Handle errors gracefully
|
|
1187
|
+
* - Consider performance implications
|
|
1188
|
+
*
|
|
1189
|
+
* @param event - The log event to process and output
|
|
1190
|
+
*
|
|
1191
|
+
* @example Basic implementation
|
|
1192
|
+
* ```typescript
|
|
1193
|
+
* append(event: LogEvent): void {
|
|
1194
|
+
* const output = this.formatter
|
|
1195
|
+
* ? this.formatter.format(event)
|
|
1196
|
+
* : event.args;
|
|
1197
|
+
*
|
|
1198
|
+
* console.log(...output);
|
|
1199
|
+
* }
|
|
1200
|
+
* ```
|
|
1201
|
+
*
|
|
1202
|
+
* @example Advanced implementation with error handling
|
|
1203
|
+
* ```typescript
|
|
1204
|
+
* append(event: LogEvent): void {
|
|
1205
|
+
* try {
|
|
1206
|
+
* // Format the event
|
|
1207
|
+
* const formatted = this.formatter
|
|
1208
|
+
* ? this.formatter.format(event)
|
|
1209
|
+
* : event.args;
|
|
1210
|
+
*
|
|
1211
|
+
* // Add timestamp and level
|
|
1212
|
+
* const output = [
|
|
1213
|
+
* new Date(event.timestamp).toISOString(),
|
|
1214
|
+
* event.level.toUpperCase(),
|
|
1215
|
+
* ...formatted
|
|
1216
|
+
* ];
|
|
1217
|
+
*
|
|
1218
|
+
* // Write to file
|
|
1219
|
+
* fs.appendFileSync(this.filename, output.join(' ') + '\n');
|
|
1220
|
+
* } catch (error) {
|
|
1221
|
+
* console.error('Failed to append log:', error);
|
|
1222
|
+
* // Optionally write to fallback location
|
|
1223
|
+
* fs.appendFileSync(
|
|
1224
|
+
* 'error.log',
|
|
1225
|
+
* `Failed to write log: ${error.message}\n`
|
|
1226
|
+
* );
|
|
1227
|
+
* }
|
|
1228
|
+
* }
|
|
1229
|
+
* ```
|
|
1230
|
+
*
|
|
1231
|
+
* @example Asynchronous implementation
|
|
1232
|
+
* ```typescript
|
|
1233
|
+
* async append(event: LogEvent): Promise<void> {
|
|
1234
|
+
* // Format the event
|
|
1235
|
+
* const message = this.formatter
|
|
1236
|
+
* ? this.formatter.format(event)
|
|
1237
|
+
* : event.args.join(' ');
|
|
1238
|
+
*
|
|
1239
|
+
* // Prepare log entry
|
|
1240
|
+
* const entry = {
|
|
1241
|
+
* timestamp: event.timestamp,
|
|
1242
|
+
* level: event.level,
|
|
1243
|
+
* message,
|
|
1244
|
+
* metadata: event.context?.value
|
|
1245
|
+
* };
|
|
1246
|
+
*
|
|
1247
|
+
* try {
|
|
1248
|
+
* // Send to logging service
|
|
1249
|
+
* await fetch('https://logging.example.com/ingest', {
|
|
1250
|
+
* method: 'POST',
|
|
1251
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
1252
|
+
* body: JSON.stringify(entry)
|
|
1253
|
+
* });
|
|
1254
|
+
* } catch (error) {
|
|
1255
|
+
* // Handle failure
|
|
1256
|
+
* console.error('Failed to send log:', error);
|
|
1257
|
+
* // Add to retry queue
|
|
1258
|
+
* this.retryQueue.push(entry);
|
|
1259
|
+
* }
|
|
1260
|
+
* }
|
|
1261
|
+
* ```
|
|
1262
|
+
*/
|
|
1263
|
+
append(event: LogEvent<Ctx>): void;
|
|
1264
|
+
/**
|
|
1265
|
+
* Sets or updates the formatter for this handler
|
|
1266
|
+
*
|
|
1267
|
+
* The formatter is responsible for converting log events into
|
|
1268
|
+
* the desired output format. This method allows changing the
|
|
1269
|
+
* formatter at runtime.
|
|
1270
|
+
*
|
|
1271
|
+
* Implementation considerations:
|
|
1272
|
+
* - Handle null formatters gracefully
|
|
1273
|
+
* - Consider thread safety in async environments
|
|
1274
|
+
* - Clean up old formatter resources if necessary
|
|
1275
|
+
*
|
|
1276
|
+
* @param formatter - The formatter instance to use
|
|
1277
|
+
*
|
|
1278
|
+
* @example Basic implementation
|
|
1279
|
+
* ```typescript
|
|
1280
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1281
|
+
* this.formatter = formatter;
|
|
1282
|
+
* }
|
|
1283
|
+
* ```
|
|
1284
|
+
*
|
|
1285
|
+
* @example Implementation with cleanup
|
|
1286
|
+
* ```typescript
|
|
1287
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1288
|
+
* if (this.formatter && 'dispose' in this.formatter) {
|
|
1289
|
+
* // Clean up old formatter resources
|
|
1290
|
+
* this.formatter.dispose();
|
|
1291
|
+
* }
|
|
1292
|
+
* this.formatter = formatter;
|
|
1293
|
+
* }
|
|
1294
|
+
* ```
|
|
1295
|
+
*
|
|
1296
|
+
* @example Thread-safe implementation
|
|
1297
|
+
* ```typescript
|
|
1298
|
+
* setFormatter(formatter: FormatterInterface): void {
|
|
1299
|
+
* // Ensure atomic formatter update
|
|
1300
|
+
* this.lock.acquire();
|
|
1301
|
+
* try {
|
|
1302
|
+
* const oldFormatter = this.formatter;
|
|
1303
|
+
* this.formatter = formatter;
|
|
1304
|
+
*
|
|
1305
|
+
* // Flush any pending logs with old formatter
|
|
1306
|
+
* if (oldFormatter) {
|
|
1307
|
+
* this.flushBuffer(oldFormatter);
|
|
1308
|
+
* }
|
|
1309
|
+
* } finally {
|
|
1310
|
+
* this.lock.release();
|
|
1311
|
+
* }
|
|
1312
|
+
* }
|
|
1313
|
+
* ```
|
|
1314
|
+
*/
|
|
1315
|
+
setFormatter(formatter: FormatterInterface): void;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Type-safe container for log event context data
|
|
1320
|
+
*
|
|
1321
|
+
* This class provides a generic wrapper for additional context data
|
|
1322
|
+
* that can be attached to log events. It ensures type safety through
|
|
1323
|
+
* its generic type parameter and provides a consistent structure for
|
|
1324
|
+
* context handling.
|
|
1325
|
+
*
|
|
1326
|
+
* Core features:
|
|
1327
|
+
* - Generic type support
|
|
1328
|
+
* - Optional value handling
|
|
1329
|
+
* - Immutable context data
|
|
1330
|
+
* - Type-safe access
|
|
1331
|
+
*
|
|
1332
|
+
* @typeParam Value - Type of the context value
|
|
1333
|
+
*
|
|
1334
|
+
* @example Basic usage
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* // Simple context
|
|
1337
|
+
* const context = new LogContext({ userId: 123 });
|
|
1338
|
+
* logger.info('User action', context);
|
|
1339
|
+
* ```
|
|
1340
|
+
*
|
|
1341
|
+
* @example Type-safe context
|
|
1342
|
+
* ```typescript
|
|
1343
|
+
* interface RequestContext {
|
|
1344
|
+
* requestId: string;
|
|
1345
|
+
* path: string;
|
|
1346
|
+
* method: string;
|
|
1347
|
+
* userId?: string;
|
|
1348
|
+
* }
|
|
1349
|
+
*
|
|
1350
|
+
* const context = new LogContext<RequestContext>({
|
|
1351
|
+
* requestId: 'req-123',
|
|
1352
|
+
* path: '/api/users',
|
|
1353
|
+
* method: 'GET'
|
|
1354
|
+
* });
|
|
1355
|
+
*
|
|
1356
|
+
* logger.info('Request received', context);
|
|
1357
|
+
* ```
|
|
1358
|
+
*
|
|
1359
|
+
* @example Optional context
|
|
1360
|
+
* ```typescript
|
|
1361
|
+
* // Empty context
|
|
1362
|
+
* const emptyContext = new LogContext();
|
|
1363
|
+
*
|
|
1364
|
+
* // Context with undefined value
|
|
1365
|
+
* const context = new LogContext<string | undefined>(undefined);
|
|
1366
|
+
*
|
|
1367
|
+
* // Context with null value
|
|
1368
|
+
* const nullContext = new LogContext<string | null>(null);
|
|
1369
|
+
* ```
|
|
1370
|
+
*
|
|
1371
|
+
* @example Complex context
|
|
1372
|
+
* ```typescript
|
|
1373
|
+
* interface PerformanceContext {
|
|
1374
|
+
* operation: string;
|
|
1375
|
+
* metrics: {
|
|
1376
|
+
* duration: number;
|
|
1377
|
+
* memory: {
|
|
1378
|
+
* before: number;
|
|
1379
|
+
* after: number;
|
|
1380
|
+
* };
|
|
1381
|
+
* cpu: {
|
|
1382
|
+
* user: number;
|
|
1383
|
+
* system: number;
|
|
1384
|
+
* };
|
|
1385
|
+
* };
|
|
1386
|
+
* metadata: Record<string, unknown>;
|
|
1387
|
+
* }
|
|
1388
|
+
*
|
|
1389
|
+
* const context = new LogContext<PerformanceContext>({
|
|
1390
|
+
* operation: 'data-processing',
|
|
1391
|
+
* metrics: {
|
|
1392
|
+
* duration: 1500,
|
|
1393
|
+
* memory: {
|
|
1394
|
+
* before: 100000,
|
|
1395
|
+
* after: 150000
|
|
1396
|
+
* },
|
|
1397
|
+
* cpu: {
|
|
1398
|
+
* user: 80,
|
|
1399
|
+
* system: 20
|
|
1400
|
+
* }
|
|
1401
|
+
* },
|
|
1402
|
+
* metadata: {
|
|
1403
|
+
* batchSize: 1000,
|
|
1404
|
+
* compression: true,
|
|
1405
|
+
* priority: 'high'
|
|
1406
|
+
* }
|
|
1407
|
+
* });
|
|
1408
|
+
*
|
|
1409
|
+
* logger.debug('Operation metrics', context);
|
|
1410
|
+
* ```
|
|
1411
|
+
*
|
|
1412
|
+
* @example Context in formatters
|
|
1413
|
+
* ```typescript
|
|
1414
|
+
* class DetailedFormatter implements FormatterInterface<RequestContext> {
|
|
1415
|
+
* format(event: LogEvent<RequestContext>): string[] {
|
|
1416
|
+
* const ctx = event.context?.value;
|
|
1417
|
+
* if (ctx) {
|
|
1418
|
+
* return [
|
|
1419
|
+
* `[${ctx.method} ${ctx.path}]`,
|
|
1420
|
+
* `[${ctx.requestId}]`,
|
|
1421
|
+
* ...event.args
|
|
1422
|
+
* ];
|
|
1423
|
+
* }
|
|
1424
|
+
* return event.args;
|
|
1425
|
+
* }
|
|
1426
|
+
* }
|
|
1427
|
+
* ```
|
|
1428
|
+
*/
|
|
1429
|
+
declare class LogContext<Value> {
|
|
1430
|
+
/**
|
|
1431
|
+
* The context value that will be attached to log events
|
|
1432
|
+
*
|
|
1433
|
+
* This value is typed according to the Value type parameter
|
|
1434
|
+
* and can be accessed safely through optional chaining.
|
|
1435
|
+
*/
|
|
1436
|
+
value?: Value | undefined;
|
|
1437
|
+
/**
|
|
1438
|
+
* Creates a new LogContext instance
|
|
1439
|
+
*
|
|
1440
|
+
* @param value - Optional context value of type Value
|
|
1441
|
+
*
|
|
1442
|
+
* The value parameter is marked as optional to support:
|
|
1443
|
+
* - Empty contexts (no value provided)
|
|
1444
|
+
* - Explicitly undefined values
|
|
1445
|
+
* - Nullable types (Value | null)
|
|
1446
|
+
*
|
|
1447
|
+
* The value is stored as a public property to allow:
|
|
1448
|
+
* - Direct access in formatters and handlers
|
|
1449
|
+
* - Type-safe value extraction
|
|
1450
|
+
* - Optional chaining support
|
|
1451
|
+
*
|
|
1452
|
+
* @example Basic construction
|
|
1453
|
+
* ```typescript
|
|
1454
|
+
* // With value
|
|
1455
|
+
* const context = new LogContext({ userId: 123 });
|
|
1456
|
+
* console.log(context.value?.userId); // 123
|
|
1457
|
+
*
|
|
1458
|
+
* // Empty context
|
|
1459
|
+
* const empty = new LogContext();
|
|
1460
|
+
* console.log(empty.value); // undefined
|
|
1461
|
+
* ```
|
|
1462
|
+
*
|
|
1463
|
+
* @example Type-safe construction
|
|
1464
|
+
* ```typescript
|
|
1465
|
+
* interface UserContext {
|
|
1466
|
+
* id: number;
|
|
1467
|
+
* name: string;
|
|
1468
|
+
* role?: string;
|
|
1469
|
+
* }
|
|
1470
|
+
*
|
|
1471
|
+
* const context = new LogContext<UserContext>({
|
|
1472
|
+
* id: 123,
|
|
1473
|
+
* name: 'John',
|
|
1474
|
+
* role: 'admin' // Optional property
|
|
1475
|
+
* });
|
|
1476
|
+
*
|
|
1477
|
+
* // Type-safe access
|
|
1478
|
+
* const userId = context.value?.id; // number | undefined
|
|
1479
|
+
* const role = context.value?.role; // string | undefined
|
|
1480
|
+
* ```
|
|
1481
|
+
*
|
|
1482
|
+
* @example Nullable context
|
|
1483
|
+
* ```typescript
|
|
1484
|
+
* type NullableContext = {
|
|
1485
|
+
* sessionId: string;
|
|
1486
|
+
* } | null;
|
|
1487
|
+
*
|
|
1488
|
+
* // With value
|
|
1489
|
+
* const active = new LogContext<NullableContext>({
|
|
1490
|
+
* sessionId: 'sess-123'
|
|
1491
|
+
* });
|
|
1492
|
+
*
|
|
1493
|
+
* // With null
|
|
1494
|
+
* const inactive = new LogContext<NullableContext>(null);
|
|
1495
|
+
*
|
|
1496
|
+
* // Type-safe null handling
|
|
1497
|
+
* console.log(active.value?.sessionId); // 'sess-123'
|
|
1498
|
+
* console.log(inactive.value?.sessionId); // undefined
|
|
1499
|
+
* ```
|
|
1500
|
+
*
|
|
1501
|
+
* @example Context in logging
|
|
1502
|
+
* ```typescript
|
|
1503
|
+
* interface RequestContext {
|
|
1504
|
+
* path: string;
|
|
1505
|
+
* method: string;
|
|
1506
|
+
* duration: number;
|
|
1507
|
+
* }
|
|
1508
|
+
*
|
|
1509
|
+
* const context = new LogContext<RequestContext>({
|
|
1510
|
+
* path: '/api/users',
|
|
1511
|
+
* method: 'GET',
|
|
1512
|
+
* duration: 150
|
|
1513
|
+
* });
|
|
1514
|
+
*
|
|
1515
|
+
* // Using context in log messages
|
|
1516
|
+
* logger.info('Request completed', context);
|
|
1517
|
+
*
|
|
1518
|
+
* // Accessing context in formatters
|
|
1519
|
+
* class RequestFormatter implements FormatterInterface<RequestContext> {
|
|
1520
|
+
* format(event: LogEvent<RequestContext>): string[] {
|
|
1521
|
+
* const ctx = event.context?.value;
|
|
1522
|
+
* return [
|
|
1523
|
+
* ctx ? `${ctx.method} ${ctx.path} (${ctx.duration}ms)` : 'Unknown',
|
|
1524
|
+
* ...event.args
|
|
1525
|
+
* ];
|
|
1526
|
+
* }
|
|
1527
|
+
* }
|
|
1528
|
+
* ```
|
|
1529
|
+
*/
|
|
1530
|
+
constructor(
|
|
1531
|
+
/**
|
|
1532
|
+
* The context value that will be attached to log events
|
|
1533
|
+
*
|
|
1534
|
+
* This value is typed according to the Value type parameter
|
|
1535
|
+
* and can be accessed safely through optional chaining.
|
|
1536
|
+
*/
|
|
1537
|
+
value?: Value | undefined);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Core interface for logger implementations
|
|
1542
|
+
*
|
|
1543
|
+
* This interface defines the standard contract for logger implementations,
|
|
1544
|
+
* providing methods for different log levels, handler management, and
|
|
1545
|
+
* context creation.
|
|
1546
|
+
*
|
|
1547
|
+
* Core functionality:
|
|
1548
|
+
* - Standard log level methods (fatal, error, warn, info, debug, trace)
|
|
1549
|
+
* - Handler (appender) management
|
|
1550
|
+
* - Context creation for structured logging
|
|
1551
|
+
* - Type-safe logging with generic context support
|
|
1552
|
+
*
|
|
1553
|
+
* @example Basic usage
|
|
1554
|
+
* ```typescript
|
|
1555
|
+
* class MyLogger implements LoggerInterface {
|
|
1556
|
+
* info(...args: unknown[]): void {
|
|
1557
|
+
* console.log('[INFO]', ...args);
|
|
1558
|
+
* }
|
|
1559
|
+
* // ... implement other methods
|
|
1560
|
+
* }
|
|
1561
|
+
*
|
|
1562
|
+
* const logger = new MyLogger();
|
|
1563
|
+
* logger.info('Application started');
|
|
1564
|
+
* ```
|
|
1565
|
+
*
|
|
1566
|
+
* @example With handlers and context
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* class AdvancedLogger implements LoggerInterface {
|
|
1569
|
+
* private handlers: HandlerInterface[] = [];
|
|
1570
|
+
*
|
|
1571
|
+
* addAppender(handler: HandlerInterface): void {
|
|
1572
|
+
* this.handlers.push(handler);
|
|
1573
|
+
* }
|
|
1574
|
+
*
|
|
1575
|
+
* info(...args: unknown[]): void {
|
|
1576
|
+
* const event = {
|
|
1577
|
+
* level: 'info',
|
|
1578
|
+
* args,
|
|
1579
|
+
* timestamp: Date.now()
|
|
1580
|
+
* };
|
|
1581
|
+
*
|
|
1582
|
+
* this.handlers.forEach(handler => handler.append(event));
|
|
1583
|
+
* }
|
|
1584
|
+
*
|
|
1585
|
+
* context<T>(value?: T): LogContext<T> {
|
|
1586
|
+
* return new LogContext(value);
|
|
1587
|
+
* }
|
|
1588
|
+
* // ... implement other methods
|
|
1589
|
+
* }
|
|
1590
|
+
*
|
|
1591
|
+
* const logger = new AdvancedLogger();
|
|
1592
|
+
* logger.addAppender(new ConsoleHandler());
|
|
1593
|
+
* logger.info('User logged in', logger.context({ userId: 123 }));
|
|
1594
|
+
* ```
|
|
1595
|
+
*
|
|
1596
|
+
* @example Type-safe context usage
|
|
1597
|
+
* ```typescript
|
|
1598
|
+
* interface UserContext {
|
|
1599
|
+
* userId: number;
|
|
1600
|
+
* username: string;
|
|
1601
|
+
* role: string;
|
|
1602
|
+
* }
|
|
1603
|
+
*
|
|
1604
|
+
* const logger: LoggerInterface = new Logger();
|
|
1605
|
+
*
|
|
1606
|
+
* // Type-safe context
|
|
1607
|
+
* logger.info('User action', logger.context<UserContext>({
|
|
1608
|
+
* userId: 123,
|
|
1609
|
+
* username: 'john_doe',
|
|
1610
|
+
* role: 'admin'
|
|
1611
|
+
* }));
|
|
1612
|
+
* ```
|
|
1613
|
+
*/
|
|
1614
|
+
interface LoggerInterface<Ctx = unknown> {
|
|
1615
|
+
/**
|
|
1616
|
+
* General purpose logging method (alias for info)
|
|
1617
|
+
*
|
|
1618
|
+
* @param args - Message content followed by optional context
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```typescript
|
|
1622
|
+
* logger.log('Application started');
|
|
1623
|
+
* logger.log('User action', { userId: 123 });
|
|
1624
|
+
* logger.log('Process completed', logger.context({ duration: 1500 }));
|
|
1625
|
+
* ```
|
|
1626
|
+
*/
|
|
1627
|
+
log(...args: unknown[]): void;
|
|
1628
|
+
/**
|
|
1629
|
+
* Logs a critical error that causes application termination
|
|
1630
|
+
*
|
|
1631
|
+
* Use for severe errors that:
|
|
1632
|
+
* - Cause immediate application shutdown
|
|
1633
|
+
* - Require immediate administrator attention
|
|
1634
|
+
* - Indicate system is unusable
|
|
1635
|
+
*
|
|
1636
|
+
* @param args - Message content followed by optional context
|
|
1637
|
+
*
|
|
1638
|
+
* @example
|
|
349
1639
|
* ```typescript
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
1640
|
+
* logger.fatal('Database connection permanently lost');
|
|
1641
|
+
* logger.fatal('Out of memory', { memoryUsage: process.memoryUsage() });
|
|
1642
|
+
* logger.fatal('System crash', logger.context({
|
|
1643
|
+
* error: new Error('Unrecoverable error'),
|
|
1644
|
+
* state: 'corrupted'
|
|
1645
|
+
* }));
|
|
353
1646
|
* ```
|
|
1647
|
+
*/
|
|
1648
|
+
fatal(...args: unknown[]): void;
|
|
1649
|
+
/**
|
|
1650
|
+
* Logs an error condition that doesn't cause termination
|
|
354
1651
|
*
|
|
355
|
-
*
|
|
1652
|
+
* Use for errors that:
|
|
1653
|
+
* - Prevent normal operation but allow recovery
|
|
1654
|
+
* - Require administrator attention
|
|
1655
|
+
* - Need to be tracked for troubleshooting
|
|
1656
|
+
*
|
|
1657
|
+
* @param args - Message content followed by optional context
|
|
1658
|
+
*
|
|
1659
|
+
* @example
|
|
356
1660
|
* ```typescript
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
1661
|
+
* logger.error('Failed to process payment');
|
|
1662
|
+
* logger.error('API request failed', { status: 500, path: '/api/users' });
|
|
1663
|
+
* logger.error('Database query error', logger.context({
|
|
1664
|
+
* query: 'SELECT * FROM users',
|
|
1665
|
+
* error: new Error('Connection timeout')
|
|
1666
|
+
* }));
|
|
1667
|
+
* ```
|
|
1668
|
+
*/
|
|
1669
|
+
error(...args: unknown[]): void;
|
|
1670
|
+
/**
|
|
1671
|
+
* Logs a warning condition that might cause issues
|
|
360
1672
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
1673
|
+
* Use for situations that:
|
|
1674
|
+
* - Are unusual but not errors
|
|
1675
|
+
* - Might cause problems in the future
|
|
1676
|
+
* - Indicate deprecated feature usage
|
|
363
1677
|
*
|
|
364
|
-
*
|
|
365
|
-
* ${changelog}
|
|
1678
|
+
* @param args - Message content followed by optional context
|
|
366
1679
|
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
1680
|
+
* @example
|
|
1681
|
+
* ```typescript
|
|
1682
|
+
* logger.warn('High memory usage detected');
|
|
1683
|
+
* logger.warn('Deprecated API used', { api: 'oldMethod', alternative: 'newMethod' });
|
|
1684
|
+
* logger.warn('Cache miss rate high', logger.context({
|
|
1685
|
+
* rate: '85%',
|
|
1686
|
+
* threshold: '75%'
|
|
1687
|
+
* }));
|
|
373
1688
|
* ```
|
|
374
1689
|
*/
|
|
375
|
-
|
|
1690
|
+
warn(...args: unknown[]): void;
|
|
376
1691
|
/**
|
|
377
|
-
*
|
|
1692
|
+
* Logs normal but significant events
|
|
378
1693
|
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
1694
|
+
* Use for events that:
|
|
1695
|
+
* - Mark major application state changes
|
|
1696
|
+
* - Track business process completion
|
|
1697
|
+
* - Record user actions
|
|
383
1698
|
*
|
|
384
|
-
*
|
|
385
|
-
* - Automated label application
|
|
386
|
-
* - Customizable label appearance
|
|
387
|
-
* - Consistent release identification
|
|
388
|
-
* - Integration with GitHub labeling system
|
|
389
|
-
* - Support for custom label descriptions
|
|
1699
|
+
* @param args - Message content followed by optional context
|
|
390
1700
|
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
1701
|
+
* @example
|
|
1702
|
+
* ```typescript
|
|
1703
|
+
* logger.info('Application started on port 3000');
|
|
1704
|
+
* logger.info('User logged in', { userId: 123, role: 'admin' });
|
|
1705
|
+
* logger.info('Order processed', logger.context({
|
|
1706
|
+
* orderId: 'ORD-123',
|
|
1707
|
+
* amount: 99.99,
|
|
1708
|
+
* currency: 'USD'
|
|
1709
|
+
* }));
|
|
1710
|
+
* ```
|
|
1711
|
+
*/
|
|
1712
|
+
info(...args: unknown[]): void;
|
|
1713
|
+
/**
|
|
1714
|
+
* Logs detailed information for debugging
|
|
395
1715
|
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
1716
|
+
* Use for information that:
|
|
1717
|
+
* - Helps diagnose problems
|
|
1718
|
+
* - Shows detailed flow of execution
|
|
1719
|
+
* - Exposes internal state
|
|
1720
|
+
*
|
|
1721
|
+
* @param args - Message content followed by optional context
|
|
1722
|
+
*
|
|
1723
|
+
* @example
|
|
398
1724
|
* ```typescript
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
* };
|
|
1725
|
+
* logger.debug('Processing request payload');
|
|
1726
|
+
* logger.debug('Cache state', { size: 1024, entries: 50 });
|
|
1727
|
+
* logger.debug('Query execution plan', logger.context({
|
|
1728
|
+
* sql: 'SELECT * FROM users WHERE role = ?',
|
|
1729
|
+
* params: ['admin'],
|
|
1730
|
+
* executionTime: 150
|
|
1731
|
+
* }));
|
|
406
1732
|
* ```
|
|
1733
|
+
*/
|
|
1734
|
+
debug(...args: unknown[]): void;
|
|
1735
|
+
/**
|
|
1736
|
+
* Logs the most detailed level of information
|
|
407
1737
|
*
|
|
408
|
-
*
|
|
1738
|
+
* Use for information that:
|
|
1739
|
+
* - Shows step-by-step execution flow
|
|
1740
|
+
* - Includes method entry/exit points
|
|
1741
|
+
* - Contains variable state changes
|
|
1742
|
+
*
|
|
1743
|
+
* @param args - Message content followed by optional context
|
|
1744
|
+
*
|
|
1745
|
+
* @example
|
|
409
1746
|
* ```typescript
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
* };
|
|
1747
|
+
* logger.trace('Entering method processUser');
|
|
1748
|
+
* logger.trace('Variable state', { counter: 5, buffer: [1,2,3] });
|
|
1749
|
+
* logger.trace('Method execution', logger.context({
|
|
1750
|
+
* method: 'processUser',
|
|
1751
|
+
* args: { id: 123 },
|
|
1752
|
+
* stack: new Error().stack
|
|
1753
|
+
* }));
|
|
417
1754
|
* ```
|
|
418
1755
|
*/
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Hexadecimal color code for label appearance
|
|
422
|
-
*
|
|
423
|
-
* Color format: 6-character hex string without '#'
|
|
424
|
-
* Used for visual distinction in GitHub interface
|
|
425
|
-
* Supports standard web color codes
|
|
426
|
-
*
|
|
427
|
-
* @optional
|
|
428
|
-
* @default `'1A7F37'`
|
|
429
|
-
* @example Green color
|
|
430
|
-
* ```typescript
|
|
431
|
-
* color: '1A7F37'
|
|
432
|
-
* ```
|
|
433
|
-
*
|
|
434
|
-
* @example Blue color
|
|
435
|
-
* ```typescript
|
|
436
|
-
* color: '0366D6'
|
|
437
|
-
* ```
|
|
438
|
-
*/
|
|
439
|
-
color?: string;
|
|
440
|
-
/**
|
|
441
|
-
* Descriptive text for label documentation
|
|
442
|
-
*
|
|
443
|
-
* Provides context about the label's purpose
|
|
444
|
-
* Used in GitHub label management interface
|
|
445
|
-
* Helps team members understand label usage
|
|
446
|
-
*
|
|
447
|
-
* @optional
|
|
448
|
-
* @default `'Release PR'`
|
|
449
|
-
* @example
|
|
450
|
-
* ```typescript
|
|
451
|
-
* description: 'Automated release pull request'
|
|
452
|
-
* ```
|
|
453
|
-
*/
|
|
454
|
-
description?: string;
|
|
455
|
-
/**
|
|
456
|
-
* Label name for identification and display
|
|
457
|
-
*
|
|
458
|
-
* Used as the primary identifier for the label
|
|
459
|
-
* Displayed in GitHub PR interface
|
|
460
|
-
* Should be descriptive and consistent
|
|
461
|
-
*
|
|
462
|
-
* @optional
|
|
463
|
-
* @default `'CI-Release'`
|
|
464
|
-
* @example
|
|
465
|
-
* ```typescript
|
|
466
|
-
* name: 'release'
|
|
467
|
-
* ```
|
|
468
|
-
*/
|
|
469
|
-
name?: string;
|
|
470
|
-
};
|
|
1756
|
+
trace(...args: unknown[]): void;
|
|
471
1757
|
/**
|
|
472
|
-
*
|
|
1758
|
+
* Adds a new handler (appender) to the logger
|
|
473
1759
|
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
* enabling targeted review and deployment.
|
|
1760
|
+
* Handlers are responsible for processing and outputting log events.
|
|
1761
|
+
* Multiple handlers can be added to send logs to different destinations
|
|
1762
|
+
* or format them differently.
|
|
478
1763
|
*
|
|
479
|
-
*
|
|
480
|
-
* - Applied to PRs when specific packages change
|
|
481
|
-
* - Enables package-specific review processes
|
|
482
|
-
* - Supports selective deployment strategies
|
|
483
|
-
* - Improves monorepo change tracking
|
|
484
|
-
* - Facilitates team collaboration and review
|
|
1764
|
+
* @param appender - The handler instance to add
|
|
485
1765
|
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
1766
|
+
* @example Basic console handler
|
|
1767
|
+
* ```typescript
|
|
1768
|
+
* const logger = new Logger();
|
|
1769
|
+
* logger.addAppender(new ConsoleHandler());
|
|
1770
|
+
* ```
|
|
488
1771
|
*
|
|
489
|
-
* @
|
|
490
|
-
* @default `'changes:${name}'`
|
|
491
|
-
* @example Basic change label
|
|
1772
|
+
* @example Multiple handlers
|
|
492
1773
|
* ```typescript
|
|
493
|
-
* const
|
|
494
|
-
*
|
|
495
|
-
*
|
|
1774
|
+
* const logger = new Logger();
|
|
1775
|
+
*
|
|
1776
|
+
* // Console output for development
|
|
1777
|
+
* logger.addAppender(new ConsoleHandler(
|
|
1778
|
+
* new TimestampFormatter({ locale: 'en-US' })
|
|
1779
|
+
* ));
|
|
1780
|
+
*
|
|
1781
|
+
* // File output for errors
|
|
1782
|
+
* logger.addAppender(new FileHandler('./logs/errors.log', {
|
|
1783
|
+
* level: 'error',
|
|
1784
|
+
* formatter: new JSONFormatter()
|
|
1785
|
+
* }));
|
|
1786
|
+
*
|
|
1787
|
+
* // Metrics collection
|
|
1788
|
+
* logger.addAppender(new MetricsHandler({
|
|
1789
|
+
* service: 'user-api',
|
|
1790
|
+
* endpoint: 'https://metrics.example.com'
|
|
1791
|
+
* }));
|
|
496
1792
|
* ```
|
|
497
1793
|
*
|
|
498
|
-
* @example
|
|
1794
|
+
* @example Conditional handlers
|
|
499
1795
|
* ```typescript
|
|
500
|
-
* const
|
|
501
|
-
*
|
|
502
|
-
*
|
|
1796
|
+
* const logger = new Logger();
|
|
1797
|
+
*
|
|
1798
|
+
* // Development handlers
|
|
1799
|
+
* if (process.env.NODE_ENV === 'development') {
|
|
1800
|
+
* logger.addAppender(new ConsoleHandler(
|
|
1801
|
+
* new PrettyFormatter({ colors: true })
|
|
1802
|
+
* ));
|
|
1803
|
+
* logger.addAppender(new FileHandler('./logs/dev.log'));
|
|
1804
|
+
* }
|
|
1805
|
+
*
|
|
1806
|
+
* // Production handlers
|
|
1807
|
+
* if (process.env.NODE_ENV === 'production') {
|
|
1808
|
+
* logger.addAppender(new CloudWatchHandler({
|
|
1809
|
+
* region: 'us-east-1',
|
|
1810
|
+
* logGroupName: '/aws/api'
|
|
1811
|
+
* }));
|
|
1812
|
+
* logger.addAppender(new AlertHandler({
|
|
1813
|
+
* level: 'error',
|
|
1814
|
+
* webhook: 'https://alerts.example.com'
|
|
1815
|
+
* }));
|
|
1816
|
+
* }
|
|
503
1817
|
* ```
|
|
504
1818
|
*/
|
|
505
|
-
|
|
1819
|
+
addAppender(appender: HandlerInterface<Ctx>): void;
|
|
506
1820
|
/**
|
|
507
|
-
*
|
|
1821
|
+
* Creates a new LogContext instance for structured logging
|
|
508
1822
|
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
1823
|
+
* Context objects allow adding structured data to log messages
|
|
1824
|
+
* in a type-safe way. They can be used to add metadata,
|
|
1825
|
+
* performance metrics, error details, or any other relevant
|
|
1826
|
+
* information to log entries.
|
|
513
1827
|
*
|
|
514
|
-
*
|
|
515
|
-
* -
|
|
516
|
-
*
|
|
517
|
-
* - Supports nested directory structures
|
|
518
|
-
* - Facilitates monorepo organization
|
|
519
|
-
* - Enables workspace-specific configurations
|
|
1828
|
+
* @since 0.1.0
|
|
1829
|
+
* @param value - Optional value to be stored in the context
|
|
1830
|
+
* @returns A new LogContext instance with the provided value
|
|
520
1831
|
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
1832
|
+
* @example Basic context usage
|
|
1833
|
+
* ```typescript
|
|
1834
|
+
* logger.info('User logged in', logger.context({
|
|
1835
|
+
* userId: 123,
|
|
1836
|
+
* role: 'admin',
|
|
1837
|
+
* loginTime: new Date()
|
|
1838
|
+
* }));
|
|
1839
|
+
* ```
|
|
527
1840
|
*
|
|
528
|
-
* @
|
|
529
|
-
* @default `[]`
|
|
530
|
-
* @example Basic package directories
|
|
1841
|
+
* @example Type-safe context
|
|
531
1842
|
* ```typescript
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
1843
|
+
* interface RequestContext {
|
|
1844
|
+
* method: string;
|
|
1845
|
+
* path: string;
|
|
1846
|
+
* duration: number;
|
|
1847
|
+
* statusCode: number;
|
|
1848
|
+
* }
|
|
1849
|
+
*
|
|
1850
|
+
* logger.info('Request completed', logger.context<RequestContext>({
|
|
1851
|
+
* method: 'GET',
|
|
1852
|
+
* path: '/api/users',
|
|
1853
|
+
* duration: 150,
|
|
1854
|
+
* statusCode: 200
|
|
1855
|
+
* }));
|
|
535
1856
|
* ```
|
|
536
1857
|
*
|
|
537
|
-
* @example
|
|
1858
|
+
* @example Error context
|
|
538
1859
|
* ```typescript
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* }
|
|
1860
|
+
* try {
|
|
1861
|
+
* await processOrder(orderId);
|
|
1862
|
+
* } catch (error) {
|
|
1863
|
+
* logger.error('Order processing failed', logger.context({
|
|
1864
|
+
* orderId,
|
|
1865
|
+
* error: error instanceof Error ? error.message : String(error),
|
|
1866
|
+
* stack: error instanceof Error ? error.stack : undefined
|
|
1867
|
+
* }));
|
|
1868
|
+
* }
|
|
1869
|
+
* ```
|
|
1870
|
+
*
|
|
1871
|
+
* @example Performance monitoring
|
|
1872
|
+
* ```typescript
|
|
1873
|
+
* const startTime = Date.now();
|
|
1874
|
+
* try {
|
|
1875
|
+
* const result = await heavyOperation();
|
|
1876
|
+
* logger.info('Operation completed', logger.context({
|
|
1877
|
+
* duration: Date.now() - startTime,
|
|
1878
|
+
* resultSize: result.length,
|
|
1879
|
+
* memoryUsage: process.memoryUsage()
|
|
1880
|
+
* }));
|
|
1881
|
+
* } catch (error) {
|
|
1882
|
+
* logger.error('Operation failed', logger.context({
|
|
1883
|
+
* duration: Date.now() - startTime,
|
|
1884
|
+
* error,
|
|
1885
|
+
* memoryUsage: process.memoryUsage()
|
|
1886
|
+
* }));
|
|
1887
|
+
* }
|
|
542
1888
|
* ```
|
|
543
1889
|
*/
|
|
544
|
-
|
|
1890
|
+
context<Value>(value?: Value): LogContext<Value>;
|
|
545
1891
|
}
|
|
546
1892
|
|
|
547
1893
|
/**
|
|
@@ -2938,7 +4284,7 @@ declare class ConfigSearch {
|
|
|
2938
4284
|
* });
|
|
2939
4285
|
* ```
|
|
2940
4286
|
*/
|
|
2941
|
-
declare class ScriptContext<Opt extends ScriptSharedInterface> implements ScriptContextInterface<Opt> {
|
|
4287
|
+
declare class ScriptContext<Opt extends ScriptSharedInterface> extends ExecutorContextImpl<Opt> implements ScriptContextInterface<Opt> {
|
|
2942
4288
|
readonly name: string;
|
|
2943
4289
|
/**
|
|
2944
4290
|
* Logger instance for structured logging
|
|
@@ -2975,13 +4321,6 @@ declare class ScriptContext<Opt extends ScriptSharedInterface> implements Script
|
|
|
2975
4321
|
* detailed information display.
|
|
2976
4322
|
*/
|
|
2977
4323
|
readonly verbose: boolean;
|
|
2978
|
-
/**
|
|
2979
|
-
* Script-specific options
|
|
2980
|
-
*
|
|
2981
|
-
* Contains all script configuration options
|
|
2982
|
-
* with defaults applied and environment integration.
|
|
2983
|
-
*/
|
|
2984
|
-
options: Opt;
|
|
2985
4324
|
/**
|
|
2986
4325
|
* Creates a new ScriptContext instance with the specified configuration
|
|
2987
4326
|
*
|
|
@@ -3029,6 +4368,10 @@ declare class ScriptContext<Opt extends ScriptSharedInterface> implements Script
|
|
|
3029
4368
|
* ```
|
|
3030
4369
|
*/
|
|
3031
4370
|
constructor(name: string, opts?: Partial<ScriptContextInterface<Opt>>);
|
|
4371
|
+
/**
|
|
4372
|
+
* @override
|
|
4373
|
+
*/
|
|
4374
|
+
get options(): Opt;
|
|
3032
4375
|
/**
|
|
3033
4376
|
* Environment instance for variable access and management
|
|
3034
4377
|
*
|
|
@@ -3071,101 +4414,6 @@ declare class ScriptContext<Opt extends ScriptSharedInterface> implements Script
|
|
|
3071
4414
|
* ```
|
|
3072
4415
|
*/
|
|
3073
4416
|
get env(): Env;
|
|
3074
|
-
/**
|
|
3075
|
-
* Extracts configuration store from feConfig based on script name
|
|
3076
|
-
*
|
|
3077
|
-
* Core concept:
|
|
3078
|
-
* Safely extracts script-specific configuration from the
|
|
3079
|
-
* merged fe-config object, handling various data types
|
|
3080
|
-
* and providing fallback values.
|
|
3081
|
-
*
|
|
3082
|
-
* Extraction process:
|
|
3083
|
-
* 1. Uses lodash get to safely access nested configuration
|
|
3084
|
-
* 2. Validates that the extracted value is an object
|
|
3085
|
-
* 3. Converts primitive values to empty objects
|
|
3086
|
-
* 4. Logs warnings for non-object configurations
|
|
3087
|
-
* 5. Returns type-safe configuration object
|
|
3088
|
-
*
|
|
3089
|
-
* Safety features:
|
|
3090
|
-
* - Safe nested property access with lodash get
|
|
3091
|
-
* - Null and undefined handling
|
|
3092
|
-
* - Type validation for configuration objects
|
|
3093
|
-
* - Warning logging for invalid configurations
|
|
3094
|
-
* - Fallback to empty object for primitives
|
|
3095
|
-
*
|
|
3096
|
-
* Configuration validation:
|
|
3097
|
-
* - Checks if extracted value is an object
|
|
3098
|
-
* - Warns when configuration is not an object
|
|
3099
|
-
* - Converts primitives to empty objects
|
|
3100
|
-
* - Maintains type safety through generic constraints
|
|
3101
|
-
*
|
|
3102
|
-
* @param scriptName - Script name or array of names for nested access
|
|
3103
|
-
* @param sources - Configuration source object (feConfig)
|
|
3104
|
-
* @returns Extracted configuration object with type safety
|
|
3105
|
-
*
|
|
3106
|
-
* @example Basic extraction
|
|
3107
|
-
* ```typescript
|
|
3108
|
-
* const config = this.getDefaultStore('build-script', feConfig);
|
|
3109
|
-
* // Returns configuration from feConfig['build-script']
|
|
3110
|
-
* ```
|
|
3111
|
-
*
|
|
3112
|
-
* @example Nested access
|
|
3113
|
-
* ```typescript
|
|
3114
|
-
* const config = this.getDefaultStore(['scripts', 'build'], feConfig);
|
|
3115
|
-
* // Returns configuration from feConfig.scripts.build
|
|
3116
|
-
* ```
|
|
3117
|
-
*/
|
|
3118
|
-
protected getDefaultStore(scriptName: string | string[], sources: Record<string, unknown>): Opt;
|
|
3119
|
-
/**
|
|
3120
|
-
* Applies default values to script options with environment integration
|
|
3121
|
-
*
|
|
3122
|
-
* Core concept:
|
|
3123
|
-
* Enhances provided options with sensible defaults and
|
|
3124
|
-
* environment variable integration, ensuring all required
|
|
3125
|
-
* configuration is available.
|
|
3126
|
-
*
|
|
3127
|
-
* Default logic:
|
|
3128
|
-
* 1. Uses existing environment or loads from files
|
|
3129
|
-
* 2. Sets rootPath to current working directory if not specified
|
|
3130
|
-
* 3. Determines sourceBranch from environment or defaults to 'master'
|
|
3131
|
-
* 4. Merges all options with proper precedence
|
|
3132
|
-
* 5. Ensures environment is properly initialized
|
|
3133
|
-
*
|
|
3134
|
-
* Environment variable priority:
|
|
3135
|
-
* 1. FE_RELEASE_BRANCH (primary environment variable)
|
|
3136
|
-
* 2. FE_RELEASE_SOURCE_BRANCH (fallback environment variable)
|
|
3137
|
-
* 3. 'master' (default value)
|
|
3138
|
-
* 4. Options.sourceBranch (if provided)
|
|
3139
|
-
*
|
|
3140
|
-
* Path handling:
|
|
3141
|
-
* - rootPath defaults to process.cwd() if not specified
|
|
3142
|
-
* - Supports both absolute and relative paths
|
|
3143
|
-
* - Maintains path consistency across environments
|
|
3144
|
-
* - Used for file operations and configuration loading
|
|
3145
|
-
*
|
|
3146
|
-
* Environment integration:
|
|
3147
|
-
* - Loads environment from files if not provided
|
|
3148
|
-
* - Uses configurable file loading order
|
|
3149
|
-
* - Integrates with fe-config environment settings
|
|
3150
|
-
* - Provides fallback to default environment
|
|
3151
|
-
*
|
|
3152
|
-
* @param options - Current options to enhance with defaults
|
|
3153
|
-
* @returns Options with default values and environment integration
|
|
3154
|
-
*
|
|
3155
|
-
* @example Basic defaults
|
|
3156
|
-
* ```typescript
|
|
3157
|
-
* const options = this.getDefaultOptions({});
|
|
3158
|
-
* // Returns: { rootPath: process.cwd(), sourceBranch: 'master', env: Env instance }
|
|
3159
|
-
* ```
|
|
3160
|
-
*
|
|
3161
|
-
* @example With environment variables
|
|
3162
|
-
* ```typescript
|
|
3163
|
-
* // If FE_RELEASE_BRANCH=develop
|
|
3164
|
-
* const options = this.getDefaultOptions({});
|
|
3165
|
-
* // Returns: { rootPath: process.cwd(), sourceBranch: 'develop', env: Env instance }
|
|
3166
|
-
* ```
|
|
3167
|
-
*/
|
|
3168
|
-
protected getDefaultOptions(options: Opt): Opt;
|
|
3169
4417
|
/**
|
|
3170
4418
|
* Updates script options with deep merging support
|
|
3171
4419
|
*
|
|
@@ -3439,7 +4687,7 @@ interface ScriptPluginProps {
|
|
|
3439
4687
|
* }
|
|
3440
4688
|
* ```
|
|
3441
4689
|
*/
|
|
3442
|
-
declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props extends ScriptPluginProps = ScriptPluginProps> implements
|
|
4690
|
+
declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props extends ScriptPluginProps = ScriptPluginProps> implements LifecyclePluginInterface<Context> {
|
|
3443
4691
|
protected context: Context;
|
|
3444
4692
|
readonly pluginName: string;
|
|
3445
4693
|
protected props: Props;
|
|
@@ -3574,7 +4822,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3574
4822
|
* plugin.enabled('onExec', context); // Returns true
|
|
3575
4823
|
* ```
|
|
3576
4824
|
*/
|
|
3577
|
-
enabled(_name: string, _context:
|
|
4825
|
+
enabled(_name: string, _context: Context): boolean;
|
|
3578
4826
|
/**
|
|
3579
4827
|
* Retrieves configuration values with nested path support
|
|
3580
4828
|
*
|
|
@@ -3668,7 +4916,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3668
4916
|
* }
|
|
3669
4917
|
* ```
|
|
3670
4918
|
*/
|
|
3671
|
-
onBefore?(_context:
|
|
4919
|
+
onBefore?(_context: Context): Context extends ExecutorContextInterface<infer P> ? P | Promise<P> | void | Promise<void> : unknown | Promise<unknown> | void | Promise<void>;
|
|
3672
4920
|
/**
|
|
3673
4921
|
* Lifecycle method called during script execution
|
|
3674
4922
|
*
|
|
@@ -3683,7 +4931,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3683
4931
|
*
|
|
3684
4932
|
* @example
|
|
3685
4933
|
* ```typescript
|
|
3686
|
-
* async onExec(context:
|
|
4934
|
+
* async onExec(context: Context): Promise<void> {
|
|
3687
4935
|
* await this.step({
|
|
3688
4936
|
* label: 'Building project',
|
|
3689
4937
|
* task: async () => {
|
|
@@ -3702,7 +4950,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3702
4950
|
* }
|
|
3703
4951
|
* ```
|
|
3704
4952
|
*/
|
|
3705
|
-
onExec?(_context:
|
|
4953
|
+
onExec?(_context: Context): void | Promise<void>;
|
|
3706
4954
|
/**
|
|
3707
4955
|
* Lifecycle method called after successful script execution
|
|
3708
4956
|
*
|
|
@@ -3717,7 +4965,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3717
4965
|
*
|
|
3718
4966
|
* @example
|
|
3719
4967
|
* ```typescript
|
|
3720
|
-
* async onSuccess(context:
|
|
4968
|
+
* async onSuccess(context: Context): Promise<void> {
|
|
3721
4969
|
* // Send success notification
|
|
3722
4970
|
* await this.sendNotification('Build completed successfully');
|
|
3723
4971
|
*
|
|
@@ -3733,7 +4981,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3733
4981
|
* }
|
|
3734
4982
|
* ```
|
|
3735
4983
|
*/
|
|
3736
|
-
onSuccess?(_context:
|
|
4984
|
+
onSuccess?(_context: Context): void | Promise<void>;
|
|
3737
4985
|
/**
|
|
3738
4986
|
* Lifecycle method called when script execution fails
|
|
3739
4987
|
*
|
|
@@ -3748,7 +4996,7 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3748
4996
|
*
|
|
3749
4997
|
* @example
|
|
3750
4998
|
* ```typescript
|
|
3751
|
-
* async onError(context:
|
|
4999
|
+
* async onError(context: Context): Promise<void> {
|
|
3752
5000
|
* // Log detailed error information
|
|
3753
5001
|
* this.logger.error('Script execution failed', {
|
|
3754
5002
|
* error: context.error,
|
|
@@ -3766,7 +5014,28 @@ declare abstract class ScriptPlugin<Context extends ScriptContext<any>, Props ex
|
|
|
3766
5014
|
* }
|
|
3767
5015
|
* ```
|
|
3768
5016
|
*/
|
|
3769
|
-
onError?(_context:
|
|
5017
|
+
onError?(_context: Context): Promise<ExecutorError | void> | ExecutorError | Error | void;
|
|
5018
|
+
/**
|
|
5019
|
+
* Lifecycle method called after script execution
|
|
5020
|
+
*
|
|
5021
|
+
* Override this method to perform cleanup tasks such as:
|
|
5022
|
+
* - Resource cleanup
|
|
5023
|
+
* - Success notifications
|
|
5024
|
+
* - Result processing
|
|
5025
|
+
* - Post-execution reporting
|
|
5026
|
+
*
|
|
5027
|
+
* @override
|
|
5028
|
+
* @param _context - Executor context containing execution state
|
|
5029
|
+
*
|
|
5030
|
+
* @example
|
|
5031
|
+
* ```typescript
|
|
5032
|
+
* async onFinally(context: Context): Promise<void> {
|
|
5033
|
+
* // Clean up temporary files
|
|
5034
|
+
* await this.shell.rmdir('./temp');
|
|
5035
|
+
* }
|
|
5036
|
+
* ```
|
|
5037
|
+
*/
|
|
5038
|
+
onFinally?(_context: Context): void | Promise<void>;
|
|
3770
5039
|
/**
|
|
3771
5040
|
* Executes a step with structured logging and error handling
|
|
3772
5041
|
*
|