diff --git a/CHANGES.md b/CHANGES.md index 025e6dc1e..48550eedf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,17 @@ To be released. [#1207]: https://github.com/fedify-dev/fedify/issues/1207 [#1208]: https://github.com/fedify-dev/fedify/pull/1208 +### @fedify/vocab-runtime + + - Added the [FEP-6757] context to the preloaded JSON-LD contexts. The default + document loader now resolves locally, so + transient Codeberg Pages outages no longer prevent otherwise valid inbound + documents from being parsed or verified. [[#1211], [#1216]] + +[FEP-6757]: https://w3id.org/fep/6757 +[#1211]: https://github.com/fedify-dev/fedify/issues/1211 +[#1216]: https://github.com/fedify-dev/fedify/pull/1216 + ### @fedify/vocab-tools - Fixed generated constructors and `clone()` methods to copy property diff --git a/changes.d/vocab-runtime/fep-6757-context.md b/changes.d/vocab-runtime/fep-6757-context.md new file mode 100644 index 000000000..5ad4e0368 --- /dev/null +++ b/changes.d/vocab-runtime/fep-6757-context.md @@ -0,0 +1,11 @@ +--- +links: + '#1211': https://github.com/fedify-dev/fedify/issues/1211 + '#1216': https://github.com/fedify-dev/fedify/pull/1216 +--- + - Added the [FEP-6757] context to the preloaded JSON-LD contexts. The default + document loader now resolves locally, so + transient Codeberg Pages outages no longer prevent otherwise valid inbound + documents from being parsed or verified. [[#1211], [#1216]] + +[FEP-6757]: https://w3id.org/fep/6757 diff --git a/packages/vocab-runtime/src/contexts.ts b/packages/vocab-runtime/src/contexts.ts index 7aac34f96..f4159ef0f 100644 --- a/packages/vocab-runtime/src/contexts.ts +++ b/packages/vocab-runtime/src/contexts.ts @@ -3,6 +3,7 @@ // cSpell: disable import cidV1Context from "./contexts/cid-v1.json" with { type: "json" }; +import fep6757 from "./contexts/fep-6757.json" with { type: "json" }; import fep7aa9 from "./contexts/fep-7aa9.json" with { type: "json" }; import fepEf61 from "./contexts/fep-ef61.json" with { type: "json" }; import joinLemmyContext from "./contexts/join-lemmy.json" with { type: "json" }; @@ -4345,6 +4346,13 @@ const preloadedContexts: Record = { // https://github.com/fedify-dev/fedify/issues/1078 "https://w3id.org/fep/7aa9": fep7aa9, + // The FEP-6757 context is hosted on Codeberg Pages, whose outages can + // prevent documents with license metadata from being parsed. Resolve it + // locally so parsing does not depend on the remote context server. + // See: https://w3id.org/fep/6757 + // https://github.com/fedify-dev/fedify/issues/1211 + "https://w3id.org/fep/6757": fep6757, + // Lemmy's context document is served as application/json without the JSON-LD // context Link header. The default document loader treats that as a regular // JSON response instead of a JSON-LD context, so every Lemmy activity that diff --git a/packages/vocab-runtime/src/contexts/fep-6757.json b/packages/vocab-runtime/src/contexts/fep-6757.json new file mode 100644 index 000000000..f3e00f587 --- /dev/null +++ b/packages/vocab-runtime/src/contexts/fep-6757.json @@ -0,0 +1,12 @@ +{ + "@context": { + "license": { + "@id": "http://purl.org/dc/terms/license", + "@type": "@id" + }, + "preferredLicense": { + "@id": "https://w3id.org/fep/6757#preferredLicense", + "@type": "@id" + } + } +} diff --git a/packages/vocab-runtime/src/docloader.test.ts b/packages/vocab-runtime/src/docloader.test.ts index 9e19de16f..0a1221cd8 100644 --- a/packages/vocab-runtime/src/docloader.test.ts +++ b/packages/vocab-runtime/src/docloader.test.ts @@ -8,6 +8,7 @@ import { gzipSync } from "node:zlib"; import preloadedContexts from "./contexts.ts"; import cidV1Context from "./contexts/cid-v1.json" with { type: "json" }; import { getDocumentLoader, getRemoteDocument } from "./docloader.ts"; +import jsonld from "./jsonld.ts"; import { FetchError } from "./request.ts"; import { UrlError } from "./url.ts"; @@ -367,6 +368,47 @@ test("getDocumentLoader()", async (t) => { deepStrictEqual(fetchMock.callHistory.calls(url).length, 0); }); + // A Codeberg Pages outage must not prevent loading or expanding FEP-6757. + // See: https://github.com/fedify-dev/fedify/issues/1211 + fetchMock.get("https://w3id.org/fep/6757", { status: 502 }); + await t.test("preloaded FEP-6757 context", async () => { + const url = "https://w3id.org/fep/6757"; + deepStrictEqual(await fetchDocumentLoader(url), { + contextUrl: null, + documentUrl: url, + document: { + "@context": { + license: { + "@id": "http://purl.org/dc/terms/license", + "@type": "@id", + }, + preferredLicense: { + "@id": "https://w3id.org/fep/6757#preferredLicense", + "@type": "@id", + }, + }, + }, + }); + deepStrictEqual( + await jsonld.expand({ + "@context": ["https://www.w3.org/ns/activitystreams", url], + type: "Person", + license: "https://creativecommons.org/licenses/by/4.0/", + preferredLicense: "https://creativecommons.org/licenses/by-nc/4.0/", + }, { documentLoader: fetchDocumentLoader }), + [{ + "@type": ["https://www.w3.org/ns/activitystreams#Person"], + "http://purl.org/dc/terms/license": [{ + "@id": "https://creativecommons.org/licenses/by/4.0/", + }], + "https://w3id.org/fep/6757#preferredLicense": [{ + "@id": "https://creativecommons.org/licenses/by-nc/4.0/", + }], + }], + ); + deepStrictEqual(fetchMock.callHistory.calls(url).length, 0); + }); + await t.test("deny non-HTTP/HTTPS", async () => { await rejects( () => fetchDocumentLoader("ftp://localhost"), diff --git a/packages/vocab-runtime/src/docloader.ts b/packages/vocab-runtime/src/docloader.ts index f89586cad..5827e176b 100644 --- a/packages/vocab-runtime/src/docloader.ts +++ b/packages/vocab-runtime/src/docloader.ts @@ -280,6 +280,7 @@ export interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions { * - * - * - + * - * - * - * @param options Options for the document loader.