@vltpkg/cli-sdk 1.0.0-rc.32 → 1.0.0-rc.34

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.
Files changed (70) hide show
  1. package/dist/commands/access.d.ts +1 -0
  2. package/dist/commands/access.js +11 -6
  3. package/dist/commands/build.d.ts +1 -0
  4. package/dist/commands/build.js +1 -0
  5. package/dist/commands/cache.d.ts +1 -0
  6. package/dist/commands/cache.js +1 -0
  7. package/dist/commands/ci.d.ts +1 -0
  8. package/dist/commands/ci.js +1 -0
  9. package/dist/commands/create.d.ts +1 -0
  10. package/dist/commands/create.js +1 -0
  11. package/dist/commands/deprecate.d.ts +1 -0
  12. package/dist/commands/deprecate.js +4 -1
  13. package/dist/commands/dist-tag.d.ts +1 -0
  14. package/dist/commands/dist-tag.js +3 -1
  15. package/dist/commands/exec-cache.d.ts +1 -0
  16. package/dist/commands/exec-cache.js +1 -0
  17. package/dist/commands/exec.d.ts +1 -0
  18. package/dist/commands/exec.js +1 -0
  19. package/dist/commands/install.d.ts +1 -0
  20. package/dist/commands/install.js +12 -0
  21. package/dist/commands/list.d.ts +1 -0
  22. package/dist/commands/list.js +1 -0
  23. package/dist/commands/login.js +16 -3
  24. package/dist/commands/logout.d.ts +1 -0
  25. package/dist/commands/logout.js +7 -2
  26. package/dist/commands/pack.d.ts +1 -0
  27. package/dist/commands/pack.js +1 -0
  28. package/dist/commands/ping.d.ts +1 -0
  29. package/dist/commands/ping.js +5 -2
  30. package/dist/commands/profile.d.ts +1 -0
  31. package/dist/commands/profile.js +4 -2
  32. package/dist/commands/publish.d.ts +1 -0
  33. package/dist/commands/publish.js +9 -6
  34. package/dist/commands/query.d.ts +1 -0
  35. package/dist/commands/query.js +42 -1
  36. package/dist/commands/registry.d.ts +26 -0
  37. package/dist/commands/registry.js +99 -0
  38. package/dist/commands/run-exec.d.ts +1 -0
  39. package/dist/commands/run-exec.js +1 -0
  40. package/dist/commands/setup.d.ts +25 -0
  41. package/dist/commands/setup.js +142 -0
  42. package/dist/commands/token.d.ts +1 -0
  43. package/dist/commands/token.js +8 -3
  44. package/dist/commands/uninstall.d.ts +1 -0
  45. package/dist/commands/uninstall.js +1 -0
  46. package/dist/commands/unpublish.d.ts +1 -0
  47. package/dist/commands/unpublish.js +4 -1
  48. package/dist/commands/update.d.ts +1 -0
  49. package/dist/commands/update.js +1 -0
  50. package/dist/commands/view.d.ts +1 -0
  51. package/dist/commands/view.js +2 -1
  52. package/dist/commands/whoami.d.ts +1 -0
  53. package/dist/commands/whoami.js +7 -2
  54. package/dist/config/definition.d.ts +34 -0
  55. package/dist/config/definition.js +84 -11
  56. package/dist/config/index.js +6 -0
  57. package/dist/custom-help.js +81 -132
  58. package/dist/index.js +4 -0
  59. package/dist/load-command.d.ts +6 -0
  60. package/dist/output.js +15 -1
  61. package/dist/print-err.js +10 -1
  62. package/dist/registry-error-message.d.ts +2 -0
  63. package/dist/registry-error-message.js +29 -0
  64. package/dist/require-registry.d.ts +48 -0
  65. package/dist/require-registry.js +122 -0
  66. package/dist/select-registry.d.ts +23 -0
  67. package/dist/select-registry.js +40 -0
  68. package/dist/verbose-log.d.ts +31 -0
  69. package/dist/verbose-log.js +62 -0
  70. package/package.json +33 -26
@@ -40,9 +40,11 @@ const canonicalCommands = {
40
40
  profile: 'profile',
41
41
  publish: 'publish',
42
42
  query: 'query',
43
+ registry: 'registry',
43
44
  repo: 'repo',
44
45
  'run-exec': 'run-exec',
45
46
  run: 'run',
47
+ setup: 'setup',
46
48
  token: 'token',
47
49
  uninstall: 'uninstall',
48
50
  unpublish: 'unpublish',
@@ -155,26 +157,46 @@ export const definition = j
155
157
  .opt({
156
158
  registry: {
157
159
  hint: 'url',
158
- default: 'https://registry.npmjs.org/',
159
160
  description: `Sets the registry for fetching packages, when no registry
160
161
  is explicitly set on a specifier.
161
162
 
162
163
  For example, \`express@latest\` will be resolved by looking
163
164
  up the metadata from this registry.
164
165
 
165
- Note that alias specifiers starting with \`npm:\` will
166
- still map to \`https://registry.npmjs.org/\` if this is
167
- changed, unless the a new mapping is created via the
168
- \`--registries\` option.
166
+ There is **no default**. When unset, bare specifiers fall
167
+ back to the registry alias named by
168
+ \`--default-registry-alias\` (\`npm\` by default). If that
169
+ alias is also unconfigured, commands that need a registry
170
+ fail with a config error. Run \`vlt setup\` (or
171
+ \`vlt login\`) to configure one.
172
+
173
+ See https://docs.vlt.sh/cli
174
+ `,
175
+ },
176
+ 'default-registry-alias': {
177
+ hint: 'name',
178
+ default: 'npm',
179
+ description: `The name of the registry alias (a key in
180
+ \`--registries\`) that bare specifiers (e.g.
181
+ \`express@latest\`) and transitive dependencies without an
182
+ explicit registry protocol resolve through, when neither
183
+ \`--registry\` nor a matching \`--scoped-registries\` entry
184
+ applies.
185
+
186
+ Defaults to \`npm\`. Note that the \`npm\` alias has **no**
187
+ built-in URL -- it must be configured (for example via
188
+ \`vlt setup\`, which points it at your vlt.io registry),
189
+ otherwise bare specifiers will have no registry to resolve
190
+ against.
169
191
  `,
170
192
  },
171
193
  })
172
194
  .optList({
173
195
  registries: {
174
196
  hint: 'name=url',
175
- description: `Specify named registry hosts by their prefix. To set the
176
- default registry used for non-namespaced specifiers,
177
- use the \`--registry\` option.
197
+ description: `Specify named registry hosts by their prefix. To choose
198
+ which alias is used for non-namespaced specifiers, use the
199
+ \`--default-registry-alias\` option.
178
200
 
179
201
  Prefixes can be used as a package alias. For example:
180
202
 
@@ -182,9 +204,10 @@ export const definition = j
182
204
  vlt --registries loc=http://reg.local install foo@loc:foo@1.x
183
205
  \`\`\`
184
206
 
185
- By default, the public npm registry is registered to the
186
- \`npm:\` prefix. It is not recommended to change this
187
- mapping in most cases.
207
+ There is **no** built-in \`npm\` registry URL; the \`npm\`
208
+ alias must be configured (e.g. via \`vlt setup\`) before it
209
+ can be used. The \`gh\` and \`jsr\` aliases have built-in
210
+ defaults that can be overridden here.
188
211
  `,
189
212
  },
190
213
  'scoped-registries': {
@@ -554,6 +577,41 @@ export const definition = j
554
577
  'silent',
555
578
  ],
556
579
  },
580
+ })
581
+ .opt({
582
+ loglevel: {
583
+ hint: 'level',
584
+ default: 'info',
585
+ description: `Sets the verbosity of diagnostic logging written to
586
+ stderr (registry requests, warnings, etc). This is
587
+ independent of \`--view\`, which controls a command's
588
+ result output on stdout.
589
+
590
+ - silent: No diagnostic logging.
591
+ - error: Only errors.
592
+ - warn: Errors and warnings.
593
+ - info: Default. High-level progress.
594
+ - verbose: Adds per-request logging (each registry URL
595
+ with its cache/stale/fetch outcome and status code).
596
+ - debug: Everything, including low-level detail.
597
+
598
+ \`--verbose\` is shorthand for \`--loglevel=verbose\`.`,
599
+ validOptions: [
600
+ 'silent',
601
+ 'error',
602
+ 'warn',
603
+ 'info',
604
+ 'verbose',
605
+ 'debug',
606
+ ],
607
+ },
608
+ })
609
+ .flag({
610
+ verbose: {
611
+ description: `Shorthand for \`--loglevel=verbose\`. Streams each
612
+ registry request to stderr with its cache/fetch outcome
613
+ and status code, useful for debugging request behavior.`,
614
+ },
557
615
  })
558
616
  .optList({
559
617
  'dashboard-root': {
@@ -568,6 +626,12 @@ export const definition = j
568
626
  description: `Save installed packages to a package.json file as
569
627
  devDependencies`,
570
628
  },
629
+ 'save-exact': {
630
+ short: 'E',
631
+ description: `Save installed packages to package.json with an exact
632
+ version rather than using the default semver range
633
+ operator (e.g. \`1.2.3\` instead of \`^1.2.3\`).`,
634
+ },
571
635
  'save-optional': {
572
636
  short: 'O',
573
637
  description: `Save installed packages to a package.json file as
@@ -584,6 +648,15 @@ export const definition = j
584
648
  devDependencies or optionalDependencies, but you want to
585
649
  move it to be a non-optional production dependency.`,
586
650
  },
651
+ })
652
+ .opt({
653
+ 'save-prefix': {
654
+ description: `The version prefix to use when saving dependencies to
655
+ package.json (e.g. \`^\`, \`~\`, \`>=\`, or empty string
656
+ for exact versions). Defaults to \`^\`.
657
+ Ignored when \`--save-exact\` is set.`,
658
+ default: '^',
659
+ },
587
660
  })
588
661
  .opt({
589
662
  'expect-results': {
@@ -212,6 +212,12 @@ export class Config {
212
212
  else
213
213
  this.command = getCommand(p.values['fallback-command']);
214
214
  Object.assign(this, p);
215
+ // `--verbose` is shorthand for `--loglevel=verbose`. Only apply it
216
+ // when loglevel is still at its default so an explicit `--loglevel`
217
+ // always wins.
218
+ if (p.values.verbose && p.values.loglevel === 'info') {
219
+ p.values.loglevel = 'verbose';
220
+ }
215
221
  /* c8 ignore start - unpossible */
216
222
  if (!isParsed(this))
217
223
  throw error('failed to parse config');
@@ -1,5 +1,6 @@
1
1
  import { loadPackageJson } from 'package-json-from-dist';
2
2
  import chalk from 'chalk';
3
+ import { commandAliases } from "./config/definition.js";
3
4
  const { version } = loadPackageJson(import.meta.filename, process.env.__VLT_INTERNAL_CLI_PACKAGE_JSON);
4
5
  // Custom yellow color: #FFE15D
5
6
  const customYellow = chalk.hex('#FFE15D');
@@ -42,227 +43,176 @@ const makeStyler = (colors) => {
42
43
  return styledText;
43
44
  };
44
45
  };
45
- // Single source of truth for all commands with metadata
46
- const allCommands = [
47
- {
48
- name: 'bugs',
49
- aliases: [],
46
+ /**
47
+ * Help metadata for every command target in the command registry. Using a
48
+ * record keyed by CommandName makes missing or stale entries a type error;
49
+ * aliases are derived separately from the registry below.
50
+ */
51
+ const commandHelp = {
52
+ access: {
53
+ args: '<command> [<args>]',
54
+ desc: 'Manage package access and team permissions',
55
+ },
56
+ bugs: {
50
57
  args: '[<spec>]',
51
58
  desc: 'Open the bug tracker for a package',
52
- showByDefault: false,
53
59
  },
54
- {
55
- name: 'build',
56
- aliases: ['b'],
60
+ build: {
57
61
  args: '<selector>',
58
62
  desc: 'Build packages with lifecycle scripts',
59
- showByDefault: true,
60
63
  defaultOrder: 4,
61
64
  },
62
- {
63
- name: 'cache',
64
- aliases: [],
65
+ cache: {
65
66
  args: '[add|ls|info|clean|delete|delete-before|delete-all]',
66
67
  desc: 'Manage the package cache',
67
- showByDefault: false,
68
68
  },
69
- {
70
- name: 'ci',
71
- aliases: [],
69
+ ci: {
72
70
  args: '',
73
71
  desc: 'Clean install (frozen lockfile)',
74
- showByDefault: false,
75
72
  },
76
- {
77
- name: 'config',
78
- aliases: [],
73
+ config: {
79
74
  args: '[get|pick|list|set|delete|edit|location]',
80
75
  desc: 'Get or set configuration',
81
- showByDefault: false,
82
76
  },
83
- {
84
- name: 'create',
85
- aliases: [],
77
+ create: {
86
78
  args: '<initializer> [args...]',
87
79
  desc: 'Create a new project from a template',
88
- showByDefault: false,
89
80
  },
90
- {
91
- name: 'docs',
92
- aliases: [],
81
+ deprecate: {
82
+ args: '<pkg>[@<version>] <message>',
83
+ desc: 'Deprecate a package or version range',
84
+ },
85
+ 'dist-tag': {
86
+ args: '[add|rm|ls] [<args>]',
87
+ desc: 'Manage package distribution tags',
88
+ },
89
+ docs: {
93
90
  args: '',
94
91
  desc: 'Open the docs of the current project',
95
- showByDefault: false,
96
92
  },
97
- {
98
- name: 'exec',
99
- aliases: ['x'],
93
+ exec: {
100
94
  args: '<executable>',
101
95
  desc: 'Execute a package bin',
102
- showByDefault: true,
103
96
  defaultOrder: 6,
104
97
  },
105
- {
106
- name: 'exec-cache',
107
- aliases: ['xc'],
98
+ 'exec-cache': {
108
99
  args: '[ls|delete|info|install]',
109
100
  desc: 'Manage the exec cache',
110
- showByDefault: false,
111
101
  },
112
- {
113
- name: 'exec-local',
114
- aliases: ['xl'],
102
+ 'exec-local': {
115
103
  args: '<command>',
116
104
  desc: 'Execute a local package bin',
117
- showByDefault: false,
118
105
  },
119
- {
120
- name: 'help',
121
- aliases: ['h', '?'],
106
+ help: {
122
107
  args: '[<command>]',
123
108
  desc: 'Show help for a command',
124
- showByDefault: false,
125
109
  },
126
- {
127
- name: 'init',
128
- aliases: [],
110
+ init: {
129
111
  args: '',
130
112
  desc: 'Initialize a new project',
131
- showByDefault: true,
132
113
  defaultOrder: 1,
133
114
  },
134
- {
135
- name: 'install',
136
- aliases: ['i', 'add'],
115
+ install: {
137
116
  args: '[<package>...]',
138
117
  desc: 'Install dependencies',
139
- showByDefault: true,
140
118
  defaultOrder: 2,
141
119
  },
142
- {
143
- name: 'list',
144
- aliases: ['ls'],
120
+ list: {
145
121
  args: '',
146
122
  desc: 'List installed packages',
147
- showByDefault: false,
148
123
  },
149
- {
150
- name: 'login',
151
- aliases: [],
124
+ login: {
152
125
  args: '',
153
126
  desc: 'Authenticate with a registry',
154
- showByDefault: false,
155
127
  },
156
- {
157
- name: 'logout',
158
- aliases: [],
128
+ logout: {
159
129
  args: '',
160
130
  desc: 'Log out from a registry',
161
- showByDefault: false,
162
131
  },
163
- {
164
- name: 'pack',
165
- aliases: [],
132
+ pack: {
166
133
  args: '',
167
134
  desc: 'Create a tarball from a package',
168
- showByDefault: false,
169
135
  },
170
- {
171
- name: 'ping',
172
- aliases: [],
136
+ ping: {
173
137
  args: '[<registry-alias>]',
174
138
  desc: 'Ping configured registries',
175
- showByDefault: false,
176
139
  },
177
- {
178
- name: 'pkg',
179
- aliases: ['p'],
140
+ pkg: {
180
141
  args: '<command>',
181
142
  desc: 'Manage package metadata',
182
- showByDefault: true,
183
143
  defaultOrder: 7,
184
144
  },
185
- {
186
- name: 'publish',
187
- aliases: ['pub'],
145
+ profile: {
146
+ args: '<command> [<args>]',
147
+ desc: 'Get or set registry profile properties',
148
+ },
149
+ publish: {
188
150
  args: '',
189
151
  desc: 'Publish package to registry',
190
- showByDefault: true,
191
152
  defaultOrder: 8,
192
153
  },
193
- {
194
- name: 'query',
195
- aliases: ['q'],
154
+ query: {
196
155
  args: '<selector>',
197
156
  desc: 'Query for packages in the project',
198
- showByDefault: true,
199
157
  defaultOrder: 3,
200
158
  },
201
- {
202
- name: 'repo',
203
- aliases: [],
159
+ registry: {
160
+ args: '<alias> <command>',
161
+ desc: 'Run an account command against a named registry',
162
+ defaultOrder: 9,
163
+ },
164
+ repo: {
204
165
  args: '[<spec>]',
205
166
  desc: 'Open the repository page for a package',
206
- showByDefault: false,
207
167
  },
208
- {
209
- name: 'run',
210
- aliases: ['r'],
168
+ run: {
211
169
  args: '<script>',
212
170
  desc: 'Run a script defined in package.json',
213
- showByDefault: true,
214
171
  defaultOrder: 5,
215
172
  },
216
- {
217
- name: 'run-exec',
218
- aliases: ['rx'],
173
+ 'run-exec': {
219
174
  args: '<script>',
220
175
  desc: 'Run a script &/or fallback to executing a binary',
221
- showByDefault: false,
222
176
  },
223
- {
224
- name: 'token',
225
- aliases: [],
177
+ setup: {
178
+ args: '[<account>]',
179
+ desc: 'Configure your vlt.io account registries',
180
+ defaultOrder: 0,
181
+ },
182
+ token: {
226
183
  args: '[add|rm]',
227
184
  desc: 'Manage authentication tokens',
228
- showByDefault: false,
229
185
  },
230
- {
231
- name: 'uninstall',
232
- aliases: ['rm'],
186
+ uninstall: {
233
187
  args: '[<package>...]',
234
188
  desc: 'Remove dependencies',
235
- showByDefault: false,
236
189
  },
237
- {
238
- name: 'unpublish',
239
- aliases: [],
190
+ unpublish: {
240
191
  args: '<pkg>[@<version>]',
241
192
  desc: 'Remove a package from the registry',
242
- showByDefault: false,
243
193
  },
244
- {
245
- name: 'update',
246
- aliases: ['u'],
194
+ update: {
247
195
  args: '',
248
196
  desc: 'Update package versions to latest in-range',
249
- showByDefault: false,
250
197
  },
251
- {
252
- name: 'version',
253
- aliases: [],
198
+ version: {
254
199
  args: '<increment>',
255
200
  desc: 'Bump package version',
256
- showByDefault: false,
257
201
  },
258
- {
259
- name: 'whoami',
260
- aliases: [],
202
+ view: {
203
+ args: '<pkg>[@<version>] [<field>]',
204
+ desc: 'View registry information about a package',
205
+ },
206
+ whoami: {
261
207
  args: '',
262
208
  desc: 'Display the current user',
263
- showByDefault: false,
264
209
  },
265
- ];
210
+ };
211
+ const allCommands = Object.entries(commandHelp).map(([name, metadata]) => ({
212
+ name,
213
+ aliases: commandAliases.get(name) ?? [],
214
+ ...metadata,
215
+ }));
266
216
  /**
267
217
  * Generates the custom default help output for vlt
268
218
  */
@@ -270,8 +220,8 @@ export const generateDefaultHelp = (colors = false) => {
270
220
  const s = makeStyler(colors);
271
221
  // Get default commands and sort by defaultOrder
272
222
  const defaultCommands = allCommands
273
- .filter(cmd => cmd.showByDefault)
274
- .sort((a, b) => (a.defaultOrder || 0) - (b.defaultOrder || 0));
223
+ .filter(cmd => cmd.defaultOrder !== undefined)
224
+ .sort((a, b) => (a.defaultOrder ?? 0) - (b.defaultOrder ?? 0));
275
225
  // Generate commands with tighter alias spacing but proper table structure
276
226
  const commandsSection = defaultCommands
277
227
  .map(cmd => {
@@ -282,7 +232,7 @@ export const generateDefaultHelp = (colors = false) => {
282
232
  const nameColumn = cmd.name.padEnd(10);
283
233
  // Consistent args column (14 chars)
284
234
  const argsColumn = cmd.args.padEnd(16);
285
- return ` ${s('dim', aliasColumn)}${s(['yellow', 'bold'], nameColumn)}${s('dim', argsColumn)}${cmd.desc}`;
235
+ return ` ${s('dim', aliasColumn)}${s(['yellow', 'bold'], nameColumn)}${s('dim', argsColumn)} ${cmd.desc}`;
286
236
  })
287
237
  .join('\n');
288
238
  return `${s(['bold'], '⚡️ vlt')} ${s('dim', '/vōlt/')} next-gen package management ${s('dim', `v${version}`)}
@@ -318,6 +268,8 @@ export const generateFullHelp = (colors = false) => {
318
268
  const s = makeStyler(colors);
319
269
  // Use all commands sorted alphabetically
320
270
  const commands = [...allCommands].sort((a, b) => a.name.localeCompare(b.name));
271
+ const aliasLabels = commands.map(cmd => cmd.aliases.length ? `${cmd.aliases.join(', ')},` : '');
272
+ const aliasWidth = Math.max(...aliasLabels.map(label => label.length));
321
273
  // Define only globally applicable flags (alphabetically sorted by long name)
322
274
  const flags = [
323
275
  {
@@ -348,7 +300,7 @@ export const generateFullHelp = (colors = false) => {
348
300
  shorts: [],
349
301
  long: 'registry',
350
302
  args: '<url>',
351
- desc: 'Override default registry',
303
+ desc: 'Set the registry to resolve packages against',
352
304
  },
353
305
  {
354
306
  shorts: ['v'],
@@ -372,17 +324,14 @@ export const generateFullHelp = (colors = false) => {
372
324
  if (firstLetter !== lastFirstLetter && index > 0) {
373
325
  commandsSection += '\n';
374
326
  }
375
- // Fixed width columns for proper alignment with space after comma
376
- const aliasColumn = cmd.aliases.length > 0 ?
377
- (cmd.aliases.join(', ') + ', ').padEnd(9)
378
- : ' ';
327
+ const aliasColumn = aliasLabels[index]?.padEnd(aliasWidth) ?? '';
379
328
  const nameColumn = cmd.name.padEnd(12);
380
329
  // Truncate args if longer than 16 chars and add ellipsis
381
330
  const truncatedArgs = cmd.args.length > 16 ?
382
331
  cmd.args.substring(0, 13) + '...'
383
332
  : cmd.args;
384
333
  const argsColumn = truncatedArgs.padEnd(16);
385
- commandsSection += `${s('dim', aliasColumn)}${s(['yellow', 'bold'], nameColumn)}${s('dim', argsColumn)}${cmd.desc}`;
334
+ commandsSection += ` ${s('dim', aliasColumn)} ${s(['yellow', 'bold'], nameColumn)} ${s('dim', argsColumn)} ${cmd.desc}`;
386
335
  if (index < commands.length - 1) {
387
336
  commandsSection += '\n';
388
337
  }
package/dist/index.js CHANGED
@@ -66,6 +66,10 @@ const run = async () => {
66
66
  }
67
67
  return process.exit(process.exitCode || 1);
68
68
  }
69
+ if (vlt.command === 'registry') {
70
+ const { dispatchRegistry } = await import("./commands/registry.js");
71
+ await dispatchRegistry(vlt);
72
+ }
69
73
  const command = await loadCommand(vlt.command);
70
74
  await outputCommand(command, vlt, { start, vltVersion: version });
71
75
  };
@@ -11,5 +11,11 @@ export type Command<T> = {
11
11
  command: CommandFn<T>;
12
12
  usage: CommandUsage;
13
13
  views: Views<T>;
14
+ /**
15
+ * Set to `true` by commands that hit a registry or resolve registry
16
+ * specs. There is no default registry, so `outputCommand()` fails
17
+ * early with an `ECONFIG` error when one of these runs unconfigured.
18
+ */
19
+ needsRegistry?: boolean;
14
20
  };
15
21
  export declare const loadCommand: <T>(command: Commands[keyof Commands] | undefined) => Promise<Command<T>>;
package/dist/output.js CHANGED
@@ -2,9 +2,11 @@ import { formatWithOptions, styleText as utilStyleText, } from 'node:util';
2
2
  import { createSupportsColor } from 'supports-color';
3
3
  import { defaultView } from "./config/definition.js";
4
4
  import { printErr, formatOptions } from "./print-err.js";
5
+ import { hasConfiguredRegistry, missingRegistryError, } from "./require-registry.js";
5
6
  import { isViewClass } from "./view.js";
6
7
  import { generateDefaultHelp, generateFullHelp, } from "./custom-help.js";
7
8
  import { flush as flushTelemetry, trackCommand, trackError, } from "./telemetry.js";
9
+ import { startRequestLog } from "./verbose-log.js";
8
10
  /* c8 ignore start - CI env detection is a best-effort heuristic */
9
11
  const isCI = () => !!(process.env.CI ||
10
12
  process.env.GITHUB_ACTIONS ||
@@ -90,7 +92,7 @@ const startView = (conf, opts, views, { start } = { start: Date.now() }) => {
90
92
  export const outputCommand = async (cliCommand, conf, { start, vltVersion } = {
91
93
  start: Date.now(),
92
94
  }) => {
93
- const { usage, views, command } = cliCommand;
95
+ const { usage, views, command, needsRegistry } = cliCommand;
94
96
  const stdoutColor = conf.values.color ?? supportsColor(process.stdout);
95
97
  const stderrColor = conf.values.color ?? supportsColor(process.stderr);
96
98
  if (conf.values.help) {
@@ -111,13 +113,24 @@ export const outputCommand = async (cliCommand, conf, { start, vltVersion } = {
111
113
  if (stderrColor)
112
114
  styleTextStderr = styleText;
113
115
  /* c8 ignore stop */
116
+ // Stream per-request diagnostics to stderr when `--loglevel` is verbose
117
+ // or higher (e.g. via `--verbose`). No-op otherwise.
118
+ const stopRequestLog = startRequestLog(conf.values.loglevel, line => stderr(line), styleTextStderr);
114
119
  const { onDone, onError } = startView(conf,
115
120
  // assume views will always output to stdout so use color support from there
116
121
  { colors: stdoutColor }, views, { start });
117
122
  const telemetryEnabled = conf.values.telemetry;
118
123
  const commandName = conf.command;
119
124
  try {
125
+ // checked here rather than in run() so that the missing-registry
126
+ // error is rendered, tracked and exited like any other command
127
+ // error, and so that the `--help` return above still works when
128
+ // nothing is configured.
129
+ if (needsRegistry && !hasConfiguredRegistry(conf)) {
130
+ throw missingRegistryError();
131
+ }
120
132
  const output = await onDone(await command(conf));
133
+ stopRequestLog();
121
134
  const duration_ms = Date.now() - start;
122
135
  trackCommand({
123
136
  command: commandName,
@@ -145,6 +158,7 @@ export const outputCommand = async (cliCommand, conf, { start, vltVersion } = {
145
158
  await flushTelemetry();
146
159
  }
147
160
  catch (err) {
161
+ stopRequestLog();
148
162
  onError?.(err);
149
163
  process.exitCode ||= 1;
150
164
  const duration_ms = Date.now() - start;
package/dist/print-err.js CHANGED
@@ -13,6 +13,15 @@ export const formatOptions = {
13
13
  };
14
14
  const isNonEmptyString = (v) => !!v && typeof v === 'string';
15
15
  const formatURL = (v, format) => v instanceof URL ? v.toString() : /* c8 ignore next */ format(v);
16
+ // True when `v` overrides the default `Object.prototype.toString`, i.e.
17
+ // stringifying it yields something meaningful (like a `Spec` → `next@*`)
18
+ // rather than `[object Object]`.
19
+ const hasCustomToString = (v) => isObject(v) && v.toString !== Object.prototype.toString;
20
+ // Render a value that has a meaningful custom `toString()` (such as a
21
+ // `Spec`, e.g. `next@*`) as a concise string rather than dumping its
22
+ // entire internal structure via util.inspect. Falls back to `format`
23
+ // for plain values/objects.
24
+ const formatToString = (v, format) => hasCustomToString(v) ? v.toString() : format(v);
16
25
  const formatArray = (v, format, joiner = ', ') => Array.isArray(v) ? v.join(joiner) : /* c8 ignore next */ format(v);
17
26
  export const indent = (lines, num = 2) => lines
18
27
  .split('\n')
@@ -147,7 +156,7 @@ const printCode = (err, usage, stderr, format) => {
147
156
  stderr(indent(`While fetching: ${formatURL(url, format)}`));
148
157
  }
149
158
  if (spec) {
150
- stderr(indent(`To satisfy: ${format(spec)}`));
159
+ stderr(indent(`To satisfy: ${formatToString(spec, format)}`));
151
160
  }
152
161
  if (from) {
153
162
  stderr(indent(`From: ${format(from)}`));
@@ -0,0 +1,2 @@
1
+ import type { CacheEntry } from '@vltpkg/registry-client';
2
+ export declare const registryErrorMessage: (response: CacheEntry) => string;
@@ -0,0 +1,29 @@
1
+ import { STATUS_CODES } from 'node:http';
2
+ export const registryErrorMessage = (response) => {
3
+ const statusMessage = STATUS_CODES[response.statusCode];
4
+ const status = `${response.statusCode}${statusMessage ? ` ${statusMessage}` : ''}`;
5
+ let text;
6
+ try {
7
+ text = response.text().trim();
8
+ }
9
+ catch {
10
+ return status;
11
+ }
12
+ if (!text)
13
+ return status;
14
+ let detail = text;
15
+ try {
16
+ const body = JSON.parse(text);
17
+ if (body &&
18
+ typeof body === 'object' &&
19
+ 'error' in body &&
20
+ typeof body.error === 'string' &&
21
+ body.error) {
22
+ detail = body.error;
23
+ }
24
+ }
25
+ catch {
26
+ // A non-JSON response body is itself the most useful error detail.
27
+ }
28
+ return `${status} — ${detail}`;
29
+ };