@bobfrankston/npmglobalize 1.0.136 → 1.0.138

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,36 @@ 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
+ // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
677
+ if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
678
+ console.log(colors.yellow('Git push failed with transient error — retrying...'));
679
+ pushResult = runCommand('git', ['push'], { cwd, silent: true });
680
+ if (pushResult.success) {
681
+ if (verbose)
682
+ console.log(colors.green(' ✓ Pushed to remote (retry)'));
683
+ return true;
684
+ }
685
+ const retryCombo = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
686
+ if (retryCombo.includes('everything up-to-date')) {
687
+ if (verbose)
688
+ console.log(colors.green(' ✓ Already up-to-date'));
689
+ return true;
690
+ }
691
+ }
668
692
  const ppInfo = parsePushProtection(pushResult.stderr, cwd);
669
693
  if (!ppInfo.detected) {
670
694
  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.
@@ -697,7 +689,18 @@ function hasLocalChanges(packageName, version, targetPath, verbose) {
697
689
  }
698
690
  return true;
699
691
  }
700
- // Check 2: Compare latest commit time in directory vs npm publish time
692
+ // Check 2: Compare latest non-bookkeeping commit time vs npm publish time
693
+ // Skip npmglobalize's own commits (restore deps, pre-release, cleanup)
694
+ const bookkeepingPatterns = ['Restore file: dependencies', 'Pre-release commit', 'Pre-version cleanup', 'Untrack node_modules'];
695
+ const logResult = spawnSafe('git', ['-C', targetPath, 'log', '-1', '--format=%aI',
696
+ '--invert-grep', ...bookkeepingPatterns.map(p => `--grep=${p}`),
697
+ '--', '.'], {
698
+ encoding: 'utf-8',
699
+ stdio: 'pipe',
700
+ shell: true
701
+ });
702
+ if (logResult.status !== 0 || !logResult.stdout.trim())
703
+ return false;
701
704
  const timeResult = spawnSafe('npm', ['view', packageName, 'time', '--json'], {
702
705
  encoding: 'utf-8',
703
706
  stdio: 'pipe',
@@ -710,13 +713,6 @@ function hasLocalChanges(packageName, version, targetPath, verbose) {
710
713
  if (!publishTime)
711
714
  return false;
712
715
  const publishDate = new Date(publishTime);
713
- const logResult = spawnSafe('git', ['-C', targetPath, 'log', '-1', '--format=%aI', '--', '.'], {
714
- encoding: 'utf-8',
715
- stdio: 'pipe',
716
- shell: true
717
- });
718
- if (logResult.status !== 0 || !logResult.stdout.trim())
719
- return false;
720
716
  const latestCommitDate = new Date(logResult.stdout.trim());
721
717
  if (latestCommitDate > publishDate) {
722
718
  if (verbose) {
@@ -1637,12 +1633,36 @@ function showPushProtectionGuidance(ppInfo) {
1637
1633
  /** Push to git with push-protection detection and auto-bypass for installed OAuth.
1638
1634
  * Returns true if push succeeded (possibly after auto-bypass). */
1639
1635
  function pushWithProtection(cwd, verbose) {
1640
- const pushResult = runCommand('git', ['push'], { cwd, silent: true });
1636
+ let pushResult = runCommand('git', ['push'], { cwd, silent: true });
1641
1637
  if (pushResult.success) {
1642
1638
  if (verbose)
1643
1639
  console.log(colors.green(' ✓ Pushed to remote'));
1644
1640
  return true;
1645
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
+ // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
1651
+ if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
1652
+ console.log(colors.yellow('Git push failed with transient error — retrying...'));
1653
+ pushResult = runCommand('git', ['push'], { cwd, silent: true });
1654
+ if (pushResult.success) {
1655
+ if (verbose)
1656
+ console.log(colors.green(' ✓ Pushed to remote (retry)'));
1657
+ return true;
1658
+ }
1659
+ const retryCombo = (pushResult.output + ' ' + pushResult.stderr).toLowerCase();
1660
+ if (retryCombo.includes('everything up-to-date')) {
1661
+ if (verbose)
1662
+ console.log(colors.green(' ✓ Already up-to-date'));
1663
+ return true;
1664
+ }
1665
+ }
1646
1666
  const ppInfo = parsePushProtection(pushResult.stderr, cwd);
1647
1667
  if (!ppInfo.detected) {
1648
1668
  console.error(colors.red('Git push failed:'));
@@ -2031,9 +2051,17 @@ export async function initGit(cwd, visibility, dryRun) {
2031
2051
  const createResult = runCommand('gh', ['repo', 'create', repoName, visFlag, '--source=.', '--push'], { cwd, silent: true });
2032
2052
  if (!createResult.success) {
2033
2053
  const errText = createResult.stderr + createResult.output;
2034
- if (errText.includes('Name already exists')) {
2054
+ // Check if repo was created but push failed (GitHub propagation delay)
2055
+ const repoCreatedButPushFailed = /repository not found|hung up unexpectedly|rpc failed/i.test(errText)
2056
+ && /https?:\/\/github\.com\//i.test(errText);
2057
+ if (errText.includes('Name already exists') || repoCreatedButPushFailed) {
2035
2058
  // Repo exists on GitHub — look up the owner and add as remote
2036
- console.log(colors.yellow(` GitHub repo '${repoName}' already exists — linking as remote...`));
2059
+ if (repoCreatedButPushFailed) {
2060
+ console.log(colors.yellow(` GitHub repo created but initial push failed — retrying...`));
2061
+ }
2062
+ else {
2063
+ console.log(colors.yellow(` GitHub repo '${repoName}' already exists — linking as remote...`));
2064
+ }
2037
2065
  const whoResult = runCommand('gh', ['api', 'user', '--jq', '.login'], { cwd, silent: true });
2038
2066
  const ghUser = (whoResult.output || '').trim();
2039
2067
  if (!ghUser) {
@@ -2046,9 +2074,22 @@ export async function initGit(cwd, visibility, dryRun) {
2046
2074
  // Remote might already exist with wrong URL
2047
2075
  runCommand('git', ['remote', 'set-url', 'origin', remoteUrl], { cwd, silent: true });
2048
2076
  }
2049
- // Push existing commits
2050
- runCommand('git', ['push', '-u', 'origin', 'master'], { cwd, silent: true });
2051
- console.log(colors.green(` ✓ Linked to existing repo: ${remoteUrl}`));
2077
+ // Push existing commits (retry up to 2 times for propagation delay)
2078
+ let pushed = false;
2079
+ for (let attempt = 0; attempt < 2 && !pushed; attempt++) {
2080
+ if (attempt > 0) {
2081
+ console.log(colors.yellow(' Retrying push...'));
2082
+ spawnSafe(process.platform === 'win32' ? 'timeout' : 'sleep', process.platform === 'win32' ? ['\t', '3', '\nobreak'] : ['3'], { stdio: 'pipe' }); // brief delay for GitHub propagation
2083
+ }
2084
+ const pushRes = runCommand('git', ['push', '-u', 'origin', 'master'], { cwd, silent: true });
2085
+ pushed = pushRes.success;
2086
+ }
2087
+ if (pushed) {
2088
+ console.log(colors.green(` ✓ Linked to repo: ${remoteUrl}`));
2089
+ }
2090
+ else {
2091
+ console.log(colors.yellow(` ✓ Repo created but push failed — push manually: git push -u origin master`));
2092
+ }
2052
2093
  }
2053
2094
  else {
2054
2095
  console.error(colors.red(`Failed to create GitHub repo: ${errText}`));
@@ -3637,12 +3678,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3637
3678
  }
3638
3679
  }
3639
3680
  else if (error.message?.includes('unknown git error')) {
3640
- console.error(colors.yellow('\nPossible causes:'));
3641
- console.error(' • Git hooks (pre-commit, commit-msg) might be failing');
3642
- console.error(' • GPG signing might be required but not configured');
3643
- console.error(' • Git config issues (user.name, user.email)');
3644
- console.error(' • Disk space or permissions issues');
3645
- console.error(colors.yellow('\nTry running with --verbose or check git status manually'));
3681
+ console.error(colors.yellow('Unknown git error — check git hooks, signing, or permissions'));
3646
3682
  }
3647
3683
  else if (combinedOutput.includes('gh013') || combinedOutput.includes('push protection') || combinedOutput.includes('push declined due to repository rule')) {
3648
3684
  // GitHub push protection blocked a push (from postversion script)
@@ -3690,29 +3726,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3690
3726
  // Check npm authentication right before publishing
3691
3727
  const authStatus = checkNpmAuth();
3692
3728
  if (!authStatus.authenticated) {
3693
- console.error(colors.red('ERROR: npm authentication required'));
3694
- console.error('');
3695
- if (authStatus.error === 'token expired') {
3696
- console.error('Your npm access token has expired or been revoked.');
3697
- console.error('');
3698
- console.error('To fix this, run:');
3699
- console.error(colors.yellow(' npm logout'));
3700
- console.error(colors.yellow(' npm login'));
3701
- }
3702
- else if (authStatus.error === 'not logged in') {
3703
- console.error('You are not logged in to npm.');
3704
- console.error('');
3705
- console.error('To fix this, run:');
3706
- console.error(colors.yellow(' npm login'));
3707
- }
3708
- else {
3709
- console.error(`Authentication error: ${authStatus.error}`);
3710
- console.error('');
3711
- console.error('Try running:');
3712
- console.error(colors.yellow(' npm whoami'));
3713
- }
3714
- console.error('');
3715
- console.error('After logging in, try running this command again.');
3729
+ console.error(colors.red(`Not authenticated to npm (${authStatus.error}) — run: npm login`));
3716
3730
  return false;
3717
3731
  }
3718
3732
  if (verbose) {
@@ -3734,9 +3748,40 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3734
3748
  return false;
3735
3749
  }
3736
3750
  const tarballPath = path.join(cwd, tarballName);
3737
- if (verbose) {
3738
- console.log(`Created tarball: ${tarballName}`);
3751
+ // Check tarball size — warn if suspiciously large
3752
+ try {
3753
+ const tarballSize = fs.statSync(tarballPath).size;
3754
+ const sizeMB = tarballSize / (1024 * 1024);
3755
+ if (sizeMB > 50) {
3756
+ console.error(colors.red(`\n⚠ Tarball is ${sizeMB.toFixed(0)}MB — something large is being included!`));
3757
+ // Show biggest files in the tarball
3758
+ const listResult = runCommand('npm', ['pack', '--dry-run'], { cwd, silent: true });
3759
+ if (listResult.success) {
3760
+ const lines = listResult.output.trim().split('\n')
3761
+ .filter(l => l.includes('B '))
3762
+ .map(l => { const m = l.match(/([\d.]+[kMG]?B)\s+(.*)/); return m ? { size: m[1], file: m[2] } : null; })
3763
+ .filter(Boolean)
3764
+ .filter((e) => e.size.includes('M') || e.size.includes('G'))
3765
+ .slice(0, 10);
3766
+ if (lines.length > 0) {
3767
+ console.error(colors.yellow(' Large files in tarball:'));
3768
+ for (const e of lines)
3769
+ console.error(colors.yellow(` ${e.size}\t${e.file}`));
3770
+ }
3771
+ }
3772
+ console.error(colors.yellow(' Check .npmignore — you may need to exclude large files or directories.'));
3773
+ // Clean up and abort
3774
+ try {
3775
+ fs.unlinkSync(tarballPath);
3776
+ }
3777
+ catch { /* */ }
3778
+ return false;
3779
+ }
3780
+ if (verbose) {
3781
+ console.log(` Tarball: ${tarballName} (${sizeMB.toFixed(1)}MB)`);
3782
+ }
3739
3783
  }
3784
+ catch { /* proceed if stat fails */ }
3740
3785
  // Publish the tarball
3741
3786
  const npmArgs = ['publish', tarballName];
3742
3787
  // Determine access level (use effectiveNpmVisibility which accounts for current npm status)
@@ -3785,78 +3830,22 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3785
3830
  }
3786
3831
  if (!publishResult.success) {
3787
3832
  console.error(colors.red('\nERROR: npm publish failed\n'));
3788
- // Show the actual error output
3789
- if (publishResult.stderr && publishResult.stderr.trim()) {
3790
- console.error('npm error output:');
3791
- console.error(publishResult.stderr.trim());
3792
- console.error('');
3793
- }
3794
- else if (publishResult.output && publishResult.output.trim()) {
3795
- console.error('npm output:');
3796
- console.error(publishResult.output.trim());
3797
- console.error('');
3798
- }
3799
- // Check for E409 conflict (publishing too fast)
3833
+ // Check for specific error types
3800
3834
  const output = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
3801
- if (output.includes('e409') || output.includes('409 conflict')) {
3802
- console.error(colors.yellow(' Publishing too quickly'));
3803
- console.error('');
3804
- console.error('npm is still processing the previous version after multiple retries.');
3805
- console.error(colors.green('Solution: Wait a few more minutes and try again'));
3806
- console.error('');
3807
- }
3808
- else if (output.includes('403') || output.includes('forbidden') || output.includes('cannot publish over')) {
3809
- // Check if it's the "version already published" case
3810
- if (output.includes('cannot publish over') || output.includes('previously published')) {
3811
- // Extract the version number from the error if possible
3812
- const versionMatch = output.match(/versions?[:\s]+(\d+\.\d+\.\d+)/);
3813
- const failedVersion = versionMatch ? versionMatch[1] : null;
3814
- // Check if this is the version we just bumped to
3815
- const currentPkgVersion = readPackageJson(cwd).version;
3816
- const isCurrentVersion = failedVersion === currentPkgVersion;
3817
- console.error(colors.yellow('⚠ Version already published to npm'));
3818
- console.error('');
3819
- if (isCurrentVersion) {
3820
- console.error(colors.green('✓ Good news: This version was already published (possibly just now)'));
3821
- console.error('');
3822
- console.error('The git commit and tag were created, but npm says this version exists.');
3823
- console.error('This is actually SUCCESS - your package is published!');
3824
- console.error('');
3825
- console.error('Verify with: ' + colors.yellow(`npm view ${pkg.name}@${currentPkgVersion}`));
3826
- console.error('');
3827
- console.error(colors.yellow('Note: You may have a postversion script that auto-publishes.'));
3828
- console.error(colors.yellow('npmglobalize will now skip that hook to prevent this issue.'));
3829
- console.error('');
3830
- // Treat as success and continue
3831
- console.log(colors.green('✓ Treating as successful publish'));
3832
- // Don't return false - let it continue to push git tags
3833
- }
3834
- else {
3835
- console.error('Version ' + colors.yellow(failedVersion || 'unknown') + ' is already on npm.');
3836
- console.error('This shouldn\'t happen - the version was bumped but publish failed.');
3837
- console.error('');
3838
- console.error(colors.yellow('To recover:'));
3839
- console.error(' Just run npmglobalize again (it will bump to next version)');
3840
- console.error('');
3841
- if (transformResult.transformed) {
3842
- console.log('Restoring file: dependencies...');
3843
- const failPkg = readPackageJson(cwd);
3844
- restoreDeps(failPkg, verbose);
3845
- writePackageJson(cwd, failPkg);
3846
- runCommand('git', ['add', 'package.json'], { cwd, silent: true });
3847
- gitCommit('Restore file: dependencies', cwd);
3848
- }
3849
- return false;
3850
- }
3835
+ if (output.includes('err_string_too_long') || output.includes('string longer than')) {
3836
+ console.error(colors.red('Tarball too large — check .npmignore. Run: npm pack --dry-run'));
3837
+ }
3838
+ else if (output.includes('e409') || output.includes('409 conflict')) {
3839
+ console.error(colors.yellow('npm still processing previous version wait and retry'));
3840
+ }
3841
+ else if (output.includes('cannot publish over') || output.includes('previously published')) {
3842
+ const currentPkgVersion = readPackageJson(cwd).version;
3843
+ if (output.includes(currentPkgVersion)) {
3844
+ console.log(colors.green(' Already published continuing'));
3851
3845
  }
3852
3846
  else {
3853
- console.error(colors.yellow('Common causes:'));
3854
- console.error(' 1. Package name already taken by another user');
3855
- console.error(' 2. No publish access to this package');
3856
- console.error(' 3. Scope (@username) requires authentication');
3857
- console.error('');
3847
+ console.error(colors.yellow('Version conflict — run npmglobalize again'));
3858
3848
  if (transformResult.transformed) {
3859
- console.log('Restoring file: dependencies...');
3860
3849
  const failPkg = readPackageJson(cwd);
3861
3850
  restoreDeps(failPkg, verbose);
3862
3851
  writePackageJson(cwd, failPkg);
@@ -3866,18 +3855,22 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3866
3855
  return false;
3867
3856
  }
3868
3857
  }
3858
+ else if (output.includes('403') || output.includes('forbidden')) {
3859
+ console.error(colors.yellow('Publish forbidden — check npm login and package access'));
3860
+ if (transformResult.transformed) {
3861
+ const failPkg = readPackageJson(cwd);
3862
+ restoreDeps(failPkg, verbose);
3863
+ writePackageJson(cwd, failPkg);
3864
+ runCommand('git', ['add', 'package.json'], { cwd, silent: true });
3865
+ gitCommit('Restore file: dependencies', cwd);
3866
+ }
3867
+ return false;
3868
+ }
3869
3869
  else if (output.includes('402') || output.includes('payment required')) {
3870
- console.error(colors.yellow('Private packages require a paid npm account'));
3871
- console.error(' • Use --npm public to publish as public');
3872
- console.error(' • Or upgrade your npm account for private packages');
3873
- console.error('');
3870
+ console.error(colors.yellow('Private packages need paid npm account — use --npm public'));
3874
3871
  }
3875
3872
  else {
3876
- console.error(colors.yellow('Common causes:'));
3877
- console.error(' 1. Not logged in - run: npm login');
3878
- console.error(' 2. Version already published');
3879
- console.error(' 3. Authentication token expired');
3880
- console.error('');
3873
+ console.error(colors.yellow('Publish failed — run npm login or check npm whoami'));
3881
3874
  }
3882
3875
  if (transformResult.transformed) {
3883
3876
  console.log('Restoring file: dependencies...');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.136",
3
+ "version": "1.0.138",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -31,8 +31,9 @@
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",
34
+ "@bobfrankston/freezepak": "^0.1.6",
35
+ "@bobfrankston/importgen": "^0.1.32",
36
+ "@bobfrankston/themecolors": "^0.1.0",
36
37
  "@bobfrankston/userconfig": "^1.0.5",
37
38
  "@npmcli/package-json": "^7.0.4",
38
39
  "json5": "^2.2.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",