@bobfrankston/npmglobalize 1.0.137 → 1.0.139

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/colors.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Functional color module for npmglobalize.
3
+ * Uses semantic names (success, warn, error, info, muted) instead of raw colors.
4
+ * Adapts to light/dark terminal backgrounds.
5
+ */
6
+ /** Semantic color functions — use these instead of raw colors */
7
+ export declare const colors: {
8
+ /** Success: green on both themes */
9
+ success: (text: string) => string;
10
+ /** Warning: yellow on dark, magenta on light */
11
+ warn: (text: string) => string;
12
+ /** Error: red on both themes */
13
+ error: (text: string) => string;
14
+ /** Info: blue on both themes */
15
+ info: (text: string) => string;
16
+ /** Accent: cyan on both themes */
17
+ accent: (text: string) => string;
18
+ /** Muted/dim text */
19
+ muted: (text: string) => string;
20
+ /** Italic */
21
+ italic: (text: string) => string;
22
+ red: (text: string) => string;
23
+ yellow: (text: string) => string;
24
+ green: (text: string) => string;
25
+ blue: (text: string) => string;
26
+ cyan: (text: string) => string;
27
+ dim: (text: string) => string;
28
+ };
29
+ //# sourceMappingURL=colors.d.ts.map
package/colors.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Functional color module for npmglobalize.
3
+ * Uses semantic names (success, warn, error, info, muted) instead of raw colors.
4
+ * Adapts to light/dark terminal backgrounds.
5
+ */
6
+ import { styleText } from 'util';
7
+ /** Detect terminal theme from environment or default to dark */
8
+ function detectTheme() {
9
+ // COLORFGBG is set by some terminals: "fg;bg" — high bg = light theme
10
+ const colorfgbg = process.env.COLORFGBG;
11
+ if (colorfgbg) {
12
+ const bg = parseInt(colorfgbg.split(';').pop() || '0', 10);
13
+ if (bg > 8)
14
+ return 'light';
15
+ }
16
+ // Explicit override
17
+ if (process.env.NPMG_THEME === 'light')
18
+ return 'light';
19
+ if (process.env.NPMG_THEME === 'dark')
20
+ return 'dark';
21
+ return 'dark';
22
+ }
23
+ const theme = detectTheme();
24
+ // Raw ANSI colors per theme — semantic → [dark, light]
25
+ const themeMap = {
26
+ success: ['green', 'green'],
27
+ warn: ['yellow', 'magenta'], // yellow is invisible on light bg
28
+ error: ['red', 'red'],
29
+ info: ['blue', 'blue'],
30
+ accent: ['cyan', 'cyan'],
31
+ muted: ['dim', 'dim'],
32
+ italic: ['italic', 'italic'],
33
+ };
34
+ function pick(semantic) {
35
+ const entry = themeMap[semantic];
36
+ if (!entry)
37
+ return semantic;
38
+ return theme === 'light' ? entry[1] : entry[0];
39
+ }
40
+ /** Semantic color functions — use these instead of raw colors */
41
+ export const colors = {
42
+ /** Success: green on both themes */
43
+ success: (text) => styleText(pick('success'), text),
44
+ /** Warning: yellow on dark, magenta on light */
45
+ warn: (text) => styleText(pick('warn'), text),
46
+ /** Error: red on both themes */
47
+ error: (text) => styleText(pick('error'), text),
48
+ /** Info: blue on both themes */
49
+ info: (text) => styleText(pick('info'), text),
50
+ /** Accent: cyan on both themes */
51
+ accent: (text) => styleText(pick('accent'), text),
52
+ /** Muted/dim text */
53
+ muted: (text) => styleText(pick('muted'), text),
54
+ /** Italic */
55
+ italic: (text) => styleText('italic', text),
56
+ // Legacy aliases for gradual migration
57
+ red: (text) => styleText(pick('error'), text),
58
+ yellow: (text) => styleText(pick('warn'), text),
59
+ green: (text) => styleText(pick('success'), text),
60
+ blue: (text) => styleText(pick('info'), text),
61
+ cyan: (text) => styleText(pick('accent'), text),
62
+ dim: (text) => styleText(pick('muted'), text),
63
+ };
64
+ //# sourceMappingURL=colors.js.map
@@ -35,6 +35,7 @@
35
35
  // Auto-added silently (security-sensitive)
36
36
  security: [
37
37
  "node_modules/",
38
+ "*.tgz",
38
39
  ".claude/",
39
40
  ".env*",
40
41
  "token*",
package/lib/git.js CHANGED
@@ -659,12 +659,49 @@ export function showPushProtectionGuidance(ppInfo) {
659
659
  /** Push to git with push-protection detection and auto-bypass for installed OAuth.
660
660
  * Returns true if push succeeded (possibly after auto-bypass). */
661
661
  export function pushWithProtection(cwd, verbose) {
662
- const pushResult = runCommand('git', ['push'], { cwd, silent: true });
662
+ let pushResult = runCommand('git', ['push'], { cwd, silent: true });
663
663
  if (pushResult.success) {
664
664
  if (verbose)
665
665
  console.log(colors.green(' ✓ Pushed to remote'));
666
666
  return true;
667
667
  }
668
+ // Check for "Everything up-to-date" — git sometimes returns non-zero
669
+ // alongside transient errors even though there's nothing to push
670
+ const combined = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
671
+ if (combined.includes('everything up-to-date')) {
672
+ if (verbose)
673
+ console.log(colors.green(' ✓ Already up-to-date'));
674
+ return true;
675
+ }
676
+ // No upstream branch — auto-set and retry
677
+ if (combined.includes('no upstream branch') || combined.includes('has no upstream')) {
678
+ if (verbose)
679
+ console.log(colors.yellow(' No upstream branch — setting upstream...'));
680
+ const branchResult = runCommand('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, silent: true });
681
+ const branch = branchResult.output.trim() || 'master';
682
+ pushResult = runCommand('git', ['push', '--set-upstream', 'origin', branch], { cwd, silent: true });
683
+ if (pushResult.success) {
684
+ if (verbose)
685
+ console.log(colors.green(' ✓ Pushed to remote (set upstream)'));
686
+ return true;
687
+ }
688
+ }
689
+ // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
690
+ if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
691
+ console.log(colors.yellow('Git push failed with transient error — retrying...'));
692
+ pushResult = runCommand('git', ['push'], { cwd, silent: true });
693
+ if (pushResult.success) {
694
+ if (verbose)
695
+ console.log(colors.green(' ✓ Pushed to remote (retry)'));
696
+ return true;
697
+ }
698
+ const retryCombo = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
699
+ if (retryCombo.includes('everything up-to-date')) {
700
+ if (verbose)
701
+ console.log(colors.green(' ✓ Already up-to-date'));
702
+ return true;
703
+ }
704
+ }
668
705
  const ppInfo = parsePushProtection(pushResult.stderr, cwd);
669
706
  if (!ppInfo.detected) {
670
707
  console.error(colors.red('Git push failed:'));
package/lib.js CHANGED
@@ -30,17 +30,9 @@ import readline from 'readline';
30
30
  import libversion from 'libnpmversion';
31
31
  import JSON5 from 'json5';
32
32
  import { fileURLToPath } from 'url';
33
- import { styleText } from 'util';
34
- /** Color/style helpers using Node.js styleText (Node 24+) */
35
- const colors = {
36
- red: (text) => styleText('red', text),
37
- yellow: (text) => styleText('yellow', text),
38
- green: (text) => styleText('green', text),
39
- italic: (text) => styleText('italic', text),
40
- blue: (text) => styleText('blue', text),
41
- cyan: (text) => styleText('cyan', text),
42
- dim: (text) => styleText('dim', text),
43
- };
33
+ import { themeColors } from '@bobfrankston/themecolors';
34
+ /** Semantic color functions adapts to terminal light/dark theme */
35
+ const colors = themeColors();
44
36
  /**
45
37
  * Remove 'nul' files from a directory tree (Windows reserved name issue).
46
38
  * These files break git and npm on Windows. Uses \\?\ prefix to bypass name validation.
@@ -1641,12 +1633,50 @@ function showPushProtectionGuidance(ppInfo) {
1641
1633
  /** Push to git with push-protection detection and auto-bypass for installed OAuth.
1642
1634
  * Returns true if push succeeded (possibly after auto-bypass). */
1643
1635
  function pushWithProtection(cwd, verbose) {
1644
- const pushResult = runCommand('git', ['push'], { cwd, silent: true });
1636
+ let pushResult = runCommand('git', ['push'], { cwd, silent: true });
1645
1637
  if (pushResult.success) {
1646
1638
  if (verbose)
1647
1639
  console.log(colors.green(' ✓ Pushed to remote'));
1648
1640
  return true;
1649
1641
  }
1642
+ // Check for "Everything up-to-date" — git sometimes returns non-zero
1643
+ // alongside transient errors even though there's nothing to push
1644
+ const combined = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
1645
+ if (combined.includes('everything up-to-date')) {
1646
+ if (verbose)
1647
+ console.log(colors.green(' ✓ Already up-to-date'));
1648
+ return true;
1649
+ }
1650
+ // No upstream branch — auto-set and retry
1651
+ if (combined.includes('no upstream branch') || combined.includes('has no upstream')) {
1652
+ if (verbose)
1653
+ console.log(colors.yellow(' No upstream branch — setting upstream...'));
1654
+ // Detect current branch name
1655
+ const branchResult = runCommand('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, silent: true });
1656
+ const branch = branchResult.output.trim() || 'master';
1657
+ pushResult = runCommand('git', ['push', '--set-upstream', 'origin', branch], { cwd, silent: true });
1658
+ if (pushResult.success) {
1659
+ if (verbose)
1660
+ console.log(colors.green(' ✓ Pushed to remote (set upstream)'));
1661
+ return true;
1662
+ }
1663
+ }
1664
+ // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
1665
+ if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
1666
+ console.log(colors.yellow('Git push failed with transient error — retrying...'));
1667
+ pushResult = runCommand('git', ['push'], { cwd, silent: true });
1668
+ if (pushResult.success) {
1669
+ if (verbose)
1670
+ console.log(colors.green(' ✓ Pushed to remote (retry)'));
1671
+ return true;
1672
+ }
1673
+ const retryCombo = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
1674
+ if (retryCombo.includes('everything up-to-date')) {
1675
+ if (verbose)
1676
+ console.log(colors.green(' ✓ Already up-to-date'));
1677
+ return true;
1678
+ }
1679
+ }
1650
1680
  const ppInfo = parsePushProtection(pushResult.stderr, cwd);
1651
1681
  if (!ppInfo.detected) {
1652
1682
  console.error(colors.red('Git push failed:'));
@@ -2035,9 +2065,17 @@ export async function initGit(cwd, visibility, dryRun) {
2035
2065
  const createResult = runCommand('gh', ['repo', 'create', repoName, visFlag, '--source=.', '--push'], { cwd, silent: true });
2036
2066
  if (!createResult.success) {
2037
2067
  const errText = createResult.stderr + createResult.output;
2038
- if (errText.includes('Name already exists')) {
2068
+ // Check if repo was created but push failed (GitHub propagation delay)
2069
+ const repoCreatedButPushFailed = /repository not found|hung up unexpectedly|rpc failed/i.test(errText)
2070
+ && /https?:\/\/github\.com\//i.test(errText);
2071
+ if (errText.includes('Name already exists') || repoCreatedButPushFailed) {
2039
2072
  // Repo exists on GitHub — look up the owner and add as remote
2040
- console.log(colors.yellow(` GitHub repo '${repoName}' already exists — linking as remote...`));
2073
+ if (repoCreatedButPushFailed) {
2074
+ console.log(colors.yellow(` GitHub repo created but initial push failed — retrying...`));
2075
+ }
2076
+ else {
2077
+ console.log(colors.yellow(` GitHub repo '${repoName}' already exists — linking as remote...`));
2078
+ }
2041
2079
  const whoResult = runCommand('gh', ['api', 'user', '--jq', '.login'], { cwd, silent: true });
2042
2080
  const ghUser = (whoResult.output || '').trim();
2043
2081
  if (!ghUser) {
@@ -2050,9 +2088,22 @@ export async function initGit(cwd, visibility, dryRun) {
2050
2088
  // Remote might already exist with wrong URL
2051
2089
  runCommand('git', ['remote', 'set-url', 'origin', remoteUrl], { cwd, silent: true });
2052
2090
  }
2053
- // Push existing commits
2054
- runCommand('git', ['push', '-u', 'origin', 'master'], { cwd, silent: true });
2055
- console.log(colors.green(` ✓ Linked to existing repo: ${remoteUrl}`));
2091
+ // Push existing commits (retry up to 2 times for propagation delay)
2092
+ let pushed = false;
2093
+ for (let attempt = 0; attempt < 2 && !pushed; attempt++) {
2094
+ if (attempt > 0) {
2095
+ console.log(colors.yellow(' Retrying push...'));
2096
+ spawnSafe(process.platform === 'win32' ? 'timeout' : 'sleep', process.platform === 'win32' ? ['\t', '3', '\nobreak'] : ['3'], { stdio: 'pipe' }); // brief delay for GitHub propagation
2097
+ }
2098
+ const pushRes = runCommand('git', ['push', '-u', 'origin', 'master'], { cwd, silent: true });
2099
+ pushed = pushRes.success;
2100
+ }
2101
+ if (pushed) {
2102
+ console.log(colors.green(` ✓ Linked to repo: ${remoteUrl}`));
2103
+ }
2104
+ else {
2105
+ console.log(colors.yellow(` ✓ Repo created but push failed — push manually: git push -u origin master`));
2106
+ }
2056
2107
  }
2057
2108
  else {
2058
2109
  console.error(colors.red(`Failed to create GitHub repo: ${errText}`));
@@ -3641,12 +3692,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3641
3692
  }
3642
3693
  }
3643
3694
  else if (error.message?.includes('unknown git error')) {
3644
- console.error(colors.yellow('\nPossible causes:'));
3645
- console.error(' • Git hooks (pre-commit, commit-msg) might be failing');
3646
- console.error(' • GPG signing might be required but not configured');
3647
- console.error(' • Git config issues (user.name, user.email)');
3648
- console.error(' • Disk space or permissions issues');
3649
- console.error(colors.yellow('\nTry running with --verbose or check git status manually'));
3695
+ console.error(colors.yellow('Unknown git error — check git hooks, signing, or permissions'));
3650
3696
  }
3651
3697
  else if (combinedOutput.includes('gh013') || combinedOutput.includes('push protection') || combinedOutput.includes('push declined due to repository rule')) {
3652
3698
  // GitHub push protection blocked a push (from postversion script)
@@ -3688,35 +3734,43 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3688
3734
  else if (!skipVersionBump) {
3689
3735
  console.log(` [dry-run] Would run: npm version ${bump}`);
3690
3736
  }
3737
+ // Check for public package depending on private dependencies
3738
+ const publishAccess = effectiveNpmVisibility || currentAccess || (isScoped ? 'restricted' : 'public');
3739
+ if (publishAccess === 'public') {
3740
+ const privateDeps = [];
3741
+ for (const depKey of ['dependencies', 'peerDependencies']) {
3742
+ const deps = pkg[depKey];
3743
+ if (!deps)
3744
+ continue;
3745
+ for (const depName of Object.keys(deps)) {
3746
+ const depAccess = checkNpmAccess(depName);
3747
+ if (depAccess === 'restricted') {
3748
+ privateDeps.push(depName);
3749
+ }
3750
+ else if (depAccess === null && depName.startsWith('@')) {
3751
+ // Scoped package not on npm — will be private by default
3752
+ privateDeps.push(`${depName} (not yet published)`);
3753
+ }
3754
+ }
3755
+ }
3756
+ if (privateDeps.length > 0) {
3757
+ console.log('');
3758
+ console.error(colors.red('WARNING: Public package depends on PRIVATE dependencies:'));
3759
+ for (const d of privateDeps) {
3760
+ console.error(colors.red(` ${d}`));
3761
+ }
3762
+ console.error(colors.yellow('Consumers will get E404 when installing this package.'));
3763
+ console.error(colors.yellow('Fix: publish those deps as public, or make this package private.'));
3764
+ console.log('');
3765
+ }
3766
+ }
3691
3767
  // Publish
3692
3768
  console.log('Publishing to npm...');
3693
3769
  if (!dryRun) {
3694
3770
  // Check npm authentication right before publishing
3695
3771
  const authStatus = checkNpmAuth();
3696
3772
  if (!authStatus.authenticated) {
3697
- console.error(colors.red('ERROR: npm authentication required'));
3698
- console.error('');
3699
- if (authStatus.error === 'token expired') {
3700
- console.error('Your npm access token has expired or been revoked.');
3701
- console.error('');
3702
- console.error('To fix this, run:');
3703
- console.error(colors.yellow(' npm logout'));
3704
- console.error(colors.yellow(' npm login'));
3705
- }
3706
- else if (authStatus.error === 'not logged in') {
3707
- console.error('You are not logged in to npm.');
3708
- console.error('');
3709
- console.error('To fix this, run:');
3710
- console.error(colors.yellow(' npm login'));
3711
- }
3712
- else {
3713
- console.error(`Authentication error: ${authStatus.error}`);
3714
- console.error('');
3715
- console.error('Try running:');
3716
- console.error(colors.yellow(' npm whoami'));
3717
- }
3718
- console.error('');
3719
- console.error('After logging in, try running this command again.');
3773
+ console.error(colors.red(`Not authenticated to npm (${authStatus.error}) — run: npm login`));
3720
3774
  return false;
3721
3775
  }
3722
3776
  if (verbose) {
@@ -3738,9 +3792,40 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3738
3792
  return false;
3739
3793
  }
3740
3794
  const tarballPath = path.join(cwd, tarballName);
3741
- if (verbose) {
3742
- console.log(`Created tarball: ${tarballName}`);
3795
+ // Check tarball size — warn if suspiciously large
3796
+ try {
3797
+ const tarballSize = fs.statSync(tarballPath).size;
3798
+ const sizeMB = tarballSize / (1024 * 1024);
3799
+ if (sizeMB > 50) {
3800
+ console.error(colors.red(`\n⚠ Tarball is ${sizeMB.toFixed(0)}MB — something large is being included!`));
3801
+ // Show biggest files in the tarball
3802
+ const listResult = runCommand('npm', ['pack', '--dry-run'], { cwd, silent: true });
3803
+ if (listResult.success) {
3804
+ const lines = listResult.output.trim().split('\n')
3805
+ .filter(l => l.includes('B '))
3806
+ .map(l => { const m = l.match(/([\d.]+[kMG]?B)\s+(.*)/); return m ? { size: m[1], file: m[2] } : null; })
3807
+ .filter(Boolean)
3808
+ .filter((e) => e.size.includes('M') || e.size.includes('G'))
3809
+ .slice(0, 10);
3810
+ if (lines.length > 0) {
3811
+ console.error(colors.yellow(' Large files in tarball:'));
3812
+ for (const e of lines)
3813
+ console.error(colors.yellow(` ${e.size}\t${e.file}`));
3814
+ }
3815
+ }
3816
+ console.error(colors.yellow(' Check .npmignore — you may need to exclude large files or directories.'));
3817
+ // Clean up and abort
3818
+ try {
3819
+ fs.unlinkSync(tarballPath);
3820
+ }
3821
+ catch { /* */ }
3822
+ return false;
3823
+ }
3824
+ if (verbose) {
3825
+ console.log(` Tarball: ${tarballName} (${sizeMB.toFixed(1)}MB)`);
3826
+ }
3743
3827
  }
3828
+ catch { /* proceed if stat fails */ }
3744
3829
  // Publish the tarball
3745
3830
  const npmArgs = ['publish', tarballName];
3746
3831
  // Determine access level (use effectiveNpmVisibility which accounts for current npm status)
@@ -3789,78 +3874,22 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3789
3874
  }
3790
3875
  if (!publishResult.success) {
3791
3876
  console.error(colors.red('\nERROR: npm publish failed\n'));
3792
- // Show the actual error output
3793
- if (publishResult.stderr && publishResult.stderr.trim()) {
3794
- console.error('npm error output:');
3795
- console.error(publishResult.stderr.trim());
3796
- console.error('');
3797
- }
3798
- else if (publishResult.output && publishResult.output.trim()) {
3799
- console.error('npm output:');
3800
- console.error(publishResult.output.trim());
3801
- console.error('');
3802
- }
3803
- // Check for E409 conflict (publishing too fast)
3877
+ // Check for specific error types
3804
3878
  const output = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
3805
- if (output.includes('e409') || output.includes('409 conflict')) {
3806
- console.error(colors.yellow(' Publishing too quickly'));
3807
- console.error('');
3808
- console.error('npm is still processing the previous version after multiple retries.');
3809
- console.error(colors.green('Solution: Wait a few more minutes and try again'));
3810
- console.error('');
3811
- }
3812
- else if (output.includes('403') || output.includes('forbidden') || output.includes('cannot publish over')) {
3813
- // Check if it's the "version already published" case
3814
- if (output.includes('cannot publish over') || output.includes('previously published')) {
3815
- // Extract the version number from the error if possible
3816
- const versionMatch = output.match(/versions?[:\s]+(\d+\.\d+\.\d+)/);
3817
- const failedVersion = versionMatch ? versionMatch[1] : null;
3818
- // Check if this is the version we just bumped to
3819
- const currentPkgVersion = readPackageJson(cwd).version;
3820
- const isCurrentVersion = failedVersion === currentPkgVersion;
3821
- console.error(colors.yellow('⚠ Version already published to npm'));
3822
- console.error('');
3823
- if (isCurrentVersion) {
3824
- console.error(colors.green('✓ Good news: This version was already published (possibly just now)'));
3825
- console.error('');
3826
- console.error('The git commit and tag were created, but npm says this version exists.');
3827
- console.error('This is actually SUCCESS - your package is published!');
3828
- console.error('');
3829
- console.error('Verify with: ' + colors.yellow(`npm view ${pkg.name}@${currentPkgVersion}`));
3830
- console.error('');
3831
- console.error(colors.yellow('Note: You may have a postversion script that auto-publishes.'));
3832
- console.error(colors.yellow('npmglobalize will now skip that hook to prevent this issue.'));
3833
- console.error('');
3834
- // Treat as success and continue
3835
- console.log(colors.green('✓ Treating as successful publish'));
3836
- // Don't return false - let it continue to push git tags
3837
- }
3838
- else {
3839
- console.error('Version ' + colors.yellow(failedVersion || 'unknown') + ' is already on npm.');
3840
- console.error('This shouldn\'t happen - the version was bumped but publish failed.');
3841
- console.error('');
3842
- console.error(colors.yellow('To recover:'));
3843
- console.error(' Just run npmglobalize again (it will bump to next version)');
3844
- console.error('');
3845
- if (transformResult.transformed) {
3846
- console.log('Restoring file: dependencies...');
3847
- const failPkg = readPackageJson(cwd);
3848
- restoreDeps(failPkg, verbose);
3849
- writePackageJson(cwd, failPkg);
3850
- runCommand('git', ['add', 'package.json'], { cwd, silent: true });
3851
- gitCommit('Restore file: dependencies', cwd);
3852
- }
3853
- return false;
3854
- }
3879
+ if (output.includes('err_string_too_long') || output.includes('string longer than')) {
3880
+ console.error(colors.red('Tarball too large — check .npmignore. Run: npm pack --dry-run'));
3881
+ }
3882
+ else if (output.includes('e409') || output.includes('409 conflict')) {
3883
+ console.error(colors.yellow('npm still processing previous version wait and retry'));
3884
+ }
3885
+ else if (output.includes('cannot publish over') || output.includes('previously published')) {
3886
+ const currentPkgVersion = readPackageJson(cwd).version;
3887
+ if (output.includes(currentPkgVersion)) {
3888
+ console.log(colors.green(' Already published continuing'));
3855
3889
  }
3856
3890
  else {
3857
- console.error(colors.yellow('Common causes:'));
3858
- console.error(' 1. Package name already taken by another user');
3859
- console.error(' 2. No publish access to this package');
3860
- console.error(' 3. Scope (@username) requires authentication');
3861
- console.error('');
3891
+ console.error(colors.yellow('Version conflict — run npmglobalize again'));
3862
3892
  if (transformResult.transformed) {
3863
- console.log('Restoring file: dependencies...');
3864
3893
  const failPkg = readPackageJson(cwd);
3865
3894
  restoreDeps(failPkg, verbose);
3866
3895
  writePackageJson(cwd, failPkg);
@@ -3870,18 +3899,22 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3870
3899
  return false;
3871
3900
  }
3872
3901
  }
3902
+ else if (output.includes('403') || output.includes('forbidden')) {
3903
+ console.error(colors.yellow('Publish forbidden — check npm login and package access'));
3904
+ if (transformResult.transformed) {
3905
+ const failPkg = readPackageJson(cwd);
3906
+ restoreDeps(failPkg, verbose);
3907
+ writePackageJson(cwd, failPkg);
3908
+ runCommand('git', ['add', 'package.json'], { cwd, silent: true });
3909
+ gitCommit('Restore file: dependencies', cwd);
3910
+ }
3911
+ return false;
3912
+ }
3873
3913
  else if (output.includes('402') || output.includes('payment required')) {
3874
- console.error(colors.yellow('Private packages require a paid npm account'));
3875
- console.error(' • Use --npm public to publish as public');
3876
- console.error(' • Or upgrade your npm account for private packages');
3877
- console.error('');
3914
+ console.error(colors.yellow('Private packages need paid npm account — use --npm public'));
3878
3915
  }
3879
3916
  else {
3880
- console.error(colors.yellow('Common causes:'));
3881
- console.error(' 1. Not logged in - run: npm login');
3882
- console.error(' 2. Version already published');
3883
- console.error(' 3. Authentication token expired');
3884
- console.error('');
3917
+ console.error(colors.yellow('Publish failed — run npm login or check npm whoami'));
3885
3918
  }
3886
3919
  if (transformResult.transformed) {
3887
3920
  console.log('Restoring file: dependencies...');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.137",
3
+ "version": "1.0.139",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -31,9 +31,10 @@
31
31
  "url": "https://github.com/BobFrankston/npmglobalize.git"
32
32
  },
33
33
  "dependencies": {
34
- "@bobfrankston/freezepak": "^0.1.5",
35
- "@bobfrankston/importgen": "^0.1.31",
36
- "@bobfrankston/userconfig": "^1.0.5",
34
+ "@bobfrankston/freezepak": "^0.1.6",
35
+ "@bobfrankston/importgen": "^0.1.32",
36
+ "@bobfrankston/themecolors": "^0.1.2",
37
+ "@bobfrankston/userconfig": "^1.0.6",
37
38
  "@npmcli/package-json": "^7.0.4",
38
39
  "json5": "^2.2.3",
39
40
  "libnpmversion": "^8.0.3",
@@ -47,6 +48,7 @@
47
48
  ".dependencies": {
48
49
  "@bobfrankston/freezepak": "file:../freezepak",
49
50
  "@bobfrankston/importgen": "file:../importgen",
51
+ "@bobfrankston/themecolors": "file:../themecolors",
50
52
  "@bobfrankston/userconfig": "file:../userconfig",
51
53
  "@npmcli/package-json": "^7.0.4",
52
54
  "json5": "^2.2.3",