` with anchor tags for each microformat property (`in-reply-to`, `like-of`, `bookmark-of`, `repost-of`, `syndication`) plus the stored `content.html`. This is reliable, fast, and requires no networking.
+
+Additional changes:
+- livefetch v6: adds `console.log` per post showing which properties produced links — makes future debugging possible without server access
+- livefetch v6: upgrades from any prior version (v1–v5) in-place via per-version end-marker detection
+- retry patch: regex now matches `[patched:livefetch]` and `[patched:livefetch:vN]` for all versions
+- reset-stale v11: bumped to retry posts stuck before v6 deployment
+- start.sh: poller now uses `INDIEKIT_DIRECT_URL=http://INDIEKIT_BIND_HOST:PORT` instead of `INTERNAL_FETCH_URL` (nginx); poller was timing out for 180s every restart due to the 444 responses
+
+**chore: `sharp_from_source=true` in `.npmrc`**
+Builds the `sharp` native module from source for FreeBSD compatibility (no prebuilt binary available).
+
### 2026-03-19
**feat: deliver likes as bookmarks, revert announce cc, add OG images** (`45f8ba9` in svemagie/indiekit-endpoint-activitypub)
diff --git a/package-lock.json b/package-lock.json
index 808123c3..374c5609 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,7 @@
"@rmdes/indiekit-endpoint-github": "^1.2.3",
"@rmdes/indiekit-endpoint-homepage": "^1.0.22",
"@rmdes/indiekit-endpoint-lastfm": "^1.0.12",
- "@rmdes/indiekit-endpoint-microsub": "github:svemagie/indiekit-endpoint-microsub#bookmarks-import",
+ "@rmdes/indiekit-endpoint-microsub": "github:svemagie/indiekit-endpoint-microsub",
"@rmdes/indiekit-endpoint-podroll": "^1.0.11",
"@rmdes/indiekit-endpoint-posts": "^1.0.0-beta.44",
"@rmdes/indiekit-endpoint-readlater": "github:rmdes/indiekit-endpoint-readlater",
@@ -62,15 +62,15 @@
}
},
"node_modules/@atproto/api": {
- "version": "0.14.22",
- "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.14.22.tgz",
- "integrity": "sha512-ziXPau+sUdFovObSnsoN7JbOmUw1C5e5L28/yXf3P8vbEnSS3HVVGD1jYcscBYY34xQqi4bVDpwMYx/4yRsTuQ==",
+ "version": "0.19.4",
+ "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz",
+ "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==",
"license": "MIT",
"dependencies": {
- "@atproto/common-web": "^0.4.1",
- "@atproto/lexicon": "^0.4.10",
- "@atproto/syntax": "^0.4.0",
- "@atproto/xrpc": "^0.6.12",
+ "@atproto/common-web": "^0.4.18",
+ "@atproto/lexicon": "^0.6.2",
+ "@atproto/syntax": "^0.5.1",
+ "@atproto/xrpc": "^0.7.7",
"await-lock": "^2.2.2",
"multiformats": "^9.9.0",
"tlds": "^1.234.0",
@@ -78,30 +78,21 @@
}
},
"node_modules/@atproto/common-web": {
- "version": "0.4.18",
- "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.18.tgz",
- "integrity": "sha512-ilImzP+9N/mtse440kN60pGrEzG7wi4xsV13nGeLrS+Zocybc/ISOpKlbZM13o+twPJ+Q7veGLw9CtGg0GAFoQ==",
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz",
+ "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==",
"license": "MIT",
"dependencies": {
- "@atproto/lex-data": "^0.0.13",
- "@atproto/lex-json": "^0.0.13",
- "@atproto/syntax": "^0.5.0",
+ "@atproto/lex-data": "^0.0.14",
+ "@atproto/lex-json": "^0.0.14",
+ "@atproto/syntax": "^0.5.1",
"zod": "^3.23.8"
}
},
- "node_modules/@atproto/common-web/node_modules/@atproto/syntax": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.0.tgz",
- "integrity": "sha512-UA2DSpGdOQzUQ4gi5SH+NEJz/YR3a3Fg3y2oh+xETDSiTRmA4VhHRCojhXAVsBxUT6EnItw190C/KN+DWW90kw==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.8.1"
- }
- },
"node_modules/@atproto/lex-data": {
- "version": "0.0.13",
- "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.13.tgz",
- "integrity": "sha512-7Z7RwZ1Y/JzBF/Tcn/I4UJ/vIGfh5zn1zjv0KX+flke2JtgFkSE8uh2hOtqgBQMNqE3zdJFM+dcSWln86hR3MQ==",
+ "version": "0.0.14",
+ "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz",
+ "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==",
"license": "MIT",
"dependencies": {
"multiformats": "^9.9.0",
@@ -111,44 +102,44 @@
}
},
"node_modules/@atproto/lex-json": {
- "version": "0.0.13",
- "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.13.tgz",
- "integrity": "sha512-hwLhkKaIHulGJpt0EfXAEWdrxqM2L1tV/tvilzhMp3QxPqYgXchFnrfVmLsyFDx6P6qkH1GsX/XC2V36U0UlPQ==",
+ "version": "0.0.14",
+ "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz",
+ "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==",
"license": "MIT",
"dependencies": {
- "@atproto/lex-data": "^0.0.13",
+ "@atproto/lex-data": "^0.0.14",
"tslib": "^2.8.1"
}
},
"node_modules/@atproto/lexicon": {
- "version": "0.4.14",
- "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.14.tgz",
- "integrity": "sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ==",
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz",
+ "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==",
"license": "MIT",
"dependencies": {
- "@atproto/common-web": "^0.4.2",
- "@atproto/syntax": "^0.4.0",
+ "@atproto/common-web": "^0.4.18",
+ "@atproto/syntax": "^0.5.0",
"iso-datestring-validator": "^2.2.2",
"multiformats": "^9.9.0",
"zod": "^3.23.8"
}
},
"node_modules/@atproto/syntax": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz",
- "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==",
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz",
+ "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.8.1"
}
},
"node_modules/@atproto/xrpc": {
- "version": "0.6.12",
- "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.12.tgz",
- "integrity": "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w==",
+ "version": "0.7.7",
+ "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz",
+ "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==",
"license": "MIT",
"dependencies": {
- "@atproto/lexicon": "^0.4.10",
+ "@atproto/lexicon": "^0.6.0",
"zod": "^3.23.8"
}
},
@@ -176,9 +167,9 @@
}
},
"node_modules/@borewit/text-codec": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz",
- "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz",
+ "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -330,9 +321,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
- "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
+ "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -772,12 +763,12 @@
"license": "MIT"
},
"node_modules/@fedify/debugger": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.0.3.tgz",
- "integrity": "sha512-Crs9ibtJ1kWUDAetZ9GtoATSj3Hu+KRhgwQuTW3sb+kqgXJhhKNoXFteC3T713A+f7ZFaWfy1rMSrp5HSlrIHA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.1.1.tgz",
+ "integrity": "sha512-n3fdo3u3uZwng+4NkgkDReyKg1tJhJ4B+5qX4AA49p+NJUdOBJYihmuovoXdZ0fxO2E3UK+X0o0q+OFOdMRBKw==",
"dependencies": {
"@js-temporal/polyfill": "^0.5.1",
- "@logtape/logtape": "^2.0.0",
+ "@logtape/logtape": "^2.0.5",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/context-async-hooks": "^2.5.0",
"@opentelemetry/core": "^2.5.0",
@@ -785,24 +776,24 @@
"hono": "^4.0.0"
},
"peerDependencies": {
- "@fedify/fedify": "^2.0.3"
+ "@fedify/fedify": "^2.1.1"
}
},
"node_modules/@fedify/fedify": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.0.3.tgz",
- "integrity": "sha512-XQ9NevJz/Ch7vKVPU+sEn5ybRllQULcX8gkNUS+F2LoPyq4b1qqRwvcLXcWA+RNXH551jyWJFGo36ufEo4Xuyw==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.1.1.tgz",
+ "integrity": "sha512-DHhtrfBrg899Voi6W9rjDr6QDFcRQi/Ur7mmttGFnVJa5fVnXbOCZaQ9Bb9di8559Zbn+xX3sqWKJfW2v8lvAQ==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
],
"license": "MIT",
"dependencies": {
- "@fedify/vocab": "2.0.3",
- "@fedify/vocab-runtime": "2.0.3",
- "@fedify/webfinger": "2.0.3",
+ "@fedify/vocab": "2.1.1",
+ "@fedify/vocab-runtime": "2.1.1",
+ "@fedify/webfinger": "2.1.1",
"@js-temporal/polyfill": "^0.5.1",
- "@logtape/logtape": "^2.0.0",
+ "@logtape/logtape": "^2.0.5",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^2.5.0",
"@opentelemetry/sdk-trace-base": "^2.5.0",
@@ -811,7 +802,6 @@
"es-toolkit": "1.43.0",
"json-canon": "^1.0.1",
"jsonld": "^9.0.0",
- "multicodec": "^3.2.1",
"structured-field-values": "^2.0.4",
"uri-template-router": "^1.0.0",
"url-template": "^3.1.1",
@@ -824,9 +814,9 @@
}
},
"node_modules/@fedify/redis": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.0.3.tgz",
- "integrity": "sha512-viTE9tDPZl0+3tHOG0eNKxXVDCoJDvK1u1WfcTDC9bVtNe9NsbVRshCc/r01/YfTxuoS9aHSNWUzgpm1nV21Fg==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.1.1.tgz",
+ "integrity": "sha512-ZJTbZ555RErVJg7RUIYIY1lzX2Ihfsy3bqnX58AUQTngYmfwgBAl9FAv275shjiSuNVNhWCr0pyjAdnQTb18YA==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
@@ -834,34 +824,33 @@
"license": "MIT",
"dependencies": {
"@js-temporal/polyfill": "^0.5.1",
- "@logtape/logtape": "^2.0.0"
+ "@logtape/logtape": "^2.0.5"
},
"peerDependencies": {
- "@fedify/fedify": "^2.0.3",
+ "@fedify/fedify": "^2.1.1",
"ioredis": "^5.8.2"
}
},
"node_modules/@fedify/vocab": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.0.3.tgz",
- "integrity": "sha512-dfIsU9+2YHYLk9jBGspFyWBGzkwfxXf/d2da3yc/oT4ZaZiPt7KZjKeTuws0xsiBCAewwz/4v5ASoTa/aVHAeA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.1.1.tgz",
+ "integrity": "sha512-Jy5t4jAzrR0+sF0b+aQRuvZC4pvsEsutjeayJf6RVTNSs+QugvbyCz7k+GXtMvofnpP9QjuK6nUKal+c/3qfiQ==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
],
"license": "MIT",
"dependencies": {
- "@fedify/vocab-runtime": "2.0.3",
- "@fedify/vocab-tools": "2.0.3",
- "@fedify/webfinger": "2.0.3",
+ "@fedify/vocab-runtime": "2.1.1",
+ "@fedify/vocab-tools": "2.1.1",
+ "@fedify/webfinger": "2.1.1",
"@js-temporal/polyfill": "^0.5.1",
- "@logtape/logtape": "^2.0.0",
+ "@logtape/logtape": "^2.0.5",
"@multiformats/base-x": "^4.0.1",
"@opentelemetry/api": "^1.9.0",
"asn1js": "^3.0.6",
"es-toolkit": "1.43.0",
"jsonld": "^9.0.0",
- "multicodec": "^3.2.1",
"pkijs": "^3.3.3"
},
"engines": {
@@ -871,21 +860,21 @@
}
},
"node_modules/@fedify/vocab-runtime": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.0.3.tgz",
- "integrity": "sha512-1JHyWPVo0PF1XzZfr5bumw9OjR88ElYrM5/9/m2vpgSsJB6lSPXpbc53s79UVsAkavi1yNNgpnPTgSvhW3DFcA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.1.1.tgz",
+ "integrity": "sha512-W/R/+AOKld4S5HuwPf8bgT8dA+apNoaF8j0wVqFcj9/nCWYrTzWX8KfiHK2kOGxK1gK1ia+AtJJ/uFUcBpbc/A==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
],
"license": "MIT",
"dependencies": {
- "@logtape/logtape": "^2.0.0",
+ "@logtape/logtape": "^2.0.5",
"@multiformats/base-x": "^4.0.1",
"@opentelemetry/api": "^1.9.0",
"asn1js": "^3.0.6",
"byte-encodings": "^1.0.11",
- "multicodec": "^3.2.1",
+ "jsonld": "^9.0.0",
"pkijs": "^3.3.3"
},
"engines": {
@@ -895,9 +884,9 @@
}
},
"node_modules/@fedify/vocab-tools": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.0.3.tgz",
- "integrity": "sha512-H3a0j+1STsfn7Ccgjd4CcKl5mwgVevYFZaXUcbBhXTRv72MrrzWPi9K33kuZSpr1ULh4pXapoeXOidE2hxz33A==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.1.1.tgz",
+ "integrity": "sha512-c64ZKjeJjqllEQ0WWPF+5s5V2PADJoL63MV5HqR/wQVvOyBpMK/oOcjLYLtwmwAgqQY2HHDOQwYvsIbTTPujTA==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
@@ -916,17 +905,17 @@
}
},
"node_modules/@fedify/webfinger": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.0.3.tgz",
- "integrity": "sha512-KjCDtGXFBY+iAgrPpKhBHV7/gfJ9MoIu4x95Ukd0pCwxzXAfFqC4nTRINivJ2OuN2QKdqDgtATG2VrCRM+teVg==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.1.1.tgz",
+ "integrity": "sha512-yCtHWMA/sA1NZUvopuFQ4KEgKHHj129WyPdVSpOiWGneUw+p3sgf8ffO1oj13FYwiR3qvpIV8SMcJ9aFmOa6Ug==",
"funding": [
"https://opencollective.com/fedify",
"https://github.com/sponsors/dahlia"
],
"license": "MIT",
"dependencies": {
- "@fedify/vocab-runtime": "2.0.3",
- "@logtape/logtape": "^2.0.0",
+ "@fedify/vocab-runtime": "2.1.1",
+ "@logtape/logtape": "^2.0.5",
"@opentelemetry/api": "^1.9.0",
"es-toolkit": "1.43.0"
},
@@ -937,13 +926,10 @@
}
},
"node_modules/@gar/promise-retry": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.2.tgz",
- "integrity": "sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz",
+ "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==",
"license": "MIT",
- "dependencies": {
- "retry": "^0.13.1"
- },
"engines": {
"node": "^20.17.0 || >=22.9.0"
}
@@ -1040,6 +1026,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1056,6 +1045,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1072,6 +1064,9 @@
"cpu": [
"ppc64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1088,6 +1083,9 @@
"cpu": [
"riscv64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1104,6 +1102,9 @@
"cpu": [
"s390x"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1120,6 +1121,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1136,6 +1140,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1152,6 +1159,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1168,6 +1178,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1190,6 +1203,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1212,6 +1228,9 @@
"cpu": [
"ppc64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1234,6 +1253,9 @@
"cpu": [
"riscv64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1256,6 +1278,9 @@
"cpu": [
"s390x"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1278,6 +1303,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1300,6 +1328,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1322,6 +1353,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1415,12 +1449,12 @@
},
"node_modules/@indiekit/endpoint-auth": {
"name": "@rmdes/indiekit-endpoint-auth",
- "version": "1.0.0-beta.29",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-auth/-/indiekit-endpoint-auth-1.0.0-beta.29.tgz",
- "integrity": "sha512-NeFqwuYWhkKxvStaDnQQNTAoTl4c4aS7uzrIuos/b2lSA7rJLmGanhzVWzZ2Pr9jH6L4CSI3rPhY5BOhEwbSOQ==",
+ "version": "1.0.0-beta.30",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-auth/-/indiekit-endpoint-auth-1.0.0-beta.30.tgz",
+ "integrity": "sha512-uP5LdoDxU6qwoPuuPAqfO0S3M6JJDFC0V+OYJOkuMNp7Xo8GeITfXnHRjNOGAzgaWk/WAXDzK2uOEcwxis/vgw==",
"license": "MIT",
"dependencies": {
- "@indiekit/error": "^1.0.0-beta.25",
+ "@indiekit/error": "^1.0.0-beta.27",
"@indiekit/util": "^1.0.0-beta.25",
"bcrypt": "^6.0.0",
"express": "^5.0.0",
@@ -1434,12 +1468,12 @@
},
"node_modules/@indiekit/endpoint-files": {
"name": "@rmdes/indiekit-endpoint-files",
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-files/-/indiekit-endpoint-files-1.0.0.tgz",
- "integrity": "sha512-RAKg+ZSxEHEwCgtyRlLelsNe6TqHtpDUi/wx7qEpiFQ2OeHyJvQ2uQcVRLra9hiD8OsFFF6syUs42dX0C8Slkg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-files/-/indiekit-endpoint-files-1.0.2.tgz",
+ "integrity": "sha512-m6l7HDyM9WPBDG1OsP8aGNShjyOv3ipaXPMJZjM2XZbLz91UHwV9NCwlzjaxJhMnDC+9ZLfkA0b9kagdm8uhZQ==",
"license": "MIT",
"dependencies": {
- "@indiekit/error": "^1.0.0-beta.25",
+ "@indiekit/error": "^1.0.0-beta.27",
"express": "^5.0.0",
"express-validator": "^7.0.0"
},
@@ -1515,9 +1549,9 @@
},
"node_modules/@indiekit/endpoint-posts": {
"name": "@rmdes/indiekit-endpoint-posts",
- "version": "1.0.0-beta.44",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-posts/-/indiekit-endpoint-posts-1.0.0-beta.44.tgz",
- "integrity": "sha512-xGvsmqIOiQU0tpocDmNJw0CRvdj98zOVzpX+qzpPHBcYXSUZmbhRHd9/OaOD4dOWsmNT4dHYhmJ0OjTdUMTtUw==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-posts/-/indiekit-endpoint-posts-1.0.0.tgz",
+ "integrity": "sha512-AHJgiG3pRpMZNZD+hF3QuwBbVoJecHL+9bWl6o8zpdVw0WtW0U+pnP0ArgcuGqiuJIYlI9z8oFeOp5Csy0WOfw==",
"license": "MIT",
"dependencies": {
"@indiekit/endpoint-micropub": "^1.0.0-beta.27",
@@ -1535,12 +1569,12 @@
},
"node_modules/@indiekit/endpoint-share": {
"name": "@rmdes/indiekit-endpoint-share",
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-share/-/indiekit-endpoint-share-1.0.2.tgz",
- "integrity": "sha512-rIjdvto0k97zEwDxExsXBCkusbSP/Dbn9CEHYh2675XWFZuzCdZtq+oO9SrMui5dLVl4MuGIxfGnhinCgE+n0A==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-share/-/indiekit-endpoint-share-1.0.3.tgz",
+ "integrity": "sha512-xAB8U2rLAK8RHhp8rgmqeJGrUKjLBdtcvvhV4QbF7H6zfZIvy9zA7k+sU0oGRqkCGAvhqIMkD2ZaUpepGLMm/A==",
"license": "MIT",
"dependencies": {
- "@indiekit/error": "^1.0.0-beta.25",
+ "@indiekit/error": "^1.0.0-beta.27",
"express": "^5.0.0",
"express-validator": "^7.0.0"
},
@@ -1577,13 +1611,13 @@
},
"node_modules/@indiekit/frontend": {
"name": "@rmdes/indiekit-frontend",
- "version": "1.0.0-beta.38",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-frontend/-/indiekit-frontend-1.0.0-beta.38.tgz",
- "integrity": "sha512-rgk9412YbomBYsp2a1l3vs9Mvbn2ZPMSlG+mpEQJJixYeq+Zeec9eftsYqiGfF4KiMoJWQu9QFg2rqj3uv0V+A==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-frontend/-/indiekit-frontend-1.0.0-beta.39.tgz",
+ "integrity": "sha512-ObqgNIsVzNn1Hg2TzSwe5Amt4cHbMpmDyhfyUmGPYf6w2vhMQ/MnDQa+oLZXozIenpOKHXO0XyUFzlyxq4kS7w==",
"license": "MIT",
"dependencies": {
"@accessible-components/tag-input": "^0.2.0",
- "@indiekit/error": "^1.0.0-beta.25",
+ "@indiekit/error": "^1.0.0-beta.27",
"@indiekit/util": "^1.0.0-beta.25",
"color": "^5.0.0",
"easymde": "^2.18.0",
@@ -1816,9 +1850,9 @@
"license": "MIT"
},
"node_modules/@logtape/logtape": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.4.tgz",
- "integrity": "sha512-Z4COeAMdedcBFuFkXaPFvDPOVuHoEom1hwNnPCIkSyojyikuNguplwPoSG+kZthWrS7GiOJo1USQyjWwIFfTKA==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.5.tgz",
+ "integrity": "sha512-UizDkh20ZPJVOddRxG1F77WhHdlNl/sbQgoO8T534R7XvUBMAJ9En9f35u+meW2tRsNLvjz6R87Zanwf53tspQ==",
"funding": [
"https://github.com/sponsors/dahlia"
],
@@ -1918,9 +1952,9 @@
}
},
"node_modules/@npmcli/agent/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -1938,19 +1972,28 @@
"node": "^20.17.0 || >=22.9.0"
}
},
+ "node_modules/@npmcli/redact": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz",
+ "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
"node_modules/@opentelemetry/api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
- "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
"license": "Apache-2.0",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@opentelemetry/context-async-hooks": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.0.tgz",
- "integrity": "sha512-L8UyDwqpTcbkIK5cgwDRDYDoEhQoj8wp8BwsO19w3LB1Z41yEQm2VJyNfAi9DrLP/YTqXqWpKHyZfR9/tFYo1Q==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.1.tgz",
+ "integrity": "sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==",
"license": "Apache-2.0",
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -1960,9 +2003,9 @@
}
},
"node_modules/@opentelemetry/core": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.0.tgz",
- "integrity": "sha512-HLM1v2cbZ4TgYN6KEOj+Bbj8rAKriOdkF9Ed3tG25FoprSiQl7kYc+RRT6fUZGOvx0oMi5U67GoFdT+XUn8zEg==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz",
+ "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
@@ -1975,12 +2018,12 @@
}
},
"node_modules/@opentelemetry/resources": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.0.tgz",
- "integrity": "sha512-D4y/+OGe3JSuYUCBxtH5T9DSAWNcvCb/nQWIga8HNtXTVPQn59j0nTBAgaAXxUVBDl40mG3Tc76b46wPlZaiJQ==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz",
+ "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.0",
+ "@opentelemetry/core": "2.6.1",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
@@ -1991,13 +2034,13 @@
}
},
"node_modules/@opentelemetry/sdk-trace-base": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.0.tgz",
- "integrity": "sha512-g/OZVkqlxllgFM7qMKqbPV9c1DUPhQ7d4n3pgZFcrnrNft9eJXZM2TNHTPYREJBrtNdRytYyvwjgL5geDKl3EQ==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz",
+ "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.0",
- "@opentelemetry/resources": "2.6.0",
+ "@opentelemetry/core": "2.6.1",
+ "@opentelemetry/resources": "2.6.1",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
@@ -2138,6 +2181,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2158,6 +2204,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2178,6 +2227,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2198,6 +2250,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2218,6 +2273,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2238,6 +2296,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2356,15 +2417,16 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-activitypub": {
- "version": "3.6.8",
- "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-activitypub.git#f029c3128e4f47a4213c01264b816d76c170095e",
+ "version": "3.10.0",
+ "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-activitypub.git#b54146ce5b1d1eed1003ca2d046e746e90381b43",
"license": "MIT",
"dependencies": {
- "@fedify/debugger": "^2.0.0",
- "@fedify/fedify": "^2.0.0",
- "@fedify/redis": "^2.0.0",
+ "@fedify/debugger": "^2.1.0",
+ "@fedify/fedify": "^2.1.0",
+ "@fedify/redis": "^2.1.0",
"@js-temporal/polyfill": "^0.5.0",
"express": "^5.0.0",
+ "express-rate-limit": "^7.5.1",
"ioredis": "^5.9.3",
"sanitize-html": "^2.13.1",
"unfurl.js": "^6.4.0"
@@ -2378,6 +2440,21 @@
"@indiekit/frontend": "^1.0.0-beta.25"
}
},
+ "node_modules/@rmdes/indiekit-endpoint-activitypub/node_modules/express-rate-limit": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
+ "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
"node_modules/@rmdes/indiekit-endpoint-auth": {
"version": "1.0.0-beta.25",
"resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-auth/-/indiekit-endpoint-auth-1.0.0-beta.25.tgz",
@@ -2398,7 +2475,7 @@
},
"node_modules/@rmdes/indiekit-endpoint-blogroll": {
"version": "1.0.23",
- "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-blogroll.git#381b0397a55f75747af28c7221af31af3cc32c3f",
+ "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-blogroll.git#f5a62b966d1881df58014505b410547a38b3a5fb",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2416,9 +2493,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-comments": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-comments/-/indiekit-endpoint-comments-1.0.11.tgz",
- "integrity": "sha512-qUy0wr4Vc6pSnQmSLsBacm/MO3qZL02Ezg1uQiz9uM2fIUEx1dxv/G3i864ZyaCf7oW2pSOoUNtpyI3YybG+PQ==",
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-comments/-/indiekit-endpoint-comments-1.0.13.tgz",
+ "integrity": "sha512-TvWKSMsx4oRLnboQpHFzrwIif+mjKxr/2xEnQwFdnpMKiq8ED/8Hwu0UJiBu7GjSqL2EHS3J5LDbEu+5FT0twA==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2430,9 +2507,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-conversations": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-conversations/-/indiekit-endpoint-conversations-2.2.0.tgz",
- "integrity": "sha512-o5ZiKMjcqAB1TDIvdzxPBH8Pai0vTEefyKJWhNEGt0da3ooZ9fkAuBg10vFyhMYFIVRLjRxkzKZKvrhb2e6MhQ==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-conversations/-/indiekit-endpoint-conversations-2.3.1.tgz",
+ "integrity": "sha512-/9wpbL/WcSNtJ6YVIk+2nzcgY2XVMdUA+BqYLEQIG13V4W1d80ujqEOXbZhV+bVqENGYwZtfHLx8EA9lXTNhxA==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2443,9 +2520,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-cv": {
- "version": "1.0.24",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-cv/-/indiekit-endpoint-cv-1.0.24.tgz",
- "integrity": "sha512-S6af420v6FYZspTMVr4yU6pjiyY+TszlcktMn3/Wpj4xW7vlFqGR/LC4RKhH0zRHI2A5koMVXVfl30bu1/POPw==",
+ "version": "1.0.25",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-cv/-/indiekit-endpoint-cv-1.0.25.tgz",
+ "integrity": "sha512-cg/JtTQRE/g/ce7svGvfvhBo3br+TvBxw4f/sUcIWAIGw1me12ldk5RBxq4OCwjN7W6LRkXYlAIphhdD8+lKvQ==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2460,9 +2537,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-funkwhale": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-funkwhale/-/indiekit-endpoint-funkwhale-1.0.11.tgz",
- "integrity": "sha512-AbPobqh5P/2SG8l+fJ4IGJmelGr0u0lfNsNHOxkOfhEckC5iIELssj/1e/E+eW42y58StpzsdQ+xqPIT5ur9PA==",
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-funkwhale/-/indiekit-endpoint-funkwhale-1.0.12.tgz",
+ "integrity": "sha512-B/dnnIRljD54mVN8CCBpcsNC02U2i/DtM+0O6V2YizTkWVV8kFG/JzQYGedV3EDDpFckCrSq+KAi5OcQGfbdLg==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2476,9 +2553,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-github": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-github/-/indiekit-endpoint-github-1.2.3.tgz",
- "integrity": "sha512-HTKEcwvR21u/LOJD5e3iEbeAnm3OLkxok8TG3/32DxdVdTtN4MBOgtZP58bM7IAvIBIobEVyyp0Rpyn7EG0odQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-github/-/indiekit-endpoint-github-1.2.5.tgz",
+ "integrity": "sha512-LxACv/uMUQHEFItW4JqlgjqEUCeW9FnVYfVTesPbPTu0d7SwM0eh0q3XzvgsGhDc6luCwbJYaxa6wbkbfI/Mbw==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2492,9 +2569,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-homepage": {
- "version": "1.0.22",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-homepage/-/indiekit-endpoint-homepage-1.0.22.tgz",
- "integrity": "sha512-lvOJOL/OK7VfibutCaHosP8Sk3kPChavHuo4ufYmigsxSI62lHsAUZbkpKrdEY6qbxJv0dr/b9Yax6Hw1jZqIA==",
+ "version": "1.0.23",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-homepage/-/indiekit-endpoint-homepage-1.0.23.tgz",
+ "integrity": "sha512-J9wpfnLIuJ74fUV212cM11RexDrQPMRPxDfbC9C6nWWy1hZTuzSqKoEOstykuALZvRlC7xHwPfyLBf842oROlg==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2525,8 +2602,8 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-microsub": {
- "version": "1.0.45",
- "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-microsub.git#1bb80588fdd154e9c74628d69aa3118ff9620422",
+ "version": "1.0.49",
+ "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-microsub.git#f3e8648fedfc3fa6a66a5d24c4635ab96bbc414d",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2546,9 +2623,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-podroll": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-podroll/-/indiekit-endpoint-podroll-1.0.11.tgz",
- "integrity": "sha512-ShCVRfeGntKhXUtCDOIKbAAWBHM+ssj+QVCCGNq7rFL5tSdFEKFtwVCIYS/nvrQztzN1tCxT5AjjGGrXc9Xz9g==",
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-podroll/-/indiekit-endpoint-podroll-1.0.13.tgz",
+ "integrity": "sha512-FyQyhCPRnjpCh5xu+sWocrO8NuuJ5NNmWyIhdrpQWlM49QX6mDfSXCoAFmfrETSnPZa+WQ0EsDFk9BTra4B/2A==",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2564,9 +2641,9 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-posts": {
- "version": "1.0.0-beta.44",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-posts/-/indiekit-endpoint-posts-1.0.0-beta.44.tgz",
- "integrity": "sha512-xGvsmqIOiQU0tpocDmNJw0CRvdj98zOVzpX+qzpPHBcYXSUZmbhRHd9/OaOD4dOWsmNT4dHYhmJ0OjTdUMTtUw==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-endpoint-posts/-/indiekit-endpoint-posts-1.0.0.tgz",
+ "integrity": "sha512-AHJgiG3pRpMZNZD+hF3QuwBbVoJecHL+9bWl6o8zpdVw0WtW0U+pnP0ArgcuGqiuJIYlI9z8oFeOp5Csy0WOfw==",
"license": "MIT",
"dependencies": {
"@indiekit/endpoint-micropub": "^1.0.0-beta.27",
@@ -2583,8 +2660,8 @@
}
},
"node_modules/@rmdes/indiekit-endpoint-readlater": {
- "version": "1.0.5",
- "resolved": "git+ssh://git@github.com/rmdes/indiekit-endpoint-readlater.git#ee09c683a3c1ff91357cfd0b17d4151299583290",
+ "version": "1.0.6",
+ "resolved": "git+ssh://git@github.com/rmdes/indiekit-endpoint-readlater.git#2d1493c14cbb0e0a437ddc2b98ab03cab57d6342",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2668,7 +2745,7 @@
},
"node_modules/@rmdes/indiekit-endpoint-youtube": {
"version": "1.3.0",
- "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-youtube.git#ae0b87940177ab93b1b222049c6f830a3935143c",
+ "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-youtube.git#b8e2b6472f9dda9371ab0ed07b9d09d7c1783d6a",
"license": "MIT",
"dependencies": {
"@indiekit/error": "^1.0.0-beta.25",
@@ -2705,18 +2782,18 @@
}
},
"node_modules/@rmdes/indiekit-syndicator-bluesky": {
- "version": "1.0.19",
- "resolved": "https://registry.npmjs.org/@rmdes/indiekit-syndicator-bluesky/-/indiekit-syndicator-bluesky-1.0.19.tgz",
- "integrity": "sha512-+vbiA+B20TJqQumd+591Xb1lip1LbuE1jXwcjx2bQ6ZrZcgd8jeFIbeZ3IRkQ0Yg6ru7gYsYV+byTbmusu3eIQ==",
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/@rmdes/indiekit-syndicator-bluesky/-/indiekit-syndicator-bluesky-1.0.20.tgz",
+ "integrity": "sha512-YlNDgVK1GamUAe40nUPxfdpeBibuUZrAL5fN0v2n4jtoWUrlpJnIfdRdDDG2B7O4DFPNSs4PtBJ6wY5Y+xhpjg==",
"license": "MIT",
"dependencies": {
- "@atproto/api": "^0.14.0",
+ "@atproto/api": "^0.19.3",
"html-to-text": "^9.0.0",
"jsdom": "^24.0.0",
"sharp": "^0.33.0"
},
"peerDependencies": {
- "@indiekit/error": "^1.0.0-beta.25",
+ "@indiekit/error": "^1.0.0-beta.27",
"@indiekit/indiekit": "1.x",
"@indiekit/util": "^1.0.0-beta.25"
}
@@ -2804,6 +2881,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2820,6 +2900,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2836,6 +2919,9 @@
"cpu": [
"s390x"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2852,6 +2938,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2868,6 +2957,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2884,6 +2976,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2900,6 +2995,9 @@
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -2922,6 +3020,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -2944,6 +3045,9 @@
"cpu": [
"s390x"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -2966,6 +3070,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -2988,6 +3095,9 @@
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -3010,6 +3120,9 @@
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -3548,9 +3661,9 @@
}
},
"node_modules/cacache": {
- "version": "20.0.3",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz",
- "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==",
+ "version": "20.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz",
+ "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==",
"license": "ISC",
"dependencies": {
"@npmcli/fs": "^5.0.0",
@@ -3562,17 +3675,16 @@
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"p-map": "^7.0.2",
- "ssri": "^13.0.0",
- "unique-filename": "^5.0.0"
+ "ssri": "^13.0.0"
},
"engines": {
"node": "^20.17.0 || >=22.9.0"
}
},
"node_modules/cacache/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -3655,6 +3767,12 @@
"canonicalize": "bin/canonicalize.js"
}
},
+ "node_modules/change-case": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz",
+ "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==",
+ "license": "MIT"
+ },
"node_modules/cheerio": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
@@ -4739,9 +4857,9 @@
}
},
"node_modules/feedparser": {
- "version": "2.2.10",
- "resolved": "https://registry.npmjs.org/feedparser/-/feedparser-2.2.10.tgz",
- "integrity": "sha512-WoAOooa61V8/xuKMi2pEtK86qQ3ZH/M72EEGdqlOTxxb3m6ve1NPvZcmPFs3wEDfcBbFLId2GqZ4YjsYi+h1xA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/feedparser/-/feedparser-2.3.0.tgz",
+ "integrity": "sha512-vLmlMV/7pLWpsPmzfvmtYxo+kmkQgjCZPDmB/H+0eMXG29NNssvlxSbpLzJ0LHRyLsGW8ewFOpfRInHjMUgX1g==",
"license": "MIT",
"dependencies": {
"addressparser": "^1.0.1",
@@ -4752,7 +4870,7 @@
"lodash.uniq": "^4.5.0",
"mri": "^1.1.5",
"readable-stream": "^2.3.7",
- "sax": "^1.2.4"
+ "sax": ">=1.2.4 <1.4.4"
},
"bin": {
"feedparser": "bin/feedparser.js"
@@ -4785,9 +4903,9 @@
}
},
"node_modules/file-type": {
- "version": "21.3.2",
- "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.2.tgz",
- "integrity": "sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==",
+ "version": "21.3.4",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz",
+ "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==",
"license": "MIT",
"dependencies": {
"@tokenizer/inflate": "^0.4.1",
@@ -5008,9 +5126,9 @@
"license": "ISC"
},
"node_modules/h3": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.6.tgz",
- "integrity": "sha512-oi15ESLW5LRthZ+qPCi5GNasY/gvynSKUQxgiovrY63bPAtG59wtM+LSrlcwvOHAXzGrXVLnI97brbkdPF9WoQ==",
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.10.tgz",
+ "integrity": "sha512-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg==",
"license": "MIT",
"dependencies": {
"cookie-es": "^1.2.2",
@@ -5073,9 +5191,9 @@
}
},
"node_modules/hono": {
- "version": "4.12.7",
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz",
- "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==",
+ "version": "4.12.9",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz",
+ "integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@@ -5292,15 +5410,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8.19"
- }
- },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -5308,9 +5417,9 @@
"license": "ISC"
},
"node_modules/ioredis": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.0.tgz",
- "integrity": "sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==",
+ "version": "5.10.1",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz",
+ "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==",
"license": "MIT",
"dependencies": {
"@ioredis/commands": "1.5.1",
@@ -6163,13 +6272,14 @@
}
},
"node_modules/make-fetch-happen": {
- "version": "15.0.4",
- "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.4.tgz",
- "integrity": "sha512-vM2sG+wbVeVGYcCm16mM3d5fuem9oC28n436HjsGO3LcxoTI8LNVa4rwZDn3f76+cWyT4GGJDxjTYU1I2nr6zw==",
+ "version": "15.0.5",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz",
+ "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==",
"license": "ISC",
"dependencies": {
"@gar/promise-retry": "^1.0.0",
"@npmcli/agent": "^4.0.0",
+ "@npmcli/redact": "^4.0.0",
"cacache": "^20.0.1",
"http-cache-semantics": "^4.1.1",
"minipass": "^7.0.2",
@@ -6552,9 +6662,9 @@
}
},
"node_modules/mlly": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz",
- "integrity": "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==",
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz",
+ "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==",
"license": "MIT",
"dependencies": {
"acorn": "^8.16.0",
@@ -6643,17 +6753,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/multicodec": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-3.2.1.tgz",
- "integrity": "sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw==",
- "deprecated": "This module has been superseded by the multiformats module",
- "license": "MIT",
- "dependencies": {
- "uint8arrays": "^3.0.0",
- "varint": "^6.0.0"
- }
- },
"node_modules/multiformats": {
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
@@ -7144,9 +7243,9 @@
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -7207,9 +7306,9 @@
}
},
"node_modules/pkijs": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz",
- "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz",
+ "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==",
"license": "BSD-3-Clause",
"dependencies": {
"@noble/hashes": "1.4.0",
@@ -7512,15 +7611,6 @@
"node": ">=4"
}
},
- "node_modules/retry": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
- "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
@@ -7608,19 +7698,31 @@
"license": "MIT"
},
"node_modules/sanitize-html": {
- "version": "2.17.1",
- "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.1.tgz",
- "integrity": "sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==",
+ "version": "2.17.2",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.2.tgz",
+ "integrity": "sha512-EnffJUl46VE9uvZ0XeWzObHLurClLlT12gsOk1cHyP2Ol1P0BnBnsXmShlBmWVJM+dKieQI68R0tsPY5m/B+Jg==",
"license": "MIT",
"dependencies": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
- "htmlparser2": "^8.0.0",
+ "htmlparser2": "^10.1.0",
"is-plain-object": "^5.0.0",
"parse-srcset": "^1.0.2",
"postcss": "^8.3.11"
}
},
+ "node_modules/sanitize-html/node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/sanitize-html/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -7634,9 +7736,9 @@
}
},
"node_modules/sanitize-html/node_modules/htmlparser2": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
- "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
+ "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -7648,18 +7750,15 @@
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
- "domutils": "^3.0.1",
- "entities": "^4.4.0"
+ "domutils": "^3.2.2",
+ "entities": "^7.0.1"
}
},
"node_modules/sax": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
- "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=11.0.0"
- }
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
+ "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
+ "license": "BlueOak-1.0.0"
},
"node_modules/saxes": {
"version": "6.0.0",
@@ -7942,12 +8041,6 @@
"node": ">=22"
}
},
- "node_modules/snakecase-keys/node_modules/change-case": {
- "version": "5.4.4",
- "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz",
- "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==",
- "license": "MIT"
- },
"node_modules/snakecase-keys/node_modules/map-obj": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.2.tgz",
@@ -8087,9 +8180,9 @@
}
},
"node_modules/strtok3": {
- "version": "10.3.4",
- "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz",
- "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==",
+ "version": "10.3.5",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz",
+ "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==",
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0"
@@ -8142,6 +8235,15 @@
"node": ">=16"
}
},
+ "node_modules/svgo/node_modules/sax": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -8173,9 +8275,9 @@
}
},
"node_modules/tar": {
- "version": "7.5.11",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz",
- "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==",
+ "version": "7.5.12",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.12.tgz",
+ "integrity": "sha512-9TsuLcdhOn4XztcQqhNyq1KOwOOED/3k58JAvtULiYqbO8B/0IBAAIE1hj0Svmm58k27TmcigyDI0deMlgG3uw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@@ -8292,9 +8394,9 @@
}
},
"node_modules/type-fest": {
- "version": "5.4.4",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz",
- "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz",
+ "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==",
"license": "(MIT OR CC0-1.0)",
"dependencies": {
"tagged-tag": "^1.0.0"
@@ -8366,9 +8468,9 @@
"license": "MIT"
},
"node_modules/undici": {
- "version": "7.24.2",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.2.tgz",
- "integrity": "sha512-P9J1HWYV/ajFr8uCqk5QixwiRKmB1wOamgS0e+o2Z4A44Ej2+thFVRLG/eA7qprx88XXhnV5Bl8LHXTURpzB3Q==",
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.5.tgz",
+ "integrity": "sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
@@ -8478,30 +8580,6 @@
"integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==",
"license": "MIT"
},
- "node_modules/unique-filename": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz",
- "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==",
- "license": "ISC",
- "dependencies": {
- "unique-slug": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/unique-slug": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz",
- "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==",
- "license": "ISC",
- "dependencies": {
- "imurmurhash": "^0.1.4"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
@@ -8617,9 +8695,9 @@
}
},
"node_modules/unstorage/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -8709,12 +8787,6 @@
"node": ">= 0.10"
}
},
- "node_modules/varint": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
- "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
- "license": "MIT"
- },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -8811,9 +8883,9 @@
"license": "ISC"
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -8900,9 +8972,9 @@
}
},
"node_modules/yaml": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
- "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
diff --git a/package.json b/package.json
index f59059aa..477d1b65 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,8 @@
"description": "",
"main": "index.js",
"scripts": {
- "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-ap-like-note-dispatcher.mjs && node scripts/patch-ap-like-activity-id.mjs && node scripts/patch-ap-like-activity-dispatcher.mjs && node scripts/patch-ap-url-lookup-api-like.mjs && node scripts/patch-ap-repost-commentary.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-remove-federation-diag.mjs",
- "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-ap-like-note-dispatcher.mjs && node scripts/patch-ap-like-activity-id.mjs && node scripts/patch-ap-like-activity-dispatcher.mjs && node scripts/patch-ap-url-lookup-api-like.mjs && node scripts/patch-ap-repost-commentary.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-remove-federation-diag.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs",
+ "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-skip-draft-syndication.mjs",
+ "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-microsub-compose-draft-guard.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-skip-draft-syndication.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
@@ -36,7 +36,7 @@
"@rmdes/indiekit-endpoint-github": "^1.2.3",
"@rmdes/indiekit-endpoint-homepage": "^1.0.22",
"@rmdes/indiekit-endpoint-lastfm": "^1.0.12",
- "@rmdes/indiekit-endpoint-microsub": "github:svemagie/indiekit-endpoint-microsub#bookmarks-import",
+ "@rmdes/indiekit-endpoint-microsub": "github:svemagie/indiekit-endpoint-microsub",
"@rmdes/indiekit-endpoint-podroll": "^1.0.11",
"@rmdes/indiekit-endpoint-posts": "^1.0.0-beta.44",
"@rmdes/indiekit-endpoint-readlater": "github:rmdes/indiekit-endpoint-readlater",
diff --git a/scripts/patch-ap-allow-private-address.mjs b/scripts/patch-ap-allow-private-address.mjs
deleted file mode 100644
index 9118b8a4..00000000
--- a/scripts/patch-ap-allow-private-address.mjs
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * Patch: allow Fedify to fetch URLs that resolve to private IP addresses.
- *
- * Root cause:
- * blog.giersig.eu resolves to 10.100.0.10 (a private RFC-1918 address)
- * from within the home network where the indiekit server runs. When a
- * remote Fediverse server sends an activity (Like, Announce, etc.) whose
- * object URL points to blog.giersig.eu, Fedify tries to dereference that
- * URL to validate the object. Its built-in SSRF guard calls
- * validatePublicUrl(), sees the resolved IP is private, and throws:
- *
- * Disallowed private URL: 'https://blog.giersig.eu/likes/ed6d1/'
- * Invalid or private address: 10.100.0.10
- *
- * This causes WebFinger lookups and lookupObject() calls for own-site URLs
- * to fail, producing ERR-level noise in the log and breaking thread loading
- * in the ActivityPub reader for local posts.
- *
- * Fix:
- * Pass allowPrivateAddress: true to createFederation. This disables the
- * SSRF IP check so Fedify can dereference own-site URLs. The network-level
- * solution (split-horizon DNS returning the public IP inside the LAN) is
- * cleaner but requires router/DNS changes outside the codebase.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
-];
-
-const MARKER = "// allow private address fix";
-
-const patchSpecs = [
- // Case 1: v2.15+ — signatureTimeWindow present, upstream comment style (no marker suffix)
- {
- name: "upstream-v2.15-with-signature-time-window",
- oldSnippet: ` const federation = createFederation({
- kv,
- queue,
- // Accept signatures up to 12 h old.
- // Mastodon retries failed deliveries with the original signature, which
- // can be hours old by the time the delivery succeeds.
- signatureTimeWindow: { hours: 12 },
- });`,
- newSnippet: ` const federation = createFederation({
- kv,
- queue,
- // Accept signatures up to 12 h old.
- // Mastodon retries failed deliveries with the original signature, which
- // can be hours old by the time the delivery succeeds.
- signatureTimeWindow: { hours: 12 },
- // Allow fetching own-site URLs that resolve to private IPs. // allow private address fix
- // blog.giersig.eu resolves to 10.100.0.10 on the home LAN. Without this,
- // Fedify's SSRF guard blocks lookupObject() / WebFinger for own posts.
- allowPrivateAddress: true,
- });`,
- },
- // Case 2: signatureTimeWindow present with old marker comment style
- {
- name: "with-signature-time-window-marker",
- oldSnippet: ` const federation = createFederation({
- kv,
- queue,
- // Accept signatures up to 12 h old. // signature time window fix
- // Mastodon retries failed deliveries with the original signature, which
- // can be hours old by the time the delivery succeeds.
- signatureTimeWindow: { hours: 12 },
- });`,
- newSnippet: ` const federation = createFederation({
- kv,
- queue,
- // Accept signatures up to 12 h old. // signature time window fix
- // Mastodon retries failed deliveries with the original signature, which
- // can be hours old by the time the delivery succeeds.
- signatureTimeWindow: { hours: 12 },
- // Allow fetching own-site URLs that resolve to private IPs. // allow private address fix
- // blog.giersig.eu resolves to 10.100.0.10 on the home LAN. Without this,
- // Fedify's SSRF guard blocks lookupObject() / WebFinger for own posts.
- allowPrivateAddress: true,
- });`,
- },
- // Case 3: fresh install without signatureTimeWindow — add both
- {
- name: "fresh-without-signature-time-window",
- oldSnippet: ` const federation = createFederation({
- kv,
- queue,
- });`,
- newSnippet: ` const federation = createFederation({
- kv,
- queue,
- // Accept signatures up to 12 h old. // signature time window fix
- // Mastodon retries failed deliveries with the original signature, which
- // can be hours old by the time the delivery succeeds.
- signatureTimeWindow: { hours: 12 },
- // Allow fetching own-site URLs that resolve to private IPs. // allow private address fix
- // blog.giersig.eu resolves to 10.100.0.10 on the home LAN. Without this,
- // Fedify's SSRF guard blocks lookupObject() / WebFinger for own posts.
- allowPrivateAddress: true,
- });`,
- },
-];
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER) || source.includes("allowPrivateAddress")) {
- continue;
- }
-
- let applied = false;
- for (const spec of patchSpecs) {
- if (!source.includes(spec.oldSnippet)) continue;
- const updated = source.replace(spec.oldSnippet, spec.newSnippet);
- if (updated === source) continue;
- await writeFile(filePath, updated, "utf8");
- patched += 1;
- applied = true;
- console.log(`[postinstall] Applied patch-ap-allow-private-address (${spec.name}) to ${filePath}`);
- break;
- }
-
- if (!applied) {
- console.log(`[postinstall] patch-ap-allow-private-address: no matching snippet in ${filePath} — skipping`);
- }
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-allow-private-address: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-allow-private-address: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-allow-private-address: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-like-activity-dispatcher.mjs b/scripts/patch-ap-like-activity-dispatcher.mjs
deleted file mode 100644
index 20d7409b..00000000
--- a/scripts/patch-ap-like-activity-dispatcher.mjs
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Patch: register a Fedify Like activity dispatcher in federation-setup.js.
- *
- * Per ActivityPub §3.1, objects with an `id` MUST be dereferenceable at that
- * URI. The Like activities produced by jf2ToAS2Activity (after patch-ap-like-
- * activity-id.mjs adds an id) need a corresponding Fedify object dispatcher so
- * that fetching /activitypub/activities/like/{id} returns the Like activity.
- *
- * Fix:
- * Add federation.setObjectDispatcher(Like, ...) after the Article dispatcher
- * in setupObjectDispatchers(). The handler looks up the post, calls
- * jf2ToAS2Activity, and returns the Like if that's what was produced.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
-];
-
-const MARKER = "// ap-like-activity-dispatcher patch";
-
-const OLD_SNIPPET = ` // Article dispatcher
- federation.setObjectDispatcher(
- Article,
- \`\${mountPath}/objects/article/{+id}\`,
- async (ctx, { id }) => {
- const obj = await resolvePost(ctx, id);
- return obj instanceof Article ? obj : null;
- },
- );
-}`;
-
-const NEW_SNIPPET = ` // Article dispatcher
- federation.setObjectDispatcher(
- Article,
- \`\${mountPath}/objects/article/{+id}\`,
- async (ctx, { id }) => {
- const obj = await resolvePost(ctx, id);
- return obj instanceof Article ? obj : null;
- },
- );
-
- // Like activity dispatcher — makes AP-like activities dereferenceable (AP §3.1)
- // ap-like-activity-dispatcher patch
- federation.setObjectDispatcher(
- Like,
- \`\${mountPath}/activities/like/{+id}\`,
- async (ctx, { id }) => {
- if (!collections.posts || !publicationUrl) return null;
- const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`;
- const post = await collections.posts.findOne({
- "properties.url": { $in: [postUrl, postUrl + "/"] },
- });
- if (!post) return null;
- if (post?.properties?.["post-status"] === "draft") return null;
- if (post?.properties?.visibility === "unlisted") return null;
- if (post.properties?.deleted) return null;
- const actorUrl = ctx.getActorUri(handle).href;
- const activity = await jf2ToAS2Activity(post.properties, actorUrl, publicationUrl);
- return activity instanceof Like ? activity : null;
- },
- );
-}`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue; // already patched
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-like-activity-dispatcher: snippet not found in ${filePath}`);
- continue;
- }
-
- // Ensure Like is imported from @fedify/fedify/vocab (may be absent on fresh installs)
- if (!source.includes(" Like,")) {
- source = source.replace(" Note,", " Like,\n Note,");
- }
-
- source = source.replace(OLD_SNIPPET, NEW_SNIPPET);
- await writeFile(filePath, source, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-like-activity-dispatcher to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-like-activity-dispatcher: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-like-activity-dispatcher: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-like-activity-dispatcher: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-like-activity-id.mjs b/scripts/patch-ap-like-activity-id.mjs
deleted file mode 100644
index 0ef1ff0a..00000000
--- a/scripts/patch-ap-like-activity-id.mjs
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Patch: add a canonical `id` to the Like activity produced by jf2ToAS2Activity.
- *
- * Per ActivityPub §6.2.1, activities sent from a server SHOULD have an `id`
- * URI so that remote servers can dereference them. The current Like activity
- * has no `id`, which means it cannot be looked up by its URL.
- *
- * Fix:
- * In jf2-to-as2.js, derive the mount path from the actor URL and construct
- * a canonical id at /activitypub/activities/like/{post-path}.
- *
- * This enables:
- * - The Like activity dispatcher (patch-ap-like-activity-dispatcher.mjs) to
- * serve the Like at its canonical URL.
- * - Remote servers to dereference the Like activity by its id.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
-];
-
-const MARKER = "// ap-like-activity-id patch";
-
-const OLD_SNIPPET = ` return new Like({
- actor: actorUri,
- object: new URL(likeOfUrl),
- to: new URL("https://www.w3.org/ns/activitystreams#Public"),
- });`;
-
-const NEW_SNIPPET = ` // ap-like-activity-id patch
- // Derive mount path from actor URL (e.g. "/activitypub") so we can
- // construct the canonical id without needing mountPath in options.
- const actorPath = new URL(actorUrl).pathname; // e.g. "/activitypub/users/sven"
- const mp = actorPath.replace(/\\/users\\/[^/]+$/, ""); // → "/activitypub"
- const postRelPath = (properties.url || "")
- .replace(publicationUrl.replace(/\\/$/, ""), "")
- .replace(/^\\//, "")
- .replace(/\\/$/, ""); // e.g. "likes/9acc3"
- const likeActivityId = \`\${publicationUrl.replace(/\\/$/, "")}\${mp}/activities/like/\${postRelPath}\`;
- return new Like({
- id: new URL(likeActivityId),
- actor: actorUri,
- object: new URL(likeOfUrl),
- to: new URL("https://www.w3.org/ns/activitystreams#Public"),
- });`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue; // already patched
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-like-activity-id: snippet not found in ${filePath}`);
- continue;
- }
-
- source = source.replace(OLD_SNIPPET, NEW_SNIPPET);
- await writeFile(filePath, source, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-like-activity-id to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-like-activity-id: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-like-activity-id: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-like-activity-id: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-like-note-dispatcher.mjs b/scripts/patch-ap-like-note-dispatcher.mjs
deleted file mode 100644
index ce7666ce..00000000
--- a/scripts/patch-ap-like-note-dispatcher.mjs
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Patch: REVERT the wrong ap-like-note-dispatcher change in federation-setup.js.
- *
- * The previous version of this script served AP-likes as fake Notes at the
- * Note dispatcher URL, which violated ActivityPub semantics (Like activities
- * should not be served as Notes).
- *
- * This rewritten version removes that fake-Note block and restores the original
- * resolvePost() logic. The correct AP-compliant fixes are handled by:
- * - patch-ap-like-activity-id.mjs (adds id to Like activity)
- * - patch-ap-like-activity-dispatcher.mjs (registers Like object dispatcher)
- * - patch-ap-url-lookup-api-like.mjs (returns likeOf URL for AP-likes in widget)
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
-];
-
-// Marker from the old wrong patch — if this is present, we need to revert
-const WRONG_PATCH_MARKER = "// ap-like-note-dispatcher patch";
-
-// Clean up the Like import comment added by the old patch
-const OLD_IMPORT = ` Like, // Like import for ap-like-note-dispatcher patch`;
-const NEW_IMPORT = ` Like,`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (!source.includes(WRONG_PATCH_MARKER)) {
- // Already reverted (or never applied)
- continue;
- }
-
- let modified = false;
-
- // 1. Clean up Like import comment
- if (source.includes(OLD_IMPORT)) {
- source = source.replace(OLD_IMPORT, NEW_IMPORT);
- modified = true;
- }
-
- // 2. Remove fake Note block — use regex to avoid escaping issues with
- // unicode escapes and template literals inside the block.
- // Match from the opening comment through `return await activity.getObject();`
- const fakeNoteBlock = / \/\/ Only Create activities wrap Note\/Article objects\.\n[\s\S]*? return await activity\.getObject\(\);/;
- if (fakeNoteBlock.test(source)) {
- source = source.replace(
- fakeNoteBlock,
- ` // Only Create activities wrap Note/Article objects\n if (!(activity instanceof Create)) return null;\n return await activity.getObject();`,
- );
- modified = true;
- }
-
- if (modified) {
- await writeFile(filePath, source, "utf8");
- patched += 1;
- console.log(`[postinstall] Reverted ap-like-note-dispatcher patch in ${filePath}`);
- }
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-like-note-dispatcher: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-like-note-dispatcher: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-like-note-dispatcher: reverted ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-normalize-nested-tags.mjs b/scripts/patch-ap-normalize-nested-tags.mjs
deleted file mode 100644
index 12f29d43..00000000
--- a/scripts/patch-ap-normalize-nested-tags.mjs
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Patch: normalize nested/hierarchical tags before syndicating to the fediverse.
- *
- * Root cause:
- * Posts use nested tag notation like `on/art/music` or `art/music`. When
- * these are sent as ActivityPub Hashtag objects, the full path becomes the
- * hashtag name (e.g. #on/art/music), which is invalid on Mastodon and other
- * fediverse platforms. Clients display them as broken links or plain text.
- *
- * Fix:
- * Extract only the last segment of each slash-separated tag before building
- * the hashtag name. `on/art/music` → `music`, `art/music` → `music`.
- * The href still links to the full category path on the publication so
- * internal navigation is unaffected.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
-];
-
-const MARKER = "// normalize nested tags fix";
-
-const OLD_PLAIN = ` tags.push({
- type: "Hashtag",
- name: \`#\${cat.replace(/\\s+/g, "")}\`,
- href: \`\${publicationUrl}categories/\${encodeURIComponent(cat)}\`,
- });`;
-
-const NEW_PLAIN = ` tags.push({
- type: "Hashtag",
- name: \`#\${cat.split("/").at(-1).replace(/\\s+/g, "")}\`, // normalize nested tags fix
- href: \`\${publicationUrl}categories/\${encodeURIComponent(cat)}\`,
- });`;
-
-const OLD_FEDIFY = ` tags.push(
- new Hashtag({
- name: \`#\${cat.replace(/\\s+/g, "")}\`,
- href: new URL(
- \`\${publicationUrl}categories/\${encodeURIComponent(cat)}\`,
- ),
- }),
- );`;
-
-const NEW_FEDIFY = ` tags.push(
- new Hashtag({
- name: \`#\${cat.split("/").at(-1).replace(/\\s+/g, "")}\`, // normalize nested tags fix
- href: new URL(
- \`\${publicationUrl}categories/\${encodeURIComponent(cat)}\`,
- ),
- }),
- );`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue;
- }
-
- let updated = source;
- let changed = false;
-
- if (source.includes(OLD_PLAIN)) {
- updated = updated.replace(OLD_PLAIN, NEW_PLAIN);
- changed = true;
- } else {
- console.log(`[postinstall] patch-ap-normalize-nested-tags: buildPlainTags snippet not found in ${filePath}`);
- }
-
- if (source.includes(OLD_FEDIFY)) {
- updated = updated.replace(OLD_FEDIFY, NEW_FEDIFY);
- changed = true;
- } else {
- console.log(`[postinstall] patch-ap-normalize-nested-tags: buildFedifyTags snippet not found in ${filePath}`);
- }
-
- if (!changed || updated === source) {
- continue;
- }
-
- await writeFile(filePath, updated, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-normalize-nested-tags to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-normalize-nested-tags: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-normalize-nested-tags: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-normalize-nested-tags: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-object-url-trailing-slash.mjs b/scripts/patch-ap-object-url-trailing-slash.mjs
deleted file mode 100644
index e12d094b..00000000
--- a/scripts/patch-ap-object-url-trailing-slash.mjs
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Patch: make the Fedify object dispatcher's post lookup tolerate trailing-slash
- * differences between the AP object URL and the stored post URL.
- *
- * Root cause:
- * setupObjectDispatchers resolvePost() builds postUrl from the {+id} template
- * variable (e.g. "replies/bd78a") and does an exact findOne() match against
- * posts.properties.url. Posts in MongoDB are stored with a trailing slash
- * ("https://blog.giersig.eu/replies/bd78a/"), but the AP object URL returned
- * by the /api/ap-url lookup endpoint has no trailing slash. The exact match
- * fails → Fedify returns 404 → remote instance shows "Could not connect".
- *
- * Fix:
- * Replace the single-value findOne() with a $in query that tries both the
- * bare URL and the URL with a trailing slash appended.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
-];
-
-const MARKER = "// trailing-slash url fix";
-
-const OLD_SNIPPET = ` const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`;
- const post = await collections.posts.findOne({ "properties.url": postUrl });`;
-
-const NEW_SNIPPET = ` const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`; // trailing-slash url fix
- const post = await collections.posts.findOne({
- "properties.url": { $in: [postUrl, postUrl + "/"] },
- });`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- const source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue;
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-object-url-trailing-slash: snippet not found in ${filePath}`);
- continue;
- }
-
- const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
-
- if (updated === source) {
- continue;
- }
-
- await writeFile(filePath, updated, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-object-url-trailing-slash to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-object-url-trailing-slash: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-object-url-trailing-slash: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-object-url-trailing-slash: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-remove-federation-diag.mjs b/scripts/patch-ap-remove-federation-diag.mjs
deleted file mode 100644
index 0d4b82f0..00000000
--- a/scripts/patch-ap-remove-federation-diag.mjs
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Patch: remove federation-diag inbox logging from the ActivityPub endpoint.
- *
- * The diagnostic block logs every inbox POST to detect federation stalls.
- * It is no longer needed and produces noise in indiekit.log.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
-];
-
-const MARKER = "// ap-remove-federation-diag patch";
-
-const OLD_SNIPPET = ` // Diagnostic: log inbox POSTs to detect federation stalls
- if (req.method === "POST" && req.path.includes("inbox")) {
- const ua = req.get("user-agent") || "unknown";
- const bodyParsed = req.body !== undefined && Object.keys(req.body || {}).length > 0;
- console.info(\`[federation-diag] POST \${req.path} from=\${ua.slice(0, 60)} bodyParsed=\${bodyParsed} readable=\${req.readable}\`);
- }
-
- return self._fedifyMiddleware(req, res, next);`;
-
-const NEW_SNIPPET = ` // ap-remove-federation-diag patch
- return self._fedifyMiddleware(req, res, next);`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue; // already patched
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-remove-federation-diag: snippet not found in ${filePath}`);
- continue;
- }
-
- source = source.replace(OLD_SNIPPET, NEW_SNIPPET);
- await writeFile(filePath, source, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-remove-federation-diag to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-remove-federation-diag: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-remove-federation-diag: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-remove-federation-diag: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-repost-commentary.mjs b/scripts/patch-ap-repost-commentary.mjs
deleted file mode 100644
index b8881c0a..00000000
--- a/scripts/patch-ap-repost-commentary.mjs
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * Patch: include commentary in ActivityPub output for reposts.
- *
- * Root cause (two bugs in jf2-to-as2.js):
- *
- * 1. jf2ToAS2Activity() (Fedify delivery) always generates a bare
- * `Announce { object:
}` for repost posts, even when the
- * post has a body (the author's commentary). External URLs like
- * fromjason.xyz don't serve ActivityPub JSON, so Mastodon receives the
- * Announce but cannot fetch the object — the activity is silently dropped
- * from followers' timelines. The post only appears when searched because
- * Mastodon then fetches the blog's own AP Note representation directly.
- *
- * 2. jf2ToActivityStreams() (content negotiation / search) returns a Note
- * whose `content` field is hardcoded to `🔁 `, completely ignoring
- * any commentary text in properties.content.
- *
- * Fix:
- * - jf2ToAS2Activity(): if the repost has commentary, skip the early
- * Announce return and fall through to the existing Create(Note) path so
- * the text is included and the activity is a proper federated Note.
- * Pure reposts (no commentary) keep the Announce behaviour.
- * - jf2ToAS2Activity() content block: add a `repost` branch that formats
- * the note as `
🔁 ` (mirroring bookmark/like).
- * - jf2ToActivityStreams(): extract commentary from properties.content and
- * prepend it to the note content when present.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/jf2-to-as2.js",
-];
-
-const MARKER = "// repost-commentary fix";
-
-// ---------------------------------------------------------------------------
-// Fix A – jf2ToActivityStreams(): add commentary variable before the return
-// ---------------------------------------------------------------------------
-const OLD_CN_VARS = ` const repostOf = properties["repost-of"];
- const postUrl = resolvePostUrl(properties.url, publicationUrl);
- return {
- "@context": "https://www.w3.org/ns/activitystreams",`;
-
-const NEW_CN_VARS = ` const repostOf = properties["repost-of"];
- const postUrl = resolvePostUrl(properties.url, publicationUrl);
- const commentary = linkifyUrls(properties.content?.html || properties.content || ""); // repost-commentary fix
- return {
- "@context": "https://www.w3.org/ns/activitystreams",`;
-
-// ---------------------------------------------------------------------------
-// Fix B – jf2ToActivityStreams(): use commentary in the content field
-// ---------------------------------------------------------------------------
-const OLD_CN_CONTENT = ` cc: [\`\${actorUrl.replace(/\\/$/, "")}/followers\`],
- content: \`\\u{1F501} \${repostOf}\`,`;
-
-const NEW_CN_CONTENT = ` cc: [\`\${actorUrl.replace(/\\/$/, "")}/followers\`],
- content: commentary // repost-commentary fix
- ? \`\${commentary}
\\u{1F501} \${repostOf}\` // repost-commentary fix
- : \`\\u{1F501} \${repostOf}\`, // repost-commentary fix`;
-
-// ---------------------------------------------------------------------------
-// Fix C – jf2ToAS2Activity(): only Announce when there is no commentary;
-// fall through to Create(Note) when commentary is present
-// ---------------------------------------------------------------------------
-const OLD_AS2_ANNOUNCE = ` if (!repostOf) return null;
- return new Announce({
- actor: actorUri,
- object: new URL(repostOf),
- to: new URL("https://www.w3.org/ns/activitystreams#Public"),
- });
- }`;
-
-const NEW_AS2_ANNOUNCE = ` if (!repostOf) return null;
- const repostContent = properties.content?.html || properties.content || ""; // repost-commentary fix
- if (!repostContent) { // repost-commentary fix
- return new Announce({
- actor: actorUri,
- object: new URL(repostOf),
- to: new URL("https://www.w3.org/ns/activitystreams#Public"),
- });
- } // repost-commentary fix
- // Has commentary — fall through to Create(Note) so the text is federated // repost-commentary fix
- }`;
-
-// ---------------------------------------------------------------------------
-// Fix D – jf2ToAS2Activity() content block: add repost branch
-// ---------------------------------------------------------------------------
-const OLD_AS2_CONTENT = ` } else {
- noteOptions.content = linkifyUrls(properties.content?.html || properties.content || "");
- }`;
-
-const NEW_AS2_CONTENT = ` } else if (postType === "repost") { // repost-commentary fix
- const repostUrl = properties["repost-of"]; // repost-commentary fix
- const commentary = linkifyUrls(properties.content?.html || properties.content || ""); // repost-commentary fix
- noteOptions.content = commentary // repost-commentary fix
- ? \`\${commentary}
\\u{1F501} \${repostUrl}\` // repost-commentary fix
- : \`\\u{1F501} \${repostUrl}\`; // repost-commentary fix
- } else {
- noteOptions.content = linkifyUrls(properties.content?.html || properties.content || "");
- }`;
-
-// ---------------------------------------------------------------------------
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- console.log(`[postinstall] patch-ap-repost-commentary: already applied to ${filePath}`);
- continue;
- }
-
- let updated = source;
- let changed = false;
-
- // Apply each replacement, warn if the old string is not found
- const replacements = [
- ["Fix A (CN vars)", OLD_CN_VARS, NEW_CN_VARS],
- ["Fix B (CN content)", OLD_CN_CONTENT, NEW_CN_CONTENT],
- ["Fix C (AS2 announce)", OLD_AS2_ANNOUNCE, NEW_AS2_ANNOUNCE],
- ["Fix D (AS2 content block)", OLD_AS2_CONTENT, NEW_AS2_CONTENT],
- ];
-
- for (const [label, oldStr, newStr] of replacements) {
- if (updated.includes(oldStr)) {
- updated = updated.replace(oldStr, newStr);
- changed = true;
- } else {
- console.warn(`[postinstall] patch-ap-repost-commentary: ${label} snippet not found in ${filePath} — skipping`);
- }
- }
-
- if (!changed || updated === source) {
- console.log(`[postinstall] patch-ap-repost-commentary: no changes applied to ${filePath}`);
- continue;
- }
-
- await writeFile(filePath, updated, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-repost-commentary to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-repost-commentary: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-repost-commentary: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-repost-commentary: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-skip-draft-syndication.mjs b/scripts/patch-ap-skip-draft-syndication.mjs
new file mode 100644
index 00000000..51869106
--- /dev/null
+++ b/scripts/patch-ap-skip-draft-syndication.mjs
@@ -0,0 +1,109 @@
+/**
+ * Patch: add a post-status === "draft" guard to the ActivityPub syndicator's
+ * syndicate() method, mirroring the existing visibility === "unlisted" guard.
+ *
+ * Without this patch, a draft post that somehow reaches the AP syndicator
+ * directly (bypassing the syndicate-endpoint DB-level filter) would be
+ * federated to followers.
+ */
+import { access, readFile, writeFile } from "node:fs/promises";
+
+const candidates = [
+ "node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
+ "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
+];
+
+const oldSnippet = ` const visibility = String(properties?.visibility || "").toLowerCase();
+ if (visibility === "unlisted") {
+ console.info(
+ "[ActivityPub] Skipping federation for unlisted post: " +
+ (properties?.url || "unknown"),
+ );
+ await logActivity(self._collections.ap_activities, {
+ direction: "outbound",
+ type: "Syndicate",
+ actorUrl: self._publicationUrl,
+ objectUrl: properties?.url,
+ summary: "Syndication skipped: post visibility is unlisted",
+ }).catch(() => {});
+ return undefined;
+ }`;
+
+const newSnippet = ` const postStatus = String(properties?.["post-status"] || "").toLowerCase();
+ if (postStatus === "draft") {
+ console.info(
+ "[ActivityPub] Skipping federation for draft post: " +
+ (properties?.url || "unknown"),
+ );
+ await logActivity(self._collections.ap_activities, {
+ direction: "outbound",
+ type: "Syndicate",
+ actorUrl: self._publicationUrl,
+ objectUrl: properties?.url,
+ summary: "Syndication skipped: post is a draft",
+ }).catch(() => {});
+ return undefined;
+ }
+
+ const visibility = String(properties?.visibility || "").toLowerCase();
+ if (visibility === "unlisted") {
+ console.info(
+ "[ActivityPub] Skipping federation for unlisted post: " +
+ (properties?.url || "unknown"),
+ );
+ await logActivity(self._collections.ap_activities, {
+ direction: "outbound",
+ type: "Syndicate",
+ actorUrl: self._publicationUrl,
+ objectUrl: properties?.url,
+ summary: "Syndication skipped: post visibility is unlisted",
+ }).catch(() => {});
+ return undefined;
+ }`;
+
+async function exists(filePath) {
+ try {
+ await access(filePath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+let checked = 0;
+let patched = 0;
+
+for (const filePath of candidates) {
+ if (!(await exists(filePath))) {
+ continue;
+ }
+
+ checked += 1;
+
+ const source = await readFile(filePath, "utf8");
+
+ if (source.includes(newSnippet)) {
+ continue;
+ }
+
+ if (!source.includes(oldSnippet)) {
+ console.warn(
+ `[postinstall] Skipping ap-skip-draft-syndication patch for ${filePath}: upstream format changed`,
+ );
+ continue;
+ }
+
+ const updated = source.replace(oldSnippet, newSnippet);
+ await writeFile(filePath, updated, "utf8");
+ patched += 1;
+}
+
+if (checked === 0) {
+ console.log("[postinstall] No AP endpoint files found for draft guard patch");
+} else if (patched === 0) {
+ console.log("[postinstall] ap-skip-draft-syndication patch already applied");
+} else {
+ console.log(
+ `[postinstall] Patched AP draft syndication guard in ${patched} file(s)`,
+ );
+}
diff --git a/scripts/patch-ap-url-lookup-api-like.mjs b/scripts/patch-ap-url-lookup-api-like.mjs
deleted file mode 100644
index 7a5ac402..00000000
--- a/scripts/patch-ap-url-lookup-api-like.mjs
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Patch: make the /api/ap-url endpoint return the liked post URL for AP-likes.
- *
- * Root cause:
- * For like posts where like-of is an ActivityPub URL (e.g. a Mastodon status),
- * the "Also on: Fediverse" widget's authorize_interaction flow needs to send
- * the user to the original AP object, not to a blog-side Note URL.
- *
- * The current handler always returns a /activitypub/objects/note/{id} URL,
- * which 404s for AP-likes (because jf2ToAS2Activity returns a Like activity,
- * not a Create(Note), so the Note dispatcher returns null).
- *
- * Fix:
- * Before building the Note/Article URL, check whether the post is an AP-like
- * (like-of is a URL that responds with application/activity+json). If it is,
- * return { apUrl: likeOf } so that authorize_interaction opens the original
- * AP object on the remote instance, where the user can interact with it.
- *
- * Non-AP likes (like-of is a plain web URL) fall through to the existing
- * Note URL logic unchanged.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
-];
-
-const MARKER = "// ap-url-lookup-api-like patch";
-
-const OLD_SNIPPET = ` // Determine the AP object type (mirrors jf2-to-as2.js logic)
- const postType = post.properties?.["post-type"];
- const isArticle = postType === "article" && !!post.properties?.name;
- const objectType = isArticle ? "article" : "note";`;
-
-const NEW_SNIPPET = ` // Determine the AP object type (mirrors jf2-to-as2.js logic)
- const postType = post.properties?.["post-type"];
-
- // For AP-likes: the widget should open the liked post on the remote instance.
- // We detect AP URLs the same way as jf2-to-as2.js: HEAD with activity+json Accept.
- // ap-url-lookup-api-like patch
- if (postType === "like") {
- const likeOf = post.properties?.["like-of"] || "";
- if (likeOf) {
- let isAp = false;
- try {
- const ctrl = new AbortController();
- const tid = setTimeout(() => ctrl.abort(), 3000);
- const r = await fetch(likeOf, {
- method: "HEAD",
- headers: { Accept: "application/activity+json, application/ld+json" },
- signal: ctrl.signal,
- });
- clearTimeout(tid);
- const ct = r.headers.get("content-type") || "";
- isAp = ct.includes("activity+json") || ct.includes("ld+json");
- } catch { /* network error — treat as non-AP */ }
- if (isAp) {
- res.set("Cache-Control", "public, max-age=60");
- return res.json({ apUrl: likeOf });
- }
- }
- }
-
- const isArticle = postType === "article" && !!post.properties?.name;
- const objectType = isArticle ? "article" : "note";`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- let source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue; // already patched
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-url-lookup-api-like: snippet not found in ${filePath}`);
- continue;
- }
-
- source = source.replace(OLD_SNIPPET, NEW_SNIPPET);
- await writeFile(filePath, source, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-url-lookup-api-like to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-url-lookup-api-like: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-url-lookup-api-like: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-url-lookup-api-like: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-ap-url-lookup-api.mjs b/scripts/patch-ap-url-lookup-api.mjs
deleted file mode 100644
index a7d3bda0..00000000
--- a/scripts/patch-ap-url-lookup-api.mjs
+++ /dev/null
@@ -1,188 +0,0 @@
-/**
- * Patch: add a public GET /api/ap-url endpoint to the ActivityPub endpoint.
- *
- * Problem:
- * The "Also on fediverse" widget on blog post pages passes the blog post URL
- * (e.g. https://blog.giersig.eu/replies/bd78a/) to the Mastodon
- * authorize_interaction flow:
- * https://{instance}/authorize_interaction?uri={blog-post-url}
- *
- * When the remote instance fetches that URI with Accept: application/activity+json,
- * it may hit a static file server (nginx/Caddy) that returns HTML instead of
- * AP JSON, causing the interaction to fail with "Could not connect to the given
- * address" or a similar error.
- *
- * Fix:
- * Add a public API route to the AP endpoint:
- * GET /activitypub/api/ap-url?post={blog-post-url}
- *
- * This resolves the post in MongoDB, determines its object type (Note or Article),
- * and returns the canonical Fedify-served AP object URL:
- * { apUrl: "https://blog.giersig.eu/activitypub/objects/note/replies/bd78a/" }
- *
- * The "Also on fediverse" JS widget can then call this API and use the returned
- * apUrl in the authorize_interaction redirect instead of the blog post URL.
- * Fedify-served URLs (/activitypub/objects/…) are always proxied to Node.js and
- * will reliably return AP JSON with correct content negotiation.
- *
- * The patch inserts the new route in the `routesPublic` getter of index.js,
- * just before the closing `return router` statement.
- */
-
-import { access, readFile, writeFile } from "node:fs/promises";
-
-const candidates = [
- "node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
- "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/index.js",
-];
-
-const MARKER = "// AP URL lookup endpoint";
-
-const OLD_SNIPPET = ` router.all("/inbox", (req, res) => {
- res
- .status(405)
- .set("Allow", "POST")
- .type("application/activity+json")
- .json({
- error: "Method Not Allowed",
- message: "The shared inbox only accepts POST requests",
- });
- });
-
- return router;
- }
-
- /**
- * Authenticated admin routes — mounted at mountPath, behind IndieAuth.
- */`;
-
-const NEW_SNIPPET = ` router.all("/inbox", (req, res) => {
- res
- .status(405)
- .set("Allow", "POST")
- .type("application/activity+json")
- .json({
- error: "Method Not Allowed",
- message: "The shared inbox only accepts POST requests",
- });
- });
-
- // AP URL lookup endpoint
- // Public API: resolve a blog post URL → its Fedify-served AP object URL.
- // GET /api/ap-url?post=https://blog.example.com/notes/foo/
- // Returns { apUrl: "https://blog.example.com/activitypub/objects/note/notes/foo/" }
- //
- // Use this in "Also on fediverse" widgets so that authorize_interaction
- // uses a URL that is always routed to Node.js (never intercepted by a
- // static file server), ensuring reliable AP content negotiation.
- router.get("/api/ap-url", async (req, res) => {
- try {
- const postParam = req.query.post;
- if (!postParam) {
- return res.status(400).json({ error: "post parameter required" });
- }
-
- const { application } = req.app.locals;
- const postsCollection = application.collections?.get("posts");
-
- if (!postsCollection) {
- return res.status(503).json({ error: "Database unavailable" });
- }
-
- const publicationUrl = (self._publicationUrl || application.url || "").replace(/\\/$/, "");
-
- // Match with or without trailing slash
- const postUrl = postParam.replace(/\\/$/, "");
- const post = await postsCollection.findOne({
- "properties.url": { $in: [postUrl, postUrl + "/"] },
- });
-
- if (!post) {
- return res.status(404).json({ error: "Post not found" });
- }
-
- // Draft and unlisted posts are not federated
- if (post?.properties?.["post-status"] === "draft") {
- return res.status(404).json({ error: "Post not found" });
- }
- if (post?.properties?.visibility === "unlisted") {
- return res.status(404).json({ error: "Post not found" });
- }
-
- // Determine the AP object type (mirrors jf2-to-as2.js logic)
- const postType = post.properties?.["post-type"];
- const isArticle = postType === "article" && !!post.properties?.name;
- const objectType = isArticle ? "article" : "note";
-
- // Extract the path portion after the publication base URL
- const resolvedUrl = (post.properties?.url || "").replace(/\\/$/, "");
- if (!resolvedUrl.startsWith(publicationUrl)) {
- return res.status(500).json({ error: "Post URL does not match publication base" });
- }
- const postPath = resolvedUrl.slice(publicationUrl.length).replace(/^\\//, "");
-
- const mp = (self.options.mountPath || "").replace(/\\/$/, "");
- const apBase = publicationUrl;
- const apUrl = \`\${apBase}\${mp}/objects/\${objectType}/\${postPath}\`;
-
- res.set("Cache-Control", "public, max-age=300");
- res.json({ apUrl });
- } catch (error) {
- res.status(500).json({ error: error.message });
- }
- });
-
- return router;
- }
-
- /**
- * Authenticated admin routes — mounted at mountPath, behind IndieAuth.
- */`;
-
-async function exists(filePath) {
- try {
- await access(filePath);
- return true;
- } catch {
- return false;
- }
-}
-
-let checked = 0;
-let patched = 0;
-
-for (const filePath of candidates) {
- if (!(await exists(filePath))) {
- continue;
- }
-
- checked += 1;
- const source = await readFile(filePath, "utf8");
-
- if (source.includes(MARKER)) {
- continue;
- }
-
- if (!source.includes(OLD_SNIPPET)) {
- console.log(`[postinstall] patch-ap-url-lookup-api: old snippet not found in ${filePath}`);
- continue;
- }
-
- const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
-
- if (updated === source) {
- continue;
- }
-
- await writeFile(filePath, updated, "utf8");
- patched += 1;
- console.log(`[postinstall] Applied patch-ap-url-lookup-api to ${filePath}`);
-}
-
-if (checked === 0) {
- console.log("[postinstall] patch-ap-url-lookup-api: no target files found");
-} else if (patched === 0) {
- console.log("[postinstall] patch-ap-url-lookup-api: already up to date");
-} else {
- console.log(`[postinstall] patch-ap-url-lookup-api: patched ${patched}/${checked} file(s)`);
-}
diff --git a/scripts/patch-endpoint-github-changelog-categories.mjs b/scripts/patch-endpoint-github-changelog-categories.mjs
index efd8e6d1..053bfb44 100644
--- a/scripts/patch-endpoint-github-changelog-categories.mjs
+++ b/scripts/patch-endpoint-github-changelog-categories.mjs
@@ -38,6 +38,8 @@ const newCategorize = `function categorizeCommit(title) {
if (/^perf[:(]/i.test(title)) return "performance";
if (/^a11y[:(]/i.test(title)) return "accessibility";
if (/^docs[:(]/i.test(title)) return "documentation";
+ if (/^chore[:(]/i.test(title)) return "chores";
+ if (/^refactor[:(]/i.test(title)) return "refactor";
return "other";
}
@@ -47,6 +49,8 @@ const CATEGORY_LABELS = {
performance: "Performance",
accessibility: "Accessibility",
documentation: "Documentation",
+ chores: "Chores",
+ refactor: "Refactor",
other: "Other",
};`;
diff --git a/scripts/patch-indiekit-endpoint-urls-protocol.mjs b/scripts/patch-indiekit-endpoint-urls-protocol.mjs
new file mode 100644
index 00000000..7a6fcf13
--- /dev/null
+++ b/scripts/patch-indiekit-endpoint-urls-protocol.mjs
@@ -0,0 +1,65 @@
+/**
+ * Patch: fix endpoint URL resolution to use application.url (HTTPS) instead
+ * of getUrl(request) (HTTP) as the base URL for relative endpoint paths.
+ *
+ * Indiekit resolves relative endpoint paths (e.g. "/media") to absolute URLs
+ * using getUrl(request), which returns `http://` because Express sees HTTP
+ * connections from nginx (no trust proxy set). This results in
+ * `application.mediaEndpoint = "http://blog.giersig.eu/media"` being passed
+ * to the frontend, causing mixed-content failures in Safari ("Load failed")
+ * when the media browser tries to fetch that URL from an HTTPS page.
+ *
+ * Fix: prefer application.url (the configured HTTPS base URL) over
+ * getUrl(request) when resolving relative endpoint paths.
+ */
+
+import { access, readFile, writeFile } from "node:fs/promises";
+
+const candidates = [
+ "node_modules/@indiekit/indiekit/lib/endpoints.js",
+];
+
+const oldCode =
+ ": new URL(application[endpoint], getUrl(request)).href;";
+const newCode =
+ ": new URL(application[endpoint], application.url || getUrl(request)).href;";
+
+async function exists(filePath) {
+ try {
+ await access(filePath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+let checked = 0;
+let patched = 0;
+
+for (const filePath of candidates) {
+ if (!(await exists(filePath))) continue;
+
+ checked += 1;
+ const source = await readFile(filePath, "utf8");
+
+ if (source.includes(newCode)) {
+ continue;
+ }
+
+ if (!source.includes(oldCode)) {
+ console.warn(`[postinstall] endpoint-urls-protocol: snippet not found in ${filePath} — skipping`);
+ continue;
+ }
+
+ const updated = source.replace(oldCode, newCode);
+ await writeFile(filePath, updated, "utf8");
+ patched += 1;
+}
+
+if (checked === 0) {
+ console.log("[postinstall] No endpoints.js found");
+} else if (patched === 0) {
+ console.log("[postinstall] endpoint URL protocol patch already applied");
+} else {
+ console.log(`[postinstall] Patched endpoint URL protocol in ${patched} file(s)`);
+}
diff --git a/scripts/patch-micropub-session-token.mjs b/scripts/patch-micropub-session-token.mjs
new file mode 100644
index 00000000..c1ce0799
--- /dev/null
+++ b/scripts/patch-micropub-session-token.mjs
@@ -0,0 +1,59 @@
+/**
+ * Patch: fix session.token → session.access_token in micropub action controller.
+ *
+ * The indieauth authenticate middleware stores the bearer token as
+ * `session.access_token`, but the micropub action controller destructures it
+ * as `session.token`. This causes `uploadMedia` to be called with
+ * `token = undefined`, resulting in `Authorization: Bearer undefined` on the
+ * internal /media fetch — a 500 for any Micropub client that uploads files
+ * directly (e.g. OwnYourSwarm).
+ */
+
+import { access, readFile, writeFile } from "node:fs/promises";
+
+const candidates = [
+ "node_modules/@indiekit/endpoint-micropub/lib/controllers/action.js",
+];
+
+const oldCode = "const { scope, token } = session;";
+const newCode = "const { scope, access_token: token } = session;";
+
+async function exists(filePath) {
+ try {
+ await access(filePath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+let checked = 0;
+let patched = 0;
+
+for (const filePath of candidates) {
+ if (!(await exists(filePath))) continue;
+
+ checked += 1;
+ const source = await readFile(filePath, "utf8");
+
+ if (source.includes(newCode)) {
+ continue;
+ }
+
+ if (!source.includes(oldCode)) {
+ console.warn(`[postinstall] micropub-session-token: snippet not found in ${filePath} — skipping`);
+ continue;
+ }
+
+ const updated = source.replace(oldCode, newCode);
+ await writeFile(filePath, updated, "utf8");
+ patched += 1;
+}
+
+if (checked === 0) {
+ console.log("[postinstall] No micropub action controller found");
+} else if (patched === 0) {
+ console.log("[postinstall] micropub session token patch already applied");
+} else {
+ console.log(`[postinstall] Patched micropub session token in ${patched} file(s)`);
+}
diff --git a/scripts/patch-microsub-compose-draft-guard.mjs b/scripts/patch-microsub-compose-draft-guard.mjs
new file mode 100644
index 00000000..6c3a00ef
--- /dev/null
+++ b/scripts/patch-microsub-compose-draft-guard.mjs
@@ -0,0 +1,139 @@
+/**
+ * Patch: honour post-status in the microsub compose submitCompose handler.
+ *
+ * When a post is submitted via the microsub compose form with
+ * post-status: draft:
+ * 1. Forward the post-status to Micropub so the post is saved as a draft.
+ * 2. Suppress all mp-syndicate-to targets — draft posts must never be
+ * syndicated (not to Mastodon, Bluesky, or ActivityPub).
+ *
+ * The syndicate endpoint already filters out drafts at the DB-query level
+ * (patch-federation-unlisted-guards), and the AP syndicator has its own
+ * guard (patch-ap-skip-draft-syndication), but preventing syndication
+ * targets from being stored in the first place is the cleanest approach.
+ */
+import { access, readFile, writeFile } from "node:fs/promises";
+
+const candidates = [
+ "node_modules/@rmdes/indiekit-endpoint-microsub/lib/controllers/reader.js",
+ "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-microsub/lib/controllers/reader.js",
+];
+
+const patchSpecs = [
+ {
+ name: "microsub-compose-extract-post-status",
+ oldSnippet: [
+ ` const syndicateTo = request.body["mp-syndicate-to"];`,
+ ``,
+ ` // Debug logging`,
+ ` console.info(`,
+ ` "[Microsub] submitCompose request.body:",`,
+ ` JSON.stringify(request.body),`,
+ ` );`,
+ ` console.info("[Microsub] Extracted values:", {`,
+ ` content,`,
+ ` inReplyTo,`,
+ ` likeOf,`,
+ ` repostOf,`,
+ ` bookmarkOf,`,
+ ` syndicateTo,`,
+ ` });`,
+ ].join("\n"),
+ newSnippet: [
+ ` const syndicateTo = request.body["mp-syndicate-to"];`,
+ ` const postStatus = request.body["post-status"];`,
+ ` const isDraft = postStatus === "draft";`,
+ ``,
+ ` // Debug logging`,
+ ` console.info(`,
+ ` "[Microsub] submitCompose request.body:",`,
+ ` JSON.stringify(request.body),`,
+ ` );`,
+ ` console.info("[Microsub] Extracted values:", {`,
+ ` content,`,
+ ` inReplyTo,`,
+ ` likeOf,`,
+ ` repostOf,`,
+ ` bookmarkOf,`,
+ ` syndicateTo,`,
+ ` postStatus,`,
+ ` });`,
+ ].join("\n"),
+ },
+ {
+ name: "microsub-compose-draft-suppresses-syndication",
+ oldSnippet: [
+ ` // Add syndication targets`,
+ ` if (syndicateTo) {`,
+ ` const targets = Array.isArray(syndicateTo) ? syndicateTo : [syndicateTo];`,
+ ` for (const target of targets) {`,
+ ` micropubData.append("mp-syndicate-to", target);`,
+ ` }`,
+ ` }`,
+ ].join("\n"),
+ newSnippet: [
+ ` // Set post status (e.g. draft) — must be appended before syndication logic`,
+ ` if (postStatus) {`,
+ ` micropubData.append("post-status", postStatus);`,
+ ` }`,
+ ``,
+ ` // Add syndication targets — suppressed entirely for draft posts`,
+ ` if (syndicateTo && !isDraft) {`,
+ ` const targets = Array.isArray(syndicateTo) ? syndicateTo : [syndicateTo];`,
+ ` for (const target of targets) {`,
+ ` micropubData.append("mp-syndicate-to", target);`,
+ ` }`,
+ ` }`,
+ ].join("\n"),
+ },
+];
+
+async function exists(filePath) {
+ try {
+ await access(filePath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+const checkedFiles = new Set();
+const patchedFiles = new Set();
+
+for (const spec of patchSpecs) {
+ for (const filePath of candidates) {
+ if (!(await exists(filePath))) {
+ continue;
+ }
+
+ checkedFiles.add(filePath);
+
+ const source = await readFile(filePath, "utf8");
+
+ if (source.includes(spec.newSnippet)) {
+ // Already patched
+ continue;
+ }
+
+ if (!source.includes(spec.oldSnippet)) {
+ console.warn(
+ `[postinstall] Skipping ${spec.name} patch for ${filePath}: upstream format changed`,
+ );
+ continue;
+ }
+
+ const updated = source.replace(spec.oldSnippet, spec.newSnippet);
+ await writeFile(filePath, updated, "utf8");
+ patchedFiles.add(filePath);
+ }
+}
+
+if (checkedFiles.size === 0) {
+ console.log("[postinstall] No microsub reader files found for draft guard patch");
+} else if (patchedFiles.size === 0) {
+ console.log("[postinstall] microsub compose draft guard already applied");
+} else {
+ console.log(
+ `[postinstall] Patched microsub compose draft guard in ${patchedFiles.size} file(s)`,
+ );
+}
diff --git a/scripts/patch-webmention-sender-livefetch.mjs b/scripts/patch-webmention-sender-livefetch.mjs
index 0bb9b099..529cf1a9 100644
--- a/scripts/patch-webmention-sender-livefetch.mjs
+++ b/scripts/patch-webmention-sender-livefetch.mjs
@@ -22,7 +22,7 @@ import { access, readFile, writeFile } from "node:fs/promises";
const filePath =
"node_modules/@rmdes/indiekit-endpoint-webmention-sender/lib/controllers/webmention-sender.js";
-const patchMarker = "// [patched:livefetch:v5]";
+const patchMarker = "// [patched:livefetch:v6]";
// Original upstream code
const originalBlock = ` // If no content, try fetching the published page
@@ -64,6 +64,8 @@ const retryPatchedBlock = ` // If no content, try fetching the published
if (!contentToProcess) {
if (fetchFailed) {
+ // Page not yet available — skip and retry on next poll rather than
+ // permanently marking this post as sent with zero webmentions.
console.log(\`[webmention] Page not yet available for \${postUrl}, will retry next poll\`);
continue;
}
@@ -72,7 +74,7 @@ const retryPatchedBlock = ` // If no content, try fetching the published
continue;
}`;
-const newBlock = ` // [patched:livefetch:v5] Build synthetic h-entry HTML from stored post properties.
+const newBlock = ` // [patched:livefetch:v6] Build synthetic h-entry HTML from stored post properties.
// The stored properties already contain all microformat target URLs
// (in-reply-to, like-of, bookmark-of, repost-of) and content.html has inline
// links — no live page fetch needed, and no exposure to internal DNS issues.
@@ -95,7 +97,8 @@ const newBlock = ` // [patched:livefetch:v5] Build synthetic h-entry HTML
}
}
const _bodyHtml = post.properties.content?.html || post.properties.content?.value || "";
- const contentToProcess = \`\${_anchors.join("")}\${_bodyHtml ? \`
\${_bodyHtml}
\` : ""}
\`;`;
+ const contentToProcess = \`\${_anchors.join("")}\${_bodyHtml ? \`
\${_bodyHtml}
\` : ""}
\`;
+ console.log(\`[webmention] Built synthetic h-entry for \${postUrl}: \${_anchors.length} prop link(s) [\${Object.entries(_propLinks).filter(([p]) => post.properties[p]).map(([p]) => p).join(", ") || "none"}]\`);`;
async function exists(p) {
try {
@@ -114,21 +117,26 @@ if (!(await exists(filePath))) {
const source = await readFile(filePath, "utf8");
if (source.includes(patchMarker)) {
- console.log("[patch-webmention-sender-livefetch] Already patched (v5)");
+ console.log("[patch-webmention-sender-livefetch] Already patched (v6)");
process.exit(0);
}
-// For v1–v4: extract the old patched block by finding the marker and the
-// closing "continue;\n }" that ends the if (!contentToProcess) block.
-const priorMarkers = [
+// Extract the old patched block by finding the marker and the end of the block.
+// v1–v4 end with "continue;\n }" (the if (!contentToProcess) block).
+// v5+ end with the contentToProcess assignment line (no continue block).
+const priorMarkersWithContinue = [
"// [patched:livefetch:v4]",
"// [patched:livefetch:v3]",
"// [patched:livefetch:v2]",
"// [patched:livefetch]",
];
+const priorMarkersNoContinue = [
+ "// [patched:livefetch:v5]",
+];
let oldPatchBlock = null;
-for (const marker of priorMarkers) {
+
+for (const marker of priorMarkersWithContinue) {
if (!source.includes(marker)) continue;
const startIdx = source.lastIndexOf(` ${marker}`);
const endMarker = " continue;\n }";
@@ -139,6 +147,21 @@ for (const marker of priorMarkers) {
}
}
+if (!oldPatchBlock) {
+ for (const marker of priorMarkersNoContinue) {
+ if (!source.includes(marker)) continue;
+ const startIdx = source.lastIndexOf(` ${marker}`);
+ // v5 block ends with the contentToProcess = `...`; line
+ // Find the semicolon that closes the last template literal on that line
+ const endMarker = '""} `;\n';
+ const endSearch = source.indexOf(endMarker, startIdx);
+ if (startIdx !== -1 && endSearch !== -1) {
+ oldPatchBlock = source.slice(startIdx, endSearch + endMarker.length);
+ break;
+ }
+ }
+}
+
const targetBlock = oldPatchBlock
? oldPatchBlock
: source.includes(originalBlock)
@@ -162,4 +185,4 @@ if (!patched.includes(patchMarker)) {
}
await writeFile(filePath, patched, "utf8");
-console.log("[patch-webmention-sender-livefetch] Patched successfully (v5)");
+console.log("[patch-webmention-sender-livefetch] Patched successfully (v6)");
diff --git a/scripts/patch-webmention-sender-reset-stale.mjs b/scripts/patch-webmention-sender-reset-stale.mjs
index 6ca0e507..5a471162 100644
--- a/scripts/patch-webmention-sender-reset-stale.mjs
+++ b/scripts/patch-webmention-sender-reset-stale.mjs
@@ -9,7 +9,7 @@
import { MongoClient } from "mongodb";
import config from "../indiekit.config.mjs";
-const MIGRATION_ID = "webmention-sender-reset-stale-v10";
+const MIGRATION_ID = "webmention-sender-reset-stale-v11";
const mongodbUrl = config.application?.mongodbUrl;
if (!mongodbUrl) {
diff --git a/start.example.sh b/start.example.sh
index 537bb307..022344c9 100644
--- a/start.example.sh
+++ b/start.example.sh
@@ -61,11 +61,11 @@ done
INDIEKIT_PID="$!"
# Webmention sender — polls every N seconds (see @rmdes/indiekit-endpoint-webmention-sender README)
-# Routes through INTERNAL_FETCH_URL (nginx) so the request gets correct Host header
-# and X-Forwarded-Proto, avoiding empty-reply issues with direct jail connections.
+# Connects directly to Indiekit (not through nginx) so the Host header doesn't
+# need to match any nginx server_name. nginx port 80 returns 444 for unknown hosts.
WEBMENTION_POLL_INTERVAL="${WEBMENTION_SENDER_POLL_INTERVAL:-300}"
-INDIEKIT_INTERNAL_URL="${INTERNAL_FETCH_URL:-http://${INDIEKIT_BIND_HOST:-127.0.0.1}:${PORT:-3000}}"
-WEBMENTION_ENDPOINT="${INDIEKIT_INTERNAL_URL}${WEBMENTION_SENDER_MOUNT_PATH:-/webmention-sender}"
+INDIEKIT_DIRECT_URL="http://${INDIEKIT_BIND_HOST:-127.0.0.1}:${PORT:-3000}"
+WEBMENTION_ENDPOINT="${INDIEKIT_DIRECT_URL}${WEBMENTION_SENDER_MOUNT_PATH:-/webmention-sender}"
WEBMENTION_ORIGIN="${PUBLICATION_URL:-${SITE_URL:-}}"
(
@@ -104,6 +104,6 @@ WEBMENTION_ORIGIN="${PUBLICATION_URL:-${SITE_URL:-}}"
) &
POLLER_PID="$!"
-trap 'kill "${POLLER_PID}" 2>/dev/null || true' EXIT INT TERM
+trap 'kill "${INDIEKIT_PID}" "${POLLER_PID}" 2>/dev/null || true; wait "${INDIEKIT_PID}" 2>/dev/null || true' EXIT INT TERM
wait "${INDIEKIT_PID}"