pulso-doctor 1.0.0 → 1.1.0

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/CHANGELOG.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # pulso-doctor
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Quando há um token gitlab configurado, verifica se o token está válido, expirado, ou sem as permissões necessárias. Traz um feedback de como proceder, sem mostrar o valor do token verificado.
8
+
9
+ ## 1.1.0 - 11/11/2026
10
+
11
+ ### Minor Changes
12
+
13
+ - Adiciona teste real de validação do token do registry Pulso contra o GitLab via requisição HTTP autenticada.
14
+ - Detecta e reporta tokens expirados, revogados ou sem permissão de acesso ao registry, com mensagens de erro claras e orientações de correção indicando a variável de ambiente ou arquivo correspondente.
15
+ - Adiciona opção `--skip-token-check` na CLI e opção `skipTokenVerification` na API programática (além do suporte à variável `PULSO_DOCTOR_SKIP_TOKEN_CHECK=1`).
16
+ - Suporta chaves de autenticação em nível de projeto e em nível de instância GitLab.
17
+ - Bloqueia a execução do auto-fix para instalar dependências quando o token de autenticação do registry estiver inválido ou expirado.
18
+
3
19
  ## 1.0.0 - 05/11/2026
20
+
4
21
  ### Major Changes
5
22
 
6
23
  - 963a91d: Disponibiliza o diagnóstico público do ambiente necessário para consumir os componentes Pulso Web e App, com saída orientada, API reutilizável e auto-fix opcional, sem depender do registry privado para sua execução.
package/README.md CHANGED
@@ -45,6 +45,7 @@ Outras opções:
45
45
  ```bash
46
46
  npx pulso-doctor --cwd ./apps/site
47
47
  npx pulso-doctor --json
48
+ npx pulso-doctor --skip-token-check
48
49
  npx pulso-doctor --log-file ./diagnosticos/pulso.jsonl
49
50
  ```
50
51
 
@@ -61,6 +62,7 @@ desativar as cores explicitamente, use `NO_COLOR=1 npx pulso-doctor`.
61
62
  - presença e sintaxe do `package.json`;
62
63
  - configuração do registry `@pulso` e sua autenticação no `.npmrc`, no
63
64
  `.yarnrc` do Yarn Classic ou no `.yarnrc.yml` do Yarn Berry;
65
+ - validação real do token contra o registry GitLab (testa se o token é válido, se expirou ou se foi revogado);
64
66
  - registry `@rd-core-svg` para projetos App;
65
67
  - proteção do `.npmrc` no `.gitignore` ou uso de variável de ambiente nas
66
68
  configurações do Yarn;
@@ -73,7 +75,11 @@ Quando existe `node_modules`, a versão instalada é a fonte principal. Sem uma
73
75
  instalação local, o Doctor avalia o range declarado e informa que a versão
74
76
  resolvida ainda precisa ser confirmada.
75
77
 
76
- O Doctor não testa o token contra o GitLab, não altera configurações e não
78
+ O Doctor valida o token fazendo uma consulta HTTP ao endpoint de pacotes do GitLab
79
+ (sem expor o token nos logs ou saída). Em caso de falha de conexão (offline),
80
+ registra um alerta em vez de reprovar o diagnóstico. Caso queira desativar a
81
+ verificação de rede do token, utilize a opção `--skip-token-check` ou a variável de
82
+ ambiente `PULSO_DOCTOR_SKIP_TOKEN_CHECK=1`. O Doctor não altera configurações e não
77
83
  instala dependências durante o diagnóstico normal. Alterações só são executadas
78
84
  depois da confirmação interativa ou com `--fix`.
79
85
 
@@ -185,7 +191,9 @@ mesmo diagnóstico e o fluxo de correção.
185
191
 
186
192
  ## Privacidade
187
193
 
188
- O pacote não possui analytics ou telemetria. O diagnóstico sem auto-fix não faz
189
- chamadas de rede e só escreve o arquivo de log. Quando autorizado, o auto-fix
190
- pode editar os arquivos de configuração citados acima e executar o gerenciador
191
- de pacotes do projeto.
194
+ O pacote não possui analytics ou telemetria. A única chamada de rede realizada
195
+ pelo diagnóstico é a validação do token diretamente contra o registry privado do
196
+ GitLab configurado no projeto (podendo ser desativada com `--skip-token-check`).
197
+ O valor do token nunca é gravado nos logs ou exibido na saída do terminal. Quando
198
+ autorizado, o auto-fix pode editar os arquivos de configuração citados acima e
199
+ executar o gerenciador de pacotes do projeto.
@@ -5,7 +5,9 @@ import { intersects, minVersion, satisfies, subset, valid } from "semver";
5
5
  import { parse as parseYaml } from "yaml";
6
6
  var PULSO_REGISTRY = "https://gitlab.com/api/v4/projects/75913903/packages/npm/";
7
7
  var PULSO_AUTH_KEY = "//gitlab.com/api/v4/projects/75913903/packages/npm/:_authToken";
8
+ var INSTANCE_AUTH_KEY = "//gitlab.com/api/v4/packages/npm/:_authToken";
8
9
  var RD_CORE_SVG_REGISTRY = "https://gitlab.com/api/v4/projects/44326237/packages/npm/";
10
+ var RD_CORE_SVG_AUTH_KEY = "//gitlab.com/api/v4/projects/44326237/packages/npm/:_authToken";
9
11
  var SHARED_DEPENDENCIES = [
10
12
  { name: "@pulso/design-tokens" },
11
13
  { name: "@pulso/icons" }
@@ -134,6 +136,10 @@ function normalizeRegistry(value) {
134
136
  if (!value) return void 0;
135
137
  return `${value.replace(/\/+$/, "")}/`;
136
138
  }
139
+ function stripQuotes(value) {
140
+ if (!value) return void 0;
141
+ return value.trim().replace(/(?:^["'])|(?:["']$)/g, "");
142
+ }
137
143
  function npmrcSnippet(platforms) {
138
144
  const appRegistry = platforms.includes("app") ? `@rd-core-svg:registry=${RD_CORE_SVG_REGISTRY}
139
145
 
@@ -218,7 +224,87 @@ async function readRegistryConfiguration(cwd) {
218
224
  return null;
219
225
  }
220
226
  }
221
- async function validateRegistryConfiguration(cwd, platforms, environment) {
227
+ async function verifyRegistryToken(options) {
228
+ const fetchFn = options.fetchFn ?? globalThis.fetch;
229
+ if (typeof fetchFn !== "function") {
230
+ return {
231
+ ok: false,
232
+ status: "network-error",
233
+ detail: "fetch n\xE3o dispon\xEDvel no ambiente de execu\xE7\xE3o."
234
+ };
235
+ }
236
+ const normalizedRegistry = options.registry.replace(/\/+$/, "");
237
+ const packageName = options.packageName ?? (normalizedRegistry.includes("44326237") ? "@rd-core-svg/svg" : "@pulso/design-tokens");
238
+ const targetUrl = `${normalizedRegistry}/${packageName}`;
239
+ const controller = new AbortController();
240
+ const timeoutMs = options.timeoutMs ?? 1e4;
241
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
242
+ try {
243
+ const response = await fetchFn(targetUrl, {
244
+ method: "GET",
245
+ headers: {
246
+ Authorization: `Bearer ${options.token.trim()}`,
247
+ "User-Agent": "pulso-doctor",
248
+ Accept: "application/json"
249
+ },
250
+ signal: controller.signal
251
+ });
252
+ if (response.ok) {
253
+ return {
254
+ ok: true,
255
+ status: "valid",
256
+ httpStatus: response.status
257
+ };
258
+ }
259
+ let detail;
260
+ try {
261
+ const data = await response.json();
262
+ detail = data.error_description || data.message || data.error;
263
+ } catch {
264
+ }
265
+ const isExpired = Boolean(detail) && (detail.toLowerCase().includes("expired") || detail.toLowerCase().includes("expirado"));
266
+ if (response.status === 401) {
267
+ return {
268
+ ok: false,
269
+ status: isExpired ? "expired" : "invalid",
270
+ httpStatus: response.status,
271
+ detail
272
+ };
273
+ }
274
+ if (response.status === 403) {
275
+ return {
276
+ ok: false,
277
+ status: "forbidden",
278
+ httpStatus: response.status,
279
+ detail
280
+ };
281
+ }
282
+ if (response.status === 404) {
283
+ return {
284
+ ok: false,
285
+ status: "not-found",
286
+ httpStatus: response.status,
287
+ detail
288
+ };
289
+ }
290
+ return {
291
+ ok: false,
292
+ status: "invalid",
293
+ httpStatus: response.status,
294
+ detail: detail ?? `HTTP ${response.status}`
295
+ };
296
+ } catch (error) {
297
+ const isTimeout = error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
298
+ return {
299
+ ok: false,
300
+ status: "network-error",
301
+ detail: isTimeout ? "tempo limite esgotado ao conectar ao GitLab (timeout)" : error instanceof Error ? error.message : "falha de conex\xE3o com o registry"
302
+ };
303
+ } finally {
304
+ clearTimeout(timeoutId);
305
+ }
306
+ }
307
+ async function validateRegistryConfiguration(cwd, platforms, environment, options = {}) {
222
308
  const configuration = await readRegistryConfiguration(cwd);
223
309
  if (!configuration) {
224
310
  const usesYarnBerry = await fileExists(join(cwd, ".yarnrc.yml"));
@@ -262,7 +348,9 @@ async function validateRegistryConfiguration(cwd, platforms, environment) {
262
348
  resolution: source === ".yarnrc.yml" ? `Defina npmScopes.pulso.npmRegistryServer=${PULSO_REGISTRY} no .yarnrc.yml.` : source === ".yarnrc" ? `Defina "@pulso:registry" "${PULSO_REGISTRY}" no .yarnrc.` : `Defina @pulso:registry=${PULSO_REGISTRY} no .npmrc.`
263
349
  })
264
350
  );
265
- const authValue = entries.get(PULSO_AUTH_KEY);
351
+ const authValue = entries.get(PULSO_AUTH_KEY) ?? entries.get(INSTANCE_AUTH_KEY);
352
+ let pulsoToken;
353
+ let pulsoVariableName;
266
354
  if (!authValue) {
267
355
  results.push(
268
356
  check({
@@ -276,21 +364,105 @@ async function validateRegistryConfiguration(cwd, platforms, environment) {
276
364
  })
277
365
  );
278
366
  } else {
279
- const variableMatch = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(authValue);
280
- const variableName = variableMatch?.[1];
281
- const available = variableName ? Boolean(environment[variableName]) : true;
367
+ const rawAuth = stripQuotes(authValue) ?? "";
368
+ const variableMatch = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(rawAuth) ?? /^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(rawAuth);
369
+ pulsoVariableName = variableMatch?.[1];
370
+ pulsoToken = pulsoVariableName ? stripQuotes(environment[pulsoVariableName]) : rawAuth;
371
+ const available = Boolean(pulsoToken);
282
372
  results.push(
283
373
  check({
284
374
  id: "registry.pulso-auth",
285
375
  category: "registry",
286
376
  status: available ? "pass" : "error",
287
377
  title: "Autentica\xE7\xE3o do registry Pulso",
288
- message: available ? `Autentica\xE7\xE3o configurada em ${source} via ${variableName ? `vari\xE1vel ${variableName}` : "valor local"}.` : `A vari\xE1vel ${variableName} referenciada em ${source} n\xE3o est\xE1 dispon\xEDvel.`,
289
- actual: variableName ? `vari\xE1vel ${variableName}` : "valor configurado",
290
- resolution: available ? void 0 : `Exporte ${variableName} antes de instalar pacotes Pulso.`
378
+ message: available ? `Autentica\xE7\xE3o configurada em ${source} via ${pulsoVariableName ? `vari\xE1vel ${pulsoVariableName}` : "valor local"}.` : `A vari\xE1vel ${pulsoVariableName} referenciada em ${source} n\xE3o est\xE1 dispon\xEDvel.`,
379
+ actual: pulsoVariableName ? `vari\xE1vel ${pulsoVariableName}` : "valor configurado",
380
+ resolution: available ? void 0 : `Exporte ${pulsoVariableName} antes de instalar pacotes Pulso.`
291
381
  })
292
382
  );
293
383
  }
384
+ const skipEnv = environment.PULSO_DOCTOR_SKIP_TOKEN_CHECK === "1" || environment.PULSO_DOCTOR_SKIP_TOKEN_CHECK === "true";
385
+ const shouldVerifyTokens = !options.skipTokenVerification && !skipEnv;
386
+ if (pulsoToken && shouldVerifyTokens) {
387
+ const verifier = options.tokenVerifier ?? verifyRegistryToken;
388
+ const verification = await verifier({
389
+ registry: pulsoRegistry ?? PULSO_REGISTRY,
390
+ token: pulsoToken,
391
+ fetchFn: options.fetchFn
392
+ });
393
+ if (verification.status === "valid") {
394
+ results.push(
395
+ check({
396
+ id: "registry.pulso-token",
397
+ category: "registry",
398
+ status: "pass",
399
+ title: "Valida\xE7\xE3o do token do registry Pulso",
400
+ message: "Token de autentica\xE7\xE3o validado com sucesso no GitLab.",
401
+ actual: "token v\xE1lido (HTTP 200)"
402
+ })
403
+ );
404
+ } else if (verification.status === "expired") {
405
+ results.push(
406
+ check({
407
+ id: "registry.pulso-token",
408
+ category: "registry",
409
+ status: "error",
410
+ title: "Valida\xE7\xE3o do token do registry Pulso",
411
+ message: `O token do registry Pulso est\xE1 expirado no GitLab (${verification.detail ?? "Token is expired"}).`,
412
+ actual: "token expirado (HTTP 401)",
413
+ resolution: pulsoVariableName ? `Gere um novo token no GitLab com escopo de leitura do registry e atualize a vari\xE1vel ${pulsoVariableName} (se j\xE1 atualizou no ~/.zshrc ou ~/.bashrc, execute "source ~/.zshrc" ou abra um novo terminal).` : `Gere um novo token no GitLab com escopo de leitura do registry e atualize a configura\xE7\xE3o em ${source}.`
414
+ })
415
+ );
416
+ } else if (verification.status === "forbidden") {
417
+ results.push(
418
+ check({
419
+ id: "registry.pulso-token",
420
+ category: "registry",
421
+ status: "error",
422
+ title: "Valida\xE7\xE3o do token do registry Pulso",
423
+ message: `O token n\xE3o possui permiss\xE3o para acessar o registry privado do Pulso no GitLab${verification.detail ? ` (${verification.detail})` : ""}.`,
424
+ actual: "permiss\xE3o negada (HTTP 403)",
425
+ resolution: "Verifique se o token possui as permiss\xF5es necess\xE1rias (escopo read_package_registry / read_api) no projeto Pulso."
426
+ })
427
+ );
428
+ } else if (verification.status === "not-found") {
429
+ results.push(
430
+ check({
431
+ id: "registry.pulso-token",
432
+ category: "registry",
433
+ status: "error",
434
+ title: "Valida\xE7\xE3o do token do registry Pulso",
435
+ message: "O registry privado do Pulso n\xE3o foi encontrado ou n\xE3o \xE9 acess\xEDvel com este token (HTTP 404).",
436
+ actual: "n\xE3o encontrado (HTTP 404)",
437
+ resolution: "Verifique se a URL do registry est\xE1 correta e se o token tem acesso ao projeto no GitLab."
438
+ })
439
+ );
440
+ } else if (verification.status === "network-error") {
441
+ results.push(
442
+ check({
443
+ id: "registry.pulso-token",
444
+ category: "registry",
445
+ status: "warning",
446
+ title: "Valida\xE7\xE3o do token do registry Pulso",
447
+ message: `N\xE3o foi poss\xEDvel validar o token junto ao GitLab: ${verification.detail ?? "falha de conex\xE3o"}.`,
448
+ actual: "falha de conex\xE3o",
449
+ resolution: "Verifique sua conex\xE3o com a internet ou acesso ao GitLab e tente novamente."
450
+ })
451
+ );
452
+ } else {
453
+ results.push(
454
+ check({
455
+ id: "registry.pulso-token",
456
+ category: "registry",
457
+ status: "error",
458
+ title: "Valida\xE7\xE3o do token do registry Pulso",
459
+ message: `O token do registry Pulso n\xE3o foi aceito pelo GitLab${verification.detail ? ` (${verification.detail})` : ""}.`,
460
+ actual: `n\xE3o autorizado (HTTP ${verification.httpStatus ?? 401})`,
461
+ resolution: pulsoVariableName ? `Gere um novo token no GitLab com escopo de leitura do registry e atualize a vari\xE1vel ${pulsoVariableName}.` : `Gere um novo token no GitLab com escopo de leitura do registry e atualize a configura\xE7\xE3o em ${source}.`
462
+ })
463
+ );
464
+ }
465
+ }
294
466
  if (platforms.includes("app")) {
295
467
  const svgRegistry = normalizeRegistry(entries.get("@rd-core-svg:registry"));
296
468
  results.push(
@@ -312,13 +484,52 @@ async function validateRegistryConfiguration(cwd, platforms, environment) {
312
484
  resolution: source === ".yarnrc.yml" ? `Defina npmScopes.rd-core-svg.npmRegistryServer=${RD_CORE_SVG_REGISTRY} no .yarnrc.yml.` : source === ".yarnrc" ? `Defina "@rd-core-svg:registry" "${RD_CORE_SVG_REGISTRY}" no .yarnrc.` : `Defina @rd-core-svg:registry=${RD_CORE_SVG_REGISTRY} no .npmrc.`
313
485
  })
314
486
  );
487
+ const svgAuthValue = entries.get(RD_CORE_SVG_AUTH_KEY);
488
+ if (svgAuthValue && shouldVerifyTokens) {
489
+ const rawSvgAuth = stripQuotes(svgAuthValue) ?? "";
490
+ const svgVarMatch = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(rawSvgAuth) ?? /^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(rawSvgAuth);
491
+ const svgVarName = svgVarMatch?.[1];
492
+ const svgToken = svgVarName ? stripQuotes(environment[svgVarName]) : rawSvgAuth;
493
+ if (svgToken) {
494
+ const verifier = options.tokenVerifier ?? verifyRegistryToken;
495
+ const svgVerification = await verifier({
496
+ registry: svgRegistry ?? RD_CORE_SVG_REGISTRY,
497
+ token: svgToken,
498
+ fetchFn: options.fetchFn
499
+ });
500
+ if (svgVerification.status === "valid") {
501
+ results.push(
502
+ check({
503
+ id: "registry.rd-core-svg-token",
504
+ category: "registry",
505
+ status: "pass",
506
+ title: "Valida\xE7\xE3o do token do registry @rd-core-svg",
507
+ message: "Token de autentica\xE7\xE3o do registry @rd-core-svg validado com sucesso no GitLab.",
508
+ actual: "token v\xE1lido (HTTP 200)"
509
+ })
510
+ );
511
+ } else if (svgVerification.status === "expired" || svgVerification.status === "invalid") {
512
+ results.push(
513
+ check({
514
+ id: "registry.rd-core-svg-token",
515
+ category: "registry",
516
+ status: "error",
517
+ title: "Valida\xE7\xE3o do token do registry @rd-core-svg",
518
+ message: `O token do registry @rd-core-svg \xE9 inv\xE1lido ou est\xE1 expirado no GitLab${svgVerification.detail ? ` (${svgVerification.detail})` : ""}.`,
519
+ actual: `n\xE3o autorizado (HTTP ${svgVerification.httpStatus ?? 401})`,
520
+ resolution: svgVarName ? `Gere um novo token no GitLab e atualize a vari\xE1vel ${svgVarName}.` : `Gere um novo token no GitLab e atualize a configura\xE7\xE3o em ${source}.`
521
+ })
522
+ );
523
+ }
524
+ }
525
+ }
315
526
  }
316
527
  return { checks: results, configuration };
317
528
  }
318
529
  async function validateRegistryProtection(cwd, configuration) {
319
530
  if (configuration?.source === ".yarnrc.yml" || configuration?.source === ".yarnrc") {
320
531
  const source = configuration.source;
321
- const authentication = configuration.entries.get(PULSO_AUTH_KEY);
532
+ const authentication = configuration.entries.get(PULSO_AUTH_KEY) ?? configuration.entries.get(INSTANCE_AUTH_KEY);
322
533
  const usesEnvironment = /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(
323
534
  authentication ?? ""
324
535
  );
@@ -523,7 +734,12 @@ async function runDoctor(options = {}) {
523
734
  const registry = await validateRegistryConfiguration(
524
735
  cwd,
525
736
  platforms,
526
- environment
737
+ environment,
738
+ {
739
+ tokenVerifier: options.tokenVerifier,
740
+ skipTokenVerification: options.skipTokenVerification,
741
+ fetchFn: options.fetchFn
742
+ }
527
743
  );
528
744
  checks.push(...registry.checks);
529
745
  checks.push(await validateRegistryProtection(cwd, registry.configuration));
@@ -989,7 +1205,7 @@ async function applyDoctorFixes(report, options = {}) {
989
1205
  (item) => item.id === "registry.configuration" || isMissingAuthentication(item)
990
1206
  );
991
1207
  const authenticationError = report.checks.find(
992
- (item) => item.id === "registry.pulso-auth" && item.status === "error"
1208
+ (item) => (item.id === "registry.pulso-auth" || item.id === "registry.pulso-token") && item.status === "error"
993
1209
  );
994
1210
  const credentialAvailable = Boolean(
995
1211
  (options.environment ?? process.env).CI_JOB_TOKEN
@@ -1098,6 +1314,7 @@ function formatDoctorFixReport(report, options = {}) {
1098
1314
  }
1099
1315
 
1100
1316
  export {
1317
+ verifyRegistryToken,
1101
1318
  runDoctor,
1102
1319
  formatDoctorReport,
1103
1320
  hasAutomaticFixes,
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatDoctorReport,
6
6
  hasAutomaticFixes,
7
7
  runDoctor
8
- } from "./chunk-FQDXA3BM.js";
8
+ } from "./chunk-B4E4SOCQ.js";
9
9
 
10
10
  // src/cli-core.ts
11
11
  import { createInterface } from "readline/promises";
@@ -19,6 +19,7 @@ Op\xE7\xF5es:
19
19
  --fix aplica corre\xE7\xF5es autom\xE1ticas seguras
20
20
  --json imprime o relat\xF3rio estruturado em JSON
21
21
  --log-file <arquivo> caminho do log JSON Lines
22
+ --skip-token-check n\xE3o realiza a valida\xE7\xE3o de rede do token
22
23
  -h, --help exibe esta ajuda`;
23
24
  function terminalSupportsColor() {
24
25
  if (process.env.NO_COLOR !== void 0 || process.env.FORCE_COLOR === "0") {
@@ -47,12 +48,19 @@ function readValue(argv, index, flag) {
47
48
  return value;
48
49
  }
49
50
  function parseCliArguments(argv) {
50
- const parsed = { platform: "auto", json: false, fix: false, help: false };
51
+ const parsed = {
52
+ platform: "auto",
53
+ json: false,
54
+ fix: false,
55
+ help: false,
56
+ skipTokenCheck: false
57
+ };
51
58
  for (let index = 0; index < argv.length; index += 1) {
52
59
  const argument = argv[index];
53
60
  if (argument === "-h" || argument === "--help") parsed.help = true;
54
61
  else if (argument === "--json") parsed.json = true;
55
62
  else if (argument === "--fix") parsed.fix = true;
63
+ else if (argument === "--skip-token-check") parsed.skipTokenCheck = true;
56
64
  else if (argument === "--cwd") {
57
65
  parsed.cwd = readValue(argv, index, argument);
58
66
  index += 1;
@@ -92,7 +100,8 @@ async function runCli(argv = process.argv.slice(2), io = {
92
100
  cwd: options.cwd,
93
101
  platform: options.platform,
94
102
  logFile: options.logFile,
95
- writeLog: options.fix ? false : void 0
103
+ writeLog: options.fix ? false : void 0,
104
+ skipTokenVerification: options.skipTokenCheck
96
105
  });
97
106
  io.stdout(
98
107
  options.json ? JSON.stringify(report, null, 2) : formatDoctorReport(report, { color: io.color ?? false })
package/dist/index.d.ts CHANGED
@@ -72,6 +72,19 @@ interface DoctorFixOptions {
72
72
  commandRunner?: DoctorCommandRunner;
73
73
  environment?: Readonly<Record<string, string | undefined>>;
74
74
  }
75
+ type RegistryTokenVerificationStatus = 'valid' | 'expired' | 'invalid' | 'forbidden' | 'not-found' | 'network-error';
76
+ interface RegistryTokenVerificationResult {
77
+ ok: boolean;
78
+ status: RegistryTokenVerificationStatus;
79
+ httpStatus?: number;
80
+ detail?: string;
81
+ }
82
+ type RegistryTokenVerifier = (options: {
83
+ registry: string;
84
+ token: string;
85
+ fetchFn?: typeof fetch;
86
+ timeoutMs?: number;
87
+ }) => Promise<RegistryTokenVerificationResult>;
75
88
  interface RunDoctorOptions {
76
89
  cwd?: string;
77
90
  platform?: DoctorPlatformOption;
@@ -80,8 +93,18 @@ interface RunDoctorOptions {
80
93
  now?: Date;
81
94
  nodeVersion?: string;
82
95
  environment?: Readonly<Record<string, string | undefined>>;
96
+ tokenVerifier?: RegistryTokenVerifier;
97
+ fetchFn?: typeof fetch;
98
+ skipTokenVerification?: boolean;
83
99
  }
84
100
 
101
+ declare function verifyRegistryToken(options: {
102
+ registry: string;
103
+ token: string;
104
+ packageName?: string;
105
+ fetchFn?: typeof fetch;
106
+ timeoutMs?: number;
107
+ }): Promise<RegistryTokenVerificationResult>;
85
108
  declare function runDoctor(options?: RunDoctorOptions): Promise<DoctorReport>;
86
109
  declare function formatDoctorReport(report: DoctorReport, options?: DoctorFormatOptions): string;
87
110
 
@@ -89,4 +112,4 @@ declare function hasAutomaticFixes(report: DoctorReport): boolean;
89
112
  declare function applyDoctorFixes(report: DoctorReport, options?: DoctorFixOptions): Promise<DoctorFixReport>;
90
113
  declare function formatDoctorFixReport(report: DoctorFixReport, options?: DoctorFormatOptions): string;
91
114
 
92
- export { type DoctorCheckCategory, type DoctorCheckResult, type DoctorCheckStatus, type DoctorCommandRunner, type DoctorDependencyVersion, type DoctorFixAction, type DoctorFixOptions, type DoctorFixReport, type DoctorFixStatus, type DoctorFixSummary, type DoctorFormatOptions, type DoctorLogResult, type DoctorPlatform, type DoctorPlatformOption, type DoctorProject, type DoctorReport, type DoctorSummary, type RunDoctorOptions, applyDoctorFixes, formatDoctorFixReport, formatDoctorReport, hasAutomaticFixes, runDoctor };
115
+ export { type DoctorCheckCategory, type DoctorCheckResult, type DoctorCheckStatus, type DoctorCommandRunner, type DoctorDependencyVersion, type DoctorFixAction, type DoctorFixOptions, type DoctorFixReport, type DoctorFixStatus, type DoctorFixSummary, type DoctorFormatOptions, type DoctorLogResult, type DoctorPlatform, type DoctorPlatformOption, type DoctorProject, type DoctorReport, type DoctorSummary, type RegistryTokenVerificationResult, type RegistryTokenVerificationStatus, type RegistryTokenVerifier, type RunDoctorOptions, applyDoctorFixes, formatDoctorFixReport, formatDoctorReport, hasAutomaticFixes, runDoctor, verifyRegistryToken };
package/dist/index.js CHANGED
@@ -3,12 +3,14 @@ import {
3
3
  formatDoctorFixReport,
4
4
  formatDoctorReport,
5
5
  hasAutomaticFixes,
6
- runDoctor
7
- } from "./chunk-FQDXA3BM.js";
6
+ runDoctor,
7
+ verifyRegistryToken
8
+ } from "./chunk-B4E4SOCQ.js";
8
9
  export {
9
10
  applyDoctorFixes,
10
11
  formatDoctorFixReport,
11
12
  formatDoctorReport,
12
13
  hasAutomaticFixes,
13
- runDoctor
14
+ runDoctor,
15
+ verifyRegistryToken
14
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulso-doctor",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Diagnostico publico do ambiente de consumo das bibliotecas Pulso Web e App.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,11 +33,12 @@
33
33
  "scripts": {
34
34
  "build": "tsup && chmod 755 dist/cli.js",
35
35
  "lint": "cd ../.. && biome check packages/doctor",
36
- "test": "cd ../.. && vitest run packages/doctor/src",
36
+ "test": "cd ../.. && vitest run packages/doctor/src packages/doctor/scripts",
37
37
  "test:package": "pnpm build && node scripts/verify-package.mjs",
38
- "publish:stage": "npm run stage publish",
39
- "publish:list": "npm run stage list pulso-doctor",
40
- "publish:approve": "npm run stage approve"
38
+ "publish:stage": "npm stage publish",
39
+ "publish:list": "npm stage list pulso-doctor",
40
+ "publish:approve": "npm stage approve",
41
+ "publish:direct": "npm publish --access public"
41
42
  },
42
43
  "dependencies": {
43
44
  "semver": "7.7.4",