diff --git a/src/app.ts b/src/app.ts index 58df4354..7a5a33ff 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,6 +15,7 @@ import { Organization, Service, Update, + Announce, Context, } from '@fedify/fedify'; import { federation } from '@fedify/fedify/x/hono'; @@ -45,6 +46,7 @@ import { acceptDispatcher, createDispatcher, updateDispatcher, + handleAnnounce, } from './dispatchers'; import { followAction, inboxHandler, postPublishedWebhook, siteChangedWebhook } from './handlers'; @@ -110,6 +112,8 @@ inboxListener .on(Accept, ensureCorrectContext(handleAccept)) .onError(inboxErrorHandler) .on(Create, ensureCorrectContext(handleCreate)) + .onError(inboxErrorHandler) + .on(Announce, ensureCorrectContext(handleAnnounce)) .onError(inboxErrorHandler); fedify diff --git a/src/dispatchers.ts b/src/dispatchers.ts index c23696f5..3d84c849 100644 --- a/src/dispatchers.ts +++ b/src/dispatchers.ts @@ -10,6 +10,7 @@ import { Activity, Update, Context, + Announce, } from '@fedify/fedify'; import { v4 as uuidv4 } from 'uuid'; import { addToList } from './kv-helpers'; @@ -137,6 +138,69 @@ export async function handleCreate( await addToList(ctx.data.db, ['inbox'], create.id.href); } +export async function handleAnnounce( + ctx: Context, + announce: Announce, +) { + console.log('Handling Announce'); + + // Validate announce + if (!announce.id) { + console.log('Invalid Announce - no id'); + return; + } + + if (!announce.objectId) { + console.log('Invalid Announce - no object id'); + return; + } + + // Validate sender + const sender = await announce.getActor(ctx); + + if (sender === null || sender.id === null) { + console.log('Sender missing, exit early'); + return; + } + + // Lookup announced object - If not found in globalDb, perform network lookup + let object = null; + let existing = await ctx.data.globaldb.get([announce.objectId.href]) ?? null; + + if (!existing) { + console.log('Object not found in globalDb, performing network lookup'); + + object = await lookupObject(announce.objectId); + } + + // Validate object + if (!existing && !object) { + console.log('Invalid Announce - could not find object'); + return; + } + + if (object && !object.id) { + console.log('Invalid Announce - could not find object id'); + return; + } + + // Persist announce + const announceJson = await announce.toJsonLd(); + ctx.data.globaldb.set([announce.id.href], announceJson); + + // Persist object if not already persisted + if (!existing && object && object.id) { + console.log('Storing object in globalDb'); + + const objectJson = await object.toJsonLd(); + + ctx.data.globaldb.set([object.id.href], objectJson); + } + + // Add announce to inbox + await addToList(ctx.data.db, ['inbox'], announce.id.href); +} + export async function inboxErrorHandler( ctx: Context, error: unknown, diff --git a/src/handlers.ts b/src/handlers.ts index 41e03ac2..2c2bd692 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -18,6 +18,10 @@ import { getSiteSettings } from './ghost'; import type { PersonData } from './user'; import { ACTOR_DEFAULT_HANDLE } from './constants'; +type StoredThing = { + object: object | string; +} + async function postToArticle(ctx: RequestContext, post: any) { if (!post) { return { @@ -195,7 +199,16 @@ export async function inboxHandler( let items: unknown[] = []; for (const result of results) { try { - const thing = await ctx.get('globaldb').get([result]); + const db = ctx.get('globaldb'); + const thing = await db.get([result]); + + // If the object is a string, it's probably a URI, so we should + // look it up the db. If it's not in the db, we should just leave + // it as is + if (thing && typeof thing.object === 'string') { + thing.object = await db.get([thing.object]) ?? thing.object; + } + items.push(thing); } catch (err) { console.log(err); diff --git a/yarn.lock b/yarn.lock index cc02beec..4ec086ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -215,9 +215,9 @@ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@^0.3.12": version "0.3.25" @@ -236,15 +236,20 @@ tslib "^2.4.1" "@logtape/logtape@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@logtape/logtape/-/logtape-0.4.0.tgz#8727db1aa79899e3876b202b42ec844be6db5995" - integrity sha512-9T65HMtVoOE/2oyYaZII8gQ7Svw+PeGHnuBXZgiS6WDITGpunZ7cu9HHle1JSgwygYB/LdUwC9EN67Lhejd6Bg== + version "0.4.2" + resolved "https://registry.yarnpkg.com/@logtape/logtape/-/logtape-0.4.2.tgz#a0e493472a9f66b3af3da99b270c9bdc397afec5" + integrity sha512-AIhwFxU3hZTaP9SOY266DQde1H/4nzjfhZOO1/yGEFQT3mlSuYMwxeW2xgnNFDmyRzBrRpNXLA3vdZvnilvLCA== "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + "@opentelemetry/api-logs@0.52.1": version "0.52.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz#52906375da4d64c206b0c4cb8ffa209214654ecc" @@ -737,11 +742,11 @@ "@types/node" "*" "@types/node@*": - version "20.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" - integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== + version "22.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.0.tgz#04862a2a71e62264426083abe1e27e87cac05a30" + integrity sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw== dependencies: - undici-types "~5.26.4" + undici-types "~6.11.1" "@types/node@20.12.12": version "20.12.12" @@ -803,9 +808,9 @@ "@types/send" "*" "@types/shimmer@^1.0.2": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.5.tgz#491d8984d4510e550bfeb02d518791d7f59d2b88" - integrity sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww== + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" + integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== "@types/sinon@17.0.3": version "17.0.3" @@ -842,9 +847,9 @@ acorn-import-attributes@^1.9.5: integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn@^8.8.2: - version "8.12.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" - integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== ansi-colors@4.1.1: version "4.1.1" @@ -1050,10 +1055,10 @@ debug@4.3.4: dependencies: ms "2.1.2" -debug@^4.1.1: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== +debug@^4.3.5: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -1217,9 +1222,9 @@ get-package-type@^0.1.0: integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-tsconfig@^4.7.5: - version "4.7.5" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" - integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + version "4.7.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.6.tgz#118fd5b7b9bae234cc7705a00cd771d7eb65d62a" + integrity sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA== dependencies: resolve-pkg-maps "^1.0.0" @@ -1247,9 +1252,9 @@ glob@8.1.0: once "^1.3.0" glob@^10.4.1: - version "10.4.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" - integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== dependencies: foreground-child "^3.1.0" jackspeak "^3.1.2" @@ -1303,9 +1308,9 @@ import-in-the-middle@1.4.2: module-details-from-path "^1.0.3" import-in-the-middle@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.8.1.tgz#8b51c2cc631b64e53e958d7048d2d9463ce628f8" - integrity sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng== + version "1.11.0" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.0.tgz#a94c4925b8da18256cde3b3b7b38253e6ca5e708" + integrity sha512-5DimNQGoe0pLUHbR9qK84iWaWjjbsxiqXnw6Qz64+azRgleqv9k2kTt5fw7QsOpmaGYtuxxursnPPsnTKEx10Q== dependencies: acorn "^8.8.2" acorn-import-attributes "^1.9.5" @@ -1338,9 +1343,9 @@ is-binary-path@~2.1.0: binary-extensions "^2.0.0" is-core-module@^2.13.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" - integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== dependencies: hasown "^2.0.2" @@ -1414,9 +1419,9 @@ istanbul-reports@^3.1.6: istanbul-lib-report "^3.0.0" jackspeak@^3.1.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" - integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: @@ -1528,9 +1533,9 @@ long@^5.2.1: integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== lru-cache@^10.2.0: - version "10.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" - integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^6.0.0: version "6.0.0" @@ -1812,15 +1817,16 @@ picomatch@^2.0.4, picomatch@^2.2.1: integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pkijs@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-3.1.0.tgz#ea7b84e2b3870582cc38d41473433bbe8b5c3c3c" - integrity sha512-N+OCWUp6xrg7OkG+4DIiZUOsp3qMztjq8RGCc1hSY92dsUG8cTlAo7pEkfRGjcdyBv2c1Y9bjAzqdTJAlctuNg== + version "3.2.4" + resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-3.2.4.tgz#55ed72b363a20fbd42b139ee3b72e54483635171" + integrity sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg== dependencies: + "@noble/hashes" "^1.4.0" asn1js "^3.0.5" bytestreamjs "^2.0.0" pvtsutils "^1.3.2" pvutils "^1.1.3" - tslib "^2.4.0" + tslib "^2.6.3" postgres-array@~2.0.0: version "2.0.0" @@ -1917,13 +1923,13 @@ require-directory@^2.1.1: integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-in-the-middle@^7.1.1: - version "7.3.0" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz#ce64a1083647dc07b3273b348357efac8a9945c9" - integrity sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw== + version "7.4.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.4.0.tgz#606977820d4b5f9be75e5a108ce34cfed25b3bb4" + integrity sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ== dependencies: - debug "^4.1.1" + debug "^4.3.5" module-details-from-path "^1.0.3" - resolve "^1.22.1" + resolve "^1.22.8" resolve-from@^5.0.0: version "5.0.0" @@ -1935,7 +1941,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.20.0, resolve@^1.22.1: +resolve@^1.20.0, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -1955,9 +1961,9 @@ safe-buffer@^5.1.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== semver@^7.5.2, semver@^7.5.3: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== seq-queue@^0.0.5: version "0.0.5" @@ -2097,7 +2103,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tslib@^2.4.0, tslib@^2.4.1, tslib@^2.6.1, tslib@^2.6.2: +tslib@^2.4.0, tslib@^2.4.1, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -2139,6 +2145,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" + integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== + undici@^5.21.2: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068"