@fedify/vocab 2.4.0-dev.1583 → 2.4.0-dev.1618

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,7 +1,7 @@
1
- import { Temporal } from "@js-temporal/polyfill";
1
+ import { Temporal } from "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
- import { A as Person, C as Link, D as Offer, E as Object$1, F as QuoteRequest, H as vocab_exports, I as Reject, M as Proposal, N as Question, O as OrderedCollectionPage, P as QuoteAuthorization, R as Source, S as InteractionRule, T as Note, _ as Follow, b as Intent, c as Create, d as Document, f as Endpoints, g as FeaturedItem, h as FeaturedCollection, i as Announce, j as Place, l as CryptographicKey, m as FeatureRequest, n as Activity, o as Collection, p as FeatureAuthorization, r as Agreement, s as Commitment, t as Accept, u as Delete, w as Measure, x as InteractionPolicy, y as Hashtag, z as Tombstone } from "./vocab-DOHLQ4UQ.mjs";
4
- import { t as assertInstanceOf } from "./utils-CE8Dk5hm.mjs";
3
+ import { B as Tombstone, C as Link, D as Object$1, E as Note, F as QuoteAuthorization, I as QuoteRequest, L as Reject, M as Place, N as Proposal, O as Offer, P as Question, S as InteractionRule, T as Multikey, U as vocab_exports, _ as Follow, b as Intent, c as Create, d as Document, f as Endpoints, g as FeaturedItem, h as FeaturedCollection, i as Announce, j as Person, k as OrderedCollectionPage, l as CryptographicKey, m as FeatureRequest, n as Activity, o as Collection, p as FeatureAuthorization, r as Agreement, s as Commitment, t as Accept, u as Delete, w as Measure, x as InteractionPolicy, y as Hashtag, z as Source } from "./vocab-OU5GYbNg.mjs";
4
+ import { t as assertInstanceOf } from "./utils-CQjrpUkt.mjs";
5
5
  import { mockDocumentLoader, test } from "@fedify/fixture";
6
6
  import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
7
7
  import { configure, reset } from "@logtape/logtape";
@@ -1311,6 +1311,25 @@ test("Endpoints.toJsonLd() omits type", async () => {
1311
1311
  contextLoader: mockDocumentLoader
1312
1312
  }), ep);
1313
1313
  });
1314
+ test("Endpoints.uploadMedia round-trips", async () => {
1315
+ const ep = new Endpoints({ uploadMedia: new URL("https://example.com/users/alice/media") });
1316
+ deepStrictEqual(ep.uploadMedia?.href, "https://example.com/users/alice/media");
1317
+ deepStrictEqual((await ep.toJsonLd())["uploadMedia"], "https://example.com/users/alice/media");
1318
+ for (const format of [
1319
+ void 0,
1320
+ "compact",
1321
+ "expand"
1322
+ ]) {
1323
+ const jsonLd = await ep.toJsonLd({
1324
+ format,
1325
+ contextLoader: mockDocumentLoader
1326
+ });
1327
+ deepStrictEqual((await Endpoints.fromJsonLd(jsonLd, {
1328
+ documentLoader: mockDocumentLoader,
1329
+ contextLoader: mockDocumentLoader
1330
+ })).uploadMedia?.href, "https://example.com/users/alice/media", `round-trip failed for format=${format ?? "heuristic"}`);
1331
+ }
1332
+ });
1314
1333
  test("Source.toJsonLd() omits type", async () => {
1315
1334
  const src = new Source({
1316
1335
  content: "Hello, world!",
@@ -1361,7 +1380,8 @@ test("Endpoints with all properties set omits type", async () => {
1361
1380
  oauthTokenEndpoint: new URL("https://example.com/oauth/token"),
1362
1381
  provideClientKey: new URL("https://example.com/provide-key"),
1363
1382
  signClientKey: new URL("https://example.com/sign-key"),
1364
- sharedInbox: new URL("https://example.com/inbox")
1383
+ sharedInbox: new URL("https://example.com/inbox"),
1384
+ uploadMedia: new URL("https://example.com/upload-media")
1365
1385
  });
1366
1386
  const compact = await ep.toJsonLd();
1367
1387
  ok(!("type" in compact), "compact output should not have 'type'");
@@ -1371,6 +1391,7 @@ test("Endpoints with all properties set omits type", async () => {
1371
1391
  deepStrictEqual(compact["provideClientKey"], "https://example.com/provide-key");
1372
1392
  deepStrictEqual(compact["signClientKey"], "https://example.com/sign-key");
1373
1393
  deepStrictEqual(compact["sharedInbox"], "https://example.com/inbox");
1394
+ deepStrictEqual(compact["uploadMedia"], "https://example.com/upload-media");
1374
1395
  for (const format of [
1375
1396
  void 0,
1376
1397
  "compact",
@@ -2555,6 +2576,23 @@ test("FEP-fe34: Same origin objects are trusted", async () => {
2555
2576
  deepStrictEqual(result?.id, new URL("https://example.com/note"));
2556
2577
  deepStrictEqual(result?.content, "This is a legitimate note");
2557
2578
  });
2579
+ test("FEP-fe34: Same-authority non-FE34 embedded objects are trusted", async () => {
2580
+ const result = await (await Create.fromJsonLd({
2581
+ "@context": "https://www.w3.org/ns/activitystreams",
2582
+ "@type": "Create",
2583
+ "@id": "at://did:plc:example/collection/item",
2584
+ "actor": "at://did:plc:example/actor/self",
2585
+ "object": {
2586
+ "@type": "Note",
2587
+ "@id": "at://did:plc:example/collection/reply",
2588
+ "content": "Embedded AT Protocol note"
2589
+ }
2590
+ })).getObject({ documentLoader: async (url) => {
2591
+ throw new Error(`Unexpected fetch: ${url}`);
2592
+ } });
2593
+ assertInstanceOf(result, Note);
2594
+ deepStrictEqual(result.content, "Embedded AT Protocol note");
2595
+ });
2558
2596
  test("FEP-fe34: Embedded cross-origin objects from JSON-LD are ignored by default", async () => {
2559
2597
  const createDocumentLoader = async (url) => {
2560
2598
  if (url === "https://example.com/create") return {
@@ -2656,6 +2694,47 @@ test("FEP-fe34: Portable DID authorities are cross-origin boundaries", async ()
2656
2694
  assertInstanceOf(result, Note);
2657
2695
  deepStrictEqual(result.content, "Fetched portable note");
2658
2696
  });
2697
+ test("FEP-fe34: DID verification methods share portable actor cryptographic origin", async () => {
2698
+ const person = await Person.fromJsonLd({
2699
+ "@id": "ap://did:key:z6MkOwner/actor",
2700
+ "@type": ["https://www.w3.org/ns/activitystreams#Person"],
2701
+ "https://w3id.org/security#assertionMethod": [{
2702
+ "@id": "did:key:z6MkOwner#z6MkOwner",
2703
+ "@type": ["https://w3id.org/security#Multikey"],
2704
+ "https://w3id.org/security#controller": [{ "@id": "did:key:z6MkOwner" }]
2705
+ }]
2706
+ });
2707
+ const documentLoader = async (url) => {
2708
+ throw new Error(`Unexpected fetch: ${url}`);
2709
+ };
2710
+ const methods = [];
2711
+ for await (const method of person.getAssertionMethods({ documentLoader })) methods.push(method);
2712
+ deepStrictEqual(methods.length, 1);
2713
+ assertInstanceOf(methods[0], Multikey);
2714
+ deepStrictEqual(methods[0].id, new URL("did:key:z6MkOwner#z6MkOwner"));
2715
+ });
2716
+ test("FEP-fe34: DID verification methods from another cryptographic origin are untrusted", async () => {
2717
+ const person = await Person.fromJsonLd({
2718
+ "@id": "ap://did:key:z6MkOwner/actor",
2719
+ "@type": ["https://www.w3.org/ns/activitystreams#Person"],
2720
+ "https://w3id.org/security#assertionMethod": [{
2721
+ "@id": "did:key:z6MkOther#z6MkOther",
2722
+ "@type": ["https://w3id.org/security#Multikey"],
2723
+ "https://w3id.org/security#controller": [{ "@id": "did:key:z6MkOther" }]
2724
+ }]
2725
+ });
2726
+ let fetches = 0;
2727
+ const methods = [];
2728
+ for await (const method of person.getAssertionMethods({
2729
+ suppressError: true,
2730
+ documentLoader: async (url) => {
2731
+ fetches++;
2732
+ throw new Error(`Unexpected fetch: ${url}`);
2733
+ }
2734
+ })) methods.push(method);
2735
+ deepStrictEqual(methods, []);
2736
+ deepStrictEqual(fetches, 1);
2737
+ });
2659
2738
  test("FEP-fe34: Array properties respect cross-origin policy", async () => {
2660
2739
  const crossOriginDocumentLoader = async (url) => {
2661
2740
  if (url === "https://different-origin.com/note1") return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.1583+1a6f31cb",
3
+ "version": "2.4.0-dev.1618+58858243",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,6 @@
35
35
  "./package.json": "./package.json"
36
36
  },
37
37
  "dependencies": {
38
- "@js-temporal/polyfill": "^0.5.1",
39
38
  "@multiformats/base-x": "^4.0.1",
40
39
  "@logtape/logtape": "^2.2.0",
41
40
  "@opentelemetry/api": "^1.9.1",
@@ -43,9 +42,10 @@
43
42
  "es-toolkit": "1.46.1",
44
43
  "jsonld": "^9.0.0",
45
44
  "pkijs": "^3.3.3",
46
- "@fedify/vocab-tools": "2.4.0-dev.1583+1a6f31cb",
47
- "@fedify/vocab-runtime": "2.4.0-dev.1583+1a6f31cb",
48
- "@fedify/webfinger": "2.4.0-dev.1583+1a6f31cb"
45
+ "temporal-polyfill": "^1.0.1",
46
+ "@fedify/vocab-tools": "2.4.0-dev.1618+58858243",
47
+ "@fedify/webfinger": "2.4.0-dev.1618+58858243",
48
+ "@fedify/vocab-runtime": "2.4.0-dev.1618+58858243"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.17.0",
@@ -5003,7 +5003,8 @@ snapshot[`Deno.inspect(Endpoints) [auto] 1`] = `
5003
5003
  oauthTokenEndpoint: URL "https://example.com/",
5004
5004
  provideClientKey: URL "https://example.com/",
5005
5005
  signClientKey: URL "https://example.com/",
5006
- sharedInbox: URL "https://example.com/"
5006
+ sharedInbox: URL "https://example.com/",
5007
+ uploadMedia: URL "https://example.com/"
5007
5008
  }'
5008
5009
  `;
5009
5010
 
@@ -5015,7 +5016,8 @@ snapshot[`Deno.inspect(Endpoints) [auto] 2`] = `
5015
5016
  oauthTokenEndpoint: URL "https://example.com/",
5016
5017
  provideClientKey: URL "https://example.com/",
5017
5018
  signClientKey: URL "https://example.com/",
5018
- sharedInbox: URL "https://example.com/"
5019
+ sharedInbox: URL "https://example.com/",
5020
+ uploadMedia: URL "https://example.com/"
5019
5021
  }'
5020
5022
  `;
5021
5023
 
@@ -5027,7 +5029,8 @@ snapshot[`Deno.inspect(Endpoints) [auto] 3`] = `
5027
5029
  oauthTokenEndpoint: URL "https://example.com/",
5028
5030
  provideClientKey: URL "https://example.com/",
5029
5031
  signClientKey: URL "https://example.com/",
5030
- sharedInbox: URL "https://example.com/"
5032
+ sharedInbox: URL "https://example.com/",
5033
+ uploadMedia: URL "https://example.com/"
5031
5034
  }'
5032
5035
  `;
5033
5036
 
@@ -0,0 +1,31 @@
1
+ import { ok, strictEqual, throws } from "node:assert";
2
+ import { createRequire } from "node:module";
3
+ import { mockDocumentLoader, test } from "@fedify/fixture";
4
+
5
+ test("CommonJS entry bundles temporal-polyfill internally", {
6
+ ignore: "Deno" in globalThis,
7
+ }, async () => {
8
+ const originalTemporal = globalThis.Temporal;
9
+
10
+ const require = createRequire(import.meta.url);
11
+ throws(
12
+ () => require("temporal-polyfill"),
13
+ (error: unknown) => {
14
+ if (error == null || typeof error !== "object") return false;
15
+ const { code } = error as { code?: unknown };
16
+ return code === "ERR_PACKAGE_PATH_NOT_EXPORTED" ||
17
+ code === "MODULE_NOT_FOUND";
18
+ },
19
+ );
20
+
21
+ const vocab = require("../dist/mod.cjs");
22
+ const obj = await vocab.Object.fromJsonLd({
23
+ "@context": "https://www.w3.org/ns/activitystreams",
24
+ type: "Object",
25
+ published: "2025-01-01T12:34:56Z",
26
+ }, { documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader });
27
+
28
+ ok(obj.published != null);
29
+ strictEqual(obj.published.toString(), "2025-01-01T12:34:56Z");
30
+ strictEqual(globalThis.Temporal, originalTemporal);
31
+ });
@@ -84,3 +84,17 @@ properties:
84
84
  objects which are not addressed to the `Public`` endpoint.
85
85
  range:
86
86
  - "http://www.w3.org/2001/XMLSchema#anyURI"
87
+
88
+ - singularName: uploadMedia
89
+ functional: true
90
+ compactName: uploadMedia
91
+ uri: "https://www.w3.org/ns/activitystreams#uploadMedia"
92
+ description: |
93
+ Upload endpoint URI for this user for binary data, such as an image or
94
+ video, as described in the [ActivityPub Media
95
+ Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
96
+ Clients `POST` a `multipart/form-data` request containing a `file` part
97
+ (the binary payload) and an `object` part (an ActivityStreams object shell)
98
+ to this endpoint.
99
+ range:
100
+ - "http://www.w3.org/2001/XMLSchema#anyURI"
@@ -416,6 +416,127 @@ test("FEP-fe34: lookupObject() cross-origin security", {
416
416
  );
417
417
  });
418
418
 
419
+ await t.step(
420
+ "same-authority non-FE34 IRIs are still trusted",
421
+ async () => {
422
+ // deno-lint-ignore require-await
423
+ const atProtoDocumentLoader = async (url: string) => {
424
+ if (url === "https://gateway.example/at-item") {
425
+ return {
426
+ documentUrl: "at://did%3Aplc%3Aexample/collection/item",
427
+ contextUrl: null,
428
+ document: {
429
+ "@context": "https://www.w3.org/ns/activitystreams",
430
+ type: "Note",
431
+ id: "at://did:plc:example/collection/reply",
432
+ content: "AT Protocol note",
433
+ },
434
+ };
435
+ }
436
+ throw new Error(`Unexpected URL: ${url}`);
437
+ };
438
+
439
+ const result = await lookupObject(
440
+ "https://gateway.example/at-item",
441
+ {
442
+ documentLoader: atProtoDocumentLoader,
443
+ contextLoader: mockDocumentLoader,
444
+ },
445
+ );
446
+
447
+ assertInstanceOf(result, Note);
448
+ deepStrictEqual(
449
+ result.id,
450
+ new URL("at://did%3Aplc%3Aexample/collection/reply"),
451
+ );
452
+ deepStrictEqual(result.content, "AT Protocol note");
453
+ },
454
+ );
455
+
456
+ await t.step(
457
+ "portable object IDs are trusted by DID cryptographic origin",
458
+ async () => {
459
+ // deno-lint-ignore require-await
460
+ const portableDocumentLoader = async (url: string) => {
461
+ if (
462
+ url ===
463
+ "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note"
464
+ ) {
465
+ return {
466
+ documentUrl: "ap+ef61://did:key:z6Mkabc/note",
467
+ contextUrl: null,
468
+ document: {
469
+ "@context": "https://www.w3.org/ns/activitystreams",
470
+ type: "Note",
471
+ id: "ap://did:key:z6Mkabc/note",
472
+ content: "Portable note",
473
+ },
474
+ };
475
+ }
476
+ throw new Error(`Unexpected URL: ${url}`);
477
+ };
478
+
479
+ const result = await lookupObject(
480
+ "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note",
481
+ {
482
+ documentLoader: portableDocumentLoader,
483
+ contextLoader: mockDocumentLoader,
484
+ },
485
+ );
486
+
487
+ assertInstanceOf(result, Note);
488
+ deepStrictEqual(result.content, "Portable note");
489
+ },
490
+ );
491
+
492
+ await t.step(
493
+ "portable object IDs reject mismatched cryptographic origins",
494
+ async () => {
495
+ // deno-lint-ignore require-await
496
+ const portableDocumentLoader = async (url: string) => {
497
+ if (
498
+ url ===
499
+ "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note"
500
+ ) {
501
+ return {
502
+ documentUrl: "ap+ef61://did:key:z6Mkabc/note",
503
+ contextUrl: null,
504
+ document: {
505
+ "@context": "https://www.w3.org/ns/activitystreams",
506
+ type: "Note",
507
+ id: "ap://did:key:z6Mkdef/note",
508
+ content: "Spoofed portable note",
509
+ },
510
+ };
511
+ }
512
+ throw new Error(`Unexpected URL: ${url}`);
513
+ };
514
+
515
+ const result = await lookupObject(
516
+ "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note",
517
+ {
518
+ documentLoader: portableDocumentLoader,
519
+ contextLoader: mockDocumentLoader,
520
+ },
521
+ );
522
+
523
+ deepStrictEqual(result, null);
524
+ await rejects(
525
+ () =>
526
+ lookupObject(
527
+ "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note",
528
+ {
529
+ documentLoader: portableDocumentLoader,
530
+ contextLoader: mockDocumentLoader,
531
+ crossOrigin: "throw",
532
+ },
533
+ ),
534
+ Error,
535
+ "The object's @id (ap+ef61://did%3Akey%3Az6Mkdef/note) has a different origin than the document URL (ap+ef61://did:key:z6Mkabc/note)",
536
+ );
537
+ },
538
+ );
539
+
419
540
  await t.step("objects without @id are trusted", async () => {
420
541
  // deno-lint-ignore require-await
421
542
  const noIdDocumentLoader = async (url: string) => {
package/src/lookup.ts CHANGED
@@ -2,6 +2,7 @@ import type { GetUserAgentOptions } from "@fedify/vocab-runtime";
2
2
  import {
3
3
  type DocumentLoader,
4
4
  getDocumentLoader,
5
+ haveSameFe34Origin,
5
6
  haveSameIriOrigin,
6
7
  parseIri,
7
8
  type RemoteDocument,
@@ -335,7 +336,8 @@ async function lookupObjectInternal(
335
336
  }
336
337
  if (
337
338
  options.crossOrigin !== "trust" && object.id != null &&
338
- !haveSameIriOrigin(object.id, documentUrl)
339
+ !haveSameIriOrigin(object.id, documentUrl) &&
340
+ !haveSameFe34Origin(object.id, documentUrl)
339
341
  ) {
340
342
  if (options.crossOrigin === "throw") {
341
343
  throw new Error(
package/src/vocab.test.ts CHANGED
@@ -47,6 +47,7 @@ import {
47
47
  InteractionRule,
48
48
  Link,
49
49
  Measure,
50
+ Multikey,
50
51
  Note,
51
52
  Object,
52
53
  Offer,
@@ -1963,6 +1964,39 @@ test("Endpoints.toJsonLd() omits type", async () => {
1963
1964
  deepStrictEqual(restored, ep);
1964
1965
  });
1965
1966
 
1967
+ test("Endpoints.uploadMedia round-trips", async () => {
1968
+ const ep = new Endpoints({
1969
+ uploadMedia: new URL("https://example.com/users/alice/media"),
1970
+ });
1971
+ deepStrictEqual(
1972
+ ep.uploadMedia?.href,
1973
+ "https://example.com/users/alice/media",
1974
+ );
1975
+
1976
+ const compact = await ep.toJsonLd() as Record<string, unknown>;
1977
+ deepStrictEqual(
1978
+ compact["uploadMedia"],
1979
+ "https://example.com/users/alice/media",
1980
+ );
1981
+
1982
+ // Round-trip through every format under the standard AS term.
1983
+ for (const format of [undefined, "compact" as const, "expand" as const]) {
1984
+ const jsonLd = await ep.toJsonLd({
1985
+ format,
1986
+ contextLoader: mockDocumentLoader,
1987
+ });
1988
+ const restored = await Endpoints.fromJsonLd(jsonLd, {
1989
+ documentLoader: mockDocumentLoader,
1990
+ contextLoader: mockDocumentLoader,
1991
+ });
1992
+ deepStrictEqual(
1993
+ restored.uploadMedia?.href,
1994
+ "https://example.com/users/alice/media",
1995
+ `round-trip failed for format=${format ?? "heuristic"}`,
1996
+ );
1997
+ }
1998
+ });
1999
+
1966
2000
  test("Source.toJsonLd() omits type", async () => {
1967
2001
  const src = new Source({
1968
2002
  content: "Hello, world!",
@@ -2029,6 +2063,7 @@ test("Endpoints with all properties set omits type", async () => {
2029
2063
  provideClientKey: new URL("https://example.com/provide-key"),
2030
2064
  signClientKey: new URL("https://example.com/sign-key"),
2031
2065
  sharedInbox: new URL("https://example.com/inbox"),
2066
+ uploadMedia: new URL("https://example.com/upload-media"),
2032
2067
  });
2033
2068
 
2034
2069
  // Compact heuristic path
@@ -2049,6 +2084,7 @@ test("Endpoints with all properties set omits type", async () => {
2049
2084
  );
2050
2085
  deepStrictEqual(compact["signClientKey"], "https://example.com/sign-key");
2051
2086
  deepStrictEqual(compact["sharedInbox"], "https://example.com/inbox");
2087
+ deepStrictEqual(compact["uploadMedia"], "https://example.com/upload-media");
2052
2088
 
2053
2089
  // Round-trip all three formats
2054
2090
  for (
@@ -3800,6 +3836,30 @@ test("FEP-fe34: Same origin objects are trusted", async () => {
3800
3836
  deepStrictEqual(result?.content, "This is a legitimate note");
3801
3837
  });
3802
3838
 
3839
+ test("FEP-fe34: Same-authority non-FE34 embedded objects are trusted", async () => {
3840
+ const create = await Create.fromJsonLd({
3841
+ "@context": "https://www.w3.org/ns/activitystreams",
3842
+ "@type": "Create",
3843
+ "@id": "at://did:plc:example/collection/item",
3844
+ "actor": "at://did:plc:example/actor/self",
3845
+ "object": {
3846
+ "@type": "Note",
3847
+ "@id": "at://did:plc:example/collection/reply",
3848
+ "content": "Embedded AT Protocol note",
3849
+ },
3850
+ });
3851
+
3852
+ const result = await create.getObject({
3853
+ // deno-lint-ignore require-await
3854
+ documentLoader: async (url) => {
3855
+ throw new Error(`Unexpected fetch: ${url}`);
3856
+ },
3857
+ });
3858
+
3859
+ assertInstanceOf(result, Note);
3860
+ deepStrictEqual(result.content, "Embedded AT Protocol note");
3861
+ });
3862
+
3803
3863
  test(
3804
3864
  "FEP-fe34: Embedded cross-origin objects from JSON-LD are ignored by default",
3805
3865
  async () => {
@@ -3951,6 +4011,74 @@ test(
3951
4011
  },
3952
4012
  );
3953
4013
 
4014
+ test(
4015
+ "FEP-fe34: DID verification methods share portable actor cryptographic origin",
4016
+ async () => {
4017
+ const person = await Person.fromJsonLd({
4018
+ "@id": "ap://did:key:z6MkOwner/actor",
4019
+ "@type": ["https://www.w3.org/ns/activitystreams#Person"],
4020
+ "https://w3id.org/security#assertionMethod": [{
4021
+ "@id": "did:key:z6MkOwner#z6MkOwner",
4022
+ "@type": ["https://w3id.org/security#Multikey"],
4023
+ "https://w3id.org/security#controller": [{
4024
+ "@id": "did:key:z6MkOwner",
4025
+ }],
4026
+ }],
4027
+ });
4028
+
4029
+ // deno-lint-ignore require-await
4030
+ const documentLoader = async (url: string) => {
4031
+ throw new Error(`Unexpected fetch: ${url}`);
4032
+ };
4033
+
4034
+ const methods = [];
4035
+ for await (const method of person.getAssertionMethods({ documentLoader })) {
4036
+ methods.push(method);
4037
+ }
4038
+
4039
+ deepStrictEqual(methods.length, 1);
4040
+ assertInstanceOf(methods[0], Multikey);
4041
+ deepStrictEqual(
4042
+ methods[0].id,
4043
+ new URL("did:key:z6MkOwner#z6MkOwner"),
4044
+ );
4045
+ },
4046
+ );
4047
+
4048
+ test(
4049
+ "FEP-fe34: DID verification methods from another cryptographic origin are untrusted",
4050
+ async () => {
4051
+ const person = await Person.fromJsonLd({
4052
+ "@id": "ap://did:key:z6MkOwner/actor",
4053
+ "@type": ["https://www.w3.org/ns/activitystreams#Person"],
4054
+ "https://w3id.org/security#assertionMethod": [{
4055
+ "@id": "did:key:z6MkOther#z6MkOther",
4056
+ "@type": ["https://w3id.org/security#Multikey"],
4057
+ "https://w3id.org/security#controller": [{
4058
+ "@id": "did:key:z6MkOther",
4059
+ }],
4060
+ }],
4061
+ });
4062
+
4063
+ let fetches = 0;
4064
+ const methods = [];
4065
+ for await (
4066
+ const method of person.getAssertionMethods({
4067
+ suppressError: true,
4068
+ // deno-lint-ignore require-await
4069
+ documentLoader: async (url) => {
4070
+ fetches++;
4071
+ throw new Error(`Unexpected fetch: ${url}`);
4072
+ },
4073
+ })
4074
+ ) {
4075
+ methods.push(method);
4076
+ }
4077
+ deepStrictEqual(methods, []);
4078
+ deepStrictEqual(fetches, 1);
4079
+ },
4080
+ );
4081
+
3954
4082
  test("FEP-fe34: Array properties respect cross-origin policy", async () => {
3955
4083
  // deno-lint-ignore require-await
3956
4084
  const crossOriginDocumentLoader = async (url: string) => {
package/tsdown.config.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import { glob, mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join, sep } from "node:path";
3
3
  import { defineConfig } from "tsdown";
4
+ import {
5
+ temporalPolyfillCjsBanner,
6
+ temporalPolyfillCjsDeps,
7
+ temporalPolyfillEsmBanner,
8
+ temporalPolyfillImportPlugin,
9
+ temporalPolyfillIntro,
10
+ } from "../../scripts/tsdown/temporal.mts";
4
11
 
5
12
  async function copyFileSafely(
6
13
  source: string,
@@ -16,17 +23,21 @@ export default [
16
23
  "./src/mod.ts",
17
24
  ],
18
25
  dts: { compilerOptions: { isolatedDeclarations: true, declaration: true } },
19
- format: ["esm", "cjs"],
26
+ format: {
27
+ esm: {
28
+ banner: temporalPolyfillEsmBanner(),
29
+ },
30
+ cjs: {
31
+ deps: temporalPolyfillCjsDeps({
32
+ neverBundle: [/^node:/],
33
+ }),
34
+ plugins: [temporalPolyfillImportPlugin],
35
+ banner: temporalPolyfillCjsBanner(),
36
+ },
37
+ },
20
38
  platform: "neutral",
21
- deps: { neverBundle: [/^node:/] },
22
- banner({ format }) {
23
- const js = format === "cjs"
24
- ? `const { Temporal } = require("@js-temporal/polyfill");`
25
- : `import { Temporal } from "@js-temporal/polyfill";`;
26
- return {
27
- js,
28
- dts: `/// <reference lib="esnext.temporal" />`,
29
- };
39
+ deps: {
40
+ neverBundle: [/^node:/],
30
41
  },
31
42
  }),
32
43
  defineConfig({
@@ -49,8 +60,7 @@ export default [
49
60
  },
50
61
  },
51
62
  outputOptions: {
52
- intro: `
53
- import { Temporal } from "@js-temporal/polyfill";
63
+ intro: `${temporalPolyfillIntro}
54
64
  globalThis.addEventListener = () => {};
55
65
  `,
56
66
  },