@chrisburnell/eleventy-cache-webmentions 1.1.4 → 1.2.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 +89 -89
- package/eleventy-cache-webmentions.js +109 -32
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -21,12 +21,12 @@ Inside your Eleventy config file (typically `.eleventy.js`), use `addPlugin`:
|
|
|
21
21
|
const pluginWebmentions = require("@chrisburnell/eleventy-cache-webmentions")
|
|
22
22
|
|
|
23
23
|
module.exports = function (eleventyConfig) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
eleventyConfig.addPlugin(pluginWebmentions, {
|
|
25
|
+
// these 3 fields are all required!
|
|
26
|
+
domain: "https://example.com",
|
|
27
|
+
feed: "https://webmentions.example.com?token=S3cr3tT0k3n",
|
|
28
|
+
key: "children",
|
|
29
|
+
})
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -38,51 +38,51 @@ Advanced control over how the Webmentions are cached and processed is done by pa
|
|
|
38
38
|
const pluginWebmentions = require("@chrisburnell/eleventy-cache-webmentions")
|
|
39
39
|
|
|
40
40
|
module.exports = function (eleventyConfig) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
41
|
+
eleventyConfig.addPlugin(pluginWebmentions, {
|
|
42
|
+
// domain: required or the plugin will not function
|
|
43
|
+
// this is the website that you want to pull in Webmentions for
|
|
44
|
+
domain: "https://example.com",
|
|
45
|
+
// feed: required or the plugin will not function
|
|
46
|
+
// defines the URL of your Webmention server where a feed of Webmentions for your domain can be found
|
|
47
|
+
feed: "https://webmentions.example.com?token=S3cr3tT0k3n",
|
|
48
|
+
// key: required or the plugin will not function
|
|
49
|
+
// dictates the key inside the feed where the array of Webmentions is located
|
|
50
|
+
key: "children",
|
|
51
|
+
// directory: ".cache" by default
|
|
52
|
+
// see https://www.11ty.dev/docs/plugins/cache/#cache-directory for more info
|
|
53
|
+
directory: ".cache",
|
|
54
|
+
// duration: "1d" by default
|
|
55
|
+
// see https://www.11ty.dev/docs/plugins/cache/#change-the-cache-duration for more info
|
|
56
|
+
duration: "1d",
|
|
57
|
+
// uniquekey: "webmentions" by default
|
|
58
|
+
// dictates the name sent to eleventy-fetch to name the file
|
|
59
|
+
uniqueKey: "webmentions",
|
|
60
|
+
// allowedHTML: Object by default
|
|
61
|
+
// see https://www.npmjs.com/package/sanitize-html for more info
|
|
62
|
+
allowedHTML: {
|
|
63
|
+
allowedTags: ["b", "i", "em", "strong", "a"],
|
|
64
|
+
allowedAttributes: {
|
|
65
|
+
a: ["href"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
// allowlist: [] by default
|
|
69
|
+
// array of root URLs from which webmentions are wanted exclusively
|
|
70
|
+
allowlist: [],
|
|
71
|
+
// blocklist: [] by default
|
|
72
|
+
// array of root URLs from which webmentions are not wanted
|
|
73
|
+
// exclusively
|
|
74
|
+
blocklist: [],
|
|
75
|
+
// urlReplacements: {} by default
|
|
76
|
+
// object of key:value pairs containing from:to URL replacements
|
|
77
|
+
urlReplacements: {},
|
|
78
|
+
// maximumHtmlLength: 2000 by default
|
|
79
|
+
// number of characters in the HTML content at which a different
|
|
80
|
+
// message is shown instead of the content
|
|
81
|
+
maximumHtmlLength: 2000,
|
|
82
|
+
// maximumHtmlText: "mentioned this in" by default
|
|
83
|
+
// message shown when maximumHtmlLength is reached
|
|
84
|
+
maximumHtmlText: "mentioned this in",
|
|
85
|
+
})
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -92,9 +92,9 @@ Accessing the plugin in JavaScript in the way shown below will give you an Objec
|
|
|
92
92
|
|
|
93
93
|
```javascript
|
|
94
94
|
const Webmentions = require("@chrisburnell/eleventy-cache-webmentions")(null, {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
domain: "https://example.com",
|
|
96
|
+
feed: "https://webmentions.example.com?token=S3cr3tT0k3n",
|
|
97
|
+
key: "children",
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
const webmentionsByUrl = await Webmentions()
|
|
@@ -104,28 +104,28 @@ This can prove to be very useful when building out your pages. Using [Eleventy
|
|
|
104
104
|
|
|
105
105
|
```javascript
|
|
106
106
|
const Webmentions = require("@chrisburnell/eleventy-cache-webmentions")(null, {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
domain: "https://example.com",
|
|
108
|
+
feed: "https://webmentions.example.com?token=S3cr3tT0k3n",
|
|
109
|
+
key: "children",
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
module.exports = async () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
const webmentionsByUrl = await Webmentions()
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
eleventyComputed: {
|
|
117
|
+
webmentions: (data) => {
|
|
118
|
+
const webmentionsForUrl = webmentionsByUrl["https://example.com" + data.page.url] || []
|
|
119
|
+
|
|
120
|
+
if (webmentionsForUrl.length) {
|
|
121
|
+
return webmentionsForUrl.sort((a, b) => {
|
|
122
|
+
return (b.data.published || b.verified_date) - (a.data.published || a.verified_date)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
return []
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
129
|
}
|
|
130
130
|
```
|
|
131
131
|
|
|
@@ -133,11 +133,11 @@ You can now use this data in a number of useful ways, not limited to things like
|
|
|
133
133
|
|
|
134
134
|
```javascript
|
|
135
135
|
module.exports = (eleventyConfig) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
eleventyConfig.addCollection("popular", (collection) => {
|
|
137
|
+
return collection.sort((a, b) => {
|
|
138
|
+
return b.data.webmentions.length - a.data.webmentions.length
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
143
|
|
|
@@ -166,7 +166,7 @@ And, if you need it, the entire Object of sorted Webmentions is available too:
|
|
|
166
166
|
|
|
167
167
|
```twig
|
|
168
168
|
{% raw %}{% set count = 0 %}
|
|
169
|
-
{% for url, array in
|
|
169
|
+
{% for url, array in webmentionsAll %}
|
|
170
170
|
{% set count = array.length + count %}
|
|
171
171
|
{% endfor %}
|
|
172
172
|
<p>This site has received {{ count }} Webmentions!</p>{% endraw %}
|
|
@@ -190,11 +190,11 @@ WEBMENTION_IO_TOKEN=njJql0lKXnotreal4x3Wmd
|
|
|
190
190
|
const pluginWebmentions = require("@chrisburnell/eleventy-cache-webmentions")
|
|
191
191
|
|
|
192
192
|
module.exports = function (eleventyConfig) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
eleventyConfig.addPlugin(pluginWebmentions, {
|
|
194
|
+
domain: "https://example.com",
|
|
195
|
+
feed: `https://webmention.io/api/mentions.jf2?domain=example.com&per-page=9001&token=${process.env.WEBMENTION_IO_TOKEN}`,
|
|
196
|
+
key: "children",
|
|
197
|
+
})
|
|
198
198
|
}
|
|
199
199
|
```
|
|
200
200
|
|
|
@@ -216,11 +216,11 @@ GO_JAMMING_TOKEN=njJql0lKXnotreal4x3Wmd
|
|
|
216
216
|
const pluginWebmentions = require("@chrisburnell/eleventy-cache-webmentions")
|
|
217
217
|
|
|
218
218
|
module.exports = function (eleventyConfig) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
eleventyConfig.addPlugin(pluginWebmentions, {
|
|
220
|
+
domain: "https://example.com",
|
|
221
|
+
feed: `https://jam.example.com/webmention/example.com/${process.env.GO_JAMMING_TOKEN}`,
|
|
222
|
+
key: "json",
|
|
223
|
+
})
|
|
224
224
|
}
|
|
225
225
|
```
|
|
226
226
|
|
|
@@ -7,7 +7,9 @@ const absoluteURL = (url, domain) => {
|
|
|
7
7
|
try {
|
|
8
8
|
return new URL(url, domain).toString()
|
|
9
9
|
} catch (e) {
|
|
10
|
-
console.log(
|
|
10
|
+
console.log(
|
|
11
|
+
`Trying to convert ${url} to be an absolute url with base ${domain} and failed.`
|
|
12
|
+
)
|
|
11
13
|
return url
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -19,10 +21,13 @@ const baseUrl = (url) => {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const fixUrl = (url, urlReplacements) => {
|
|
22
|
-
return Object.entries(urlReplacements).reduce(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
return Object.entries(urlReplacements).reduce(
|
|
25
|
+
(accumulator, [key, value]) => {
|
|
26
|
+
const regex = new RegExp(key, "g")
|
|
27
|
+
return accumulator.replace(regex, value)
|
|
28
|
+
},
|
|
29
|
+
url
|
|
30
|
+
)
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
const hostname = (value) => {
|
|
@@ -38,15 +43,30 @@ const epoch = (value) => {
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
const getPublished = (webmention) => {
|
|
41
|
-
return
|
|
46
|
+
return (
|
|
47
|
+
webmention["wm-received"] ||
|
|
48
|
+
webmention?.["data"]["published"] ||
|
|
49
|
+
webmention["verified_date"] ||
|
|
50
|
+
webmention["published"]
|
|
51
|
+
)
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
const getContent = (webmention) => {
|
|
45
|
-
return
|
|
55
|
+
return (
|
|
56
|
+
webmention?.["content"]?.["html"] ||
|
|
57
|
+
webmention?.["content"] ||
|
|
58
|
+
webmention?.["data"]?.["content"] ||
|
|
59
|
+
""
|
|
60
|
+
)
|
|
46
61
|
}
|
|
47
62
|
|
|
48
63
|
const getSource = (webmention) => {
|
|
49
|
-
return
|
|
64
|
+
return (
|
|
65
|
+
webmention["url"] ||
|
|
66
|
+
webmention?.["data"]["url"] ||
|
|
67
|
+
webmention["wm-source"] ||
|
|
68
|
+
webmention["source"]
|
|
69
|
+
)
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
const getTarget = (webmention) => {
|
|
@@ -54,7 +74,11 @@ const getTarget = (webmention) => {
|
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
const getType = (webmention) => {
|
|
57
|
-
return
|
|
77
|
+
return (
|
|
78
|
+
webmention["wm-property"] ||
|
|
79
|
+
webmention?.["activity"]["type"] ||
|
|
80
|
+
webmention["type"]
|
|
81
|
+
)
|
|
58
82
|
}
|
|
59
83
|
|
|
60
84
|
const defaults = {
|
|
@@ -74,19 +98,27 @@ const defaults = {
|
|
|
74
98
|
|
|
75
99
|
const fetchWebmentions = async (options) => {
|
|
76
100
|
if (!options.domain) {
|
|
77
|
-
throw new Error(
|
|
101
|
+
throw new Error(
|
|
102
|
+
"domain is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
103
|
+
)
|
|
78
104
|
}
|
|
79
105
|
|
|
80
106
|
if (!options.feed) {
|
|
81
|
-
throw new Error(
|
|
107
|
+
throw new Error(
|
|
108
|
+
"feed is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
109
|
+
)
|
|
82
110
|
}
|
|
83
111
|
|
|
84
112
|
if (!options.key) {
|
|
85
|
-
throw new Error(
|
|
113
|
+
throw new Error(
|
|
114
|
+
"key is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
115
|
+
)
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
if (!options.uniqueKey) {
|
|
89
|
-
throw new Error(
|
|
119
|
+
throw new Error(
|
|
120
|
+
"uniqueKey is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
121
|
+
)
|
|
90
122
|
}
|
|
91
123
|
|
|
92
124
|
let asset = new AssetCache(options.uniqueKey, options.directory)
|
|
@@ -103,16 +135,27 @@ const fetchWebmentions = async (options) => {
|
|
|
103
135
|
// results since the most recent webmention
|
|
104
136
|
if (!asset.isCacheValid(options.duration)) {
|
|
105
137
|
const since = webmentions.length ? getPublished(webmentions[0]) : false
|
|
106
|
-
const url = `${options.feed}${
|
|
138
|
+
const url = `${options.feed}${
|
|
139
|
+
since
|
|
140
|
+
? `${options.feed.includes("?") ? "&" : "?"}since=${since}`
|
|
141
|
+
: ""
|
|
142
|
+
}`
|
|
107
143
|
await fetch(url)
|
|
108
144
|
.then(async (response) => {
|
|
109
145
|
if (response.ok) {
|
|
110
146
|
const feed = await response.json()
|
|
111
147
|
if (feed[options.key].length) {
|
|
112
|
-
webmentions = uniqBy(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
148
|
+
webmentions = uniqBy(
|
|
149
|
+
[...feed[options.key], ...webmentions],
|
|
150
|
+
(entry) => {
|
|
151
|
+
return getSource(entry)
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
console.log(
|
|
155
|
+
`[${hostname(options.domain)}] ${
|
|
156
|
+
feed[options.key].length
|
|
157
|
+
} new Webmentions fetched into cache.`
|
|
158
|
+
)
|
|
116
159
|
}
|
|
117
160
|
await asset.save(webmentions, "json")
|
|
118
161
|
return webmentions
|
|
@@ -120,16 +163,27 @@ const fetchWebmentions = async (options) => {
|
|
|
120
163
|
return Promise.reject(response)
|
|
121
164
|
})
|
|
122
165
|
.catch((error) => {
|
|
123
|
-
console.log(
|
|
166
|
+
console.log(
|
|
167
|
+
`[${hostname(
|
|
168
|
+
options.domain
|
|
169
|
+
)}] Something went wrong with your request to ${hostname(
|
|
170
|
+
options.feed
|
|
171
|
+
)}!`,
|
|
172
|
+
error
|
|
173
|
+
)
|
|
124
174
|
})
|
|
125
175
|
}
|
|
126
176
|
|
|
127
177
|
return webmentions
|
|
128
178
|
}
|
|
129
179
|
|
|
180
|
+
let filtered = {}
|
|
130
181
|
const filteredWebmentions = async (options) => {
|
|
182
|
+
if (Object.entries(filtered).length) {
|
|
183
|
+
return filtered
|
|
184
|
+
}
|
|
185
|
+
|
|
131
186
|
let rawWebmentions = await fetchWebmentions(options)
|
|
132
|
-
let webmentions = {}
|
|
133
187
|
|
|
134
188
|
// Process the blocklist, if it has any entries
|
|
135
189
|
if (options.blocklist.length) {
|
|
@@ -164,23 +218,28 @@ const filteredWebmentions = async (options) => {
|
|
|
164
218
|
// Fix local URLs based on urlReplacements and sort Webmentions into groups
|
|
165
219
|
// by target base URL
|
|
166
220
|
rawWebmentions.forEach((webmention) => {
|
|
167
|
-
let url = baseUrl(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
221
|
+
let url = baseUrl(
|
|
222
|
+
fixUrl(
|
|
223
|
+
getTarget(webmention).replace(/\/?$/, "/"),
|
|
224
|
+
options.urlReplacements
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if (!filtered[url]) {
|
|
229
|
+
filtered[url] = []
|
|
171
230
|
}
|
|
172
231
|
|
|
173
|
-
|
|
232
|
+
filtered[url].push(webmention)
|
|
174
233
|
})
|
|
175
234
|
|
|
176
235
|
// Remove duplicates by source URL
|
|
177
|
-
for (let url in
|
|
178
|
-
|
|
236
|
+
for (let url in filtered) {
|
|
237
|
+
filtered[url] = uniqBy(filtered[url], (entry) => {
|
|
179
238
|
return getSource(entry)
|
|
180
239
|
})
|
|
181
240
|
}
|
|
182
241
|
|
|
183
|
-
return
|
|
242
|
+
return filtered
|
|
184
243
|
}
|
|
185
244
|
|
|
186
245
|
const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
@@ -195,7 +254,10 @@ const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
|
195
254
|
webmentions[url]
|
|
196
255
|
// filter webmentions by allowed response post types
|
|
197
256
|
.filter((entry) => {
|
|
198
|
-
return typeof allowedTypes === "object" &&
|
|
257
|
+
return typeof allowedTypes === "object" &&
|
|
258
|
+
Object.keys(allowedTypes).length
|
|
259
|
+
? allowedTypes.includes(getType(entry))
|
|
260
|
+
: true
|
|
199
261
|
})
|
|
200
262
|
// sanitize content of webmentions against HTML limit
|
|
201
263
|
.map((entry) => {
|
|
@@ -204,7 +266,9 @@ const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
|
204
266
|
if (html.length) {
|
|
205
267
|
entry.content = sanitizeHTML(html, options.allowedHTML)
|
|
206
268
|
if (html.length > options.maximumHtmlLength) {
|
|
207
|
-
entry.content = `${
|
|
269
|
+
entry.content = `${
|
|
270
|
+
options.maximumHtmlText
|
|
271
|
+
} <a href="${getSource(entry)}">${getSource(entry)}</a>`
|
|
208
272
|
}
|
|
209
273
|
}
|
|
210
274
|
|
|
@@ -231,13 +295,26 @@ module.exports = (eleventyConfig, options = {}) => {
|
|
|
231
295
|
|
|
232
296
|
if (eleventyConfig) {
|
|
233
297
|
// Global Data
|
|
234
|
-
|
|
298
|
+
const filtered = async () => await filteredWebmentions(options)
|
|
299
|
+
eleventyConfig.addGlobalData("webmentionsByUrl", filtered)
|
|
300
|
+
const unfiltered = async () =>
|
|
301
|
+
await filteredWebmentions(options).then((filtered) =>
|
|
302
|
+
Object.values(filtered).reduce(
|
|
303
|
+
(array, webmentions) => [...array, ...webmentions],
|
|
304
|
+
[]
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
eleventyConfig.addGlobalData("webmentionsAll", unfiltered)
|
|
308
|
+
eleventyConfig.addGlobalData("webmentions", unfiltered)
|
|
235
309
|
|
|
236
310
|
// Liquid Filter
|
|
237
311
|
eleventyConfig.addLiquidFilter("getWebmentions", getWebmentionsFilter)
|
|
238
312
|
|
|
239
313
|
// Nunjucks Filter
|
|
240
|
-
eleventyConfig.addNunjucksAsyncFilter(
|
|
314
|
+
eleventyConfig.addNunjucksAsyncFilter(
|
|
315
|
+
"getWebmentions",
|
|
316
|
+
getWebmentionsFilter
|
|
317
|
+
)
|
|
241
318
|
}
|
|
242
319
|
|
|
243
320
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrisburnell/eleventy-cache-webmentions",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Fetch and cache webmentions using eleventy-fetch.",
|
|
5
5
|
"author": "Chris Burnell <me@chrisburnell.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"webmention"
|
|
21
21
|
],
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"eslint": "^8.
|
|
23
|
+
"eslint": "^8.41.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@11ty/eleventy-fetch": "^
|
|
26
|
+
"@11ty/eleventy-fetch": "^4.0.0",
|
|
27
27
|
"lodash": "^4.17.21",
|
|
28
|
-
"node-fetch": "^
|
|
29
|
-
"sanitize-html": "^2.
|
|
28
|
+
"node-fetch": "^3.3.1",
|
|
29
|
+
"sanitize-html": "^2.10.0"
|
|
30
30
|
},
|
|
31
31
|
"main": "eleventy-cache-webmentions.js",
|
|
32
32
|
"scripts": {
|