@rmdes/indiekit-endpoint-rss 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -20,6 +20,9 @@ const defaults = {
20
20
  retentionDays: 30,
21
21
  minItemsPerFeed: 10,
22
22
  maxPostsPerCycle: 10,
23
+ // Eleventy's watcher settles a new file in stabilityThreshold (2s) + watch
24
+ // throttle (3s); posting faster than that loses builds.
25
+ postIntervalMs: 5000,
23
26
  };
24
27
 
25
28
  export default class RssEndpoint {
package/lib/publisher.js CHANGED
@@ -4,6 +4,14 @@ import { buildJf2 } from "./jf2-builder.js";
4
4
 
5
5
  const TOKEN_TTL = "5m";
6
6
 
7
+ /**
8
+ * Pause between posts
9
+ * @param {number} ms - Milliseconds
10
+ * @returns {Promise<void>} Resolves after the delay
11
+ */
12
+ const sleep = (ms) =>
13
+ ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
14
+
7
15
  /**
8
16
  * Mint a short-lived token for background publishing.
9
17
  *
@@ -18,7 +26,10 @@ export function mintToken(me) {
18
26
  throw new Error("Cannot publish: SECRET is not configured");
19
27
  }
20
28
 
21
- return jwt.sign({ me, scope: "create" }, process.env.SECRET, {
29
+ // Both scopes: an item that already has a post is updated, not created, and
30
+ // checkScope treats them as distinct actions — a create-only token gets a
31
+ // 403 insufficient_scope on every republish.
32
+ return jwt.sign({ me, scope: "create update" }, process.env.SECRET, {
22
33
  expiresIn: TOKEN_TTL,
23
34
  });
24
35
  }
@@ -160,8 +171,10 @@ export function pendingQuery(feed) {
160
171
  * @param {string} options.micropubEndpoint - Micropub endpoint URL
161
172
  * @param {string} options.me - Publication URL
162
173
  * @param {number} options.maxPostsPerCycle - Cap per cycle
174
+ * @param {number} [options.postIntervalMs] - Pause between posts
163
175
  * @param {Function} [options.postImpl] - Post implementation, for tests
164
176
  * @param {Function} [options.mintImpl] - Token minter, for tests
177
+ * @param {Function} [options.sleepImpl] - Sleep implementation, for tests
165
178
  * @returns {Promise<object>} Counts for this feed
166
179
  */
167
180
  export async function publishPending(feed, itemsCollection, options) {
@@ -175,6 +188,8 @@ export async function publishPending(feed, itemsCollection, options) {
175
188
  maxPostsPerCycle,
176
189
  postImpl = postToMicropub,
177
190
  mintImpl = mintToken,
191
+ postIntervalMs = 0,
192
+ sleepImpl = sleep,
178
193
  } = options;
179
194
 
180
195
  const query = pendingQuery(feed);
@@ -196,7 +211,7 @@ export async function publishPending(feed, itemsCollection, options) {
196
211
  // postData.create replaceOne's on the same URL, so it overwrites rather
197
212
  // than duplicates. Add a q=source reconciliation if a feed ever produces
198
213
  // non-deterministic slugs.
199
- for (const item of items) {
214
+ for (const [index, item] of items.entries()) {
200
215
  try {
201
216
  // Minted per item, not once for the batch: signing is a cheap HMAC, and
202
217
  // a single batch token can expire mid-cycle on a slow endpoint. The
@@ -240,6 +255,14 @@ export async function publishPending(feed, itemsCollection, options) {
240
255
 
241
256
  console.error(`[RSS] Publish failed for ${item.guid}: ${error.message}`);
242
257
  }
258
+
259
+ // Space the posts out. Each one writes a markdown file, and Eleventy's
260
+ // watcher needs chokidar's stabilityThreshold (2s) plus its own watch
261
+ // throttle (3s) to absorb one before the next lands. A burst arriving
262
+ // faster makes it log "Wrote 0 files" and leave the posts unbuilt.
263
+ if (index < items.length - 1) {
264
+ await sleepImpl(postIntervalMs);
265
+ }
243
266
  }
244
267
 
245
268
  console.log(
package/lib/sync.js CHANGED
@@ -154,6 +154,7 @@ export async function runSync(dbOrIndiekit, options, publishContext) {
154
154
  const result = await publishPending(feed, itemsCollection, {
155
155
  ...publishContext,
156
156
  maxPostsPerCycle: options.maxPostsPerCycle || 10,
157
+ postIntervalMs: options.postIntervalMs ?? 5000,
157
158
  });
158
159
  itemsPublished += result.published;
159
160
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-rss",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "RSS feed reader endpoint for Indiekit. Aggregates multiple feeds, caches in MongoDB, displays on frontend.",
5
5
  "keywords": [
6
6
  "indiekit",