@eldrforge/kodrdriv 1.2.4 → 1.2.5

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.
@@ -114,6 +114,11 @@ const updateInterProjectDependencies = async (packageDir, publishedVersions, all
114
114
  ];
115
115
  for (const publishedVersion of publishedVersions){
116
116
  const { packageName, version } = publishedVersion;
117
+ // Do not propagate prerelease versions to consumers (often not available on registry)
118
+ if (typeof version === 'string' && version.includes('-')) {
119
+ packageLogger.verbose(`Skipping prerelease version for ${packageName}: ${version}`);
120
+ continue;
121
+ }
117
122
  // Only update if this is an inter-project dependency (exists in our build tree)
118
123
  if (!allPackageNames.has(packageName)) {
119
124
  continue;
@@ -592,6 +597,7 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
592
597
  }
593
598
  // For built-in commands, shell out to a separate kodrdriv process
594
599
  // This preserves individual project configurations
600
+ let publishWasSkipped;
595
601
  if (isBuiltInCommand) {
596
602
  // Extract the command name from "kodrdriv <command>"
597
603
  const builtInCommandName = commandToRun.replace('kodrdriv ', '');
@@ -601,28 +607,39 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
601
607
  // Ensure dry-run propagates to subprocess even during overall dry-run mode
602
608
  const effectiveCommand = runConfig.dryRun && !commandToRun.includes('--dry-run') ? `${commandToRun} --dry-run` : commandToRun;
603
609
  // Use runWithLogging for built-in commands to capture all output
604
- await runWithLogging(effectiveCommand, packageLogger, {}, showOutput);
610
+ const { stdout } = await runWithLogging(effectiveCommand, packageLogger, {}, showOutput);
611
+ // Detect explicit skip marker from publish to avoid propagating versions
612
+ if (builtInCommandName === 'publish' && stdout && stdout.includes('KODRDRIV_PUBLISH_SKIPPED')) {
613
+ packageLogger.info('Publish skipped for this package; will not record or propagate a version.');
614
+ publishWasSkipped = true;
615
+ }
605
616
  } else {
606
617
  // For custom commands, use the existing logic
607
618
  await runWithLogging(commandToRun, packageLogger, {}, showOutput);
608
619
  }
609
620
  // Track published version after successful publish (skip during dry run)
610
621
  if (!isDryRun && isBuiltInCommand && commandToRun.includes('publish')) {
611
- const publishedVersion = await extractPublishedVersion(packageDir, packageLogger);
612
- if (publishedVersion) {
613
- let mutexLocked = false;
614
- try {
615
- await globalStateMutex.lock();
616
- mutexLocked = true;
617
- publishedVersions.push(publishedVersion);
618
- packageLogger.info(`Tracked published version: ${publishedVersion.packageName}@${publishedVersion.version}`);
619
- globalStateMutex.unlock();
620
- mutexLocked = false;
621
- } catch (error) {
622
- if (mutexLocked) {
622
+ // If publish was skipped, do not record a version
623
+ if (publishWasSkipped) {
624
+ packageLogger.verbose('Skipping version tracking due to earlier skip.');
625
+ } else {
626
+ // Only record a published version if a new tag exists (avoid recording for skipped publishes)
627
+ const publishedVersion = await extractPublishedVersion(packageDir, packageLogger);
628
+ if (publishedVersion) {
629
+ let mutexLocked = false;
630
+ try {
631
+ await globalStateMutex.lock();
632
+ mutexLocked = true;
633
+ publishedVersions.push(publishedVersion);
634
+ packageLogger.info(`Tracked published version: ${publishedVersion.packageName}@${publishedVersion.version}`);
623
635
  globalStateMutex.unlock();
636
+ mutexLocked = false;
637
+ } catch (error) {
638
+ if (mutexLocked) {
639
+ globalStateMutex.unlock();
640
+ }
641
+ throw error;
624
642
  }
625
- throw error;
626
643
  }
627
644
  }
628
645
  }
@@ -802,13 +819,16 @@ const execute = async (runConfig)=>{
802
819
  const startFrom = (_runConfig_tree6 = runConfig.tree) === null || _runConfig_tree6 === void 0 ? void 0 : _runConfig_tree6.startFrom;
803
820
  if (startFrom) {
804
821
  logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for start package: ${startFrom}`);
805
- // Find the package that matches the startFrom directory name
806
- const startIndex = buildOrder.findIndex((packageName)=>{
807
- const packageInfo = dependencyGraph.packages.get(packageName);
808
- const dirName = path__default.basename(packageInfo.path);
809
- return dirName === startFrom || packageName === startFrom;
810
- });
811
- if (startIndex === -1) {
822
+ // Resolve the actual package name (can be package name or directory name)
823
+ let startPackageName = null;
824
+ for (const [pkgName, pkgInfo] of dependencyGraph.packages){
825
+ const dirName = path__default.basename(pkgInfo.path);
826
+ if (dirName === startFrom || pkgName === startFrom) {
827
+ startPackageName = pkgName;
828
+ break;
829
+ }
830
+ }
831
+ if (!startPackageName) {
812
832
  // Check if the package exists but was excluded across all directories
813
833
  let allPackageJsonPathsForCheck = [];
814
834
  for (const targetDirectory of directories){
@@ -842,11 +862,60 @@ const execute = async (runConfig)=>{
842
862
  throw new Error(`Package directory '${startFrom}' not found. Available packages: ${availablePackages}`);
843
863
  }
844
864
  }
845
- const skippedCount = startIndex;
846
- buildOrder = buildOrder.slice(startIndex);
847
- if (skippedCount > 0) {
848
- logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Resuming from '${startFrom}' - skipping ${skippedCount} package${skippedCount === 1 ? '' : 's'}`);
865
+ // Build reverse dependency map (who depends on whom)
866
+ const reverseEdges = new Map();
867
+ for (const [pkg, deps] of dependencyGraph.edges){
868
+ for (const dep of deps){
869
+ if (!reverseEdges.has(dep)) reverseEdges.set(dep, new Set());
870
+ reverseEdges.get(dep).add(pkg);
871
+ }
872
+ if (!reverseEdges.has(pkg)) reverseEdges.set(pkg, new Set());
873
+ }
874
+ // Step 1: collect the start package and all its transitive dependents (consumers)
875
+ const dependentsClosure = new Set();
876
+ const queueDependents = [
877
+ startPackageName
878
+ ];
879
+ while(queueDependents.length > 0){
880
+ const current = queueDependents.shift();
881
+ if (dependentsClosure.has(current)) continue;
882
+ dependentsClosure.add(current);
883
+ const consumers = reverseEdges.get(current) || new Set();
884
+ for (const consumer of consumers){
885
+ if (!dependentsClosure.has(consumer)) queueDependents.push(consumer);
886
+ }
887
+ }
888
+ // Step 2: expand to include all forward dependencies required to build those packages
889
+ const relevantPackages = new Set(dependentsClosure);
890
+ const queueDependencies = Array.from(relevantPackages);
891
+ while(queueDependencies.length > 0){
892
+ const current = queueDependencies.shift();
893
+ const deps = dependencyGraph.edges.get(current) || new Set();
894
+ for (const dep of deps){
895
+ if (!relevantPackages.has(dep)) {
896
+ relevantPackages.add(dep);
897
+ queueDependencies.push(dep);
898
+ }
899
+ }
900
+ }
901
+ // Filter graph to only relevant packages
902
+ const filteredGraph = {
903
+ packages: new Map(),
904
+ edges: new Map()
905
+ };
906
+ for (const pkgName of relevantPackages){
907
+ const info = dependencyGraph.packages.get(pkgName);
908
+ filteredGraph.packages.set(pkgName, info);
909
+ const deps = dependencyGraph.edges.get(pkgName) || new Set();
910
+ const filteredDeps = new Set();
911
+ for (const dep of deps){
912
+ if (relevantPackages.has(dep)) filteredDeps.add(dep);
913
+ }
914
+ filteredGraph.edges.set(pkgName, filteredDeps);
849
915
  }
916
+ // Recompute build order for the filtered subgraph
917
+ buildOrder = topologicalSort(filteredGraph);
918
+ logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Limiting scope to '${startFrom}' and its dependencies (${buildOrder.length} package${buildOrder.length === 1 ? '' : 's'}).`);
850
919
  }
851
920
  // Handle stop-at functionality if specified
852
921
  const stopAt = (_runConfig_tree7 = runConfig.tree) === null || _runConfig_tree7 === void 0 ? void 0 : _runConfig_tree7.stopAt;