astro-indexnow 2.3.4 โ†’ 2.3.5

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
@@ -6,11 +6,23 @@ to **IndexNow** after each build.
6
6
  This package is designed for **modern CI/CD pipelines**, **Docker-based deployments**, and
7
7
  **large static sites**, while remaining fully deterministic and explicit.
8
8
 
9
- > **Current release:** v2.3.4
9
+ > **Current release:** v2.3.5
10
10
  > **Stateful by design. `changed` by default, `all` available. Batch-safe. CI-aware.**
11
11
 
12
12
  ---
13
13
 
14
+ ## Compatibility Matrix
15
+
16
+ | Platform | Supported | Notes |
17
+ | --- | --- | --- |
18
+ | Astro | 4.x, 5.x, 6.x, 7.x | Build-time integration works in static and hybrid builds |
19
+ | Node.js | `>=22.12.0` | Matches Astro v7 runtime floor |
20
+ | Static builds | Yes | Primary supported use case |
21
+ | Hybrid builds | Yes | Works for prerendered output generated at build time |
22
+ | SSR request-time submission | No | Use a webhook or server-side helper instead |
23
+
24
+ ---
25
+
14
26
  ## What is IndexNow?
15
27
 
16
28
  [IndexNow](https://www.indexnow.org/) is a protocol supported by search engines such as **Bing**
@@ -31,6 +43,7 @@ Instead of waiting for crawlers, your site tells search engines exactly which UR
31
43
  - ๐Ÿงช Optional `dryRun` plans submissions without sending requests
32
44
  - ๐Ÿ—ฃ Optional `logMode` controls verbosity with `quiet`, `normal`, and `verbose`
33
45
  - ๐Ÿ“ฆ Optional `batchSize` lets you lower the submit batch size for testing
46
+ - ๐Ÿ” Optional retry controls let you tune transient failure handling
34
47
  - ๐Ÿงช Fully **CI/CD and Docker safe**
35
48
  - ๐Ÿ”’ No secrets prompted, stored, or mutated
36
49
  - ๐Ÿงฉ No client-side or runtime code added
@@ -306,6 +319,33 @@ indexnow({
306
319
 
307
320
  ---
308
321
 
322
+ ### `retryAttempts` (optional)
323
+
324
+ Type: `number`
325
+ Default: `3`
326
+
327
+ Controls how many times a failed batch submission is retried.
328
+
329
+ ---
330
+
331
+ ### `retryBaseDelayMs` (optional)
332
+
333
+ Type: `number`
334
+ Default: `1000`
335
+
336
+ Controls the starting delay for exponential backoff between retries.
337
+
338
+ ---
339
+
340
+ ### `retryMaxDelayMs` (optional)
341
+
342
+ Type: `number`
343
+ Default: `8000`
344
+
345
+ Controls the maximum backoff delay between retries.
346
+
347
+ ---
348
+
309
349
  ## IndexNow Limits & Batching
310
350
 
311
351
  - IndexNow allows **up to 10,000 URLs per request**
@@ -321,6 +361,7 @@ indexnow({
321
361
  - No prompts or side effects
322
362
  - No reliance on Git history
323
363
  - Deterministic output-based change detection
364
+ - Intended for static and hybrid builds, not SSR request-time submission
324
365
 
325
366
  ---
326
367
 
package/dist/index.d.ts CHANGED
@@ -8,6 +8,9 @@ export interface IndexNowOptions {
8
8
  logMode?: "quiet" | "normal" | "verbose";
9
9
  submissionMode?: "changed" | "all";
10
10
  batchSize?: number;
11
+ retryAttempts?: number;
12
+ retryBaseDelayMs?: number;
13
+ retryMaxDelayMs?: number;
11
14
  }
12
15
  export default function indexNow(options?: IndexNowOptions): AstroIntegration;
13
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAM9C,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,cAAc,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAC9B,OAAO,GAAE,eAAoB,GAC5B,gBAAgB,CA0PlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAM9C,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,cAAc,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAC9B,OAAO,GAAE,eAAoB,GAC5B,gBAAgB,CAuTlB"}
package/dist/index.js CHANGED
@@ -14,6 +14,15 @@ export default function indexNow(options = {}) {
14
14
  const batchSize = typeof options.batchSize === "number" && options.batchSize > 0
15
15
  ? Math.floor(options.batchSize)
16
16
  : INDEXNOW_BATCH_SIZE;
17
+ const retryAttempts = typeof options.retryAttempts === "number" && options.retryAttempts > 0
18
+ ? Math.floor(options.retryAttempts)
19
+ : 3;
20
+ const retryBaseDelayMs = typeof options.retryBaseDelayMs === "number" && options.retryBaseDelayMs > 0
21
+ ? Math.floor(options.retryBaseDelayMs)
22
+ : 1000;
23
+ const retryMaxDelayMs = typeof options.retryMaxDelayMs === "number" && options.retryMaxDelayMs > 0
24
+ ? Math.floor(options.retryMaxDelayMs)
25
+ : 8000;
17
26
  /* =========================================================
18
27
  Helpers
19
28
  ========================================================= */
@@ -57,6 +66,9 @@ export default function indexNow(options = {}) {
57
66
  }
58
67
  return chunks;
59
68
  }
69
+ function sleep(ms) {
70
+ return new Promise((resolve) => setTimeout(resolve, ms));
71
+ }
60
72
  function isQuiet() {
61
73
  return options.logMode === "quiet";
62
74
  }
@@ -75,6 +87,47 @@ export default function indexNow(options = {}) {
75
87
  if (isVerbose())
76
88
  logger.info(message);
77
89
  }
90
+ function isRetryableStatus(status) {
91
+ return status === 429 || (status >= 500 && status < 600);
92
+ }
93
+ async function submitBatch(logger, batch, batchIndex) {
94
+ for (let attempt = 1; attempt <= retryAttempts; attempt++) {
95
+ try {
96
+ const response = await fetch(INDEXNOW_ENDPOINT, {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify({
100
+ host: new URL(site).host,
101
+ key: options.key,
102
+ keyLocation: `${site}/${options.key}.txt`,
103
+ urlList: batch,
104
+ }),
105
+ });
106
+ if (response.ok) {
107
+ return true;
108
+ }
109
+ if (!isRetryableStatus(response.status) || attempt === retryAttempts) {
110
+ logWarn(logger, `batch ${batchIndex} failed (${response.status})`);
111
+ return false;
112
+ }
113
+ const delay = Math.min(retryBaseDelayMs * 2 ** (attempt - 1), retryMaxDelayMs) +
114
+ Math.floor(Math.random() * 250);
115
+ logWarn(logger, `batch ${batchIndex} attempt ${attempt} failed (${response.status}), retrying in ${delay}ms`);
116
+ await sleep(delay);
117
+ }
118
+ catch {
119
+ if (attempt === retryAttempts) {
120
+ logWarn(logger, `batch ${batchIndex} submission failed (network error)`);
121
+ return false;
122
+ }
123
+ const delay = Math.min(retryBaseDelayMs * 2 ** (attempt - 1), retryMaxDelayMs) +
124
+ Math.floor(Math.random() * 250);
125
+ logWarn(logger, `batch ${batchIndex} submission failed (network error), retrying in ${delay}ms`);
126
+ await sleep(delay);
127
+ }
128
+ }
129
+ return false;
130
+ }
78
131
  /* =========================================================
79
132
  Integration
80
133
  ========================================================= */
@@ -159,30 +212,22 @@ export default function indexNow(options = {}) {
159
212
  logInfo(logger, "dry run enabled, skipping submission");
160
213
  return;
161
214
  }
215
+ let anyBatchFailed = false;
162
216
  for (let i = 0; i < batches.length; i++) {
163
217
  const batch = batches[i];
164
218
  logVerbose(logger, `submitting batch ${i + 1}/${batches.length} (${batch.length} URLs)`);
165
- try {
166
- const response = await fetch(INDEXNOW_ENDPOINT, {
167
- method: "POST",
168
- headers: { "Content-Type": "application/json" },
169
- body: JSON.stringify({
170
- host: new URL(site).host,
171
- key: options.key,
172
- keyLocation: `${site}/${options.key}.txt`,
173
- urlList: batch,
174
- }),
175
- });
176
- if (!response.ok) {
177
- logWarn(logger, `batch ${i + 1} failed (${response.status})`);
178
- }
179
- }
180
- catch {
181
- logWarn(logger, `batch ${i + 1} submission failed (network error)`);
219
+ const batchSucceeded = await submitBatch(logger, batch, i + 1);
220
+ if (!batchSucceeded) {
221
+ anyBatchFailed = true;
182
222
  }
183
223
  }
184
224
  saveCache(logger, nextCache);
185
- logInfo(logger, `IndexNow submission complete`);
225
+ if (anyBatchFailed) {
226
+ logWarn(logger, `IndexNow submission failed`);
227
+ }
228
+ else {
229
+ logInfo(logger, `IndexNow submission complete`);
230
+ }
186
231
  },
187
232
  },
188
233
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-indexnow",
3
- "version": "2.3.4",
3
+ "version": "2.3.5",
4
4
  "description": "Astro integration to submit pages to IndexNow automatically after build",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,6 +11,7 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "tsc",
14
+ "release:check": "node ./scripts/release-check.mjs",
14
15
  "prepublishOnly": "npm run build"
15
16
  },
16
17
  "keywords": [