@qlover/fe-release 3.0.0 → 3.1.2

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.js CHANGED
@@ -2392,6 +2392,31 @@ import {
2392
2392
  ScriptContext
2393
2393
  } from "@qlover/scripts-context";
2394
2394
  var ReleaseContext = class extends ScriptContext {
2395
+ /**
2396
+ * Creates a new ReleaseContext instance
2397
+ *
2398
+ * Initializes the context with provided options and sets up
2399
+ * default values for required configuration:
2400
+ * - rootPath: Defaults to current working directory
2401
+ * - sourceBranch: Uses environment variables or default
2402
+ * - releaseEnv: Uses environment variables or 'development'
2403
+ *
2404
+ * Environment Variable Priority:
2405
+ * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > DEFAULT_SOURCE_BRANCH
2406
+ * - releaseEnv: FE_RELEASE_ENV > NODE_ENV > 'development'
2407
+ *
2408
+ * @param name - Unique identifier for this release context
2409
+ * @param options - Configuration options
2410
+ *
2411
+ * @example
2412
+ * ```typescript
2413
+ * const context = new ReleaseContext('web-app', {
2414
+ * rootPath: '/projects/web-app',
2415
+ * sourceBranch: 'main',
2416
+ * releaseEnv: 'production'
2417
+ * });
2418
+ * ```
2419
+ */
2395
2420
  constructor(name, options) {
2396
2421
  super(name, options);
2397
2422
  if (!this.options.rootPath) {
@@ -2408,27 +2433,124 @@ var ReleaseContext = class extends ScriptContext {
2408
2433
  });
2409
2434
  }
2410
2435
  }
2436
+ /**
2437
+ * Gets the root path of the project
2438
+ *
2439
+ * @returns Absolute path to project root
2440
+ *
2441
+ * @example
2442
+ * ```typescript
2443
+ * const root = context.rootPath;
2444
+ * // '/path/to/project'
2445
+ * ```
2446
+ */
2411
2447
  get rootPath() {
2412
2448
  return this.getOptions("rootPath");
2413
2449
  }
2450
+ /**
2451
+ * Gets the source branch for the release
2452
+ *
2453
+ * @returns Branch name to use as source
2454
+ *
2455
+ * @example
2456
+ * ```typescript
2457
+ * const branch = context.sourceBranch;
2458
+ * // 'main' or custom branch name
2459
+ * ```
2460
+ */
2414
2461
  get sourceBranch() {
2415
2462
  return this.getOptions("sourceBranch");
2416
2463
  }
2464
+ /**
2465
+ * Gets the release environment
2466
+ *
2467
+ * @returns Environment name (e.g., 'development', 'production')
2468
+ *
2469
+ * @example
2470
+ * ```typescript
2471
+ * const env = context.releaseEnv;
2472
+ * // 'development' or custom environment
2473
+ * ```
2474
+ */
2417
2475
  get releaseEnv() {
2418
2476
  return this.getOptions("releaseEnv");
2419
2477
  }
2478
+ /**
2479
+ * Gets all configured workspaces
2480
+ *
2481
+ * @returns Array of workspace configurations or undefined
2482
+ *
2483
+ * @example
2484
+ * ```typescript
2485
+ * const allWorkspaces = context.workspaces;
2486
+ * // [{ name: 'pkg-a', version: '1.0.0', ... }]
2487
+ * ```
2488
+ */
2420
2489
  get workspaces() {
2421
2490
  return this.getOptions("workspaces.workspaces");
2422
2491
  }
2492
+ /**
2493
+ * Gets the current active workspace
2494
+ *
2495
+ * @returns Current workspace configuration or undefined
2496
+ *
2497
+ * @example
2498
+ * ```typescript
2499
+ * const current = context.workspace;
2500
+ * // { name: 'pkg-a', version: '1.0.0', ... }
2501
+ * ```
2502
+ */
2423
2503
  get workspace() {
2424
2504
  return this.getOptions("workspaces.workspace");
2425
2505
  }
2506
+ /**
2507
+ * Sets the workspace configurations
2508
+ *
2509
+ * Updates the workspace list while preserving other workspace settings
2510
+ *
2511
+ * @param workspaces - Array of workspace configurations
2512
+ *
2513
+ * @example
2514
+ * ```typescript
2515
+ * context.setWorkspaces([{
2516
+ * name: 'pkg-a',
2517
+ * version: '1.0.0',
2518
+ * path: 'packages/a'
2519
+ * }]);
2520
+ * ```
2521
+ */
2426
2522
  setWorkspaces(workspaces) {
2427
2523
  this.options.workspaces = {
2428
2524
  ...this.options.workspaces,
2429
2525
  workspaces
2430
2526
  };
2431
2527
  }
2528
+ /**
2529
+ * Gets package.json data for the current workspace
2530
+ *
2531
+ * Provides type-safe access to package.json fields with optional
2532
+ * path and default value support.
2533
+ *
2534
+ * @param key - Optional dot-notation path to specific field
2535
+ * @param defaultValue - Default value if field not found
2536
+ * @returns Package data of type T
2537
+ * @throws Error if package.json not found
2538
+ *
2539
+ * @example Basic usage
2540
+ * ```typescript
2541
+ * // Get entire package.json
2542
+ * const pkg = context.getPkg();
2543
+ *
2544
+ * // Get specific field
2545
+ * const version = context.getPkg<string>('version');
2546
+ *
2547
+ * // Get nested field with default
2548
+ * const script = context.getPkg<string>(
2549
+ * 'scripts.build',
2550
+ * 'echo "No build script"'
2551
+ * );
2552
+ * ```
2553
+ */
2432
2554
  getPkg(key, defaultValue) {
2433
2555
  const packageJson = this.workspace?.packageJson;
2434
2556
  if (!packageJson) {
@@ -2439,6 +2561,28 @@ var ReleaseContext = class extends ScriptContext {
2439
2561
  }
2440
2562
  return (0, import_get.default)(packageJson, key, defaultValue);
2441
2563
  }
2564
+ /**
2565
+ * Generates template context for string interpolation
2566
+ *
2567
+ * Combines context options, workspace data, and specific paths
2568
+ * for use in template processing. Includes deprecated fields
2569
+ * for backward compatibility.
2570
+ *
2571
+ * @returns Combined template context
2572
+ *
2573
+ * @example
2574
+ * ```typescript
2575
+ * const context = releaseContext.getTemplateContext();
2576
+ * // {
2577
+ * // publishPath: 'packages/my-pkg',
2578
+ * // env: 'production', // deprecated
2579
+ * // branch: 'main', // deprecated
2580
+ * // releaseEnv: 'production', // use this instead
2581
+ * // sourceBranch: 'main', // use this instead
2582
+ * // ...other options
2583
+ * // }
2584
+ * ```
2585
+ */
2442
2586
  getTemplateContext() {
2443
2587
  return {
2444
2588
  ...this.getOptions(),
@@ -2449,6 +2593,28 @@ var ReleaseContext = class extends ScriptContext {
2449
2593
  branch: this.sourceBranch
2450
2594
  };
2451
2595
  }
2596
+ /**
2597
+ * Executes changeset CLI commands
2598
+ *
2599
+ * Automatically detects and uses appropriate package manager
2600
+ * (pnpm or npx) to run changeset commands.
2601
+ *
2602
+ * @param name - Changeset command name
2603
+ * @param args - Optional command arguments
2604
+ * @returns Command output
2605
+ *
2606
+ * @example Version bump
2607
+ * ```typescript
2608
+ * // Bump version with snapshot
2609
+ * await context.runChangesetsCli('version', ['--snapshot', 'alpha']);
2610
+ *
2611
+ * // Create new changeset
2612
+ * await context.runChangesetsCli('add');
2613
+ *
2614
+ * // Status check
2615
+ * await context.runChangesetsCli('status');
2616
+ * ```
2617
+ */
2452
2618
  async runChangesetsCli(name, args) {
2453
2619
  let packageManager = "pnpm";
2454
2620
  try {
@@ -2483,6 +2649,28 @@ var DEFAULT_RELEASE_CONFIG = {
2483
2649
  batchTagName: "batch-${length}-packages-${timestamp}"
2484
2650
  };
2485
2651
  var ReleaseParams = class {
2652
+ /**
2653
+ * Creates a new ReleaseParams instance
2654
+ *
2655
+ * Initializes with logger for debug output and optional configuration
2656
+ * overrides. Uses default configuration values for any unspecified options.
2657
+ *
2658
+ * @param logger - Logger instance for debug output
2659
+ * @param config - Optional configuration overrides
2660
+ *
2661
+ * @example
2662
+ * ```typescript
2663
+ * // Basic initialization
2664
+ * const params = new ReleaseParams(logger);
2665
+ *
2666
+ * // With custom configuration
2667
+ * const params = new ReleaseParams(logger, {
2668
+ * maxWorkspace: 5,
2669
+ * multiWorkspaceSeparator: '-',
2670
+ * workspaceVersionSeparator: '#'
2671
+ * });
2672
+ * ```
2673
+ */
2486
2674
  constructor(logger, config = {}) {
2487
2675
  this.logger = logger;
2488
2676
  this.config = {
@@ -2490,7 +2678,49 @@ var ReleaseParams = class {
2490
2678
  ...config
2491
2679
  };
2492
2680
  }
2681
+ /**
2682
+ * Current configuration
2683
+ * @private
2684
+ */
2493
2685
  config;
2686
+ /**
2687
+ * Generates a release branch name for a single package
2688
+ *
2689
+ * Uses the branch name template from shared configuration or
2690
+ * falls back to the default template 'release-${tagName}'.
2691
+ * Supports variable interpolation in the template.
2692
+ *
2693
+ * Template Variables:
2694
+ * - ${pkgName}: Package name
2695
+ * - ${releaseName}: Release name (same as pkgName)
2696
+ * - ${tagName}: Release tag
2697
+ * - All shared config properties
2698
+ *
2699
+ * @param releaseName - Name of the package being released
2700
+ * @param tagName - Version tag for the release
2701
+ * @param shared - Shared configuration with branch template
2702
+ * @returns Formatted branch name
2703
+ * @throws Error if branch name template is not a string
2704
+ *
2705
+ * @example
2706
+ * ```typescript
2707
+ * // With default template
2708
+ * const branch = params.getReleaseBranchName(
2709
+ * 'my-pkg',
2710
+ * 'v1.0.0',
2711
+ * {}
2712
+ * );
2713
+ * // 'release-v1.0.0'
2714
+ *
2715
+ * // With custom template
2716
+ * const branch = params.getReleaseBranchName(
2717
+ * 'my-pkg',
2718
+ * 'v1.0.0',
2719
+ * { branchName: '${pkgName}-release-${tagName}' }
2720
+ * );
2721
+ * // 'my-pkg-release-v1.0.0'
2722
+ * ```
2723
+ */
2494
2724
  getReleaseBranchName(releaseName, tagName, shared) {
2495
2725
  const branchNameTpl = shared.branchName || "release-${tagName}";
2496
2726
  if (typeof branchNameTpl !== "string") {
@@ -2505,6 +2735,49 @@ var ReleaseParams = class {
2505
2735
  ...shared
2506
2736
  });
2507
2737
  }
2738
+ /**
2739
+ * Generates a release branch name for multiple packages
2740
+ *
2741
+ * Uses the batch branch name template from configuration.
2742
+ * Supports variable interpolation with additional batch-specific
2743
+ * variables.
2744
+ *
2745
+ * Template Variables:
2746
+ * - ${pkgName}: Package name
2747
+ * - ${releaseName}: Combined release name
2748
+ * - ${tagName}: Combined tag name
2749
+ * - ${length}: Number of packages
2750
+ * - ${timestamp}: Current timestamp
2751
+ * - All shared config properties
2752
+ *
2753
+ * @param releaseName - Combined name of packages
2754
+ * @param tagName - Combined version tag
2755
+ * @param shared - Shared configuration
2756
+ * @param length - Number of packages in batch
2757
+ * @returns Formatted batch branch name
2758
+ * @throws Error if branch name template is not a string
2759
+ *
2760
+ * @example
2761
+ * ```typescript
2762
+ * // With default template
2763
+ * const branch = params.getBatchReleaseBranchName(
2764
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
2765
+ * 'batch-2-packages-1234567890',
2766
+ * {},
2767
+ * 2
2768
+ * );
2769
+ * // 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
2770
+ *
2771
+ * // With custom template
2772
+ * const branch = params.getBatchReleaseBranchName(
2773
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
2774
+ * 'v1.0.0',
2775
+ * {},
2776
+ * 2,
2777
+ * );
2778
+ * // Custom formatted branch name
2779
+ * ```
2780
+ */
2508
2781
  getBatchReleaseBranchName(releaseName, tagName, shared, length) {
2509
2782
  const branchNameTpl = this.config.batchBranchName;
2510
2783
  if (typeof branchNameTpl !== "string") {
@@ -2521,6 +2794,45 @@ var ReleaseParams = class {
2521
2794
  timestamp: Date.now()
2522
2795
  });
2523
2796
  }
2797
+ /**
2798
+ * Generates a release name from workspace configurations
2799
+ *
2800
+ * For single packages, returns the package name.
2801
+ * For multiple packages, combines names and versions up to
2802
+ * the configured maximum number of workspaces.
2803
+ *
2804
+ * Format:
2805
+ * - Single: packageName
2806
+ * - Multiple: pkg1@1.0.0_pkg2@2.0.0_pkg3@3.0.0
2807
+ *
2808
+ * @param composeWorkspaces - Array of workspace configurations
2809
+ * @returns Formatted release name
2810
+ *
2811
+ * @example
2812
+ * ```typescript
2813
+ * // Single package
2814
+ * const name = params.getReleaseName([
2815
+ * { name: 'pkg-a', version: '1.0.0' }
2816
+ * ]);
2817
+ * // 'pkg-a'
2818
+ *
2819
+ * // Multiple packages
2820
+ * const name = params.getReleaseName([
2821
+ * { name: 'pkg-a', version: '1.0.0' },
2822
+ * { name: 'pkg-b', version: '2.0.0' }
2823
+ * ]);
2824
+ * // 'pkg-a@1.0.0_pkg-b@2.0.0'
2825
+ *
2826
+ * // With max limit
2827
+ * const name = params.getReleaseName([
2828
+ * { name: 'pkg-a', version: '1.0.0' },
2829
+ * { name: 'pkg-b', version: '2.0.0' },
2830
+ * { name: 'pkg-c', version: '3.0.0' },
2831
+ * { name: 'pkg-d', version: '4.0.0' }
2832
+ * ]);
2833
+ * // Only first 3: 'pkg-a@1.0.0_pkg-b@2.0.0_pkg-c@3.0.0'
2834
+ * ```
2835
+ */
2524
2836
  getReleaseName(composeWorkspaces) {
2525
2837
  if (composeWorkspaces.length === 1) {
2526
2838
  return composeWorkspaces[0].name;
@@ -2530,6 +2842,36 @@ var ReleaseParams = class {
2530
2842
  ({ name, version }) => `${name}${workspaceVersionSeparator}${version}`
2531
2843
  ).join(multiWorkspaceSeparator);
2532
2844
  }
2845
+ /**
2846
+ * Generates a tag name for the release
2847
+ *
2848
+ * For single packages, uses the package version.
2849
+ * For multiple packages, generates a batch tag name using
2850
+ * the configured template.
2851
+ *
2852
+ * Format:
2853
+ * - Single: version
2854
+ * - Multiple: batch-${length}-packages-${timestamp}
2855
+ *
2856
+ * @param composeWorkspaces - Array of workspace configurations
2857
+ * @returns Formatted tag name
2858
+ *
2859
+ * @example
2860
+ * ```typescript
2861
+ * // Single package
2862
+ * const tag = params.getReleaseTagName([
2863
+ * { name: 'pkg-a', version: '1.0.0' }
2864
+ * ]);
2865
+ * // '1.0.0'
2866
+ *
2867
+ * // Multiple packages
2868
+ * const tag = params.getReleaseTagName([
2869
+ * { name: 'pkg-a', version: '1.0.0' },
2870
+ * { name: 'pkg-b', version: '2.0.0' }
2871
+ * ]);
2872
+ * // 'batch-2-packages-1234567890'
2873
+ * ```
2874
+ */
2533
2875
  getReleaseTagName(composeWorkspaces) {
2534
2876
  if (composeWorkspaces.length === 1) {
2535
2877
  return composeWorkspaces[0].version;
@@ -2540,6 +2882,44 @@ var ReleaseParams = class {
2540
2882
  timestamp: Date.now()
2541
2883
  });
2542
2884
  }
2885
+ /**
2886
+ * Generates branch and tag parameters for the release
2887
+ *
2888
+ * Combines the generation of branch name and tag name into
2889
+ * a single operation. Handles both single and multi-package
2890
+ * releases appropriately.
2891
+ *
2892
+ * @param composeWorkspaces - Array of workspace configurations
2893
+ * @param shared - Shared configuration
2894
+ * @returns Object containing tag name and branch name
2895
+ *
2896
+ * @example Single package
2897
+ * ```typescript
2898
+ * const params = releaseParams.getReleaseBranchParams(
2899
+ * [{ name: 'pkg-a', version: '1.0.0' }],
2900
+ * { branchName: 'release-${tagName}' }
2901
+ * );
2902
+ * // {
2903
+ * // tagName: '1.0.0',
2904
+ * // releaseBranch: 'release-1.0.0'
2905
+ * // }
2906
+ * ```
2907
+ *
2908
+ * @example Multiple packages
2909
+ * ```typescript
2910
+ * const params = releaseParams.getReleaseBranchParams(
2911
+ * [
2912
+ * { name: 'pkg-a', version: '1.0.0' },
2913
+ * { name: 'pkg-b', version: '2.0.0' }
2914
+ * ],
2915
+ * {}
2916
+ * );
2917
+ * // {
2918
+ * // tagName: 'batch-2-packages-1234567890',
2919
+ * // releaseBranch: 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
2920
+ * // }
2921
+ * ```
2922
+ */
2543
2923
  getReleaseBranchParams(composeWorkspaces, shared) {
2544
2924
  const releaseTagName = this.getReleaseTagName(composeWorkspaces);
2545
2925
  const releaseName = this.getReleaseName(composeWorkspaces);
@@ -2554,6 +2934,50 @@ var ReleaseParams = class {
2554
2934
  releaseBranch: releaseBranchName
2555
2935
  };
2556
2936
  }
2937
+ /**
2938
+ * Generates a title for the release pull request
2939
+ *
2940
+ * Uses the configured PR title template or falls back to the default.
2941
+ * Supports variable interpolation with release parameters and context.
2942
+ *
2943
+ * Template Variables:
2944
+ * - ${tagName}: Release tag name
2945
+ * - ${pkgName}: Package/branch name
2946
+ * - All template context properties
2947
+ *
2948
+ * @param releaseBranchParams - Release branch parameters
2949
+ * @param context - Template context for variable interpolation
2950
+ * @returns Formatted PR title
2951
+ *
2952
+ * @example
2953
+ * ```typescript
2954
+ * // With default template
2955
+ * const title = params.getPRTitle(
2956
+ * {
2957
+ * tagName: '1.0.0',
2958
+ * releaseBranch: 'release-1.0.0'
2959
+ * },
2960
+ * {
2961
+ * env: 'production',
2962
+ * pkgName: 'my-package'
2963
+ * }
2964
+ * );
2965
+ * // 'Release production my-package 1.0.0'
2966
+ *
2967
+ * // With custom template
2968
+ * const title = params.getPRTitle(
2969
+ * {
2970
+ * tagName: '1.0.0',
2971
+ * releaseBranch: 'release-1.0.0'
2972
+ * },
2973
+ * {
2974
+ * env: 'staging',
2975
+ * pkgName: 'my-package'
2976
+ * }
2977
+ * );
2978
+ * // Custom formatted title
2979
+ * ```
2980
+ */
2557
2981
  getPRTitle(releaseBranchParams, context) {
2558
2982
  const prTitleTpl = this.config.PRTitle || DEFAULT_PR_TITLE;
2559
2983
  return Shell.format(prTitleTpl, {
@@ -2563,10 +2987,67 @@ var ReleaseParams = class {
2563
2987
  });
2564
2988
  }
2565
2989
  /**
2566
- * Gets the body for the release pull request.
2990
+ * Generates the body content for the release pull request
2991
+ *
2992
+ * Handles both single and multi-package releases, combining
2993
+ * changelogs appropriately. For batch releases, formats each
2994
+ * package's changelog according to the batch template.
2995
+ *
2996
+ * Template Variables:
2997
+ * - ${tagName}: Release tag or combined workspace versions
2998
+ * - ${changelog}: Single changelog or combined batch changelogs
2999
+ * - All template context properties
3000
+ *
3001
+ * @param composeWorkspaces - Array of workspace configurations
3002
+ * @param releaseBranchParams - Release branch parameters
3003
+ * @param context - Template context for variable interpolation
3004
+ * @returns Formatted PR body content
2567
3005
  *
2568
- * @param options - The options containing tag name and changelog.
2569
- * @returns The formatted release pull request body.
3006
+ * @example Single package
3007
+ * ```typescript
3008
+ * const body = params.getPRBody(
3009
+ * [{
3010
+ * name: 'pkg-a',
3011
+ * version: '1.0.0',
3012
+ * changelog: '- Feature: New functionality\n- Fix: Bug fix'
3013
+ * }],
3014
+ * {
3015
+ * tagName: '1.0.0',
3016
+ * releaseBranch: 'release-1.0.0'
3017
+ * },
3018
+ * context
3019
+ * );
3020
+ * // Custom formatted body with single changelog
3021
+ * ```
3022
+ *
3023
+ * @example Multiple packages
3024
+ * ```typescript
3025
+ * const body = params.getPRBody(
3026
+ * [
3027
+ * {
3028
+ * name: 'pkg-a',
3029
+ * version: '1.0.0',
3030
+ * changelog: '- Feature: Package A changes'
3031
+ * },
3032
+ * {
3033
+ * name: 'pkg-b',
3034
+ * version: '2.0.0',
3035
+ * changelog: '- Feature: Package B changes'
3036
+ * }
3037
+ * ],
3038
+ * {
3039
+ * tagName: 'batch-2-packages-1234567890',
3040
+ * releaseBranch: 'batch-release'
3041
+ * },
3042
+ * context
3043
+ * );
3044
+ * // Formatted body with combined changelogs:
3045
+ * // ## pkg-a 1.0.0
3046
+ * // - Feature: Package A changes
3047
+ * //
3048
+ * // ## pkg-b 2.0.0
3049
+ * // - Feature: Package B changes
3050
+ * ```
2570
3051
  */
2571
3052
  getPRBody(composeWorkspaces, releaseBranchParams, context) {
2572
3053
  const PRBodyTpl = this.config.PRBody;
@@ -2592,10 +3073,36 @@ var ReleaseParams = class {
2592
3073
  import { Shell as Shell2 } from "@qlover/scripts-context";
2593
3074
  import { Octokit } from "@octokit/rest";
2594
3075
  var GithubManager = class {
3076
+ /**
3077
+ * Creates a new GithubManager instance
3078
+ *
3079
+ * @param context - Release context containing configuration
3080
+ *
3081
+ * @example
3082
+ * ```typescript
3083
+ * const manager = new GithubManager(context);
3084
+ * ```
3085
+ */
2595
3086
  constructor(context) {
2596
3087
  this.context = context;
2597
3088
  }
3089
+ /** Lazy-loaded Octokit instance */
2598
3090
  _octokit = null;
3091
+ /**
3092
+ * Gets GitHub repository information
3093
+ *
3094
+ * Retrieves the owner and repository name from the context.
3095
+ * This information is required for most GitHub API operations.
3096
+ *
3097
+ * @returns Repository owner and name
3098
+ * @throws Error if owner or repo name is not set
3099
+ *
3100
+ * @example
3101
+ * ```typescript
3102
+ * const info = manager.getGitHubUserInfo();
3103
+ * // { owner: 'org-name', repo: 'repo-name' }
3104
+ * ```
3105
+ */
2599
3106
  getGitHubUserInfo() {
2600
3107
  const { authorName, repoName } = this.context.getOptions();
2601
3108
  if (!authorName || !repoName) {
@@ -2606,6 +3113,28 @@ var GithubManager = class {
2606
3113
  repo: repoName
2607
3114
  };
2608
3115
  }
3116
+ /**
3117
+ * Gets GitHub API token
3118
+ *
3119
+ * Retrieves the GitHub API token from environment variables.
3120
+ * The token name can be configured via the tokenRef option.
3121
+ *
3122
+ * @returns GitHub API token
3123
+ * @throws Error if token is not set
3124
+ *
3125
+ * @example Default token
3126
+ * ```typescript
3127
+ * const token = manager.getToken();
3128
+ * // Uses GITHUB_TOKEN env var
3129
+ * ```
3130
+ *
3131
+ * @example Custom token
3132
+ * ```typescript
3133
+ * context.options.githubPR.tokenRef = 'CUSTOM_TOKEN';
3134
+ * const token = manager.getToken();
3135
+ * // Uses CUSTOM_TOKEN env var
3136
+ * ```
3137
+ */
2609
3138
  getToken() {
2610
3139
  const { tokenRef = "GITHUB_TOKEN" } = this.context.getOptions("githubPR");
2611
3140
  const token = this.context.env.get(tokenRef);
@@ -2616,6 +3145,24 @@ var GithubManager = class {
2616
3145
  }
2617
3146
  return token;
2618
3147
  }
3148
+ /**
3149
+ * Gets Octokit instance
3150
+ *
3151
+ * Lazily initializes and returns an Octokit instance configured
3152
+ * with the GitHub token and timeout settings.
3153
+ *
3154
+ * @returns Configured Octokit instance
3155
+ * @throws Error if token retrieval fails
3156
+ *
3157
+ * @example
3158
+ * ```typescript
3159
+ * const octokit = manager.octokit;
3160
+ * await octokit.rest.issues.create({
3161
+ * ...manager.getGitHubUserInfo(),
3162
+ * title: 'Issue title'
3163
+ * });
3164
+ * ```
3165
+ */
2619
3166
  get octokit() {
2620
3167
  if (this._octokit) {
2621
3168
  return this._octokit;
@@ -2630,9 +3177,37 @@ var GithubManager = class {
2630
3177
  this._octokit = new Octokit(options);
2631
3178
  return this._octokit;
2632
3179
  }
3180
+ /**
3181
+ * Gets logger instance
3182
+ *
3183
+ * Provides access to the context's logger for consistent
3184
+ * logging across the manager.
3185
+ *
3186
+ * @returns Logger instance
3187
+ *
3188
+ * @example
3189
+ * ```typescript
3190
+ * manager.logger.info('Creating release...');
3191
+ * manager.logger.debug('API response:', response);
3192
+ * ```
3193
+ */
2633
3194
  get logger() {
2634
3195
  return this.context.logger;
2635
3196
  }
3197
+ /**
3198
+ * Gets shell interface
3199
+ *
3200
+ * Provides access to the context's shell interface for
3201
+ * executing Git commands and other shell operations.
3202
+ *
3203
+ * @returns Shell interface
3204
+ *
3205
+ * @example
3206
+ * ```typescript
3207
+ * await manager.shell.exec('git fetch origin');
3208
+ * await manager.shell.exec(['git', 'push', 'origin', 'main']);
3209
+ * ```
3210
+ */
2636
3211
  get shell() {
2637
3212
  return this.context.shell;
2638
3213
  }
@@ -2641,6 +3216,23 @@ var GithubManager = class {
2641
3216
  *
2642
3217
  * @default `squash`
2643
3218
  */
3219
+ /**
3220
+ * Gets auto-merge type for pull requests
3221
+ *
3222
+ * Determines how pull requests should be merged when
3223
+ * auto-merge is enabled. Defaults to 'squash'.
3224
+ *
3225
+ * @returns Auto-merge type ('merge', 'squash', or 'rebase')
3226
+ *
3227
+ * @example
3228
+ * ```typescript
3229
+ * const mergeType = manager.autoMergeType;
3230
+ * // 'squash' (default)
3231
+ *
3232
+ * context.options.autoMergeType = 'rebase';
3233
+ * manager.autoMergeType; // 'rebase'
3234
+ * ```
3235
+ */
2644
3236
  get autoMergeType() {
2645
3237
  return this.context.getOptions().autoMergeType || DEFAULT_AUTO_MERGE_TYPE;
2646
3238
  }
@@ -2649,6 +3241,25 @@ var GithubManager = class {
2649
3241
  *
2650
3242
  * @default `999999`
2651
3243
  */
3244
+ /**
3245
+ * Gets pull request number for dry runs
3246
+ *
3247
+ * Returns a placeholder PR number when running in dry-run mode.
3248
+ * This allows testing PR-related functionality without creating
3249
+ * actual pull requests.
3250
+ *
3251
+ * @returns Dry run PR number (default: '999999')
3252
+ *
3253
+ * @example
3254
+ * ```typescript
3255
+ * context.dryRun = true;
3256
+ * const prNumber = manager.dryRunPRNumber;
3257
+ * // '999999'
3258
+ *
3259
+ * context.options.githubPR.dryRunPRNumber = '123456';
3260
+ * manager.dryRunPRNumber; // '123456'
3261
+ * ```
3262
+ */
2652
3263
  get dryRunPRNumber() {
2653
3264
  return this.context.getOptions("githubPR.dryRunPRNumber", "999999");
2654
3265
  }
@@ -2657,6 +3268,23 @@ var GithubManager = class {
2657
3268
  *
2658
3269
  * @default `false`
2659
3270
  */
3271
+ /**
3272
+ * Gets auto-merge setting for release PRs
3273
+ *
3274
+ * Determines whether release pull requests should be
3275
+ * automatically merged after creation. Defaults to false.
3276
+ *
3277
+ * @returns True if auto-merge is enabled
3278
+ *
3279
+ * @example
3280
+ * ```typescript
3281
+ * const autoMerge = manager.autoMergeReleasePR;
3282
+ * // false (default)
3283
+ *
3284
+ * context.options.autoMergeReleasePR = true;
3285
+ * manager.autoMergeReleasePR; // true
3286
+ * ```
3287
+ */
2660
3288
  get autoMergeReleasePR() {
2661
3289
  return this.context.getOptions("autoMergeReleasePR") || DEFAULT_AUTO_MERGE_RELEASE_PR;
2662
3290
  }
@@ -2666,6 +3294,30 @@ var GithubManager = class {
2666
3294
  * @param prNumber - The pull request number to merge.
2667
3295
  * @param releaseBranch - The branch to merge into.
2668
3296
  */
3297
+ /**
3298
+ * Merges a pull request
3299
+ *
3300
+ * Merges the specified pull request using the configured
3301
+ * merge method. In dry-run mode, logs the merge action
3302
+ * without performing it.
3303
+ *
3304
+ * @param prNumber - Pull request number
3305
+ * @param releaseBranch - Branch to merge
3306
+ * @throws Error if merge fails
3307
+ *
3308
+ * @example Basic merge
3309
+ * ```typescript
3310
+ * await manager.mergePR('123', 'release-1.0.0');
3311
+ * // Merges PR #123 using configured merge method
3312
+ * ```
3313
+ *
3314
+ * @example Dry run
3315
+ * ```typescript
3316
+ * context.dryRun = true;
3317
+ * await manager.mergePR('123', 'release-1.0.0');
3318
+ * // Logs merge action without performing it
3319
+ * ```
3320
+ */
2669
3321
  async mergePR(prNumber, releaseBranch) {
2670
3322
  if (!prNumber) {
2671
3323
  this.logger.error("Failed to create Pull Request.", prNumber);
@@ -2685,6 +3337,24 @@ var GithubManager = class {
2685
3337
  merge_method: mergeMethod
2686
3338
  });
2687
3339
  }
3340
+ /**
3341
+ * Gets commits from a pull request
3342
+ *
3343
+ * Retrieves all commits associated with the specified pull request.
3344
+ * Useful for generating changelogs or analyzing changes.
3345
+ *
3346
+ * @param prNumber - Pull request number
3347
+ * @returns Promise resolving to array of commit information
3348
+ * @throws Error if request fails
3349
+ *
3350
+ * @example
3351
+ * ```typescript
3352
+ * const commits = await manager.getPullRequestCommits(123);
3353
+ * commits.forEach(commit => {
3354
+ * console.log(commit.sha, commit.commit.message);
3355
+ * });
3356
+ * ```
3357
+ */
2688
3358
  async getPullRequestCommits(prNumber) {
2689
3359
  const pr = await this.octokit.rest.pulls.listCommits({
2690
3360
  ...this.getGitHubUserInfo(),
@@ -2692,6 +3362,23 @@ var GithubManager = class {
2692
3362
  });
2693
3363
  return pr.data;
2694
3364
  }
3365
+ /**
3366
+ * Gets detailed information about a commit
3367
+ *
3368
+ * Retrieves detailed information about a specific commit,
3369
+ * including files changed, author details, and commit message.
3370
+ *
3371
+ * @param commitSha - Commit SHA
3372
+ * @returns Promise resolving to commit information
3373
+ * @throws Error if request fails
3374
+ *
3375
+ * @example
3376
+ * ```typescript
3377
+ * const info = await manager.getCommitInfo('abc123');
3378
+ * console.log(info.commit.message);
3379
+ * console.log(info.files.map(f => f.filename));
3380
+ * ```
3381
+ */
2695
3382
  async getCommitInfo(commitSha) {
2696
3383
  const pr = await this.octokit.rest.repos.getCommit({
2697
3384
  ...this.getGitHubUserInfo(),
@@ -2699,6 +3386,24 @@ var GithubManager = class {
2699
3386
  });
2700
3387
  return pr.data;
2701
3388
  }
3389
+ /**
3390
+ * Gets pull request information
3391
+ *
3392
+ * Retrieves detailed information about a pull request,
3393
+ * including title, body, labels, and review status.
3394
+ *
3395
+ * @param prNumber - Pull request number
3396
+ * @returns Promise resolving to pull request information
3397
+ * @throws Error if request fails
3398
+ *
3399
+ * @example
3400
+ * ```typescript
3401
+ * const pr = await manager.getPullRequest(123);
3402
+ * console.log(pr.title);
3403
+ * console.log(pr.labels.map(l => l.name));
3404
+ * console.log(pr.mergeable_state);
3405
+ * ```
3406
+ */
2702
3407
  async getPullRequest(prNumber) {
2703
3408
  const pr = await this.octokit.rest.pulls.get({
2704
3409
  ...this.getGitHubUserInfo(),
@@ -2712,6 +3417,27 @@ var GithubManager = class {
2712
3417
  * @param prNumber - The pull request number to check.
2713
3418
  * @param releaseBranch - The branch to check against.
2714
3419
  */
3420
+ /**
3421
+ * Checks pull request status and cleans up
3422
+ *
3423
+ * Verifies pull request status and deletes the release branch
3424
+ * if the PR has been merged. Used for post-merge cleanup.
3425
+ *
3426
+ * Process:
3427
+ * 1. Verify PR exists and status
3428
+ * 2. Delete release branch if PR merged
3429
+ * 3. Log cleanup results
3430
+ *
3431
+ * @param prNumber - Pull request number
3432
+ * @param releaseBranch - Branch to clean up
3433
+ * @throws Error if verification or cleanup fails
3434
+ *
3435
+ * @example
3436
+ * ```typescript
3437
+ * await manager.checkedPR('123', 'release-1.0.0');
3438
+ * // Verifies PR #123 and deletes release-1.0.0 if merged
3439
+ * ```
3440
+ */
2715
3441
  async checkedPR(prNumber, releaseBranch) {
2716
3442
  try {
2717
3443
  await this.getPullRequest(Number(prNumber));
@@ -2812,10 +3538,55 @@ var GithubManager = class {
2812
3538
  throw error;
2813
3539
  }
2814
3540
  }
3541
+ /**
3542
+ * Truncates long PR/release body text
3543
+ *
3544
+ * GitHub has a limit on PR and release body length.
3545
+ * This method ensures the text stays within limits by
3546
+ * truncating if necessary.
3547
+ *
3548
+ * @param body - Body text to truncate
3549
+ * @returns Truncated text (if > 124000 chars)
3550
+ *
3551
+ * @example
3552
+ * ```typescript
3553
+ * const body = manager.truncateBody(veryLongText);
3554
+ * // Returns truncated text if > 124000 chars
3555
+ * // Adds '...' to indicate truncation
3556
+ * ```
3557
+ * @private
3558
+ */
2815
3559
  truncateBody(body) {
2816
3560
  if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
2817
3561
  return body;
2818
3562
  }
3563
+ /**
3564
+ * Builds GitHub release options
3565
+ *
3566
+ * Combines default release options with provided overrides
3567
+ * and context configuration. Handles formatting of release
3568
+ * name, body, and other settings.
3569
+ *
3570
+ * @param options - Override options for release
3571
+ * @returns Complete release options
3572
+ *
3573
+ * @example
3574
+ * ```typescript
3575
+ * const opts = manager.getOctokitReleaseOptions({
3576
+ * tag_name: 'v1.0.0',
3577
+ * body: 'Release notes...'
3578
+ * });
3579
+ * // Returns merged options with defaults:
3580
+ * // {
3581
+ * // name: 'Release v1.0.0',
3582
+ * // body: 'Release notes...',
3583
+ * // draft: false,
3584
+ * // prerelease: false,
3585
+ * // ...
3586
+ * // }
3587
+ * ```
3588
+ * @private
3589
+ */
2819
3590
  getOctokitReleaseOptions(options) {
2820
3591
  const {
2821
3592
  releaseName,
@@ -2841,6 +3612,37 @@ var GithubManager = class {
2841
3612
  ...this.getGitHubUserInfo()
2842
3613
  };
2843
3614
  }
3615
+ /**
3616
+ * Creates a GitHub release
3617
+ *
3618
+ * Creates a new GitHub release for a workspace with:
3619
+ * - Formatted release name
3620
+ * - Changelog as release notes
3621
+ * - Proper tag
3622
+ * - Configurable settings (draft, prerelease, etc.)
3623
+ *
3624
+ * Handles dry run mode and error cases gracefully.
3625
+ *
3626
+ * @param workspace - Workspace to create release for
3627
+ * @throws Error if tag name is missing or creation fails
3628
+ *
3629
+ * @example Basic release
3630
+ * ```typescript
3631
+ * await manager.createRelease({
3632
+ * name: 'pkg-a',
3633
+ * version: '1.0.0',
3634
+ * tagName: 'v1.0.0',
3635
+ * changelog: '...'
3636
+ * });
3637
+ * ```
3638
+ *
3639
+ * @example Dry run
3640
+ * ```typescript
3641
+ * context.dryRun = true;
3642
+ * await manager.createRelease(workspace);
3643
+ * // Logs release info without creating
3644
+ * ```
3645
+ */
2844
3646
  async createRelease(workspace) {
2845
3647
  const meragedOptions = this.getOctokitReleaseOptions({
2846
3648
  tag_name: workspace.tagName,
@@ -2880,6 +3682,30 @@ var GithubManager = class {
2880
3682
  var import_isString = __toESM(require_isString(), 1);
2881
3683
  import { ScriptPlugin } from "@qlover/scripts-context";
2882
3684
  var GitBase = class extends ScriptPlugin {
3685
+ /**
3686
+ * Plugin initialization hook
3687
+ *
3688
+ * Runs before plugin execution to set up repository context:
3689
+ * 1. Retrieves repository information
3690
+ * 2. Gets current branch
3691
+ * 3. Switches to current branch if needed
3692
+ * 4. Updates context with repository info
3693
+ *
3694
+ * @throws Error if repository information cannot be retrieved
3695
+ *
3696
+ * @example
3697
+ * ```typescript
3698
+ * class MyPlugin extends GitBase<GitBaseProps> {
3699
+ * async onExec() {
3700
+ * // onBefore has already:
3701
+ * // - Set up repository info
3702
+ * // - Switched to correct branch
3703
+ * // - Updated context
3704
+ * await this.doSomething();
3705
+ * }
3706
+ * }
3707
+ * ```
3708
+ */
2883
3709
  async onBefore() {
2884
3710
  const repoInfo = await this.getUserInfo();
2885
3711
  if (!repoInfo) {
@@ -2900,12 +3726,44 @@ var GitBase = class extends ScriptPlugin {
2900
3726
  currentBranch
2901
3727
  });
2902
3728
  }
3729
+ /**
3730
+ * Gets the current Git branch name
3731
+ *
3732
+ * Retrieves the name of the currently checked out Git branch.
3733
+ * Includes a small delay to ensure Git's internal state is updated.
3734
+ *
3735
+ * @returns Promise resolving to branch name
3736
+ * @throws Error if branch name cannot be retrieved
3737
+ *
3738
+ * @example
3739
+ * ```typescript
3740
+ * const branch = await plugin.getCurrentBranch();
3741
+ * // 'main' or 'feature/new-feature'
3742
+ * ```
3743
+ */
2903
3744
  async getCurrentBranch() {
2904
3745
  await new Promise((resolve2) => setTimeout(resolve2, 100));
2905
3746
  return this.context.shell.exec("git rev-parse --abbrev-ref HEAD", {
2906
3747
  dryRun: false
2907
3748
  });
2908
3749
  }
3750
+ /**
3751
+ * Gets the Git remote URL
3752
+ *
3753
+ * Retrieves the URL of the 'origin' remote from Git configuration.
3754
+ * This URL is used to identify the GitHub repository.
3755
+ *
3756
+ * @returns Promise resolving to remote URL
3757
+ * @throws Error if remote URL cannot be retrieved
3758
+ *
3759
+ * @example
3760
+ * ```typescript
3761
+ * const url = await plugin.getRemoteUrl();
3762
+ * // 'https://github.com/org/repo.git'
3763
+ * // or
3764
+ * // 'git@github.com:org/repo.git'
3765
+ * ```
3766
+ */
2909
3767
  async getRemoteUrl() {
2910
3768
  return (await this.context.shell.exec("git config --get remote.origin.url", {
2911
3769
  dryRun: false
@@ -2959,9 +3817,53 @@ var GitBase = class extends ScriptPlugin {
2959
3817
  * @param value - The value to check.
2960
3818
  * @returns True if the value is a valid string, otherwise false.
2961
3819
  */
3820
+ /**
3821
+ * Type guard for valid string values
3822
+ *
3823
+ * Checks if a value is a non-empty string. Used for validating
3824
+ * repository information and other string inputs.
3825
+ *
3826
+ * @param value - Value to check
3827
+ * @returns True if value is a non-empty string
3828
+ *
3829
+ * @example
3830
+ * ```typescript
3831
+ * if (plugin.isValidString(value)) {
3832
+ * // value is definitely a non-empty string
3833
+ * console.log(value.toUpperCase());
3834
+ * }
3835
+ * ```
3836
+ */
2962
3837
  isValidString(value) {
2963
3838
  return !!value && (0, import_isString.default)(value);
2964
3839
  }
3840
+ /**
3841
+ * Creates a Git commit
3842
+ *
3843
+ * Creates a new Git commit with the specified message and optional
3844
+ * additional arguments. The message is automatically JSON-stringified
3845
+ * to handle special characters properly.
3846
+ *
3847
+ * @param message - Commit message
3848
+ * @param args - Additional Git commit arguments
3849
+ * @returns Promise resolving to command output
3850
+ *
3851
+ * @example Basic commit
3852
+ * ```typescript
3853
+ * await plugin.commit('feat: add new feature');
3854
+ * ```
3855
+ *
3856
+ * @example Commit with arguments
3857
+ * ```typescript
3858
+ * await plugin.commit('fix: update deps', ['--no-verify']);
3859
+ * ```
3860
+ *
3861
+ * @example Commit with special characters
3862
+ * ```typescript
3863
+ * await plugin.commit('fix: handle "quotes" & symbols');
3864
+ * // Message is automatically escaped
3865
+ * ```
3866
+ */
2965
3867
  commit(message, args = []) {
2966
3868
  return this.context.shell.exec([
2967
3869
  "git",
@@ -2996,14 +3898,50 @@ var CHANGELOG_ALL_FIELDS = [
2996
3898
  "tag"
2997
3899
  ];
2998
3900
  var GitChangelog = class {
3901
+ /**
3902
+ * Creates a new GitChangelog instance
3903
+ *
3904
+ * @param options - Configuration options including shell and logger
3905
+ *
3906
+ * @example
3907
+ * ```typescript
3908
+ * const changelog = new GitChangelog({
3909
+ * shell: new Shell(),
3910
+ * logger: new Logger(),
3911
+ * directory: 'packages/my-pkg',
3912
+ * noMerges: true
3913
+ * });
3914
+ * ```
3915
+ */
2999
3916
  constructor(options) {
3000
3917
  this.options = options;
3001
3918
  }
3002
3919
  /**
3003
- * Get the git log
3920
+ * Retrieves Git commit history with specified options
3921
+ *
3922
+ * Fetches commit information between specified tags or commits,
3923
+ * with support for filtering and field selection.
3924
+ *
3925
+ * @param options - Configuration options for Git log retrieval
3926
+ * @returns Array of commit objects with requested fields
3004
3927
  *
3005
- * @param options
3006
- * @returns
3928
+ * @example Basic usage
3929
+ * ```typescript
3930
+ * const commits = await changelog.getGitLog({
3931
+ * from: 'v1.0.0',
3932
+ * to: 'v2.0.0',
3933
+ * directory: 'packages/my-pkg',
3934
+ * noMerges: true
3935
+ * });
3936
+ * ```
3937
+ *
3938
+ * @example Custom fields
3939
+ * ```typescript
3940
+ * const commits = await changelog.getGitLog({
3941
+ * fields: ['hash', 'subject', 'authorName'],
3942
+ * directory: 'src'
3943
+ * });
3944
+ * ```
3007
3945
  */
3008
3946
  async getGitLog(options = {}) {
3009
3947
  const { directory, noMerges = true, fields } = options;
@@ -3024,6 +3962,38 @@ var GitChangelog = class {
3024
3962
  this.options.logger?.debug("GitChangelog commits", commits);
3025
3963
  return commits;
3026
3964
  }
3965
+ /**
3966
+ * Retrieves and parses Git commits with metadata
3967
+ *
3968
+ * Gets commit history and enhances it with parsed conventional
3969
+ * commit information and PR metadata.
3970
+ *
3971
+ * @param options - Configuration options for Git log retrieval
3972
+ * @returns Array of enhanced commit objects with parsed metadata
3973
+ *
3974
+ * @example Basic usage
3975
+ * ```typescript
3976
+ * const commits = await changelog.getCommits({
3977
+ * from: 'v1.0.0',
3978
+ * to: 'v2.0.0'
3979
+ * });
3980
+ * // [
3981
+ * // {
3982
+ * // base: { hash: '...', subject: '...' },
3983
+ * // commitlint: { type: 'feat', scope: 'api', ... },
3984
+ * // commits: []
3985
+ * // }
3986
+ * // ]
3987
+ * ```
3988
+ *
3989
+ * @example Filtered commits
3990
+ * ```typescript
3991
+ * const commits = await changelog.getCommits({
3992
+ * directory: 'packages/my-pkg',
3993
+ * noMerges: true
3994
+ * });
3995
+ * ```
3996
+ */
3027
3997
  async getCommits(options) {
3028
3998
  const gitCommits = await this.getGitLog(options);
3029
3999
  return gitCommits.map((commit) => {
@@ -3036,6 +4006,28 @@ var GitChangelog = class {
3036
4006
  };
3037
4007
  });
3038
4008
  }
4009
+ /**
4010
+ * Creates a base commit object from message and optional data
4011
+ *
4012
+ * Utility method to create a standardized commit object with
4013
+ * basic metadata. Used internally for commit value creation.
4014
+ *
4015
+ * @param message - Commit message
4016
+ * @param target - Optional additional commit data
4017
+ * @returns Base commit object
4018
+ * @protected
4019
+ *
4020
+ * @example
4021
+ * ```typescript
4022
+ * const commit = changelog.createBaseCommit(
4023
+ * 'feat: new feature',
4024
+ * {
4025
+ * hash: 'abc123',
4026
+ * authorName: 'John Doe'
4027
+ * }
4028
+ * );
4029
+ * ```
4030
+ */
3039
4031
  createBaseCommit(message, target) {
3040
4032
  return {
3041
4033
  subject: message,
@@ -3045,16 +4037,66 @@ var GitChangelog = class {
3045
4037
  };
3046
4038
  }
3047
4039
  /**
3048
- * Tabify the body
4040
+ * Indents each line of a text block
4041
+ *
4042
+ * Adds specified number of spaces to the start of each line
4043
+ * in a multi-line string. Used for formatting commit body text.
3049
4044
  *
3050
4045
  * @since 2.3.2
3051
- * @param body
3052
- * @param size
3053
- * @returns
4046
+ * @param body - Text to indent
4047
+ * @param size - Number of spaces to add (default: 2)
4048
+ * @returns Indented text
4049
+ *
4050
+ * @example
4051
+ * ```typescript
4052
+ * const text = changelog.tabify(
4053
+ * 'Line 1\nLine 2\nLine 3',
4054
+ * 4
4055
+ * );
4056
+ * // ' Line 1\n Line 2\n Line 3'
4057
+ * ```
3054
4058
  */
3055
4059
  tabify(body, size = 2) {
3056
4060
  return body.split("\n").map((line) => " ".repeat(size) + line.trim()).join("\n");
3057
4061
  }
4062
+ /**
4063
+ * Parses a commit message into conventional commit format
4064
+ *
4065
+ * Extracts type, scope, message, and body from a commit message
4066
+ * following the conventional commit specification.
4067
+ *
4068
+ * Format: type(scope): message
4069
+ *
4070
+ * @param subject - Commit subject line
4071
+ * @param rawBody - Full commit message body
4072
+ * @returns Parsed conventional commit data
4073
+ *
4074
+ * @example Basic commit
4075
+ * ```typescript
4076
+ * const commit = changelog.parseCommitlint(
4077
+ * 'feat(api): add new endpoint'
4078
+ * );
4079
+ * // {
4080
+ * // type: 'feat',
4081
+ * // scope: 'api',
4082
+ * // message: 'add new endpoint'
4083
+ * // }
4084
+ * ```
4085
+ *
4086
+ * @example With body
4087
+ * ```typescript
4088
+ * const commit = changelog.parseCommitlint(
4089
+ * 'fix(core): memory leak',
4090
+ * 'Fixed memory leak in core module\n\nBREAKING CHANGE: API changed'
4091
+ * );
4092
+ * // {
4093
+ * // type: 'fix',
4094
+ * // scope: 'core',
4095
+ * // message: 'memory leak',
4096
+ * // body: ' Fixed memory leak in core module\n\n BREAKING CHANGE: API changed'
4097
+ * // }
4098
+ * ```
4099
+ */
3058
4100
  parseCommitlint(subject, rawBody = "") {
3059
4101
  const [title] = subject.trim().split("\n");
3060
4102
  const bodyLines = rawBody.startsWith(title) ? rawBody.replace(title, "") : rawBody;
@@ -3073,6 +4115,51 @@ var GitChangelog = class {
3073
4115
  body: bodyLines ? this.tabify(bodyLines) : void 0
3074
4116
  };
3075
4117
  }
4118
+ /**
4119
+ * Creates a complete commit value object from hash and message
4120
+ *
4121
+ * Combines commit hash, parsed conventional commit data, and
4122
+ * PR information into a single commit value object.
4123
+ *
4124
+ * @param hash - Commit hash
4125
+ * @param message - Full commit message
4126
+ * @returns Complete commit value object
4127
+ *
4128
+ * @example Basic commit
4129
+ * ```typescript
4130
+ * const commit = changelog.toCommitValue(
4131
+ * 'abc123',
4132
+ * 'feat(api): new endpoint'
4133
+ * );
4134
+ * // {
4135
+ * // base: {
4136
+ * // hash: 'abc123',
4137
+ * // abbrevHash: 'abc123',
4138
+ * // subject: 'feat(api): new endpoint'
4139
+ * // },
4140
+ * // commitlint: {
4141
+ * // type: 'feat',
4142
+ * // scope: 'api',
4143
+ * // message: 'new endpoint'
4144
+ * // },
4145
+ * // commits: []
4146
+ * // }
4147
+ * ```
4148
+ *
4149
+ * @example PR commit
4150
+ * ```typescript
4151
+ * const commit = changelog.toCommitValue(
4152
+ * 'def456',
4153
+ * 'fix(core): memory leak (#123)'
4154
+ * );
4155
+ * // {
4156
+ * // base: { hash: 'def456', ... },
4157
+ * // commitlint: { type: 'fix', ... },
4158
+ * // commits: [],
4159
+ * // prNumber: '123'
4160
+ * // }
4161
+ * ```
4162
+ */
3076
4163
  toCommitValue(hash, message) {
3077
4164
  const [title] = message.trim().split("\n");
3078
4165
  const prMatch = title.match(/\(#(\d+)\)/);
@@ -3090,6 +4177,33 @@ var GitChangelog = class {
3090
4177
  prNumber: prMatch?.[1]
3091
4178
  };
3092
4179
  }
4180
+ /**
4181
+ * Resolves a Git tag or reference to a valid commit reference
4182
+ *
4183
+ * Attempts to resolve a tag name to a valid Git reference.
4184
+ * Falls back to root commit or HEAD if tag doesn't exist.
4185
+ *
4186
+ * @param tag - Tag name to resolve
4187
+ * @param fallback - Fallback value ('root' or 'HEAD')
4188
+ * @returns Resolved Git reference
4189
+ * @protected
4190
+ *
4191
+ * @example Basic tag resolution
4192
+ * ```typescript
4193
+ * const ref = await changelog.resolveTag('v1.0.0');
4194
+ * // 'v1.0.0' if tag exists
4195
+ * // 'HEAD' if tag doesn't exist
4196
+ * ```
4197
+ *
4198
+ * @example Root commit fallback
4199
+ * ```typescript
4200
+ * const ref = await changelog.resolveTag(
4201
+ * 'non-existent-tag',
4202
+ * 'root'
4203
+ * );
4204
+ * // First commit hash if tag doesn't exist
4205
+ * ```
4206
+ */
3093
4207
  async resolveTag(tag, fallback) {
3094
4208
  if (tag) {
3095
4209
  try {
@@ -3110,9 +4224,75 @@ var import_groupBy = __toESM(require_groupBy(), 1);
3110
4224
  import { Shell as Shell3 } from "@qlover/scripts-context";
3111
4225
  var DEFAULT_TEMPLATE = "\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}";
3112
4226
  var GitChangelogFormatter = class {
4227
+ /**
4228
+ * Creates a new GitChangelogFormatter instance
4229
+ *
4230
+ * @param options - Configuration options including shell interface
4231
+ *
4232
+ * @example
4233
+ * ```typescript
4234
+ * const formatter = new GitChangelogFormatter({
4235
+ * shell: new Shell(),
4236
+ * repoUrl: 'https://github.com/org/repo',
4237
+ * types: [
4238
+ * { type: 'feat', section: '### Features' }
4239
+ * ],
4240
+ * formatTemplate: '- ${commitlint.message}'
4241
+ * });
4242
+ * ```
4243
+ */
3113
4244
  constructor(options) {
3114
4245
  this.options = options;
3115
4246
  }
4247
+ /**
4248
+ * Formats an array of commits into changelog entries
4249
+ *
4250
+ * Groups commits by type and formats them according to the
4251
+ * configured template and options. Supports commit body
4252
+ * inclusion and type-based sections.
4253
+ *
4254
+ * @param commits - Array of commit values to format
4255
+ * @param options - Optional formatting options
4256
+ * @returns Array of formatted changelog lines
4257
+ *
4258
+ * @example Basic formatting
4259
+ * ```typescript
4260
+ * const changelog = formatter.format([
4261
+ * {
4262
+ * base: { hash: 'abc123' },
4263
+ * commitlint: {
4264
+ * type: 'feat',
4265
+ * scope: 'api',
4266
+ * message: 'new endpoint'
4267
+ * }
4268
+ * }
4269
+ * ]);
4270
+ * // [
4271
+ * // '### Features',
4272
+ * // '- **api:** new endpoint ([abc123](...))'
4273
+ * // ]
4274
+ * ```
4275
+ *
4276
+ * @example With commit body
4277
+ * ```typescript
4278
+ * const changelog = formatter.format(
4279
+ * [{
4280
+ * commitlint: {
4281
+ * type: 'fix',
4282
+ * message: 'memory leak',
4283
+ * body: 'Fixed memory allocation\nAdded cleanup'
4284
+ * }
4285
+ * }],
4286
+ * { commitBody: true }
4287
+ * );
4288
+ * // [
4289
+ * // '### Bug Fixes',
4290
+ * // '- memory leak',
4291
+ * // ' Fixed memory allocation',
4292
+ * // ' Added cleanup'
4293
+ * // ]
4294
+ * ```
4295
+ */
3116
4296
  format(commits, options) {
3117
4297
  const { types = [], commitBody = false } = { ...this.options, ...options };
3118
4298
  const changelog = [];
@@ -3139,6 +4319,41 @@ var GitChangelogFormatter = class {
3139
4319
  });
3140
4320
  return changelog;
3141
4321
  }
4322
+ /**
4323
+ * Formats a single commit into a changelog entry
4324
+ *
4325
+ * Applies the configured template to a commit, including
4326
+ * scope formatting, PR links, and commit hash links.
4327
+ *
4328
+ * @param commit - Commit value to format
4329
+ * @param options - Optional formatting options
4330
+ * @returns Formatted changelog entry
4331
+ *
4332
+ * @example Basic formatting
4333
+ * ```typescript
4334
+ * const entry = formatter.formatCommit({
4335
+ * base: { hash: 'abc123' },
4336
+ * commitlint: {
4337
+ * type: 'feat',
4338
+ * scope: 'api',
4339
+ * message: 'new endpoint'
4340
+ * }
4341
+ * });
4342
+ * // '- **api:** new endpoint ([abc123](...))'
4343
+ * ```
4344
+ *
4345
+ * @example With PR number
4346
+ * ```typescript
4347
+ * const entry = formatter.formatCommit({
4348
+ * base: { hash: 'def456' },
4349
+ * commitlint: {
4350
+ * message: 'fix bug'
4351
+ * },
4352
+ * prNumber: '123'
4353
+ * });
4354
+ * // '- fix bug ([def456](...)) (#123)'
4355
+ * ```
4356
+ */
3142
4357
  formatCommit(commit, options) {
3143
4358
  const {
3144
4359
  commitlint,
@@ -3165,12 +4380,65 @@ var GitChangelogFormatter = class {
3165
4380
  prLink
3166
4381
  });
3167
4382
  }
4383
+ /**
4384
+ * Formats a target string as a Markdown link
4385
+ *
4386
+ * Creates a Markdown-formatted link with optional URL.
4387
+ * If no URL is provided, formats as a plain reference.
4388
+ *
4389
+ * @param target - Text to display
4390
+ * @param url - Optional URL for the link
4391
+ * @returns Formatted Markdown link
4392
+ *
4393
+ * @example With URL
4394
+ * ```typescript
4395
+ * const link = formatter.foramtLink('abc123', 'https://github.com/org/repo/commit/abc123');
4396
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
4397
+ * ```
4398
+ *
4399
+ * @example Without URL
4400
+ * ```typescript
4401
+ * const link = formatter.foramtLink('abc123');
4402
+ * // '(abc123)'
4403
+ * ```
4404
+ */
3168
4405
  foramtLink(target, url) {
3169
4406
  return url ? `([${target}](${url}))` : `(${target})`;
3170
4407
  }
4408
+ /**
4409
+ * Formats a commit hash as a Markdown link
4410
+ *
4411
+ * @deprecated Use foramtLink instead
4412
+ * @param target - Commit hash to display
4413
+ * @param url - Optional URL to the commit
4414
+ * @returns Formatted Markdown link
4415
+ *
4416
+ * @example
4417
+ * ```typescript
4418
+ * const link = formatter.formatCommitLink(
4419
+ * 'abc123',
4420
+ * 'https://github.com/org/repo/commit/abc123'
4421
+ * );
4422
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
4423
+ * ```
4424
+ */
3171
4425
  formatCommitLink(target, url) {
3172
4426
  return url ? `([${target}](${url}))` : `(${target})`;
3173
4427
  }
4428
+ /**
4429
+ * Formats a commit scope in Markdown
4430
+ *
4431
+ * Wraps the scope in bold syntax and adds a colon.
4432
+ *
4433
+ * @param scope - Scope to format
4434
+ * @returns Formatted scope in Markdown
4435
+ *
4436
+ * @example
4437
+ * ```typescript
4438
+ * const scope = formatter.formatScope('api');
4439
+ * // '**api:**'
4440
+ * ```
4441
+ */
3174
4442
  formatScope(scope) {
3175
4443
  return `**${scope}:**`;
3176
4444
  }
@@ -3224,7 +4492,30 @@ var Pather = class {
3224
4492
  return child[boundaryIndex] === sep;
3225
4493
  }
3226
4494
  /**
3227
- * Normalised `startsWith` helper.
4495
+ * Normalized path prefix check
4496
+ *
4497
+ * Checks if sourcePath starts with targetPath after normalization.
4498
+ * Handles cross-platform path separators and trailing separators.
4499
+ *
4500
+ * @param sourcePath - Path to check
4501
+ * @param targetPath - Prefix path to match
4502
+ * @returns True if sourcePath starts with targetPath
4503
+ *
4504
+ * @example Basic usage
4505
+ * ```typescript
4506
+ * const pather = new Pather();
4507
+ *
4508
+ * pather.startsWith('src/utils/file.ts', 'src') // true
4509
+ * pather.startsWith('src\\utils\\file.ts', 'src') // true
4510
+ * pather.startsWith('lib/utils/file.ts', 'src') // false
4511
+ * ```
4512
+ *
4513
+ * @example Trailing separators
4514
+ * ```typescript
4515
+ * pather.startsWith('src/utils', 'src/') // true
4516
+ * pather.startsWith('src/utils/', 'src') // true
4517
+ * pather.startsWith('src2/utils', 'src') // false
4518
+ * ```
3228
4519
  */
3229
4520
  startsWith(sourcePath, targetPath) {
3230
4521
  let src = this.toLocalPath(sourcePath);
@@ -3238,7 +4529,44 @@ var Pather = class {
3238
4529
  return src.startsWith(tgt);
3239
4530
  }
3240
4531
  /**
3241
- * Segment-aware containment check (not mere substring).
4532
+ * Segment-aware path containment check
4533
+ *
4534
+ * Checks if sourcePath contains targetPath as a complete path segment.
4535
+ * Unlike simple substring matching, this ensures proper path boundaries.
4536
+ * For example, 'src/abc' does not contain 'src/a' even though 'src/a'
4537
+ * is a substring.
4538
+ *
4539
+ * Features:
4540
+ * - Cross-platform path handling
4541
+ * - Proper segment boundary checking
4542
+ * - Trailing separator normalization
4543
+ * - Exact match support
4544
+ *
4545
+ * @param sourcePath - Path to search in
4546
+ * @param targetPath - Path to search for
4547
+ * @returns True if sourcePath contains targetPath as a segment
4548
+ *
4549
+ * @example Basic usage
4550
+ * ```typescript
4551
+ * const pather = new Pather();
4552
+ *
4553
+ * pather.containsPath('src/utils/file.ts', 'utils') // true
4554
+ * pather.containsPath('src/utils/file.ts', 'src/utils') // true
4555
+ * pather.containsPath('src/utils/file.ts', 'til') // false
4556
+ * ```
4557
+ *
4558
+ * @example Segment boundaries
4559
+ * ```typescript
4560
+ * pather.containsPath('src/abc/file.ts', 'src/a') // false
4561
+ * pather.containsPath('src/abc/file.ts', 'src/abc') // true
4562
+ * ```
4563
+ *
4564
+ * @example Trailing separators
4565
+ * ```typescript
4566
+ * pather.containsPath('src/utils/', 'utils') // true
4567
+ * pather.containsPath('src/utils', 'utils/') // true
4568
+ * pather.containsPath('src/utils/', 'utils/') // true
4569
+ * ```
3242
4570
  */
3243
4571
  containsPath(sourcePath, targetPath) {
3244
4572
  let src = this.toLocalPath(sourcePath);
@@ -3264,18 +4592,49 @@ var Pather = class {
3264
4592
  // src/plugins/githubPR/GithubChangelog.ts
3265
4593
  var DOMAIN = "https://github.com";
3266
4594
  var GithubChangelog = class _GithubChangelog extends GitChangelog {
4595
+ /**
4596
+ * Creates a new GitHub changelog generator
4597
+ *
4598
+ * @param options - Changelog generation options
4599
+ * @param githubManager - GitHub API manager
4600
+ *
4601
+ * @example
4602
+ * ```typescript
4603
+ * const changelog = new GithubChangelog({
4604
+ * shell,
4605
+ * logger,
4606
+ * mergePRcommit: true,
4607
+ * githubRootPath: 'https://github.com/org/repo'
4608
+ * }, githubManager);
4609
+ * ```
4610
+ */
3267
4611
  constructor(options, githubManager) {
3268
4612
  super(options);
3269
4613
  this.options = options;
3270
4614
  this.githubManager = githubManager;
3271
4615
  }
4616
+ /** Path manipulation utility */
3272
4617
  pather = new Pather();
3273
4618
  /**
3274
- * Filter commits by directory
3275
- * @param commits - commits
3276
- * @param directory - directory
3277
- * @returns filtered commits
4619
+ * Filters commits by directory
4620
+ *
4621
+ * Filters commits based on whether they contain changes in
4622
+ * the specified directory. Uses GitHub API to get detailed
4623
+ * commit information.
4624
+ *
4625
+ * @param commits - Array of commits to filter
4626
+ * @param directory - Directory path to filter by
4627
+ * @returns Promise resolving to filtered commits
3278
4628
  * @since 2.4.0
4629
+ *
4630
+ * @example
4631
+ * ```typescript
4632
+ * const commits = await changelog.filterCommitsByDirectory(
4633
+ * allCommits,
4634
+ * 'packages/pkg-a'
4635
+ * );
4636
+ * // Only commits that modified files in packages/pkg-a
4637
+ * ```
3279
4638
  */
3280
4639
  async filterCommitsByDirectory(commits, directory) {
3281
4640
  const result = [];
@@ -3293,6 +4652,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
3293
4652
  }
3294
4653
  return result;
3295
4654
  }
4655
+ /**
4656
+ * Gets complete commit information with PR details
4657
+ *
4658
+ * Retrieves commits and enhances them with pull request
4659
+ * information. For commits associated with PRs, includes
4660
+ * all PR commits and filters by directory.
4661
+ *
4662
+ * Process:
4663
+ * 1. Get base commits
4664
+ * 2. Extract PR numbers
4665
+ * 3. Fetch PR commits
4666
+ * 4. Filter by directory
4667
+ * 5. Flatten results
4668
+ *
4669
+ * @param options - Changelog options
4670
+ * @returns Promise resolving to enhanced commits
4671
+ *
4672
+ * @example Basic usage
4673
+ * ```typescript
4674
+ * const commits = await changelog.getFullCommit({
4675
+ * from: 'v1.0.0',
4676
+ * directory: 'packages/pkg-a'
4677
+ * });
4678
+ * // Returns commits with PR information
4679
+ * ```
4680
+ *
4681
+ * @example With PR merging
4682
+ * ```typescript
4683
+ * const commits = await changelog.getFullCommit({
4684
+ * mergePRcommit: true,
4685
+ * directory: 'packages/pkg-a'
4686
+ * });
4687
+ * // Includes all PR commits
4688
+ * ```
4689
+ */
3296
4690
  async getFullCommit(options) {
3297
4691
  const _options = { ...this.options, ...options };
3298
4692
  const allCommits = await this.getCommits(_options);
@@ -3320,6 +4714,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
3320
4714
  );
3321
4715
  return newallCommits.flat();
3322
4716
  }
4717
+ /**
4718
+ * Transforms workspaces with GitHub changelogs
4719
+ *
4720
+ * Processes each workspace to add GitHub-specific changelog
4721
+ * information. Includes:
4722
+ * - GitHub repository URL
4723
+ * - PR-aware commit history
4724
+ * - Formatted changelog with links
4725
+ *
4726
+ * Process:
4727
+ * 1. Build GitHub root path
4728
+ * 2. Configure changelog options
4729
+ * 3. Get commits for each workspace
4730
+ * 4. Format changelog with links
4731
+ * 5. Update workspace objects
4732
+ *
4733
+ * @param workspaces - Array of workspaces to process
4734
+ * @param context - Release context
4735
+ * @returns Promise resolving to updated workspaces
4736
+ *
4737
+ * @example
4738
+ * ```typescript
4739
+ * const workspaces = await changelog.transformWorkspace(
4740
+ * [
4741
+ * {
4742
+ * name: 'pkg-a',
4743
+ * path: 'packages/a',
4744
+ * lastTag: 'v1.0.0'
4745
+ * }
4746
+ * ],
4747
+ * context
4748
+ * );
4749
+ * // Returns workspaces with GitHub-formatted changelogs
4750
+ * ```
4751
+ */
3323
4752
  async transformWorkspace(workspaces, context) {
3324
4753
  const githubRootPath = [
3325
4754
  DOMAIN,
@@ -3369,6 +4798,26 @@ import { Shell as Shell4 } from "@qlover/scripts-context";
3369
4798
  var DEFAULT_RELEASE_NAME = "Release ${name} v${version}";
3370
4799
  var DEFAULT_COMMIT_MESSAGE = "chore(tag): ${name} v${version}";
3371
4800
  var GithubPR = class extends GitBase {
4801
+ /**
4802
+ * Creates a new GithubPR plugin instance
4803
+ *
4804
+ * Initializes the plugin with GitHub-specific configuration and
4805
+ * sets up release parameters and GitHub manager.
4806
+ *
4807
+ * @param context - Release context
4808
+ * @param props - Plugin configuration
4809
+ *
4810
+ * @example
4811
+ * ```typescript
4812
+ * const plugin = new GithubPR(context, {
4813
+ * releasePR: true,
4814
+ * releaseName: 'Release v${version}',
4815
+ * commitMessage: 'chore: release v${version}',
4816
+ * draft: false,
4817
+ * preRelease: false
4818
+ * });
4819
+ * ```
4820
+ */
3372
4821
  constructor(context, props) {
3373
4822
  super(context, "githubPR", {
3374
4823
  releaseName: DEFAULT_RELEASE_NAME,
@@ -3384,15 +4833,67 @@ var GithubPR = class extends GitBase {
3384
4833
  }
3385
4834
  releaseParams;
3386
4835
  githubManager;
4836
+ /**
4837
+ * Determines if the plugin should be enabled
4838
+ *
4839
+ * Plugin is enabled unless explicitly skipped via configuration.
4840
+ * This allows for conditional PR creation and release publishing.
4841
+ *
4842
+ * @param _name - Plugin name (unused)
4843
+ * @returns True if plugin should be enabled
4844
+ *
4845
+ * @example
4846
+ * ```typescript
4847
+ * const plugin = new GithubPR(context, { skip: true });
4848
+ * plugin.enabled(); // false
4849
+ *
4850
+ * const plugin2 = new GithubPR(context, {});
4851
+ * plugin2.enabled(); // true
4852
+ * ```
4853
+ */
3387
4854
  enabled(_name) {
3388
4855
  if (this.getConfig("skip")) {
3389
4856
  return false;
3390
4857
  }
3391
4858
  return true;
3392
4859
  }
4860
+ /**
4861
+ * Determines if the plugin is in publish mode
4862
+ *
4863
+ * In publish mode, the plugin publishes releases directly.
4864
+ * In non-publish mode (releasePR=true), it creates pull requests.
4865
+ *
4866
+ * @returns True if in publish mode
4867
+ *
4868
+ * @example
4869
+ * ```typescript
4870
+ * const plugin = new GithubPR(context, { releasePR: true });
4871
+ * plugin.isPublish; // false (PR mode)
4872
+ *
4873
+ * const plugin2 = new GithubPR(context, { releasePR: false });
4874
+ * plugin2.isPublish; // true (publish mode)
4875
+ * ```
4876
+ */
3393
4877
  get isPublish() {
3394
4878
  return !this.getConfig("releasePR");
3395
4879
  }
4880
+ /**
4881
+ * Checks if the current repository is a GitHub repository
4882
+ *
4883
+ * Verifies that the remote URL contains 'github.com' to ensure
4884
+ * GitHub-specific features can be used.
4885
+ *
4886
+ * @returns Promise resolving to true if GitHub repository
4887
+ *
4888
+ * @example
4889
+ * ```typescript
4890
+ * const isGithub = await plugin.isGithubRepository();
4891
+ * if (isGithub) {
4892
+ * // Use GitHub-specific features
4893
+ * }
4894
+ * ```
4895
+ * @private
4896
+ */
3396
4897
  async isGithubRepository() {
3397
4898
  try {
3398
4899
  const remoteUrl = await this.getRemoteUrl();
@@ -3401,6 +4902,24 @@ var GithubPR = class extends GitBase {
3401
4902
  return false;
3402
4903
  }
3403
4904
  }
4905
+ /**
4906
+ * Plugin initialization hook
4907
+ *
4908
+ * Performs pre-execution setup:
4909
+ * 1. Verifies repository is on GitHub
4910
+ * 2. Runs parent class initialization
4911
+ * 3. Sets up NPM token for publishing
4912
+ *
4913
+ * @throws Error if not a GitHub repository
4914
+ * @throws Error if NPM_TOKEN missing in publish mode
4915
+ *
4916
+ * @example
4917
+ * ```typescript
4918
+ * const plugin = new GithubPR(context, {});
4919
+ * await plugin.onBefore();
4920
+ * // Throws if not GitHub repo or missing NPM token
4921
+ * ```
4922
+ */
3404
4923
  async onBefore() {
3405
4924
  this.logger.debug("GithubPR onBefore");
3406
4925
  const isGithub = await this.isGithubRepository();
@@ -3420,6 +4939,24 @@ var GithubPR = class extends GitBase {
3420
4939
  );
3421
4940
  }
3422
4941
  }
4942
+ /**
4943
+ * Main plugin execution hook
4944
+ *
4945
+ * Processes changelogs for all workspaces using GitHub-specific
4946
+ * formatting and updates the context with the results.
4947
+ *
4948
+ * Process:
4949
+ * 1. Initialize GitHub changelog processor
4950
+ * 2. Transform workspace changelogs
4951
+ * 3. Update context with new workspace info
4952
+ *
4953
+ * @example
4954
+ * ```typescript
4955
+ * const plugin = new GithubPR(context, {});
4956
+ * await plugin.onExec();
4957
+ * // Transforms changelogs with GitHub links
4958
+ * ```
4959
+ */
3423
4960
  async onExec() {
3424
4961
  const workspaces = this.context.workspaces;
3425
4962
  const githubChangelog = new GithubChangelog(
@@ -3432,6 +4969,27 @@ var GithubPR = class extends GitBase {
3432
4969
  });
3433
4970
  this.context.setWorkspaces(newWorkspaces);
3434
4971
  }
4972
+ /**
4973
+ * Success hook after plugin execution
4974
+ *
4975
+ * Handles either PR creation or release publishing based on
4976
+ * configuration. In publish mode, publishes to NPM and creates
4977
+ * GitHub releases. In PR mode, creates release pull requests.
4978
+ *
4979
+ * @example PR mode
4980
+ * ```typescript
4981
+ * const plugin = new GithubPR(context, { releasePR: true });
4982
+ * await plugin.onSuccess();
4983
+ * // Creates release PR
4984
+ * ```
4985
+ *
4986
+ * @example Publish mode
4987
+ * ```typescript
4988
+ * const plugin = new GithubPR(context, { releasePR: false });
4989
+ * await plugin.onSuccess();
4990
+ * // Publishes to NPM and creates GitHub release
4991
+ * ```
4992
+ */
3435
4993
  async onSuccess() {
3436
4994
  if (this.isPublish) {
3437
4995
  await this.publishPR(this.context.workspaces);
@@ -3439,6 +4997,28 @@ var GithubPR = class extends GitBase {
3439
4997
  }
3440
4998
  await this.releasePR(this.context.workspaces);
3441
4999
  }
5000
+ /**
5001
+ * Creates a release pull request
5002
+ *
5003
+ * Handles the complete process of creating a release PR:
5004
+ * 1. Creates release commit
5005
+ * 2. Creates release branch
5006
+ * 3. Creates and configures pull request
5007
+ *
5008
+ * @param workspaces - Array of workspace configurations
5009
+ *
5010
+ * @example
5011
+ * ```typescript
5012
+ * const workspaces = [{
5013
+ * name: 'pkg-a',
5014
+ * version: '1.0.0',
5015
+ * changelog: '...'
5016
+ * }];
5017
+ *
5018
+ * await plugin.releasePR(workspaces);
5019
+ * // Creates PR with release changes
5020
+ * ```
5021
+ */
3442
5022
  async releasePR(workspaces) {
3443
5023
  await this.step({
3444
5024
  label: "Release Commit",
@@ -3450,6 +5030,28 @@ var GithubPR = class extends GitBase {
3450
5030
  });
3451
5031
  await this.releasePullRequest(workspaces, releaseBranchParams);
3452
5032
  }
5033
+ /**
5034
+ * Publishes releases to NPM and GitHub
5035
+ *
5036
+ * In non-dry-run mode:
5037
+ * 1. Publishes packages to NPM
5038
+ * 2. Pushes tags to GitHub
5039
+ * 3. Creates GitHub releases
5040
+ *
5041
+ * @param workspaces - Array of workspace configurations
5042
+ *
5043
+ * @example
5044
+ * ```typescript
5045
+ * const workspaces = [{
5046
+ * name: 'pkg-a',
5047
+ * version: '1.0.0',
5048
+ * changelog: '...'
5049
+ * }];
5050
+ *
5051
+ * await plugin.publishPR(workspaces);
5052
+ * // Publishes to NPM and creates GitHub releases
5053
+ * ```
5054
+ */
3453
5055
  async publishPR(workspaces) {
3454
5056
  if (!this.getConfig("dryRunCreatePR")) {
3455
5057
  await this.context.runChangesetsCli("publish");
@@ -3465,6 +5067,34 @@ var GithubPR = class extends GitBase {
3465
5067
  )
3466
5068
  });
3467
5069
  }
5070
+ /**
5071
+ * Creates release commit(s)
5072
+ *
5073
+ * Creates either a single commit for all workspaces or
5074
+ * individual commits per workspace. Uses configured commit
5075
+ * message template.
5076
+ *
5077
+ * @param workspaces - Array of workspace configurations
5078
+ *
5079
+ * @example Single workspace
5080
+ * ```typescript
5081
+ * await plugin.relesaeCommit([{
5082
+ * name: 'pkg-a',
5083
+ * version: '1.0.0'
5084
+ * }]);
5085
+ * // Creates: "chore(tag): pkg-a v1.0.0"
5086
+ * ```
5087
+ *
5088
+ * @example Multiple workspaces
5089
+ * ```typescript
5090
+ * await plugin.relesaeCommit([
5091
+ * { name: 'pkg-a', version: '1.0.0' },
5092
+ * { name: 'pkg-b', version: '2.0.0' }
5093
+ * ]);
5094
+ * // Creates: "chore(tag): pkg-a v1.0.0,pkg-b v2.0.0"
5095
+ * ```
5096
+ * @private
5097
+ */
3468
5098
  async relesaeCommit(workspaces) {
3469
5099
  const commitArgs = this.getConfig("commitArgs", []);
3470
5100
  if (workspaces.length === 1) {
@@ -3476,6 +5106,34 @@ var GithubPR = class extends GitBase {
3476
5106
  const commitMessage = `chore(tag): ${workspaces.map((w) => `${w.name} v${w.version}`).join(",")}`;
3477
5107
  await this.commit(commitMessage, commitArgs);
3478
5108
  }
5109
+ /**
5110
+ * Creates and optionally merges a release pull request
5111
+ *
5112
+ * Creates a PR with release changes and handles auto-merge
5113
+ * if configured. Adds release and change labels to the PR.
5114
+ *
5115
+ * @param workspaces - Array of workspace configurations
5116
+ * @param releaseBranchParams - Branch and tag information
5117
+ *
5118
+ * @example Manual merge
5119
+ * ```typescript
5120
+ * await plugin.releasePullRequest(
5121
+ * workspaces,
5122
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
5123
+ * );
5124
+ * // Creates PR for manual merge
5125
+ * ```
5126
+ *
5127
+ * @example Auto-merge
5128
+ * ```typescript
5129
+ * const plugin = new GithubPR(context, {
5130
+ * autoMergeReleasePR: true
5131
+ * });
5132
+ *
5133
+ * await plugin.releasePullRequest(workspaces, params);
5134
+ * // Creates and auto-merges PR
5135
+ * ```
5136
+ */
3479
5137
  async releasePullRequest(workspaces, releaseBranchParams) {
3480
5138
  const prNumber = await this.step({
3481
5139
  label: "Create Release PR",
@@ -3497,6 +5155,34 @@ var GithubPR = class extends GitBase {
3497
5155
  `Please manually merge PR(#${prNumber}) and complete the publishing process afterwards`
3498
5156
  );
3499
5157
  }
5158
+ /**
5159
+ * Creates a commit for a single workspace
5160
+ *
5161
+ * Uses the configured commit message template to create a
5162
+ * commit for the workspace's changes.
5163
+ *
5164
+ * @param workspace - Workspace configuration
5165
+ * @param commitArgs - Additional Git commit arguments
5166
+ * @returns Promise resolving to commit output
5167
+ *
5168
+ * @example Basic commit
5169
+ * ```typescript
5170
+ * await plugin.commitWorkspace({
5171
+ * name: 'pkg-a',
5172
+ * version: '1.0.0'
5173
+ * });
5174
+ * // Creates: "chore(tag): pkg-a v1.0.0"
5175
+ * ```
5176
+ *
5177
+ * @example With arguments
5178
+ * ```typescript
5179
+ * await plugin.commitWorkspace(
5180
+ * { name: 'pkg-a', version: '1.0.0' },
5181
+ * ['--no-verify']
5182
+ * );
5183
+ * ```
5184
+ * @private
5185
+ */
3500
5186
  async commitWorkspace(workspace, commitArgs = []) {
3501
5187
  const commitMessage = Shell4.format(
3502
5188
  this.getConfig("commitMessage", DEFAULT_COMMIT_MESSAGE),
@@ -3514,6 +5200,37 @@ var GithubPR = class extends GitBase {
3514
5200
  *
3515
5201
  * @returns The release branch.
3516
5202
  */
5203
+ /**
5204
+ * Creates a release branch for changes
5205
+ *
5206
+ * Creates a new branch from the current branch for release
5207
+ * changes. The branch name is generated from the configured
5208
+ * template and workspace information.
5209
+ *
5210
+ * Process:
5211
+ * 1. Generate branch parameters
5212
+ * 2. Fetch required branches
5213
+ * 3. Create and push release branch
5214
+ *
5215
+ * @param workspaces - Array of workspace configurations
5216
+ * @returns Promise resolving to branch parameters
5217
+ *
5218
+ * @example
5219
+ * ```typescript
5220
+ * const params = await plugin.createReleaseBranch([{
5221
+ * name: 'pkg-a',
5222
+ * version: '1.0.0'
5223
+ * }]);
5224
+ * // {
5225
+ * // tagName: 'pkg-a@1.0.0',
5226
+ * // releaseBranch: 'release-pkg-a-1.0.0'
5227
+ * // }
5228
+ * ```
5229
+ *
5230
+ * @throws Error if tag name is invalid
5231
+ * @throws Error if branch creation fails
5232
+ * @private
5233
+ */
3517
5234
  async createReleaseBranch(workspaces) {
3518
5235
  const params = this.releaseParams.getReleaseBranchParams(
3519
5236
  workspaces,
@@ -3556,6 +5273,42 @@ var GithubPR = class extends GitBase {
3556
5273
  * @param releaseBranchParams - The release branch params.
3557
5274
  * @returns The created pull request number.
3558
5275
  */
5276
+ /**
5277
+ * Creates a release pull request
5278
+ *
5279
+ * Creates a pull request with:
5280
+ * 1. Release label
5281
+ * 2. Change labels (if configured)
5282
+ * 3. Generated title and body
5283
+ * 4. Proper branch configuration
5284
+ *
5285
+ * @param workspaces - Array of workspace configurations
5286
+ * @param releaseBranchParams - Branch and tag information
5287
+ * @returns Promise resolving to PR number
5288
+ *
5289
+ * @example Basic PR
5290
+ * ```typescript
5291
+ * const prNumber = await plugin.createReleasePR(
5292
+ * workspaces,
5293
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
5294
+ * );
5295
+ * // Creates PR with default labels
5296
+ * ```
5297
+ *
5298
+ * @example With change labels
5299
+ * ```typescript
5300
+ * const plugin = new GithubPR(context, {
5301
+ * pushChangeLabels: true
5302
+ * });
5303
+ *
5304
+ * const prNumber = await plugin.createReleasePR(
5305
+ * workspaces,
5306
+ * params
5307
+ * );
5308
+ * // Creates PR with release and change labels
5309
+ * ```
5310
+ * @private
5311
+ */
3559
5312
  async createReleasePR(workspaces, releaseBranchParams) {
3560
5313
  const label = await this.githubManager.createReleasePRLabel();
3561
5314
  let labels = [label.name];
@@ -3589,21 +5342,137 @@ import { join as join2, resolve, relative } from "path";
3589
5342
 
3590
5343
  // src/implments/ReleaseLabel.ts
3591
5344
  var ReleaseLabel = class {
5345
+ /**
5346
+ * Creates a new ReleaseLabel instance
5347
+ *
5348
+ * @param options - Configuration options for label management
5349
+ *
5350
+ * @example
5351
+ * ```typescript
5352
+ * const label = new ReleaseLabel({
5353
+ * // Label template with ${name} placeholder
5354
+ * changePackagesLabel: 'changed:${name}',
5355
+ *
5356
+ * // Package directories to monitor
5357
+ * packagesDirectories: ['packages/a', 'packages/b'],
5358
+ *
5359
+ * // Optional custom comparison logic
5360
+ * compare: (file, pkg) => file.includes(pkg)
5361
+ * });
5362
+ * ```
5363
+ */
3592
5364
  constructor(options) {
3593
5365
  this.options = options;
3594
5366
  }
5367
+ /**
5368
+ * Compares a changed file path against a package path
5369
+ *
5370
+ * Uses custom comparison function if provided, otherwise
5371
+ * checks if the file path starts with the package path.
5372
+ *
5373
+ * @param changedFilePath - Path of the changed file
5374
+ * @param packagePath - Path of the package to check against
5375
+ * @returns True if the file belongs to the package
5376
+ *
5377
+ * @example
5378
+ * ```typescript
5379
+ * // Default comparison
5380
+ * label.compare('packages/a/src/index.ts', 'packages/a');
5381
+ * // true
5382
+ *
5383
+ * // Custom comparison
5384
+ * const label = new ReleaseLabel({
5385
+ * ...options,
5386
+ * compare: (file, pkg) => file.includes(pkg)
5387
+ * });
5388
+ * label.compare('src/packages/a/index.ts', 'packages/a');
5389
+ * // true
5390
+ * ```
5391
+ */
3595
5392
  compare(changedFilePath, packagePath) {
3596
5393
  if (typeof this.options.compare === "function") {
3597
5394
  return this.options.compare(changedFilePath, packagePath);
3598
5395
  }
3599
5396
  return changedFilePath.startsWith(packagePath);
3600
5397
  }
5398
+ /**
5399
+ * Generates a change label for a single package
5400
+ *
5401
+ * Replaces ${name} placeholder in the label template with
5402
+ * the package path.
5403
+ *
5404
+ * @param packagePath - Path of the package
5405
+ * @param label - Optional custom label template
5406
+ * @returns Formatted change label
5407
+ *
5408
+ * @example
5409
+ * ```typescript
5410
+ * // Default label template
5411
+ * label.toChangeLabel('packages/a');
5412
+ * // 'changed:packages/a'
5413
+ *
5414
+ * // Custom label template
5415
+ * label.toChangeLabel('packages/a', 'modified:${name}');
5416
+ * // 'modified:packages/a'
5417
+ * ```
5418
+ */
3601
5419
  toChangeLabel(packagePath, label = this.options.changePackagesLabel) {
3602
5420
  return label.replace("${name}", packagePath);
3603
5421
  }
5422
+ /**
5423
+ * Generates change labels for multiple packages
5424
+ *
5425
+ * Maps each package path to a formatted change label.
5426
+ *
5427
+ * @param packages - Array of package paths
5428
+ * @param label - Optional custom label template
5429
+ * @returns Array of formatted change labels
5430
+ *
5431
+ * @example
5432
+ * ```typescript
5433
+ * // Default label template
5434
+ * label.toChangeLabels(['packages/a', 'packages/b']);
5435
+ * // ['changed:packages/a', 'changed:packages/b']
5436
+ *
5437
+ * // Custom label template
5438
+ * label.toChangeLabels(
5439
+ * ['packages/a', 'packages/b'],
5440
+ * 'modified:${name}'
5441
+ * );
5442
+ * // ['modified:packages/a', 'modified:packages/b']
5443
+ * ```
5444
+ */
3604
5445
  toChangeLabels(packages, label = this.options.changePackagesLabel) {
3605
5446
  return packages.map((pkg) => this.toChangeLabel(pkg, label));
3606
5447
  }
5448
+ /**
5449
+ * Identifies packages affected by changed files
5450
+ *
5451
+ * Checks each changed file against package paths to determine
5452
+ * which packages have been modified.
5453
+ *
5454
+ * @param changedFiles - Array or Set of changed file paths
5455
+ * @param packages - Optional array of package paths to check
5456
+ * @returns Array of affected package paths
5457
+ *
5458
+ * @example
5459
+ * ```typescript
5460
+ * // Check against default packages
5461
+ * label.pick(['packages/a/src/index.ts']);
5462
+ * // ['packages/a']
5463
+ *
5464
+ * // Check specific packages
5465
+ * label.pick(
5466
+ * ['packages/a/index.ts', 'packages/b/test.ts'],
5467
+ * ['packages/a', 'packages/c']
5468
+ * );
5469
+ * // ['packages/a']
5470
+ *
5471
+ * // Using Set of files
5472
+ * label.pick(new Set(['packages/a/index.ts']));
5473
+ * // ['packages/a']
5474
+ * ```
5475
+ */
3607
5476
  pick(changedFiles, packages = this.options.packagesDirectories) {
3608
5477
  const result = [];
3609
5478
  for (const pkgPath of packages) {
@@ -3972,6 +5841,31 @@ import {
3972
5841
  } from "@qlover/scripts-context";
3973
5842
  var contentTmplate = "---\n'${name}': '${increment}'\n---\n\n${changelog}";
3974
5843
  var Changelog = class extends ScriptPlugin3 {
5844
+ /**
5845
+ * Creates a new Changelog plugin instance
5846
+ *
5847
+ * Initializes the plugin with default configuration values and
5848
+ * merges them with provided options.
5849
+ *
5850
+ * Default values:
5851
+ * - increment: 'patch'
5852
+ * - changesetRoot: '.changeset'
5853
+ * - tagTemplate: '${name}@${version}'
5854
+ * - tagPrefix: '${name}'
5855
+ * - tagMatch: '${name}@*'
5856
+ *
5857
+ * @param context - Release context
5858
+ * @param props - Plugin configuration
5859
+ *
5860
+ * @example
5861
+ * ```typescript
5862
+ * const plugin = new Changelog(context, {
5863
+ * increment: 'minor',
5864
+ * changesetRoot: 'custom/changeset',
5865
+ * tagTemplate: 'v${version}'
5866
+ * });
5867
+ * ```
5868
+ */
3975
5869
  constructor(context, props) {
3976
5870
  super(context, "changelog", {
3977
5871
  increment: "patch",
@@ -3982,15 +5876,78 @@ var Changelog = class extends ScriptPlugin3 {
3982
5876
  ...props
3983
5877
  });
3984
5878
  }
5879
+ /**
5880
+ * Gets the absolute path to the changeset root directory
5881
+ *
5882
+ * Combines the project root path with the configured changeset
5883
+ * directory path.
5884
+ *
5885
+ * @returns Absolute path to changeset directory
5886
+ *
5887
+ * @example
5888
+ * ```typescript
5889
+ * const root = plugin.changesetRoot;
5890
+ * // '/path/to/project/.changeset'
5891
+ * ```
5892
+ */
3985
5893
  get changesetRoot() {
3986
5894
  return join4(this.context.rootPath, this.getConfig("changesetRoot"));
3987
5895
  }
5896
+ /**
5897
+ * Gets the path to the changeset configuration file
5898
+ *
5899
+ * Returns the absolute path to the config.json file in the
5900
+ * changeset directory.
5901
+ *
5902
+ * @returns Path to changeset config file
5903
+ *
5904
+ * @example
5905
+ * ```typescript
5906
+ * const configPath = plugin.changesetConfigPath;
5907
+ * // '/path/to/project/.changeset/config.json'
5908
+ * ```
5909
+ */
3988
5910
  get changesetConfigPath() {
3989
5911
  return join4(this.changesetRoot, "config.json");
3990
5912
  }
5913
+ /**
5914
+ * Determines if the plugin should be enabled
5915
+ *
5916
+ * Plugin is enabled unless explicitly skipped via configuration.
5917
+ * This allows for conditional changelog generation.
5918
+ *
5919
+ * @returns True if plugin should be enabled
5920
+ *
5921
+ * @example
5922
+ * ```typescript
5923
+ * const plugin = new Changelog(context, { skip: true });
5924
+ * plugin.enabled(); // false
5925
+ *
5926
+ * const plugin2 = new Changelog(context, {});
5927
+ * plugin2.enabled(); // true
5928
+ * ```
5929
+ */
3991
5930
  enabled() {
3992
5931
  return !this.getConfig("skip");
3993
5932
  }
5933
+ /**
5934
+ * Plugin initialization hook
5935
+ *
5936
+ * Verifies that the changeset directory exists before proceeding
5937
+ * with changelog generation.
5938
+ *
5939
+ * @throws Error if changeset directory does not exist
5940
+ *
5941
+ * @example
5942
+ * ```typescript
5943
+ * const plugin = new Changelog(context, {
5944
+ * changesetRoot: '.changeset'
5945
+ * });
5946
+ *
5947
+ * await plugin.onBefore();
5948
+ * // Throws if .changeset directory doesn't exist
5949
+ * ```
5950
+ */
3994
5951
  async onBefore() {
3995
5952
  if (!existsSync(this.changesetRoot)) {
3996
5953
  throw new Error(
@@ -3999,6 +5956,33 @@ var Changelog = class extends ScriptPlugin3 {
3999
5956
  }
4000
5957
  this.logger.debug(`${this.changesetRoot} exists`);
4001
5958
  }
5959
+ /**
5960
+ * Updates workspace information with latest versions
5961
+ *
5962
+ * Reads the latest version information from each workspace's
5963
+ * package.json and updates the workspace objects with new
5964
+ * versions and tag names.
5965
+ *
5966
+ * @param workspaces - Array of workspace configurations
5967
+ * @returns Updated workspace configurations
5968
+ *
5969
+ * @example
5970
+ * ```typescript
5971
+ * const workspaces = [
5972
+ * { name: 'pkg-a', path: 'packages/a', version: '1.0.0' }
5973
+ * ];
5974
+ *
5975
+ * const updated = plugin.mergeWorkspaces(workspaces);
5976
+ * // [
5977
+ * // {
5978
+ * // name: 'pkg-a',
5979
+ * // path: 'packages/a',
5980
+ * // version: '1.1.0', // Updated version
5981
+ * // tagName: 'pkg-a@1.1.0'
5982
+ * // }
5983
+ * // ]
5984
+ * ```
5985
+ */
4002
5986
  mergeWorkspaces(workspaces) {
4003
5987
  return workspaces.map((workspace) => {
4004
5988
  const newPackgeJson = WorkspaceCreator.toWorkspace(
@@ -4015,6 +5999,25 @@ var Changelog = class extends ScriptPlugin3 {
4015
5999
  return newWorkspace;
4016
6000
  });
4017
6001
  }
6002
+ /**
6003
+ * Main plugin execution hook
6004
+ *
6005
+ * Generates changelogs for all workspaces in parallel and updates
6006
+ * the context with the results.
6007
+ *
6008
+ * Process:
6009
+ * 1. Generate changelogs for each workspace
6010
+ * 2. Update context with new workspace information
6011
+ *
6012
+ * @param _context - Execution context
6013
+ *
6014
+ * @example
6015
+ * ```typescript
6016
+ * const plugin = new Changelog(context, {});
6017
+ * await plugin.onExec(execContext);
6018
+ * // Generates changelogs for all workspaces
6019
+ * ```
6020
+ */
4018
6021
  async onExec(_context) {
4019
6022
  const workspaces = await this.step({
4020
6023
  label: "Generate Changelogs",
@@ -4026,6 +6029,28 @@ var Changelog = class extends ScriptPlugin3 {
4026
6029
  });
4027
6030
  this.context.setWorkspaces(workspaces);
4028
6031
  }
6032
+ /**
6033
+ * Success hook after plugin execution
6034
+ *
6035
+ * Handles post-changelog generation tasks:
6036
+ * 1. Creates changeset files (if not skipped)
6037
+ * 2. Updates package versions
6038
+ * 3. Restores unchanged packages (if configured)
6039
+ * 4. Updates workspace information
6040
+ *
6041
+ * @example
6042
+ * ```typescript
6043
+ * const plugin = new Changelog(context, {
6044
+ * skipChangeset: false,
6045
+ * ignoreNonUpdatedPackages: true
6046
+ * });
6047
+ *
6048
+ * await plugin.onSuccess();
6049
+ * // - Creates changeset files
6050
+ * // - Updates versions
6051
+ * // - Restores unchanged packages
6052
+ * ```
6053
+ */
4029
6054
  async onSuccess() {
4030
6055
  const workspaces = this.context.workspaces;
4031
6056
  if (!this.getConfig("skipChangeset")) {
@@ -4049,6 +6074,25 @@ var Changelog = class extends ScriptPlugin3 {
4049
6074
  this.logger.debug("new workspaces", newWorkspaces);
4050
6075
  this.context.setWorkspaces(newWorkspaces);
4051
6076
  }
6077
+ /**
6078
+ * Restores unchanged packages to their original state
6079
+ *
6080
+ * When ignoreNonUpdatedPackages is enabled, this method:
6081
+ * 1. Identifies packages without changes
6082
+ * 2. Uses git restore to revert them to original state
6083
+ *
6084
+ * @example
6085
+ * ```typescript
6086
+ * // With changed and unchanged packages
6087
+ * context.options.workspaces = {
6088
+ * packages: ['pkg-a', 'pkg-b', 'pkg-c'],
6089
+ * changedPaths: ['pkg-a', 'pkg-b']
6090
+ * };
6091
+ *
6092
+ * await plugin.restoreIgnorePackages();
6093
+ * // Restores 'pkg-c' to original state
6094
+ * ```
6095
+ */
4052
6096
  async restoreIgnorePackages() {
4053
6097
  const { changedPaths = [], packages = [] } = this.context.getOptions(
4054
6098
  "workspaces"
@@ -4061,12 +6105,60 @@ var Changelog = class extends ScriptPlugin3 {
4061
6105
  await this.shell.exec(["git", "restore", ...noChangedPackages]);
4062
6106
  }
4063
6107
  }
6108
+ /**
6109
+ * Gets the tag prefix for a workspace
6110
+ *
6111
+ * Formats the configured tag prefix template with workspace
6112
+ * information. Used for generating Git tag names.
6113
+ *
6114
+ * @param workspace - Workspace configuration
6115
+ * @returns Formatted tag prefix
6116
+ *
6117
+ * @example
6118
+ * ```typescript
6119
+ * const workspace = {
6120
+ * name: 'pkg-a',
6121
+ * version: '1.0.0'
6122
+ * };
6123
+ *
6124
+ * const prefix = plugin.getTagPrefix(workspace);
6125
+ * // With default template: 'pkg-a'
6126
+ * // With custom template: 'v1.0.0'
6127
+ * ```
6128
+ */
4064
6129
  getTagPrefix(workspace) {
4065
6130
  return Shell5.format(
4066
6131
  this.getConfig("tagPrefix"),
4067
6132
  workspace
4068
6133
  );
4069
6134
  }
6135
+ /**
6136
+ * Generates a changelog for a workspace
6137
+ *
6138
+ * Creates a changelog by:
6139
+ * 1. Getting the appropriate tag name
6140
+ * 2. Retrieving commits since last tag
6141
+ * 3. Formatting commits into changelog entries
6142
+ *
6143
+ * @param workspace - Workspace configuration
6144
+ * @returns Updated workspace with changelog
6145
+ *
6146
+ * @example
6147
+ * ```typescript
6148
+ * const workspace = {
6149
+ * name: 'pkg-a',
6150
+ * path: 'packages/a',
6151
+ * version: '1.0.0'
6152
+ * };
6153
+ *
6154
+ * const updated = await plugin.generateChangelog(workspace);
6155
+ * // {
6156
+ * // ...workspace,
6157
+ * // lastTag: 'pkg-a@1.0.0',
6158
+ * // changelog: '- feat: new feature\n- fix: bug fix'
6159
+ * // }
6160
+ * ```
6161
+ */
4070
6162
  async generateChangelog(workspace) {
4071
6163
  let tagName = await this.getTagName(workspace);
4072
6164
  if (workspace.lastTag) {
@@ -4092,6 +6184,32 @@ var Changelog = class extends ScriptPlugin3 {
4092
6184
  changelog: changelog.join("\n")
4093
6185
  };
4094
6186
  }
6187
+ /**
6188
+ * Generates a tag name for a workspace
6189
+ *
6190
+ * Uses the configured tag template to generate a tag name
6191
+ * for the workspace. Handles errors by providing a fallback.
6192
+ *
6193
+ * @param workspace - Workspace configuration
6194
+ * @returns Generated tag name
6195
+ *
6196
+ * @example
6197
+ * ```typescript
6198
+ * // With default template
6199
+ * const tag = plugin.generateTagName({
6200
+ * name: 'pkg-a',
6201
+ * version: '1.0.0'
6202
+ * });
6203
+ * // 'pkg-a@1.0.0'
6204
+ *
6205
+ * // With error (fallback)
6206
+ * const tag = plugin.generateTagName({
6207
+ * name: 'pkg-a'
6208
+ * });
6209
+ * // 'pkg-a-v0.0.0'
6210
+ * ```
6211
+ * @private
6212
+ */
4095
6213
  generateTagName(workspace) {
4096
6214
  try {
4097
6215
  const tagTemplate = this.getConfig("tagTemplate");
@@ -4104,6 +6222,38 @@ var Changelog = class extends ScriptPlugin3 {
4104
6222
  return `${workspace.name}-v0.0.0`;
4105
6223
  }
4106
6224
  }
6225
+ /**
6226
+ * Gets the appropriate tag name for a workspace
6227
+ *
6228
+ * Attempts to find the latest tag for the workspace, falling back
6229
+ * to generating a new tag if none exists. Uses git commands to
6230
+ * find and sort tags by creation date.
6231
+ *
6232
+ * Process:
6233
+ * 1. Generate current tag pattern
6234
+ * 2. Search for existing tags matching pattern
6235
+ * 3. Return latest tag or generate new one
6236
+ *
6237
+ * @param workspace - Workspace configuration
6238
+ * @returns Promise resolving to tag name
6239
+ *
6240
+ * @example
6241
+ * ```typescript
6242
+ * // With existing tags
6243
+ * const tag = await plugin.getTagName({
6244
+ * name: 'pkg-a',
6245
+ * version: '1.0.0'
6246
+ * });
6247
+ * // Returns latest matching tag: 'pkg-a@0.9.0'
6248
+ *
6249
+ * // Without existing tags
6250
+ * const tag = await plugin.getTagName({
6251
+ * name: 'pkg-b',
6252
+ * version: '1.0.0'
6253
+ * });
6254
+ * // Returns new tag: 'pkg-b@1.0.0'
6255
+ * ```
6256
+ */
4107
6257
  async getTagName(workspace) {
4108
6258
  try {
4109
6259
  const currentTagPattern = this.generateTagName(workspace);
@@ -4130,6 +6280,31 @@ var Changelog = class extends ScriptPlugin3 {
4130
6280
  return fallbackTag;
4131
6281
  }
4132
6282
  }
6283
+ /**
6284
+ * Determines the version increment type
6285
+ *
6286
+ * Checks for increment labels in the following order:
6287
+ * 1. 'increment:major' label
6288
+ * 2. 'increment:minor' label
6289
+ * 3. Configured increment value
6290
+ * 4. Default to 'patch'
6291
+ *
6292
+ * @returns Version increment type
6293
+ *
6294
+ * @example
6295
+ * ```typescript
6296
+ * // With labels
6297
+ * context.options.workspaces.changeLabels = ['increment:major'];
6298
+ * plugin.getIncrement(); // 'major'
6299
+ *
6300
+ * // With configuration
6301
+ * const plugin = new Changelog(context, { increment: 'minor' });
6302
+ * plugin.getIncrement(); // 'minor'
6303
+ *
6304
+ * // Default
6305
+ * plugin.getIncrement(); // 'patch'
6306
+ * ```
6307
+ */
4133
6308
  getIncrement() {
4134
6309
  const lables = this.context.getOptions("workspaces.changeLabels");
4135
6310
  if (Array.isArray(lables) && lables.length > 0) {
@@ -4143,6 +6318,43 @@ var Changelog = class extends ScriptPlugin3 {
4143
6318
  const increment = this.getConfig("increment", "patch");
4144
6319
  return increment;
4145
6320
  }
6321
+ /**
6322
+ * Generates a changeset file for a workspace
6323
+ *
6324
+ * Creates a changeset file containing version increment
6325
+ * information and changelog content. Handles dry run mode
6326
+ * and existing files.
6327
+ *
6328
+ * File format:
6329
+ * ```yaml
6330
+ * ---
6331
+ * 'package-name': 'increment-type'
6332
+ * ---
6333
+ *
6334
+ * changelog content
6335
+ * ```
6336
+ *
6337
+ * @param workspace - Workspace configuration
6338
+ *
6339
+ * @example
6340
+ * ```typescript
6341
+ * const workspace = {
6342
+ * name: 'pkg-a',
6343
+ * version: '1.0.0',
6344
+ * changelog: '- feat: new feature'
6345
+ * };
6346
+ *
6347
+ * await plugin.generateChangesetFile(workspace);
6348
+ * // Creates .changeset/pkg-a-1.0.0.md
6349
+ * ```
6350
+ *
6351
+ * @example Dry run
6352
+ * ```typescript
6353
+ * context.dryRun = true;
6354
+ * await plugin.generateChangesetFile(workspace);
6355
+ * // Logs file content without creating file
6356
+ * ```
6357
+ */
4146
6358
  async generateChangesetFile(workspace) {
4147
6359
  const { name, version } = workspace;
4148
6360
  const changesetName = `${name}-${version}`.replace(/[\/\\]/g, "_");
@@ -4176,15 +6388,89 @@ var innerTuples = [
4176
6388
  ];
4177
6389
  var defaultName = "release";
4178
6390
  var ReleaseTask = class {
6391
+ /**
6392
+ * Creates a new ReleaseTask instance
6393
+ *
6394
+ * Initializes the release context and sets up plugin configuration.
6395
+ * Supports custom executors and plugin configurations.
6396
+ *
6397
+ * @param options - Release context configuration
6398
+ * @param executor - Custom async executor (optional)
6399
+ * @param defaultTuples - Plugin configuration tuples (optional)
6400
+ *
6401
+ * @example
6402
+ * ```typescript
6403
+ * // Basic initialization
6404
+ * const task = new ReleaseTask({
6405
+ * rootPath: '/path/to/project',
6406
+ * sourceBranch: 'main'
6407
+ * });
6408
+ *
6409
+ * // With custom executor and plugins
6410
+ * const task = new ReleaseTask(
6411
+ * { rootPath: '/path/to/project' },
6412
+ * new AsyncExecutor(),
6413
+ * [tuple(CustomPlugin, { option: 'value' })]
6414
+ * );
6415
+ * ```
6416
+ */
4179
6417
  constructor(options = {}, executor = new AsyncExecutor(), defaultTuples = innerTuples) {
4180
6418
  this.executor = executor;
4181
6419
  this.defaultTuples = defaultTuples;
4182
6420
  this.context = new ReleaseContext(defaultName, options);
4183
6421
  }
6422
+ /**
6423
+ * Release context instance
6424
+ * @protected
6425
+ */
4184
6426
  context;
6427
+ /**
6428
+ * Gets the current release context
6429
+ *
6430
+ * @returns Release context instance
6431
+ *
6432
+ * @example
6433
+ * ```typescript
6434
+ * const task = new ReleaseTask();
6435
+ * const context = task.getContext();
6436
+ *
6437
+ * console.log(context.releaseEnv);
6438
+ * console.log(context.sourceBranch);
6439
+ * ```
6440
+ */
4185
6441
  getContext() {
4186
6442
  return this.context;
4187
6443
  }
6444
+ /**
6445
+ * Loads and configures plugins for the release task
6446
+ *
6447
+ * Combines default and external plugins, initializes them with
6448
+ * the current context, and configures special cases like the
6449
+ * Workspaces plugin.
6450
+ *
6451
+ * Plugin Loading Process:
6452
+ * 1. Merge default and external plugins
6453
+ * 2. Initialize plugins with context
6454
+ * 3. Configure special plugins
6455
+ * 4. Add plugins to executor
6456
+ *
6457
+ * @param externalTuples - Additional plugin configurations
6458
+ * @returns Array of initialized plugins
6459
+ *
6460
+ * @example Basic usage
6461
+ * ```typescript
6462
+ * const task = new ReleaseTask();
6463
+ * const plugins = await task.usePlugins();
6464
+ * ```
6465
+ *
6466
+ * @example Custom plugins
6467
+ * ```typescript
6468
+ * const task = new ReleaseTask();
6469
+ * const plugins = await task.usePlugins([
6470
+ * tuple(CustomPlugin, { option: 'value' })
6471
+ * ]);
6472
+ * ```
6473
+ */
4188
6474
  async usePlugins(externalTuples) {
4189
6475
  externalTuples = externalTuples || this.context.options.plugins || [];
4190
6476
  const plugins = await loaderPluginsFromPluginTuples(this.context, [
@@ -4199,12 +6485,65 @@ var ReleaseTask = class {
4199
6485
  });
4200
6486
  return plugins;
4201
6487
  }
6488
+ /**
6489
+ * Executes the release task
6490
+ *
6491
+ * Internal method that runs the task through the executor.
6492
+ * Preserves the context through the execution chain.
6493
+ *
6494
+ * @returns Execution result
6495
+ * @internal
6496
+ */
4202
6497
  async run() {
4203
6498
  return this.executor.exec(
4204
6499
  this.context,
4205
6500
  (context) => Promise.resolve(context)
4206
6501
  );
4207
6502
  }
6503
+ /**
6504
+ * Main entry point for executing the release task
6505
+ *
6506
+ * Checks environment conditions, loads plugins, and executes
6507
+ * the release process. Supports additional plugin configuration
6508
+ * at execution time.
6509
+ *
6510
+ * Environment Control:
6511
+ * - Checks FE_RELEASE environment variable
6512
+ * - Skips release if FE_RELEASE=false
6513
+ *
6514
+ * @param externalTuples - Additional plugin configurations
6515
+ * @returns Execution result
6516
+ * @throws Error if release is skipped via environment variable
6517
+ *
6518
+ * @example Basic execution
6519
+ * ```typescript
6520
+ * const task = new ReleaseTask();
6521
+ * await task.exec();
6522
+ * ```
6523
+ *
6524
+ * @example With additional plugins
6525
+ * ```typescript
6526
+ * const task = new ReleaseTask();
6527
+ * await task.exec([
6528
+ * tuple(CustomPlugin, { option: 'value' })
6529
+ * ]);
6530
+ * ```
6531
+ *
6532
+ * @example Environment control
6533
+ * ```typescript
6534
+ * // Skip release
6535
+ * process.env.FE_RELEASE = 'false';
6536
+ *
6537
+ * const task = new ReleaseTask();
6538
+ * try {
6539
+ * await task.exec();
6540
+ * } catch (e) {
6541
+ * if (e.message === 'Skip Release') {
6542
+ * console.log('Release skipped via environment variable');
6543
+ * }
6544
+ * }
6545
+ * ```
6546
+ */
4208
6547
  async exec(externalTuples) {
4209
6548
  if (this.context.env.get("FE_RELEASE") === "false") {
4210
6549
  throw new Error("Skip Release");