@chrisburnell/eleventy-cache-webmentions 2.3.2 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -4
- package/eleventy-cache-webmentions.cjs +4 -688
- package/eleventy-cache-webmentions.js +40 -675
- package/package.json +8 -6
- package/src/core.cjs +689 -0
- package/.cache/eleventy-fetch-68a403136c61abd104ade94e69ba62 +0 -1
- package/.cache/eleventy-fetch-68a403136c61abd104ade94e69ba62.json +0 -1
- package/eleventy-cache-webmentions.min.cjs +0 -1
- package/eleventy-cache-webmentions.min.js +0 -1
- package/eslint.config.js +0 -42
|
@@ -1,693 +1,9 @@
|
|
|
1
1
|
const { AssetCache } = require("@11ty/eleventy-fetch");
|
|
2
2
|
const { styleText } = require("node:util");
|
|
3
3
|
const sanitizeHTML = require("sanitize-html");
|
|
4
|
+
const createCore = require("./src/core.cjs");
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
* @typedef {sanitizeHTML.IOptions} AllowedHTML
|
|
7
|
-
*/
|
|
6
|
+
const core = createCore({ AssetCache, styleText, sanitizeHTML });
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* @property {boolean} refresh
|
|
12
|
-
* @property {string} duration
|
|
13
|
-
* @property {string} uniqueKey
|
|
14
|
-
* @property {string} [cacheDirectory]
|
|
15
|
-
* @property {AllowedHTML} allowedHTML
|
|
16
|
-
* @property {Array<string>} allowlist
|
|
17
|
-
* @property {Array<string>} blocklist
|
|
18
|
-
* @property {{[key: string]: string}} urlReplacements
|
|
19
|
-
* @property {number} maximumHtmlLength
|
|
20
|
-
* @property {string} maximumHtmlText
|
|
21
|
-
*/
|
|
22
|
-
const defaults = {
|
|
23
|
-
refresh: false,
|
|
24
|
-
duration: "1d",
|
|
25
|
-
uniqueKey: "webmentions",
|
|
26
|
-
cacheDirectory: undefined,
|
|
27
|
-
allowedHTML: {
|
|
28
|
-
allowedTags: ["a", "b", "em", "i", "strong"],
|
|
29
|
-
allowedAttributes: {
|
|
30
|
-
a: ["href"],
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
allowlist: [],
|
|
34
|
-
blocklist: [],
|
|
35
|
-
urlReplacements: {},
|
|
36
|
-
maximumHtmlLength: 1000,
|
|
37
|
-
maximumHtmlText: "mentioned this in",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @typedef {object} OptionsUserInput
|
|
42
|
-
* @property {string} domain
|
|
43
|
-
* @property {string} feed
|
|
44
|
-
* @property {string} key
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @typedef {OptionsDefaults & OptionsUserInput} Options
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {object} Webmention
|
|
53
|
-
* @property {string} [source]
|
|
54
|
-
* @property {string} [url]
|
|
55
|
-
* @property {string} [target]
|
|
56
|
-
* @property {string} [published]
|
|
57
|
-
* @property {string} [contentSanitized]
|
|
58
|
-
* @property {object} [content]
|
|
59
|
-
* @property {string} [content.html]
|
|
60
|
-
* @property {string} [content.value]
|
|
61
|
-
* @property {object} [data]
|
|
62
|
-
* @property {string} [data.title]
|
|
63
|
-
* @property {string} [data.url]
|
|
64
|
-
* @property {string} [data.published]
|
|
65
|
-
* @property {string} [data.content]
|
|
66
|
-
* @property {string} [type]
|
|
67
|
-
* @property {object} [activity]
|
|
68
|
-
* @property {string} [activity.type]
|
|
69
|
-
* @property {boolean} [verified]
|
|
70
|
-
* @property {string} ["wm-property"]
|
|
71
|
-
* @property {string} ["wm-received"]
|
|
72
|
-
* @property {string} ["wm-source"]
|
|
73
|
-
* @property {string} ["wm-target"]
|
|
74
|
-
* @property {string} ["verified_date"]
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @typedef {"bookmark-of"|"like-of"|"repost-of"|"mention-of"|"in-reply-to"} WebmentionType
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @param {string} url
|
|
83
|
-
* @param {string} domain
|
|
84
|
-
* @returns {string}
|
|
85
|
-
*/
|
|
86
|
-
const absoluteURL = (url, domain) => {
|
|
87
|
-
try {
|
|
88
|
-
return new URL(url, domain).toString();
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error(
|
|
91
|
-
`Trying to convert ${styleText(
|
|
92
|
-
"bold",
|
|
93
|
-
url,
|
|
94
|
-
)} to be an absolute url with base ${styleText(
|
|
95
|
-
"bold",
|
|
96
|
-
domain,
|
|
97
|
-
)} and failed.`,
|
|
98
|
-
error,
|
|
99
|
-
);
|
|
100
|
-
return url;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @param {string} url
|
|
106
|
-
* @returns {string}
|
|
107
|
-
*/
|
|
108
|
-
const baseURL = (url) => {
|
|
109
|
-
let hashSplit = url.split("#");
|
|
110
|
-
let queryparamSplit = hashSplit[0].split("?");
|
|
111
|
-
return queryparamSplit[0];
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @param {string} url
|
|
116
|
-
* @param {{[key: string], string}} [urlReplacements]
|
|
117
|
-
* @returns {string}
|
|
118
|
-
*/
|
|
119
|
-
const fixURL = (url, urlReplacements) => {
|
|
120
|
-
return Object.entries(urlReplacements).reduce(
|
|
121
|
-
(accumulator, [key, value]) => {
|
|
122
|
-
const regex = new RegExp(key, "g");
|
|
123
|
-
return accumulator.replace(regex, value);
|
|
124
|
-
},
|
|
125
|
-
url,
|
|
126
|
-
);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* @param {string} url
|
|
131
|
-
* @returns {string}
|
|
132
|
-
*/
|
|
133
|
-
const hostname = (url) => {
|
|
134
|
-
if (typeof url === "string" && url.includes("//")) {
|
|
135
|
-
const urlObject = new URL(url);
|
|
136
|
-
return urlObject.hostname;
|
|
137
|
-
}
|
|
138
|
-
return url;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @param {string|number|Date} date
|
|
143
|
-
* @returns {number}
|
|
144
|
-
*/
|
|
145
|
-
const epoch = (date) => {
|
|
146
|
-
return new Date(date).getTime();
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @param {Array<Webmention>} webmentions
|
|
151
|
-
* @returns {Array<Webmention>}
|
|
152
|
-
*/
|
|
153
|
-
const removeDuplicates = (webmentions) => {
|
|
154
|
-
return [
|
|
155
|
-
...webmentions
|
|
156
|
-
.reduce((map, webmention) => {
|
|
157
|
-
const key =
|
|
158
|
-
webmention === null || webmention === undefined
|
|
159
|
-
? webmention
|
|
160
|
-
: getUniqueKey(webmention);
|
|
161
|
-
if (!map.has(key)) {
|
|
162
|
-
map.set(key, webmention);
|
|
163
|
-
}
|
|
164
|
-
return map;
|
|
165
|
-
}, new Map())
|
|
166
|
-
.values(),
|
|
167
|
-
];
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* @param {Webmention} webmention
|
|
172
|
-
* @returns {string|undefined}
|
|
173
|
-
*/
|
|
174
|
-
const getPublished = (webmention) => {
|
|
175
|
-
return (
|
|
176
|
-
webmention?.["data"]?.["published"] ||
|
|
177
|
-
webmention["published"] ||
|
|
178
|
-
webmention["wm-received"] ||
|
|
179
|
-
webmention["verified_date"]
|
|
180
|
-
);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* @param {Webmention} webmention
|
|
185
|
-
* @returns {string|undefined}
|
|
186
|
-
*/
|
|
187
|
-
const getReceived = (webmention) => {
|
|
188
|
-
return (
|
|
189
|
-
webmention["wm-received"] ||
|
|
190
|
-
webmention["verified_date"] ||
|
|
191
|
-
webmention["published"] ||
|
|
192
|
-
webmention?.["data"]?.["published"]
|
|
193
|
-
);
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* @param {Webmention} webmention
|
|
198
|
-
* @returns {string}
|
|
199
|
-
*/
|
|
200
|
-
const getContent = (webmention) => {
|
|
201
|
-
return (
|
|
202
|
-
webmention?.["contentSanitized"] ||
|
|
203
|
-
webmention?.["content"]?.["html"] ||
|
|
204
|
-
webmention?.["content"]?.["value"] ||
|
|
205
|
-
webmention?.["content"] ||
|
|
206
|
-
webmention?.["data"]?.["content"] ||
|
|
207
|
-
""
|
|
208
|
-
);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* @param {Webmention} webmention
|
|
213
|
-
* @returns {string|undefined}
|
|
214
|
-
*/
|
|
215
|
-
const getSource = (webmention) => {
|
|
216
|
-
return (
|
|
217
|
-
webmention["wm-source"] ||
|
|
218
|
-
webmention["source"] ||
|
|
219
|
-
webmention?.["data"]?.["url"] ||
|
|
220
|
-
webmention["url"]
|
|
221
|
-
);
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @param {Webmention} webmention
|
|
226
|
-
* @returns {string|undefined}
|
|
227
|
-
*/
|
|
228
|
-
const getURL = (webmention) => {
|
|
229
|
-
return (
|
|
230
|
-
webmention?.["data"]?.["url"] ||
|
|
231
|
-
webmention["url"] ||
|
|
232
|
-
webmention["wm-source"] ||
|
|
233
|
-
webmention["source"]
|
|
234
|
-
);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* @param {Webmention} webmention
|
|
239
|
-
* @returns {string|undefined}
|
|
240
|
-
*/
|
|
241
|
-
const getTarget = (webmention) => {
|
|
242
|
-
return webmention["wm-target"] || webmention["target"];
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* @param {Webmention} webmention
|
|
247
|
-
* @returns {string}
|
|
248
|
-
*/
|
|
249
|
-
const getUniqueKey = (webmention) => {
|
|
250
|
-
return JSON.stringify([getSource(webmention), getTarget(webmention)]);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @param {Webmention} webmention
|
|
255
|
-
* @returns {string|undefined}
|
|
256
|
-
*/
|
|
257
|
-
const getType = (webmention) => {
|
|
258
|
-
return (
|
|
259
|
-
webmention["wm-property"] ||
|
|
260
|
-
webmention?.["activity"]?.["type"] ||
|
|
261
|
-
webmention["type"]
|
|
262
|
-
);
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* @param {Array<Webmention>} webmentions
|
|
267
|
-
* @param {WebmentionType|Array<WebmentionType>} types
|
|
268
|
-
* @returns {Array<Webmention>}
|
|
269
|
-
*/
|
|
270
|
-
const getByTypes = (webmentions, types) => {
|
|
271
|
-
return webmentions.filter((webmention) => {
|
|
272
|
-
if (typeof types === "string") {
|
|
273
|
-
return types === getType(webmention);
|
|
274
|
-
}
|
|
275
|
-
return types.includes(getType(webmention));
|
|
276
|
-
});
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* @param {Array<Webmention>} webmentions
|
|
281
|
-
* @param {Array<string>} blocklist
|
|
282
|
-
* @returns {Array<Webmention>}
|
|
283
|
-
*/
|
|
284
|
-
const processBlocklist = (webmentions, blocklist) => {
|
|
285
|
-
return webmentions.filter((webmention) => {
|
|
286
|
-
let url = getSource(webmention);
|
|
287
|
-
let source = getSource(webmention);
|
|
288
|
-
for (let blocklistURL of blocklist) {
|
|
289
|
-
if (
|
|
290
|
-
url.includes(blocklistURL.replace(/\/?$/, "/")) ||
|
|
291
|
-
source.includes(blocklistURL.replace(/\/?$/, "/"))
|
|
292
|
-
) {
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return true;
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* @param {Array<Webmention>} webmentions
|
|
302
|
-
* @param {Array<string>} allowlist
|
|
303
|
-
* @returns {Array<Webmention>}
|
|
304
|
-
*/
|
|
305
|
-
const processAllowlist = (webmentions, allowlist) => {
|
|
306
|
-
return webmentions.filter((webmention) => {
|
|
307
|
-
let url = getSource(webmention);
|
|
308
|
-
let source = getSource(webmention);
|
|
309
|
-
for (let allowlistURL of allowlist) {
|
|
310
|
-
if (
|
|
311
|
-
url.includes(allowlistURL.replace(/\/?$/, "/")) ||
|
|
312
|
-
source.includes(allowlistURL.replace(/\/?$/, "/"))
|
|
313
|
-
) {
|
|
314
|
-
return true;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return false;
|
|
318
|
-
});
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* @param {Options} options
|
|
323
|
-
* @param {Array<Webmention>} webmentions
|
|
324
|
-
* @param {string} url
|
|
325
|
-
* @returns {Promise<{found: number, webmentions: Array<Webmention>}>}
|
|
326
|
-
*/
|
|
327
|
-
const fetchWebmentions = async (options, webmentions, url) => {
|
|
328
|
-
return await fetch(url)
|
|
329
|
-
.then(async (response) => {
|
|
330
|
-
if (!response.ok) {
|
|
331
|
-
return Promise.reject(response);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const feed = await response.json();
|
|
335
|
-
|
|
336
|
-
if (!(options.key in feed)) {
|
|
337
|
-
console.log(
|
|
338
|
-
`${styleText("gray", `[${hostname(options.domain)}]`)} ${
|
|
339
|
-
options.key
|
|
340
|
-
} was not found as a key in the response from ${styleText(
|
|
341
|
-
"bold",
|
|
342
|
-
hostname(options.feed),
|
|
343
|
-
)}!`,
|
|
344
|
-
);
|
|
345
|
-
return Promise.reject(response);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Fetched Webmentions replace cached ones with the same
|
|
349
|
-
// source / target pair
|
|
350
|
-
const fetchedKeys = new Set(
|
|
351
|
-
feed[options.key].map(getUniqueKey),
|
|
352
|
-
);
|
|
353
|
-
webmentions = [
|
|
354
|
-
...feed[options.key],
|
|
355
|
-
...webmentions.filter(
|
|
356
|
-
(wm) => !fetchedKeys.has(getUniqueKey(wm)),
|
|
357
|
-
),
|
|
358
|
-
];
|
|
359
|
-
// Remove any remaining duplicates by source / target pair
|
|
360
|
-
webmentions = removeDuplicates(webmentions);
|
|
361
|
-
// Process the blocklist, if it has any entries
|
|
362
|
-
if (options.blocklist.length) {
|
|
363
|
-
webmentions = processBlocklist(webmentions, options.blocklist);
|
|
364
|
-
}
|
|
365
|
-
// Process the allowlist, if it has any entries
|
|
366
|
-
if (options.allowlist.length) {
|
|
367
|
-
webmentions = processAllowlist(webmentions, options.allowlist);
|
|
368
|
-
}
|
|
369
|
-
// Sort webmentions by received date for getting most recent Webmention on subsequent requests
|
|
370
|
-
webmentions = webmentions.sort((a, b) => {
|
|
371
|
-
return epoch(getReceived(b)) - epoch(getReceived(a));
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
return {
|
|
375
|
-
found: feed[options.key].length,
|
|
376
|
-
webmentions: webmentions,
|
|
377
|
-
};
|
|
378
|
-
})
|
|
379
|
-
.catch((error) => {
|
|
380
|
-
console.warn(
|
|
381
|
-
`${styleText(
|
|
382
|
-
"gray",
|
|
383
|
-
`[${hostname(options.domain)}]`,
|
|
384
|
-
)} Something went wrong with your Webmention request to ${styleText(
|
|
385
|
-
"bold",
|
|
386
|
-
hostname(options.feed),
|
|
387
|
-
)}!`,
|
|
388
|
-
);
|
|
389
|
-
console.warn(error instanceof Error ? error.message : error);
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
found: 0,
|
|
393
|
-
webmentions: webmentions,
|
|
394
|
-
};
|
|
395
|
-
});
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* @param {Options} options
|
|
400
|
-
* @returns {Promise<Array<Webmention>>}
|
|
401
|
-
*/
|
|
402
|
-
const retrieveWebmentions = async (options) => {
|
|
403
|
-
if (!options.domain) {
|
|
404
|
-
throw new Error(
|
|
405
|
-
"`domain` is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.",
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (!options.feed) {
|
|
410
|
-
throw new Error(
|
|
411
|
-
"`feed` is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.",
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (!options.key) {
|
|
416
|
-
throw new Error(
|
|
417
|
-
"`key` is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.",
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
let asset = new AssetCache(
|
|
422
|
-
options.uniqueKey || `webmentions-${hostname(options.domain)}`,
|
|
423
|
-
options.cacheDirectory,
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
let webmentions = [];
|
|
427
|
-
|
|
428
|
-
// Unless specifically getting fresh Webmentions, if there is a cached file
|
|
429
|
-
// at all, grab its contents now
|
|
430
|
-
if (asset.isCacheValid("9001y") && !options.refresh) {
|
|
431
|
-
webmentions = await asset.getCachedValue();
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Get the number of cached Webmentions for diffing against fetched
|
|
435
|
-
// Webmentions later
|
|
436
|
-
const webmentionsCachedLength = webmentions.length;
|
|
437
|
-
|
|
438
|
-
// If there is a cached file but it is outside of expiry, fetch fresh
|
|
439
|
-
// results since the most recent Webmention
|
|
440
|
-
if (!asset.isCacheValid(options.refresh ? "0s" : options.duration)) {
|
|
441
|
-
const performanceStart = process.hrtime();
|
|
442
|
-
// Get the received date of the most recent Webmention, if it exists
|
|
443
|
-
const since = webmentions.length ? getReceived(webmentions[0]) : false;
|
|
444
|
-
// Build the URL for the fetch request
|
|
445
|
-
const url = `${options.feed}${
|
|
446
|
-
since
|
|
447
|
-
? `${options.feed.includes("?") ? "&" : "?"}since=${since}`
|
|
448
|
-
: ""
|
|
449
|
-
}`;
|
|
450
|
-
|
|
451
|
-
// If using webmention.io, loop through pages until no results found
|
|
452
|
-
if (url.includes("https://webmention.io")) {
|
|
453
|
-
const urlObject = new URL(url);
|
|
454
|
-
const perPage =
|
|
455
|
-
Number(urlObject.searchParams.get("per-page")) || 1000;
|
|
456
|
-
urlObject.searchParams.delete("per-page");
|
|
457
|
-
// Start on page 0, to increment per subsequent request
|
|
458
|
-
let page = 0;
|
|
459
|
-
// Loop until a break condition is hit
|
|
460
|
-
while (true) {
|
|
461
|
-
const urlPaginated =
|
|
462
|
-
urlObject.href + `&per-page=${perPage}&page=${page}`;
|
|
463
|
-
const fetched = await fetchWebmentions(
|
|
464
|
-
options,
|
|
465
|
-
webmentions,
|
|
466
|
-
urlPaginated,
|
|
467
|
-
);
|
|
468
|
-
|
|
469
|
-
// An error occurred during fetching paged results → break
|
|
470
|
-
if (!fetched && !fetched.found && !fetched.webmentions) {
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Page has no Webmentions → break
|
|
475
|
-
if (fetched.found === 0) {
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
webmentions = fetched.webmentions;
|
|
480
|
-
|
|
481
|
-
// If there are less Webmentions found than should be in each
|
|
482
|
-
// page → break
|
|
483
|
-
if (fetched.found < perPage) {
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Increment page
|
|
488
|
-
page += 1;
|
|
489
|
-
// Throttle next request
|
|
490
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
491
|
-
}
|
|
492
|
-
} else {
|
|
493
|
-
const fetched = await fetchWebmentions(options, webmentions, url);
|
|
494
|
-
webmentions = fetched.webmentions;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Process the blocklist, if it has any entries
|
|
498
|
-
if (options.blocklist.length) {
|
|
499
|
-
webmentions = processBlocklist(webmentions, options.blocklist);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Process the allowlist, if it has any entries
|
|
503
|
-
if (options.allowlist.length) {
|
|
504
|
-
webmentions = processAllowlist(webmentions, options.allowlist);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
await asset.save(webmentions, "json");
|
|
508
|
-
|
|
509
|
-
const performance = process.hrtime(performanceStart);
|
|
510
|
-
|
|
511
|
-
// Add a console message with the number of fetched and processed Webmentions, if any
|
|
512
|
-
if (webmentionsCachedLength < webmentions.length) {
|
|
513
|
-
console.log(
|
|
514
|
-
`${styleText(
|
|
515
|
-
"gray",
|
|
516
|
-
`[${hostname(options.domain)}]`,
|
|
517
|
-
)} ${styleText(
|
|
518
|
-
"bold",
|
|
519
|
-
String(webmentions.length - webmentionsCachedLength),
|
|
520
|
-
)} new Webmentions fetched into cache in ${styleText(
|
|
521
|
-
"bold",
|
|
522
|
-
(performance[0] + performance[1] / 1e9).toFixed(3) +
|
|
523
|
-
" seconds",
|
|
524
|
-
)}.`,
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return webmentions;
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
/** @type {Array<Webmention>} */
|
|
533
|
-
const WEBMENTIONS = {};
|
|
534
|
-
|
|
535
|
-
/**
|
|
536
|
-
* @param {Options} options
|
|
537
|
-
* @returns {Promise<{[key: string]: Array<Webmention>}>}
|
|
538
|
-
*/
|
|
539
|
-
const webmentionsByURL = async (options) => {
|
|
540
|
-
if (Object.keys(WEBMENTIONS).length) {
|
|
541
|
-
return WEBMENTIONS;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
let rawWebmentions = await retrieveWebmentions(options);
|
|
545
|
-
|
|
546
|
-
// Fix local URLs based on urlReplacements and sort Webmentions into groups
|
|
547
|
-
// by target base URL
|
|
548
|
-
rawWebmentions.forEach((webmention) => {
|
|
549
|
-
let url = baseURL(
|
|
550
|
-
fixURL(
|
|
551
|
-
getTarget(webmention).replace(/\/?$/, "/"),
|
|
552
|
-
options.urlReplacements,
|
|
553
|
-
),
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
if (!WEBMENTIONS[url]) {
|
|
557
|
-
WEBMENTIONS[url] = [];
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
WEBMENTIONS[url].push(webmention);
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
return WEBMENTIONS;
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* @param {Options} options
|
|
568
|
-
* @param {string} url
|
|
569
|
-
* @param {WebmentionType|Array<WebmentionType>} [types]
|
|
570
|
-
* @returns {Promise<Array<Webmention>>}
|
|
571
|
-
*/
|
|
572
|
-
const getWebmentions = async (options, url, types = []) => {
|
|
573
|
-
const webmentions = await webmentionsByURL(options);
|
|
574
|
-
url = absoluteURL(url, options.domain);
|
|
575
|
-
|
|
576
|
-
if (!url || !webmentions || !webmentions[url]) {
|
|
577
|
-
return [];
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
return (
|
|
581
|
-
webmentions[url]
|
|
582
|
-
// Filter webmentions by allowed response post types
|
|
583
|
-
.filter((entry) => {
|
|
584
|
-
return typeof types === "object" && Object.keys(types).length
|
|
585
|
-
? types.includes(getType(entry))
|
|
586
|
-
: typeof types === "string"
|
|
587
|
-
? types === getType(entry)
|
|
588
|
-
: true;
|
|
589
|
-
})
|
|
590
|
-
// Sanitize content of webmentions against HTML limit
|
|
591
|
-
.map((entry) => {
|
|
592
|
-
const html = getContent(entry);
|
|
593
|
-
|
|
594
|
-
if (html.length) {
|
|
595
|
-
entry.contentSanitized = sanitizeHTML(
|
|
596
|
-
html,
|
|
597
|
-
options.allowedHTML,
|
|
598
|
-
);
|
|
599
|
-
if (html.length > options.maximumHtmlLength) {
|
|
600
|
-
entry.contentSanitized = `${
|
|
601
|
-
options.maximumHtmlText
|
|
602
|
-
} <a href="${getSource(entry)}">${getSource(
|
|
603
|
-
entry,
|
|
604
|
-
)}</a>`;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
return entry;
|
|
609
|
-
})
|
|
610
|
-
// Sort by published
|
|
611
|
-
.sort((a, b) => {
|
|
612
|
-
return epoch(getPublished(a)) - epoch(getPublished(b));
|
|
613
|
-
})
|
|
614
|
-
);
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* @param {object} eleventyConfig
|
|
619
|
-
* @param {Options} [options]
|
|
620
|
-
*/
|
|
621
|
-
const eleventyCacheWebmentions = async (eleventyConfig, options = {}) => {
|
|
622
|
-
options = Object.assign(defaults, options);
|
|
623
|
-
|
|
624
|
-
const byURL = await webmentionsByURL(options);
|
|
625
|
-
const all = Object.values(byURL).reduce(
|
|
626
|
-
(array, webmentions) => [...array, ...webmentions],
|
|
627
|
-
[],
|
|
628
|
-
);
|
|
629
|
-
|
|
630
|
-
// Global Data
|
|
631
|
-
eleventyConfig.addGlobalData("webmentionsDefaults", defaults);
|
|
632
|
-
eleventyConfig.addGlobalData("webmentionsOptions", options);
|
|
633
|
-
eleventyConfig.addGlobalData("webmentionsByURL", byURL);
|
|
634
|
-
eleventyConfig.addGlobalData("webmentionsByUrl", byURL);
|
|
635
|
-
eleventyConfig.addGlobalData("webmentionsAll", all);
|
|
636
|
-
|
|
637
|
-
// Liquid Filters
|
|
638
|
-
eleventyConfig.addLiquidFilter("getWebmentionsByType", getByTypes);
|
|
639
|
-
eleventyConfig.addLiquidFilter("getWebmentionsByTypes", getByTypes);
|
|
640
|
-
eleventyConfig.addLiquidFilter("getWebmentionPublished", getPublished);
|
|
641
|
-
eleventyConfig.addLiquidFilter("getWebmentionReceived", getReceived);
|
|
642
|
-
eleventyConfig.addLiquidFilter("getWebmentionContent", getContent);
|
|
643
|
-
eleventyConfig.addLiquidFilter("getWebmentionSource", getSource);
|
|
644
|
-
eleventyConfig.addLiquidFilter("getWebmentionURL", getURL);
|
|
645
|
-
eleventyConfig.addLiquidFilter("getWebmentionTarget", getTarget);
|
|
646
|
-
eleventyConfig.addLiquidFilter("getWebmentionType", getType);
|
|
647
|
-
|
|
648
|
-
// Nunjucks Filters
|
|
649
|
-
eleventyConfig.addNunjucksFilter("getWebmentionsByType", getByTypes);
|
|
650
|
-
eleventyConfig.addNunjucksFilter("getWebmentionsByTypes", getByTypes);
|
|
651
|
-
eleventyConfig.addNunjucksFilter("getWebmentionPublished", getPublished);
|
|
652
|
-
eleventyConfig.addNunjucksFilter("getWebmentionReceived", getReceived);
|
|
653
|
-
eleventyConfig.addNunjucksFilter("getWebmentionContent", getContent);
|
|
654
|
-
eleventyConfig.addNunjucksFilter("getWebmentionSource", getSource);
|
|
655
|
-
eleventyConfig.addNunjucksFilter("getWebmentionURL", getURL);
|
|
656
|
-
eleventyConfig.addNunjucksFilter("getWebmentionTarget", getTarget);
|
|
657
|
-
eleventyConfig.addNunjucksFilter("getWebmentionType", getType);
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
module.exports = eleventyCacheWebmentions;
|
|
661
|
-
module.exports.defaults = defaults;
|
|
662
|
-
module.exports.getPublished = getPublished;
|
|
663
|
-
module.exports.getWebmentionPublished = getPublished;
|
|
664
|
-
module.exports.getReceived = getReceived;
|
|
665
|
-
module.exports.getWebmentionReceived = getReceived;
|
|
666
|
-
module.exports.getContent = getContent;
|
|
667
|
-
module.exports.getWebmentionContent = getContent;
|
|
668
|
-
module.exports.getSource = getSource;
|
|
669
|
-
module.exports.getWebmentionSource = getSource;
|
|
670
|
-
module.exports.getURL = getURL;
|
|
671
|
-
module.exports.getWebmentionURL = getURL;
|
|
672
|
-
module.exports.getTarget = getTarget;
|
|
673
|
-
module.exports.getWebmentionTarget = getTarget;
|
|
674
|
-
module.exports.getUniqueKey = getUniqueKey;
|
|
675
|
-
module.exports.getWebmentionUniqueKey = getUniqueKey;
|
|
676
|
-
module.exports.getType = getType;
|
|
677
|
-
module.exports.getWebmentionType = getType;
|
|
678
|
-
module.exports.getByTypes = getByTypes;
|
|
679
|
-
module.exports.getByType = getByTypes;
|
|
680
|
-
module.exports.getWebmentionsByTypes = getByTypes;
|
|
681
|
-
module.exports.getWebmentionsByType = getByTypes;
|
|
682
|
-
module.exports.processBlocklist = processBlocklist;
|
|
683
|
-
module.exports.processWebmentionBlocklist = processBlocklist;
|
|
684
|
-
module.exports.processWebmentionsBlocklist = processBlocklist;
|
|
685
|
-
module.exports.processAllowlist = processAllowlist;
|
|
686
|
-
module.exports.processWebmentionAllowlist = processAllowlist;
|
|
687
|
-
module.exports.processWebmentionsAllowlist = processAllowlist;
|
|
688
|
-
module.exports.fetchWebmentions = fetchWebmentions;
|
|
689
|
-
module.exports.retrieveWebmentions = retrieveWebmentions;
|
|
690
|
-
module.exports.webmentionsByURL = webmentionsByURL;
|
|
691
|
-
module.exports.webmentionsByUrl = webmentionsByURL;
|
|
692
|
-
module.exports.filteredWebmentions = webmentionsByURL;
|
|
693
|
-
module.exports.getWebmentions = getWebmentions;
|
|
8
|
+
module.exports = core.eleventyCacheWebmentions;
|
|
9
|
+
Object.assign(module.exports, core);
|