@fedify/vocab-runtime 2.0.31-dev.2297 → 2.0.31-dev.2308

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-DlZHvlsq.mjs";
2
- import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-DkPsw4D7.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BV7yaWdl.mjs";
2
+ import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-Bx6-vXo_.mjs";
3
3
  import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
4
4
  import { o as validatePublicUrl, t as UrlError } from "./url-DzlMS01z.mjs";
5
5
  import { test } from "node:test";
@@ -1344,6 +1344,36 @@ var cid_v1_default = { "@context": {
1344
1344
  }
1345
1345
  } };
1346
1346
  //#endregion
1347
+ //#region src/contexts/fep-22cd.json
1348
+ var fep_22cd_default = { "@context": {
1349
+ "fep-22cd": "https://w3id.org/fep/22cd#",
1350
+ "schema": "https://schema.org/",
1351
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
1352
+ "translations": {
1353
+ "@id": "fep-22cd:translations",
1354
+ "@container": "@set"
1355
+ },
1356
+ "Translation": "fep-22cd:Translation",
1357
+ "sourceUpdated": {
1358
+ "@id": "fep-22cd:sourceUpdated",
1359
+ "@type": "xsd:dateTime"
1360
+ },
1361
+ "translator": {
1362
+ "@id": "schema:translator",
1363
+ "@type": "@id",
1364
+ "@container": "@set"
1365
+ },
1366
+ "inLanguage": "schema:inLanguage",
1367
+ "translationOfWork": {
1368
+ "@id": "schema:translationOfWork",
1369
+ "@type": "@id"
1370
+ },
1371
+ "isBasedOn": {
1372
+ "@id": "schema:isBasedOn",
1373
+ "@type": "@id"
1374
+ }
1375
+ } };
1376
+ //#endregion
1347
1377
  //#region src/contexts/fep-6757.json
1348
1378
  var fep_6757_default = { "@context": {
1349
1379
  "license": {
@@ -5657,6 +5687,7 @@ const preloadedContexts = {
5657
5687
  "https://w3id.org/fep/ef61": fep_ef61_default,
5658
5688
  "https://w3id.org/fep/7aa9": fep_7aa9_default,
5659
5689
  "https://w3id.org/fep/6757": fep_6757_default,
5690
+ "https://w3id.org/fep/22cd": fep_22cd_default,
5660
5691
  "https://join-lemmy.org/context.json": join_lemmy_default,
5661
5692
  "http://joinmastodon.org/ns": { "@context": {
5662
5693
  "toot": "http://joinmastodon.org/ns#",
@@ -5799,6 +5830,7 @@ async function getRemoteDocument(url, response, fetch) {
5799
5830
  * - <https://w3id.org/fep/ef61>
5800
5831
  * - <https://w3id.org/fep/7aa9>
5801
5832
  * - <https://w3id.org/fep/6757>
5833
+ * - <https://w3id.org/fep/22cd>
5802
5834
  * - <https://purl.archive.org/socialweb/webfinger>
5803
5835
  * - <http://schema.org/>
5804
5836
  * @param options Options for the document loader.
@@ -6197,6 +6229,85 @@ test("getDocumentLoader()", async (t) => {
6197
6229
  }]);
6198
6230
  deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
6199
6231
  });
6232
+ esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
6233
+ await t.test("preloaded FEP-22cd context", async () => {
6234
+ const url = "https://w3id.org/fep/22cd";
6235
+ const articleId = "https://example.com/articles/1";
6236
+ const translatorId = "https://example.com/users/alice";
6237
+ const revisionId = "https://example.com/articles/1/revisions/1";
6238
+ deepStrictEqual(await fetchDocumentLoader(url), {
6239
+ contextUrl: null,
6240
+ documentUrl: url,
6241
+ document: { "@context": {
6242
+ "fep-22cd": "https://w3id.org/fep/22cd#",
6243
+ schema: "https://schema.org/",
6244
+ xsd: "http://www.w3.org/2001/XMLSchema#",
6245
+ translations: {
6246
+ "@id": "fep-22cd:translations",
6247
+ "@container": "@set"
6248
+ },
6249
+ Translation: "fep-22cd:Translation",
6250
+ sourceUpdated: {
6251
+ "@id": "fep-22cd:sourceUpdated",
6252
+ "@type": "xsd:dateTime"
6253
+ },
6254
+ translator: {
6255
+ "@id": "schema:translator",
6256
+ "@type": "@id",
6257
+ "@container": "@set"
6258
+ },
6259
+ inLanguage: "schema:inLanguage",
6260
+ translationOfWork: {
6261
+ "@id": "schema:translationOfWork",
6262
+ "@type": "@id"
6263
+ },
6264
+ isBasedOn: {
6265
+ "@id": "schema:isBasedOn",
6266
+ "@type": "@id"
6267
+ }
6268
+ } }
6269
+ });
6270
+ deepStrictEqual(await jsonld_default.expand({
6271
+ "@context": ["https://www.w3.org/ns/activitystreams", url],
6272
+ id: articleId,
6273
+ type: "Article",
6274
+ contentMap: {
6275
+ en: "Original",
6276
+ ko: "Translation"
6277
+ },
6278
+ translations: [{
6279
+ type: "Translation",
6280
+ inLanguage: "ko",
6281
+ translator: [translatorId],
6282
+ translationOfWork: articleId,
6283
+ sourceUpdated: "2026-09-01T00:00:00Z",
6284
+ isBasedOn: revisionId
6285
+ }]
6286
+ }, { documentLoader: fetchDocumentLoader }), [{
6287
+ "@id": articleId,
6288
+ "@type": ["https://www.w3.org/ns/activitystreams#Article"],
6289
+ "https://www.w3.org/ns/activitystreams#content": [{
6290
+ "@language": "en",
6291
+ "@value": "Original"
6292
+ }, {
6293
+ "@language": "ko",
6294
+ "@value": "Translation"
6295
+ }],
6296
+ "https://w3id.org/fep/22cd#translations": [{
6297
+ "@type": ["https://w3id.org/fep/22cd#Translation"],
6298
+ "https://schema.org/inLanguage": [{ "@value": "ko" }],
6299
+ "https://schema.org/translator": [{ "@id": translatorId }],
6300
+ "https://schema.org/translationOfWork": [{ "@id": articleId }],
6301
+ "https://w3id.org/fep/22cd#sourceUpdated": [{
6302
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
6303
+ "@value": "2026-09-01T00:00:00Z"
6304
+ }],
6305
+ "https://schema.org/isBasedOn": [{ "@id": revisionId }]
6306
+ }]
6307
+ }]);
6308
+ deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
6309
+ deepStrictEqual(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
6310
+ });
6200
6311
  await t.test("deny non-HTTP/HTTPS", async () => {
6201
6312
  await rejects(() => fetchDocumentLoader("ftp://localhost"), UrlError);
6202
6313
  });
@@ -1,4 +1,5 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
+ const require_langtag = require("./langtag-Bm8oKhIO.cjs");
2
3
  let node_test = require("node:test");
3
4
  let node_assert = require("node:assert");
4
5
  let node_util = require("node:util");
@@ -17,11 +18,13 @@ var LanguageString = class extends String {
17
18
  * Constructs a new `LanguageString`.
18
19
  * @param value A string value written in the given language.
19
20
  * @param language The language of the string. If a string is given, it will
20
- * be parsed as a `Intl.Locale` object.
21
+ * be parsed as a `Intl.Locale` object. Extended language
22
+ * subtags are normalized first, e.g., `zh-yue` is parsed as
23
+ * `yue`.
21
24
  */
22
25
  constructor(value, language) {
23
26
  super(value);
24
- this.locale = typeof language === "string" ? new Intl.Locale(language) : language;
27
+ this.locale = typeof language === "string" ? new Intl.Locale(require_langtag.normalizeLanguageTag(language)) : language;
25
28
  }
26
29
  };
27
30
  LanguageString.prototype[Symbol.for("Deno.customInspect")] = function(inspect, options) {
@@ -38,6 +41,13 @@ LanguageString.prototype[Symbol.for("nodejs.util.inspect.custom")] = function(_d
38
41
  (0, node_assert.deepStrictEqual)(langStr.locale, new Intl.Locale("en"));
39
42
  (0, node_assert.deepStrictEqual)(new LanguageString("Hello", new Intl.Locale("en")), langStr);
40
43
  });
44
+ (0, node_test.test)("new LanguageString() with extlang tags", () => {
45
+ (0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
46
+ (0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-yue-HK").locale.baseName, "yue-HK");
47
+ (0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-cmn-Hant").locale.baseName, new Intl.Locale("cmn-Hant").baseName);
48
+ (0, node_assert.throws)(() => new LanguageString("Hello", "en-USA"), RangeError);
49
+ (0, node_assert.throws)(() => new LanguageString("你好", "zh-yue-"), RangeError);
50
+ });
41
51
  (0, node_test.test)("Deno.inspect(LanguageString)", () => {
42
52
  const langStr = new LanguageString("Hello, 'world'", "en");
43
53
  (0, node_assert.deepStrictEqual)(node_util.default.inspect(langStr, { colors: false }), "<en> \"Hello, 'world'\"");
@@ -1,5 +1,6 @@
1
+ import { t as normalizeLanguageTag } from "./langtag-QZZgy903.mjs";
1
2
  import { test } from "node:test";
2
- import { deepStrictEqual } from "node:assert";
3
+ import { deepStrictEqual, throws } from "node:assert";
3
4
  import util from "node:util";
4
5
  //#region src/langstr.ts
5
6
  /**
@@ -15,11 +16,13 @@ var LanguageString = class extends String {
15
16
  * Constructs a new `LanguageString`.
16
17
  * @param value A string value written in the given language.
17
18
  * @param language The language of the string. If a string is given, it will
18
- * be parsed as a `Intl.Locale` object.
19
+ * be parsed as a `Intl.Locale` object. Extended language
20
+ * subtags are normalized first, e.g., `zh-yue` is parsed as
21
+ * `yue`.
19
22
  */
20
23
  constructor(value, language) {
21
24
  super(value);
22
- this.locale = typeof language === "string" ? new Intl.Locale(language) : language;
25
+ this.locale = typeof language === "string" ? new Intl.Locale(normalizeLanguageTag(language)) : language;
23
26
  }
24
27
  };
25
28
  LanguageString.prototype[Symbol.for("Deno.customInspect")] = function(inspect, options) {
@@ -36,6 +39,13 @@ test("new LanguageString()", () => {
36
39
  deepStrictEqual(langStr.locale, new Intl.Locale("en"));
37
40
  deepStrictEqual(new LanguageString("Hello", new Intl.Locale("en")), langStr);
38
41
  });
42
+ test("new LanguageString() with extlang tags", () => {
43
+ deepStrictEqual(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
44
+ deepStrictEqual(new LanguageString("你好", "zh-yue-HK").locale.baseName, "yue-HK");
45
+ deepStrictEqual(new LanguageString("你好", "zh-cmn-Hant").locale.baseName, new Intl.Locale("cmn-Hant").baseName);
46
+ throws(() => new LanguageString("Hello", "en-USA"), RangeError);
47
+ throws(() => new LanguageString("你好", "zh-yue-"), RangeError);
48
+ });
39
49
  test("Deno.inspect(LanguageString)", () => {
40
50
  const langStr = new LanguageString("Hello, 'world'", "en");
41
51
  deepStrictEqual(util.inspect(langStr, { colors: false }), "<en> \"Hello, 'world'\"");
@@ -0,0 +1,50 @@
1
+ //#region src/langtag.ts
2
+ const EXTLANG_PAIRS = new Set(Object.entries({
3
+ ar: "aao abh abv acm acq acw acx acy adf aeb aec afb ajp apc apd arb arq ars ary arz auz avl ayh ayl ayn ayp bbz pga shu ssh",
4
+ kok: "gom knn",
5
+ lv: "ltg lvs",
6
+ ms: "bjn btj bve bvu coa dup hji jak jax kvb kvr kxd lce lcf liw max meo mfa mfb min mqg msi mui orn ors pel pse tmw urk vkk vkt xmm zlm zmi zsm",
7
+ sgn: "ads aed aen afg ajs ase asf asp asq asw bfi bfk bog bqn bqy bvl bzs cds csc csd cse csf csg csl csn csq csr csx doq dse dsl dsz dyl ecs ehs esl esn eso eth fcs fse fsl fss gds gse gsg gsm gss gus hab haf hds hks hos hps hsh hsl icl iks ils inl ins ise isg isr jcs jhs jks jls jos jsl jus kgi kvk lbs lgs lls lsb lsc lsg lsl lsn lso lsp lst lsv lsw lsy lws mdl mfs mre msd msr mzc mzg mzy nbs ncs nsi nsl nsp nsr nzs okl pgz pks prl prz psc psd psg psl pso psp psr pys rib rms rnb rsi rsl rsm rsn sdl sfb sfs sgg sgx slf sls sqk sqs sqx ssp ssr svk swl syy szs tse tsm tsq tss tsy tza ugn ugy ukl uks vgt vsi vsl vsv wbs xki xml xms yds ygs yhs ysl ysm zhk zib zsl",
8
+ sw: "swc swh",
9
+ uz: "uzn uzs",
10
+ zh: "cdo cjy cmn cnp cpx csp czh czo gan hak hnm hsn luh lzh mnp nan sjc wuu yue"
11
+ }).flatMap(([prefix, extlangs]) => extlangs.split(" ").map((extlang) => `${prefix}-${extlang}`)));
12
+ const PREFIX_PATTERN = /^[A-Za-z]{2,3}$/;
13
+ const EXTLANG_PATTERN = /^[A-Za-z]{3}$/;
14
+ /**
15
+ * Normalizes a BCP 47 language tag that has an extended language subtag
16
+ * (extlang), such as `zh-yue`, into its canonical form, such as `yue`, as
17
+ * described in [RFC 5646, Section 4.5][1]. `Intl.Locale` rejects extlang
18
+ * tags, so tags should be normalized before being passed to it.
19
+ *
20
+ * Only registered extlangs that follow their registered prefix are removed;
21
+ * any other tag is returned unchanged, so the result still needs to be
22
+ * validated, e.g., by `Intl.Locale`.
23
+ *
24
+ * NOTE: This function is marked as internal in the 2.0.x–2.4.x patch
25
+ * releases. When merging it into the *main* branch, remove the `@internal`
26
+ * tag, formalize it as a public API (`@since 2.5.0`), and add it to
27
+ * the changelog fragment.
28
+ *
29
+ * [1]: https://www.rfc-editor.org/rfc/rfc5646.html#section-4.5
30
+ *
31
+ * @param tag The BCP 47 language tag to normalize.
32
+ * @returns The normalized language tag.
33
+ * @internal
34
+ */
35
+ function normalizeLanguageTag(tag) {
36
+ const subtags = tag.split("-");
37
+ if (subtags.length < 2) return tag;
38
+ const [prefix, extlang, ...rest] = subtags;
39
+ if (!PREFIX_PATTERN.test(prefix) || !EXTLANG_PATTERN.test(extlang)) return tag;
40
+ const language = extlang.toLowerCase();
41
+ if (!EXTLANG_PAIRS.has(`${prefix.toLowerCase()}-${language}`)) return tag;
42
+ return [language, ...rest].join("-");
43
+ }
44
+ //#endregion
45
+ Object.defineProperty(exports, "normalizeLanguageTag", {
46
+ enumerable: true,
47
+ get: function() {
48
+ return normalizeLanguageTag;
49
+ }
50
+ });
@@ -0,0 +1,45 @@
1
+ //#region src/langtag.ts
2
+ const EXTLANG_PAIRS = new Set(Object.entries({
3
+ ar: "aao abh abv acm acq acw acx acy adf aeb aec afb ajp apc apd arb arq ars ary arz auz avl ayh ayl ayn ayp bbz pga shu ssh",
4
+ kok: "gom knn",
5
+ lv: "ltg lvs",
6
+ ms: "bjn btj bve bvu coa dup hji jak jax kvb kvr kxd lce lcf liw max meo mfa mfb min mqg msi mui orn ors pel pse tmw urk vkk vkt xmm zlm zmi zsm",
7
+ sgn: "ads aed aen afg ajs ase asf asp asq asw bfi bfk bog bqn bqy bvl bzs cds csc csd cse csf csg csl csn csq csr csx doq dse dsl dsz dyl ecs ehs esl esn eso eth fcs fse fsl fss gds gse gsg gsm gss gus hab haf hds hks hos hps hsh hsl icl iks ils inl ins ise isg isr jcs jhs jks jls jos jsl jus kgi kvk lbs lgs lls lsb lsc lsg lsl lsn lso lsp lst lsv lsw lsy lws mdl mfs mre msd msr mzc mzg mzy nbs ncs nsi nsl nsp nsr nzs okl pgz pks prl prz psc psd psg psl pso psp psr pys rib rms rnb rsi rsl rsm rsn sdl sfb sfs sgg sgx slf sls sqk sqs sqx ssp ssr svk swl syy szs tse tsm tsq tss tsy tza ugn ugy ukl uks vgt vsi vsl vsv wbs xki xml xms yds ygs yhs ysl ysm zhk zib zsl",
8
+ sw: "swc swh",
9
+ uz: "uzn uzs",
10
+ zh: "cdo cjy cmn cnp cpx csp czh czo gan hak hnm hsn luh lzh mnp nan sjc wuu yue"
11
+ }).flatMap(([prefix, extlangs]) => extlangs.split(" ").map((extlang) => `${prefix}-${extlang}`)));
12
+ const PREFIX_PATTERN = /^[A-Za-z]{2,3}$/;
13
+ const EXTLANG_PATTERN = /^[A-Za-z]{3}$/;
14
+ /**
15
+ * Normalizes a BCP 47 language tag that has an extended language subtag
16
+ * (extlang), such as `zh-yue`, into its canonical form, such as `yue`, as
17
+ * described in [RFC 5646, Section 4.5][1]. `Intl.Locale` rejects extlang
18
+ * tags, so tags should be normalized before being passed to it.
19
+ *
20
+ * Only registered extlangs that follow their registered prefix are removed;
21
+ * any other tag is returned unchanged, so the result still needs to be
22
+ * validated, e.g., by `Intl.Locale`.
23
+ *
24
+ * NOTE: This function is marked as internal in the 2.0.x–2.4.x patch
25
+ * releases. When merging it into the *main* branch, remove the `@internal`
26
+ * tag, formalize it as a public API (`@since 2.5.0`), and add it to
27
+ * the changelog fragment.
28
+ *
29
+ * [1]: https://www.rfc-editor.org/rfc/rfc5646.html#section-4.5
30
+ *
31
+ * @param tag The BCP 47 language tag to normalize.
32
+ * @returns The normalized language tag.
33
+ * @internal
34
+ */
35
+ function normalizeLanguageTag(tag) {
36
+ const subtags = tag.split("-");
37
+ if (subtags.length < 2) return tag;
38
+ const [prefix, extlang, ...rest] = subtags;
39
+ if (!PREFIX_PATTERN.test(prefix) || !EXTLANG_PATTERN.test(extlang)) return tag;
40
+ const language = extlang.toLowerCase();
41
+ if (!EXTLANG_PAIRS.has(`${prefix.toLowerCase()}-${language}`)) return tag;
42
+ return [language, ...rest].join("-");
43
+ }
44
+ //#endregion
45
+ export { normalizeLanguageTag as t };
@@ -0,0 +1,46 @@
1
+ require("./chunk-Do9eywBl.cjs");
2
+ const require_langtag = require("./langtag-Bm8oKhIO.cjs");
3
+ let node_test = require("node:test");
4
+ let node_assert = require("node:assert");
5
+ //#region src/langtag.test.ts
6
+ (0, node_test.test)("normalizeLanguageTag() removes extlang subtags", () => {
7
+ for (const [input, expected] of [
8
+ ["zh-yue", "yue"],
9
+ ["zh-YUE", "yue"],
10
+ ["ZH-Yue-hk", "yue-hk"],
11
+ ["zh-yue-HK", "yue-HK"],
12
+ ["zh-cmn-Hant", "cmn-Hant"],
13
+ ["zh-yue-u-nu-hanidec", "yue-u-nu-hanidec"],
14
+ ["sgn-ase", "ase"],
15
+ ["ar-arz", "arz"],
16
+ ["ms-zlm", "zlm"],
17
+ ["kok-gom", "gom"],
18
+ ["koK-gom", "gom"],
19
+ ["lv-ltg", "ltg"],
20
+ ["sw-swc", "swc"],
21
+ ["uz-uzs", "uzs"],
22
+ ["zh-yue-cmn", "yue-cmn"],
23
+ ["zh-yue-", "yue-"]
24
+ ]) (0, node_assert.deepStrictEqual)(require_langtag.normalizeLanguageTag(input), expected, input);
25
+ });
26
+ (0, node_test.test)("normalizeLanguageTag() leaves other tags unchanged", () => {
27
+ for (const tag of [
28
+ "",
29
+ "en",
30
+ "en-US",
31
+ "en-USA",
32
+ "en-yue",
33
+ "zh",
34
+ "zh-Hant-TW",
35
+ "yue",
36
+ "yue-HK",
37
+ "x-private",
38
+ "i-klingon",
39
+ "zh-min-nan",
40
+ "koK-gom",
41
+ "zh-yuK",
42
+ "constructor-yue",
43
+ "toString-yue"
44
+ ]) (0, node_assert.deepStrictEqual)(require_langtag.normalizeLanguageTag(tag), tag, tag);
45
+ });
46
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,46 @@
1
+ import { t as normalizeLanguageTag } from "./langtag-QZZgy903.mjs";
2
+ import { test } from "node:test";
3
+ import { deepStrictEqual } from "node:assert";
4
+ //#region src/langtag.test.ts
5
+ test("normalizeLanguageTag() removes extlang subtags", () => {
6
+ for (const [input, expected] of [
7
+ ["zh-yue", "yue"],
8
+ ["zh-YUE", "yue"],
9
+ ["ZH-Yue-hk", "yue-hk"],
10
+ ["zh-yue-HK", "yue-HK"],
11
+ ["zh-cmn-Hant", "cmn-Hant"],
12
+ ["zh-yue-u-nu-hanidec", "yue-u-nu-hanidec"],
13
+ ["sgn-ase", "ase"],
14
+ ["ar-arz", "arz"],
15
+ ["ms-zlm", "zlm"],
16
+ ["kok-gom", "gom"],
17
+ ["koK-gom", "gom"],
18
+ ["lv-ltg", "ltg"],
19
+ ["sw-swc", "swc"],
20
+ ["uz-uzs", "uzs"],
21
+ ["zh-yue-cmn", "yue-cmn"],
22
+ ["zh-yue-", "yue-"]
23
+ ]) deepStrictEqual(normalizeLanguageTag(input), expected, input);
24
+ });
25
+ test("normalizeLanguageTag() leaves other tags unchanged", () => {
26
+ for (const tag of [
27
+ "",
28
+ "en",
29
+ "en-US",
30
+ "en-USA",
31
+ "en-yue",
32
+ "zh",
33
+ "zh-Hant-TW",
34
+ "yue",
35
+ "yue-HK",
36
+ "x-private",
37
+ "i-klingon",
38
+ "zh-min-nan",
39
+ "koK-gom",
40
+ "zh-yuK",
41
+ "constructor-yue",
42
+ "toString-yue"
43
+ ]) deepStrictEqual(normalizeLanguageTag(tag), tag, tag);
44
+ });
45
+ //#endregion
46
+ export {};
@@ -1,7 +1,7 @@
1
1
  import process from "node:process";
2
2
  //#region deno.json
3
3
  var name = "@fedify/vocab-runtime";
4
- var version = "2.0.31-dev.2297+58cbc917";
4
+ var version = "2.0.31-dev.2308+34090b3a";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -3,7 +3,7 @@ let node_process = require("node:process");
3
3
  node_process = require_chunk.__toESM(node_process);
4
4
  //#region deno.json
5
5
  var name = "@fedify/vocab-runtime";
6
- var version = "2.0.31-dev.2297+58cbc917";
6
+ var version = "2.0.31-dev.2308+34090b3a";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-ClhOmVbs.cjs");
2
+ const require_request = require("./request-Ban3HXuI.cjs");
3
3
  let node_test = require("node:test");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
@@ -1,4 +1,4 @@
1
- import { o as version, r as getUserAgent } from "./request-DlZHvlsq.mjs";
1
+ import { o as version, r as getUserAgent } from "./request-BV7yaWdl.mjs";
2
2
  import { test } from "node:test";
3
3
  import process from "node:process";
4
4
  import { deepStrictEqual } from "node:assert";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.0.31-dev.2297+58cbc917",
3
+ "version": "2.0.31-dev.2308+34090b3a",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,14 @@
1
+ {
2
+ "@context": {
3
+ "fep-22cd": "https://w3id.org/fep/22cd#",
4
+ "schema": "https://schema.org/",
5
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
6
+ "translations": { "@id": "fep-22cd:translations", "@container": "@set" },
7
+ "Translation": "fep-22cd:Translation",
8
+ "sourceUpdated": { "@id": "fep-22cd:sourceUpdated", "@type": "xsd:dateTime" },
9
+ "translator": { "@id": "schema:translator", "@type": "@id", "@container": "@set" },
10
+ "inLanguage": "schema:inLanguage",
11
+ "translationOfWork": { "@id": "schema:translationOfWork", "@type": "@id" },
12
+ "isBasedOn": { "@id": "schema:isBasedOn", "@type": "@id" }
13
+ }
14
+ }
package/src/contexts.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  // cSpell: disable
4
4
 
5
5
  import cidV1Context from "./contexts/cid-v1.json" with { type: "json" };
6
+ import fep22cd from "./contexts/fep-22cd.json" with { type: "json" };
6
7
  import fep6757 from "./contexts/fep-6757.json" with { type: "json" };
7
8
  import fep7aa9 from "./contexts/fep-7aa9.json" with { type: "json" };
8
9
  import fepEf61 from "./contexts/fep-ef61.json" with { type: "json" };
@@ -4353,6 +4354,13 @@ const preloadedContexts: Record<string, unknown> = {
4353
4354
  // https://github.com/fedify-dev/fedify/issues/1211
4354
4355
  "https://w3id.org/fep/6757": fep6757,
4355
4356
 
4357
+ // Resolve translation metadata locally even on versions without Translation.
4358
+ // Copied verbatim from the upstream context at the pinned revision below.
4359
+ // See: https://w3id.org/fep/22cd
4360
+ // https://github.com/fedify-dev/fedify/issues/1214
4361
+ // Source: https://codeberg.org/fediverse/fep/src/commit/7526048b018ccc0a7d05597a8f1eb33518373066/fep/22cd/fep-22cd.jsonld
4362
+ "https://w3id.org/fep/22cd": fep22cd,
4363
+
4356
4364
  // Lemmy's context document is served as application/json without the JSON-LD
4357
4365
  // context Link header. The default document loader treats that as a regular
4358
4366
  // JSON response instead of a JSON-LD context, so every Lemmy activity that
@@ -409,6 +409,88 @@ test("getDocumentLoader()", async (t) => {
409
409
  deepStrictEqual(fetchMock.callHistory.calls(url).length, 0);
410
410
  });
411
411
 
412
+ // A Codeberg Pages outage must not prevent expanding translation metadata.
413
+ // See: https://github.com/fedify-dev/fedify/issues/1214
414
+ fetchMock.get("https://w3id.org/fep/22cd", { status: 502 });
415
+ await t.test("preloaded FEP-22cd context", async () => {
416
+ const url = "https://w3id.org/fep/22cd";
417
+ const articleId = "https://example.com/articles/1";
418
+ const translatorId = "https://example.com/users/alice";
419
+ const revisionId = "https://example.com/articles/1/revisions/1";
420
+ deepStrictEqual(await fetchDocumentLoader(url), {
421
+ contextUrl: null,
422
+ documentUrl: url,
423
+ document: {
424
+ "@context": {
425
+ "fep-22cd": "https://w3id.org/fep/22cd#",
426
+ schema: "https://schema.org/",
427
+ xsd: "http://www.w3.org/2001/XMLSchema#",
428
+ translations: {
429
+ "@id": "fep-22cd:translations",
430
+ "@container": "@set",
431
+ },
432
+ Translation: "fep-22cd:Translation",
433
+ sourceUpdated: {
434
+ "@id": "fep-22cd:sourceUpdated",
435
+ "@type": "xsd:dateTime",
436
+ },
437
+ translator: {
438
+ "@id": "schema:translator",
439
+ "@type": "@id",
440
+ "@container": "@set",
441
+ },
442
+ inLanguage: "schema:inLanguage",
443
+ translationOfWork: {
444
+ "@id": "schema:translationOfWork",
445
+ "@type": "@id",
446
+ },
447
+ isBasedOn: { "@id": "schema:isBasedOn", "@type": "@id" },
448
+ },
449
+ },
450
+ });
451
+ deepStrictEqual(
452
+ await jsonld.expand({
453
+ "@context": ["https://www.w3.org/ns/activitystreams", url],
454
+ id: articleId,
455
+ type: "Article",
456
+ contentMap: { en: "Original", ko: "Translation" },
457
+ translations: [{
458
+ type: "Translation",
459
+ inLanguage: "ko",
460
+ translator: [translatorId],
461
+ translationOfWork: articleId,
462
+ sourceUpdated: "2026-09-01T00:00:00Z",
463
+ isBasedOn: revisionId,
464
+ }],
465
+ }, { documentLoader: fetchDocumentLoader }),
466
+ [{
467
+ "@id": articleId,
468
+ "@type": ["https://www.w3.org/ns/activitystreams#Article"],
469
+ "https://www.w3.org/ns/activitystreams#content": [
470
+ { "@language": "en", "@value": "Original" },
471
+ { "@language": "ko", "@value": "Translation" },
472
+ ],
473
+ "https://w3id.org/fep/22cd#translations": [{
474
+ "@type": ["https://w3id.org/fep/22cd#Translation"],
475
+ "https://schema.org/inLanguage": [{ "@value": "ko" }],
476
+ "https://schema.org/translator": [{ "@id": translatorId }],
477
+ "https://schema.org/translationOfWork": [{ "@id": articleId }],
478
+ "https://w3id.org/fep/22cd#sourceUpdated": [{
479
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
480
+ "@value": "2026-09-01T00:00:00Z",
481
+ }],
482
+ "https://schema.org/isBasedOn": [{ "@id": revisionId }],
483
+ }],
484
+ }],
485
+ );
486
+ deepStrictEqual(fetchMock.callHistory.calls(url).length, 0);
487
+ deepStrictEqual(
488
+ fetchMock.callHistory.calls("https://www.w3.org/ns/activitystreams")
489
+ .length,
490
+ 0,
491
+ );
492
+ });
493
+
412
494
  await t.test("deny non-HTTP/HTTPS", async () => {
413
495
  await rejects(
414
496
  () => fetchDocumentLoader("ftp://localhost"),
package/src/docloader.ts CHANGED
@@ -281,6 +281,7 @@ export interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
281
281
  * - <https://w3id.org/fep/ef61>
282
282
  * - <https://w3id.org/fep/7aa9>
283
283
  * - <https://w3id.org/fep/6757>
284
+ * - <https://w3id.org/fep/22cd>
284
285
  * - <https://purl.archive.org/socialweb/webfinger>
285
286
  * - <http://schema.org/>
286
287
  * @param options Options for the document loader.
@@ -1,4 +1,4 @@
1
- import { deepStrictEqual } from "node:assert";
1
+ import { deepStrictEqual, throws } from "node:assert";
2
2
  import { test } from "node:test";
3
3
  import util from "node:util";
4
4
  import { LanguageString } from "./langstr.ts";
@@ -11,6 +11,23 @@ test("new LanguageString()", () => {
11
11
  deepStrictEqual(new LanguageString("Hello", new Intl.Locale("en")), langStr);
12
12
  });
13
13
 
14
+ test("new LanguageString() with extlang tags", () => {
15
+ // Intl.Locale objects have no own enumerable properties, so their base names
16
+ // are compared instead:
17
+ deepStrictEqual(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
18
+ deepStrictEqual(
19
+ new LanguageString("你好", "zh-yue-HK").locale.baseName,
20
+ "yue-HK",
21
+ );
22
+ // Whether cmn is canonicalized to zh depends on the runtime:
23
+ deepStrictEqual(
24
+ new LanguageString("你好", "zh-cmn-Hant").locale.baseName,
25
+ new Intl.Locale("cmn-Hant").baseName,
26
+ );
27
+ throws(() => new LanguageString("Hello", "en-USA"), RangeError);
28
+ throws(() => new LanguageString("你好", "zh-yue-"), RangeError);
29
+ });
30
+
14
31
  test("Deno.inspect(LanguageString)", () => {
15
32
  const langStr = new LanguageString("Hello, 'world'", "en");
16
33
  deepStrictEqual(
package/src/langstr.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { normalizeLanguageTag } from "./langtag.ts";
2
+
1
3
  /**
2
4
  * A language-tagged string which corresponds to the `rdf:langString` type.
3
5
  */
@@ -12,12 +14,14 @@ export class LanguageString extends String {
12
14
  * Constructs a new `LanguageString`.
13
15
  * @param value A string value written in the given language.
14
16
  * @param language The language of the string. If a string is given, it will
15
- * be parsed as a `Intl.Locale` object.
17
+ * be parsed as a `Intl.Locale` object. Extended language
18
+ * subtags are normalized first, e.g., `zh-yue` is parsed as
19
+ * `yue`.
16
20
  */
17
21
  constructor(value: string, language: Intl.Locale | string) {
18
22
  super(value);
19
23
  this.locale = typeof language === "string"
20
- ? new Intl.Locale(language)
24
+ ? new Intl.Locale(normalizeLanguageTag(language))
21
25
  : language;
22
26
  }
23
27
  }