@qlover/scripts-context 1.3.0 → 2.1.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.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import * as _commitlint_types from '@commitlint/types';
2
- import { LoggerInterface, FormatterInterface, LogEvent } from '@qlover/logger';
1
+ import { UserConfig } from '@commitlint/types';
3
2
  import { Env } from '@qlover/env-loader';
4
- import { ExecutorPlugin, ExecutorContext } from '@qlover/fe-corekit';
3
+ import { ExecutorContextImpl, LifecyclePluginInterface, ExecutorContextInterface, ExecutorError } from '@qlover/fe-corekit';
5
4
 
6
5
  declare const defaultFeConfig: FeConfig;
7
- type FeConfig = {
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?: _commitlint_types.UserConfig;
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
- * const config: FeReleaseConfig = {
351
- * PRBody: 'Release ${pkgName} v${tagName}\n\n${changelog}'
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
- * @example Detailed PR body
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
- * const config: FeReleaseConfig = {
358
- * PRBody: `
359
- * ## Release ${pkgName} v${tagName}
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
- * **Environment:** ${env}
362
- * **Branch:** ${branch}
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
- * ### Changes
365
- * ${changelog}
1678
+ * @param args - Message content followed by optional context
366
1679
  *
367
- * ### Checklist
368
- * - [ ] Version number is correct
369
- * - [ ] All tests pass
370
- * - [ ] Documentation updated
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
- PRBody?: string;
1690
+ warn(...args: unknown[]): void;
376
1691
  /**
377
- * Configuration for release pull request labels
1692
+ * Logs normal but significant events
378
1693
  *
379
- * Core concept:
380
- * Defines the label configuration for release pull requests,
381
- * enabling automated categorization and visual identification
382
- * of release-related PRs.
1694
+ * Use for events that:
1695
+ * - Mark major application state changes
1696
+ * - Track business process completion
1697
+ * - Record user actions
383
1698
  *
384
- * Label features:
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
- * Label properties:
392
- * - name: Label identifier and display name
393
- * - color: Hexadecimal color code for visual distinction
394
- * - description: Label description for documentation
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
- * @optional
397
- * @example Basic label configuration
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
- * const config: FeReleaseConfig = {
400
- * label: {
401
- * name: 'release',
402
- * color: '1A7F37',
403
- * description: 'Automated release PR'
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
- * @example Custom label
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
- * const config: FeReleaseConfig = {
411
- * label: {
412
- * name: 'CI-Release',
413
- * color: '0366D6',
414
- * description: 'Release created by CI/CD'
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
- label?: {
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
- * Template for package change labels in monorepos
1758
+ * Adds a new handler (appender) to the logger
473
1759
  *
474
- * Core concept:
475
- * Defines the naming pattern for labels that identify
476
- * which packages have changed in monorepo releases,
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
- * Label usage:
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
- * Template variables:
487
- * - `${name}`: Package name for label identification
1766
+ * @example Basic console handler
1767
+ * ```typescript
1768
+ * const logger = new Logger();
1769
+ * logger.addAppender(new ConsoleHandler());
1770
+ * ```
488
1771
  *
489
- * @optional
490
- * @default `'changes:${name}'`
491
- * @example Basic change label
1772
+ * @example Multiple handlers
492
1773
  * ```typescript
493
- * const config: FeReleaseConfig = {
494
- * changePackagesLabel: 'changes:${name}'
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 Custom change label
1794
+ * @example Conditional handlers
499
1795
  * ```typescript
500
- * const config: FeReleaseConfig = {
501
- * changePackagesLabel: 'package:${name}'
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
- changePackagesLabel?: string;
1819
+ addAppender(appender: HandlerInterface<Ctx>): void;
506
1820
  /**
507
- * Directories containing packages for monorepo releases
1821
+ * Creates a new LogContext instance for structured logging
508
1822
  *
509
- * Core concept:
510
- * Specifies the directories that contain packages for
511
- * monorepo release management, enabling selective
512
- * package discovery and release coordination.
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
- * Directory patterns:
515
- * - Supports glob patterns for flexible matching
516
- * - Enables selective package inclusion
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
- * Use cases:
522
- * - Monorepo package discovery
523
- * - Selective package releases
524
- * - Workspace-specific configurations
525
- * - Multi-package coordination
526
- * - Dependency-aware releases
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
- * @optional
529
- * @default `[]`
530
- * @example Basic package directories
1841
+ * @example Type-safe context
531
1842
  * ```typescript
532
- * const config: FeReleaseConfig = {
533
- * packagesDirectories: ['packages/*']
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 Multiple package directories
1858
+ * @example Error context
538
1859
  * ```typescript
539
- * const config: FeReleaseConfig = {
540
- * packagesDirectories: ['packages/*', 'apps/*', 'libs/*']
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
- packagesDirectories?: string[];
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 ExecutorPlugin<Context> {
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: ExecutorContext<Context>): boolean;
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: ExecutorContext<Context>): void | Promise<void>;
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: ExecutorContext<MyContext>): Promise<void> {
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: ExecutorContext<Context>): void | Promise<void>;
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: ExecutorContext<MyContext>): Promise<void> {
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: ExecutorContext<Context>): void | Promise<void>;
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: ExecutorContext<MyContext>): Promise<void> {
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: ExecutorContext<Context>): void | Promise<void>;
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
  *