@vijayhardaha/next-indexnow 1.0.1 → 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/README.md CHANGED
@@ -29,6 +29,10 @@ npx next-indexnow --key my-api-key
29
29
  # Use a custom sitemap path
30
30
  npx next-indexnow --sitemap ./public/sitemap.xml
31
31
 
32
+ # Submit only a slice of the sitemap (avoid bulk submission)
33
+ npx next-indexnow -o 0 -l 50
34
+ npx next-indexnow -o 50 -l 50
35
+
32
36
  # Preview URLs without submitting
33
37
  npx next-indexnow --dry-run
34
38
  ```
@@ -76,7 +80,9 @@ All 10 urls submitted to IndexNow successfully! 🥳
76
80
  | `--site-url <url>` | Site URL (e.g. `https://example.com`). Overrides config value |
77
81
  | `--key <key>` | IndexNow API key. Falls back to `INDEXNOW_KEY` env variable or a built-in default |
78
82
  | `--sitemap <path>` | Path to the sitemap XML file |
79
- | `--chunk-size <number>` | URLs per submission batch (default: 100) |
83
+ | `--chunk-size <number>` | URLs per submission batch (positive integer, default: 100) |
84
+ | `-o, --offset <number>` | Skip this many URLs from the start of the sitemap |
85
+ | `-l, --limit <number>` | Submit at most this many URLs (after offset) |
80
86
  | `-d, --dry-run` | Preview URLs without submitting to the IndexNow API |
81
87
  | `-h, --help` | Show help |
82
88
  | `--version` | Show version |
@@ -89,8 +95,9 @@ All 10 urls submitted to IndexNow successfully! 🥳
89
95
  4. **Resolves** the API key from CLI option, `INDEXNOW_KEY` env variable, or a built-in default key
90
96
  5. **Creates** the IndexNow verification file at `public/<key>.txt`
91
97
  6. **Parses** all `<loc>` URLs from the sitemap XML
92
- 7. **Submits** URLs in batches (default 100) to the IndexNow API
93
- 8. **Reports** results with per-chunk success/failure details
98
+ 7. **Applies** the `-o, --offset` / `-l, --limit` range to the parsed URLs
99
+ 8. **Submits** URLs in batches (default 100) to the IndexNow API
100
+ 9. **Reports** results with per-chunk success/failure details
94
101
 
95
102
  ### Environment Variables
96
103
 
@@ -114,19 +121,21 @@ console.log(`Found ${result.urlsFound} URLs`);
114
121
 
115
122
  ### `NextIndexnowOptions`
116
123
 
117
- | Option | Type | Default | Description |
118
- | ----------- | --------- | ---------------- | --------------------------- |
119
- | `siteUrl` | `string` | — | Site URL (overrides config) |
120
- | `key` | `string` | built-in default | IndexNow API key |
121
- | `sitemap` | `string` | — | Custom sitemap path |
122
- | `chunkSize` | `number` | `100` | URLs per submission batch |
123
- | `dryRun` | `boolean` | `false` | Preview without submitting |
124
+ | Option | Type | Default | Description |
125
+ | ----------- | --------- | ---------------- | --------------------------------- |
126
+ | `siteUrl` | `string` | — | Site URL (overrides config) |
127
+ | `key` | `string` | built-in default | IndexNow API key |
128
+ | `sitemap` | `string` | — | Custom sitemap path |
129
+ | `chunkSize` | `number` | `100` | URLs per submission batch |
130
+ | `offset` | `number` | | URLs to skip from the start |
131
+ | `limit` | `number` | — | Max URLs to submit (after offset) |
132
+ | `dryRun` | `boolean` | `false` | Preview without submitting |
124
133
 
125
134
  ### `NextIndexnowResult`
126
135
 
127
136
  ```typescript
128
137
  interface NextIndexnowResult {
129
- urlsFound: number; // Total URLs extracted from sitemap
138
+ urlsFound: number; // URLs selected after offset/limit
130
139
  urlsSubmitted: number; // Successfully submitted URLs
131
140
  urlsFailed: number; // URLs that failed to submit
132
141
  chunks: SubmissionResult[];
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from
3
3
  import { resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import chalk from "chalk";
6
- import { Command } from "commander";
6
+ import { Command, InvalidArgumentError } from "commander";
7
7
  import logSymbols from "log-symbols";
8
8
  import ora from "ora";
9
9
  import xml2js from "xml2js";
@@ -223,6 +223,27 @@ function resolveSitemapPath(outDir, sitemapFile) {
223
223
  return resolve(process.cwd(), dir, file);
224
224
  }
225
225
  /**
226
+ * Apply offset and limit options to a list of URLs.
227
+ *
228
+ * Skips the first `offset` URLs and keeps at most `limit` URLs from the
229
+ * remaining list. Omitted options leave the corresponding bound unlimited.
230
+ *
231
+ * @param {string[]} urls - The full list of URLs extracted from the sitemap.
232
+ * @param {number} [offset] - Number of URLs to skip from the start.
233
+ * @param {number} [limit] - Maximum number of URLs to keep (after offset).
234
+ *
235
+ * @returns {string[]} The sliced list of URLs to submit.
236
+ *
237
+ * @throws {Error} If offset or limit is a negative or non-integer value.
238
+ */
239
+ function applyOffsetLimit(urls, offset, limit) {
240
+ if (offset !== void 0 && (!Number.isInteger(offset) || offset < 0)) throw new Error("offset must be a non-negative integer");
241
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 0)) throw new Error("limit must be a non-negative integer");
242
+ const start = offset ?? 0;
243
+ const end = limit === void 0 ? void 0 : start + limit;
244
+ return urls.slice(start, end);
245
+ }
246
+ /**
226
247
  * Fetch XML content from a remote sitemap URL.
227
248
  *
228
249
  * @param {string} url - The remote sitemap URL to fetch.
@@ -479,19 +500,21 @@ async function submitAllUrls(urls, siteHost, key, keyLocation, chunkSize, onProg
479
500
  * Run the IndexNow submission process.
480
501
  *
481
502
  * Validates the environment (Next.js project, .next dir, sitemap config),
482
- * reads the sitemap, and submits all URLs to the IndexNow API in chunks.
503
+ * reads the sitemap, applies the offset/limit range, and submits the
504
+ * selected URLs to the IndexNow API in chunks.
483
505
  *
484
- * @param {NextIndexnowOptions} options - CLI options (siteUrl, key, sitemap, chunkSize, dryRun, onProgress).
506
+ * @param {NextIndexnowOptions} options - CLI options (siteUrl, key, sitemap, chunkSize, offset, limit, dryRun, onProgress).
485
507
  *
486
508
  * @returns {Promise<NextIndexnowResult>} Aggregate result with per-chunk details.
487
509
  */
488
510
  async function run(options = {}) {
489
511
  const startTime = Date.now();
490
512
  const chunkSize = options.chunkSize ?? 100;
513
+ if (!Number.isInteger(chunkSize) || chunkSize < 1) throw new Error("chunkSize must be a positive integer");
491
514
  const parsedConfig = validateEnvironment();
492
515
  const { siteUrl, siteHost } = resolveSiteConfig(options, parsedConfig);
493
516
  const { key, keyLocation } = resolveKeyValue(options, siteUrl);
494
- const urls = await loadSitemapUrls(options, parsedConfig);
517
+ const urls = applyOffsetLimit(await loadSitemapUrls(options, parsedConfig), options.offset, options.limit);
495
518
  if (options.dryRun) return {
496
519
  urlsFound: urls.length,
497
520
  urlsSubmitted: 0,
@@ -523,6 +546,7 @@ async function run(options = {}) {
523
546
  * next-indexnow --site-url https://example.com
524
547
  * next-indexnow --key my-api-key
525
548
  * next-indexnow --sitemap ./public/sitemap-0.xml
549
+ * next-indexnow -o 100 -l 50
526
550
  * next-indexnow --dry-run
527
551
  * next-indexnow --help
528
552
  */
@@ -535,6 +559,32 @@ ${chalk.white("██║██║ ╚████║██████╔╝█
535
559
  ${chalk.white("╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝")}`;
536
560
  var SEPARATOR = chalk.dim("=".repeat(69));
537
561
  /**
562
+ * Parse a non-negative integer CLI option value.
563
+ *
564
+ * @param {string} value - Raw string value from the command line.
565
+ *
566
+ * @returns {number} The parsed integer.
567
+ *
568
+ * @throws {InvalidArgumentError} If the value is not a non-negative integer.
569
+ */
570
+ function parseNonNegativeInt(value) {
571
+ if (!/^\d+$/.test(value)) throw new InvalidArgumentError("must be a non-negative integer");
572
+ return Number.parseInt(value, 10);
573
+ }
574
+ /**
575
+ * Parse a positive integer CLI option value.
576
+ *
577
+ * @param {string} value - Raw string value from the command line.
578
+ *
579
+ * @returns {number} The parsed integer.
580
+ *
581
+ * @throws {InvalidArgumentError} If the value is not a positive integer.
582
+ */
583
+ function parsePositiveInt(value) {
584
+ if (!/^[1-9]\d*$/.test(value)) throw new InvalidArgumentError("must be a positive integer");
585
+ return Number.parseInt(value, 10);
586
+ }
587
+ /**
538
588
  * Print a checkmark or error icon with a status label and optional detail.
539
589
  *
540
590
  * @param {boolean} valid - Whether the check passed.
@@ -581,12 +631,13 @@ var pkgPathDist = resolve(__filename, "..", "..", "package.json");
581
631
  var pkgPath = existsSync(pkgPathDist) ? pkgPathDist : pkgPathSrc;
582
632
  var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
583
633
  var program = new Command();
584
- program.name("next-indexnow").description("Submit Next.js sitemap URLs to the IndexNow API for faster search engine indexing").version(pkg.version).option("--site-url <url>", "The site URL (e.g. https://example.com). Overrides next-sitemap.config value.").option("--key <key>", "IndexNow API key. Falls back to INDEXNOW_KEY environment variable or a built-in default key.").option("--sitemap <path>", "Path to the sitemap XML file").option("--chunk-size <number>", "URLs per submission batch", (v) => Number.parseInt(v, 10), 100).option("-d, --dry-run", "Preview URLs without submitting to the IndexNow API").addHelpText("after", `
634
+ program.name("next-indexnow").description("Submit Next.js sitemap URLs to the IndexNow API for faster search engine indexing").version(pkg.version).option("--site-url <url>", "The site URL (e.g. https://example.com). Overrides next-sitemap.config value.").option("--key <key>", "IndexNow API key. Falls back to INDEXNOW_KEY environment variable or a built-in default key.").option("--sitemap <path>", "Path to the sitemap XML file").option("--chunk-size <number>", "URLs per submission batch (positive integer)", parsePositiveInt, 100).option("-o, --offset <number>", "Skip this many URLs from the start of the sitemap", parseNonNegativeInt).option("-l, --limit <number>", "Submit at most this many URLs (after offset)", parseNonNegativeInt).option("-d, --dry-run", "Preview URLs without submitting to the IndexNow API").addHelpText("after", `
585
635
  Examples:
586
636
  $ next-indexnow Submit URLs using settings from next-sitemap.config
587
637
  $ next-indexnow --site-url https://example.com Override the site URL
588
638
  $ next-indexnow --key my-api-key Provide IndexNow API key
589
639
  $ next-indexnow --sitemap ./public/sitemap.xml Use a custom sitemap path
640
+ $ next-indexnow -o 100 -l 50 Submit a slice of the sitemap
590
641
  $ next-indexnow --dry-run Preview URLs without submitting
591
642
  $ next-indexnow --help Show this help message
592
643
  `).parse(process.argv);
@@ -638,9 +689,7 @@ function displayResults(result) {
638
689
  case "yellow":
639
690
  coloredValue = chalk.yellow(value);
640
691
  break;
641
- default:
642
- coloredValue = value;
643
- break;
692
+ default: coloredValue = value;
644
693
  }
645
694
  console.log(`${logSymbols.success} ${chalk.bold(label)}: ${coloredValue}`);
646
695
  }
@@ -701,7 +750,8 @@ function runValidationChecks(opts) {
701
750
  console.log(chalk.red(`\n ${keyFileCheck.error}`));
702
751
  process.exit(1);
703
752
  }
704
- checkMark(true, "Key verification file", existsSync(resolve(process.cwd(), "public", `${key}.txt`)) ? `${key}.txt exists` : `${key}.txt created`);
753
+ const keyFilePath = resolve(process.cwd(), "public", `${key}.txt`);
754
+ checkMark(true, "Key verification file", existsSync(keyFilePath) ? `${key}.txt exists` : `${key}.txt created`);
705
755
  return {
706
756
  parsedConfig,
707
757
  siteUrl,
@@ -748,7 +798,14 @@ async function main() {
748
798
  siteUrl: opts.siteUrl,
749
799
  key: opts.key
750
800
  });
751
- await loadSitemapCheck({ sitemap: opts.sitemap }, parsedConfig);
801
+ const sitemapUrls = await loadSitemapCheck({ sitemap: opts.sitemap }, parsedConfig);
802
+ if (opts.offset !== void 0 || opts.limit !== void 0) {
803
+ const selected = applyOffsetLimit(sitemapUrls, opts.offset, opts.limit);
804
+ const rangeParts = [];
805
+ if (opts.offset !== void 0) rangeParts.push(`offset ${opts.offset}`);
806
+ if (opts.limit !== void 0) rangeParts.push(`limit ${opts.limit}`);
807
+ checkMark(true, "URL range applied", `${selected.length} of ${sitemapUrls.length} URLs (${rangeParts.join(", ")})`);
808
+ }
752
809
  console.log("");
753
810
  spinner = ora({
754
811
  color: "cyan",
@@ -759,6 +816,8 @@ async function main() {
759
816
  key: opts.key,
760
817
  sitemap: opts.sitemap,
761
818
  chunkSize: opts.chunkSize,
819
+ offset: opts.offset,
820
+ limit: opts.limit,
762
821
  dryRun: opts.dryRun ?? false,
763
822
  onProgress: ({ batch, totalBatches, urlCount }) => {
764
823
  if (spinner) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/constants.ts","../src/utils.ts","../src/index.ts","../src/bin/cli.ts"],"sourcesContent":["/**\n * Constants and configuration defaults for the IndexNow CLI tool.\n *\n * @module constants\n */\n\n/** IndexNow API endpoint for URL submission. */\nexport const INDEXNOW_API_URL = 'https://api.indexnow.org/indexnow';\n\n/** Number of URLs to submit per API request. */\nexport const CHUNK_SIZE = 100;\n\n/** Default sitemap index filename (generated by next-sitemap). */\nexport const DEFAULT_SITEMAP_FILE = 'sitemap.xml';\n\n/** Sitemap output directory (relative to project root or configured outDir). */\nexport const DEFAULT_SITEMAP_DIR = 'public';\n\n/** IndexNow key verification filename extension. */\nexport const KEY_FILE_EXTENSION = '.txt';\n\n/** Default IndexNow API key — used when neither --key option nor INDEXNOW_KEY env var is provided. */\nexport const DEFAULT_INDEXNOW_KEY = '91c80f732f4e4e5b80b4c02a7e8c9e9c';\n\n/** Conventional Next.js config filenames to detect a Next.js project. */\nexport const NEXT_CONFIG_FILES = ['next.config.ts', 'next.config.mjs', 'next.config.cjs', 'next.config.js'];\n\n/** Conventional next-sitemap config filenames. */\nexport const NEXT_SITEMAP_CONFIG_FILES = [\n 'next-sitemap.config.ts',\n 'next-sitemap.config.mjs',\n 'next-sitemap.config.cjs',\n 'next-sitemap.config.js',\n];\n\n/** Directories that indicate a Next.js build has been run. */\nexport const NEXT_BUILD_DIRS = ['.next'];\n","/**\n * Validation and helper functions for the IndexNow CLI.\n *\n * @module utils\n */\n\nimport { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport xml2js from 'xml2js';\n\nimport {\n INDEXNOW_API_URL,\n NEXT_CONFIG_FILES,\n NEXT_SITEMAP_CONFIG_FILES,\n NEXT_BUILD_DIRS,\n KEY_FILE_EXTENSION,\n DEFAULT_SITEMAP_DIR,\n DEFAULT_SITEMAP_FILE,\n} from './constants.ts';\nimport type { NextSitemapConfig, ValidationResult, SubmissionResult } from './types.ts';\n\n/**\n * Validate that a URL string is a valid site domain with http:// or https:// scheme.\n *\n * Accepts standard domains (example.com), subdomains, and localhost with ports.\n *\n * @param {string} url - The URL string to validate.\n *\n * @returns {ValidationResult} Valid result on success, or invalid with an error message.\n */\nexport function validateSiteUrl(url: string): ValidationResult {\n if (!url || typeof url !== 'string') {\n return { valid: false, error: 'Site URL is required.' };\n }\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n return { valid: false, error: 'Site URL must start with http:// or https://' };\n }\n\n try {\n const parsed = new URL(url);\n\n /* v8 ignore next 3 */\n if (!parsed.hostname) {\n return { valid: false, error: 'Site URL must have a valid hostname.' };\n }\n\n // Reject URLs with path, query, or fragment beyond a single trailing slash\n /* v8 ignore next 2 */\n if ((parsed.pathname !== '/' && parsed.pathname !== '') || parsed.search || parsed.hash) {\n return { valid: false, error: 'Site URL should be a domain root (e.g. https://example.com).' };\n }\n } catch {\n return { valid: false, error: `Invalid URL: \"${url}\". Please provide a valid URL.` };\n }\n\n return { valid: true };\n}\n\n/**\n * Check if the current working directory is a Next.js project by looking for\n * a next.config.* file.\n *\n * @returns {ValidationResult} Valid result if a Next.js config file is found.\n */\nexport function validateNextProject(): ValidationResult {\n for (const configFile of NEXT_CONFIG_FILES) {\n if (existsSync(resolve(process.cwd(), configFile))) {\n return { valid: true };\n }\n }\n\n return { valid: false, error: 'No next.config.* file found. This command must be run from a Next.js project root.' };\n}\n\n/**\n * Check if the `.next` build directory exists and contains files.\n *\n * @returns {ValidationResult} Valid result if `.next` exists and is not empty.\n */\nexport function validateDotNext(): ValidationResult {\n for (const dir of NEXT_BUILD_DIRS) {\n const dotNextPath = resolve(process.cwd(), dir);\n\n if (!existsSync(dotNextPath)) {\n return { valid: false, error: `\"${dir}\" directory not found. Run \"next build\" first.` };\n }\n\n try {\n const entries = readdirSync(dotNextPath);\n if (entries.length === 0) {\n return { valid: false, error: `\"${dir}\" directory is empty. Run \"next build\" first.` };\n }\n } catch {\n /* v8 ignore next */\n return { valid: false, error: `Cannot read \"${dir}\" directory.` };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Check if a next-sitemap config file exists and is not empty.\n *\n * Searches for next-sitemap.config.{ts,mjs,cjs,js} in the current directory.\n *\n * @returns {ValidationResult & { filePath?: string }} Valid result with the config file path if found.\n */\nexport function validateNextSitemapConfig(): ValidationResult & { filePath?: string } {\n for (const configFile of NEXT_SITEMAP_CONFIG_FILES) {\n const configPath = resolve(process.cwd(), configFile);\n\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8').trim();\n\n if (content.length === 0) {\n return { valid: false, error: `\"${configFile}\" exists but is empty.` };\n }\n\n return { valid: true, filePath: configPath };\n }\n }\n\n return { valid: false, error: 'No next-sitemap.config.* file found. Create one to configure your sitemap.' };\n}\n\n/**\n * Read the next-sitemap config file and extract the `siteUrl` and optional `outDir`.\n *\n * Parses the file to extract `siteUrl` from the config object, handling both\n * inline string literals (`siteUrl: 'https://...'`) and variable references\n * (`const siteDomain = 'https://...'; siteUrl: siteDomain`). Avoids the\n * complexity of dynamic module loading for mixed CJS/ESM configs.\n *\n * @param {string} configPath - Absolute path to the next-sitemap config file.\n *\n * @returns {NextSitemapConfig | null} Parsed config, or null if siteUrl could not be extracted.\n */\nexport function readSitemapConfig(configPath: string): NextSitemapConfig | null {\n try {\n const content = readFileSync(configPath, 'utf-8');\n\n // Build a map of const variable names to their string literal values\n const constMap = new Map<string, string>();\n for (const match of content.matchAll(/const\\s+(\\w+)\\s*=\\s*['\"]([^'\"]+)['\"]/g)) {\n constMap.set(match[1]!, match[2]!);\n }\n\n // Try inline string literal first, then variable reference lookup\n const siteUrlMatch = content.match(/siteUrl:\\s*['\"]([^'\"]+)['\"]/);\n const siteUrlVarMatch = content.match(/siteUrl:\\s*(\\w+)/);\n\n const siteUrl = siteUrlMatch?.[1] ?? (siteUrlVarMatch?.[1] ? constMap.get(siteUrlVarMatch[1]) : undefined);\n\n if (!siteUrl) {\n return null;\n }\n\n // Extract optional outDir value (inline string literal or variable)\n const outDirMatch = content.match(/outDir:\\s*['\"]([^'\"]+)['\"]/);\n const outDirVarMatch = content.match(/outDir:\\s*(\\w+)/);\n const outDir = outDirMatch?.[1] ?? (outDirVarMatch?.[1] ? constMap.get(outDirVarMatch[1]) : undefined);\n\n return { siteUrl, outDir };\n } catch {\n return null;\n }\n}\n\n/**\n * Ensure the IndexNow verification key file exists at public/<key>.txt with the\n * correct content. Creates the file and directory if they don't exist.\n *\n * @param {string} key - The IndexNow API key.\n *\n * @returns {ValidationResult} Valid result if the key file exists or was created successfully.\n */\nexport function ensureKeyFile(key: string): ValidationResult {\n if (!key || typeof key !== 'string') {\n return { valid: false, error: 'IndexNow API key is required.' };\n }\n\n const publicDir = resolve(process.cwd(), DEFAULT_SITEMAP_DIR);\n const keyFilePath = resolve(publicDir, `${key}${KEY_FILE_EXTENSION}`);\n\n try {\n if (!existsSync(publicDir)) {\n mkdirSync(publicDir, { recursive: true });\n }\n\n if (existsSync(keyFilePath)) {\n const existingContent = readFileSync(keyFilePath, 'utf-8').trim();\n\n if (existingContent !== key) {\n writeFileSync(keyFilePath, key, 'utf-8');\n }\n } else {\n writeFileSync(keyFilePath, key, 'utf-8');\n }\n\n return { valid: true };\n } catch (error) {\n /* v8 ignore next 3 */\n return {\n valid: false,\n error: `Failed to create key file: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Resolve the sitemap file path.\n *\n * Uses the configured outDir (from next-sitemap.config) or falls back to\n * the default `public/` directory.\n *\n * @param {string} [outDir] - Custom output directory from next-sitemap config.\n * @param {string} [sitemapFile] - Custom sitemap filename. Defaults to sitemap-0.xml.\n *\n * @returns {string} Absolute path to the sitemap file.\n */\nexport function resolveSitemapPath(outDir?: string, sitemapFile?: string): string {\n const dir = outDir ?? DEFAULT_SITEMAP_DIR;\n const file = sitemapFile ?? DEFAULT_SITEMAP_FILE;\n return resolve(process.cwd(), dir, file);\n}\n\n/**\n * Fetch XML content from a remote sitemap URL.\n *\n * @param {string} url - The remote sitemap URL to fetch.\n *\n * @returns {Promise<string>} The XML content of the sitemap.\n *\n * @throws {Error} If the fetch fails or the response is not OK.\n */\nasync function fetchSitemapXml(url: string): Promise<string> {\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch sitemap: ${url} (${response.status})`);\n }\n\n return response.text();\n}\n\n/**\n * Read and parse a sitemap XML file, extracting all `<loc>` URLs.\n *\n * Supports both regular sitemaps (`<urlset>`) and sitemap index files\n * (`<sitemapindex>`). For sitemap index files, each referenced sub-sitemap\n * is fetched and parsed to collect all URLs.\n *\n * @param {string} sitemapPath - Absolute path to the sitemap XML file.\n *\n * @returns {Promise<ValidationResult & { urls?: string[] }>} Valid result with URL list, or invalid with error.\n */\nexport async function readSitemap(sitemapPath: string): Promise<ValidationResult & { urls?: string[] }> {\n let content: string;\n\n try {\n content = await readFileSync(sitemapPath, 'utf-8');\n } catch {\n return { valid: false, error: `Sitemap file not found: ${sitemapPath}` };\n }\n\n let parsed: { urlset?: { url?: Array<{ loc?: string[] }> }; sitemapindex?: { sitemap?: Array<{ loc?: string[] }> } };\n\n try {\n parsed = await xml2js.parseStringPromise(content);\n } catch {\n return { valid: false, error: 'Failed to parse sitemap XML. Ensure the file is valid XML.' };\n }\n\n // Check if this is a sitemap index file\n if (parsed.sitemapindex) {\n const subSitemapUrls = (parsed.sitemapindex.sitemap ?? [])\n .map((entry) => entry.loc?.[0])\n .filter(Boolean) as string[];\n\n if (subSitemapUrls.length === 0) {\n return { valid: false, error: 'No sub-sitemap URLs found in the sitemap index.' };\n }\n\n const allUrls: string[] = [];\n\n for (const subSitemapUrl of subSitemapUrls) {\n try {\n const subContent = await fetchSitemapXml(subSitemapUrl);\n const subParsed: { urlset?: { url?: Array<{ loc?: string[] }> } } = await xml2js.parseStringPromise(subContent);\n\n const urls = subParsed.urlset?.url?.map((entry) => entry.loc?.[0]).filter(Boolean) as string[] | undefined;\n\n if (urls && urls.length > 0) {\n allUrls.push(...urls);\n }\n } catch {\n // Skip failed sub-sitemaps silently\n }\n }\n\n if (allUrls.length === 0) {\n return { valid: false, error: 'No URLs found in any of the sub-sitemaps.' };\n }\n\n return { valid: true, urls: allUrls };\n }\n\n // Regular sitemap\n const urls = parsed.urlset?.url?.map((entry) => entry.loc?.[0]).filter(Boolean) as string[] | undefined;\n\n if (!urls || urls.length === 0) {\n return { valid: false, error: 'No URLs found in the sitemap.' };\n }\n\n return { valid: true, urls };\n}\n\n/**\n * Submit a batch of URLs to the IndexNow API.\n *\n * @param {string} host - The site hostname (e.g. example.com).\n * @param {string} key - The IndexNow API key.\n * @param {string} keyLocation - Public URL of the key verification file.\n * @param {string[]} urlList - URLs to submit in this batch.\n *\n * @returns {Promise<SubmissionResult>} The submission result for this batch.\n */\nexport async function submitUrls(\n host: string,\n key: string,\n keyLocation: string,\n urlList: string[]\n): Promise<SubmissionResult> {\n try {\n const response = await fetch(INDEXNOW_API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ host, key, keyLocation, urlList }),\n });\n\n if (response.ok) {\n return { count: urlList.length, success: true };\n }\n\n const errorText = await response.text();\n return { count: urlList.length, success: false, error: errorText };\n } catch (error) {\n /* v8 ignore next 4 */\n return {\n count: urlList.length,\n success: false,\n error: `Network error: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","/**\n * Core orchestration for the IndexNow CLI tool.\n *\n * Coordinates validation, sitemap parsing, and URL submission to the\n * IndexNow API for faster search engine indexing.\n *\n * @module next-indexnow\n */\n\nimport { CHUNK_SIZE, DEFAULT_INDEXNOW_KEY } from './constants.ts';\nimport type {\n NextIndexnowOptions,\n NextIndexnowResult,\n NextSitemapConfig,\n SubmissionResult,\n IndexnowProgress,\n} from './types.ts';\nimport {\n validateSiteUrl,\n validateNextProject,\n validateDotNext,\n validateNextSitemapConfig,\n readSitemapConfig,\n ensureKeyFile,\n resolveSitemapPath,\n readSitemap,\n submitUrls,\n} from './utils.ts';\n\n/**\n * Validate basic project structure: Next.js project and .next build directory.\n *\n * @throws {Error} If the project is not a Next.js project or .next is missing/empty.\n */\nfunction validateBasicEnvironment(): void {\n const projectCheck = validateNextProject();\n if (!projectCheck.valid) {\n throw new Error(projectCheck.error);\n }\n\n const dotNextCheck = validateDotNext();\n if (!dotNextCheck.valid) {\n throw new Error(dotNextCheck.error);\n }\n}\n\n/**\n * Validate that a sitemap config file exists and parse its contents.\n *\n * @returns {NextSitemapConfig} The parsed sitemap config (siteUrl + optional outDir).\n *\n * @throws {Error} If no config file is found, it is empty, or siteUrl cannot be parsed.\n */\nfunction validateSitemapSetup(): NextSitemapConfig {\n const sitemapConfigCheck = validateNextSitemapConfig();\n if (!sitemapConfigCheck.valid) {\n throw new Error(sitemapConfigCheck.error!);\n }\n\n const parsedConfig = readSitemapConfig(sitemapConfigCheck.filePath!);\n if (!parsedConfig) {\n throw new Error(`Could not parse \"siteUrl\" from ${sitemapConfigCheck.filePath}.`);\n }\n\n return parsedConfig;\n}\n\n/**\n * Validate that the current directory is a Next.js project with a build\n * artifact and a sitemap config file.\n *\n * @returns {NextSitemapConfig} The parsed sitemap config (siteUrl + optional outDir).\n *\n * @throws {Error} If any validation check fails.\n */\nfunction validateEnvironment(): NextSitemapConfig {\n validateBasicEnvironment();\n return validateSitemapSetup();\n}\n\n/**\n * Resolve the effective siteUrl from options (takes precedence) or config file,\n * validate it, and extract the hostname.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config.\n *\n * @returns {{ siteUrl: string; siteHost: string }} The resolved site URL and host.\n *\n * @throws {Error} If the site URL is invalid.\n */\nfunction resolveSiteConfig(\n options: NextIndexnowOptions,\n parsedConfig: NextSitemapConfig\n): { siteUrl: string; siteHost: string } {\n const siteUrl = options.siteUrl ?? parsedConfig.siteUrl;\n\n const urlValidation = validateSiteUrl(siteUrl);\n if (!urlValidation.valid) {\n throw new Error(urlValidation.error);\n }\n\n return { siteUrl, siteHost: new URL(siteUrl).host };\n}\n\n/**\n * Resolve the IndexNow API key from options or the INDEXNOW_KEY env var,\n * and ensure the verification key file exists on disk.\n *\n * Falls back to a hard-coded default key when neither the --key option nor\n * the INDEXNOW_KEY environment variable is provided.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {string} siteUrl - The resolved site URL (for keyLocation).\n *\n * @returns {{ key: string; keyLocation: string }} The resolved key and its public URL.\n *\n * @throws {Error} If the key file cannot be created.\n */\nfunction resolveKeyValue(options: NextIndexnowOptions, siteUrl: string): { key: string; keyLocation: string } {\n const key = options.key ?? process.env.INDEXNOW_KEY ?? DEFAULT_INDEXNOW_KEY;\n\n const keyFileCheck = ensureKeyFile(key);\n if (!keyFileCheck.valid) {\n throw new Error(keyFileCheck.error);\n }\n\n return { key, keyLocation: `${siteUrl}/${key}.txt` };\n}\n\n/**\n * Resolve the sitemap path (from options or default) and read all URLs.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config (for outDir).\n *\n * @returns {Promise<string[]>} The list of URLs found in the sitemap.\n *\n * @throws {Error} If the sitemap cannot be read or contains no URLs.\n */\nasync function loadSitemapUrls(options: NextIndexnowOptions, parsedConfig: NextSitemapConfig): Promise<string[]> {\n const sitemapPath = options.sitemap ?? resolveSitemapPath(parsedConfig.outDir);\n\n const sitemapResult = await readSitemap(sitemapPath);\n if (!sitemapResult.valid) {\n throw new Error(sitemapResult.error!);\n }\n\n return sitemapResult.urls!;\n}\n\n/**\n * Submit all URLs to the IndexNow API in chunks and aggregate the results.\n *\n * @param {string[]} urls - All URLs to submit.\n * @param {string} siteHost - The site hostname.\n * @param {string} key - The IndexNow API key.\n * @param {string} keyLocation - Public URL of the key verification file.\n * @param {number} chunkSize - Maximum URLs per submission batch.\n * @param {IndexnowProgress} [onProgress] - Callback invoked before each batch submission.\n *\n * @returns {Promise<Pick<NextIndexnowResult, 'urlsSubmitted' | 'urlsFailed' | 'chunks'>>} Aggregate submission counts and per-chunk details.\n */\nasync function submitAllUrls(\n urls: string[],\n siteHost: string,\n key: string,\n keyLocation: string,\n chunkSize: number,\n onProgress?: IndexnowProgress\n): Promise<Pick<NextIndexnowResult, 'urlsSubmitted' | 'urlsFailed' | 'chunks'>> {\n const chunks: SubmissionResult[] = [];\n const totalChunks = Math.ceil(urls.length / chunkSize);\n\n for (let i = 0; i < urls.length; i += chunkSize) {\n const batchNum = Math.floor(i / chunkSize) + 1;\n const chunk = urls.slice(i, i + chunkSize);\n\n onProgress?.({ batch: batchNum, totalBatches: totalChunks, urlCount: chunk.length });\n\n const result = await submitUrls(siteHost, key, keyLocation, chunk);\n chunks.push(result);\n }\n\n const urlsSubmitted = chunks.filter((c) => c.success).reduce((sum, c) => sum + c.count, 0);\n const urlsFailed = chunks.filter((c) => !c.success).reduce((sum, c) => sum + c.count, 0);\n\n return { urlsSubmitted, urlsFailed, chunks };\n}\n\n/**\n * Run the IndexNow submission process.\n *\n * Validates the environment (Next.js project, .next dir, sitemap config),\n * reads the sitemap, and submits all URLs to the IndexNow API in chunks.\n *\n * @param {NextIndexnowOptions} options - CLI options (siteUrl, key, sitemap, chunkSize, dryRun, onProgress).\n *\n * @returns {Promise<NextIndexnowResult>} Aggregate result with per-chunk details.\n */\nexport async function run(options: NextIndexnowOptions = {}): Promise<NextIndexnowResult> {\n const startTime = Date.now();\n const chunkSize = options.chunkSize ?? CHUNK_SIZE;\n\n // 1. Validate environment & parse sitemap config\n const parsedConfig = validateEnvironment();\n\n // 2. Resolve siteUrl & key\n const { siteUrl, siteHost } = resolveSiteConfig(options, parsedConfig);\n const { key, keyLocation } = resolveKeyValue(options, siteUrl);\n\n // 3. Read sitemap\n const urls = await loadSitemapUrls(options, parsedConfig);\n\n // 4. Dry-run short-circuit\n if (options.dryRun) {\n return { urlsFound: urls.length, urlsSubmitted: 0, urlsFailed: 0, chunks: [], durationMs: Date.now() - startTime };\n }\n\n // 5. Submit URLs in chunks\n const { urlsSubmitted, urlsFailed, chunks } = await submitAllUrls(\n urls,\n siteHost,\n key,\n keyLocation,\n chunkSize,\n options.onProgress\n );\n\n return { urlsFound: urls.length, urlsSubmitted, urlsFailed, chunks, durationMs: Date.now() - startTime };\n}\n","#!/usr/bin/env node\n\n/**\n * next-indexnow — CLI entry point.\n *\n * Parses command-line arguments with commander, validates the environment,\n * reads the Next.js sitemap, and submits URLs to the IndexNow API.\n *\n * @module cli\n *\n * Usage:\n * next-indexnow\n * next-indexnow --site-url https://example.com\n * next-indexnow --key my-api-key\n * next-indexnow --sitemap ./public/sitemap-0.xml\n * next-indexnow --dry-run\n * next-indexnow --help\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport logSymbols from 'log-symbols';\nimport ora from 'ora';\n\nimport { DEFAULT_INDEXNOW_KEY } from '../constants.ts';\nimport { run } from '../index.ts';\nimport type { NextIndexnowResult, NextSitemapConfig } from '../types.ts';\nimport {\n validateNextProject,\n validateDotNext,\n validateNextSitemapConfig,\n readSitemapConfig,\n validateSiteUrl,\n ensureKeyFile,\n resolveSitemapPath,\n readSitemap,\n} from '../utils.ts';\n\n// ── ASCII Banner ───────────────────────────────────────────────────────\n\nconst BANNER = `\n${chalk.white('██╗███╗ ██╗██████╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗ ██╗ ██╗')}\n${chalk.white('██║████╗ ██║██╔══██╗██╔════╝╚██╗██╔╝████╗ ██║██╔═══██╗██║ ██║')}\n${chalk.white('██║██╔██╗ ██║██║ ██║█████╗ ╚███╔╝ ██╔██╗ ██║██║ ██║██║ █╗ ██║')}\n${chalk.white('██║██║╚██╗██║██║ ██║██╔══╝ ██╔██╗ ██║╚██╗██║██║ ██║██║███╗██║')}\n${chalk.white('██║██║ ╚████║██████╔╝███████╗██╔╝ ██╗██║ ╚████║╚██████╔╝╚███╔███╔╝')}\n${chalk.white('╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝')}`;\n\nconst SEPARATOR = chalk.dim('='.repeat(69));\n\n// ── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Print a checkmark or error icon with a status label and optional detail.\n *\n * @param {boolean} valid - Whether the check passed.\n * @param {string} label - The status label text.\n * @param {string} [detail] - Optional detail text shown after a colon.\n */\nfunction checkMark(valid: boolean, label: string, detail?: string): void {\n const icon = valid ? logSymbols.success : logSymbols.error;\n const msg = detail ? `${label}: ${detail}` : label;\n console.log(`${icon} ${msg}`);\n}\n\n/**\n * Print the summary footer message after a submission run.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n * @param {boolean} dryRun - Whether this was a dry-run preview.\n */\nfunction printFooter(result: NextIndexnowResult, dryRun: boolean): void {\n console.log('');\n if (dryRun) {\n console.log(chalk.green(`Dry-run complete. ${result.urlsFound} urls would be submitted. 🚀`));\n } else if (result.urlsFailed === 0) {\n console.log(chalk.green(`All ${result.urlsSubmitted} urls submitted to IndexNow successfully! 🥳`));\n } else {\n console.log(\n chalk.yellow(\n `${result.urlsSubmitted}/${result.urlsFound} urls submitted successfully (${result.urlsFailed} failed).`\n )\n );\n }\n}\n\n/**\n * Print detailed failure information for failed submission batches.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n */\nfunction printFailures(result: NextIndexnowResult): void {\n if (result.urlsFailed === 0) return;\n\n console.error(chalk.red.bold('Failed submissions:'));\n for (const chunk of result.chunks) {\n if (!chunk.success) {\n console.error(chalk.red(` ${logSymbols.error} ${chunk.error}`));\n }\n }\n}\n\n// ── Ctrl+C handling ────────────────────────────────────────────────────\n\nlet spinner: ReturnType<typeof ora> | null = null;\n\nprocess.on('SIGINT', () => {\n if (spinner?.isSpinning) {\n spinner.stop();\n }\n console.log('');\n console.log(chalk.yellow('Process aborted by user.'));\n process.exit(130);\n});\n\nconst __filename = fileURLToPath(import.meta.url);\nconst pkgPathSrc = resolve(__filename, '..', '..', '..', 'package.json');\nconst pkgPathDist = resolve(__filename, '..', '..', 'package.json');\nconst pkgPath = existsSync(pkgPathDist) ? pkgPathDist : pkgPathSrc;\n\n// Read version from package.json\nconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('next-indexnow')\n .description('Submit Next.js sitemap URLs to the IndexNow API for faster search engine indexing')\n .version(pkg.version)\n .option('--site-url <url>', 'The site URL (e.g. https://example.com). Overrides next-sitemap.config value.')\n .option('--key <key>', 'IndexNow API key. Falls back to INDEXNOW_KEY environment variable or a built-in default key.')\n .option('--sitemap <path>', 'Path to the sitemap XML file')\n .option('--chunk-size <number>', 'URLs per submission batch', (v) => Number.parseInt(v, 10), 100)\n .option('-d, --dry-run', 'Preview URLs without submitting to the IndexNow API')\n .addHelpText(\n 'after',\n `\nExamples:\n $ next-indexnow Submit URLs using settings from next-sitemap.config\n $ next-indexnow --site-url https://example.com Override the site URL\n $ next-indexnow --key my-api-key Provide IndexNow API key\n $ next-indexnow --sitemap ./public/sitemap.xml Use a custom sitemap path\n $ next-indexnow --dry-run Preview URLs without submitting\n $ next-indexnow --help Show this help message\n `\n )\n .parse(process.argv);\n\n/**\n * Log submission results as checkmark-style lines.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n */\nfunction displayResults(result: NextIndexnowResult): void {\n const duration = (result.durationMs / 1000).toFixed(2);\n\n const items = [\n ['URLs found', String(result.urlsFound), result.urlsFound > 0 ? ('blue' as const) : ('dim' as const)],\n ['URLs submitted', String(result.urlsSubmitted), result.urlsSubmitted > 0 ? ('green' as const) : ('dim' as const)],\n ['URLs failed', String(result.urlsFailed), result.urlsFailed > 0 ? ('red' as const) : ('dim' as const)],\n ['Duration', `${duration}s`, result.durationMs > 0 ? ('yellow' as const) : ('dim' as const)],\n ];\n\n console.log('');\n for (const [label, value, color] of items) {\n let coloredValue: string;\n switch (color) {\n case 'red':\n coloredValue = chalk.red(value);\n break;\n case 'dim':\n coloredValue = chalk.dim(value);\n break;\n case 'green':\n coloredValue = chalk.green(value);\n break;\n case 'blue':\n coloredValue = chalk.blue(value);\n break;\n case 'yellow':\n coloredValue = chalk.yellow(value);\n break;\n default:\n coloredValue = value;\n break;\n }\n console.log(`${logSymbols.success} ${chalk.bold(label)}: ${coloredValue}`);\n }\n}\n\n/**\n * Run validation checks and display checkmarks, returning parsed state.\n *\n * @param {object} opts - CLI options for site URL and API key.\n * @param {string} [opts.siteUrl] - Override site URL from --site-url option.\n * @param {string} [opts.key] - Override API key from --key option.\n *\n * @returns {{ parsedConfig: NextSitemapConfig; siteUrl: string; siteHost: string; key: string; keyLocation: string }} Resolved configuration values.\n */\nfunction runValidationChecks(opts: { siteUrl?: string; key?: string }): {\n parsedConfig: NextSitemapConfig;\n siteUrl: string;\n siteHost: string;\n key: string;\n keyLocation: string;\n} {\n // 1. Next.js project check\n const projectCheck = validateNextProject();\n if (!projectCheck.valid) {\n checkMark(false, 'Next.js project', projectCheck.error);\n console.log(chalk.red(`\\n ${projectCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Next.js config found');\n\n // 2. Build directory check\n const dotNextCheck = validateDotNext();\n if (!dotNextCheck.valid) {\n checkMark(false, 'Build directory', dotNextCheck.error);\n console.log(chalk.red(`\\n ${dotNextCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Build directory exists (.next)');\n\n // 3. Sitemap config check\n const sitemapConfigCheck = validateNextSitemapConfig();\n if (!sitemapConfigCheck.valid) {\n checkMark(false, 'Sitemap config', sitemapConfigCheck.error);\n console.log(chalk.red(`\\n ${sitemapConfigCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Sitemap config found', sitemapConfigCheck.filePath);\n\n // 4. Parse sitemap config\n const parsedConfig = readSitemapConfig(sitemapConfigCheck.filePath!);\n if (!parsedConfig) {\n checkMark(false, 'Site URL', 'Could not parse siteUrl from config');\n console.log(chalk.red(`\\n Could not parse \"siteUrl\" from ${sitemapConfigCheck.filePath}.`));\n process.exit(1);\n }\n\n // 5. Validate site URL\n const siteUrl = opts.siteUrl ?? parsedConfig.siteUrl;\n const urlValidation = validateSiteUrl(siteUrl);\n if (!urlValidation.valid) {\n checkMark(false, 'Site URL', urlValidation.error);\n console.log(chalk.red(`\\n ${urlValidation.error}`));\n process.exit(1);\n }\n const siteHost = new URL(siteUrl).host;\n checkMark(true, 'Site URL resolved', siteHost);\n\n // 6. Resolve API key\n const key = opts.key ?? process.env.INDEXNOW_KEY ?? DEFAULT_INDEXNOW_KEY;\n if (opts.key) {\n checkMark(true, 'API key provided', 'via --key option');\n } else if (process.env.INDEXNOW_KEY) {\n checkMark(true, 'API key resolved', 'via INDEXNOW_KEY env');\n } else {\n checkMark(true, 'API key resolved', 'using built-in default key');\n }\n\n // 7. Ensure key file\n const keyFileCheck = ensureKeyFile(key);\n if (!keyFileCheck.valid) {\n checkMark(false, 'Key file', keyFileCheck.error);\n console.log(chalk.red(`\\n ${keyFileCheck.error}`));\n process.exit(1);\n }\n const keyFilePath = resolve(process.cwd(), 'public', `${key}.txt`);\n const keyExists = existsSync(keyFilePath);\n checkMark(true, 'Key verification file', keyExists ? `${key}.txt exists` : `${key}.txt created`);\n\n const keyLocation = `${siteUrl}/${key}.txt`;\n\n return { parsedConfig, siteUrl, siteHost, key, keyLocation };\n}\n\n/**\n * Read sitemap and display checkmark with URL count.\n *\n * @param {object} opts - CLI options for sitemap path.\n * @param {string} [opts.sitemap] - Custom sitemap path from --sitemap option.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config for default outDir.\n *\n * @returns {Promise<string[]>} The list of URLs extracted from the sitemap.\n */\nasync function loadSitemapCheck(opts: { sitemap?: string }, parsedConfig: NextSitemapConfig): Promise<string[]> {\n const sitemapPath = opts.sitemap ?? resolveSitemapPath(parsedConfig.outDir);\n\n const sitemapResult = await readSitemap(sitemapPath);\n if (!sitemapResult.valid) {\n checkMark(false, 'Sitemap', sitemapResult.error);\n console.log(chalk.red(`\\n ${sitemapResult.error}`));\n process.exit(1);\n }\n\n const urlCount = sitemapResult.urls!.length;\n checkMark(true, 'Sitemap loaded', `${urlCount} URLs found`);\n\n return sitemapResult.urls!;\n}\n\n/**\n * Main CLI entry point.\n *\n * Displays the ASCII banner, runs validation checks with checkmarks,\n * submits URLs to the IndexNow API, and shows results with a footer.\n */\nexport async function main(): Promise<void> {\n const opts = program.optsWithGlobals();\n\n // 1. Show banner + separator + version\n console.log(BANNER);\n console.log('');\n console.log(SEPARATOR);\n console.log(chalk.yellow(`next-indexnow: v${pkg.version}`));\n console.log(SEPARATOR);\n console.log('');\n\n // 2. Run validation checks with checkmarks\n const { parsedConfig } = runValidationChecks({ siteUrl: opts.siteUrl, key: opts.key });\n await loadSitemapCheck({ sitemap: opts.sitemap }, parsedConfig);\n\n console.log('');\n\n // 3. Submit URLs with progress spinner\n spinner = ora({ color: 'cyan', discardStdin: false });\n\n const result = await run({\n siteUrl: opts.siteUrl,\n key: opts.key,\n sitemap: opts.sitemap,\n chunkSize: opts.chunkSize,\n dryRun: opts.dryRun ?? false,\n onProgress: ({ batch, totalBatches, urlCount }) => {\n if (spinner) {\n spinner.text = `Submitting batch ${batch}/${totalBatches} (${urlCount} URLs)`;\n if (!spinner.isSpinning) {\n spinner.start();\n }\n }\n },\n });\n\n if (spinner?.isSpinning) {\n spinner.stop();\n }\n console.log(SEPARATOR);\n console.log(`${logSymbols.success} Submission Completed 🎉`);\n console.log(SEPARATOR);\n\n // 4. Show results\n displayResults(result);\n\n // 5. Show footer\n printFooter(result, opts.dryRun);\n\n // 6. Show failure details at end\n printFailures(result);\n\n // 7. Exit with appropriate code\n if (result.urlsFailed > 0) {\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/constants.ts","../src/utils.ts","../src/index.ts","../src/bin/cli.ts"],"sourcesContent":["/**\n * Constants and configuration defaults for the IndexNow CLI tool.\n *\n * @module constants\n */\n\n/** IndexNow API endpoint for URL submission. */\nexport const INDEXNOW_API_URL = 'https://api.indexnow.org/indexnow';\n\n/** Number of URLs to submit per API request. */\nexport const CHUNK_SIZE = 100;\n\n/** Default sitemap index filename (generated by next-sitemap). */\nexport const DEFAULT_SITEMAP_FILE = 'sitemap.xml';\n\n/** Sitemap output directory (relative to project root or configured outDir). */\nexport const DEFAULT_SITEMAP_DIR = 'public';\n\n/** IndexNow key verification filename extension. */\nexport const KEY_FILE_EXTENSION = '.txt';\n\n/** Default IndexNow API key — used when neither --key option nor INDEXNOW_KEY env var is provided. */\nexport const DEFAULT_INDEXNOW_KEY = '91c80f732f4e4e5b80b4c02a7e8c9e9c';\n\n/** Conventional Next.js config filenames to detect a Next.js project. */\nexport const NEXT_CONFIG_FILES = ['next.config.ts', 'next.config.mjs', 'next.config.cjs', 'next.config.js'];\n\n/** Conventional next-sitemap config filenames. */\nexport const NEXT_SITEMAP_CONFIG_FILES = [\n 'next-sitemap.config.ts',\n 'next-sitemap.config.mjs',\n 'next-sitemap.config.cjs',\n 'next-sitemap.config.js',\n];\n\n/** Directories that indicate a Next.js build has been run. */\nexport const NEXT_BUILD_DIRS = ['.next'];\n","/**\n * Validation and helper functions for the IndexNow CLI.\n *\n * @module utils\n */\n\nimport { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport xml2js from 'xml2js';\n\nimport {\n INDEXNOW_API_URL,\n NEXT_CONFIG_FILES,\n NEXT_SITEMAP_CONFIG_FILES,\n NEXT_BUILD_DIRS,\n KEY_FILE_EXTENSION,\n DEFAULT_SITEMAP_DIR,\n DEFAULT_SITEMAP_FILE,\n} from './constants.ts';\nimport type { NextSitemapConfig, ValidationResult, SubmissionResult } from './types.ts';\n\n/**\n * Validate that a URL string is a valid site domain with http:// or https:// scheme.\n *\n * Accepts standard domains (example.com), subdomains, and localhost with ports.\n *\n * @param {string} url - The URL string to validate.\n *\n * @returns {ValidationResult} Valid result on success, or invalid with an error message.\n */\nexport function validateSiteUrl(url: string): ValidationResult {\n if (!url || typeof url !== 'string') {\n return { valid: false, error: 'Site URL is required.' };\n }\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n return { valid: false, error: 'Site URL must start with http:// or https://' };\n }\n\n try {\n const parsed = new URL(url);\n\n /* v8 ignore next 3 */\n if (!parsed.hostname) {\n return { valid: false, error: 'Site URL must have a valid hostname.' };\n }\n\n // Reject URLs with path, query, or fragment beyond a single trailing slash\n /* v8 ignore next 2 */\n if ((parsed.pathname !== '/' && parsed.pathname !== '') || parsed.search || parsed.hash) {\n return { valid: false, error: 'Site URL should be a domain root (e.g. https://example.com).' };\n }\n } catch {\n return { valid: false, error: `Invalid URL: \"${url}\". Please provide a valid URL.` };\n }\n\n return { valid: true };\n}\n\n/**\n * Check if the current working directory is a Next.js project by looking for\n * a next.config.* file.\n *\n * @returns {ValidationResult} Valid result if a Next.js config file is found.\n */\nexport function validateNextProject(): ValidationResult {\n for (const configFile of NEXT_CONFIG_FILES) {\n if (existsSync(resolve(process.cwd(), configFile))) {\n return { valid: true };\n }\n }\n\n return { valid: false, error: 'No next.config.* file found. This command must be run from a Next.js project root.' };\n}\n\n/**\n * Check if the `.next` build directory exists and contains files.\n *\n * @returns {ValidationResult} Valid result if `.next` exists and is not empty.\n */\nexport function validateDotNext(): ValidationResult {\n for (const dir of NEXT_BUILD_DIRS) {\n const dotNextPath = resolve(process.cwd(), dir);\n\n if (!existsSync(dotNextPath)) {\n return { valid: false, error: `\"${dir}\" directory not found. Run \"next build\" first.` };\n }\n\n try {\n const entries = readdirSync(dotNextPath);\n if (entries.length === 0) {\n return { valid: false, error: `\"${dir}\" directory is empty. Run \"next build\" first.` };\n }\n } catch {\n /* v8 ignore next */\n return { valid: false, error: `Cannot read \"${dir}\" directory.` };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Check if a next-sitemap config file exists and is not empty.\n *\n * Searches for next-sitemap.config.{ts,mjs,cjs,js} in the current directory.\n *\n * @returns {ValidationResult & { filePath?: string }} Valid result with the config file path if found.\n */\nexport function validateNextSitemapConfig(): ValidationResult & { filePath?: string } {\n for (const configFile of NEXT_SITEMAP_CONFIG_FILES) {\n const configPath = resolve(process.cwd(), configFile);\n\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8').trim();\n\n if (content.length === 0) {\n return { valid: false, error: `\"${configFile}\" exists but is empty.` };\n }\n\n return { valid: true, filePath: configPath };\n }\n }\n\n return { valid: false, error: 'No next-sitemap.config.* file found. Create one to configure your sitemap.' };\n}\n\n/**\n * Read the next-sitemap config file and extract the `siteUrl` and optional `outDir`.\n *\n * Parses the file to extract `siteUrl` from the config object, handling both\n * inline string literals (`siteUrl: 'https://...'`) and variable references\n * (`const siteDomain = 'https://...'; siteUrl: siteDomain`). Avoids the\n * complexity of dynamic module loading for mixed CJS/ESM configs.\n *\n * @param {string} configPath - Absolute path to the next-sitemap config file.\n *\n * @returns {NextSitemapConfig | null} Parsed config, or null if siteUrl could not be extracted.\n */\nexport function readSitemapConfig(configPath: string): NextSitemapConfig | null {\n try {\n const content = readFileSync(configPath, 'utf-8');\n\n // Build a map of const variable names to their string literal values\n const constMap = new Map<string, string>();\n for (const match of content.matchAll(/const\\s+(\\w+)\\s*=\\s*['\"]([^'\"]+)['\"]/g)) {\n constMap.set(match[1]!, match[2]!);\n }\n\n // Try inline string literal first, then variable reference lookup\n const siteUrlMatch = content.match(/siteUrl:\\s*['\"]([^'\"]+)['\"]/);\n const siteUrlVarMatch = content.match(/siteUrl:\\s*(\\w+)/);\n\n const siteUrl = siteUrlMatch?.[1] ?? (siteUrlVarMatch?.[1] ? constMap.get(siteUrlVarMatch[1]) : undefined);\n\n if (!siteUrl) {\n return null;\n }\n\n // Extract optional outDir value (inline string literal or variable)\n const outDirMatch = content.match(/outDir:\\s*['\"]([^'\"]+)['\"]/);\n const outDirVarMatch = content.match(/outDir:\\s*(\\w+)/);\n const outDir = outDirMatch?.[1] ?? (outDirVarMatch?.[1] ? constMap.get(outDirVarMatch[1]) : undefined);\n\n return { siteUrl, outDir };\n } catch {\n return null;\n }\n}\n\n/**\n * Ensure the IndexNow verification key file exists at public/<key>.txt with the\n * correct content. Creates the file and directory if they don't exist.\n *\n * @param {string} key - The IndexNow API key.\n *\n * @returns {ValidationResult} Valid result if the key file exists or was created successfully.\n */\nexport function ensureKeyFile(key: string): ValidationResult {\n if (!key || typeof key !== 'string') {\n return { valid: false, error: 'IndexNow API key is required.' };\n }\n\n const publicDir = resolve(process.cwd(), DEFAULT_SITEMAP_DIR);\n const keyFilePath = resolve(publicDir, `${key}${KEY_FILE_EXTENSION}`);\n\n try {\n if (!existsSync(publicDir)) {\n mkdirSync(publicDir, { recursive: true });\n }\n\n if (existsSync(keyFilePath)) {\n const existingContent = readFileSync(keyFilePath, 'utf-8').trim();\n\n if (existingContent !== key) {\n writeFileSync(keyFilePath, key, 'utf-8');\n }\n } else {\n writeFileSync(keyFilePath, key, 'utf-8');\n }\n\n return { valid: true };\n } catch (error) {\n /* v8 ignore next 3 */\n return {\n valid: false,\n error: `Failed to create key file: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Resolve the sitemap file path.\n *\n * Uses the configured outDir (from next-sitemap.config) or falls back to\n * the default `public/` directory.\n *\n * @param {string} [outDir] - Custom output directory from next-sitemap config.\n * @param {string} [sitemapFile] - Custom sitemap filename. Defaults to sitemap-0.xml.\n *\n * @returns {string} Absolute path to the sitemap file.\n */\nexport function resolveSitemapPath(outDir?: string, sitemapFile?: string): string {\n const dir = outDir ?? DEFAULT_SITEMAP_DIR;\n const file = sitemapFile ?? DEFAULT_SITEMAP_FILE;\n return resolve(process.cwd(), dir, file);\n}\n\n/**\n * Apply offset and limit options to a list of URLs.\n *\n * Skips the first `offset` URLs and keeps at most `limit` URLs from the\n * remaining list. Omitted options leave the corresponding bound unlimited.\n *\n * @param {string[]} urls - The full list of URLs extracted from the sitemap.\n * @param {number} [offset] - Number of URLs to skip from the start.\n * @param {number} [limit] - Maximum number of URLs to keep (after offset).\n *\n * @returns {string[]} The sliced list of URLs to submit.\n *\n * @throws {Error} If offset or limit is a negative or non-integer value.\n */\nexport function applyOffsetLimit(urls: string[], offset?: number, limit?: number): string[] {\n if (offset !== undefined && (!Number.isInteger(offset) || offset < 0)) {\n throw new Error('offset must be a non-negative integer');\n }\n\n if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {\n throw new Error('limit must be a non-negative integer');\n }\n\n const start = offset ?? 0;\n const end = limit === undefined ? undefined : start + limit;\n\n return urls.slice(start, end);\n}\n\n/**\n * Fetch XML content from a remote sitemap URL.\n *\n * @param {string} url - The remote sitemap URL to fetch.\n *\n * @returns {Promise<string>} The XML content of the sitemap.\n *\n * @throws {Error} If the fetch fails or the response is not OK.\n */\nasync function fetchSitemapXml(url: string): Promise<string> {\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch sitemap: ${url} (${response.status})`);\n }\n\n return response.text();\n}\n\n/**\n * Read and parse a sitemap XML file, extracting all `<loc>` URLs.\n *\n * Supports both regular sitemaps (`<urlset>`) and sitemap index files\n * (`<sitemapindex>`). For sitemap index files, each referenced sub-sitemap\n * is fetched and parsed to collect all URLs.\n *\n * @param {string} sitemapPath - Absolute path to the sitemap XML file.\n *\n * @returns {Promise<ValidationResult & { urls?: string[] }>} Valid result with URL list, or invalid with error.\n */\nexport async function readSitemap(sitemapPath: string): Promise<ValidationResult & { urls?: string[] }> {\n let content: string;\n\n try {\n content = await readFileSync(sitemapPath, 'utf-8');\n } catch {\n return { valid: false, error: `Sitemap file not found: ${sitemapPath}` };\n }\n\n let parsed: { urlset?: { url?: Array<{ loc?: string[] }> }; sitemapindex?: { sitemap?: Array<{ loc?: string[] }> } };\n\n try {\n parsed = await xml2js.parseStringPromise(content);\n } catch {\n return { valid: false, error: 'Failed to parse sitemap XML. Ensure the file is valid XML.' };\n }\n\n // Check if this is a sitemap index file\n if (parsed.sitemapindex) {\n const subSitemapUrls = (parsed.sitemapindex.sitemap ?? [])\n .map((entry) => entry.loc?.[0])\n .filter(Boolean) as string[];\n\n if (subSitemapUrls.length === 0) {\n return { valid: false, error: 'No sub-sitemap URLs found in the sitemap index.' };\n }\n\n const allUrls: string[] = [];\n\n for (const subSitemapUrl of subSitemapUrls) {\n try {\n const subContent = await fetchSitemapXml(subSitemapUrl);\n const subParsed: { urlset?: { url?: Array<{ loc?: string[] }> } } = await xml2js.parseStringPromise(subContent);\n\n const urls = subParsed.urlset?.url?.map((entry) => entry.loc?.[0]).filter(Boolean) as string[] | undefined;\n\n if (urls && urls.length > 0) {\n allUrls.push(...urls);\n }\n } catch {\n // Skip failed sub-sitemaps silently\n }\n }\n\n if (allUrls.length === 0) {\n return { valid: false, error: 'No URLs found in any of the sub-sitemaps.' };\n }\n\n return { valid: true, urls: allUrls };\n }\n\n // Regular sitemap\n const urls = parsed.urlset?.url?.map((entry) => entry.loc?.[0]).filter(Boolean) as string[] | undefined;\n\n if (!urls || urls.length === 0) {\n return { valid: false, error: 'No URLs found in the sitemap.' };\n }\n\n return { valid: true, urls };\n}\n\n/**\n * Submit a batch of URLs to the IndexNow API.\n *\n * @param {string} host - The site hostname (e.g. example.com).\n * @param {string} key - The IndexNow API key.\n * @param {string} keyLocation - Public URL of the key verification file.\n * @param {string[]} urlList - URLs to submit in this batch.\n *\n * @returns {Promise<SubmissionResult>} The submission result for this batch.\n */\nexport async function submitUrls(\n host: string,\n key: string,\n keyLocation: string,\n urlList: string[]\n): Promise<SubmissionResult> {\n try {\n const response = await fetch(INDEXNOW_API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ host, key, keyLocation, urlList }),\n });\n\n if (response.ok) {\n return { count: urlList.length, success: true };\n }\n\n const errorText = await response.text();\n return { count: urlList.length, success: false, error: errorText };\n } catch (error) {\n /* v8 ignore next 4 */\n return {\n count: urlList.length,\n success: false,\n error: `Network error: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","/**\n * Core orchestration for the IndexNow CLI tool.\n *\n * Coordinates validation, sitemap parsing, and URL submission to the\n * IndexNow API for faster search engine indexing.\n *\n * @module next-indexnow\n */\n\nimport { CHUNK_SIZE, DEFAULT_INDEXNOW_KEY } from './constants.ts';\nimport type {\n NextIndexnowOptions,\n NextIndexnowResult,\n NextSitemapConfig,\n SubmissionResult,\n IndexnowProgress,\n} from './types.ts';\nimport {\n validateSiteUrl,\n validateNextProject,\n validateDotNext,\n validateNextSitemapConfig,\n readSitemapConfig,\n ensureKeyFile,\n resolveSitemapPath,\n readSitemap,\n submitUrls,\n applyOffsetLimit,\n} from './utils.ts';\n\n/**\n * Validate basic project structure: Next.js project and .next build directory.\n *\n * @throws {Error} If the project is not a Next.js project or .next is missing/empty.\n */\nfunction validateBasicEnvironment(): void {\n const projectCheck = validateNextProject();\n if (!projectCheck.valid) {\n throw new Error(projectCheck.error);\n }\n\n const dotNextCheck = validateDotNext();\n if (!dotNextCheck.valid) {\n throw new Error(dotNextCheck.error);\n }\n}\n\n/**\n * Validate that a sitemap config file exists and parse its contents.\n *\n * @returns {NextSitemapConfig} The parsed sitemap config (siteUrl + optional outDir).\n *\n * @throws {Error} If no config file is found, it is empty, or siteUrl cannot be parsed.\n */\nfunction validateSitemapSetup(): NextSitemapConfig {\n const sitemapConfigCheck = validateNextSitemapConfig();\n if (!sitemapConfigCheck.valid) {\n throw new Error(sitemapConfigCheck.error!);\n }\n\n const parsedConfig = readSitemapConfig(sitemapConfigCheck.filePath!);\n if (!parsedConfig) {\n throw new Error(`Could not parse \"siteUrl\" from ${sitemapConfigCheck.filePath}.`);\n }\n\n return parsedConfig;\n}\n\n/**\n * Validate that the current directory is a Next.js project with a build\n * artifact and a sitemap config file.\n *\n * @returns {NextSitemapConfig} The parsed sitemap config (siteUrl + optional outDir).\n *\n * @throws {Error} If any validation check fails.\n */\nfunction validateEnvironment(): NextSitemapConfig {\n validateBasicEnvironment();\n return validateSitemapSetup();\n}\n\n/**\n * Resolve the effective siteUrl from options (takes precedence) or config file,\n * validate it, and extract the hostname.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config.\n *\n * @returns {{ siteUrl: string; siteHost: string }} The resolved site URL and host.\n *\n * @throws {Error} If the site URL is invalid.\n */\nfunction resolveSiteConfig(\n options: NextIndexnowOptions,\n parsedConfig: NextSitemapConfig\n): { siteUrl: string; siteHost: string } {\n const siteUrl = options.siteUrl ?? parsedConfig.siteUrl;\n\n const urlValidation = validateSiteUrl(siteUrl);\n if (!urlValidation.valid) {\n throw new Error(urlValidation.error);\n }\n\n return { siteUrl, siteHost: new URL(siteUrl).host };\n}\n\n/**\n * Resolve the IndexNow API key from options or the INDEXNOW_KEY env var,\n * and ensure the verification key file exists on disk.\n *\n * Falls back to a hard-coded default key when neither the --key option nor\n * the INDEXNOW_KEY environment variable is provided.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {string} siteUrl - The resolved site URL (for keyLocation).\n *\n * @returns {{ key: string; keyLocation: string }} The resolved key and its public URL.\n *\n * @throws {Error} If the key file cannot be created.\n */\nfunction resolveKeyValue(options: NextIndexnowOptions, siteUrl: string): { key: string; keyLocation: string } {\n const key = options.key ?? process.env.INDEXNOW_KEY ?? DEFAULT_INDEXNOW_KEY;\n\n const keyFileCheck = ensureKeyFile(key);\n if (!keyFileCheck.valid) {\n throw new Error(keyFileCheck.error);\n }\n\n return { key, keyLocation: `${siteUrl}/${key}.txt` };\n}\n\n/**\n * Resolve the sitemap path (from options or default) and read all URLs.\n *\n * @param {NextIndexnowOptions} options - CLI options.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config (for outDir).\n *\n * @returns {Promise<string[]>} The list of URLs found in the sitemap.\n *\n * @throws {Error} If the sitemap cannot be read or contains no URLs.\n */\nasync function loadSitemapUrls(options: NextIndexnowOptions, parsedConfig: NextSitemapConfig): Promise<string[]> {\n const sitemapPath = options.sitemap ?? resolveSitemapPath(parsedConfig.outDir);\n\n const sitemapResult = await readSitemap(sitemapPath);\n if (!sitemapResult.valid) {\n throw new Error(sitemapResult.error!);\n }\n\n return sitemapResult.urls!;\n}\n\n/**\n * Submit all URLs to the IndexNow API in chunks and aggregate the results.\n *\n * @param {string[]} urls - All URLs to submit.\n * @param {string} siteHost - The site hostname.\n * @param {string} key - The IndexNow API key.\n * @param {string} keyLocation - Public URL of the key verification file.\n * @param {number} chunkSize - Maximum URLs per submission batch.\n * @param {IndexnowProgress} [onProgress] - Callback invoked before each batch submission.\n *\n * @returns {Promise<Pick<NextIndexnowResult, 'urlsSubmitted' | 'urlsFailed' | 'chunks'>>} Aggregate submission counts and per-chunk details.\n */\nasync function submitAllUrls(\n urls: string[],\n siteHost: string,\n key: string,\n keyLocation: string,\n chunkSize: number,\n onProgress?: IndexnowProgress\n): Promise<Pick<NextIndexnowResult, 'urlsSubmitted' | 'urlsFailed' | 'chunks'>> {\n const chunks: SubmissionResult[] = [];\n const totalChunks = Math.ceil(urls.length / chunkSize);\n\n for (let i = 0; i < urls.length; i += chunkSize) {\n const batchNum = Math.floor(i / chunkSize) + 1;\n const chunk = urls.slice(i, i + chunkSize);\n\n onProgress?.({ batch: batchNum, totalBatches: totalChunks, urlCount: chunk.length });\n\n const result = await submitUrls(siteHost, key, keyLocation, chunk);\n chunks.push(result);\n }\n\n const urlsSubmitted = chunks.filter((c) => c.success).reduce((sum, c) => sum + c.count, 0);\n const urlsFailed = chunks.filter((c) => !c.success).reduce((sum, c) => sum + c.count, 0);\n\n return { urlsSubmitted, urlsFailed, chunks };\n}\n\n/**\n * Run the IndexNow submission process.\n *\n * Validates the environment (Next.js project, .next dir, sitemap config),\n * reads the sitemap, applies the offset/limit range, and submits the\n * selected URLs to the IndexNow API in chunks.\n *\n * @param {NextIndexnowOptions} options - CLI options (siteUrl, key, sitemap, chunkSize, offset, limit, dryRun, onProgress).\n *\n * @returns {Promise<NextIndexnowResult>} Aggregate result with per-chunk details.\n */\nexport async function run(options: NextIndexnowOptions = {}): Promise<NextIndexnowResult> {\n const startTime = Date.now();\n const chunkSize = options.chunkSize ?? CHUNK_SIZE;\n\n if (!Number.isInteger(chunkSize) || chunkSize < 1) {\n throw new Error('chunkSize must be a positive integer');\n }\n\n // 1. Validate environment & parse sitemap config\n const parsedConfig = validateEnvironment();\n\n // 2. Resolve siteUrl & key\n const { siteUrl, siteHost } = resolveSiteConfig(options, parsedConfig);\n const { key, keyLocation } = resolveKeyValue(options, siteUrl);\n\n // 3. Read sitemap and apply offset/limit range\n const urls = applyOffsetLimit(await loadSitemapUrls(options, parsedConfig), options.offset, options.limit);\n\n // 4. Dry-run short-circuit\n if (options.dryRun) {\n return { urlsFound: urls.length, urlsSubmitted: 0, urlsFailed: 0, chunks: [], durationMs: Date.now() - startTime };\n }\n\n // 5. Submit URLs in chunks\n const { urlsSubmitted, urlsFailed, chunks } = await submitAllUrls(\n urls,\n siteHost,\n key,\n keyLocation,\n chunkSize,\n options.onProgress\n );\n\n return { urlsFound: urls.length, urlsSubmitted, urlsFailed, chunks, durationMs: Date.now() - startTime };\n}\n","#!/usr/bin/env node\n\n/**\n * next-indexnow — CLI entry point.\n *\n * Parses command-line arguments with commander, validates the environment,\n * reads the Next.js sitemap, and submits URLs to the IndexNow API.\n *\n * @module cli\n *\n * Usage:\n * next-indexnow\n * next-indexnow --site-url https://example.com\n * next-indexnow --key my-api-key\n * next-indexnow --sitemap ./public/sitemap-0.xml\n * next-indexnow -o 100 -l 50\n * next-indexnow --dry-run\n * next-indexnow --help\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport chalk from 'chalk';\nimport { Command, InvalidArgumentError } from 'commander';\nimport logSymbols from 'log-symbols';\nimport ora from 'ora';\n\nimport { DEFAULT_INDEXNOW_KEY } from '../constants.ts';\nimport { run } from '../index.ts';\nimport type { NextIndexnowResult, NextSitemapConfig } from '../types.ts';\nimport {\n validateNextProject,\n validateDotNext,\n validateNextSitemapConfig,\n readSitemapConfig,\n validateSiteUrl,\n ensureKeyFile,\n resolveSitemapPath,\n readSitemap,\n applyOffsetLimit,\n} from '../utils.ts';\n\n// ── ASCII Banner ───────────────────────────────────────────────────────\n\nconst BANNER = `\n${chalk.white('██╗███╗ ██╗██████╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗ ██╗ ██╗')}\n${chalk.white('██║████╗ ██║██╔══██╗██╔════╝╚██╗██╔╝████╗ ██║██╔═══██╗██║ ██║')}\n${chalk.white('██║██╔██╗ ██║██║ ██║█████╗ ╚███╔╝ ██╔██╗ ██║██║ ██║██║ █╗ ██║')}\n${chalk.white('██║██║╚██╗██║██║ ██║██╔══╝ ██╔██╗ ██║╚██╗██║██║ ██║██║███╗██║')}\n${chalk.white('██║██║ ╚████║██████╔╝███████╗██╔╝ ██╗██║ ╚████║╚██████╔╝╚███╔███╔╝')}\n${chalk.white('╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝')}`;\n\nconst SEPARATOR = chalk.dim('='.repeat(69));\n\n// ── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Parse a non-negative integer CLI option value.\n *\n * @param {string} value - Raw string value from the command line.\n *\n * @returns {number} The parsed integer.\n *\n * @throws {InvalidArgumentError} If the value is not a non-negative integer.\n */\nfunction parseNonNegativeInt(value: string): number {\n if (!/^\\d+$/.test(value)) {\n throw new InvalidArgumentError('must be a non-negative integer');\n }\n return Number.parseInt(value, 10);\n}\n\n/**\n * Parse a positive integer CLI option value.\n *\n * @param {string} value - Raw string value from the command line.\n *\n * @returns {number} The parsed integer.\n *\n * @throws {InvalidArgumentError} If the value is not a positive integer.\n */\nfunction parsePositiveInt(value: string): number {\n if (!/^[1-9]\\d*$/.test(value)) {\n throw new InvalidArgumentError('must be a positive integer');\n }\n return Number.parseInt(value, 10);\n}\n\n/**\n * Print a checkmark or error icon with a status label and optional detail.\n *\n * @param {boolean} valid - Whether the check passed.\n * @param {string} label - The status label text.\n * @param {string} [detail] - Optional detail text shown after a colon.\n */\nfunction checkMark(valid: boolean, label: string, detail?: string): void {\n const icon = valid ? logSymbols.success : logSymbols.error;\n const msg = detail ? `${label}: ${detail}` : label;\n console.log(`${icon} ${msg}`);\n}\n\n/**\n * Print the summary footer message after a submission run.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n * @param {boolean} dryRun - Whether this was a dry-run preview.\n */\nfunction printFooter(result: NextIndexnowResult, dryRun: boolean): void {\n console.log('');\n if (dryRun) {\n console.log(chalk.green(`Dry-run complete. ${result.urlsFound} urls would be submitted. 🚀`));\n } else if (result.urlsFailed === 0) {\n console.log(chalk.green(`All ${result.urlsSubmitted} urls submitted to IndexNow successfully! 🥳`));\n } else {\n console.log(\n chalk.yellow(\n `${result.urlsSubmitted}/${result.urlsFound} urls submitted successfully (${result.urlsFailed} failed).`\n )\n );\n }\n}\n\n/**\n * Print detailed failure information for failed submission batches.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n */\nfunction printFailures(result: NextIndexnowResult): void {\n if (result.urlsFailed === 0) return;\n\n console.error(chalk.red.bold('Failed submissions:'));\n for (const chunk of result.chunks) {\n if (!chunk.success) {\n console.error(chalk.red(` ${logSymbols.error} ${chunk.error}`));\n }\n }\n}\n\n// ── Ctrl+C handling ────────────────────────────────────────────────────\n\nlet spinner: ReturnType<typeof ora> | null = null;\n\nprocess.on('SIGINT', () => {\n if (spinner?.isSpinning) {\n spinner.stop();\n }\n console.log('');\n console.log(chalk.yellow('Process aborted by user.'));\n process.exit(130);\n});\n\nconst __filename = fileURLToPath(import.meta.url);\nconst pkgPathSrc = resolve(__filename, '..', '..', '..', 'package.json');\nconst pkgPathDist = resolve(__filename, '..', '..', 'package.json');\nconst pkgPath = existsSync(pkgPathDist) ? pkgPathDist : pkgPathSrc;\n\n// Read version from package.json\nconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('next-indexnow')\n .description('Submit Next.js sitemap URLs to the IndexNow API for faster search engine indexing')\n .version(pkg.version)\n .option('--site-url <url>', 'The site URL (e.g. https://example.com). Overrides next-sitemap.config value.')\n .option('--key <key>', 'IndexNow API key. Falls back to INDEXNOW_KEY environment variable or a built-in default key.')\n .option('--sitemap <path>', 'Path to the sitemap XML file')\n .option('--chunk-size <number>', 'URLs per submission batch (positive integer)', parsePositiveInt, 100)\n .option('-o, --offset <number>', 'Skip this many URLs from the start of the sitemap', parseNonNegativeInt)\n .option('-l, --limit <number>', 'Submit at most this many URLs (after offset)', parseNonNegativeInt)\n .option('-d, --dry-run', 'Preview URLs without submitting to the IndexNow API')\n .addHelpText(\n 'after',\n `\nExamples:\n $ next-indexnow Submit URLs using settings from next-sitemap.config\n $ next-indexnow --site-url https://example.com Override the site URL\n $ next-indexnow --key my-api-key Provide IndexNow API key\n $ next-indexnow --sitemap ./public/sitemap.xml Use a custom sitemap path\n $ next-indexnow -o 100 -l 50 Submit a slice of the sitemap\n $ next-indexnow --dry-run Preview URLs without submitting\n $ next-indexnow --help Show this help message\n `\n )\n .parse(process.argv);\n\n/**\n * Log submission results as checkmark-style lines.\n *\n * @param {NextIndexnowResult} result - The result from the IndexNow submission run.\n */\nfunction displayResults(result: NextIndexnowResult): void {\n const duration = (result.durationMs / 1000).toFixed(2);\n\n const items = [\n ['URLs found', String(result.urlsFound), result.urlsFound > 0 ? ('blue' as const) : ('dim' as const)],\n ['URLs submitted', String(result.urlsSubmitted), result.urlsSubmitted > 0 ? ('green' as const) : ('dim' as const)],\n ['URLs failed', String(result.urlsFailed), result.urlsFailed > 0 ? ('red' as const) : ('dim' as const)],\n ['Duration', `${duration}s`, result.durationMs > 0 ? ('yellow' as const) : ('dim' as const)],\n ];\n\n console.log('');\n for (const [label, value, color] of items) {\n let coloredValue: string;\n switch (color) {\n case 'red':\n coloredValue = chalk.red(value);\n break;\n case 'dim':\n coloredValue = chalk.dim(value);\n break;\n case 'green':\n coloredValue = chalk.green(value);\n break;\n case 'blue':\n coloredValue = chalk.blue(value);\n break;\n case 'yellow':\n coloredValue = chalk.yellow(value);\n break;\n default:\n coloredValue = value;\n break;\n }\n console.log(`${logSymbols.success} ${chalk.bold(label)}: ${coloredValue}`);\n }\n}\n\n/**\n * Run validation checks and display checkmarks, returning parsed state.\n *\n * @param {object} opts - CLI options for site URL and API key.\n * @param {string} [opts.siteUrl] - Override site URL from --site-url option.\n * @param {string} [opts.key] - Override API key from --key option.\n *\n * @returns {{ parsedConfig: NextSitemapConfig; siteUrl: string; siteHost: string; key: string; keyLocation: string }} Resolved configuration values.\n */\nfunction runValidationChecks(opts: { siteUrl?: string; key?: string }): {\n parsedConfig: NextSitemapConfig;\n siteUrl: string;\n siteHost: string;\n key: string;\n keyLocation: string;\n} {\n // 1. Next.js project check\n const projectCheck = validateNextProject();\n if (!projectCheck.valid) {\n checkMark(false, 'Next.js project', projectCheck.error);\n console.log(chalk.red(`\\n ${projectCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Next.js config found');\n\n // 2. Build directory check\n const dotNextCheck = validateDotNext();\n if (!dotNextCheck.valid) {\n checkMark(false, 'Build directory', dotNextCheck.error);\n console.log(chalk.red(`\\n ${dotNextCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Build directory exists (.next)');\n\n // 3. Sitemap config check\n const sitemapConfigCheck = validateNextSitemapConfig();\n if (!sitemapConfigCheck.valid) {\n checkMark(false, 'Sitemap config', sitemapConfigCheck.error);\n console.log(chalk.red(`\\n ${sitemapConfigCheck.error}`));\n process.exit(1);\n }\n checkMark(true, 'Sitemap config found', sitemapConfigCheck.filePath);\n\n // 4. Parse sitemap config\n const parsedConfig = readSitemapConfig(sitemapConfigCheck.filePath!);\n if (!parsedConfig) {\n checkMark(false, 'Site URL', 'Could not parse siteUrl from config');\n console.log(chalk.red(`\\n Could not parse \"siteUrl\" from ${sitemapConfigCheck.filePath}.`));\n process.exit(1);\n }\n\n // 5. Validate site URL\n const siteUrl = opts.siteUrl ?? parsedConfig.siteUrl;\n const urlValidation = validateSiteUrl(siteUrl);\n if (!urlValidation.valid) {\n checkMark(false, 'Site URL', urlValidation.error);\n console.log(chalk.red(`\\n ${urlValidation.error}`));\n process.exit(1);\n }\n const siteHost = new URL(siteUrl).host;\n checkMark(true, 'Site URL resolved', siteHost);\n\n // 6. Resolve API key\n const key = opts.key ?? process.env.INDEXNOW_KEY ?? DEFAULT_INDEXNOW_KEY;\n if (opts.key) {\n checkMark(true, 'API key provided', 'via --key option');\n } else if (process.env.INDEXNOW_KEY) {\n checkMark(true, 'API key resolved', 'via INDEXNOW_KEY env');\n } else {\n checkMark(true, 'API key resolved', 'using built-in default key');\n }\n\n // 7. Ensure key file\n const keyFileCheck = ensureKeyFile(key);\n if (!keyFileCheck.valid) {\n checkMark(false, 'Key file', keyFileCheck.error);\n console.log(chalk.red(`\\n ${keyFileCheck.error}`));\n process.exit(1);\n }\n const keyFilePath = resolve(process.cwd(), 'public', `${key}.txt`);\n const keyExists = existsSync(keyFilePath);\n checkMark(true, 'Key verification file', keyExists ? `${key}.txt exists` : `${key}.txt created`);\n\n const keyLocation = `${siteUrl}/${key}.txt`;\n\n return { parsedConfig, siteUrl, siteHost, key, keyLocation };\n}\n\n/**\n * Read sitemap and display checkmark with URL count.\n *\n * @param {object} opts - CLI options for sitemap path.\n * @param {string} [opts.sitemap] - Custom sitemap path from --sitemap option.\n * @param {NextSitemapConfig} parsedConfig - Parsed sitemap config for default outDir.\n *\n * @returns {Promise<string[]>} The list of URLs extracted from the sitemap.\n */\nasync function loadSitemapCheck(opts: { sitemap?: string }, parsedConfig: NextSitemapConfig): Promise<string[]> {\n const sitemapPath = opts.sitemap ?? resolveSitemapPath(parsedConfig.outDir);\n\n const sitemapResult = await readSitemap(sitemapPath);\n if (!sitemapResult.valid) {\n checkMark(false, 'Sitemap', sitemapResult.error);\n console.log(chalk.red(`\\n ${sitemapResult.error}`));\n process.exit(1);\n }\n\n const urlCount = sitemapResult.urls!.length;\n checkMark(true, 'Sitemap loaded', `${urlCount} URLs found`);\n\n return sitemapResult.urls!;\n}\n\n/**\n * Main CLI entry point.\n *\n * Displays the ASCII banner, runs validation checks with checkmarks,\n * submits URLs to the IndexNow API, and shows results with a footer.\n */\nexport async function main(): Promise<void> {\n const opts = program.optsWithGlobals();\n\n // 1. Show banner + separator + version\n console.log(BANNER);\n console.log('');\n console.log(SEPARATOR);\n console.log(chalk.yellow(`next-indexnow: v${pkg.version}`));\n console.log(SEPARATOR);\n console.log('');\n\n // 2. Run validation checks with checkmarks\n const { parsedConfig } = runValidationChecks({ siteUrl: opts.siteUrl, key: opts.key });\n const sitemapUrls = await loadSitemapCheck({ sitemap: opts.sitemap }, parsedConfig);\n\n // 2b. Show applied offset/limit range when provided\n if (opts.offset !== undefined || opts.limit !== undefined) {\n const selected = applyOffsetLimit(sitemapUrls, opts.offset, opts.limit);\n const rangeParts: string[] = [];\n if (opts.offset !== undefined) rangeParts.push(`offset ${opts.offset}`);\n if (opts.limit !== undefined) rangeParts.push(`limit ${opts.limit}`);\n checkMark(true, 'URL range applied', `${selected.length} of ${sitemapUrls.length} URLs (${rangeParts.join(', ')})`);\n }\n\n console.log('');\n\n // 3. Submit URLs with progress spinner\n spinner = ora({ color: 'cyan', discardStdin: false });\n\n const result = await run({\n siteUrl: opts.siteUrl,\n key: opts.key,\n sitemap: opts.sitemap,\n chunkSize: opts.chunkSize,\n offset: opts.offset,\n limit: opts.limit,\n dryRun: opts.dryRun ?? false,\n onProgress: ({ batch, totalBatches, urlCount }) => {\n if (spinner) {\n spinner.text = `Submitting batch ${batch}/${totalBatches} (${urlCount} URLs)`;\n if (!spinner.isSpinning) {\n spinner.start();\n }\n }\n },\n });\n\n if (spinner?.isSpinning) {\n spinner.stop();\n }\n console.log(SEPARATOR);\n console.log(`${logSymbols.success} Submission Completed 🎉`);\n console.log(SEPARATOR);\n\n // 4. Show results\n displayResults(result);\n\n // 5. Show footer\n printFooter(result, opts.dryRun);\n\n // 6. Show failure details at end\n printFailures(result);\n\n // 7. Exit with appropriate code\n if (result.urlsFailed > 0) {\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vijayhardaha/next-indexnow",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool to submit Next.js sitemaps to the IndexNow API for faster search engine indexing",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "license": "MIT",
57
57
  "dependencies": {
58
- "chalk": "^5.6.2",
58
+ "chalk": "^6.0.0",
59
59
  "commander": "^15.0.0",
60
60
  "log-symbols": "^7.0.1",
61
61
  "ora": "^9.4.1",