@fedify/fedify 0.10.0-dev.207 → 0.10.0-dev.211

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGES.md CHANGED
@@ -106,6 +106,8 @@ To be released.
106
106
  - The `Context.sendActivity()` method's first parameter now accepts
107
107
  `SenderKeyPair[]` as well.
108
108
 
109
+ - Added `Question` class to Activity Vocabulary API.
110
+
109
111
  - Added `context` option to `Object.toJsonLd()` method. This applies to
110
112
  any subclasses of the `Object` class too.
111
113
 
@@ -119,12 +121,16 @@ To be released.
119
121
  - Ephemeral actors now have the following properties: `summary`,
120
122
  `following`, `followers`, `outbox`, `manuallyApprovesFollowers`, and
121
123
  `url`.
124
+ - Improved the compatibility of the `fedify inbox` command with Misskey
125
+ and Mitra.
122
126
 
123
127
  - Added more log messages using the [LogTape] library. Currently the below
124
128
  logger categories are used:
125
129
 
126
130
  - `["fedify", "sig", "proof"]`
127
131
  - `["fedify", "sig", "key"]`
132
+ - `["fedify", "vocab", "lookup"]`
133
+ - `["fedify", "webfinger", "lookup"]`
128
134
 
129
135
  [#54]: https://github.com/dahlia/fedify/issues/54
130
136
  [#55]: https://github.com/dahlia/fedify/issues/55
package/FEDERATION.md ADDED
@@ -0,0 +1,63 @@
1
+ <!-- deno-fmt-ignore-file -->
2
+
3
+ Federation
4
+ ==========
5
+
6
+ Supported federation protocols and standards
7
+ --------------------------------------------
8
+
9
+ - [ActivityPub] (S2S)
10
+ - [WebFinger]
11
+ - [HTTP Signatures]
12
+ - [NodeInfo]
13
+
14
+ [ActivityPub]: https://www.w3.org/TR/activitypub/
15
+ [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033
16
+ [HTTP Signatures]: https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures
17
+ [NodeInfo]: https://nodeinfo.diaspora.software/
18
+
19
+
20
+ Supported FEPs
21
+ --------------
22
+
23
+ - [FEP-67ff][]: FEDERATION.md
24
+ - [FEP-8fcf][]: Followers collection synchronization across servers
25
+ - [FEP-f1d5][]: NodeInfo in Fediverse Software
26
+ - [FEP-8b32][]: Object Integrity Proofs
27
+ - [FEP-521a][]: Representing actor's public keys
28
+ - [FEP-5feb][]: Search indexing consent for actors
29
+ - [FEP-c7d3][]: Ownership
30
+
31
+ [FEP-67ff]: https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md
32
+ [FEP-8fcf]: https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
33
+ [FEP-f1d5]: https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md
34
+ [FEP-8b32]: https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md
35
+ [FEP-521a]: https://codeberg.org/fediverse/fep/src/branch/main/fep/521a/fep-521a.md
36
+ [FEP-5feb]: https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md
37
+ [FEP-c7d3]: https://codeberg.org/silverpill/feps/src/branch/main/c7d3/fep-c7d3.md
38
+
39
+
40
+ ActivityPub
41
+ -----------
42
+
43
+ Since Fedify is a framework, what activity types it uses is up to
44
+ the application developers. However, Fedify provides a set of
45
+ activity types that are commonly used in the Fediverse. The following
46
+ lists the activity types that Fedify provides:
47
+
48
+ - [`Accept`](https://jsr.io/@fedify/fedify/doc/vocab/~/Accept)
49
+ - [`Add`](https://jsr.io/@fedify/fedify/doc/vocab/~/Add)
50
+ - [`Announce`](https://jsr.io/@fedify/fedify/doc/vocab/~/Announce)
51
+ - [`Block`](https://jsr.io/@fedify/fedify/doc/vocab/~/Block)
52
+ - [`Create`](https://jsr.io/@fedify/fedify/doc/vocab/~/Create)
53
+ - [`Delete`](https://jsr.io/@fedify/fedify/doc/vocab/~/Delete)
54
+ - [`Dislike`](https://jsr.io/@fedify/fedify/doc/vocab/~/Dislike)
55
+ - [`Flag`](https://jsr.io/@fedify/fedify/doc/vocab/~/Flag)
56
+ - [`Follow`](https://jsr.io/@fedify/fedify/doc/vocab/~/Follow)
57
+ - [`Ignore`](https://jsr.io/@fedify/fedify/doc/vocab/~/Ignore)
58
+ - [`Like`](https://jsr.io/@fedify/fedify/doc/vocab/~/Like)
59
+ - [`Question`](https://jsr.io/@fedify/fedify/doc/vocab/~/Question)
60
+ - [`Reject`](https://jsr.io/@fedify/fedify/doc/vocab/~/Reject)
61
+ - [`Remove`](https://jsr.io/@fedify/fedify/doc/vocab/~/Remove)
62
+ - [`Undo`](https://jsr.io/@fedify/fedify/doc/vocab/~/Undo)
63
+ - [`Update`](https://jsr.io/@fedify/fedify/doc/vocab/~/Update)
@@ -1,6 +1,8 @@
1
+ import { getLogger } from "@logtape/logtape";
1
2
  import { fetchDocumentLoader, } from "../runtime/docloader.js";
2
3
  import { lookupWebFinger } from "../webfinger/lookup.js";
3
4
  import { Object } from "./vocab.js";
5
+ const logger = getLogger(["fedify", "vocab", "lookup"]);
4
6
  const handleRegexp = /^@?((?:[-A-Za-z0-9._~!$&'()*+,;=]|%[A-Fa-f0-9]{2})+)@([^@]+)$/;
5
7
  /**
6
8
  * Looks up an ActivityStreams object by its URI (including `acct:` URIs)
@@ -48,8 +50,8 @@ export async function lookupObject(identifier, options = {}) {
48
50
  const remoteDoc = await documentLoader(identifier.href);
49
51
  document = remoteDoc.document;
50
52
  }
51
- catch (_) {
52
- // Silently ignore errors.
53
+ catch (error) {
54
+ logger.debug("Failed to fetch remote document:\n{error}", { error });
53
55
  }
54
56
  }
55
57
  if (document == null) {
@@ -65,8 +67,8 @@ export async function lookupObject(identifier, options = {}) {
65
67
  document = remoteDoc.document;
66
68
  break;
67
69
  }
68
- catch (_) {
69
- // Silently ignore errors.
70
+ catch (error) {
71
+ logger.debug("Failed to fetch remote document:\n{error}", { error });
70
72
  continue;
71
73
  }
72
74
  }
@@ -79,9 +81,11 @@ export async function lookupObject(identifier, options = {}) {
79
81
  contextLoader: options.contextLoader,
80
82
  });
81
83
  }
82
- catch (e) {
83
- if (e instanceof TypeError)
84
+ catch (error) {
85
+ if (error instanceof TypeError) {
86
+ logger.debug("Failed to parse JSON-LD document:\n{error}", { error });
84
87
  return null;
85
- throw e;
88
+ }
89
+ throw error;
86
90
  }
87
91
  }
@@ -0,0 +1,43 @@
1
+ $schema: ../codegen/schema.yaml
2
+ name: Question
3
+ uri: "https://www.w3.org/ns/activitystreams#Question"
4
+ extends: "https://www.w3.org/ns/activitystreams#IntransitiveActivity"
5
+ entity: true
6
+ description: |
7
+ Represents a question being asked. Question objects are an extension of
8
+ {@link IntransitiveActivity}. That is, the Question object is an Activity,
9
+ but the direct object is the question itself and therefore it would not
10
+ contain an `object` property.
11
+
12
+ Either of the `anyOf` and `oneOf` properties *may* be used to express possible
13
+ answers, but a Question object *must not* have both properties.
14
+ defaultContext:
15
+ - "https://www.w3.org/ns/activitystreams"
16
+ - "https://w3id.org/security/data-integrity/v1"
17
+ - toot: "http://joinmastodon.org/ns#"
18
+ sensitive: "as:sensitive"
19
+ Emoji: "toot:Emoji"
20
+ Hashtag: "as:Hashtag"
21
+
22
+ properties:
23
+ - pluralName: exclusiveOptions
24
+ singularName: exclusiveOption
25
+ singularAccessor: false
26
+ uri: "https://www.w3.org/ns/activitystreams#oneOf"
27
+ description: |
28
+ Identifies an exclusive option for a Question. Use of `exclusiveOptions`
29
+ implies that the Question can have only a single answer. To indicate that
30
+ a Question can have multiple answers, use `inclusiveOptions`.
31
+ range:
32
+ - "https://www.w3.org/ns/activitystreams#Object"
33
+
34
+ - pluralName: inclusiveOptions
35
+ singularName: inclusiveOption
36
+ singularAccessor: false
37
+ uri: "https://www.w3.org/ns/activitystreams#anyOf"
38
+ description: |
39
+ Identifies an inclusive option for a Question. Use of `inclusiveOptions`
40
+ implies that the Question can have multiple answers. To indicate that
41
+ a Question can have only one answer, use `exclusiveOptions`.
42
+ range:
43
+ - "https://www.w3.org/ns/activitystreams#Object"
@@ -2198,6 +2198,10 @@ export class Object {
2198
2198
  delete values["@type"];
2199
2199
  return await IntransitiveActivity.fromJsonLd(values, options);
2200
2200
  }
2201
+ if (values["@type"].includes("https://www.w3.org/ns/activitystreams#Question")) {
2202
+ delete values["@type"];
2203
+ return await Question.fromJsonLd(values, options);
2204
+ }
2201
2205
  if (values["@type"].includes("https://www.w3.org/ns/activitystreams#Like")) {
2202
2206
  delete values["@type"];
2203
2207
  return await Like.fromJsonLd(values, options);
@@ -2334,6 +2338,7 @@ export class Object {
2334
2338
  "https://www.w3.org/ns/activitystreams#Ignore",
2335
2339
  "https://www.w3.org/ns/activitystreams#Block",
2336
2340
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2341
+ "https://www.w3.org/ns/activitystreams#Question",
2337
2342
  "https://www.w3.org/ns/activitystreams#Like",
2338
2343
  "https://www.w3.org/ns/activitystreams#Reject",
2339
2344
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2471,6 +2476,7 @@ export class Object {
2471
2476
  "https://www.w3.org/ns/activitystreams#Ignore",
2472
2477
  "https://www.w3.org/ns/activitystreams#Block",
2473
2478
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2479
+ "https://www.w3.org/ns/activitystreams#Question",
2474
2480
  "https://www.w3.org/ns/activitystreams#Like",
2475
2481
  "https://www.w3.org/ns/activitystreams#Reject",
2476
2482
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2563,6 +2569,7 @@ export class Object {
2563
2569
  "https://www.w3.org/ns/activitystreams#Ignore",
2564
2570
  "https://www.w3.org/ns/activitystreams#Block",
2565
2571
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2572
+ "https://www.w3.org/ns/activitystreams#Question",
2566
2573
  "https://www.w3.org/ns/activitystreams#Like",
2567
2574
  "https://www.w3.org/ns/activitystreams#Reject",
2568
2575
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2655,6 +2662,7 @@ export class Object {
2655
2662
  "https://www.w3.org/ns/activitystreams#Ignore",
2656
2663
  "https://www.w3.org/ns/activitystreams#Block",
2657
2664
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2665
+ "https://www.w3.org/ns/activitystreams#Question",
2658
2666
  "https://www.w3.org/ns/activitystreams#Like",
2659
2667
  "https://www.w3.org/ns/activitystreams#Reject",
2660
2668
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2723,6 +2731,7 @@ export class Object {
2723
2731
  "https://www.w3.org/ns/activitystreams#Ignore",
2724
2732
  "https://www.w3.org/ns/activitystreams#Block",
2725
2733
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2734
+ "https://www.w3.org/ns/activitystreams#Question",
2726
2735
  "https://www.w3.org/ns/activitystreams#Like",
2727
2736
  "https://www.w3.org/ns/activitystreams#Reject",
2728
2737
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2799,6 +2808,7 @@ export class Object {
2799
2808
  "https://www.w3.org/ns/activitystreams#Ignore",
2800
2809
  "https://www.w3.org/ns/activitystreams#Block",
2801
2810
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2811
+ "https://www.w3.org/ns/activitystreams#Question",
2802
2812
  "https://www.w3.org/ns/activitystreams#Like",
2803
2813
  "https://www.w3.org/ns/activitystreams#Reject",
2804
2814
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -2902,6 +2912,7 @@ export class Object {
2902
2912
  "https://www.w3.org/ns/activitystreams#Ignore",
2903
2913
  "https://www.w3.org/ns/activitystreams#Block",
2904
2914
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
2915
+ "https://www.w3.org/ns/activitystreams#Question",
2905
2916
  "https://www.w3.org/ns/activitystreams#Like",
2906
2917
  "https://www.w3.org/ns/activitystreams#Reject",
2907
2918
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -5195,6 +5206,10 @@ export class Activity extends Object {
5195
5206
  delete values["@type"];
5196
5207
  return await IntransitiveActivity.fromJsonLd(values, options);
5197
5208
  }
5209
+ if (values["@type"].includes("https://www.w3.org/ns/activitystreams#Question")) {
5210
+ delete values["@type"];
5211
+ return await Question.fromJsonLd(values, options);
5212
+ }
5198
5213
  if (values["@type"].includes("https://www.w3.org/ns/activitystreams#Like")) {
5199
5214
  delete values["@type"];
5200
5215
  return await Like.fromJsonLd(values, options);
@@ -8136,6 +8151,7 @@ export class Collection extends Object {
8136
8151
  "https://www.w3.org/ns/activitystreams#Ignore",
8137
8152
  "https://www.w3.org/ns/activitystreams#Block",
8138
8153
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
8154
+ "https://www.w3.org/ns/activitystreams#Question",
8139
8155
  "https://www.w3.org/ns/activitystreams#Like",
8140
8156
  "https://www.w3.org/ns/activitystreams#Reject",
8141
8157
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -11532,6 +11548,7 @@ export class Link {
11532
11548
  "https://www.w3.org/ns/activitystreams#Ignore",
11533
11549
  "https://www.w3.org/ns/activitystreams#Block",
11534
11550
  "https://www.w3.org/ns/activitystreams#IntransitiveActivity",
11551
+ "https://www.w3.org/ns/activitystreams#Question",
11535
11552
  "https://www.w3.org/ns/activitystreams#Like",
11536
11553
  "https://www.w3.org/ns/activitystreams#Reject",
11537
11554
  "https://www.w3.org/ns/activitystreams#Remove",
@@ -11994,6 +12011,10 @@ export class IntransitiveActivity extends Activity {
11994
12011
  (expanded[0] ?? {});
11995
12012
  }
11996
12013
  if ("@type" in values) {
12014
+ if (values["@type"].includes("https://www.w3.org/ns/activitystreams#Question")) {
12015
+ delete values["@type"];
12016
+ return await Question.fromJsonLd(values, options);
12017
+ }
11997
12018
  if (!values["@type"].includes("https://www.w3.org/ns/activitystreams#IntransitiveActivity")) {
11998
12019
  throw new TypeError("Invalid type: " + values["@type"]);
11999
12020
  }
@@ -15835,6 +15856,296 @@ export class Profile extends Object {
15835
15856
  return "Profile " + inspect(proxy, options);
15836
15857
  }
15837
15858
  }
15859
+ /** Represents a question being asked. Question objects are an extension of
15860
+ * {@link IntransitiveActivity}. That is, the Question object is an Activity,
15861
+ * but the direct object is the question itself and therefore it would not
15862
+ * contain an `object` property.
15863
+ *
15864
+ * Either of the `anyOf` and `oneOf` properties *may* be used to express possible
15865
+ * answers, but a Question object *must not* have both properties.
15866
+ */
15867
+ export class Question extends IntransitiveActivity {
15868
+ /**
15869
+ * The type URI of {@link Question}: `https://www.w3.org/ns/activitystreams#Question`.
15870
+ */
15871
+ static get typeId() {
15872
+ return new URL("https://www.w3.org/ns/activitystreams#Question");
15873
+ }
15874
+ #_2N5scKaVEcdYHFmfKYYacAwUhUgQ = [];
15875
+ #_2mV6isMTPRKbWdLCjcpiEysq5dAY = [];
15876
+ /**
15877
+ * Constructs a new instance of Question with the given values.
15878
+ * @param values The values to initialize the instance with.
15879
+ * @param options The options to use for initialization.
15880
+ */
15881
+ constructor(values, { documentLoader, contextLoader, } = {}) {
15882
+ super(values, { documentLoader, contextLoader });
15883
+ if ("exclusiveOptions" in values && values.exclusiveOptions != null) {
15884
+ this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ = values.exclusiveOptions;
15885
+ }
15886
+ if ("inclusiveOptions" in values && values.inclusiveOptions != null) {
15887
+ this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY = values.inclusiveOptions;
15888
+ }
15889
+ }
15890
+ /**
15891
+ * Clones this instance, optionally updating it with the given values.
15892
+ * @param values The values to update the clone with.
15893
+ * @options The options to use for cloning.
15894
+ * @returns The cloned instance.
15895
+ */
15896
+ clone(values = {}, options = {}) {
15897
+ const clone = super.clone(values, options);
15898
+ clone.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ = this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ;
15899
+ if ("exclusiveOptions" in values && values.exclusiveOptions != null) {
15900
+ clone.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ = values.exclusiveOptions;
15901
+ }
15902
+ clone.#_2mV6isMTPRKbWdLCjcpiEysq5dAY = this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY;
15903
+ if ("inclusiveOptions" in values && values.inclusiveOptions != null) {
15904
+ clone.#_2mV6isMTPRKbWdLCjcpiEysq5dAY = values.inclusiveOptions;
15905
+ }
15906
+ return clone;
15907
+ }
15908
+ async #fetchExclusiveOption(url, options = {}) {
15909
+ const documentLoader = options.documentLoader ?? this._documentLoader ??
15910
+ fetchDocumentLoader;
15911
+ const contextLoader = options.contextLoader ?? this._contextLoader ??
15912
+ fetchDocumentLoader;
15913
+ const { document } = await documentLoader(url.href);
15914
+ try {
15915
+ return await Object.fromJsonLd(document, { documentLoader, contextLoader });
15916
+ }
15917
+ catch (e) {
15918
+ if (!(e instanceof TypeError))
15919
+ throw e;
15920
+ }
15921
+ throw new TypeError("Expected an object of any type of: " +
15922
+ ["https://www.w3.org/ns/activitystreams#Object"].join(", "));
15923
+ }
15924
+ /**
15925
+ * Similar to
15926
+ * {@link Question.getExclusiveOptions},
15927
+ * but returns their `@id`s instead of the objects themselves.
15928
+ */
15929
+ get exclusiveOptionIds() {
15930
+ return this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ.map((v) => v instanceof URL ? v : v.id).filter((id) => id !== null);
15931
+ }
15932
+ /** Identifies an exclusive option for a Question. Use of `exclusiveOptions`
15933
+ * implies that the Question can have only a single answer. To indicate that
15934
+ * a Question can have multiple answers, use `inclusiveOptions`.
15935
+ */
15936
+ async *getExclusiveOptions(options = {}) {
15937
+ const vs = this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ;
15938
+ for (let i = 0; i < vs.length; i++) {
15939
+ const v = vs[i];
15940
+ if (v instanceof URL) {
15941
+ const fetched = await this.#fetchExclusiveOption(v, options);
15942
+ vs[i] = fetched;
15943
+ yield fetched;
15944
+ continue;
15945
+ }
15946
+ yield v;
15947
+ }
15948
+ }
15949
+ async #fetchInclusiveOption(url, options = {}) {
15950
+ const documentLoader = options.documentLoader ?? this._documentLoader ??
15951
+ fetchDocumentLoader;
15952
+ const contextLoader = options.contextLoader ?? this._contextLoader ??
15953
+ fetchDocumentLoader;
15954
+ const { document } = await documentLoader(url.href);
15955
+ try {
15956
+ return await Object.fromJsonLd(document, { documentLoader, contextLoader });
15957
+ }
15958
+ catch (e) {
15959
+ if (!(e instanceof TypeError))
15960
+ throw e;
15961
+ }
15962
+ throw new TypeError("Expected an object of any type of: " +
15963
+ ["https://www.w3.org/ns/activitystreams#Object"].join(", "));
15964
+ }
15965
+ /**
15966
+ * Similar to
15967
+ * {@link Question.getInclusiveOptions},
15968
+ * but returns their `@id`s instead of the objects themselves.
15969
+ */
15970
+ get inclusiveOptionIds() {
15971
+ return this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY.map((v) => v instanceof URL ? v : v.id).filter((id) => id !== null);
15972
+ }
15973
+ /** Identifies an inclusive option for a Question. Use of `inclusiveOptions`
15974
+ * implies that the Question can have multiple answers. To indicate that
15975
+ * a Question can have only one answer, use `exclusiveOptions`.
15976
+ */
15977
+ async *getInclusiveOptions(options = {}) {
15978
+ const vs = this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY;
15979
+ for (let i = 0; i < vs.length; i++) {
15980
+ const v = vs[i];
15981
+ if (v instanceof URL) {
15982
+ const fetched = await this.#fetchInclusiveOption(v, options);
15983
+ vs[i] = fetched;
15984
+ yield fetched;
15985
+ continue;
15986
+ }
15987
+ yield v;
15988
+ }
15989
+ }
15990
+ /**
15991
+ * Converts this object to a JSON-LD structure.
15992
+ * @returns The JSON-LD representation of this object.
15993
+ */
15994
+ async toJsonLd(options = {}) {
15995
+ options = {
15996
+ ...options,
15997
+ contextLoader: options.contextLoader ?? fetchDocumentLoader,
15998
+ };
15999
+ // deno-lint-ignore no-unused-vars prefer-const
16000
+ let array;
16001
+ const baseValues = await super.toJsonLd({
16002
+ ...options,
16003
+ expand: true,
16004
+ });
16005
+ const values = baseValues[0];
16006
+ array = [];
16007
+ for (const v of this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ) {
16008
+ const element = v instanceof URL
16009
+ ? { "@id": v.href }
16010
+ : await v.toJsonLd(options);
16011
+ array.push(element);
16012
+ }
16013
+ if (array.length > 0) {
16014
+ values["https://www.w3.org/ns/activitystreams#oneOf"] = array;
16015
+ }
16016
+ array = [];
16017
+ for (const v of this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY) {
16018
+ const element = v instanceof URL
16019
+ ? { "@id": v.href }
16020
+ : await v.toJsonLd(options);
16021
+ array.push(element);
16022
+ }
16023
+ if (array.length > 0) {
16024
+ values["https://www.w3.org/ns/activitystreams#anyOf"] = array;
16025
+ }
16026
+ values["@type"] = ["https://www.w3.org/ns/activitystreams#Question"];
16027
+ if (this.id)
16028
+ values["@id"] = this.id.href;
16029
+ if (options.expand) {
16030
+ return await jsonld.expand(values, { documentLoader: options.contextLoader });
16031
+ }
16032
+ return await jsonld.compact(values, options.context ??
16033
+ [
16034
+ "https://www.w3.org/ns/activitystreams",
16035
+ "https://w3id.org/security/data-integrity/v1",
16036
+ {
16037
+ "toot": "http://joinmastodon.org/ns#",
16038
+ "sensitive": "as:sensitive",
16039
+ "Emoji": "toot:Emoji",
16040
+ "Hashtag": "as:Hashtag",
16041
+ },
16042
+ ], { documentLoader: options.contextLoader });
16043
+ }
16044
+ /**
16045
+ * Converts a JSON-LD structure to an object of this type.
16046
+ * @param json The JSON-LD structure to convert.
16047
+ * @returns The object of this type.
16048
+ * @throws {TypeError} If the given `json` is invalid.
16049
+ */
16050
+ static async fromJsonLd(json, options = {}) {
16051
+ if (typeof json === "undefined") {
16052
+ throw new TypeError("Invalid JSON-LD: undefined.");
16053
+ }
16054
+ else if (json === null)
16055
+ throw new TypeError("Invalid JSON-LD: null.");
16056
+ options = {
16057
+ ...options,
16058
+ documentLoader: options.documentLoader ?? fetchDocumentLoader,
16059
+ contextLoader: options.contextLoader ?? fetchDocumentLoader,
16060
+ };
16061
+ // deno-lint-ignore no-explicit-any
16062
+ let values;
16063
+ if (globalThis.Object.keys(json).length == 0) {
16064
+ values = {};
16065
+ }
16066
+ else {
16067
+ const expanded = await jsonld.expand(json, {
16068
+ documentLoader: options.contextLoader,
16069
+ keepFreeFloatingNodes: true,
16070
+ });
16071
+ values =
16072
+ // deno-lint-ignore no-explicit-any
16073
+ (expanded[0] ?? {});
16074
+ }
16075
+ if ("@type" in values) {
16076
+ if (!values["@type"].includes("https://www.w3.org/ns/activitystreams#Question")) {
16077
+ throw new TypeError("Invalid type: " + values["@type"]);
16078
+ }
16079
+ }
16080
+ const instance = await super.fromJsonLd(values, options);
16081
+ if (!(instance instanceof Question)) {
16082
+ throw new TypeError("Unexpected type: " + instance.constructor.name);
16083
+ }
16084
+ const _2N5scKaVEcdYHFmfKYYacAwUhUgQ = [];
16085
+ for (const v of values["https://www.w3.org/ns/activitystreams#oneOf"] ?? []) {
16086
+ if (v == null)
16087
+ continue;
16088
+ if (typeof v === "object" && "@id" in v && !("@type" in v) &&
16089
+ globalThis.Object.keys(v).length === 1) {
16090
+ _2N5scKaVEcdYHFmfKYYacAwUhUgQ.push(new URL(v["@id"]));
16091
+ continue;
16092
+ }
16093
+ _2N5scKaVEcdYHFmfKYYacAwUhUgQ.push(await Object.fromJsonLd(v, options));
16094
+ }
16095
+ instance.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ = _2N5scKaVEcdYHFmfKYYacAwUhUgQ;
16096
+ const _2mV6isMTPRKbWdLCjcpiEysq5dAY = [];
16097
+ for (const v of values["https://www.w3.org/ns/activitystreams#anyOf"] ?? []) {
16098
+ if (v == null)
16099
+ continue;
16100
+ if (typeof v === "object" && "@id" in v && !("@type" in v) &&
16101
+ globalThis.Object.keys(v).length === 1) {
16102
+ _2mV6isMTPRKbWdLCjcpiEysq5dAY.push(new URL(v["@id"]));
16103
+ continue;
16104
+ }
16105
+ _2mV6isMTPRKbWdLCjcpiEysq5dAY.push(await Object.fromJsonLd(v, options));
16106
+ }
16107
+ instance.#_2mV6isMTPRKbWdLCjcpiEysq5dAY = _2mV6isMTPRKbWdLCjcpiEysq5dAY;
16108
+ return instance;
16109
+ }
16110
+ _getCustomInspectProxy() {
16111
+ const proxy = super._getCustomInspectProxy();
16112
+ const _2N5scKaVEcdYHFmfKYYacAwUhUgQ = this.#_2N5scKaVEcdYHFmfKYYacAwUhUgQ
16113
+ // deno-lint-ignore no-explicit-any
16114
+ .map((v) => v instanceof URL
16115
+ ? {
16116
+ [Symbol.for("Deno.customInspect")]: (inspect, options) => "URL " + inspect(v.href, options),
16117
+ [Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options),
16118
+ }
16119
+ : v);
16120
+ if (_2N5scKaVEcdYHFmfKYYacAwUhUgQ.length > 1 ||
16121
+ !("exclusiveOption" in proxy) &&
16122
+ _2N5scKaVEcdYHFmfKYYacAwUhUgQ.length > 0) {
16123
+ proxy.exclusiveOptions = _2N5scKaVEcdYHFmfKYYacAwUhUgQ;
16124
+ }
16125
+ const _2mV6isMTPRKbWdLCjcpiEysq5dAY = this.#_2mV6isMTPRKbWdLCjcpiEysq5dAY
16126
+ // deno-lint-ignore no-explicit-any
16127
+ .map((v) => v instanceof URL
16128
+ ? {
16129
+ [Symbol.for("Deno.customInspect")]: (inspect, options) => "URL " + inspect(v.href, options),
16130
+ [Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options),
16131
+ }
16132
+ : v);
16133
+ if (_2mV6isMTPRKbWdLCjcpiEysq5dAY.length > 1 ||
16134
+ !("inclusiveOption" in proxy) &&
16135
+ _2mV6isMTPRKbWdLCjcpiEysq5dAY.length > 0) {
16136
+ proxy.inclusiveOptions = _2mV6isMTPRKbWdLCjcpiEysq5dAY;
16137
+ }
16138
+ return proxy;
16139
+ }
16140
+ [Symbol.for("Deno.customInspect")](inspect, options) {
16141
+ const proxy = this._getCustomInspectProxy();
16142
+ return "Question " + inspect(proxy, options);
16143
+ }
16144
+ [Symbol.for("nodejs.util.inspect.custom")](_depth, options, inspect) {
16145
+ const proxy = this._getCustomInspectProxy();
16146
+ return "Question " + inspect(proxy, options);
16147
+ }
16148
+ }
15838
16149
  /** Indicates that the `actor` is rejecting the `object`. The `target` and
15839
16150
  * `origin` typically have no defined meaning.
15840
16151
  */
@@ -1,3 +1,5 @@
1
+ import { getLogger } from "@logtape/logtape";
2
+ const logger = getLogger(["fedify", "webfinger", "lookup"]);
1
3
  /**
2
4
  * Looks up a WebFinger resource.
3
5
  * @param resource The resource URL to look up.
@@ -22,6 +24,7 @@ export async function lookupWebFinger(resource) {
22
24
  let url = new URL(`https://${server}/.well-known/webfinger`);
23
25
  url.searchParams.set("resource", resource.href);
24
26
  while (true) {
27
+ logger.debug("Fetching WebFinger resource descriptor from {url}...", { url: url.href });
25
28
  const response = await fetch(url, {
26
29
  headers: { Accept: "application/jrd+json" },
27
30
  redirect: "manual",
@@ -31,14 +34,22 @@ export async function lookupWebFinger(resource) {
31
34
  url = new URL(response.headers.get("Location"));
32
35
  continue;
33
36
  }
34
- if (!response.ok)
37
+ if (!response.ok) {
38
+ logger.debug("Failed to fetch WebFinger resource descriptor: {status} {statusText}.", {
39
+ url: url.href,
40
+ status: response.status,
41
+ statusText: response.statusText,
42
+ });
35
43
  return null;
44
+ }
36
45
  try {
37
46
  return await response.json();
38
47
  }
39
48
  catch (e) {
40
- if (e instanceof SyntaxError)
49
+ if (e instanceof SyntaxError) {
50
+ logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
41
51
  return null;
52
+ }
42
53
  throw e;
43
54
  }
44
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "0.10.0-dev.207+1573cf43",
3
+ "version": "0.10.0-dev.211+aaa16745",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -1 +1 @@
1
- {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/vocab/lookup.ts"],"names":[],"mappings":";AAAA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;OAGG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,GAAG,GAAG,EACxB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA8CxB"}
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/vocab/lookup.ts"],"names":[],"mappings":";AACA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIpC;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;OAGG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,GAAG,GAAG,EACxB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiDxB"}