@nuxtseo/cli 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -172,6 +172,7 @@ nuxtseo page scan <url>
172
172
  nuxtseo performance
173
173
  nuxtseo search status
174
174
  nuxtseo search analytics <pages|keywords>
175
+ nuxtseo search indexing <summary|urls>
175
176
  nuxtseo research overview
176
177
  nuxtseo research keywords <topic>
177
178
  nuxtseo research serp <keyword>
@@ -189,6 +190,7 @@ performance, research, content, and account setup. The menu prints the direct
189
190
  command it runs.
190
191
 
191
192
  `search status` reads stored connection state. It never waits for Google.
193
+ `search indexing summary` reads retained URL Inspection coverage.
192
194
  `research keywords`, `research serp`, and `research rankings` can use the Team
193
195
  research allowance. Keyword responses report cache use in `evidence`. SERP and
194
196
  ranking responses report `cached: true`. Cached responses use no unit.
@@ -204,6 +206,7 @@ responses.
204
206
  | `backlinks recoverable` | `--limit 1..200`, `--offset >=0` | `--limit 100 --offset 0` |
205
207
  | `mentions list` | `--limit 1..200` | `--limit 100` |
206
208
  | `search analytics` | `--limit 1..100`, `--page >=1` | `--limit 25 --page 1` |
209
+ | `search indexing` | `--limit 1..500`, `--offset >=0` | `--limit 50 --offset 0` |
207
210
  | `content briefs list` | `--limit 1..100`, `--offset >=0` | `--limit 25 --offset 0` |
208
211
 
209
212
  Keep the server order for actions. For another page, pass the next offset or
package/dist/cli.js CHANGED
@@ -252,6 +252,7 @@ async function interactiveBare(runtime, globals) {
252
252
  options: [
253
253
  { value: 'performance', label: 'Performance overview' },
254
254
  { value: 'connection', label: 'Search Console connection' },
255
+ { value: 'indexing', label: 'Search indexing coverage' },
255
256
  { value: 'pages', label: 'Search Console Pages' },
256
257
  { value: 'keywords', label: 'Search Console queries' },
257
258
  ],
@@ -265,7 +266,9 @@ async function interactiveBare(runtime, globals) {
265
266
  ? ['performance']
266
267
  : task === 'connection'
267
268
  ? ['search', 'status']
268
- : ['search', 'analytics', task];
269
+ : task === 'indexing'
270
+ ? ['search', 'indexing', 'summary']
271
+ : ['search', 'analytics', task];
269
272
  return runInteractiveCommand(command, runtime, globals);
270
273
  }
271
274
  if (group === 'research') {
package/dist/commands.js CHANGED
@@ -5,7 +5,7 @@ import { openBrowser } from './browser.js';
5
5
  import { EXIT_CODE, fail, fromSdkFailure, ok } from './failures.js';
6
6
  import { awaitPairingApproval, createCliPairingClient, startPairing } from './pairing.js';
7
7
  import { parseAbsolutePageUrl, parseChoice, parseInteger } from './parse.js';
8
- import { renderAction, renderActionResolution, renderActions, renderContentBrief, renderContentBriefCreated, renderContentBriefs, renderContentDecay, renderDuplicateClusters, renderKeywordResearch, renderLinkOpportunities, renderMentions, renderPageInspection, renderPageScan, renderPerformance, renderRankingsResearch, renderRecoverableBacklinks, renderResearchOverview, renderSearchAnalytics, renderSearchStatus, renderSerpResearch, renderSites, renderUsage, } from './render.js';
8
+ import { renderAction, renderActionResolution, renderActions, renderContentBrief, renderContentBriefCreated, renderContentBriefs, renderContentDecay, renderDuplicateClusters, renderIndexingDiagnostics, renderKeywordResearch, renderLinkOpportunities, renderMentions, renderPageInspection, renderPageScan, renderPerformance, renderRankingsResearch, renderRecoverableBacklinks, renderResearchOverview, renderSearchAnalytics, renderSearchStatus, renderSerpResearch, renderSites, renderUsage, } from './render.js';
9
9
  import { writeCliResponse, writeDiagnostic, writeOutput, writeProtocolResponse } from './runtime.js';
10
10
  import { resolveSite } from './site.js';
11
11
  import { clearCredential, getCredentialStatus, readConfig, resolveApiUrl, saveCredential, updateConfig, } from './state/index.js';
@@ -447,6 +447,36 @@ async function searchAnalytics(runtime, globals, args) {
447
447
  }, { signal: runtime.requestSignal }));
448
448
  return present(runtime, globals, response, value => renderSearchAnalytics(value.data));
449
449
  }
450
+ async function searchIndexing(runtime, globals, args) {
451
+ const view = parseChoice(args.view, { name: 'view', choices: ['summary', 'urls'] });
452
+ if (view._tag === 'Err')
453
+ return view;
454
+ const status = args.status === undefined
455
+ ? ok(undefined)
456
+ : parseChoice(args.status, { name: '--status', choices: ['indexed', 'not_indexed', 'pending'] });
457
+ if (status._tag === 'Err')
458
+ return status;
459
+ const limit = parseInteger(args.limit, { name: '--limit', minimum: 1, maximum: 500, defaultValue: 50 });
460
+ if (limit._tag === 'Err')
461
+ return limit;
462
+ const offset = parseInteger(args.offset, { name: '--offset', minimum: 0, defaultValue: 0 });
463
+ if (offset._tag === 'Err')
464
+ return offset;
465
+ const resolved = await apiAndSite(runtime, globals);
466
+ if (resolved._tag === 'Err')
467
+ return resolved;
468
+ const response = await withSpinner(runtime, 'Loading Search Console indexing', () => resolved.value.api.client.search.readIndexingDiagnostics({
469
+ params: { siteId: resolved.value.siteId },
470
+ query: {
471
+ view: view.value,
472
+ issue: args.issue,
473
+ status: status.value,
474
+ limit: limit.value,
475
+ offset: offset.value,
476
+ },
477
+ }, { signal: runtime.requestSignal }));
478
+ return present(runtime, globals, response, value => renderIndexingDiagnostics(value.data));
479
+ }
450
480
  async function researchOverview(runtime, globals) {
451
481
  const resolved = await apiAndSite(runtime, globals);
452
482
  if (resolved._tag === 'Err')
@@ -928,6 +958,23 @@ export function createRootCommand(runtime, globals, execution) {
928
958
  sortDir: args['sort-dir'],
929
959
  }), args._, 1)(),
930
960
  }),
961
+ indexing: defineCommand({
962
+ meta: { name: 'indexing', description: 'Read retained Search Console indexing coverage' },
963
+ args: {
964
+ view: positional('summary-or-urls', 'summary or urls'),
965
+ issue: { type: 'string', description: 'Filter URLs by issue type' },
966
+ status: { type: 'enum', options: ['indexed', 'not_indexed', 'pending'], description: 'Filter URLs by indexing status' },
967
+ limit: { type: 'string', description: 'Maximum URLs, 1 to 500' },
968
+ offset: { type: 'string', description: 'Pagination offset' },
969
+ },
970
+ run: ({ args }) => capture(execution, () => searchIndexing(runtime, globals, {
971
+ view: args.view,
972
+ issue: args.issue,
973
+ status: args.status,
974
+ limit: args.limit,
975
+ offset: args.offset,
976
+ }), args._, 1)(),
977
+ }),
931
978
  },
932
979
  });
933
980
  return defineCommand({
package/dist/render.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { ActionList, ActionResolve, ActionShow } from '@nuxtseo/protocol/v1
3
3
  import type { AuditContentDecay, AuditDuplicateClusters, AuditLinkOpportunities } from '@nuxtseo/protocol/v1/audit';
4
4
  import type { Mentions, RecoverableBacklinks } from '@nuxtseo/protocol/v1/backlinks';
5
5
  import type { ContentBrief, ContentBriefCreateData, ContentBriefListData } from '@nuxtseo/protocol/v1/content';
6
- import type { SearchAnalyticsData, SearchStatusData } from '@nuxtseo/protocol/v1/gsc';
6
+ import type { IndexingDiagnosticsData, SearchAnalyticsData, SearchStatusData } from '@nuxtseo/protocol/v1/gsc';
7
7
  import type { PageInspect, PageScan } from '@nuxtseo/protocol/v1/pages';
8
8
  import type { SitePerformanceOverview } from '@nuxtseo/protocol/v1/performance';
9
9
  import type { KeywordResearchData, RankingsResearchData, SerpResearchData, StoredResearchOverview } from '@nuxtseo/protocol/v1/research';
@@ -17,6 +17,7 @@ export declare function renderPageScan(data: PageScan): string;
17
17
  export declare function renderPerformance(data: SitePerformanceOverview): string;
18
18
  export declare function renderSearchStatus(data: SearchStatusData): string;
19
19
  export declare function renderSearchAnalytics(data: SearchAnalyticsData): string;
20
+ export declare function renderIndexingDiagnostics(data: IndexingDiagnosticsData): string;
20
21
  export declare function renderResearchOverview(data: StoredResearchOverview): string;
21
22
  export declare function renderKeywordResearch(data: KeywordResearchData): string;
22
23
  export declare function renderSerpResearch(data: SerpResearchData): string;
package/dist/render.js CHANGED
@@ -151,6 +151,34 @@ export function renderSearchAnalytics(data) {
151
151
  ['Rows', 'rows' in data ? data.rows.length : data.type === 'timeseries' ? data.daily.length : 0],
152
152
  ]);
153
153
  }
154
+ export function renderIndexingDiagnostics(data) {
155
+ if (data.view === 'summary') {
156
+ const indexedPercent = data.totalUrls > 0
157
+ ? `${(data.indexed / data.totalUrls * 100).toFixed(1)}%`
158
+ : '0.0%';
159
+ const issues = data.issues
160
+ .filter(issue => issue.count > 0)
161
+ .sort((left, right) => right.count - left.count)
162
+ .map(issue => `${issue.count} ${issue.label} ${issue.severity}`);
163
+ return [
164
+ fields([
165
+ ['Known URLs', data.totalUrls],
166
+ ['Indexed', data.indexed],
167
+ ['Not indexed', Math.max(0, data.totalUrls - data.indexed)],
168
+ ['Indexed rate', indexedPercent],
169
+ ['As of', data.asOf],
170
+ ]),
171
+ ...(issues.length > 0 ? ['Issues', ...issues] : []),
172
+ ].join('\n');
173
+ }
174
+ if (data.urls.length === 0)
175
+ return 'No Search Console indexing URLs match these filters.';
176
+ return [
177
+ `Search Console indexing URLs (${data.urls.length} of ${data.total})`,
178
+ ...data.urls.map(row => `${row.verdict ?? 'unknown'} ${row.coverageState ?? 'unknown'}\n ${row.url}`),
179
+ data.hasMore ? `More URLs available after offset ${data.offset + data.limit}.` : '',
180
+ ].filter(Boolean).join('\n');
181
+ }
154
182
  export function renderResearchOverview(data) {
155
183
  const subject = data.subject
156
184
  ? fields([
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtseo/cli",
3
3
  "type": "module",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "description": "Command line interface for the NuxtSEO public API.",
6
6
  "license": "MIT",
7
7
  "homepage": "https://nuxtseo.com/pro",
@@ -34,7 +34,7 @@
34
34
  "@clack/prompts": "^1.7.0",
35
35
  "citty": "^0.2.2",
36
36
  "pathe": "^2.0.3",
37
- "@nuxtseo/sdk": "^0.1.2"
37
+ "@nuxtseo/sdk": "^0.1.3"
38
38
  },
39
39
  "optionalDependencies": {
40
40
  "@napi-rs/keyring": "^1.3.0"
@@ -44,7 +44,7 @@
44
44
  "@types/node": "^26.2.0",
45
45
  "publint": "^0.3.24",
46
46
  "typescript": "npm:typescript-native-bridge@6.0.3-bridge.10.tsgo.7.0.2",
47
- "@nuxtseo/protocol": "0.1.2"
47
+ "@nuxtseo/protocol": "0.1.3"
48
48
  },
49
49
  "publishConfig": {
50
50
  "access": "public"
@@ -26,6 +26,7 @@ report `cached`.
26
26
 
27
27
  `search status` reads stored connection state. It never waits for Google.
28
28
  `search analytics` reads Search Console rows through the public API.
29
+ `search indexing` reads retained URL Inspection coverage through the public API.
29
30
 
30
31
  ## Get the binary
31
32
 
@@ -115,6 +116,7 @@ handling failures, read [CLI protocol](references/protocol.md).
115
116
  | `performance` | Site performance overview | Medians for perf, a11y, SEO, LCP, TBT, CLS |
116
117
  | `search status` | Stored Search Console connection | Provider free |
117
118
  | `search analytics <pages\|keywords>` | Search Console Page or query rows | `--period`, `--limit`, `--page`, `--search` |
119
+ | `search indexing <summary\|urls>` | Retained URL Inspection coverage | `summary` returns indexed counts; `urls` supports `--issue`, `--status`, `--limit`, `--offset` |
118
120
  | `research overview` | Stored Site and competitor research | Metrics, history, gaps, and quick wins |
119
121
  | `research keywords <topic>` | Live keyword ideas | Volume, difficulty, intent, and cost data |
120
122
  | `research serp <keyword>` | Live SERP snapshot | Results, features, and fetch time |