@fedify/vocab 2.4.0-dev.2247 → 2.4.0-dev.2256

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,14 +1,15 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
- import { A as Object$1, B as QuoteAuthorization, C as Intent, D as Measure, E as Link, F as Person, G as Tombstone, H as Reject, I as Place, L as PropertyValue, N as OrderedCollectionPage, O as Multikey, P as Organization, R as Proposal, S as Image, T as InteractionRule, U as Service, V as QuoteRequest, W as Source, X as vocab_exports, _ as FeaturedCollection, a as Application, b as Group, d as CryptographicKey, f as Delete, g as FeatureRequest, h as FeatureAuthorization, i as Announce, j as Offer, k as Note, l as Commitment, m as Endpoints, n as Activity, p as Document, r as Agreement, s as Collection, t as Accept, u as Create, v as FeaturedItem, w as InteractionPolicy, x as Hashtag, y as Follow, z as Question } from "./vocab-D3C791xF.mjs";
4
- import { t as esm_default } from "./esm-2_Uqq_my.mjs";
3
+ import { A as Object$1, B as QuoteAuthorization, C as Intent, D as Measure, E as Link, F as Person, G as Tombstone, H as Reject, I as Place, L as PropertyValue, N as OrderedCollectionPage, O as Multikey, P as Organization, R as Proposal, S as Image, T as InteractionRule, U as Service, V as QuoteRequest, W as Source, X as vocab_exports, _ as FeaturedCollection, a as Application, b as Group, d as CryptographicKey, f as Delete, g as FeatureRequest, h as FeatureAuthorization, i as Announce, j as Offer, k as Note, l as Commitment, m as Endpoints, n as Activity, p as Document, r as Agreement, s as Collection, t as Accept, u as Create, v as FeaturedItem, w as InteractionPolicy, x as Hashtag, y as Follow, z as Question } from "./vocab-qCLWHa7T.mjs";
4
+ import { t as esm_default } from "./esm-CS5DInHn.mjs";
5
5
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
6
6
  import { mockDocumentLoader, test } from "@fedify/fixture";
7
7
  import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
8
- import { FetchError, LanguageString, decodeMultibase, getDocumentLoader, parseDecimal } from "@fedify/vocab-runtime";
8
+ import { FetchError, LanguageString, decodeMultibase, getDocumentLoader, parseDecimal, parseIri } from "@fedify/vocab-runtime";
9
9
  import { configure, reset } from "@logtape/logtape";
10
10
  import { pascalCase } from "es-toolkit";
11
11
  import { areAllScalarTypes, loadSchemaFiles } from "@fedify/vocab-tools";
12
+ import { inspect } from "node:util";
12
13
  //#region src/vocab.test.ts
13
14
  const NOTE_QUOTE_CONTEXT = [
14
15
  "https://www.w3.org/ns/activitystreams",
@@ -444,17 +445,112 @@ test("FEP-ef61: actor gateways round-trip as an ordered URI list", async () => {
444
445
  gateways: gateways.map((gateway) => gateway.href)
445
446
  });
446
447
  deepStrictEqual(actor.gateways, gateways);
447
- const jsonLd = await actor.toJsonLd();
448
+ const jsonLd = await actor.toJsonLd({ format: "compact" });
448
449
  deepStrictEqual(jsonLd.type, ActorClass.name);
449
- deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.href));
450
+ deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.origin));
450
451
  deepStrictEqual((await ActorClass.fromJsonLd(jsonLd)).gateways, gateways);
451
452
  }
452
453
  });
454
+ test("FEP-ef61: actor gateways serialize as origins", async () => {
455
+ const actorClasses = [
456
+ Application,
457
+ Group,
458
+ Organization,
459
+ Person,
460
+ Service
461
+ ];
462
+ const gateways = [
463
+ new URL("https://first.example:443/"),
464
+ new URL("http://second.example:80/"),
465
+ new URL("https://third.example:8443/"),
466
+ new URL("http://fourth.example:8080/")
467
+ ];
468
+ const origins = [
469
+ "https://first.example",
470
+ "http://second.example",
471
+ "https://third.example:8443",
472
+ "http://fourth.example:8080"
473
+ ];
474
+ for (const ActorClass of actorClasses) {
475
+ const actor = new ActorClass({
476
+ id: parseIri("ap://did:key:z6Mkabc/actor"),
477
+ gateways
478
+ });
479
+ const compact = await actor.toJsonLd();
480
+ deepStrictEqual(compact.gateways, origins);
481
+ const expanded = await actor.toJsonLd({ format: "expand" });
482
+ deepStrictEqual(expanded[0]["https://w3id.org/fep/ef61/gateways"], [{ "@list": origins.map((origin) => ({ "@id": origin })) }]);
483
+ deepStrictEqual(actor.gateways, gateways);
484
+ deepStrictEqual(actor.gateways.map((gateway) => gateway.href), origins.map((origin) => `${origin}/`));
485
+ deepStrictEqual((await ActorClass.fromJsonLd(compact)).gateways, gateways);
486
+ deepStrictEqual((await ActorClass.fromJsonLd(expanded)).gateways, gateways);
487
+ }
488
+ });
489
+ test("FEP-ef61: portable actors parse unmapped gateway lists", async () => {
490
+ const actorClasses = [
491
+ Application,
492
+ Group,
493
+ Organization,
494
+ Person,
495
+ Service
496
+ ];
497
+ const gateways = ["https://first.example", "https://second.example"];
498
+ const ids = ["ap://did:key:z6Mkabc/actor", "https://first.example/.well-known/apgateway/did:key:z6Mkabc/actor"];
499
+ for (const ActorClass of actorClasses) for (const id of ids) deepStrictEqual((await ActorClass.fromJsonLd({
500
+ "@context": "https://www.w3.org/ns/activitystreams",
501
+ type: ActorClass.name,
502
+ id,
503
+ inbox: `${id}/inbox`,
504
+ outbox: `${id}/outbox`,
505
+ gateways
506
+ })).gateways, gateways.map((g) => new URL(g)));
507
+ });
508
+ test("FEP-ef61: unmapped gateways retain their context meaning", async () => {
509
+ const actorClasses = [
510
+ Application,
511
+ Group,
512
+ Organization,
513
+ Person,
514
+ Service
515
+ ];
516
+ const id = "ap://did:key:z6Mkabc/actor";
517
+ for (const ActorClass of actorClasses) {
518
+ const document = {
519
+ type: ActorClass.name,
520
+ id,
521
+ inbox: `${id}/inbox`,
522
+ outbox: `${id}/outbox`,
523
+ gateways: ["https://unmapped.example"]
524
+ };
525
+ for (const context of [
526
+ { gateways: "https://example.com/unrelated" },
527
+ { gateways: null },
528
+ { "@vocab": "https://example.com/" },
529
+ { [ActorClass.name]: {
530
+ "@id": `https://www.w3.org/ns/activitystreams#${ActorClass.name}`,
531
+ "@context": { gateways: "https://example.com/scoped" }
532
+ } }
533
+ ]) deepStrictEqual((await ActorClass.fromJsonLd({
534
+ "@context": ["https://www.w3.org/ns/activitystreams", context],
535
+ ...document
536
+ })).gateways, []);
537
+ deepStrictEqual((await ActorClass.fromJsonLd({
538
+ "@context": "https://www.w3.org/ns/activitystreams",
539
+ ...document,
540
+ id: "https://ordinary.example/actor"
541
+ })).gateways, []);
542
+ deepStrictEqual((await ActorClass.fromJsonLd({
543
+ "@context": "https://www.w3.org/ns/activitystreams",
544
+ ...document,
545
+ "https://w3id.org/fep/ef61/gateways": { "@list": [{ "@id": "https://canonical.example" }] }
546
+ })).gateways, [new URL("https://canonical.example")]);
547
+ }
548
+ });
453
549
  test("FEP-ef61: actor gateways preserve single, empty, and invalid cases", async () => {
454
550
  deepStrictEqual((await new Person({
455
551
  id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
456
552
  gateways: [new URL("https://server.example/")]
457
- }).toJsonLd()).gateways, ["https://server.example/"]);
553
+ }).toJsonLd()).gateways, ["https://server.example"]);
458
554
  ok(!("gateways" in await new Person({
459
555
  id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
460
556
  gateways: []
@@ -1442,6 +1538,21 @@ test({
1442
1538
  }), "Deno" in globalThis ? "Object {\n attribution: URL \"https://example.com/foo\",\n contents: [\n <en> \"Hello\",\n <zh> \"你好\"\n ],\n id: URL \"https://example.com/\",\n name: \"Test\"\n}" : "Object {\n attribution: URL 'https://example.com/foo',\n contents: [\n <en> 'Hello',\n <zh> '你好'\n ],\n id: URL 'https://example.com/',\n name: 'Test'\n}");
1443
1539
  }
1444
1540
  });
1541
+ test("inspecting an object prints portable IRIs in their canonical form", () => {
1542
+ const did = "did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2";
1543
+ const obj = new Object$1({
1544
+ id: parseIri(`ap://${did}/note`),
1545
+ attribution: parseIri(`ap://${did}/actor`)
1546
+ });
1547
+ const inspected = [inspect(obj, { colors: false })];
1548
+ if ("Deno" in globalThis) inspected.push(Deno.inspect(obj, { colors: false }));
1549
+ for (const text of inspected) {
1550
+ ok(text.includes(`ap+ef61://${did}/note`), text);
1551
+ ok(text.includes(`ap+ef61://${did}/actor`), text);
1552
+ ok(!text.includes("%3A"), text);
1553
+ }
1554
+ ok(inspect(new Object$1({ id: new URL("ap://not-a-did/note") })).includes("ap://not-a-did/note"));
1555
+ });
1445
1556
  test("Person.fromJsonLd()", async () => {
1446
1557
  const person = await Person.fromJsonLd({
1447
1558
  "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.2247+4fe1b8c3",
3
+ "version": "2.4.0-dev.2256+683c50ba",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,9 +43,9 @@
43
43
  "jsonld": "^9.0.0",
44
44
  "pkijs": "^3.3.3",
45
45
  "temporal-polyfill": "^1.0.1",
46
- "@fedify/webfinger": "2.4.0-dev.2247+4fe1b8c3",
47
- "@fedify/vocab-tools": "2.4.0-dev.2247+4fe1b8c3",
48
- "@fedify/vocab-runtime": "2.4.0-dev.2247+4fe1b8c3"
46
+ "@fedify/vocab-tools": "2.4.0-dev.2256+683c50ba",
47
+ "@fedify/vocab-runtime": "2.4.0-dev.2256+683c50ba",
48
+ "@fedify/webfinger": "2.4.0-dev.2256+683c50ba"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^24.12.2",
package/src/vocab.test.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  getDocumentLoader,
7
7
  LanguageString,
8
8
  parseDecimal,
9
+ parseIri,
9
10
  type RemoteDocument,
10
11
  } from "@fedify/vocab-runtime";
11
12
  import {
@@ -24,6 +25,7 @@ import {
24
25
  rejects,
25
26
  throws,
26
27
  } from "node:assert/strict";
28
+ import { inspect } from "node:util";
27
29
  import { assertInstanceOf } from "./utils.ts";
28
30
  import * as vocab from "./vocab.ts";
29
31
  import {
@@ -711,15 +713,123 @@ test("FEP-ef61: actor gateways round-trip as an ordered URI list", async () => {
711
713
  });
712
714
  deepStrictEqual(actor.gateways, gateways);
713
715
 
714
- const jsonLd = await actor.toJsonLd() as Record<string, unknown>;
716
+ const jsonLd = await actor.toJsonLd({
717
+ format: "compact",
718
+ }) as Record<string, unknown>;
715
719
  deepStrictEqual(jsonLd.type, ActorClass.name);
716
- deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.href));
720
+ deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.origin));
717
721
 
718
722
  const restored = await ActorClass.fromJsonLd(jsonLd);
719
723
  deepStrictEqual(restored.gateways, gateways);
720
724
  }
721
725
  });
722
726
 
727
+ test("FEP-ef61: actor gateways serialize as origins", async () => {
728
+ const actorClasses = [Application, Group, Organization, Person, Service];
729
+ const gateways = [
730
+ new URL("https://first.example:443/"),
731
+ new URL("http://second.example:80/"),
732
+ new URL("https://third.example:8443/"),
733
+ new URL("http://fourth.example:8080/"),
734
+ ];
735
+ const origins = [
736
+ "https://first.example",
737
+ "http://second.example",
738
+ "https://third.example:8443",
739
+ "http://fourth.example:8080",
740
+ ];
741
+ for (const ActorClass of actorClasses) {
742
+ const actor = new ActorClass({
743
+ id: parseIri("ap://did:key:z6Mkabc/actor"),
744
+ gateways,
745
+ });
746
+ const compact = await actor.toJsonLd() as Record<string, unknown>;
747
+ deepStrictEqual(compact.gateways, origins);
748
+ const expanded = await actor.toJsonLd({
749
+ format: "expand",
750
+ }) as Record<string, unknown>[];
751
+ deepStrictEqual(expanded[0]["https://w3id.org/fep/ef61/gateways"], [
752
+ { "@list": origins.map((origin) => ({ "@id": origin })) },
753
+ ]);
754
+ deepStrictEqual(actor.gateways, gateways);
755
+ deepStrictEqual(
756
+ actor.gateways.map((gateway) => gateway.href),
757
+ origins.map((origin) => `${origin}/`),
758
+ );
759
+ deepStrictEqual((await ActorClass.fromJsonLd(compact)).gateways, gateways);
760
+ deepStrictEqual((await ActorClass.fromJsonLd(expanded)).gateways, gateways);
761
+ }
762
+ });
763
+
764
+ test("FEP-ef61: portable actors parse unmapped gateway lists", async () => {
765
+ const actorClasses = [Application, Group, Organization, Person, Service];
766
+ const gateways = ["https://first.example", "https://second.example"];
767
+ const ids = [
768
+ "ap://did:key:z6Mkabc/actor",
769
+ "https://first.example/.well-known/apgateway/did:key:z6Mkabc/actor",
770
+ ];
771
+ for (const ActorClass of actorClasses) {
772
+ for (const id of ids) {
773
+ const actor = await ActorClass.fromJsonLd({
774
+ "@context": "https://www.w3.org/ns/activitystreams",
775
+ type: ActorClass.name,
776
+ id,
777
+ inbox: `${id}/inbox`,
778
+ outbox: `${id}/outbox`,
779
+ gateways,
780
+ });
781
+ deepStrictEqual(actor.gateways, gateways.map((g) => new URL(g)));
782
+ }
783
+ }
784
+ });
785
+
786
+ test("FEP-ef61: unmapped gateways retain their context meaning", async () => {
787
+ const actorClasses = [Application, Group, Organization, Person, Service];
788
+ const id = "ap://did:key:z6Mkabc/actor";
789
+ for (const ActorClass of actorClasses) {
790
+ const document = {
791
+ type: ActorClass.name,
792
+ id,
793
+ inbox: `${id}/inbox`,
794
+ outbox: `${id}/outbox`,
795
+ gateways: ["https://unmapped.example"],
796
+ };
797
+ for (
798
+ const context of [
799
+ { gateways: "https://example.com/unrelated" },
800
+ { gateways: null },
801
+ { "@vocab": "https://example.com/" },
802
+ {
803
+ [ActorClass.name]: {
804
+ "@id": `https://www.w3.org/ns/activitystreams#${ActorClass.name}`,
805
+ "@context": { gateways: "https://example.com/scoped" },
806
+ },
807
+ },
808
+ ]
809
+ ) {
810
+ const actor = await ActorClass.fromJsonLd({
811
+ "@context": ["https://www.w3.org/ns/activitystreams", context],
812
+ ...document,
813
+ });
814
+ deepStrictEqual(actor.gateways, []);
815
+ }
816
+ const ordinary = await ActorClass.fromJsonLd({
817
+ "@context": "https://www.w3.org/ns/activitystreams",
818
+ ...document,
819
+ id: "https://ordinary.example/actor",
820
+ });
821
+ deepStrictEqual(ordinary.gateways, []);
822
+ const canonical = await ActorClass.fromJsonLd({
823
+ "@context": "https://www.w3.org/ns/activitystreams",
824
+ ...document,
825
+ "https://w3id.org/fep/ef61/gateways": {
826
+ "@list": [{ "@id": "https://canonical.example" }],
827
+ },
828
+ });
829
+ deepStrictEqual(canonical.gateways, [new URL("https://canonical.example")]);
830
+ }
831
+ });
832
+
723
833
  test("FEP-ef61: actor gateways preserve single, empty, and invalid cases", async () => {
724
834
  const singleGateway = new Person({
725
835
  id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
@@ -727,7 +837,7 @@ test("FEP-ef61: actor gateways preserve single, empty, and invalid cases", async
727
837
  });
728
838
  deepStrictEqual(
729
839
  (await singleGateway.toJsonLd() as Record<string, unknown>).gateways,
730
- ["https://server.example/"],
840
+ ["https://server.example"],
731
841
  );
732
842
 
733
843
  const noGateways = new Person({
@@ -2212,6 +2322,26 @@ test({
2212
2322
  },
2213
2323
  });
2214
2324
 
2325
+ test("inspecting an object prints portable IRIs in their canonical form", () => {
2326
+ const did = "did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2";
2327
+ const obj = new Object({
2328
+ id: parseIri(`ap://${did}/note`),
2329
+ attribution: parseIri(`ap://${did}/actor`),
2330
+ });
2331
+ const inspected = [inspect(obj, { colors: false })];
2332
+ if ("Deno" in globalThis) {
2333
+ inspected.push(Deno.inspect(obj, { colors: false }));
2334
+ }
2335
+ for (const text of inspected) {
2336
+ ok(text.includes(`ap+ef61://${did}/note`), text);
2337
+ ok(text.includes(`ap+ef61://${did}/actor`), text);
2338
+ ok(!text.includes("%3A"), text);
2339
+ }
2340
+ // A URL that cannot be formatted as an IRI does not break inspection:
2341
+ const malformed = new Object({ id: new URL("ap://not-a-did/note") });
2342
+ ok(inspect(malformed).includes("ap://not-a-did/note"));
2343
+ });
2344
+
2215
2345
  test("Person.fromJsonLd()", async () => {
2216
2346
  const person = await Person.fromJsonLd({
2217
2347
  "@context": [