Skip to content

Commit 290cbf4

Browse files
authored
Merge pull request #679 from dahlia/feat/452-fep-044f-vocab
Add FEP-044f quote vocabulary and recurse support
2 parents 4c2bace + 3c7cf6d commit 290cbf4

31 files changed

Lines changed: 14382 additions & 1171 deletions

CHANGES.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ To be released.
2222

2323
### @fedify/vocab
2424

25+
- Added [FEP-044f] vocabulary support for Mastodon-style quote posts.
26+
[[#452], [#679]]
27+
28+
- Added `QuoteRequest` and `QuoteAuthorization` classes.
29+
- Added `canQuote` to `InteractionPolicy`.
30+
- Added `quote` and `quoteAuthorization` properties to `Article`,
31+
`ChatMessage`, `Note`, and `Question`.
32+
2533
- Added vocabulary types for [FEP-0837], economic resource coordination
2634
in federated networks. [[#578] by Samuel Brinkmann]
2735

@@ -32,8 +40,17 @@ To be released.
3240
- Added `Measure` class for representing quantities with units of
3341
measure, with `unit` and `numericalValue` properties.
3442

43+
[FEP-044f]: https://w3id.org/fep/044f
3544
[FEP-0837]: https://w3id.org/fep/0837
45+
[#452]: https://github.com/fedify-dev/fedify/issues/452
3646
[#578]: https://github.com/fedify-dev/fedify/issues/578
47+
[#679]: https://github.com/fedify-dev/fedify/pull/679
48+
49+
### @fedify/cli
50+
51+
- Added [FEP-044f] `quote` support to `fedify lookup --recurse`, so the CLI
52+
can follow both the new quote-post relation and the older `quoteUrl`
53+
compatibility surface. [[#452], [#679]]
3754

3855
### @fedify/solidstart
3956

docs/cli.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ When this option is enabled, each argument has to resolve to a collection.
494494

495495
The `--recurse` option is used to recursively follow an object relationship.
496496
This is useful when you want to walk a reply chain through `replyTarget`, or
497-
follow quote relationships through `quoteUrl`.
497+
follow quote relationships through `quote` or `quoteUrl`.
498498

499499
~~~~ sh
500500
fedify lookup --recurse=replyTarget https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
@@ -509,13 +509,15 @@ fedify lookup --recurse=https://www.w3.org/ns/activitystreams#inReplyTo https://
509509
For quote relationships, both the short form and the full IRI are accepted:
510510

511511
~~~~ sh
512+
fedify lookup --recurse=quote https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
513+
fedify lookup --recurse=https://w3id.org/fep/044f#quote https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
512514
fedify lookup --recurse=quoteUrl https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
513515
fedify lookup --recurse=https://www.w3.org/ns/activitystreams#quoteUrl https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
514516
~~~~
515517

516518
For short names, only Fedify property naming is accepted. For example,
517-
`replyTarget` and `quoteUrl` are accepted, while `inReplyTo`, `_misskey_quote`,
518-
and `quoteUri` are not accepted as short forms.
519+
`replyTarget`, `quote`, and `quoteUrl` are accepted, while `inReplyTo`,
520+
`_misskey_quote`, and `quoteUri` are not accepted as short forms.
519521

520522
> [!NOTE]
521523
> `--recurse` and [`-t`/`--traverse`](#t-traverse-traverse-the-collection)

docs/manual/vocab.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ property.
173173
> | [`_misskey_followedMessage`] | `Application.followedMessage`/`Group.followedMessage`/`Organization.followedMessage`/`Person.followedMessage`/`Service.followedMessage` |
174174
> | [`_misskey_quote`] | `Article.quoteUrl`/`ChatMessage.quoteUrl`/`Note.quoteUrl`/`Question.quoteUrl` |
175175
176+
> [!NOTE]
177+
>
178+
> Fedify exposes both the legacy `quoteUrl` compatibility surface and the
179+
> newer [FEP-044f] `quote` surface for quote posts.
180+
>
181+
> - `Article.quoteUrl`, `ChatMessage.quoteUrl`, `Note.quoteUrl`, and
182+
> `Question.quoteUrl` map to the legacy `quoteUrl`, `quoteUri`, and
183+
> `_misskey_quote` properties.
184+
> - `Article.quoteId`/`Article.getQuote()`, `ChatMessage.quoteId`/
185+
> `ChatMessage.getQuote()`, `Note.quoteId`/`Note.getQuote()`, and
186+
> `Question.quoteId`/`Question.getQuote()` represent the
187+
> [FEP-044f] `quote` property.
188+
176189
[`alsoKnownAs`]: https://www.w3.org/TR/did-core/#dfn-alsoknownas
177190
[`anyOf`]: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-anyof
178191
[`attributedTo`]: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto
@@ -190,6 +203,7 @@ property.
190203
[`votersCount`]: https://docs.joinmastodon.org/spec/activitypub/#poll-specific-properties
191204
[`_misskey_followedMessage`]: https://misskey-hub.net/ns#_misskey_followedmessage
192205
[`_misskey_quote`]: https://misskey-hub.net/ns#_misskey_quote
206+
[FEP-044f]: https://w3id.org/fep/044f
193207

194208

195209
Object IDs and remote objects

packages/cli/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ const lookupSchema = pipe(
4141
recurse: optional(
4242
picklist([
4343
"replyTarget",
44+
"quote",
4445
"quoteUrl",
4546
"https://www.w3.org/ns/activitystreams#inReplyTo",
47+
"https://w3id.org/fep/044f#quote",
4648
"https://www.w3.org/ns/activitystreams#quoteUrl",
4749
"https://misskey-hub.net/ns#_misskey_quote",
4850
"http://fedibird.com/ns#quoteUri",

packages/cli/src/lookup.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,22 @@ test("lookupCommand - accepts short-form quoteUrl", () => {
622622
}
623623
});
624624

625+
test("lookupCommand - accepts short-form quote", () => {
626+
const result = parse(lookupCommand, [
627+
"lookup",
628+
"--recurse",
629+
"quote",
630+
"https://example.com/notes/1",
631+
]);
632+
assert.ok(result.success);
633+
if (result.success) {
634+
assert.strictEqual(result.value.recurse, "quote");
635+
}
636+
});
637+
625638
for (
626639
const recurseProperty of [
640+
"https://w3id.org/fep/044f#quote",
627641
"https://www.w3.org/ns/activitystreams#quoteUrl",
628642
"https://misskey-hub.net/ns#_misskey_quote",
629643
"http://fedibird.com/ns#quoteUri",
@@ -676,6 +690,27 @@ test("getRecursiveTargetId - returns quote URL for short name", () => {
676690
assert.equal(getRecursiveTargetId(note, "quoteUrl"), quoteUrl);
677691
});
678692

693+
test("getRecursiveTargetId - returns quote for short name", () => {
694+
const quote = new URL("https://example.com/notes/quoted");
695+
const note = new Note({
696+
id: new URL("https://example.com/notes/1"),
697+
quote,
698+
});
699+
assert.equal(getRecursiveTargetId(note, "quote"), quote);
700+
});
701+
702+
test("getRecursiveTargetId - returns quote for IRI", () => {
703+
const quote = new URL("https://example.com/notes/quoted");
704+
const note = new Note({
705+
id: new URL("https://example.com/notes/1"),
706+
quote,
707+
});
708+
assert.equal(
709+
getRecursiveTargetId(note, "https://w3id.org/fep/044f#quote"),
710+
quote,
711+
);
712+
});
713+
679714
for (
680715
const recurseProperty of [
681716
"https://www.w3.org/ns/activitystreams#quoteUrl",

packages/cli/src/lookup.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ import { colorEnabled, colors, formatObject } from "./utils.ts";
5656
const logger = getLogger(["fedify", "cli", "lookup"]);
5757

5858
const IN_REPLY_TO_IRI = "https://www.w3.org/ns/activitystreams#inReplyTo";
59+
const QUOTE_IRI = "https://w3id.org/fep/044f#quote";
5960
const QUOTE_URL_IRI = "https://www.w3.org/ns/activitystreams#quoteUrl";
6061
const MISSKEY_QUOTE_IRI = "https://misskey-hub.net/ns#_misskey_quote";
6162
const FEDIBIRD_QUOTE_IRI = "http://fedibird.com/ns#quoteUri";
6263
const recurseProperties = [
6364
"replyTarget",
65+
"quote",
6466
"quoteUrl",
6567
IN_REPLY_TO_IRI,
68+
QUOTE_IRI,
6669
QUOTE_URL_IRI,
6770
MISSKEY_QUOTE_IRI,
6871
FEDIBIRD_QUOTE_IRI,
@@ -602,6 +605,11 @@ export function getRecursiveTargetId(
602605
case "replyTarget":
603606
case IN_REPLY_TO_IRI:
604607
return object.replyTargetId;
608+
case "quote":
609+
case QUOTE_IRI: {
610+
const quote = (object as { quoteId?: unknown }).quoteId;
611+
return quote instanceof URL ? quote : null;
612+
}
605613
case "quoteUrl":
606614
case QUOTE_URL_IRI:
607615
case MISSKEY_QUOTE_IRI:

packages/fedify/src/federation/handler.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ import { InboxListenerSet } from "./inbox.ts";
4949
import { MemoryKvStore } from "./kv.ts";
5050
import { createFederation } from "./middleware.ts";
5151

52+
const QUOTE_CONTEXT_TERMS = {
53+
QuoteAuthorization: "https://w3id.org/fep/044f#QuoteAuthorization",
54+
quote: {
55+
"@id": "https://w3id.org/fep/044f#quote",
56+
"@type": "@id",
57+
},
58+
quoteAuthorization: {
59+
"@id": "https://w3id.org/fep/044f#quoteAuthorization",
60+
"@type": "@id",
61+
},
62+
} as const;
63+
64+
const WRAPPER_QUOTE_CONTEXT_TERMS = {
65+
...QUOTE_CONTEXT_TERMS,
66+
QuoteRequest: "https://w3id.org/fep/044f#QuoteRequest",
67+
} as const;
68+
5269
test("handleActor()", async () => {
5370
const federation = createFederation<void>({ kv: new MemoryKvStore() });
5471
let context = createRequestContext<void>({
@@ -408,6 +425,7 @@ test("handleObject()", async () => {
408425
_misskey_quote: "misskey:_misskey_quote",
409426
fedibird: "http://fedibird.com/ns#",
410427
misskey: "https://misskey-hub.net/ns#",
428+
...QUOTE_CONTEXT_TERMS,
411429
quoteUri: "fedibird:quoteUri",
412430
quoteUrl: "as:quoteUrl",
413431
emojiReactions: {
@@ -507,6 +525,7 @@ test("handleObject()", async () => {
507525
_misskey_quote: "misskey:_misskey_quote",
508526
fedibird: "http://fedibird.com/ns#",
509527
misskey: "https://misskey-hub.net/ns#",
528+
...QUOTE_CONTEXT_TERMS,
510529
quoteUri: "fedibird:quoteUri",
511530
quoteUrl: "as:quoteUrl",
512531
emojiReactions: {
@@ -698,6 +717,7 @@ test("handleCollection()", async () => {
698717
"@type": "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
699718
},
700719
_misskey_quote: "misskey:_misskey_quote",
720+
...WRAPPER_QUOTE_CONTEXT_TERMS,
701721
quoteUri: "fedibird:quoteUri",
702722
quoteUrl: "as:quoteUrl",
703723
emojiReactions: {
@@ -721,6 +741,7 @@ test("handleCollection()", async () => {
721741
sensitive: "as:sensitive",
722742
votersCount: "toot:votersCount",
723743
_misskey_quote: "misskey:_misskey_quote",
744+
...WRAPPER_QUOTE_CONTEXT_TERMS,
724745
quoteUri: "fedibird:quoteUri",
725746
quoteUrl: "as:quoteUrl",
726747
emojiReactions: {
@@ -820,6 +841,7 @@ test("handleCollection()", async () => {
820841
sensitive: "as:sensitive",
821842
votersCount: "toot:votersCount",
822843
_misskey_quote: "misskey:_misskey_quote",
844+
...WRAPPER_QUOTE_CONTEXT_TERMS,
823845
quoteUri: "fedibird:quoteUri",
824846
quoteUrl: "as:quoteUrl",
825847
emojiReactions: {
@@ -890,6 +912,7 @@ test("handleCollection()", async () => {
890912
sensitive: "as:sensitive",
891913
votersCount: "toot:votersCount",
892914
_misskey_quote: "misskey:_misskey_quote",
915+
...WRAPPER_QUOTE_CONTEXT_TERMS,
893916
quoteUri: "fedibird:quoteUri",
894917
quoteUrl: "as:quoteUrl",
895918
emojiReactions: {
@@ -956,6 +979,7 @@ test("handleCollection()", async () => {
956979
sensitive: "as:sensitive",
957980
votersCount: "toot:votersCount",
958981
_misskey_quote: "misskey:_misskey_quote",
982+
...WRAPPER_QUOTE_CONTEXT_TERMS,
959983
quoteUri: "fedibird:quoteUri",
960984
quoteUrl: "as:quoteUrl",
961985
emojiReactions: {
@@ -1026,6 +1050,7 @@ test("handleCollection()", async () => {
10261050
sensitive: "as:sensitive",
10271051
votersCount: "toot:votersCount",
10281052
_misskey_quote: "misskey:_misskey_quote",
1053+
...WRAPPER_QUOTE_CONTEXT_TERMS,
10291054
quoteUri: "fedibird:quoteUri",
10301055
quoteUrl: "as:quoteUrl",
10311056
emojiReactions: {
@@ -1275,6 +1300,7 @@ test("respondWithObject()", async () => {
12751300
_misskey_quote: "misskey:_misskey_quote",
12761301
fedibird: "http://fedibird.com/ns#",
12771302
misskey: "https://misskey-hub.net/ns#",
1303+
...QUOTE_CONTEXT_TERMS,
12781304
quoteUri: "fedibird:quoteUri",
12791305
quoteUrl: "as:quoteUrl",
12801306
emojiReactions: {
@@ -1409,6 +1435,7 @@ test("respondWithObjectIfAcceptable", async () => {
14091435
_misskey_quote: "misskey:_misskey_quote",
14101436
fedibird: "http://fedibird.com/ns#",
14111437
misskey: "https://misskey-hub.net/ns#",
1438+
...QUOTE_CONTEXT_TERMS,
14121439
quoteUri: "fedibird:quoteUri",
14131440
quoteUrl: "as:quoteUrl",
14141441
emojiReactions: {
@@ -1594,6 +1621,7 @@ test("handleCustomCollection()", async () => {
15941621
"@type": "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
15951622
},
15961623
_misskey_quote: "misskey:_misskey_quote",
1624+
...WRAPPER_QUOTE_CONTEXT_TERMS,
15971625
quoteUri: "fedibird:quoteUri",
15981626
quoteUrl: "as:quoteUrl",
15991627
emojiReactions: {
@@ -1616,6 +1644,7 @@ test("handleCustomCollection()", async () => {
16161644
sensitive: "as:sensitive",
16171645
votersCount: "toot:votersCount",
16181646
_misskey_quote: "misskey:_misskey_quote",
1647+
...WRAPPER_QUOTE_CONTEXT_TERMS,
16191648
quoteUri: "fedibird:quoteUri",
16201649
quoteUrl: "as:quoteUrl",
16211650
emojiReactions: {

packages/fedify/src/sig/proof.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from "@fedify/vocab";
1010
import { decodeMultibase, importMultibaseKey } from "@fedify/vocab-runtime";
1111
import { assertEquals, assertInstanceOf, assertRejects } from "@std/assert";
12-
import { decodeHex, encodeHex } from "byte-encodings/hex";
12+
import { decodeHex } from "byte-encodings/hex";
1313
import {
1414
ed25519Multikey,
1515
ed25519PrivateKey,
@@ -78,12 +78,11 @@ test("createProof()", async () => {
7878
assertEquals(proof.cryptosuite, "eddsa-jcs-2022");
7979
assertEquals(proof.verificationMethodId, ed25519PublicKey.id);
8080
assertEquals(proof.proofPurpose, "assertionMethod");
81-
console.log(encodeHex(proof.proofValue!));
8281
assertEquals(
8382
proof.proofValue,
8483
decodeHex(
85-
"860dda2bec0ab493ae9b0223ac4133ee82246d7d75d6505be2d6c53755a34454" +
86-
"dcb05feae02403ac51faf853d2e873d467f32a252d3b15a9f025e3ed0750b90b",
84+
"0e63238fdb50a979a7fbd906b471d328a03504de7aa3a0409fad1500b85d6fec" +
85+
"afa2223bfde21ba2eac9446d36f6583c45cb55a98017a0a6a275f50262a4ea06",
8786
),
8887
);
8988
assertEquals(proof.created, created);

packages/fixture/src/fixtures/gotosocial.org/ns.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
"LikeRequest": "gts:LikeRequest",
66
"ReplyRequest": "gts:ReplyRequest",
77
"AnnounceRequest": "gts:AnnounceRequest",
8-
"QuoteRequest": "gts:QuoteRequest",
8+
"QuoteRequest": "https://w3id.org/fep/044f#QuoteRequest",
99
"LikeAuthorization": "gts:LikeApproval",
1010
"ReplyAuthorization": "gts:ReplyAuthorization",
1111
"AnnounceAuthorization": "gts:AnnounceAuthorization",
12-
"QuoteAuthorization": "gts:QuoteAuthorization",
12+
"QuoteAuthorization": "https://w3id.org/fep/044f#QuoteAuthorization",
1313
"likeAuthorization": {
1414
"@id": "gts:likeAuthorization",
1515
"@type": "@id"
@@ -23,7 +23,7 @@
2323
"@type": "@id"
2424
},
2525
"quoteAuthorization": {
26-
"@id": "gts:quoteAuthorization",
26+
"@id": "https://w3id.org/fep/044f#quoteAuthorization",
2727
"@type": "@id"
2828
},
2929
"interactingObject": {

packages/vocab-runtime/src/contexts/gotosocial.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
"LikeRequest": "gts:LikeRequest",
66
"ReplyRequest": "gts:ReplyRequest",
77
"AnnounceRequest": "gts:AnnounceRequest",
8-
"QuoteRequest": "gts:QuoteRequest",
8+
"QuoteRequest": "https://w3id.org/fep/044f#QuoteRequest",
99
"LikeAuthorization": "gts:LikeApproval",
1010
"ReplyAuthorization": "gts:ReplyAuthorization",
1111
"AnnounceAuthorization": "gts:AnnounceAuthorization",
12-
"QuoteAuthorization": "gts:QuoteAuthorization",
12+
"QuoteAuthorization": "https://w3id.org/fep/044f#QuoteAuthorization",
1313
"likeAuthorization": {
1414
"@id": "gts:likeAuthorization",
1515
"@type": "@id"
@@ -23,7 +23,7 @@
2323
"@type": "@id"
2424
},
2525
"quoteAuthorization": {
26-
"@id": "gts:quoteAuthorization",
26+
"@id": "https://w3id.org/fep/044f#quoteAuthorization",
2727
"@type": "@id"
2828
},
2929
"interactingObject": {

0 commit comments

Comments
 (0)